From 66f8a9f79c3a74307a87a6b9bc97c91a6b442cf9 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 23 Jul 2025 08:24:43 -0700 Subject: [PATCH 01/16] Initial implementation of blended rate macro --- .../Residential/Solar for All Calculator.lk | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 deploy/runtime/macros/Residential/Solar for All Calculator.lk diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk new file mode 100644 index 0000000000..05e14eed22 --- /dev/null +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -0,0 +1,150 @@ +/*@ +

This is the Solar for All (SfA) macro.

+@*/ + +// functions ///// + +function blended_buy_rate( bill_wo_system, usage_wo_system ) { + T = { 'value' = null, 'status' = '' }; + if ( usage_wo_system == 0 ) { + T.value = null; + T.status = 'divide by zero (usage)'; + } + else { + T.value = bill_wo_system / usage_wo_system; + T.status = 'ok'; + } + return T; +} + +function blended_sell_rate( bill_savings, power_generated ) { + T = { 'value' = null, 'status' = '' }; + if ( power_generated == 0 ) { + T.value = null; + T.status = 'divide by zero (generation)'; + } + else { + T.value = bill_savings / power_generated; + T.status = 'ok'; + } + return T; +} + +function bill_savings_percentage( bill_wo_system, bill_w_system ) { + T = { 'value' = null, 'status' = '' }; + if ( bill_w_system == 0 ) { + T.value = null; + T.status = 'divide by zero (bill with system)'; + } + else { + T.value = (( bill_wo_system - bill_w_system ) / bill_wo_system ) * 100; + T.status = 'ok'; + } + return T; +} + +// main ///// + +// check for valid configuration only PVWatts/Residential is supported +config = configuration(); + +tech = config[0]; + +if ( tech != 'PVWatts' ) { + msgbox('Note!\n' + + 'This macro was designed for PVWatts configurations. It should work for other performance models,\n'+ + 'but please contact sam.support@nrel.gov if you encounter problems with the macro.'); +} + +// run simulation +outln('Running simulations...'); +sim_msg = ''; +ok = simulate(sim_msg, true); +if (!ok) { + msgbox('Simulation failed!\n' + sim_msg); + outln('Simulation failed!'); + outln('Simulation message: ' + sim_msg); + outln('Exiting script.'); + exit; +} +outln('Done.'); +outln(); + +// get outputs + + +// calculate rates + +blended_buy = blended_buy_rate(get('utility_bill_wo_sys_year1'), get('year1_electric_load')); +if ( blended_buy.status != 'ok' ) { + outln('Blended buy rate calculation failed: ' + blended_buy.status); + exit; +} + +blended_sell = blended_sell_rate(get('savings_year1'), get('ac_annual')); +if ( blended_sell.status != 'ok' ) { + outln('Blended sell rate calculation failed: ' + blended_sell.status); + exit; +} + +savings_percent = bill_savings_percentage(get('utility_bill_wo_sys_year1'), get('utility_bill_w_sys_year1')); +if ( savings_percent.status != 'ok' ) { + outln('Savings calculation failed: ' + savings_percent.status); + exit; +} + +// console output + +str_blended_buy = sprintf('Blended buy rate = $%.3f/kWh load (year 1)', blended_buy.value); +str_blended_sell = sprintf('Blended sell rate = $%.3f/kWh generated (year 1)', blended_sell.value); +str_savings_percent = sprintf('Bill savings = %.3f%% (year 1)', savings_percent.value); +outln(str_blended_buy); +outln(str_blended_sell); +outln(str_savings_percent); +outln(); + +// html output + +str_html = +'' + +'

Solar for All Blended Rates

' + +'' + +'' + +'' + +'' + +'' + +'
RateValue
Blended buy rate' + sprintf('$%.3f/kWh load (year 1)',blended_buy.value) + '
Blended sell rate' + sprintf('$%.3f/kWh generated (year 1)',blended_sell.value) + '
Bill savings' + sprintf('$%.1f%% (year 1)',savings_percent.value) + '
' + +''; + +html_dialog( str_html, 'Solar for All Calculator Results', [200,200,600,200] ); + +// csv output + +str_csv = +'variable,value,units\n' + +'blended buy rate' + ',' + to_string(blended_buy.value) + ',' + '$/kWh of load in year 1' + '\n' + +'blended sell rate' +',' + to_string(blended_sell.value) + ',' + '$/kWh of generation in year 1' + '\n' + +'bill savings' + ',' + to_string(savings_percent.value) + ',' + '% of annual bill in year 1'; + +// if running from .sam file that has not been saved, choose folder to save .csv +if ( project_file() == '' ) { + dir = choose_dir(homedir(),'Choose folder to save CSV results'); +} +else { + dir = cwd(); +} + +f_csv = dir + '/' + replace(case_name(),'/','-') + '-solar-for-all-calculator-results.csv'; + +ok = write_text_file(f_csv, str_csv); +if (!ok) { + outln('Failed to write results to CSV file: ' + fcsv + '\n. Exiting script'); + exit; +} +else { + browse( f_csv ); +} + +// finish + +outln('Macro done.'); From b787021cbbc5590c73951f20e35eaff2a4f6482f Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 23 Jul 2025 09:21:45 -0700 Subject: [PATCH 02/16] Draft instructions for SfA macro --- .../Residential/Solar for All Calculator.lk | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index 05e14eed22..c2c765034d 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -1,5 +1,83 @@ /*@ -

This is the Solar for All (SfA) macro.

+

The Solar for All calculator is a script that you can use to estimates the reduction in electricity bills for residents when a photovoltaic (PV) system is installed on a residential or multi-family building. The macro calculates retail blended electricity rates and a bill savings percentage. Although this macro is designed for PVWatts/Residential configurations, it should work for any configuration with the Residential or Commercial financial model.

+

For more detailed information and instructions, see [link to webpage].

+
Overall Steps
+
    +
  1. +

    Create a PVWatts/Residential case with inputs for the system design, costs, and financial parameters.

    +
  2. +
  3. +

    Run the macro.

    +
  4. +
  5. +

    Analyze the results.

    +
  6. +
  7. +

    Modify the inputs and repeat Steps 2 and 3 until you are satisfied with the results.

    +
  8. +
+
Required Information
+ +

*SAM provides tools for downloading electricity rate and synthetic time series usage data if you don't have access to the data.

+
Optional Information
+

If you are running SAM simulations to calculate metrics like the net present value (NPV) or payback period, you will need to provide the following information in addition to the required information listed above. The following optional information is not used by the Solar for All macro.

+ +
Instructions
+

These are the basic steps to use the Solar for All macro. For more detailed instructions and a demonstration video, see [link to webpage].

+
    +
  1. +

    On the Location and Resource input page, download a default TMY file by typing a street address or location name in the box under "Download Weather Files" and click Download and add to library.

    +
  2. +
  3. +

    On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type.

    +
  4. +
  5. +

    On the Electricity Rates page, click Search for rates and download the appropriate rate structure.

    +
  6. +
  7. +

    On the Electric Load page either a) import time series usage data or b) download synthetic data and scale monthly totals to match your monthly usage data.

    +
      +
    1. +

      To upload time series usage data, click Edit array, then click Import to import time series usage data from a CSV or text file

      +
    2. +
    3. +

      To download synthetic usage data, click Download Electric Load Data, then check Scale electric load profile to monthly usage to activate the Edit Values button and click Edit values and type monthly usage data.

      +
    4. +
    +
  8. +
  9. +

    On the File menu, click Save to save your data to a .sam file.

    +
  10. +
  11. +

    Click Run at the top of this window to run the macro. When the macro finishes, it will display open a new window displaying blended rate and bill savings percentage results and export the results to a CSV files with the name "[case name]-solar-for-all-calculator-results.csv" in the same folder as your .sam file.

    +
  12. +
  13. +

    Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results.

    +
  14. +
@*/ // functions ///// @@ -107,7 +185,7 @@ outln(); str_html = '' + -'

Solar for All Blended Rates

