11
11
import numpy as np
12
12
import pandas as pd
13
13
import functools
14
- from pvlib .tools import cosd , sind , tand , asind
14
+ from pvlib .tools import cosd , sind
15
15
16
16
# a dict of required parameter names for each IAM model
17
17
# keys are the function names for the IAM models
@@ -91,21 +91,22 @@ def ashrae(aoi, b=0.05):
91
91
return iam
92
92
93
93
94
- def physical (aoi , n = 1.526 , K = 4. , L = 0.002 ):
94
+ def physical (aoi , n = 1.526 , K = 4.0 , L = 0.002 , * , n_ar = None ):
95
95
r"""
96
96
Determine the incidence angle modifier using refractive index ``n``,
97
- extinction coefficient ``K``, and glazing thickness ``L``.
97
+ extinction coefficient ``K``, glazing thickness ``L`` and refractive
98
+ index ``n_ar`` of an optional anti-reflective coating.
98
99
99
100
``iam.physical`` calculates the incidence angle modifier as described in
100
- [1]_, Section 3. The calculation is based on a physical model of absorbtion
101
+ [1]_, Section 3, with additional support of an anti-reflective coating.
102
+ The calculation is based on a physical model of reflections, absorption,
101
103
and transmission through a transparent cover.
102
104
103
105
Parameters
104
106
----------
105
107
aoi : numeric
106
108
The angle of incidence between the module normal vector and the
107
- sun-beam vector in degrees. Angles of 0 are replaced with 1e-06
108
- to ensure non-nan results. Angles of nan will result in nan.
109
+ sun-beam vector in degrees. Angles of nan will result in nan.
109
110
110
111
n : numeric, default 1.526
111
112
The effective index of refraction (unitless). Reference [1]_
@@ -121,6 +122,11 @@ def physical(aoi, n=1.526, K=4., L=0.002):
121
122
indicates that 0.002 meters (2 mm) is reasonable for most
122
123
glass-covered PV panels.
123
124
125
+ n_ar : numeric, optional
126
+ The effective index of refraction of the anti-reflective (AR) coating
127
+ (unitless). If n_ar is None (default), no AR coating is applied.
128
+ A typical value for the effective index of an AR coating is 1.29.
129
+
124
130
Returns
125
131
-------
126
132
iam : numeric
@@ -149,48 +155,65 @@ def physical(aoi, n=1.526, K=4., L=0.002):
149
155
pvlib.iam.interp
150
156
pvlib.iam.sapm
151
157
"""
152
- zeroang = 1e-06
153
-
154
- # hold a new reference to the input aoi object since we're going to
155
- # overwrite the aoi reference below, but we'll need it for the
156
- # series check at the end of the function
157
- aoi_input = aoi
158
-
159
- aoi = np .where (aoi == 0 , zeroang , aoi )
160
-
161
- # angle of reflection
162
- thetar_deg = asind (1.0 / n * (sind (aoi )))
163
-
164
- # reflectance and transmittance for normal incidence light
165
- rho_zero = ((1 - n ) / (1 + n )) ** 2
166
- tau_zero = np .exp (- K * L )
167
-
168
- # reflectance for parallel and perpendicular polarized light
169
- rho_para = (tand (thetar_deg - aoi ) / tand (thetar_deg + aoi )) ** 2
170
- rho_perp = (sind (thetar_deg - aoi ) / sind (thetar_deg + aoi )) ** 2
171
-
172
- # transmittance for non-normal light
173
- tau = np .exp (- K * L / cosd (thetar_deg ))
174
-
175
- # iam is ratio of non-normal to normal incidence transmitted light
176
- # after deducting the reflected portion of each
177
- iam = ((1 - (rho_para + rho_perp ) / 2 ) / (1 - rho_zero ) * tau / tau_zero )
178
-
179
- with np .errstate (invalid = 'ignore' ):
180
- # angles near zero produce nan, but iam is defined as one
181
- small_angle = 1e-06
182
- iam = np .where (np .abs (aoi ) < small_angle , 1.0 , iam )
183
-
184
- # angles at 90 degrees can produce tiny negative values,
185
- # which should be zero. this is a result of calculation precision
186
- # rather than the physical model
187
- iam = np .where (iam < 0 , 0 , iam )
188
-
189
- # for light coming from behind the plane, none can enter the module
190
- iam = np .where (aoi > 90 , 0 , iam )
191
-
192
- if isinstance (aoi_input , pd .Series ):
193
- iam = pd .Series (iam , index = aoi_input .index )
158
+ n1 , n3 = 1 , n
159
+ if n_ar is None or np .allclose (n_ar , n1 ):
160
+ # no AR coating
161
+ n2 = n
162
+ else :
163
+ n2 = n_ar
164
+
165
+ # incidence angle
166
+ costheta = np .maximum (0 , cosd (aoi )) # always >= 0
167
+ sintheta = np .sqrt (1 - costheta ** 2 ) # always >= 0
168
+ n1costheta1 = n1 * costheta
169
+ n2costheta1 = n2 * costheta
170
+
171
+ # refraction angle of first interface
172
+ sintheta = n1 / n2 * sintheta
173
+ costheta = np .sqrt (1 - sintheta ** 2 )
174
+ n1costheta2 = n1 * costheta
175
+ n2costheta2 = n2 * costheta
176
+
177
+ # reflectance of s-, p-polarized, and normal light by the first interface
178
+ rho12_s = ((n1costheta1 - n2costheta2 ) / (n1costheta1 + n2costheta2 )) ** 2
179
+ rho12_p = ((n1costheta2 - n2costheta1 ) / (n1costheta2 + n2costheta1 )) ** 2
180
+ rho12_0 = ((n1 - n2 ) / (n1 + n2 )) ** 2
181
+
182
+ # transmittance through the first interface
183
+ tau_s = 1 - rho12_s
184
+ tau_p = 1 - rho12_p
185
+ tau_0 = 1 - rho12_0
186
+
187
+ if not np .allclose (n3 , n2 ): # AR coated glass
188
+ n3costheta2 = n3 * costheta
189
+ # refraction angle of second interface
190
+ sintheta = n2 / n3 * sintheta
191
+ costheta = np .sqrt (1 - sintheta ** 2 )
192
+ n2costheta3 = n2 * costheta
193
+ n3costheta3 = n3 * costheta
194
+
195
+ # reflectance by the second interface
196
+ rho23_s = (
197
+ (n2costheta2 - n3costheta3 ) / (n2costheta2 + n3costheta3 )
198
+ ) ** 2
199
+ rho23_p = (
200
+ (n2costheta3 - n3costheta2 ) / (n2costheta3 + n3costheta2 )
201
+ ) ** 2
202
+ rho23_0 = ((n2 - n3 ) / (n2 + n3 )) ** 2
203
+
204
+ # transmittance through the coating, including internal reflections
205
+ # 1 + rho23*rho12 + (rho23*rho12)^2 + ... = 1/(1 - rho23*rho12)
206
+ tau_s *= (1 - rho23_s ) / (1 - rho23_s * rho12_s )
207
+ tau_p *= (1 - rho23_p ) / (1 - rho23_p * rho12_p )
208
+ tau_0 *= (1 - rho23_0 ) / (1 - rho23_0 * rho12_0 )
209
+
210
+ # transmittance after absorption in the glass
211
+ tau_s *= np .exp (- K * L / costheta )
212
+ tau_p *= np .exp (- K * L / costheta )
213
+ tau_0 *= np .exp (- K * L )
214
+
215
+ # incidence angle modifier
216
+ iam = (tau_s + tau_p ) / 2 / tau_0
194
217
195
218
return iam
196
219
0 commit comments