|
17 | 17 | import pandas as pd
|
18 | 18 |
|
19 | 19 | from pvlib import atmosphere, irradiance, tools, singlediode as _singlediode
|
20 |
| -from pvlib.tools import _build_kwargs |
| 20 | +from pvlib.tools import _build_kwargs, cosd |
21 | 21 | from pvlib.location import Location
|
22 | 22 |
|
23 | 23 |
|
@@ -946,7 +946,7 @@ def ashraeiam(aoi, b=0.05):
|
946 | 946 | iam = np.where(aoi_gte_90, 0, iam)
|
947 | 947 | iam = np.maximum(0, iam)
|
948 | 948 |
|
949 |
| - if isinstance(iam, pd.Series): |
| 949 | + if isinstance(aoi, pd.Series): |
950 | 950 | iam = pd.Series(iam, index=aoi.index)
|
951 | 951 |
|
952 | 952 | return iam
|
@@ -1064,6 +1064,161 @@ def physicaliam(aoi, n=1.526, K=4., L=0.002):
|
1064 | 1064 | return iam
|
1065 | 1065 |
|
1066 | 1066 |
|
| 1067 | +def iam_martin_ruiz(aoi, a_r=0.16): |
| 1068 | + ''' |
| 1069 | + Determine the incidence angle modifier (iam) using the Martin |
| 1070 | + and Ruiz incident angle model. |
| 1071 | +
|
| 1072 | + Parameters |
| 1073 | + ---------- |
| 1074 | + aoi : numeric, degrees |
| 1075 | + The angle of incidence between the module normal vector and the |
| 1076 | + sun-beam vector in degrees. Theta must be a numeric scalar or vector. |
| 1077 | + iam is 0 where |aoi| > 90. |
| 1078 | +
|
| 1079 | + a_r : numeric |
| 1080 | + The angular losses coefficient described in equation 3 of [1]. |
| 1081 | + This is an empirical dimensionless parameter. Values of a_r are |
| 1082 | + generally on the order of 0.08 to 0.25 for flat-plate PV modules. |
| 1083 | + a_r must be a positive numeric scalar or vector (same length as aoi). |
| 1084 | +
|
| 1085 | + Returns |
| 1086 | + ------- |
| 1087 | + iam : numeric |
| 1088 | + The incident angle modifier(s) |
| 1089 | +
|
| 1090 | + Notes |
| 1091 | + ----- |
| 1092 | + iam_martin_ruiz calculates the incidence angle modifier (iam) |
| 1093 | + as described by Martin and Ruiz in [1]. The information |
| 1094 | + required is the incident angle (aoi) and the angular losses |
| 1095 | + coefficient (a_r). Please note that [1] has a corrigendum which makes |
| 1096 | + the document much simpler to understand. |
| 1097 | +
|
| 1098 | + The incident angle modifier is defined as |
| 1099 | + [1-exp(-cos(aoi/ar))] / [1-exp(-1/ar)], which is |
| 1100 | + presented as AL(alpha) = 1 - IAM in equation 4 of [1]. Thus IAM is |
| 1101 | + equal to 1 at aoi = 0, and equal to 0 at aoi = 90. This equation is only |
| 1102 | + valid for -90 <= aoi <= 90, therefore iam must be constrained to 0.0 |
| 1103 | + beyond this range. |
| 1104 | +
|
| 1105 | + References |
| 1106 | + ---------- |
| 1107 | + [1] N. Martin and J. M. Ruiz, "Calculation of the PV modules angular |
| 1108 | + losses under field conditions by means of an analytical model", Solar |
| 1109 | + Energy Materials & Solar Cells, vol. 70, pp. 25-38, 2001. |
| 1110 | +
|
| 1111 | + [2] N. Martin and J. M. Ruiz, "Corrigendum to 'Calculation of the PV |
| 1112 | + modules angular losses under field conditions by means of an |
| 1113 | + analytical model'", Solar Energy Materials & Solar Cells, vol. 110, |
| 1114 | + pp. 154, 2013. |
| 1115 | +
|
| 1116 | + See Also |
| 1117 | + -------- |
| 1118 | + physicaliam |
| 1119 | + ashraeiam |
| 1120 | + iam_interp |
| 1121 | + ''' |
| 1122 | + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019 |
| 1123 | + |
| 1124 | + aoi_input = aoi |
| 1125 | + |
| 1126 | + aoi = np.asanyarray(aoi) |
| 1127 | + a_r = np.asanyarray(a_r) |
| 1128 | + |
| 1129 | + if np.any(np.less_equal(a_r, 0)): |
| 1130 | + raise RuntimeError("The parameter 'a_r' cannot be zero or negative.") |
| 1131 | + |
| 1132 | + with np.errstate(invalid='ignore'): |
| 1133 | + iam = (1 - np.exp(-cosd(aoi) / a_r)) / (1 - np.exp(-1 / a_r)) |
| 1134 | + iam = np.where(np.abs(aoi) >= 90.0, 0.0, iam) |
| 1135 | + |
| 1136 | + if isinstance(aoi_input, pd.Series): |
| 1137 | + iam = pd.Series(iam, index=aoi_input.index) |
| 1138 | + |
| 1139 | + return iam |
| 1140 | + |
| 1141 | + |
| 1142 | +def iam_interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): |
| 1143 | + ''' |
| 1144 | + Determine the incidence angle modifier (iam) by interpolating a set of |
| 1145 | + reference values, which are usually measured values. |
| 1146 | +
|
| 1147 | + Parameters |
| 1148 | + ---------- |
| 1149 | + aoi : numeric, degrees |
| 1150 | + The angle of incidence between the module normal vector and the |
| 1151 | + sun-beam vector in degrees. |
| 1152 | +
|
| 1153 | + theta_ref : numeric, degrees |
| 1154 | + Vector of angles at which the iam is known. |
| 1155 | +
|
| 1156 | + iam_ref : numeric, unitless |
| 1157 | + iam values for each angle in theta_ref. |
| 1158 | +
|
| 1159 | + method : str, default 'linear' |
| 1160 | + Specifies the interpolation method. |
| 1161 | + Useful options are: 'linear', 'quadratic','cubic'. |
| 1162 | + See scipy.interpolate.interp1d for more options. |
| 1163 | +
|
| 1164 | + normalize : boolean |
| 1165 | + When true, the interpolated values are divided by the interpolated |
| 1166 | + value at zero degrees. This ensures that the iam at normal |
| 1167 | + incidence is equal to 1.0. |
| 1168 | +
|
| 1169 | + Returns |
| 1170 | + ------- |
| 1171 | + iam : numeric |
| 1172 | + The incident angle modifier(s) |
| 1173 | +
|
| 1174 | + Notes: |
| 1175 | + ------ |
| 1176 | + theta_ref must have two or more points and may span any range of angles. |
| 1177 | + Typically there will be a dozen or more points in the range 0-90 degrees. |
| 1178 | + iam beyond the range of theta_ref are extrapolated, but constrained to be |
| 1179 | + non-negative. |
| 1180 | +
|
| 1181 | + The sign of aoi is ignored; only the magnitude is used. |
| 1182 | +
|
| 1183 | + See Also |
| 1184 | + -------- |
| 1185 | + physicaliam |
| 1186 | + ashraeiam |
| 1187 | + iam_martin_ruiz |
| 1188 | + ''' |
| 1189 | + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. July, 2019 |
| 1190 | + |
| 1191 | + from scipy.interpolate import interp1d |
| 1192 | + |
| 1193 | + # Scipy doesn't give the clearest feedback, so check number of points here. |
| 1194 | + MIN_REF_VALS = {'linear': 2, 'quadratic': 3, 'cubic': 4, 1: 2, 2: 3, 3: 4} |
| 1195 | + |
| 1196 | + if len(theta_ref) < MIN_REF_VALS.get(method, 2): |
| 1197 | + raise ValueError("Too few reference points defined " |
| 1198 | + "for interpolation method '%s'." % method) |
| 1199 | + |
| 1200 | + if np.any(np.less(iam_ref, 0)): |
| 1201 | + raise ValueError("Negative value(s) found in 'iam_ref'. " |
| 1202 | + "This is not physically possible.") |
| 1203 | + |
| 1204 | + interpolator = interp1d(theta_ref, iam_ref, kind=method, |
| 1205 | + fill_value='extrapolate') |
| 1206 | + aoi_input = aoi |
| 1207 | + |
| 1208 | + aoi = np.asanyarray(aoi) |
| 1209 | + aoi = np.abs(aoi) |
| 1210 | + iam = interpolator(aoi) |
| 1211 | + iam = np.clip(iam, 0, None) |
| 1212 | + |
| 1213 | + if normalize: |
| 1214 | + iam /= interpolator(0) |
| 1215 | + |
| 1216 | + if isinstance(aoi_input, pd.Series): |
| 1217 | + iam = pd.Series(iam, index=aoi_input.index) |
| 1218 | + |
| 1219 | + return iam |
| 1220 | + |
| 1221 | + |
1067 | 1222 | def calcparams_desoto(effective_irradiance, temp_cell,
|
1068 | 1223 | alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s,
|
1069 | 1224 | EgRef=1.121, dEgdT=-0.0002677,
|
|
0 commit comments