' + +'
Solar for All Blended Rates
' + '' + '' + '' + From e2e92bd9136b45adfa89cd197894544a0e6fd700 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 23 Jul 2025 09:55:13 -0700 Subject: [PATCH 03/16] Use annual_energy instead of ac_annual Fix check on opening CSV file. --- .../runtime/macros/Residential/Solar for All Calculator.lk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index c2c765034d..904b433062 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -159,7 +159,7 @@ if ( blended_buy.status != 'ok' ) { exit; } -blended_sell = blended_sell_rate(get('savings_year1'), get('ac_annual')); +blended_sell = blended_sell_rate(get('savings_year1'), get('annual_energy')); if ( blended_sell.status != 'ok' ) { outln('Blended sell rate calculation failed: ' + blended_sell.status); exit; @@ -216,8 +216,8 @@ f_csv = dir + '/' + replace(case_name(),'/','-') + '-solar-for-all-calculator-re ok = write_text_file(f_csv, str_csv); if (!ok) { - outln('Failed to write results to CSV file: ' + fcsv + '\n. Exiting script'); - exit; + outln('Failed to write results to CSV file: ' + f_csv + '\n The file may be open in another program.'); + outln(); } else { browse( f_csv ); From 41a3879f638ee0dd198622252cd728b863c37987 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Tue, 29 Jul 2025 13:05:45 -0700 Subject: [PATCH 04/16] Initial draft of SfA macro --- .../Residential/Solar for All Calculator.lk | 199 +++++++++--------- 1 file changed, 97 insertions(+), 102 deletions(-) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index 904b433062..267b3ddc78 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -1,37 +1,46 @@ /*@ -

The Solar for All calculator is a script that you can use to estimates the reduction in electricity bills for residents when a photovoltaic (PV) system is installed on a residential or multi-family building. The macro calculates retail blended electricity rates and a bill savings percentage. Although this macro is designed for PVWatts/Residential configurations, it should work for any configuration with the Residential or Commercial financial model.

+

The Solar for All Calculator macro calculates the Annual Baseline Utility Costs and Solar Generation Compensation Rate as described in the U.S. Environmental Protection Agency Solar for All Household Savings Best Practices handbook. You can use these values to calculate the Household Savings Percent Savings Provided.

For more detailed information and instructions, see [link to webpage].

+

+ THIS IS A DRAFT VERSION FOR DEMONSTRATION PURPOSES ONLY +

Overall Steps
  1. -

    Create a PVWatts/Residential case with inputs for the system design, costs, and financial parameters.

    + Specify inputs on the following input pages. Inputs on other pages can be ignored. +
      +
    • Location and Resource
    • +
    • System Design
    • +
    • Electricity Rates
    • +
    • ELectric Load
    • +
  2. -

    Run the macro.

    + Run the macro.
  3. -

    Analyze the results.

    + Analyze the results.
  4. -

    Modify the inputs and repeat Steps 2 and 3 until you are satisfied with the results.

    + Modify the inputs and repeat Steps 2 and 3 until you are satisfied with the results.
Required Information
  • -

    Location of the PV system as a street address or latitude/longitude pair

    + Location of the PV system as a street address or latitude/longitude pair
  • -

    Size of the PV system

    + Size of the PV system
  • -

    Orientation (tilt and azimuth angles) of PV modules (panels)

    + Orientation (tilt and azimuth angles) of PV modules (panels)
  • -

    Retail electricity rate structure from the electricity service provider*

    + Retail electricity rate structure from the electricity service provider*
  • -

    Time series (hourly or subhourly) electricity usage data or monthly total usage data*

    + Time series (hourly or subhourly) electricity usage data or monthly total usage data*

*SAM provides tools for downloading electricity rate and synthetic time series usage data if you don't have access to the data.

@@ -39,103 +48,67 @@

If you are running SAM simulations to calculate metrics like the net present value (NPV) or payback period, you will need to provide the following information in addition to the required information listed above. The following optional information is not used by the Solar for All macro.

  • -

    Project installation and operating costs.

    + Project installation and operating costs.
  • -

    Financial parameters (taxes, debt, incentives)

    + Financial parameters (taxes, debt, incentives)
-
Instructions
+
Instructions

These are the basic steps to use the Solar for All macro. For more detailed instructions and a demonstration video, see [link to webpage].

  1. -

    On the Location and Resource input page, download a default TMY file by typing a street address or location name in the box under "Download Weather Files" and click Download and add to library.

    + On the Location and Resource input page, download a default TMY file by typing a street address or location name in the box under "Download Weather Files" and click Download and add to library.
  2. -

    On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type.

    + On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type.
  3. -

    On the Electricity Rates page, click Search for rates and download the appropriate rate structure.

    + On the Electricity Rates page, click Search for rates and download the appropriate rate structure.
  4. -

    On the Electric Load page either a) import time series usage data or b) download synthetic data and scale monthly totals to match your monthly usage data.

    -
      + On the Electric Load page either a) import time series usage data or b) download synthetic data and scale monthly totals to match your monthly usage data. +
      • -

        To upload time series usage data, click Edit array, then click Import to import time series usage data from a CSV or text file

        + To upload time series usage data from a file, click Edit array, then click Import to import time series usage data from a CSV or text file.
      • -

        To download synthetic usage data, click Download Electric Load Data, then check Scale electric load profile to monthly usage to activate the Edit Values button and click Edit values and type monthly usage data.

        + To download synthetic usage data from the OpenEI "Commercial and Residential Hourly Load Profiles for all TMY3 Locations in the United States" dataset, click Download Electric Load Data, then check Scale electric load profile to monthly usage to activate the Edit Values button and click Edit values and type monthly usage data.
      • -
    +
  5. -

    On the File menu, click Save to save your data to a .sam file.

    + On the File menu, click Save to save your data to a .sam file.
  6. -

    Click Run at the top of this window to run the macro. When the macro finishes, it will display open a new window displaying blended rate and bill savings percentage results and export the results to a CSV files with the name "[case name]-solar-for-all-calculator-results.csv" in the same folder as your .sam file.

    + Click Run at the top of this window to run the macro. When the macro finishes, it will display open a new window displaying blended rate and bill savings percentage results and export the results to a CSV files with the name "[case name]-solar-for-all-calculator-results.csv" in the same folder as your .sam file.
  7. -

    Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results.

    + Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results.
@*/ -// functions ///// - -function blended_buy_rate( bill_wo_system, usage_wo_system ) { - T = { 'value' = null, 'status' = '' }; - if ( usage_wo_system == 0 ) { - T.value = null; - T.status = 'divide by zero (usage)'; - } - else { - T.value = bill_wo_system / usage_wo_system; - T.status = 'ok'; - } - return T; -} - -function blended_sell_rate( bill_savings, power_generated ) { - T = { 'value' = null, 'status' = '' }; - if ( power_generated == 0 ) { - T.value = null; - T.status = 'divide by zero (generation)'; - } - else { - T.value = bill_savings / power_generated; - T.status = 'ok'; - } - return T; -} - -function bill_savings_percentage( bill_wo_system, bill_w_system ) { - T = { 'value' = null, 'status' = '' }; - if ( bill_w_system == 0 ) { - T.value = null; - T.status = 'divide by zero (bill with system)'; - } - else { - T.value = (( bill_wo_system - bill_w_system ) / bill_wo_system ) * 100; - T.status = 'ok'; - } - return T; -} - -// main ///// - // check for valid configuration only PVWatts/Residential is supported config = configuration(); -tech = config[0]; +fin = config[1]; -if ( tech != 'PVWatts' ) { +ok = false; +if ( fin == 'Commercial' || fin == 'Residential' ) { + ok = true; +} + +if (!ok) { msgbox('Note!\n' + - 'This macro was designed for PVWatts configurations. It should work for other performance models,\n'+ - 'but please contact sam.support@nrel.gov if you encounter problems with the macro.'); + 'This macro requires a case with the Residential or Commercial financial model.\n' + + 'The financial model for this case is: ' + fin + '\n' + + 'Exiting macro.'); + exit; } // run simulation -outln('Running simulations...'); +out('Running simulation...'); sim_msg = ''; ok = simulate(sim_msg, true); if (!ok) { @@ -145,64 +118,86 @@ if (!ok) { outln('Exiting script.'); exit; } -outln('Done.'); +outln('done.'); outln(); -// get outputs +// Annual Baseline Utility Cost ABUC +// = bill without system (year 1) +ABUC = get('utility_bill_wo_sys_year1'); // $/yr +// ABUC rate +abuc_rate = ABUC / get('year1_electric_load'); // $/kWh year 1 -// calculate rates - -blended_buy = blended_buy_rate(get('utility_bill_wo_sys_year1'), get('year1_electric_load')); -if ( blended_buy.status != 'ok' ) { - outln('Blended buy rate calculation failed: ' + blended_buy.status); +// Solar Generation Compensation Rate SGCR +A = get('savings_year1'); // $ +B = get('annual_energy'); // kWh year 1 +if ( B == 0 ) { + outln('Annual energy is zero. Cannot calculate SGCR due to divide by zero error. Exiting script.'); exit; } +SGCR = A/B; // $/kWh generation year 1 -blended_sell = blended_sell_rate(get('savings_year1'), get('annual_energy')); -if ( blended_sell.status != 'ok' ) { - outln('Blended sell rate calculation failed: ' + blended_sell.status); - exit; +// Bill savings +A = get('utility_bill_wo_sys_year1'); // $ +B = get('utility_bill_w_sys_year1'); // $ +if ( A == 0 ) { + outln('Utility bill with system for year 1 is zero. Cannot calculate savings due to divide by zero error.'); } - -savings_percent = bill_savings_percentage(get('utility_bill_wo_sys_year1'), get('utility_bill_w_sys_year1')); -if ( savings_percent.status != 'ok' ) { - outln('Savings calculation failed: ' + savings_percent.status); - exit; +else +{ + bill_savings = ( A - B ) / A * 100; // % } // console output -str_blended_buy = sprintf('Blended buy rate = $%.3f/kWh load (year 1)', blended_buy.value); -str_blended_sell = sprintf('Blended sell rate = $%.3f/kWh generated (year 1)', blended_sell.value); -str_savings_percent = sprintf('Bill savings = %.3f%% (year 1)', savings_percent.value); -outln(str_blended_buy); -outln(str_blended_sell); -outln(str_savings_percent); +label_abuc = 'Annual Baseline Utility Cost (ABUC)'; +label_load_year1 = 'Household consumption'; +label_abuc_rate = 'ABUC rate'; +label_sgcr = 'Solar Generation Compensation Rate (SGCR)'; +label_savings_year1 = 'Electricity bill savings amount'; +label_gen_year1 = 'Electricity generation'; + +str_abuc = sprintf('$%.0f (year 1)', ABUC); +str_load_year1 = sprintf('%.0f kWh consumed (year 1)', get('year1_electric_load')); +str_abuc_rate = sprintf('$%.3f/kWh consumed (year 1)', abuc_rate); +str_sgcr = sprintf('$%.3f/kWh generated (year 1)', SGCR); +str_savings_year1 = sprintf('%.0f kWh consumed (year 1)', A); +str_gen_year1 = sprintf('%.0f kWh generated (year 1)', B); + +outln(label_abuc + ' = ' + str_abuc); +outln(label_load_year1 + ' = ' + str_load_year1); +outln(label_abuc_rate + ' = ' + str_abuc_rate); +outln(label_sgcr + ' = ' + str_sgcr); +outln(label_savings_year1 + ' = ' + str_savings_year1); +outln(label_gen_year1 + ' = ' + str_gen_year1); outln(); // html output str_html = '' + -'
Solar for All Blended Rates
' + +'
Solar for All Metrics
' + '
RateValue
Blended buy rate' + sprintf('$%.3f/kWh load (year 1)',blended_buy.value) + '
' + '' + -'' + -'' + -'' + +'' + +'' + +'' + +'' + '
RateValue
Blended buy rate' + sprintf('$%.3f/kWh load (year 1)',blended_buy.value) + '
Blended sell rate' + sprintf('$%.3f/kWh generated (year 1)',blended_sell.value) + '
Bill savings' + sprintf('$%.1f%% (year 1)',savings_percent.value) + '
' + label_abuc + '' + str_abuc + '
'+ label_abuc_rate + '' + str_abuc_rate + '
'+ label_sgcr + '' + str_sgcr + '
' + label_bill_savings + '' + str_bill_savings + '
' + ''; -html_dialog( str_html, 'Solar for All Calculator Results', [200,200,600,200] ); +html_dialog( str_html, 'Solar for All Calculator Results', [200,200,600,225] ); // csv output str_csv = 'variable,value,units\n' + -'blended buy rate' + ',' + to_string(blended_buy.value) + ',' + '$/kWh of load in year 1' + '\n' + -'blended sell rate' +',' + to_string(blended_sell.value) + ',' + '$/kWh of generation in year 1' + '\n' + -'bill savings' + ',' + to_string(savings_percent.value) + ',' + '% of annual bill in year 1'; +label_abuc + ',' + to_string(ABUC) + ',' + '$/kWh of electricity consumed in year 1' + '\n' + +label_abuc_rate + ',' + to_string(abuc_rate) + ',' + '$/kWh of electricity consumed in year 1' + '\n' + +label_sgcr +',' + to_string(SGCR) + ',' + '$/kWh of electricity generated in year 1' + '\n' + +label_bill_savings + ',' + to_string(bill_savings) + ',' + '% of annual bill in year 1'; + +outln(str_csv); // if running from .sam file that has not been saved, choose folder to save .csv if ( project_file() == '' ) { From 9b5bd1c0f9a1f08ebe2d13d6febe521791fd4b26 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Thu, 31 Jul 2025 13:34:26 -0700 Subject: [PATCH 05/16] Incorporate SfA macro feedback --- .../Residential/Solar for All Calculator.lk | 269 ++++++++---------- 1 file changed, 123 insertions(+), 146 deletions(-) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index 267b3ddc78..07552c3ee2 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -1,92 +1,64 @@ /*@ -

The Solar for All Calculator macro calculates the Annual Baseline Utility Costs and Solar Generation Compensation Rate as described in the U.S. Environmental Protection Agency Solar for All Household Savings Best Practices handbook. You can use these values to calculate the Household Savings Percent Savings Provided.

-

For more detailed information and instructions, see [link to webpage].

-

- THIS IS A DRAFT VERSION FOR DEMONSTRATION PURPOSES ONLY -

-
Overall Steps
-
    -
  1. - Specify inputs on the following input pages. Inputs on other pages can be ignored. -
      -
    • Location and Resource
    • -
    • System Design
    • -
    • Electricity Rates
    • -
    • ELectric Load
    • -
    -
  2. -
  3. - Run the macro. -
  4. -
  5. - Analyze the results. -
  6. -
  7. - Modify the inputs and repeat Steps 2 and 3 until you are satisfied with the results. -
  8. -
-
Required Information
-
    -
  • - Location of the PV system as a street address or latitude/longitude pair -
  • -
  • - Size of the PV system -
  • -
  • - Orientation (tilt and azimuth angles) of PV modules (panels) -
  • -
  • - Retail electricity rate structure from the electricity service provider* -
  • -
  • - Time series (hourly or subhourly) electricity usage data or monthly total usage data* -
  • -
-

*SAM provides tools for downloading electricity rate and synthetic time series usage data if you don't have access to the data.

-
Optional Information
-

If you are running SAM simulations to calculate metrics like the net present value (NPV) or payback period, you will need to provide the following information in addition to the required information listed above. The following optional information is not used by the Solar for All macro.

-
    -
  • - Project installation and operating costs. -
  • -
  • - Financial parameters (taxes, debt, incentives) -
  • -
-
Instructions
-

These are the basic steps to use the Solar for All macro. For more detailed instructions and a demonstration video, see [link to webpage].

-
    -
  1. - On the Location and Resource input page, download a default TMY file by typing a street address or location name in the box under "Download Weather Files" and click Download and add to library. -
  2. -
  3. - On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type. -
  4. -
  5. - On the Electricity Rates page, click Search for rates and download the appropriate rate structure. -
  6. -
  7. - On the Electric Load page either a) import time series usage data or b) download synthetic data and scale monthly totals to match your monthly usage data. -
      -
    • - To upload time series usage data from a file, click Edit array, then click Import to import time series usage data from a CSV or text file. -
    • -
    • - To download synthetic usage data from the OpenEI "Commercial and Residential Hourly Load Profiles for all TMY3 Locations in the United States" dataset, click Download Electric Load Data, then check Scale electric load profile to monthly usage to activate the Edit Values button and click Edit values and type monthly usage data. -
    • -
    -
  8. -
  9. - On the File menu, click Save to save your data to a .sam file. -
  10. -
  11. - Click Run at the top of this window to run the macro. When the macro finishes, it will display open a new window displaying blended rate and bill savings percentage results and export the results to a CSV files with the name "[case name]-solar-for-all-calculator-results.csv" in the same folder as your .sam file. -
  12. -
  13. - Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results. -
  14. -
