@@ -88,10 +88,10 @@ def init_values(
8888 self .datetimes = dates
8989 else :
9090 self .datetimes = pd .date_range (
91- "2023-11-22T04 " ,
91+ "2023-11-27T04 " ,
9292 "2023-11-27T22" ,
9393 freq = pd .Timedelta (
94- hours = 0. 5
94+ minutes = 5
9595 ), # unit="s" bugs this TODO?: report to PVLIB
9696 )
9797
@@ -105,11 +105,11 @@ def reset_simulation_state(self):
105105 self .time_params = None
106106 self .input_keys = None
107107 self .results = None
108- self .times = dict ()
109-
108+ self .processing_time = dict ()
109+
110110 def times_summary (self ):
111111 print ("Timing of bench methods:" )
112- for key , value in self .times .items ():
112+ for key , value in self .processing_time .items ():
113113 if value :
114114 print (f"\t { key } : { value } s" )
115115
@@ -118,9 +118,12 @@ def simulation_prerun(self):
118118 Calculates some values from scratch, in case they were updated from the outside
119119 """
120120 start_time = time () # Initialize start time of block
121- self .fixed_params = {
121+ self .constant_params = {
122122 "surface_tilt" : self .surface_tilt , # degrees
123123 "ground_albedo" : 0.25 , # concrete pavement
124+ # ref. assumes from 0.31 to 0.3444 atm-cm & ozone does not have much impact
125+ # in spectra for Silicon (c-Si, a-Si) devices, so we are excluding it
126+ "ozone" : self .ozone ,
124127 }
125128 # Time-dependant values
126129 self .solpos = self .locus .get_solarposition (self .datetimes )
@@ -133,21 +136,18 @@ def simulation_prerun(self):
133136 self .time_params = {
134137 "apparent_zenith" : self .solpos ["apparent_zenith" ],
135138 "aoi" : self .aoi ,
136- "relative_airmass" : self .locus .get_airmass (self . datetimes , self .solpos )[
139+ "relative_airmass" : self .locus .get_airmass (solar_position = self .solpos )[
137140 "airmass_relative"
138141 ],
139142 "dayofyear" : np .fromiter (
140143 map (day_of_year , self .datetimes ), dtype = np .float64
141144 ),
142- # ref. assumes from 0.31 to 0.3444 atm-cm & ozone does not have much impact
143- # in spectra, so we are excluding it
144- "ozone" : self .ozone ,
145145 }
146- self .times ["simulation_prerun" ] = time () - start_time
146+ self .processing_time ["simulation_prerun" ] = time () - start_time
147147
148148 def simulate_from_product (self , ** inputvals ):
149149 """
150- Process a simulation from inputvals.
150+ Process a simulation from ** inputvals.
151151
152152 inputvals are keyword arguments, numpy 1-d arrays.
153153 It must contain spectrl2 required parameters:
@@ -189,7 +189,7 @@ def simulate_from_product(self, **inputvals):
189189 # 'poa_ground_diffuse', 'poa_direct', 'poa_global'
190190 spectrl2_result = spectrl2 (
191191 ** product_input ,
192- ** self .fixed_params ,
192+ ** self .constant_params ,
193193 ** self .time_params ,
194194 )
195195 self .results .iloc [index ] = [
@@ -203,8 +203,8 @@ def simulate_from_product(self, **inputvals):
203203 spectrl2_result ["poa_global" ].swapaxes (1 , 0 ),
204204 ),
205205 ]
206-
207- self .times ["simulate_from_product" ] = time () - start_time
206+
207+ self .processing_time ["simulate_from_product" ] = time () - start_time
208208
209209 self .simulation_post ()
210210
@@ -214,7 +214,7 @@ def simulation_post(self):
214214 """
215215 start_time = time () # Initialize start time of block
216216 self .post_summary ()
217- self .times ["simulation_post" ] = time () - start_time
217+ self .processing_time ["simulation_post" ] = time () - start_time
218218
219219 def post_summary (self ):
220220 """
@@ -224,7 +224,7 @@ def post_summary(self):
224224 means = self .results .iloc [:, n_inputs :].mean ().dropna ()
225225 # stdvs = self.results.iloc[:, n_inputs:].std().dropna()
226226 print ("Simulation Results" )
227- print (f"> Cutoff wavelength: { self .cutoff_lambda } " )
227+ print (f"> Cutoff wavelength: { self .cutoff_lambda } nm " )
228228 print (f"> Mean E_λ<λ₀/E = { means .mean ()} " )
229229 # print(f"Zenith\t Mean of avg(E_λ<λ₀/E)\n{means}")
230230 print (f"> Std E_λ<λ₀/E = { means .std ()} " )
@@ -247,12 +247,28 @@ def plot_results(
247247 plot_keys ,
248248 } # cast to set
249249
250+ # variable guard: only allow valid keys, from self.input_keys & self.time_params
251+ allowed_keys = set (self .input_keys ) | self .time_params .keys ()
252+ invalid_keys = plot_keys - allowed_keys
253+ if invalid_keys == {}:
254+ raise ValueError (
255+ "Incorrect key provided.\n "
256+ + f"Allowed keys are: { allowed_keys } \n "
257+ + f"Invalid keys are: { invalid_keys } "
258+ )
259+ del allowed_keys , invalid_keys
260+
250261 # assume we've got an iterable of strings
251262 # make at most two columns
252263 cols = min (max_cols , len (plot_keys ))
253264 rows = int (np .ceil (len (plot_keys ) / cols ))
254265 fig , axs = plt .subplots (ncols = cols , nrows = rows )
255- axs = axs .flatten ()
266+
267+ if isinstance (axs , np .ndarray ): # to allow iteration in one dimension
268+ axs = axs .flatten ()
269+ else : # plt.Axes type
270+ axs = [axs ] # to allow iteration of just that element
271+
256272 fig .suptitle (
257273 r"$\frac{E_{λ<λ_0}}{E}$ as function of SPECTRL2 inputs"
258274 + f"\n λ₀={ self .cutoff_lambda } nm"
@@ -262,17 +278,19 @@ def plot_results(
262278 # number of inputs from user: n-left-most columns
263279 n_inputs = len (self .input_keys )
264280
265- # treatment of special case: apparent zenith in plot_keys
266- var_name = "apparent_zenith"
267- if var_name in plot_keys :
268- ax = axs [- 1 ] # this does not interfere with later iterating of axs
281+ # for each axes, plot a relationship
282+ # Case: time-dependant variables in plot_keys
283+ for ax , var_name in zip (
284+ axs [::- 1 ], # from last to first, so it doesn't clash with generator keys
285+ plot_keys .intersection (self .time_params .keys ()),
286+ ):
269287 ax .set_title (r"$\frac{E_{λ<λ_0}}{E}$ vs. " + var_name )
270- x = self .results . columns [ n_inputs : ]
271- for _ , row in self .results .iterrows ():
288+ x = self .time_params [ var_name ]
289+ for _ , row in self .results .iloc [ n_inputs :]. iterrows ():
272290 ax .scatter (x , row [n_inputs :])
273291 plot_keys .remove (var_name )
274292
275- # for each axes, plot a relationship
293+ # Case: SPECTRL2 generator input parameters
276294 for ax , var_name in zip (axs , plot_keys ):
277295 ax .set_title (r"$\frac{E_{λ<λ_0}}{E}$ vs. " + var_name )
278296 x = self .results [var_name ]
@@ -282,8 +300,9 @@ def plot_results(
282300
283301 if savefig :
284302 fig .savefig (
285- f"E_ratio_lambda{ self .cutoff_lambda } _"
303+ f"E_ratio_lambda{ self .cutoff_lambda :04.0f } _"
286304 + datetime .now ().strftime ("%Y-%m-%dT%H-%M-%S" )
305+ + ".png"
287306 )
288307
289- self .times ["plot_results" ] = time () - start_time
308+ self .processing_time ["plot_results" ] = time () - start_time
0 commit comments