Skip to content

Commit bf64ce8

Browse files
committed
Merge branch 'ssc_195_day_of_week_improvements' of https://github.com/NREL/SAM into ssc_195_day_of_week_improvements
2 parents 892bd83 + 5837a1e commit bf64ce8

File tree

4 files changed

+274
-1004
lines changed

4 files changed

+274
-1004
lines changed

deploy/runtime/macros/Flat Plate PV/System Sizing.lk

Lines changed: 133 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<li>The <b>Annual DC degradation</b> parameter at right is for the DC/AC Ratio assumptions table (Table 3) in the report. Enter a value to represent the worst-case for an estimate of end-of-life DC/AC ratio.</li>
66
<li>The minimum, maximum, and adjustment temperatures are for the worst case Voc calculations as described in the Bill Brooks article <a href="https://solarprofessional.com/articles/design-installation/array-voltage-considerations">Array Voltage Considerations</a>, Solar Pro Magazine Issue 3.6 Oct/Nov 2010. The macro displays a table showing Voc at these temperatures along with the minimum and maximum operating Voc values calculated by SAM.
77
<li>The maximum DC/AC ratio affects the list of recommended modules. The macro will only list modules that result in a DC/AC ration less than or equal to the value you specify.
8-
<li>By default, the macro displays a report in a SAM window. If you want to export the report, choose Yes for <b>Show results in default browser?</b> to save the report as an HTML file and open it in your default browser. You can then use the browser's tools to export it to PDF or another format.
8+
<li>By default, the macro displays a report in a SAM window. If you want to export the report to an HTML file, choose Yes for <b>Save results to HTML</b> to save the report as an HTML file and open it in your default browser. You can then use the browser's tools to export it to PDF or another format.
99
</ol>
1010
<p>&nbsp;</p>
1111

@@ -16,25 +16,28 @@
1616
//@ name=t_max;type=number;label=Maximum temperature (C);value=46
1717
//@ name=t_adj;type=number;label=Adjustment temperature (C);value=30
1818
//@ name=dcac_max;type=number;label=Maximum DC/AC ratio;value=1.3
19-
//@ name=show_in_browser;type=combo;label=Show results in default browser?;value=Yes,No;sel=1
19+
//@ name=save_to_html;type=combo;label=Save results to HTML;value=Yes,No;sel=1
2020

2121
// to run as script for troubleshooting, set troubleshoot = false to use sample inputs
2222
// otherwise, display warning if the macro is run outside of a case
2323
troubleshoot = false;
24+
html_dir = '';
2425
if ( troubleshoot ) // sample inputs
2526
{
2627
macro.dc_degradation = 0.5;
2728
macro.t_min = -3;
2829
macro.t_max = 46;
2930
macro.t_adj = 30;
3031
macro.dcac_max = 1.3;
31-
macro.show_in_browser = 1;
32+
macro.save_to_html = "Yes";
3233
}
3334
elseif ( typeof(macro) == 'unknown' ) {
3435
msgbox('This macro must be run from within a case.');
3536
exit;
3637
}
37-
38+
elseif( macro.save_to_html == 'Yes') {
39+
html_dir = choose_dir( homedir(), 'Choose a folder to save HTML file');
40+
}
3841
//Max number of subarrays allowed in SAM:
3942
max_subarrays = 4;
4043