+

The Solar for All Calculator macro calculates the Blended Baseline Electric Utility Rate and Solar Generation Compensation Rate described in the U.S. Environmental Protection Agency Solar for All Household Savings Best Practices handbook.

+

For detailed instructions and how-to videos, see [link to Box folder].

+

THIS IS A DRAFT VERSION FOR DEMONSTRATION PURPOSES ONLY

+ +
Overall Steps
+ +
    +
  1. Specify inputs on the following input pages. Inputs on other pages can be ignored. +
      +
    • Location and Resource
    • +
    • System Design
    • +
    • Electricity Rates
    • +
    • Electric Load
    • +
    +
  2. +
  3. Run the macro.
  4. +
  5. Analyze the results.
  6. +
  7. Modify the inputs and rerun the macro until you are satisfied with the results.
  8. +
+ +
Required Information
+ +
    +
  • Location of the PV system as a street address or latitude/longitude pair
  • +
  • Size of the PV system in DC kilowatts (kW)
  • +
  • Orientation (tilt and azimuth angles) of PV modules (panels)
  • +
  • Retail electricity rate structure from the electricity service provider*
  • +
  • Time series (hourly or subhourly) electricity consumption data and/or monthly total usage data*
  • +
+ +

*SAM provides tools for downloading electricity rate and synthetic time series usage data if you don't have access to the data.

+ +
Optional Information
+ +

If you are running SAM simulations to calculate metrics like the net present value (NPV) or payback period, you will need to provide the following information in addition to the required information listed above. The following optional information is not used by the Solar for All macro.

+ +
    +
  • Project installation and operating costs.
  • +
  • Financial parameters (taxes, debt, incentives)
  • +
+ +
Instructions
+ +

These are the basic steps to use the Solar for All macro. For detailed instructions and how-to videos, see [link to Box folder].

+
    +
  1. On the Location and Resource input page, download a default TMY file by typing a street address or location name under "Download Weather Files" and clicking Download and add to library.
  2. +
  3. On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type.
  4. +
  5. On the Electricity Rates page, click Search for rates and download the appropriate rate structure.
  6. +
  7. On the Electric Load page either a) import time series electricity consumption data or b) download synthetic data for Year 1. You can optionally scale the data to match monthly usage data. +
      +
    • To import time series data from a CSV or text file, click Edit array, then click Import. The file must contain one year's worth of data in a single column of kW values with one header row.
    • +
    • To download synthetic usage data from the OpenEI "Commercial and Residential Hourly Load Profiles for all TMY3 Locations in the United States" dataset, click Download Electric Load Data.
    • +
    • To scale the time series data to monthly usage data, check Scale electric load profile to monthly usage, click Edit values, and type monthly usage values in the table.
    • +
    +
  8. +
  9. On the File menu, click Save to save your data to a .sam file.
  10. +
  11. Click Run at the top of this window to run the macro. When the macro finishes, it opens a window displaying results and exports them to a CSV file with the name "[case name]-solar-for-all-macro-results.csv" in the same folder as your .sam file.
  12. +
  13. Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results.
  14. +
