{ Analytica Model Time_of_use_pricing, encoding="UTF-8" } SoftwareVersion 5.5.0 { System Variables with non-default values: } SampleSize := 1000 TypeChecking := 1 Checking := 1 SaveOptions := 2 SaveValues := 0 {!-50299|DiagramColor Model: 65535,65535,65535} {!-50299|DiagramColor Module: 65535,65535,65535} {!-50299|DiagramColor LinkModule: 65535,65535,65535} {!-50299|DiagramColor Library: 65535,65535,65535} {!-50299|DiagramColor LinkLibrary: 65535,65535,65535} {!-50299|DiagramColor Form: 65535,65535,65535} NodeInfo FormNode: 1,0,0,,0,0,,,,0,,,0 {!-50299|NodeColor Text: 62258,62258,62258} Model Time_of_use_pricing Title: Time of use pricing Description: Calculates pricing according to a time-of-use rate tariffs. ~ ~ Imports some real usage data, uses that to create a forecast of usage by hour across a couple years time, and then prices the hourly usage according to California TOU-C and TOU-D tariffs (for examples). This example just covers the time-of-use variability part of the tariff, and not other components.~ ~ Developed during an Analytica user group webinar, 30-Sep-2020. Author: Lonnie Chrisman, Ph.D.~ Lumina Decision Systems Date: Wed, Sep 30, 2020 10:56 AM DiagState: 2,70,20,1080,383,17 WindState: 2,38,400,720,350 FontStyle: Arial,15 FileInfo: 0,Model Time_of_use_pricing,2,2,0,0,D:\Documents\Analytica\Example Models\Time of Usage\Time of use pricing.ana Module Historical_usage Title: Historical usage Description: This module imports historical usage data from a spreadsheet that came from ~ NationalGridUS.com Author: Lonnie Chrisman, Ph.D.~ Lumina Decision Systems Date: Wed, Sep 30, 2020 1:30 PM NodeLocation: 80,40,1 NodeSize: 48,24 DiagState: 2,291,93,624,345,17,, Variable ave_cust_usage Title: ave cust usage Units: kWh/h Description: The electrical average usage per customer (by usage_date and hour) in the Massachusetts region covered by the MECO spreadsheet data that is imported. ~ ~ The source spreadsheet data was in a fact-table format, with no headers.~ ~ This defines the global result indexes as well as the transformed data (transformed from a fact-table form into a multidimensional array form). The indexes use the ComputedBy trick to enable them to be defined at the same time as this calculation, saving the need for an intermediate global variable (in this case sheet). Definition: Local sheet:= SpreadsheetRange(wb,'MECO ALL!');~ Rate_code := Unique(sheet[.Column='A']);~ Usage_date := Unique(sheet[.Column='B']);~ MdTable(sheet,sheet.Row,sheet.Column,[Rate_code,Usage_date],~ valueColumn:Array(Hour,'C'..'Z') ) NodeLocation: 232,128,1 NodeSize: 48,24 WindState: 2,373,304,720,350 ValueState: 2,188,84,771,468,,MIDM Aliases: Alias Al1973000061 ReformVal: [Hour,Usage_date] Index Hour Title: Hour Description: The hour of the day. ~ ~ Although Analytica indexes usually start at 1 by convention, in the case of Hour it is better to use 0..23 instead of 1..24 so that Hour=8 corresponds to the hour starting at 8am. It matches the standard "military time" convention for hour numbering. Definition: 0..23 NodeLocation: 96,312,1 NodeSize: 48,24 WindState: 2,403,391,720,350 Aliases: Alias Al1939445629 {!40300|Att_SlicerPopupSize: 227,364} Index Usage_date Title: Usage date Description: Index for the date of the historic usage data.~ ~ In many cases, especially when data is being imported, the most convenient place to define the values for a globar index is in the variable Definition that is processing the incoming data. Index values may depend on the data, creating a chicken-and-egg problem. ComputedBy( ) gives you a solution to this, which maintains referential transparency since you still know where it is being computed. Definition: ComputedBy(ave_cust_usage) NodeLocation: 96,192,1 NodeSize: 48,24 WindState: 2,448,398,720,350 ValueState: 2,244,250,416,303,0,MIDM Aliases: Alias Al228169597 Constant Rate_filename Title: Rate filename Description: Demonstrates a trick for remembering which file was selected in the file explorer during the previous SpreadsheetOpen call. If the file exists, it reads it without asking. If the file isn't there, SpreadsheetOpen puts up a file explorer so you can find the spreadsheet. Then wb assignes to this variable, which remembers the default value for next time. Definition: ComputedBy(wb,'MECOLS0620.xlsx') NodeLocation: 96,40,1 NodeSize: 48,24 WindState: 2,377,409,720,350 Variable wb Title: wb Description: The excel workbook object. Notice that this saves the filename so it doesn't have to ask later. See the Description for Rate_filename.~ ~ wb->Name returns the filename without the full path. Use this when you expect the Excel file to be in the same folder as your model. Then when you share both with someone else, they won't have to find the excel file the first time.~ ~ wb->FullName returns the full file path. Use this when your Excel file might be in a different folder. The path saved might be specific to your computer, so when you share your version of the model with a colleague, they may need to find it in the file explorer dialog. Definition: Local wb:=SpreadsheetOpen(Rate_filename);~ Rate_filename := wb->Name;~ wb NodeLocation: 96,128,1 NodeSize: 48,24 WindState: 2,349,411,720,350 ValueState: 2,515,89,416,303,,MIDM Index Rate_code Title: Rate code Description: A rate code category that happens to be present in the incoming spreadsheet data. Definition: ComputedBy(ave_cust_usage) NodeLocation: 96,256,1 NodeSize: 48,24 WindState: 2,432,280,720,350 Variable cached Title: cached Description: This would hold a cached version of the spreadsheet, once the button is pressed. NodeLocation: 232,208,1 NodeSize: 48,24 Button Import_into_model Title: Import into model Description: In this model, we are not caching the data in the model. However, this button demonstrates an alternative way of structuring your import, which caches the data with your model, thus breaking the link to the spreadsheet, but allowing you to import, or re-import, the data with the press of a button.~ ~ One advantage of the button approach is you can run the model without having the spreadsheet file.~ ~ One disadvantage is that the data is stored with the file, thus your model file is larger, and you might be using older data without realizing it (if you haven't pushed the import button for a while). NodeLocation: 232,276,1 NodeSize: 48,28 WindState: 2,424,43,720,350 OnClick: MsgBox("This button is here to demonstrate how you could cache data is the model instead of keeping a live import.~ ~ However, once you press this, you will replace the ComputedBy definitions for the indexes, so I recommend not proceeding. Press Cancel to abort.", buttons:1, title:"Alert!!");~ ~ Local sheet:= SpreadsheetRange(wb,'MECO ALL!');~ Rate_code := Unique(sheet[.Column='A']);~ Usage_date := Unique(sheet[.Column='B']);~ cached := ~ MdTable(sheet,sheet.Row,sheet.Column,[Rate_code,Usage_date],~ valueColumn:Array(Hour,'C'..'Z') ) Close Historical_usage Module Projected_usage Title: Projected usage Description: Maps historical past usage data to future usage estimates.~ ~ A key challenge here is using a representative past date for the future data -- e.g., landing on the same day of the week, at the same time of year, etc.~ ~ One thing we didn't do here was to add in factors that will be different in the future compared to the past. For example, more solar panels. These could be estimate adjustments to past data. Author: Lonnie Chrisman, Ph.D.~ Lumina Decision Systems Date: Wed, Sep 30, 2020 1:30 PM NodeLocation: 80,120,1 NodeSize: 48,24 DiagState: 2,348,60,624,345,17 Variable forecasted_usage Title: forecasted usage Units: kWh/h Description: The projected future average electrical usage by residential customer by future date and hour.~ ~ There were a few 0s in the data (possible due to day light savings time). We use the preceding hour's data to replace the 0s. Definition: Local x := ave_cust_usage[ Rate_code='R-1', Usage_date = Usage_date_for_forec ];~ if x=0 then x[Hour=Hour-1] else x NodeLocation: 256,112,1 NodeSize: 48,24 ValueState: 2,374,100,635,470,1,MIDM Aliases: Alias Al2107217789 ReformVal: [Forecast_date,Hour] Att_ResultSliceState: [Rate_code,4,Hour,1,Forecast_date,1] Index Forecast_date Title: Forecast date Description: The date index used for forecasted usage data. Definition: 1-Sep-2020 .. 31-Aug-2022 NodeLocation: 256,48,1 NodeSize: 48,24 WindState: 2,401,349,720,350 Aliases: Alias Al1038325629 NumberFormat: 2,D,4,2,0,0,4,0,$,0,"d-MMM-yyyy www",0,,,0,0,15 Alias Al1973000061 Title: ave cust usage Definition: 1 NodeLocation: 96,112,1 NodeSize: 48,24 Original: ave_cust_usage Alias Al228169597 Title: Usage date Definition: 1 NodeLocation: 96,48,1 NodeSize: 48,24 Original: Usage_date Alias Al1939445629 Title: Hour Definition: 1 NodeLocation: 368,48,1 NodeSize: 48,24 Original: Hour Variable Usage_date_for_forec Title: Usage date for forecast Description: When mapping historic usage to future usage, this determines which historic date will be used for the forecast date. It isn't enough to just use the same date in the previous year, because the day of the week matters for the tariff pricing. So this finds the most recent date in Usage_date that lands on the same day of the week as forecast date and is as close as possible to the same date in the year. So it will always use usage date from a Thursday when mapping historic data to Thursday.~ ~ Note: Another interesting, but different, approach would have been to project the past data using a linear regression. The array Usage_date[Usage_date=d1 , defVal:null] would give the historic points for the regression. Left as an exercise for the reader. Definition: LocalIndex yearsBack := -1..-5;~ Local wd := DatePart(Forecast_date,'w');~ Local d1 := Nearest_date_on_wd( DateAdd( Forecast_date, yearsBack, 'Y'), wd);~ Max(Usage_date[Usage_date=d1 , defVal:null],yearsBack) NodeLocation: 96,176,1 NodeSize: 56,24 WindState: 2,409,352,720,350 ValueState: 2,221,17,680,447,,MIDM {!40700|Att_CellFormat: CellSpan(Forecast_date,CellNumberFormat('Suffix',4,0,0,dateFormat:'d-MMM-yyyy www',fullPrecision:0,numbersAsDates:0,datesAsNumbers:0,digits_:4,zeroes_:2),1,730)} Function Nearest_date_on_wd(date, wdNum) Title: Nearest date on wd Description: Returns the nearest date to «date» that falls on a given weekday, where «wdNum» is 1=Sun..7=Sat. Definition: Local wd0 := DatePart(date,'w');~ Local diff := Mod(wdNum-wd0, 7, pos:true);~ if diff <=3 then date+diff else date+diff-7 NodeLocation: 96,248,1 NodeSize: 56,24 WindState: 2,353,106,720,350 Close Projected_usage Module Tariff_periods Title: Tariff periods Description: Contains calculations that determine which billing category a forecast_date falls into. Author: Lonnie Date: Wed, Sep 30, 2020 1:30 PM NodeLocation: 208,40,1 NodeSize: 48,24 DiagState: 2,156,121,888,365,17 Index Season Title: Season Description: A list of the season names recognized by the TOU-D tariffs (which divides the year into just summer and winter). Definition: ['Summer','Winter'] NodeLocation: 816,48,1 NodeSize: 48,24 Index Peak_cat Title: Peak cat Description: The peak tier categories used in the tariffs. Definition: ['Peak','Off-Peak'] NodeLocation: 816,104,1 NodeSize: 48,24 Variable Is_peak Title: Is peak Description: True when the forecast_date & Hour combination is at a peak time in the selected Tariff. Definition: DetermTable(Tariff_plan)(16<=Hour<21,17<=Hour<=20 and is_weekday and not is_holiday) NodeLocation: 464,112,1 NodeSize: 48,24 DefnState: 2,301,165,768,303,0,DFNM Variable Is_Summer Title: Is Summer Description: True when the Forecast_day is in the summer Definition: Local y:=DatePart(Forecast_date,'y');~ Summer_start[Year=y] <= Forecast_date <= Summer_End[Year=y] NodeLocation: 464,216,1 NodeSize: 48,24 ValueState: 2,196,202,416,303,1,MIDM Variable Is_Holiday Title: Is Holiday Description: True if the Forecast_date is a holiday. Definition: Max(Date_of_holiday[Year=DatePart(Forecast_date,'y')]=Forecast_date,Holiday) NodeLocation: 336,80,1 NodeSize: 48,24 Variable Hourly_season Title: Hourly season Description: The same of the season corresponding the the given Forecast_date.~ ~ One technique to note is that it is a good practice in a case like this to set the Domain to the Season index, which will help catch errors in the event you misspell a Season name (including using the wrong capitalization) or use a season name that isn't recognized. Definition: if Is_Summer then 'Summer' else 'Winter' NodeLocation: 584,216,1 NodeSize: 48,24 ValueState: 2,212,218,416,303,0,MIDM Domain: Season {!40300|DomainExpr: Season} Variable Hourly_peak_cat Title: Hourly peak cat Description: The name of the peak category, corresponding to the season. ~ ~ One technique to note is that it is a good practice in a case like this to set the Domain to the Peak_cat index, which will help catch errors in the event you misspell a peak name (including using the wrong capitalization) or use a season name that isn't recognized. Definition: if Is_peak then 'Peak' else 'Off-Peak' NodeLocation: 584,112,1 NodeSize: 48,24 Domain: Peak_cat {!40300|DomainExpr: Peak_cat} Variable Is_Weekday Title: Is Weekday Description: True if the Forecast_date is a weekday. Definition: 10 then Slice(dayw,n) else Slice(dayw,IndexLength(dayw)+n+1);~ MakeDate(year,month,d) NodeLocation: 72,144,1 NodeSize: 48,24 WindState: 2,309,38,720,350 Index Weekday Title: Weekday Description: List of weekday names. Used to make the calling of Nth_day_of_month more convenient and readable. Definition: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] NodeLocation: 72,216,1 NodeSize: 48,24 Alias Al1038325629 Title: Forecast date Definition: 1 NodeLocation: 72,280,1 NodeSize: 48,24 Original: Forecast_date Close Tariff_periods Module Pricing Title: Pricing Description: Calculations of costs of forecasted electricity usage. Author: Lonnie Chrisman, Ph.D.~ Lumina Decision Systems Date: Wed, Sep 30, 2020 1:30 PM NodeLocation: 208,120,1 NodeSize: 48,24 DiagState: 2,267,45,727,457,17 Variable Hourly_price Title: Hourly price Units: $ Description: The forecasted cost of the electricity used in each hour. Definition: Hourly_rate * forecasted_usage NodeLocation: 336,232,1 NodeSize: 48,24 ValueState: 2,496,78,636,475,1,MIDM Aliases: FormNode Fo980260733 ReformVal: [Forecast_date,Null] Att_ResultSliceState: [Hour,21,Forecast_date,1] Att_ColorRole: Null Decision Tariff_rates Title: Tariff rates Units: $ / kWh Description: The time-of-use portions of the electricity rates as defined in the tariff documents:~ ~ TOU-C tariff rate schedule ~ TOU-D tariff rate schedule~ Definition: DetermTable(Tariff_plan,Season,Peak_cat)(~ 0.41333,0.34989,~ 0.31624,0.29891,~ 0.36476,0.2698,~ 0.29089,0.27351) NodeLocation: 224,48,1 NodeSize: 48,24 DefnState: 2,127,15,416,303,0,DFNM Aliases: FormNode Fo1718458237 ReformDef: [Peak_cat,Season] Att_EditSliceState: [Sys_LocalIndex('DomainIndex'),2,Season,1,Peak_cat,1] Variable Daily_price Title: Daily price Units: $ Description: The projected cost of electricity used in a given day. Definition: Sum(Hourly_price,Hour) NodeLocation: 336,312,1 NodeSize: 48,24 Objective Monthly_bill Title: Monthly bill Units: $ Description: Forecasted monthly bill for electricity usage. Definition: Aggregate( Daily_price, Round(Forecast_date,dateUnit:'M'), Forecast_date, Forecast_month) NodeLocation: 336,392,1 NodeSize: 48,24 ValueState: 2,83,71,747,418,1,MIDM Aliases: FormNode Fo2054002557 GraphSetup: {!50007|Graph_BarOutline:0}~ Graph_HLabelRotation:45~ Att_ForceCategorical Graph_Primary_Valdim:1 Decision Tariff_plan Title: Tariff plan Description: Select the Tariff rate plan that you with to compute (or both) Definition: Choice(Self,0) NodeLocation: 80,48,1 NodeSize: 48,24 Aliases: FormNode Fo322279293 Domain: ['TOU-C','TOU-D'] {!40300|DomainExpr: Discrete('TOU-C','TOU-D',type:['text'])} {!40200|Att_ChoiceIndexes: Keyword Self} Variable Hourly_rate Title: Hourly rate Units: $ / kWh Description: The hourly rate that applies to a given hour and date. Definition: Tariff_rates[Season=Hourly_season, Peak_cat=Hourly_peak_cat] NodeLocation: 224,120,1 NodeSize: 48,24 DefnState: 2,36,52,416,303,0,DFNM ValueState: 2,470,215,653,392,0,MIDM ReformVal: [Hour,Forecast_date] Alias Al2107217789 Title: forecasted usage Definition: 1 NodeLocation: 160,232,1 NodeSize: 48,24 Original: forecasted_usage Index Forecast_month Title: Forecast month Definition: Unique(Floor(Forecast_date,dateUnit:'M')) NodeLocation: 160,392,1 NodeSize: 48,24 ValueState: 2,423,47,416,535,,MIDM NumberFormat: 2,D,4,2,0,0,4,0,$,0,"yyyy MMM",0,,,0,0,15 Close Pricing FormNode Fo322279293 Title: Tariff plan Definition: 0 NodeLocation: 444,24,1 NodeSize: 132,16 NodeInfo: 1,,,,,,,89,,,,,,0 Original: Tariff_plan FormNode Fo980260733 Title: Hourly price Definition: 1 NodeLocation: 444,88,1 NodeSize: 132,16 NodeInfo: 1,,,,,,,89,,,,, Original: Hourly_price FormNode Fo2054002557 Title: Monthly bill Definition: 1 NodeLocation: 444,120,1 NodeSize: 132,16 NodeInfo: 1,,,,,,,89,,,,, Original: Monthly_bill FormNode Fo1718458237 Title: Tariff rates Definition: 0 NodeLocation: 444,56,1 NodeSize: 132,16 NodeInfo: 1,,,,,,,89,,,,, Original: Tariff_rates Close Time_of_use_pricing