@@ -121,7 +124,7 @@ function html_table ( T ) // arr, row_headers, title
121124
rows = #T.arr;
122125
cols = #T.arr[0];
123126
if ( T.title == '' ) { h = ''; }
124-
else { h = '<p align="center"><font face="Segoe UI"; size="3">' + T.title + '</font></p>'; }
127+
else { h = '<p align="center"><font face="Segoe UI" size="3">' + T.title + '</font></p>'; }
125128
h += '<table bgcolor="#dddddd" width="100%">\n';
126129
for ( i=0; i<rows; i++ )
127130
{
@@ -161,7 +164,8 @@ function html_table ( T ) // arr, row_headers, title
161164
// 'y_labels' = 1D array of strings with a label for each bar
162165
// 'bar_label' = string with label for legend describing bar
163166
// 'line_labels' = 1D array of two or three strings for legend describing each vertical line
164-
// 'export_graph' = true/false export graph as png file named from title string }
167+
// 'export_graph' = true/false export graph as png file named from title string
168+
// 'png_dir' = folder to save image files }
165169
function bar_graph_with_limit_lines( T )
166170
{
167171
x_axis_max = max( [max(T.bar_data), max(T.line_data)] );
@@ -190,13 +194,14 @@ function bar_graph_with_limit_lines( T )
190194
plotopt( { 'title' = T.title, 'legend' = true, 'scale' = 1, 'font' = 'Segoe UI', 'window' = [10,10,600,300], 'border' = 1, 'fine' = true, 'coarse' = true } );
191195
if ( T.export_graph )
192196
{
193-
f_path=cwd();
194-
f_name = replace( T.title , ' ' , '_' );
197+
f_path = T.png_dir;
198+
f_name = replace( T.title , ' ' , '-' );
195199
f_name = replace( f_name, ':' , '' );
196200
f_name = replace( f_name, '.' , '' );
197-
f_name = replace( f_name, '__' , '_' );
201+
f_name = replace( f_name, '--' , '-' );
198202
f = f_path + '/' + f_name + '.png';
199-
ok = plotout( f , 'png'); // png, pdf, bmp, or jpg. Defaults to png if file type not given.
203+
f = replace( f, '\\' , '/' );
204+
ok = plotout( f , 'png');
200205
if ( ok == false)
201206
{
202207
outln('Could not save the plot to ' + f);
@@ -207,6 +212,62 @@ function bar_graph_with_limit_lines( T )
207212
}
208213
}
209214

215+
// return path to .png of a bar graph of annual power limiting loss over analysis period
216+
function plot_annual_inv_cliploss( png_dir )
217+
{
218+
// determine number of time steps per year
219+
data_ts = get('inv_cliploss');
220+
analysis_period = get('analysis_period');
221+
num_ts = #get('gen');
222+
ts_per_year = num_ts / analysis_period;
223+
224+
// calculate annual totals for any given simulation time step
225+
data_ann = alloc(analysis_period);
226+
n=0;
227+
start = 0;
228+
for (y=0; y<analysis_period; y++)
229+
{
230+
x = 0;
231+
for(ts=start; ts<num_ts; ts++)
232+
{
233+
x += data_ts[ts];
234+
}
235+
data_ann[y] = x;
236+
start += ts_per_year;
237+
}
238+
239+
// generate year number for x axis
240+
years = alloc(analysis_period);
241+
for(i=0;i<#data_ann;i++)
242+
{
243+
years[i] = i;
244+
}
245+
246+
// create bar plot of annual total vs year
247+
title = 'Figure 1: Annual Power Limiting Losses';
248+
newplot(true);
249+
plot(years, data_ann, {'type'='bar', 'color' = '#457585', 'thick' = 5});
250+
axis('x1', {'min'=min(years), 'max'=max(years),'label'='year' });
251+
axis('y1', {'min'=0, 'max'=max(data_ann), 'label'='Energy (kWh)'});
252+
plotopt( {'title' = title, 'legend' = false, 'scale' = 1, 'font' = 'Segoe UI', 'window' = [10,10,600,300], 'border' = 1, 'fine' = true, 'coarse' = true } );
253+
254+
use_html_dir = macro.save_to_html == 'Yes';
255+
f_path = ? use_html_dir [cwd(), html_dir];
256+
f_name = replace( title , ' ' , '-' );
257+
f_name = replace( f_name, ':' , '' );
258+
f_name = replace( f_name, '.' , '' );
259+
f_name = replace( f_name, '--' , '-' );
260+
f = f_path + '/' + f_name + '.png';
261+
f = replace( f, '\\' , '/' );
262+
ok = plotout( f , 'png');
263+
if ( ok == false)
264+
{
265+
outln('Could not save the plot to ' + f);
266+
return null;
267+
}
268+
else { return f; }
269+
}
270+
210271
// return table of module parameters, except SPE module model return null
211272
function get_module_parameters( )
212273
{
@@ -426,42 +487,56 @@ clipping_time_severe = 0.10;
426487
clipping_energy_moderate = 0.20; // fraction of kWh/year
427488
clipping_energy_severe = 0.50;
428489

429-
// power limiting when DC input power exceeds inverter rating
430-
inv_dc_input = get('annual_dc_net');
431-
inv_ac_output = get('annual_ac_gross');
490+
num_ts = #get('gen');
491+
num_ts_ann_avg = num_ts/analysis_period;
492+
493+
// sum of DC net and AC gross over lifetime
432494
inv_dc_input_ts = get('dc_net');
495+
inv_dc_input = sum(inv_dc_input_ts);
496+
inv_ac_output_ts = get('ac_gross');
497+
inv_ac_output = sum(inv_ac_output_ts);
498+
499+
// determine number of timesteps at inverter is at rated power
433500
inv_power_at_capacity_num_ts=0;
434-
for (i=0; i<#inv_dc_input_ts; i++)
501+
for (i=0; i<#inv_dc_input_ts; i++)
435502
{
436503
if ( inv_dc_input_ts[i] >= inverter.pdco * num_inverters / 1000 )
437504
{ inv_power_at_capacity_num_ts++; }
438505
}
439-
inv_dc_input_ts = null;
440-
inv_powerlimitloss_ts = get('inv_cliploss'); //Gets time series data of inverter clipping losses
441-
num_ts = #inv_powerlimitloss_ts;
506+
inv_dc_input_ts = null; // avoid storing large array
507+
508+
// power limiting when DC input power exceeds inverter rating
509+
inv_powerlimitloss_ts = get('inv_cliploss');
442510
inv_powerlimitloss_max = max(inv_powerlimitloss_ts);
443511
inv_powerlimitloss = sum(inv_powerlimitloss_ts);
512+
inv_powerlimitloss_ann_avg = inv_powerlimitloss/analysis_period;
444513
inv_powerlimitloss_num_ts = 0;
445514
for (i=0; i<#inv_powerlimitloss_ts; i++)
446515
{
447516
if(inv_powerlimitloss_ts[i]>0)
448517
{ inv_powerlimitloss_num_ts++; }
449518
}
450-
inv_powerlimitloss_ts = null;
451-
inv_powerlimitloss_percent_dc = inv_powerlimitloss / inv_dc_input * 100;
452-
inv_powerlimitloss_percent_ac = inv_powerlimitloss / inv_ac_output * 100;
519+
inv_powerlimitloss_ts = null; // avoid storing large array
520+
inv_powerlimitloss_num_ts_ann_avg = inv_powerlimitloss_num_ts/analysis_period;
453521

454-
// first-year inverter mppt voltage clipping
522+
523+
// voltage clipping when DC voltage exceeds inverter MPPT voltage ratings
455524
mpptloss_ts = get('dc_invmppt_loss');
456525
mpptloss_max = max(mpptloss_ts);
457526
mpptloss = sum(mpptloss_ts);
458-
mpptloss_num_ts = 0; //how many hours are clipped per year
527+
mpptloss_ann_avg = mpptloss/analysis_period;
528+
mpptloss_num_ts = 0;
459529
for (i=0; i<#mpptloss_ts; i++)
460530
{
461531
if(mpptloss_ts[i]>0)
462532
{ mpptloss_num_ts++; }
463533
}
464-
mpptloss_ts = null;
534+
mpptloss_ts = null; // avoid storing large array
535+
mpptloss_num_ts_ann_avg = mpptloss_num_ts/analysis_period;
536+
537+
// calculate percentages over lifetime
538+
inv_powerlimitloss_percent_dc = inv_powerlimitloss / inv_dc_input * 100;
539+
inv_powerlimitloss_percent_ac = inv_powerlimitloss / inv_ac_output * 100;
465540
mpptloss_percent_dc = mpptloss / inv_dc_input * 100;
466541
mpptloss_percent_ac = mpptloss / inv_ac_output * 100;
467542

@@ -528,8 +603,9 @@ vmp_min_design = ( module.vmp + ( macro.t_max + macro.t_adj - t_stc ) * module.g
528603
voc_max_design = ( module.voc + ( macro.t_min - t_stc ) * module.bvoc/100 * module.voc ); // single module
529604

530605
////////////////////////////////////////////////////////////////////////////////
531-
// design suggestions
606+
// Design suggestions
532607
////////////////////////////////////////////////////////////////////////////////
608+
533609
out('Generating design suggestions ');
534610
// check 1: number of strings based on dc-ac ratio
535611
dcac_ok = true;
@@ -607,7 +683,10 @@ modules_diff = shorten_array(modules_diff, 50);
607683
// Create graphs as .png files
608684
////////////////////////////////////////////////////////////////////////////////
609685

686+
annual_cliploss_graph = plot_annual_inv_cliploss( html_dir );
687+
610688
num_plots = 0;
689+
num_start = 2; // assumes Figure 1 is cliploss graph, so these plots start at Figure 2
611690
for ( j=0; j < max_subarrays; j++ )
612691
{
613692
if ( vdc_hi[j] > -999 )
@@ -628,16 +707,18 @@ for ( j=0; j < max_subarrays; j++ )
628707
line_labels = ['V_mpptmin','V_mpptmax', 'V_dcmax'];
629708
line_data = [ inverter.vmppt_min , inverter.vmppt_max , inverter.vdcmax ];
630709
}
631-
710+
711+
use_html_dir = macro.save_to_html=='Yes';
632712
voperating_graphs[num_plots] = bar_graph_with_limit_lines( {
633-
'title' = 'Figure ' + to_string(num_plots+1) + ': ' + pre_str + 'String Voltages', // underscore makes subscript
713+
'title' = 'Figure ' + to_string(num_plots+num_start) + ': ' + pre_str + 'String Voltages', // underscore makes subscript
634714
'line_data' = line_data, // vmppt_min, vmppt_max, and Vdcmax if Vdcmax != Vmppt_max
635715
'line_labels' = line_labels,
636716
'bar_data' = [voc_max_design * modules_per_string[j], voc_hi[j], vdc_hi[j], vdc_low[j], vmp_min_design * modules_per_string[j] ], // one value for each bar
637717
'bar_label' = '',
638718
'x_label' = 'VDC',
639719
'y_labels' = ['Max Design Voc','Max Operating Voc','Max Operating Vdc','Min Operating Vdc','Min Design Vdc'], // one label for each bar
640-
'export_graph' = true} );
720+
'export_graph' = true,
721+
'png_dir' = ? use_html_dir [cwd(), html_dir] });
641722
num_plots++;
642723
}
643724
}
@@ -688,16 +769,16 @@ for( i=0; i<#lid; i++ )
688769
{ tabledata_dcac[3][i+1] = last_yr_dcac[i]; }
689770
}
690771

691-
tabletitle_powerloss = 'Estimated Power Losses Due to Inverter Voltage Clipping and Power Limiting';
772+
tabletitle_powerloss = 'Estimated Lifetime Power Losses Due to Inverter Voltage Clipping and Power Limiting';
692773
tabledata_powerloss[0][0] = '';
693774
tabledata_powerloss[0][1] = 'Power Limiting';
694775
tabledata_powerloss[0][2] = 'MPPT Voltage Clipping';
695776
tabledata_powerloss[1][0] = 'Total kWh/year lost';
696-
tabledata_powerloss[1][1] = inv_powerlimitloss;
697-
tabledata_powerloss[1][2] = mpptloss;
698-
tabledata_powerloss[2][0] = 'Number of time steps (hourly or subhourly)';
699-
tabledata_powerloss[2][1] = [inv_powerlimitloss_num_ts,clipping_time_moderate*num_ts,clipping_time_severe*num_ts]; // change background color when value exceeds 5% and 10% of timesteps per year
700-
tabledata_powerloss[2][2] = [mpptloss_num_ts,clipping_time_moderate*num_ts,clipping_time_severe*num_ts];
777+
tabledata_powerloss[1][1] = inv_powerlimitloss_ann_avg;
778+
tabledata_powerloss[1][2] = mpptloss_ann_avg;
779+
tabledata_powerloss[2][0] = 'Number of time steps/year (hourly or subhourly)';
780+
tabledata_powerloss[2][1] = [inv_powerlimitloss_num_ts_ann_avg,clipping_time_moderate*num_ts_ann_avg,clipping_time_severe*num_ts_ann_avg]; // change background color when value exceeds 5% and 10% of timesteps per year
781+
tabledata_powerloss[2][2] = [mpptloss_num_ts_ann_avg,clipping_time_moderate*num_ts_ann_avg,clipping_time_severe*num_ts_ann_avg];
701782
tabledata_powerloss[3][0] = 'Total kWh/year lost as % of inverter DC input';
702783
tabledata_powerloss[3][1] = [inv_powerlimitloss_percent_dc,clipping_energy_moderate*100,clipping_energy_severe*100]; // change background color when when value exceeds 20% and 50%
703784
tabledata_powerloss[3][2] = [mpptloss_percent_dc,clipping_energy_moderate*100,clipping_energy_severe*100];
@@ -770,7 +851,7 @@ for(column_diff = 2 ; column_diff < (max_subarrays + 2) ; column_diff++){
770851
// Create HTML report
771852
////////////////////////////////////////////////////////////////////////////////
772853
num_table = 0;
773-
str = '<html><body><h1>System Sizing Information</h1><p>This information is based';
854+
str = '<!DOCTYPE HTML><html><body><h1>System Sizing Information</h1><p>This information is based';
774855
str += ' on the inputs and simulation results for the "' + active_case() + '" case.</p>';
775856

776857
str += '<h2>Parameters Used for System Sizing</h2>';
@@ -796,21 +877,26 @@ str += ' some power limiting or voltage clipping losses may be acceptable.</p>';
796877
num_table++;
797878
str += html_table( {'arr' = tabledata_powerloss, 'row_headers' = false, 'title' = 'Table ' + num_table + ': ' + tabletitle_powerloss } );
798879
str += '<p>Table ' + num_table + ' shows the amount of power limiting and voltage';
799-
str += ' clipping from the simulation results, which give you an estimate of what to expect from the real system.';
880+
str += ' clipping over the ' + to_string(analysis_period) + '-year lifetime from the simulation results,';
881+
str += ' which you can use to estimate what to expect from the real system.';
800882
str += ' Colors indicate:</p>';
801883
str += '<ul><li>Red: More than 10% of time steps of the year, or more than 50% of annual power output.</li>';
802884
str += '<li>Orange: More than 5% of time steps or 20% of output.</li>';
803885
str += '<li>Clear: Values are within acceptable range, less than 5% of time steps or 20% of annual power output.</li></ul>';
886+
887+
str += '<p>Figure 1 shows inverter power limiting losses by year. These losses decrease due to degradation of modules in the photovoltaic array (DC degradation).</p>';
888+
str += '<p align="center"><img src="' + file_only(annual_cliploss_graph) + '"/></p>';
889+
804890
str += '<p>The figure(s) below show the inverter rated MPPT voltage range with worst case';
805891
str += ' design maximum open-circuit voltage and minimum maximum-power voltage, along with';
806892
str += ' the operating open circuit, minimum, and maximum voltages from the';
807893
str += ' simulation results for each subarray in the system.</p>';
808-
809894
for (i=0; i<#voperating_graphs; i++)
810895
{
811-
str += '<p align="center"><img src="' + voperating_graphs[i] + '" /></p>';
896+
str += '<p align="center"><img src="' + file_only(voperating_graphs[i]) + '" /></p>';
812897
}
813898
num_fig = i;
899+
814900
str += '<p>The equations for the design string voltages use parameters from Table 1:</p>';
815901
str += '<p align="center"><font face="verdana">V<sub>dc , min_design</sub>';
816902
str += ' = [ V<sub>mp</sub> + ( T<sub>max</sub> + T<sub>adj</sub> - T<sub>stc</sub> ) * &gamma;P<sub>mp</sub>/100% * P<sub>mp</sub> ] * modules_per_string</font></p>';
@@ -893,16 +979,23 @@ newplot( true );
893979

894980
// save html to file and open file in default web browser
895981
// this is useful for converting to other formats like pdf or docx
896-
897-
if ( macro.show_in_browser == 'Yes' )
982+
if ( macro.save_to_html == 'Yes' )
898983
{
899-
html_file = cwd() + '/' + active_case() + '.html';
984+
html_file = html_dir + '/' + active_case() + '.html';
900985
write_text_file( html_file , str );
901986
browse( html_file );
902987
}
903988
else
904989
{
905990
html_dialog(str, 'System Sizing Information', [300,300,800,600]); //custom window title and size
991+
// after dialog displays, delete graph png files
992+
remove_file(annual_cliploss_graph);
993+
for (i=0;i<#voperating_graphs;i++)
994+
{
995+
remove_file(voperating_graphs[i]);
996+
}
906997
}
907998

999+
1000+
9081001
outln('Done. If you do not see an HTML report, try minimizing the SAM window to see if is hidden behind the window.');

0 commit comments

Comments
 (0)