+ @*/ // check for valid configuration only PVWatts/Residential is supported @@ -121,93 +93,98 @@ if (!ok) { outln('done.'); outln(); -// Annual Baseline Utility Cost ABUC -// = bill without system (year 1) -ABUC = get('utility_bill_wo_sys_year1'); // $/yr +// All values are for year 1 only + +// Baseline electric utility cost +baseline_cost = get('utility_bill_wo_sys_year1'); // $ -// ABUC rate -abuc_rate = ABUC / get('year1_electric_load'); // $/kWh year 1 +// Baseline electric utility consumption +baseline_consumption = get('year1_electric_load'); // kWh -// Solar Generation Compensation Rate SGCR -A = get('savings_year1'); // $ -B = get('annual_energy'); // kWh year 1 -if ( B == 0 ) { - outln('Annual energy is zero. Cannot calculate SGCR due to divide by zero error. Exiting script.'); +// avoid divide by zero +if ( baseline_consumption == 0 ) { + msgbox('Divide by Zero Error!\nBaseline electric utility consumption is zero, cannot calculate blended baseline utility rate.'); + outln('Divide by zero error. Exiting script.'); exit; } -SGCR = A/B; // $/kWh generation year 1 -// Bill savings -A = get('utility_bill_wo_sys_year1'); // $ -B = get('utility_bill_w_sys_year1'); // $ -if ( A == 0 ) { - outln('Utility bill with system for year 1 is zero. Cannot calculate savings due to divide by zero error.'); -} -else -{ - bill_savings = ( A - B ) / A * 100; // % +// Blended baseline electric utility rate +blended_rate = baseline_cost / baseline_consumption; // $/kWh + +// Electricity bill savings +bill_savings = get('savings_year1'); // $ + +// Household allocated solar generation +solar_generation = get('annual_energy'); // kWh + +// avoid divide by zero +if ( solar_generation == 0 ) { + msgbox('Divide by Zero Error!\nHousehold allocated solar generation is zero, cannot calculate solar generation compensation rate.'); + outln('Divide by zero error. Exiting script.'); + exit; } -// console output +// Solar generation compensation rate +compensation_rate = bill_savings / solar_generation; // $/kWh -label_abuc = 'Annual Baseline Utility Cost (ABUC)'; -label_load_year1 = 'Household consumption'; -label_abuc_rate = 'ABUC rate'; -label_sgcr = 'Solar Generation Compensation Rate (SGCR)'; -label_savings_year1 = 'Electricity bill savings amount'; -label_gen_year1 = 'Electricity generation'; - -str_abuc = sprintf('$%.0f (year 1)', ABUC); -str_load_year1 = sprintf('%.0f kWh consumed (year 1)', get('year1_electric_load')); -str_abuc_rate = sprintf('$%.3f/kWh consumed (year 1)', abuc_rate); -str_sgcr = sprintf('$%.3f/kWh generated (year 1)', SGCR); -str_savings_year1 = sprintf('%.0f kWh consumed (year 1)', A); -str_gen_year1 = sprintf('%.0f kWh generated (year 1)', B); - -outln(label_abuc + ' = ' + str_abuc); -outln(label_load_year1 + ' = ' + str_load_year1); -outln(label_abuc_rate + ' = ' + str_abuc_rate); -outln(label_sgcr + ' = ' + str_sgcr); -outln(label_savings_year1 + ' = ' + str_savings_year1); -outln(label_gen_year1 + ' = ' + str_gen_year1); +// output labels +label_blended_rate = 'Blended Baseline Electric Utility Rate'; +label_baseline_consumption = 'Baseline Electric Utility Consumption'; +label_baseline_cost = 'Baseline Electric Utility Cost'; +label_solar_generation = 'Household Allocated Solar Generation'; +label_compensation_rate = 'Solar Generation Compensation Rate'; + +// output values as string with units +str_blended_rate = sprintf('$%.3f/kWh consumed (year 1)', blended_rate); +str_baseline_consumption = sprintf('%, kWh consumed (year 1)', round(baseline_consumption)); +str_baseline_cost = sprintf('$%m (year 1)', baseline_cost); +str_solar_generation = sprintf('%, kWh generated (year 1)', round(solar_generation)); +str_compensation_rate = sprintf('$%.3f/kWh generated (year 1)', compensation_rate); + +// console output +outln(label_blended_rate + ' = ' + str_blended_rate); +outln(label_baseline_consumption + ' = ' + str_baseline_consumption); +outln(label_baseline_cost + ' = ' + str_baseline_cost); +outln(label_solar_generation + ' = ' + str_solar_generation); +outln(label_compensation_rate + ' = ' + str_compensation_rate); outln(); -// html output +// html output str_html = '' + -'
Solar for All Metrics
' + +'
Solar for All Calculator Macro Results
' + '' + '' + -'' + -'' + -'' + -'' + +'' + +'' + +'' + +'' + +'' + '
RateValue
' + label_abuc + '' + str_abuc + '
'+ label_abuc_rate + '' + str_abuc_rate + '
'+ label_sgcr + '' + str_sgcr + '
' + label_bill_savings + '' + str_bill_savings + '
' + label_blended_rate + '' + str_blended_rate + '
'+ label_baseline_consumption + '' + str_baseline_consumption + '
'+ label_baseline_cost + '' + str_baseline_cost + '
' + label_solar_generation + '' + str_solar_generation + '
' + label_compensation_rate + '' + str_compensation_rate + '
' + ''; -html_dialog( str_html, 'Solar for All Calculator Results', [200,200,600,225] ); +html_dialog( str_html, 'Solar for All Calculator Macro Results', [200,200,600,240] ); // csv output - str_csv = -'variable,value,units\n' + -label_abuc + ',' + to_string(ABUC) + ',' + '$/kWh of electricity consumed in year 1' + '\n' + -label_abuc_rate + ',' + to_string(abuc_rate) + ',' + '$/kWh of electricity consumed in year 1' + '\n' + -label_sgcr +',' + to_string(SGCR) + ',' + '$/kWh of electricity generated in year 1' + '\n' + -label_bill_savings + ',' + to_string(bill_savings) + ',' + '% of annual bill in year 1'; - -outln(str_csv); +'Variable,Value,Units\n' + +label_blended_rate + ',' + to_string(blended_rate) + ',' + '$/kWh of electricity consumed (year 1)' + '\n' + +label_baseline_consumption + ',' + to_string(baseline_consumption) + ',' + 'kWh of electricity consumed (year 1)' + '\n' + +label_baseline_cost +',' + to_string(baseline_cost) + ',' + '$ (year 1)' + '\n' + +label_solar_generation + ',' + to_string(solar_generation) + ',' + 'kWh of electricity generated (year 1)' + '\n' + +label_compensation_rate + ',' + to_string(compensation_rate) + ',' + '$/kWh of electricity generated (year 1)'; // if running from .sam file that has not been saved, choose folder to save .csv if ( project_file() == '' ) { dir = choose_dir(homedir(),'Choose folder to save CSV results'); } else { - dir = cwd(); + dir = path_only(project_file()); } -f_csv = dir + '/' + replace(case_name(),'/','-') + '-solar-for-all-calculator-results.csv'; +// csv file name based on case name +f_csv = dir + '/' + replace(case_name(),'/','-') + '-solar-for-all-macro-results.csv'; ok = write_text_file(f_csv, str_csv); if (!ok) { From b0b599dff2a94aaecb4fe1443f93b30259088e46 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Tue, 12 Aug 2025 08:24:25 -0700 Subject: [PATCH 06/16] Initial implementation of highlight and notes options --- .../Residential/Solar for All Calculator.lk | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index 07552c3ee2..eca3426e24 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -61,6 +61,118 @@ @*/ +//@ name=generate_pagenotes;type=checkbox;label=Create page notes;value=0*/ +//@ name=highlight_inputs;type=checkbox;label=Highlight inputs;value=0*/ + +function highlight( widget_name ) { + g = widgetpos(widget_name); + transp(g, highlight_color, highlight_transparency); + g[1] = g[1] + 30; + msgbox('Click OK to continue.',g); +} + +function highlight_inputs() { + + generate_pagenotes(); + + highlight_color = 'yellow'; + highlight_transparency = 70; + transp(); + + ok = show_page('Location and Resource'); + if (ok) { + highlight('in_location'); + highlight('btn_download'); + } + else { + msgbox('Input page error!\nThis case does not have a Location and Resource page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); + } + ok = show_page('System Design'); + if (ok) { + highlight('system_capacity'); + highlight('tilt'); + highlight('azimuth'); + highlight('array_type'); + } + else { + msgbox('Input page error!\nThis case does not have a System Design page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); + } + ok = show_page('Electricity Rates'); + if (ok) { + highlight('btnQueryOpenEI'); + } + else { + msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); + } + ok = show_page('Electric Load'); + if (ok) { + highlight('load_user_data'); + highlight('normalize_to_utility_bill'); + highlight('utility_bill_data'); + } + else { + msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); + } + transp(); // clear highight +} + +function generate_pagenotes() { + ok = show_page('Location and Resource'); + if (ok) { + pagenote('## Solar for All Macro Instructions ##\n\n' + + 'Location and Resource Page\n\n' + + '1. Type the project street address or city and state in the box under Download Weather Files.\n' + + '2. Click Download and Add to Library.\n' + + '3. When the download finishes, review the data under Weather Data Information to verify the data.\n' + + '4. Go to the System Design page.'); + + } + else { + msgbox('Input page error!\nThis case does not have a Location and Resource page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); + } + ok = show_page('System Design'); + if (ok) { + pagenote('## Solar for All Macro Instructions ##\n\n' + + 'System Design Page\n\n' + + '1. For System Nameplate Capacity, type the system size in DC kilowatts.\n' + + '2. For Tilt, type the photovoltaic array tilt angle in degrees from horizontal (20 degrees is a typical value).\n' + + '3. For Azimuth, type the photovoltaic array azimuth angle in degrees East of North (180 degrees, or facing south, is a typical value).\n' + + '4. If you are modeling a ground-mounted or roof-mounted array that allows air to flow beneath the array, for Array Type, choose Fixed Open Rack.\n' + + '5. Go to the Electricity Rates page.'); + } + else { + msgbox('Input page error!\nThis case does not have a System Design page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); + } + ok = show_page('Electricity Rates'); + if (ok) { + pagenote('## Solar for All Macro Instructions ##\n\n' + + 'Electricity Rates Page\n\n' + + '1. Click Search for Rates.\n' + + '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n' + + '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n' + + '4. Choose the appropriate rate from the list.\n' + + ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n' + + '5. Go to the Electric Load page.'); + } + else { + msgbox('Input page error!\nThis case does not have a Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); + } + ok = show_page('Electric Load'); + if (ok) { + pagenote('## Solar for All Macro Instructions ##\n\n' + + 'Electricity Load Page\n\n' + + '1. Click Search for Rates.\n' + + '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n' + + '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n' + + '4. Choose the appropriate rate from the list.\n' + + ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n' + + '5. Go to the Electric Load page.'); + } + else { + msgbox('Input page error!\nThis case does not have a Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); + } +} + // check for valid configuration only PVWatts/Residential is supported config = configuration(); @@ -79,6 +191,15 @@ if (!ok) { exit; } + +if ( macro.generate_pagenotes == true ) { + generate_pagenotes(); +} + +if ( macro.highlight_inputs == true ) { + highlight_inputs(); +} + // run simulation out('Running simulation...'); sim_msg = ''; From 598d7f0abc1cef7312e9acb28b2fd15a34d89645 Mon Sep 17 00:00:00 2001 From: sjanzou Date: Tue, 12 Aug 2025 09:27:03 -0600 Subject: [PATCH 07/16] Address show_page issue not updating navigation menu (#2097) --- ...voltaicWindBatteryHybridHostDeveloper.json | 1 + ...tovoltaicWindBatteryHybridSingleOwner.json | 1 + src/casewin.cpp | 30 +++++++++++++++---- src/script.cpp | 3 +- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridHostDeveloper.json b/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridHostDeveloper.json index fad5f0fa3e..e582153f6e 100644 --- a/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridHostDeveloper.json +++ b/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridHostDeveloper.json @@ -27,6 +27,7 @@ "calculate_rack_shading": 0, "dcoptimizer_loss": 0, "en_snow_model": 0, + "snow_array": [0.000000], "subarray1_dcwiring_loss": 2, "subarray1_diodeconn_loss": 0.5, "subarray1_electrical_mismatch": 0, diff --git a/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridSingleOwner.json b/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridSingleOwner.json index 96b873d041..796e6d6481 100644 --- a/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridSingleOwner.json +++ b/api/api_autogen/library/defaults/Pvsamv1_PhotovoltaicWindBatteryHybridSingleOwner.json @@ -27,6 +27,7 @@ "calculate_rack_shading": 0, "dcoptimizer_loss": 0, "en_snow_model": 0, + "snow_array": [0.000000], "subarray1_dcwiring_loss": 2, "subarray1_diodeconn_loss": 0.5, "subarray1_electrical_mismatch": 0, diff --git a/src/casewin.cpp b/src/casewin.cpp index 7ec9f91eb6..940d086d16 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -1063,13 +1063,29 @@ wxArrayString CaseWindow::GetInputPages() bool CaseWindow::SwitchToNavigationMenu(const wxString& name) { // iterate over menu tree items and match "name" or select first item (SAM issue 1618) - wxDataViewItem dvi;// = m_navigationMenu->GetNthChild(wxDataViewItem(0), 0); + wxDataViewItem dvi, dvi_child;// = m_navigationMenu->GetNthChild(wxDataViewItem(0), 0); bool found = false; + int count = m_navigationMenu->GetChildCount(wxDataViewItem(0)); for (int i = 0; i < count && !found; i++) { dvi = m_navigationMenu->GetNthChild(wxDataViewItem(0), i); - if (dvi.IsOk() && m_navigationMenu->GetItemText(dvi) == name) - found = true; + // should be a lambda function but not more than one deep... + if (m_navigationMenu->IsContainer(dvi)) { + int count_child = m_navigationMenu->GetChildCount(dvi); + for (int j = 0; j < count_child && !found; j++) { + dvi_child = m_navigationMenu->GetNthChild(dvi, j); + wxString stmp = m_navigationMenu->GetItemText(dvi_child); // for debugging + if (dvi_child.IsOk() && stmp == name) { + found = true; + dvi = dvi_child; + } + } + } + else { + wxString stmp = m_navigationMenu->GetItemText(dvi); + if (dvi.IsOk() && m_navigationMenu->GetItemText(dvi) == name) + found = true; + } } // first item if not found if (!found) @@ -1081,10 +1097,11 @@ bool CaseWindow::SwitchToNavigationMenu(const wxString& name) if (dvi.IsOk()) { m_navigationMenu->SetCurrentItem(dvi); m_currentSelection = (dvi); + m_left_panel->SetFocus(); SwitchToInputPage(m_navigationMenu->GetItemText(dvi)); + m_left_panel->Layout(); } - return true; } @@ -1119,6 +1136,7 @@ bool CaseWindow::SwitchToInputPage( const wxString &name ) int p = m_inputPageList->Find(name); m_inputPageList->Select( p ); m_inputPageList->Refresh(); + m_navigationMenu->Layout(); m_left_panel->Layout();// try to force onPaint call for the input page list return true; @@ -1157,7 +1175,9 @@ bool CaseWindow::SwitchToPage( const wxString &name ) else { m_pageFlipper->SetSelection( PG_INPUTS ); - return SwitchToInputPage( name ); +// return SwitchToInputPage(name); + SwitchToInputPage(name); + m_left_panel->Layout(); } return true; diff --git a/src/script.cpp b/src/script.cpp index 2faf6ed63a..5ac3e3ac90 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -585,7 +585,8 @@ void fcall_show_page(lk::invoke_t &cxt) wxString page_name = cxt.arg(0).as_string(); Case *active_case = CurrentCase(); if (CaseWindow *case_window = SamApp::Window()->GetCaseWindow(active_case)) - cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); +// cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); else cxt.error("no active case"); } From bb6b07695e049fcf9f9632b36839ab5f39d57be5 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Tue, 12 Aug 2025 14:32:59 -0700 Subject: [PATCH 08/16] Fix show_page to update both navigation menu and page --- src/script.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/script.cpp b/src/script.cpp index 5ac3e3ac90..596cc312dd 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -584,9 +584,10 @@ void fcall_show_page(lk::invoke_t &cxt) LK_DOC("show_page", "Show a specific page in the user interface for the active case", "( string:page name ):boolean"); wxString page_name = cxt.arg(0).as_string(); Case *active_case = CurrentCase(); - if (CaseWindow *case_window = SamApp::Window()->GetCaseWindow(active_case)) + if (CaseWindow* case_window = SamApp::Window()->GetCaseWindow(active_case)) { cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); -// cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + } else cxt.error("no active case"); } From f97094c467c9db8f7cf46048e007edfbc63719bf Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Tue, 12 Aug 2025 14:33:31 -0700 Subject: [PATCH 09/16] Add page notes and input highlighting options to SfA macro --- .../Residential/Solar for All Calculator.lk | 100 +++++++++++------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk index eca3426e24..9df324e296 100644 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ b/deploy/runtime/macros/Residential/Solar for All Calculator.lk @@ -3,6 +3,17 @@

For detailed instructions and how-to videos, see [link to Box folder].

THIS IS A DRAFT VERSION FOR DEMONSTRATION PURPOSES ONLY

+

+ + +
+If you are new to SAM, use the check boxes at right to help guide you through the macro: +
    +
  • Create page notes creates permanent instructions on each input page that has inputs required to run the macro.
  • +
  • Highight inputs walks you through the input pages showing you where the inputs are.
  • +
+

+
Overall Steps
    @@ -64,66 +75,84 @@ //@ name=generate_pagenotes;type=checkbox;label=Create page notes;value=0*/ //@ name=highlight_inputs;type=checkbox;label=Highlight inputs;value=0*/ -function highlight( widget_name ) { +function highlight( widget_name, message ) { g = widgetpos(widget_name); transp(g, highlight_color, highlight_transparency); - g[1] = g[1] + 30; - msgbox('Click OK to continue.',g); + g[1] = g[1] + g[3]+5; + g[2] = 400; + msgbox( message + '\nClick OK or press Enter to continue.',g); } function highlight_inputs() { - generate_pagenotes(); - highlight_color = 'yellow'; highlight_transparency = 70; transp(); + go = yesno( 'Highlight Inputs Instructions.\nIf you click Yes below, SAM will switch to each input page required to run the macro, highlight each minimum required input on the page, and show brief explanation of the input. It then runs and displays results based on the current inputs so you can see how the macro works.\n\nAfter the macro finishes, you can clear the Highlight Inputs check box and go back to those pages to enter the data for your project and rerun the macro.\n\nContinue?', [100,100,400,200]); + if (!go) { + outln('Exiting script'); + exit; + } + ok = show_page('Location and Resource'); if (ok) { - highlight('in_location'); - highlight('btn_download'); + highlight('in_location', 'Type the project street address or city and state.'); + highlight('btn_download', 'Click Download and Add to Library to download the weather file.'); } else { msgbox('Input page error!\nThis case does not have a Location and Resource page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); } ok = show_page('System Design'); if (ok) { - highlight('system_capacity'); - highlight('tilt'); - highlight('azimuth'); - highlight('array_type'); + highlight('system_capacity', 'Type the system size in DC kilowatts.'); + highlight('tilt', 'Type the array tilt angle from horizontal (20 is a typical value).'); + highlight('azimuth', 'Type the array azimuth angle in degrees east of north. (180 is south-facing).'); + highlight('array_type', 'For a ground- or roof-mounted array that allows air to flow beneath the array, choose Fixed Open Rack.'); } else { msgbox('Input page error!\nThis case does not have a System Design page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); } ok = show_page('Electricity Rates'); if (ok) { - highlight('btnQueryOpenEI'); + highlight('btnQueryOpenEI', 'Click Search for Rates to download electricity rate data for the project location. If data is not available, enter the rates for fixed, energy, and demand charges data by hand.'); + highlight('ur_metering_option', 'Choose the correct option for compensation of excess generation. See Help for a detailed description of each option.'); + highlight('ur_monthly_fixed_charge', 'Verify the fixed monthly charge value.'); + // energy charge table requires special treatment + g = widgetpos('ur_ec_tou_mat'); + g[2] = g[2]*0.66; // highlight width + g[3] = g[3]*0.33; // highlight height + transp(g, 'yellow', 70); + g[0] = g[0] + g[2]+5; // popup horizontal position (position to right of highlight) + g[1] = g[1] - g[3]*0.9; // popup vertical position (position above highlight to minimize risk of hiding popup) + g[2] = 400; // popup width + msgbox( 'Verify the rates for energy charges.\nIf your rate structure also includes demand charges, scroll to the bottom of the page and expand the Demand Charges panel to verify those values.' + '\n\nClick OK or press Enter to continue.',g); } else { msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); } ok = show_page('Electric Load'); if (ok) { - highlight('load_user_data'); - highlight('normalize_to_utility_bill'); - highlight('utility_bill_data'); + highlight('load_user_data', 'Click Edit Array to import hourly load data.'); + highlight('normalize_to_utility_bill', 'Check Scale Electric Load Profile to Monthly Usage if you have monthly total consumption data.'); + highlight('utility_bill_data', 'Click Edit Values to enter the total consumption in kWh for each month.'); } else { msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); } transp(); // clear highight + show_page('Macros'); } function generate_pagenotes() { + ok = show_page('Location and Resource'); if (ok) { pagenote('## Solar for All Macro Instructions ##\n\n' + 'Location and Resource Page\n\n' + - '1. Type the project street address or city and state in the box under Download Weather Files.\n' + - '2. Click Download and Add to Library.\n' + - '3. When the download finishes, review the data under Weather Data Information to verify the data.\n' + + '1. Type the project street address or city and state in the box under Download Weather Files.\n\n' + + '2. Click Download and Add to Library.\n\n' + + '3. When the download finishes, review the data under Weather Data Information to verify the data.\n\n' + '4. Go to the System Design page.'); } @@ -134,10 +163,10 @@ function generate_pagenotes() { if (ok) { pagenote('## Solar for All Macro Instructions ##\n\n' + 'System Design Page\n\n' + - '1. For System Nameplate Capacity, type the system size in DC kilowatts.\n' + - '2. For Tilt, type the photovoltaic array tilt angle in degrees from horizontal (20 degrees is a typical value).\n' + - '3. For Azimuth, type the photovoltaic array azimuth angle in degrees East of North (180 degrees, or facing south, is a typical value).\n' + - '4. If you are modeling a ground-mounted or roof-mounted array that allows air to flow beneath the array, for Array Type, choose Fixed Open Rack.\n' + + '1. For System Nameplate Capacity, type the system size in DC kilowatts.\n\n' + + '2. For Tilt, type the photovoltaic array tilt angle in degrees from horizontal (20 degrees is a typical value).\n\n' + + '3. For Azimuth, type the photovoltaic array azimuth angle in degrees East of North (180 degrees, or facing south, is a typical value).\n\n' + + '4. If you are modeling a ground-mounted or roof-mounted array that allows air to flow beneath the array, for Array Type, choose Fixed Open Rack.\n\n' + '5. Go to the Electricity Rates page.'); } else { @@ -147,11 +176,11 @@ function generate_pagenotes() { if (ok) { pagenote('## Solar for All Macro Instructions ##\n\n' + 'Electricity Rates Page\n\n' + - '1. Click Search for Rates.\n' + - '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n' + - '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n' + - '4. Choose the appropriate rate from the list.\n' + - ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n' + + '1. Click Search for Rates.\n\n' + + '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n\n' + + '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n\n' + + '4. Choose the appropriate rate from the list.\n\n' + + ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n\n' + '5. Go to the Electric Load page.'); } else { @@ -161,11 +190,11 @@ function generate_pagenotes() { if (ok) { pagenote('## Solar for All Macro Instructions ##\n\n' + 'Electricity Load Page\n\n' + - '1. Click Search for Rates.\n' + - '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n' + - '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n' + - '4. Choose the appropriate rate from the list.\n' + - ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n' + + '1. Click Search for Rates.\n\n' + + '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n\n' + + '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n\n' + + '4. Choose the appropriate rate from the list.\n\n' + + ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n\n' + '5. Go to the Electric Load page.'); } else { @@ -191,15 +220,14 @@ if (!ok) { exit; } +if ( macro.highlight_inputs == true ) { + highlight_inputs(); +} if ( macro.generate_pagenotes == true ) { generate_pagenotes(); } -if ( macro.highlight_inputs == true ) { - highlight_inputs(); -} - // run simulation out('Running simulation...'); sim_msg = ''; From 14e2fcb7295727645a369b73dd3a3eb8f53877bc Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 13 Aug 2025 14:25:09 -0700 Subject: [PATCH 10/16] Remove SfA macro --- .../Residential/Solar for All Calculator.lk | 349 ------------------ 1 file changed, 349 deletions(-) delete mode 100644 deploy/runtime/macros/Residential/Solar for All Calculator.lk diff --git a/deploy/runtime/macros/Residential/Solar for All Calculator.lk b/deploy/runtime/macros/Residential/Solar for All Calculator.lk deleted file mode 100644 index 9df324e296..0000000000 --- a/deploy/runtime/macros/Residential/Solar for All Calculator.lk +++ /dev/null @@ -1,349 +0,0 @@ -/*@ -

    The Solar for All Calculator macro calculates the Blended Baseline Electric Utility Rate and Solar Generation Compensation Rate described in the U.S. Environmental Protection Agency Solar for All Household Savings Best Practices handbook.

    -

    For detailed instructions and how-to videos, see [link to Box folder].

    -

    THIS IS A DRAFT VERSION FOR DEMONSTRATION PURPOSES ONLY

    - -

    - - -
    -If you are new to SAM, use the check boxes at right to help guide you through the macro: -
      -
    • Create page notes creates permanent instructions on each input page that has inputs required to run the macro.
    • -
    • Highight inputs walks you through the input pages showing you where the inputs are.
    • -
    -

    - -
    Overall Steps
    - -
      -
    1. Specify inputs on the following input pages. Inputs on other pages can be ignored. -
        -
      • Location and Resource
      • -
      • System Design
      • -
      • Electricity Rates
      • -
      • Electric Load
      • -
      -
    2. -
    3. Run the macro.
    4. -
    5. Analyze the results.
    6. -
    7. Modify the inputs and rerun the macro until you are satisfied with the results.
    8. -
    - -
    Required Information
    - -
      -
    • Location of the PV system as a street address or latitude/longitude pair
    • -
    • Size of the PV system in DC kilowatts (kW)
    • -
    • Orientation (tilt and azimuth angles) of PV modules (panels)
    • -
    • Retail electricity rate structure from the electricity service provider*
    • -
    • Time series (hourly or subhourly) electricity consumption data and/or monthly total usage data*
    • -
    - -

    *SAM provides tools for downloading electricity rate and synthetic time series usage data if you don't have access to the data.

    - -
    Optional Information
    - -

    If you are running SAM simulations to calculate metrics like the net present value (NPV) or payback period, you will need to provide the following information in addition to the required information listed above. The following optional information is not used by the Solar for All macro.

    - -
      -
    • Project installation and operating costs.
    • -
    • Financial parameters (taxes, debt, incentives)
    • -
    - -
    Instructions
    - -

    These are the basic steps to use the Solar for All macro. For detailed instructions and how-to videos, see [link to Box folder].

    -
      -
    1. On the Location and Resource input page, download a default TMY file by typing a street address or location name under "Download Weather Files" and clicking Download and add to library.
    2. -
    3. On the System Design page, enter values for System nameplate capacity, Tilt, and Azimuth. If you are modeling a ground-mounted PV array or a roof-mounted array that allows air to flow beneath the modules System Design, choose "Fixed open rack" for Array type.
    4. -
    5. On the Electricity Rates page, click Search for rates and download the appropriate rate structure.
    6. -
    7. On the Electric Load page either a) import time series electricity consumption data or b) download synthetic data for Year 1. You can optionally scale the data to match monthly usage data. -
        -
      • To import time series data from a CSV or text file, click Edit array, then click Import. The file must contain one year's worth of data in a single column of kW values with one header row.
      • -
      • To download synthetic usage data from the OpenEI "Commercial and Residential Hourly Load Profiles for all TMY3 Locations in the United States" dataset, click Download Electric Load Data.
      • -
      • To scale the time series data to monthly usage data, check Scale electric load profile to monthly usage, click Edit values, and type monthly usage values in the table.
      • -
      -
    8. -
    9. On the File menu, click Save to save your data to a .sam file.
    10. -
    11. Click Run at the top of this window to run the macro. When the macro finishes, it opens a window displaying results and exports them to a CSV file with the name "[case name]-solar-for-all-macro-results.csv" in the same folder as your .sam file.
    12. -
    13. Review the blended rate and savings percentage values calculated by the macro, revise inputs, and rerun the macro until you are satisfied with the results.
    14. -
    - -@*/ - -//@ name=generate_pagenotes;type=checkbox;label=Create page notes;value=0*/ -//@ name=highlight_inputs;type=checkbox;label=Highlight inputs;value=0*/ - -function highlight( widget_name, message ) { - g = widgetpos(widget_name); - transp(g, highlight_color, highlight_transparency); - g[1] = g[1] + g[3]+5; - g[2] = 400; - msgbox( message + '\nClick OK or press Enter to continue.',g); -} - -function highlight_inputs() { - - highlight_color = 'yellow'; - highlight_transparency = 70; - transp(); - - go = yesno( 'Highlight Inputs Instructions.\nIf you click Yes below, SAM will switch to each input page required to run the macro, highlight each minimum required input on the page, and show brief explanation of the input. It then runs and displays results based on the current inputs so you can see how the macro works.\n\nAfter the macro finishes, you can clear the Highlight Inputs check box and go back to those pages to enter the data for your project and rerun the macro.\n\nContinue?', [100,100,400,200]); - if (!go) { - outln('Exiting script'); - exit; - } - - ok = show_page('Location and Resource'); - if (ok) { - highlight('in_location', 'Type the project street address or city and state.'); - highlight('btn_download', 'Click Download and Add to Library to download the weather file.'); - } - else { - msgbox('Input page error!\nThis case does not have a Location and Resource page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); - } - ok = show_page('System Design'); - if (ok) { - highlight('system_capacity', 'Type the system size in DC kilowatts.'); - highlight('tilt', 'Type the array tilt angle from horizontal (20 is a typical value).'); - highlight('azimuth', 'Type the array azimuth angle in degrees east of north. (180 is south-facing).'); - highlight('array_type', 'For a ground- or roof-mounted array that allows air to flow beneath the array, choose Fixed Open Rack.'); - } - else { - msgbox('Input page error!\nThis case does not have a System Design page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); - } - ok = show_page('Electricity Rates'); - if (ok) { - highlight('btnQueryOpenEI', 'Click Search for Rates to download electricity rate data for the project location. If data is not available, enter the rates for fixed, energy, and demand charges data by hand.'); - highlight('ur_metering_option', 'Choose the correct option for compensation of excess generation. See Help for a detailed description of each option.'); - highlight('ur_monthly_fixed_charge', 'Verify the fixed monthly charge value.'); - // energy charge table requires special treatment - g = widgetpos('ur_ec_tou_mat'); - g[2] = g[2]*0.66; // highlight width - g[3] = g[3]*0.33; // highlight height - transp(g, 'yellow', 70); - g[0] = g[0] + g[2]+5; // popup horizontal position (position to right of highlight) - g[1] = g[1] - g[3]*0.9; // popup vertical position (position above highlight to minimize risk of hiding popup) - g[2] = 400; // popup width - msgbox( 'Verify the rates for energy charges.\nIf your rate structure also includes demand charges, scroll to the bottom of the page and expand the Demand Charges panel to verify those values.' + '\n\nClick OK or press Enter to continue.',g); - } - else { - msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); - } - ok = show_page('Electric Load'); - if (ok) { - highlight('load_user_data', 'Click Edit Array to import hourly load data.'); - highlight('normalize_to_utility_bill', 'Check Scale Electric Load Profile to Monthly Usage if you have monthly total consumption data.'); - highlight('utility_bill_data', 'Click Edit Values to enter the total consumption in kWh for each month.'); - } - else { - msgbox('Input page error!\nThis case does not have an Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts/Residential configuration.'); - } - transp(); // clear highight - show_page('Macros'); -} - -function generate_pagenotes() { - - ok = show_page('Location and Resource'); - if (ok) { - pagenote('## Solar for All Macro Instructions ##\n\n' + - 'Location and Resource Page\n\n' + - '1. Type the project street address or city and state in the box under Download Weather Files.\n\n' + - '2. Click Download and Add to Library.\n\n' + - '3. When the download finishes, review the data under Weather Data Information to verify the data.\n\n' + - '4. Go to the System Design page.'); - - } - else { - msgbox('Input page error!\nThis case does not have a Location and Resource page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); - } - ok = show_page('System Design'); - if (ok) { - pagenote('## Solar for All Macro Instructions ##\n\n' + - 'System Design Page\n\n' + - '1. For System Nameplate Capacity, type the system size in DC kilowatts.\n\n' + - '2. For Tilt, type the photovoltaic array tilt angle in degrees from horizontal (20 degrees is a typical value).\n\n' + - '3. For Azimuth, type the photovoltaic array azimuth angle in degrees East of North (180 degrees, or facing south, is a typical value).\n\n' + - '4. If you are modeling a ground-mounted or roof-mounted array that allows air to flow beneath the array, for Array Type, choose Fixed Open Rack.\n\n' + - '5. Go to the Electricity Rates page.'); - } - else { - msgbox('Input page error!\nThis case does not have a System Design page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); - } - ok = show_page('Electricity Rates'); - if (ok) { - pagenote('## Solar for All Macro Instructions ##\n\n' + - 'Electricity Rates Page\n\n' + - '1. Click Search for Rates.\n\n' + - '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n\n' + - '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n\n' + - '4. Choose the appropriate rate from the list.\n\n' + - ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n\n' + - '5. Go to the Electric Load page.'); - } - else { - msgbox('Input page error!\nThis case does not have a Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); - } - ok = show_page('Electric Load'); - if (ok) { - pagenote('## Solar for All Macro Instructions ##\n\n' + - 'Electricity Load Page\n\n' + - '1. Click Search for Rates.\n\n' + - '2. Type the project zip code and click Search by Zip Code, or if you know the name of the electric service provider, type a few letters of the provider name to filter the list.\n\n' + - '3. Click the name of the electric service provider in the list, you should see a list of residential rates appear.\n\n' + - '4. Choose the appropriate rate from the list.\n\n' + - ' If the service provider or rate is not available, you will need to enter rate data manually from the appropriate rate sheet.\n\n' + - '5. Go to the Electric Load page.'); - } - else { - msgbox('Input page error!\nThis case does not have a Electricity Rates page. The Solar for All Calculator macro is designed to work with the PVWatts model that has a Location and Resource page.'); - } -} - -// check for valid configuration only PVWatts/Residential is supported -config = configuration(); - -fin = config[1]; - -ok = false; -if ( fin == 'Commercial' || fin == 'Residential' ) { - ok = true; -} - -if (!ok) { - msgbox('Note!\n' + - 'This macro requires a case with the Residential or Commercial financial model.\n' + - 'The financial model for this case is: ' + fin + '\n' + - 'Exiting macro.'); - exit; -} - -if ( macro.highlight_inputs == true ) { - highlight_inputs(); -} - -if ( macro.generate_pagenotes == true ) { - generate_pagenotes(); -} - -// run simulation -out('Running simulation...'); -sim_msg = ''; -ok = simulate(sim_msg, true); -if (!ok) { - msgbox('Simulation failed!\n' + sim_msg); - outln('Simulation failed!'); - outln('Simulation message: ' + sim_msg); - outln('Exiting script.'); - exit; -} -outln('done.'); -outln(); - -// All values are for year 1 only - -// Baseline electric utility cost -baseline_cost = get('utility_bill_wo_sys_year1'); // $ - -// Baseline electric utility consumption -baseline_consumption = get('year1_electric_load'); // kWh - -// avoid divide by zero -if ( baseline_consumption == 0 ) { - msgbox('Divide by Zero Error!\nBaseline electric utility consumption is zero, cannot calculate blended baseline utility rate.'); - outln('Divide by zero error. Exiting script.'); - exit; -} - -// Blended baseline electric utility rate -blended_rate = baseline_cost / baseline_consumption; // $/kWh - -// Electricity bill savings -bill_savings = get('savings_year1'); // $ - -// Household allocated solar generation -solar_generation = get('annual_energy'); // kWh - -// avoid divide by zero -if ( solar_generation == 0 ) { - msgbox('Divide by Zero Error!\nHousehold allocated solar generation is zero, cannot calculate solar generation compensation rate.'); - outln('Divide by zero error. Exiting script.'); - exit; -} - -// Solar generation compensation rate -compensation_rate = bill_savings / solar_generation; // $/kWh - -// output labels -label_blended_rate = 'Blended Baseline Electric Utility Rate'; -label_baseline_consumption = 'Baseline Electric Utility Consumption'; -label_baseline_cost = 'Baseline Electric Utility Cost'; -label_solar_generation = 'Household Allocated Solar Generation'; -label_compensation_rate = 'Solar Generation Compensation Rate'; - -// output values as string with units -str_blended_rate = sprintf('$%.3f/kWh consumed (year 1)', blended_rate); -str_baseline_consumption = sprintf('%, kWh consumed (year 1)', round(baseline_consumption)); -str_baseline_cost = sprintf('$%m (year 1)', baseline_cost); -str_solar_generation = sprintf('%, kWh generated (year 1)', round(solar_generation)); -str_compensation_rate = sprintf('$%.3f/kWh generated (year 1)', compensation_rate); - -// console output -outln(label_blended_rate + ' = ' + str_blended_rate); -outln(label_baseline_consumption + ' = ' + str_baseline_consumption); -outln(label_baseline_cost + ' = ' + str_baseline_cost); -outln(label_solar_generation + ' = ' + str_solar_generation); -outln(label_compensation_rate + ' = ' + str_compensation_rate); -outln(); - - -// html output -str_html = -'' + -'
    Solar for All Calculator Macro Results
    ' + -'' + -'' + -'' + -'' + -'' + -'' + -'' + -'
    RateValue
    ' + label_blended_rate + '' + str_blended_rate + '
    '+ label_baseline_consumption + '' + str_baseline_consumption + '
    '+ label_baseline_cost + '' + str_baseline_cost + '
    ' + label_solar_generation + '' + str_solar_generation + '
    ' + label_compensation_rate + '' + str_compensation_rate + '
    ' + -''; - -html_dialog( str_html, 'Solar for All Calculator Macro Results', [200,200,600,240] ); - -// csv output -str_csv = -'Variable,Value,Units\n' + -label_blended_rate + ',' + to_string(blended_rate) + ',' + '$/kWh of electricity consumed (year 1)' + '\n' + -label_baseline_consumption + ',' + to_string(baseline_consumption) + ',' + 'kWh of electricity consumed (year 1)' + '\n' + -label_baseline_cost +',' + to_string(baseline_cost) + ',' + '$ (year 1)' + '\n' + -label_solar_generation + ',' + to_string(solar_generation) + ',' + 'kWh of electricity generated (year 1)' + '\n' + -label_compensation_rate + ',' + to_string(compensation_rate) + ',' + '$/kWh of electricity generated (year 1)'; - -// if running from .sam file that has not been saved, choose folder to save .csv -if ( project_file() == '' ) { - dir = choose_dir(homedir(),'Choose folder to save CSV results'); -} -else { - dir = path_only(project_file()); -} - -// csv file name based on case name -f_csv = dir + '/' + replace(case_name(),'/','-') + '-solar-for-all-macro-results.csv'; - -ok = write_text_file(f_csv, str_csv); -if (!ok) { - outln('Failed to write results to CSV file: ' + f_csv + '\n The file may be open in another program.'); - outln(); -} -else { - browse( f_csv ); -} - -// finish - -outln('Macro done.'); From 42651b6a839a5534aefbda5580b5f590bd8efdc7 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 13 Aug 2025 14:49:06 -0700 Subject: [PATCH 11/16] Commented line fixes navigation menu sync issue when running LK `show_page` from macro. But `SwitchToPage` should not need to be called here. --- src/script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.cpp b/src/script.cpp index 596cc312dd..8b6b55bb84 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -586,7 +586,7 @@ void fcall_show_page(lk::invoke_t &cxt) Case *active_case = CurrentCase(); if (CaseWindow* case_window = SamApp::Window()->GetCaseWindow(active_case)) { cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); - cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + //cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); } else cxt.error("no active case"); } From 46b9061e459144cb3bb207a16b5cfa79d840ae92 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Wed, 13 Aug 2025 14:49:22 -0700 Subject: [PATCH 12/16] Test macro for navigation menu sync issue --- deploy/runtime/macros/show page test.lk | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 deploy/runtime/macros/show page test.lk diff --git a/deploy/runtime/macros/show page test.lk b/deploy/runtime/macros/show page test.lk new file mode 100644 index 0000000000..fc2a50180d --- /dev/null +++ b/deploy/runtime/macros/show page test.lk @@ -0,0 +1,9 @@ +/*@ +For testing only. Delete before merging into Patch or Develop. +@*/ +show_page('System Design'); +msgbox('System Design page should be visible.'); +show_page('Location and Resource'); +g = widgetpos('in_location'); +transp(g,'yellow',75); +msgbox('Location and Resource page should be visible.'); \ No newline at end of file From d214fa30d8c23f117bc749606cab2ac9c24ce629 Mon Sep 17 00:00:00 2001 From: Steven Janzou Date: Thu, 14 Aug 2025 02:20:54 -0600 Subject: [PATCH 13/16] Fix macro fcall_show_page updating of input page - testing variation --- src/script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.cpp b/src/script.cpp index 8b6b55bb84..596cc312dd 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -586,7 +586,7 @@ void fcall_show_page(lk::invoke_t &cxt) Case *active_case = CurrentCase(); if (CaseWindow* case_window = SamApp::Window()->GetCaseWindow(active_case)) { cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); - //cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); } else cxt.error("no active case"); } From e6967097b0d4b06bfc8c2baf58ecac4c5159861e Mon Sep 17 00:00:00 2001 From: Steven Janzou Date: Thu, 14 Aug 2025 02:41:19 -0600 Subject: [PATCH 14/16] Update SwitchToNavigationMenu to properly work with macros #2099 --- src/casewin.cpp | 3 ++- src/script.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/casewin.cpp b/src/casewin.cpp index 940d086d16..3e430b033f 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -1098,7 +1098,8 @@ bool CaseWindow::SwitchToNavigationMenu(const wxString& name) m_navigationMenu->SetCurrentItem(dvi); m_currentSelection = (dvi); m_left_panel->SetFocus(); - SwitchToInputPage(m_navigationMenu->GetItemText(dvi)); +// SwitchToInputPage(m_navigationMenu->GetItemText(dvi)); + SwitchToPage(m_navigationMenu->GetItemText(dvi)); m_left_panel->Layout(); } diff --git a/src/script.cpp b/src/script.cpp index 596cc312dd..1b9a6f8bb0 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -586,7 +586,7 @@ void fcall_show_page(lk::invoke_t &cxt) Case *active_case = CurrentCase(); if (CaseWindow* case_window = SamApp::Window()->GetCaseWindow(active_case)) { cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); - cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); + // cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); } else cxt.error("no active case"); } From 3578a20a90d54acdd1c85550f6633c8f3caf40f9 Mon Sep 17 00:00:00 2001 From: Steven Janzou Date: Thu, 14 Aug 2025 03:54:52 -0600 Subject: [PATCH 15/16] Address macro and create new case menu and page selection per #2099 --- ...csmoltenSalt_MSPTAllEquityPartnershipFlip.json | 1 + ...csmoltenSalt_MSPTLeveragedPartnershipFlip.json | 1 + .../defaults/TcsmoltenSalt_MSPTMerchantPlant.json | 1 + .../library/defaults/TcsmoltenSalt_MSPTNone.json | 1 + .../defaults/TcsmoltenSalt_MSPTSaleLeaseback.json | 1 + .../defaults/TcsmoltenSalt_MSPTSingleOwner.json | 1 + api/include/SAM_TcsmoltenSalt.h | 10 ++++++++++ api/modules/SAM_TcsmoltenSalt.cpp | 15 +++++++++++++++ src/casewin.cpp | 1 - src/main.cpp | 4 ++-- src/script.cpp | 1 - 11 files changed, 33 insertions(+), 4 deletions(-) diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTAllEquityPartnershipFlip.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTAllEquityPartnershipFlip.json index b574ce300c..78cc7e1153 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTAllEquityPartnershipFlip.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTAllEquityPartnershipFlip.json @@ -9813,6 +9813,7 @@ [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000] ], "dispatch_tod_factors": [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000], + "is_timestep_load_fractions": 0, "ppa_multiplier_model": 0 }, "FinancialSolutionMode": { diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTLeveragedPartnershipFlip.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTLeveragedPartnershipFlip.json index 324090428a..451eb42efc 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTLeveragedPartnershipFlip.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTLeveragedPartnershipFlip.json @@ -9813,6 +9813,7 @@ [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000] ], "dispatch_tod_factors": [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000], + "is_timestep_load_fractions": 0, "ppa_multiplier_model": 0 }, "FinancialSolutionMode": { diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTMerchantPlant.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTMerchantPlant.json index a829a5c6c5..4bf6340757 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTMerchantPlant.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTMerchantPlant.json @@ -9783,6 +9783,7 @@ "ud_m_dot_water_cool_des": 0 }, "TimeOfDeliveryFactors": { + "is_timestep_load_fractions": 0 }, "FinancialSolutionMode": { }, diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTNone.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTNone.json index 2d071e7488..cead0d937b 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTNone.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTNone.json @@ -9783,6 +9783,7 @@ "ud_m_dot_water_cool_des": 0 }, "TimeOfDeliveryFactors": { + "is_timestep_load_fractions": 0 }, "FinancialSolutionMode": { }, diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSaleLeaseback.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSaleLeaseback.json index 2045f9cf7f..2301e83c2e 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSaleLeaseback.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSaleLeaseback.json @@ -9813,6 +9813,7 @@ [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000] ], "dispatch_tod_factors": [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000], + "is_timestep_load_fractions": 0, "ppa_multiplier_model": 0 }, "FinancialSolutionMode": { diff --git a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSingleOwner.json b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSingleOwner.json index 1b3a6cefc7..f4a51a536b 100644 --- a/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSingleOwner.json +++ b/api/api_autogen/library/defaults/TcsmoltenSalt_MSPTSingleOwner.json @@ -9813,6 +9813,7 @@ [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000] ], "dispatch_tod_factors": [1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000], + "is_timestep_load_fractions": 0, "ppa_multiplier_model": 0 }, "FinancialSolutionMode": { diff --git a/api/include/SAM_TcsmoltenSalt.h b/api/include/SAM_TcsmoltenSalt.h index 6f90789daa..59bb9333ac 100644 --- a/api/include/SAM_TcsmoltenSalt.h +++ b/api/include/SAM_TcsmoltenSalt.h @@ -2272,6 +2272,14 @@ extern "C" */ SAM_EXPORT void SAM_TcsmoltenSalt_TimeOfDeliveryFactors_dispatch_tod_factors_aset(SAM_table ptr, double* arr, int length, SAM_error *err); + /** + * Set is_timestep_load_fractions: Use turbine load fraction for each timestep instead of block dispatch? + * options: None + * constraints: None + * required if: ?=0 + */ + SAM_EXPORT void SAM_TcsmoltenSalt_TimeOfDeliveryFactors_is_timestep_load_fractions_nset(SAM_table ptr, double number, SAM_error *err); + /** * Set ppa_multiplier_model: PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts [0/1] * options: 0=diurnal,1=timestep @@ -3252,6 +3260,8 @@ extern "C" SAM_EXPORT double* SAM_TcsmoltenSalt_TimeOfDeliveryFactors_dispatch_tod_factors_aget(SAM_table ptr, int* length, SAM_error *err); + SAM_EXPORT double SAM_TcsmoltenSalt_TimeOfDeliveryFactors_is_timestep_load_fractions_nget(SAM_table ptr, SAM_error *err); + SAM_EXPORT double SAM_TcsmoltenSalt_TimeOfDeliveryFactors_ppa_multiplier_model_nget(SAM_table ptr, SAM_error *err); diff --git a/api/modules/SAM_TcsmoltenSalt.cpp b/api/modules/SAM_TcsmoltenSalt.cpp index ab37cbcdfd..41cdacb2ed 100644 --- a/api/modules/SAM_TcsmoltenSalt.cpp +++ b/api/modules/SAM_TcsmoltenSalt.cpp @@ -1646,6 +1646,12 @@ SAM_EXPORT void SAM_TcsmoltenSalt_TimeOfDeliveryFactors_dispatch_tod_factors_ase }); } +SAM_EXPORT void SAM_TcsmoltenSalt_TimeOfDeliveryFactors_is_timestep_load_fractions_nset(SAM_table ptr, double number, SAM_error *err){ + translateExceptions(err, [&]{ + ssc_data_set_number(ptr, "is_timestep_load_fractions", number); + }); +} + SAM_EXPORT void SAM_TcsmoltenSalt_TimeOfDeliveryFactors_ppa_multiplier_model_nset(SAM_table ptr, double number, SAM_error *err){ translateExceptions(err, [&]{ ssc_data_set_number(ptr, "ppa_multiplier_model", number); @@ -4376,6 +4382,15 @@ SAM_EXPORT double* SAM_TcsmoltenSalt_TimeOfDeliveryFactors_dispatch_tod_factors_ return result; } +SAM_EXPORT double SAM_TcsmoltenSalt_TimeOfDeliveryFactors_is_timestep_load_fractions_nget(SAM_table ptr, SAM_error *err){ + double result; + translateExceptions(err, [&]{ + if (!ssc_data_get_number(ptr, "is_timestep_load_fractions", &result)) + make_access_error("SAM_TcsmoltenSalt", "is_timestep_load_fractions"); + }); + return result; +} + SAM_EXPORT double SAM_TcsmoltenSalt_TimeOfDeliveryFactors_ppa_multiplier_model_nget(SAM_table ptr, SAM_error *err){ double result; translateExceptions(err, [&]{ diff --git a/src/casewin.cpp b/src/casewin.cpp index 3e430b033f..f4235f2609 100644 --- a/src/casewin.cpp +++ b/src/casewin.cpp @@ -1098,7 +1098,6 @@ bool CaseWindow::SwitchToNavigationMenu(const wxString& name) m_navigationMenu->SetCurrentItem(dvi); m_currentSelection = (dvi); m_left_panel->SetFocus(); -// SwitchToInputPage(m_navigationMenu->GetItemText(dvi)); SwitchToPage(m_navigationMenu->GetItemText(dvi)); m_left_panel->Layout(); } diff --git a/src/main.cpp b/src/main.cpp index 902364d9cb..38a451c8b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -515,9 +515,9 @@ CaseWindow *MainWindow::CreateCaseWindow( Case *c ) } // load first page of hybrid and non-hybrid configurations if (c->GetConfiguration()->Technology.size() > 1) // hybrid - win->SwitchToInputPage(c->GetConfiguration()->InputPageGroups[c->GetConfiguration()->Technology.size() - 1][0]->SideBarLabel); + win->SwitchToNavigationMenu(c->GetConfiguration()->InputPageGroups[c->GetConfiguration()->Technology.size() - 1][0]->SideBarLabel); else - win->SwitchToInputPage(pages[0]); + win->SwitchToNavigationMenu(pages[0]); // reevaluate all equations address SAM #1583 c->EvaluateEquations(); diff --git a/src/script.cpp b/src/script.cpp index 1b9a6f8bb0..0d7a63675f 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -586,7 +586,6 @@ void fcall_show_page(lk::invoke_t &cxt) Case *active_case = CurrentCase(); if (CaseWindow* case_window = SamApp::Window()->GetCaseWindow(active_case)) { cxt.result().assign((case_window->SwitchToNavigationMenu(page_name) ? 1.0 : 0.0)); - // cxt.result().assign((case_window->SwitchToPage(page_name) ? 1.0 : 0.0)); } else cxt.error("no active case"); } From aa3e3d918de730d049a2161d2b865b1c7acbbab3 Mon Sep 17 00:00:00 2001 From: Paul Gilman Date: Thu, 14 Aug 2025 08:20:05 -0700 Subject: [PATCH 16/16] Remove test script --- deploy/runtime/macros/show page test.lk | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 deploy/runtime/macros/show page test.lk diff --git a/deploy/runtime/macros/show page test.lk b/deploy/runtime/macros/show page test.lk deleted file mode 100644 index fc2a50180d..0000000000 --- a/deploy/runtime/macros/show page test.lk +++ /dev/null @@ -1,9 +0,0 @@ -/*@ -For testing only. Delete before merging into Patch or Develop. -@*/ -show_page('System Design'); -msgbox('System Design page should be visible.'); -show_page('Location and Resource'); -g = widgetpos('in_location'); -transp(g,'yellow',75); -msgbox('Location and Resource page should be visible.'); \ No newline at end of file