1
1
"""
2
- Stub documentation for the module.
2
+ The ``modelchain`` module contains functions and classes that combine
3
+ many of the PV power modeling steps. These tools make it easy to
4
+ get started with pvlib and demonstrate standard ways to use the
5
+ library. With great power comes great responsibility: users should take
6
+ the time to read the source code for the module.
3
7
"""
4
8
9
+ import pandas as pd
10
+
11
+ from pvlib import solarposition , pvsystem , clearsky , atmosphere
12
+ import pvlib .irradiance # avoid name conflict with full import
13
+
14
+
15
+ def basic_chain (times , latitude , longitude ,
16
+ module_parameters , inverter_parameters ,
17
+ irradiance = None , weather = None ,
18
+ surface_tilt = None , surface_azimuth = None ,
19
+ orientation_strategy = None ,
20
+ transposition_model = 'haydavies' ,
21
+ solar_position_method = 'nrel_numpy' ,
22
+ airmass_model = 'kastenyoung1989' ,
23
+ ** kwargs ):
24
+ """
25
+ A function that computes all of the modeling steps necessary for
26
+ calculating power or energy for a PV system at a given location.
27
+
28
+ Parameters
29
+ ----------
30
+ system : dict
31
+ The connected set of modules, inverters, etc.
32
+ keys must include latitude, longitude, module_parameters
33
+
34
+ location : dict
35
+ The physical location at which to evaluate the model.
36
+
37
+ times : DatetimeIndex
38
+ Times at which to evaluate the model.
39
+
40
+ orientation_strategy : None or str
41
+ The strategy for aligning the modules.
42
+ If not None, sets the ``surface_azimuth`` and ``surface_tilt``
43
+ properties of the ``system``.
44
+
45
+ clearsky_model : str
46
+ Passed to location.get_clearsky.
47
+
48
+ transposition_model : str
49
+ Passed to system.get_irradiance.
50
+
51
+ solar_position_method : str
52
+ Passed to location.get_solarposition.
53
+
54
+ **kwargs
55
+ Arbitrary keyword arguments.
56
+ See code for details.
57
+ """
58
+
59
+ # use surface_tilt and surface_azimuth if provided,
60
+ # otherwise set them using the orientation_strategy
61
+ if surface_tilt is not None and surface_azimuth is not None :
62
+ pass
63
+ elif orientation_strategy is not None :
64
+ surface_tilt , surface_azimuth = \
65
+ get_orientation (orientation_strategy , latitude = latitude )
66
+ else :
67
+ raise ValueError ('orientation_strategy or surface_tilt and ' +
68
+ 'surface_azimuth must be provided' )
69
+
70
+ times = times
71
+
72
+ solar_position = solarposition .get_solarposition (times , latitude ,
73
+ longitude , ** kwargs )
74
+
75
+ # possible error with using apparent zenith with some models
76
+ airmass = atmosphere .relativeairmass (solar_position ['apparent_zenith' ],
77
+ model = airmass_model )
78
+ airmass = atmosphere .absoluteairmass (airmass ,
79
+ kwargs .get ('pressure' , 101325. ))
80
+ dni_extra = pvlib .irradiance .extraradiation (solar_position .index )
81
+ dni_extra = pd .Series (dni_extra , index = solar_position .index )
82
+
83
+ aoi = pvlib .irradiance .aoi (surface_tilt , surface_azimuth ,
84
+ solar_position ['apparent_zenith' ],
85
+ solar_position ['azimuth' ])
86
+
87
+ if irradiance is None :
88
+ irradiance = clearsky .ineichen (
89
+ solar_position .index ,
90
+ latitude ,
91
+ longitude ,
92
+ zenith_data = solar_position ['apparent_zenith' ],
93
+ airmass_data = airmass )
94
+
95
+ total_irrad = pvlib .irradiance .total_irrad (
96
+ surface_tilt ,
97
+ surface_azimuth ,
98
+ solar_position ['apparent_zenith' ],
99
+ solar_position ['azimuth' ],
100
+ irradiance ['dni' ],
101
+ irradiance ['ghi' ],
102
+ irradiance ['dhi' ],
103
+ model = transposition_model ,
104
+ dni_extra = dni_extra )
105
+
106
+ if weather is None :
107
+ weather = {'wind_speed' : 0 , 'temp_air' : 20 }
108
+
109
+ temps = pvsystem .sapm_celltemp (total_irrad ['poa_global' ],
110
+ weather ['wind_speed' ],
111
+ weather ['temp_air' ])
112
+
113
+ dc = pvsystem .sapm (module_parameters , total_irrad ['poa_direct' ],
114
+ total_irrad ['poa_diffuse' ],
115
+ temps ['temp_cell' ],
116
+ airmass ,
117
+ aoi )
118
+
119
+ ac = pvsystem .snlinverter (inverter_parameters , dc ['v_mp' ], dc ['p_mp' ])
120
+
121
+ return dc , ac
122
+
123
+
124
+ def get_orientation (strategy , ** kwargs ):
125
+ """
126
+ Determine a PV system's surface tilt and surface azimuth
127
+ using a named strategy.
128
+
129
+ Parameters
130
+ ----------
131
+ strategy: str
132
+ The orientation strategy.
133
+ Allowed strategies include 'flat', 'south_at_latitude_tilt'.
134
+ **kwargs:
135
+ Strategy-dependent keyword arguments. See code for details.
136
+
137
+ Returns
138
+ -------
139
+ surface_tilt, surface_azimuth
140
+ """
141
+
142
+ if strategy == 'south_at_latitude_tilt' :
143
+ surface_azimuth = 180
144
+ surface_tilt = kwargs ['latitude' ]
145
+ elif strategy == 'flat' :
146
+ surface_azimuth = 180
147
+ surface_tilt = 0
148
+ else :
149
+ raise ValueError ('invalid orientation strategy. strategy must ' +
150
+ 'be one of south_at_latitude, flat,' )
151
+
152
+ return surface_tilt , surface_azimuth
153
+
154
+
5
155
class ModelChain (object ):
6
156
"""
7
157
A class that represents all of the modeling steps necessary for
@@ -24,6 +174,7 @@ class ModelChain(object):
24
174
The strategy for aligning the modules.
25
175
If not None, sets the ``surface_azimuth`` and ``surface_tilt``
26
176
properties of the ``system``.
177
+ Allowed strategies include 'flat', 'south_at_latitude_tilt'.
27
178
28
179
clearsky_model : str
29
180
Passed to location.get_clearsky.
@@ -58,36 +209,29 @@ def __init__(self, system, location,
58
209
self .transposition_model = transposition_model
59
210
self .solar_position_method = solar_position_method
60
211
self .airmass_model = airmass_model
61
-
212
+
62
213
# calls setter
63
214
self .orientation_strategy = orientation_strategy
64
-
215
+
65
216
@property
66
217
def orientation_strategy (self ):
67
218
return self ._orientation_strategy
68
-
69
-
219
+
70
220
@orientation_strategy .setter
71
221
def orientation_strategy (self , strategy ):
72
- if strategy is None or strategy == 'None' :
73
- pass
74
- elif strategy == 'south_at_latitude_tilt' :
75
- self .system .surface_azimuth = 180
76
- self .system .surface_tilt = self .location .latitude
77
- elif strategy == 'flat' :
78
- self .system .surface_azimuth = 180
79
- self .system .surface_tilt = 0
80
- else :
81
- raise ValueError ('invalid orientation strategy. strategy must ' +
82
- 'be one of south_at_latitude, flat,' )
222
+ if strategy == 'None' :
223
+ strategy = None
224
+
225
+ if strategy is not None :
226
+ self .system .surface_tilt , self .system .surface_azimuth = \
227
+ get_orientation (strategy , latitude = self .location .latitude )
83
228
84
229
self ._orientation_strategy = strategy
85
-
86
230
87
231
def run_model (self , times , irradiance = None , weather = None ):
88
232
"""
89
233
Run the model.
90
-
234
+
91
235
Parameters
92
236
----------
93
237
times : DatetimeIndex
@@ -103,57 +247,65 @@ def run_model(self, times, irradiance=None, weather=None):
103
247
-------
104
248
output : DataFrame
105
249
Some combination of AC power, DC power, POA irrad, etc.
250
+
251
+ Assigns attributes: times, solar_position, airmass, irradiance,
252
+ total_irrad, weather, temps, aoi, dc, ac
106
253
"""
107
- solar_position = self .location .get_solarposition (times )
108
-
109
- airmass = self .location .get_airmass (solar_position = solar_position ,
110
- model = self .airmass_model )
254
+ self .times = times
255
+
256
+ self .solar_position = self .location .get_solarposition (self .times )
257
+
258
+ self .airmass = self .location .get_airmass (
259
+ solar_position = self .solar_position , model = self .airmass_model )
111
260
112
261
if irradiance is None :
113
- irradiance = self .location .get_clearsky (solar_position .index ,
114
- self .clearsky_model ,
115
- zenith_data = solar_position ['apparent_zenith' ],
116
- airmass_data = airmass ['airmass_absolute' ])
117
-
118
- total_irrad = self .system .get_irradiance (solar_position ['apparent_zenith' ],
119
- solar_position ['azimuth' ],
120
- irradiance ['dni' ],
121
- irradiance ['ghi' ],
122
- irradiance ['dhi' ],
123
- model = self .transposition_model )
262
+ irradiance = self .location .get_clearsky (
263
+ self .solar_position .index , self .clearsky_model ,
264
+ zenith_data = self .solar_position ['apparent_zenith' ],
265
+ airmass_data = self .airmass ['airmass_absolute' ])
266
+ self .irradiance = irradiance
267
+
268
+ self .total_irrad = self .system .get_irradiance (
269
+ self .solar_position ['apparent_zenith' ],
270
+ self .solar_position ['azimuth' ],
271
+ self .irradiance ['dni' ],
272
+ self .irradiance ['ghi' ],
273
+ self .irradiance ['dhi' ],
274
+ model = self .transposition_model )
124
275
125
276
if weather is None :
126
277
weather = {'wind_speed' : 0 , 'temp_air' : 20 }
278
+ self .weather = weather
127
279
128
- temps = self .system .sapm_celltemp (total_irrad ['poa_global' ],
129
- weather ['wind_speed' ],
130
- weather ['temp_air' ])
280
+ self . temps = self .system .sapm_celltemp (self . total_irrad ['poa_global' ],
281
+ self . weather ['wind_speed' ],
282
+ self . weather ['temp_air' ])
131
283
132
- aoi = self .system .get_aoi (solar_position ['apparent_zenith' ],
133
- solar_position ['azimuth' ])
284
+ self . aoi = self .system .get_aoi (self . solar_position ['apparent_zenith' ],
285
+ self . solar_position ['azimuth' ])
134
286
135
- dc = self .system .sapm (total_irrad ['poa_direct' ],
136
- total_irrad ['poa_diffuse' ],
137
- temps ['temp_cell' ],
138
- airmass ['airmass_absolute' ],
139
- aoi )
287
+ self . dc = self .system .sapm (self . total_irrad ['poa_direct' ],
288
+ self . total_irrad ['poa_diffuse' ],
289
+ self . temps ['temp_cell' ],
290
+ self . airmass ['airmass_absolute' ],
291
+ self . aoi )
140
292
141
- ac = self .system .snlinverter (dc ['v_mp' ], dc ['p_mp' ])
293
+ self . ac = self .system .snlinverter (self . dc ['v_mp' ], self . dc ['p_mp' ])
142
294
143
- return dc , ac
295
+ return self . dc , self . ac
144
296
145
297
146
298
def model_system (self ):
147
299
"""
148
300
Model the system?
149
-
301
+
150
302
I'm just copy/pasting example code...
151
-
303
+
152
304
Returns
153
305
-------
154
306
???
155
307
"""
156
-
308
+
157
309
final_output = self .run_model ()
158
310
input = self .prettify_input ()
159
311
modeling_steps = self .get_modeling_steps ()
@@ -166,7 +318,7 @@ class MoreSpecificModelChain(ModelChain):
166
318
"""
167
319
def __init__ (self , * args , ** kwargs ):
168
320
super (MoreSpecificModelChain , self ).__init__ (** kwargs )
169
-
321
+
170
322
def run_model (self ):
171
323
# overrides the parent ModelChain method
172
324
pass
0 commit comments