pvlib 0.9.4a1__py3-none-any.whl → 0.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. pvlib/__init__.py +3 -2
  2. pvlib/atmosphere.py +23 -173
  3. pvlib/bifacial/infinite_sheds.py +88 -277
  4. pvlib/bifacial/utils.py +270 -28
  5. pvlib/data/adr-library-cec-inverters-2019-03-05.csv +5009 -0
  6. pvlib/data/precise_iv_curves1.json +10251 -0
  7. pvlib/data/precise_iv_curves2.json +10251 -0
  8. pvlib/data/precise_iv_curves_parameter_sets1.csv +33 -0
  9. pvlib/data/precise_iv_curves_parameter_sets2.csv +33 -0
  10. pvlib/data/test_psm3_2017.csv +17521 -17521
  11. pvlib/data/test_psm3_2019_5min.csv +288 -288
  12. pvlib/data/test_read_psm3.csv +17522 -17522
  13. pvlib/data/test_read_pvgis_horizon.csv +49 -0
  14. pvlib/data/variables_style_rules.csv +3 -0
  15. pvlib/iam.py +207 -51
  16. pvlib/inverter.py +6 -1
  17. pvlib/iotools/__init__.py +7 -2
  18. pvlib/iotools/acis.py +516 -0
  19. pvlib/iotools/midc.py +4 -4
  20. pvlib/iotools/psm3.py +59 -42
  21. pvlib/iotools/pvgis.py +84 -28
  22. pvlib/iotools/sodapro.py +8 -6
  23. pvlib/iotools/srml.py +121 -18
  24. pvlib/iotools/surfrad.py +2 -2
  25. pvlib/iotools/tmy.py +146 -102
  26. pvlib/irradiance.py +270 -15
  27. pvlib/ivtools/sde.py +14 -20
  28. pvlib/ivtools/sdm.py +31 -20
  29. pvlib/ivtools/utils.py +127 -6
  30. pvlib/location.py +3 -2
  31. pvlib/modelchain.py +67 -70
  32. pvlib/pvarray.py +225 -0
  33. pvlib/pvsystem.py +169 -539
  34. pvlib/shading.py +43 -2
  35. pvlib/singlediode.py +216 -66
  36. pvlib/snow.py +36 -15
  37. pvlib/soiling.py +3 -3
  38. pvlib/spa.py +327 -368
  39. pvlib/spectrum/__init__.py +8 -2
  40. pvlib/spectrum/mismatch.py +335 -0
  41. pvlib/temperature.py +124 -13
  42. pvlib/tests/bifacial/test_infinite_sheds.py +44 -106
  43. pvlib/tests/bifacial/test_utils.py +102 -5
  44. pvlib/tests/conftest.py +0 -31
  45. pvlib/tests/iotools/test_acis.py +213 -0
  46. pvlib/tests/iotools/test_midc.py +6 -6
  47. pvlib/tests/iotools/test_psm3.py +7 -5
  48. pvlib/tests/iotools/test_pvgis.py +21 -14
  49. pvlib/tests/iotools/test_sodapro.py +1 -1
  50. pvlib/tests/iotools/test_srml.py +71 -6
  51. pvlib/tests/iotools/test_tmy.py +43 -8
  52. pvlib/tests/ivtools/test_sde.py +19 -17
  53. pvlib/tests/ivtools/test_sdm.py +9 -4
  54. pvlib/tests/ivtools/test_utils.py +96 -1
  55. pvlib/tests/test_atmosphere.py +8 -64
  56. pvlib/tests/test_clearsky.py +0 -1
  57. pvlib/tests/test_iam.py +74 -1
  58. pvlib/tests/test_irradiance.py +56 -2
  59. pvlib/tests/test_location.py +1 -1
  60. pvlib/tests/test_modelchain.py +33 -76
  61. pvlib/tests/test_pvarray.py +46 -0
  62. pvlib/tests/test_pvsystem.py +366 -201
  63. pvlib/tests/test_shading.py +35 -0
  64. pvlib/tests/test_singlediode.py +306 -29
  65. pvlib/tests/test_snow.py +84 -1
  66. pvlib/tests/test_soiling.py +8 -7
  67. pvlib/tests/test_solarposition.py +7 -7
  68. pvlib/tests/test_spa.py +6 -7
  69. pvlib/tests/test_spectrum.py +145 -1
  70. pvlib/tests/test_temperature.py +29 -11
  71. pvlib/tests/test_tools.py +41 -0
  72. pvlib/tests/test_tracking.py +0 -149
  73. pvlib/tools.py +49 -25
  74. pvlib/tracking.py +1 -269
  75. pvlib-0.10.0.dist-info/AUTHORS.md +35 -0
  76. {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/LICENSE +5 -2
  77. {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/METADATA +3 -13
  78. {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/RECORD +80 -75
  79. {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/WHEEL +1 -1
  80. pvlib/data/adr-library-2013-10-01.csv +0 -1762
  81. pvlib/forecast.py +0 -1211
  82. pvlib/iotools/ecmwf_macc.py +0 -312
  83. pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
  84. pvlib/tests/test_forecast.py +0 -228
  85. pvlib-0.9.4a1.dist-info/AUTHORS.md +0 -32
  86. {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/top_level.txt +0 -0
pvlib/ivtools/sde.py CHANGED
@@ -10,16 +10,6 @@ import numpy as np
10
10
  from pvlib.ivtools.utils import _schumaker_qspline
11
11
 
12
12
 
13
- # set constant for numpy.linalg.lstsq parameter rcond
14
- # rcond=-1 for numpy<1.14, rcond=None for numpy>=1.14
15
- # TODO remove after minimum numpy version >= 1.14
16
- minor = int(np.__version__.split('.')[1])
17
- if minor < 14:
18
- RCOND = -1
19
- else:
20
- RCOND = None
21
-
22
-
23
13
  def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
24
14
  vlim=0.2, ilim=0.1):
25
15
  r"""
@@ -74,7 +64,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
74
64
 
75
65
  Raises
76
66
  ------
77
- RuntimeError if parameter extraction is not successful.
67
+ RuntimeError
68
+ if parameter extraction is not successful.
78
69
 
79
70
  Notes
80
71
  -----
@@ -116,7 +107,7 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
116
107
  .. math::
117
108
 
118
109
  I &\approx \frac{I_{L}}{1 + G_{p} R_{s}}
119
- - \frac{G_{p}}{1 + G_{p}R_{s}} V
110
+ - \frac{G_{p}}{1 + G_{p}R_{s}} V \\
120
111
  &= \beta_{0} + \beta_{1} V
121
112
 
122
113
  4. The exponential portion of the IV curve is defined by
@@ -134,8 +125,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
134
125
  .. math::
135
126
 
136
127
  \log(\beta_{0} - \beta_{1} V - I)
137
- &\approx \log(\frac{I_{0}}{1 + G_{p} R_{s}} + \frac{V}{nN_sV_{th}}
138
- + \frac{I R_{s}}{nN_sV_{th}}) \\
128
+ &\approx \log(\frac{I_{0}}{1 + G_{p} R_{s}}) + \frac{V}{nN_sV_{th}}
129
+ + \frac{I R_{s}}{nN_sV_{th}} \\
139
130
  &= \beta_{2} + \beta_{3} V + \beta_{4} I
140
131
 
141
132
  6. Calculate values for ``IL, I0, Rs, Rsh,`` and ``nNsVth`` from the
@@ -149,7 +140,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
149
140
  0 86758 909 4
150
141
  .. [2] C. B. Jones, C. W. Hansen, "Single Diode Parameter Extraction from
151
142
  In-Field Photovoltaic I-V Curves on a Single Board Computer", 46th IEEE
152
- Photovoltaic Specialist Conference, Chicago, IL, 2019
143
+ Photovoltaic Specialist Conference, Chicago, IL, 2019.
144
+ :doi:`10.1109/PVSC40753.2019.8981330`
153
145
  """
154
146
 
155
147
  # If not provided, extract v_oc, i_sc, v_mp and i_mp from the IV curve data
@@ -228,7 +220,7 @@ def _sandia_beta3_beta4(voltage, current, beta0, beta1, ilim, i_sc):
228
220
  x = np.array([np.ones_like(voltage), voltage, current]).T
229
221
  # Select points where y > ilim * i_sc to regress log(y) onto x
230
222
  idx = (y > ilim * i_sc)
231
- result = np.linalg.lstsq(x[idx], np.log(y[idx]), rcond=RCOND)
223
+ result = np.linalg.lstsq(x[idx], np.log(y[idx]), rcond=None)
232
224
  coef = result[0]
233
225
  beta3 = coef[1].item()
234
226
  beta4 = coef[2].item()
@@ -299,8 +291,10 @@ def _fit_sandia_cocontent(voltage, current, nsvth):
299
291
 
300
292
  Raises
301
293
  ------
302
- ValueError if ``voltage`` and ``current`` are different lengths.
303
- ValueError if ``len(voltage)`` < 6
294
+ ValueError
295
+ if ``voltage`` and ``current`` are different lengths.
296
+ ValueError
297
+ if ``len(voltage)`` < 6
304
298
 
305
299
  Notes
306
300
  -----
@@ -444,7 +438,7 @@ def _cocontent_regress(v, i, voc, isc, cci):
444
438
  sx = np.vstack((s[:, 0], s[:, 1], s[:, 0] * s[:, 1], s[:, 0] * s[:, 0],
445
439
  s[:, 1] * s[:, 1], col1)).T
446
440
 
447
- gamma = np.linalg.lstsq(sx, scc, rcond=RCOND)[0]
441
+ gamma = np.linalg.lstsq(sx, scc, rcond=None)[0]
448
442
  # coefficients from regression in rotated coordinates
449
443
 
450
444
  # Principle components transformation steps
@@ -473,5 +467,5 @@ def _cocontent_regress(v, i, voc, isc, cci):
473
467
 
474
468
  # translate from coefficients in rotated space (gamma) to coefficients in
475
469
  # original coordinates (beta)
476
- beta = np.linalg.lstsq(np.dot(mb, ma), gamma[0:5], rcond=RCOND)[0]
470
+ beta = np.linalg.lstsq(np.dot(mb, ma), gamma[0:5], rcond=None)[0]
477
471
  return beta
pvlib/ivtools/sdm.py CHANGED
@@ -8,10 +8,9 @@ Function names should follow the pattern "fit_" + name of model + "_" +
8
8
 
9
9
  import numpy as np
10
10
 
11
- import scipy.constants
11
+ from scipy import constants
12
12
  from scipy import optimize
13
13
  from scipy.special import lambertw
14
- from scipy.misc import derivative
15
14
 
16
15
  from pvlib.pvsystem import calcparams_pvsyst, singlediode, v_from_i
17
16
  from pvlib.singlediode import bishop88_mpp
@@ -19,6 +18,11 @@ from pvlib.singlediode import bishop88_mpp
19
18
  from pvlib.ivtools.utils import rectify_iv_curve, _numdiff
20
19
  from pvlib.ivtools.sde import _fit_sandia_cocontent
21
20
 
21
+ from pvlib.tools import _first_order_centered_difference
22
+
23
+
24
+ CONSTANTS = {'E0': 1000.0, 'T0': 25.0, 'k': constants.k, 'q': constants.e}
25
+
22
26
 
23
27
  def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
24
28
  gamma_pmp, cells_in_series, temp_ref=25):
@@ -72,9 +76,10 @@ def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
72
76
 
73
77
  Raises
74
78
  ------
75
- ImportError if NREL-PySAM is not installed.
76
-
77
- RuntimeError if parameter extraction is not successful.
79
+ ImportError
80
+ if NREL-PySAM is not installed.
81
+ RuntimeError
82
+ if parameter extraction is not successful.
78
83
 
79
84
  Notes
80
85
  -----
@@ -90,7 +95,7 @@ def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
90
95
  ----------
91
96
  .. [1] A. Dobos, "An Improved Coefficient Calculator for the California
92
97
  Energy Commission 6 Parameter Photovoltaic Module Model", Journal of
93
- Solar Energy Engineering, vol 134, 2012.
98
+ Solar Energy Engineering, vol 134, 2012. :doi:`10.1115/1.4005759`
94
99
  """
95
100
 
96
101
  try:
@@ -200,11 +205,11 @@ def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series,
200
205
  ----------
201
206
  .. [1] W. De Soto et al., "Improvement and validation of a model for
202
207
  photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
203
- 2006.
208
+ 2006. :doi:`10.1016/j.solener.2005.06.010`
204
209
  """
205
210
 
206
211
  # Constants
207
- k = scipy.constants.value('Boltzmann constant in eV/K')
212
+ k = constants.value('Boltzmann constant in eV/K') # in eV/K
208
213
  Tref = temp_ref + 273.15 # [K]
209
214
 
210
215
  # initial guesses of variables for computing convergence:
@@ -340,9 +345,9 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
340
345
  T0 : float
341
346
  cell temperature at STC, default 25 [C]
342
347
  k : float
343
- 1.38066E-23 J/K (Boltzmann's constant)
348
+ Boltzmann's constant [J/K]
344
349
  q : float
345
- 1.60218E-19 Coulomb (elementary charge)
350
+ elementary charge [Coulomb]
346
351
 
347
352
  maxiter : int, default 5
348
353
  input that sets the maximum number of iterations for the parameter
@@ -401,6 +406,7 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
401
406
  .. [1] K. Sauer, T. Roessler, C. W. Hansen, Modeling the Irradiance and
402
407
  Temperature Dependence of Photovoltaic Modules in PVsyst, IEEE Journal
403
408
  of Photovoltaics v5(1), January 2015.
409
+ :doi:`10.1109/JPHOTOV.2014.2364133`
404
410
  .. [2] A. Mermoud, PV Modules modeling, Presentation at the 2nd PV
405
411
  Performance Modeling Workshop, Santa Clara, CA, May 2013
406
412
  .. [3] A. Mermoud, T. Lejeuene, Performance Assessment of a Simulation
@@ -408,16 +414,18 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
408
414
  Photovoltaic Solar Energy Conference, Valencia, Spain, Sept. 2010
409
415
  .. [4] C. Hansen, Estimating Parameters for the PVsyst Version 6
410
416
  Photovoltaic Module Performance Model, Sandia National Laboratories
411
- Report SAND2015-8598
417
+ Report SAND2015-8598. :doi:`10.2172/1223058`
412
418
  .. [5] C. Hansen, Parameter Estimation for Single Diode Models of
413
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
419
+ Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
420
+ :doi:`10.2172/1177157`
414
421
  .. [6] C. Hansen, Estimation of Parameters for Single Diode Models using
415
422
  Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
423
+ :doi:`10.1109/PVSC.2013.6744135`
416
424
  .. [7] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
417
425
  """
418
426
 
419
427
  if const is None:
420
- const = {'E0': 1000.0, 'T0': 25.0, 'k': 1.38066e-23, 'q': 1.60218e-19}
428
+ const = CONSTANTS
421
429
 
422
430
  ee = ivcurves['ee']
423
431
  tc = ivcurves['tc']
@@ -520,9 +528,9 @@ def fit_desoto_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
520
528
  T0 : float
521
529
  cell temperature at STC, default 25 [C]
522
530
  k : float
523
- 1.38066E-23 J/K (Boltzmann's constant)
531
+ Boltzmann's constant [J/K]
524
532
  q : float
525
- 1.60218E-19 Coulomb (elementary charge)
533
+ elementary charge [Coulomb]
526
534
 
527
535
  maxiter : int, default 5
528
536
  input that sets the maximum number of iterations for the parameter
@@ -570,16 +578,18 @@ def fit_desoto_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
570
578
  ----------
571
579
  .. [1] W. De Soto et al., "Improvement and validation of a model for
572
580
  photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
573
- 2006.
581
+ 2006. :doi:`10.1016/j.solener.2005.06.010`
574
582
  .. [2] C. Hansen, Parameter Estimation for Single Diode Models of
575
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
583
+ Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
584
+ :doi:`10.2172/1177157`
576
585
  .. [3] C. Hansen, Estimation of Parameters for Single Diode Models using
577
586
  Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
587
+ :doi:`10.1109/PVSC.2013.6744135`
578
588
  .. [4] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
579
589
  """
580
590
 
581
591
  if const is None:
582
- const = {'E0': 1000.0, 'T0': 25.0, 'k': 1.38066e-23, 'q': 1.60218e-19}
592
+ const = CONSTANTS
583
593
 
584
594
  ee = ivcurves['ee']
585
595
  tc = ivcurves['tc']
@@ -938,7 +948,7 @@ def _update_io(voc, iph, io, rs, rsh, nnsvth):
938
948
 
939
949
  while maxerr > eps and k < niter:
940
950
  # Predict Voc
941
- pvoc = v_from_i(rsh, rs, nnsvth, 0., tio, iph)
951
+ pvoc = v_from_i(0., iph, tio, rs, rsh, nnsvth)
942
952
 
943
953
  # Difference in Voc
944
954
  dvoc = pvoc - voc
@@ -1341,5 +1351,6 @@ def pvsyst_temperature_coeff(alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref,
1341
1351
  I_o_ref, R_sh_ref, R_sh_0, R_s, cells_in_series, R_sh_exp, EgRef,
1342
1352
  temp_ref)
1343
1353
  pmp = maxp(temp_ref, *args)
1344
- gamma_pdc = derivative(maxp, temp_ref, args=args)
1354
+ gamma_pdc = _first_order_centered_difference(maxp, x0=temp_ref, args=args)
1355
+
1345
1356
  return gamma_pdc / pmp
pvlib/ivtools/utils.py CHANGED
@@ -6,6 +6,7 @@ working with IV curves, or fitting equations to IV curve data.
6
6
 
7
7
  import numpy as np
8
8
  import pandas as pd
9
+ from numpy.polynomial.polynomial import Polynomial as Poly
9
10
 
10
11
 
11
12
  # A small number used to decide when a slope is equivalent to zero
@@ -149,12 +150,12 @@ def rectify_iv_curve(voltage, current, decimals=None):
149
150
  ``rectify_iv_curve`` ensures that the IV curve lies in the first quadrant
150
151
  of the (voltage, current) plane. The returned IV curve:
151
152
 
152
- * increases in voltage
153
- * contains no negative current or voltage values
154
- * contains no NaNs
155
- * contains no points with duplicate voltage values. Where voltage
156
- values are repeated, a single data point is substituted with current
157
- equal to the average of current at duplicated voltages.
153
+ * increases in voltage
154
+ * contains no negative current or voltage values
155
+ * contains no NaNs
156
+ * contains no points with duplicate voltage values. Where voltage
157
+ values are repeated, a single data point is substituted with current
158
+ equal to the average of current at duplicated voltages.
158
159
  """
159
160
 
160
161
  df = pd.DataFrame(data=np.vstack((voltage, current)).T, columns=['v', 'i'])
@@ -423,3 +424,123 @@ def _schumaker_qspline(x, y):
423
424
  yhat = tmp2[:, 4]
424
425
  kflag = tmp2[:, 5]
425
426
  return t, c, yhat, kflag
427
+
428
+
429
+ def astm_e1036(v, i, imax_limits=(0.75, 1.15), vmax_limits=(0.75, 1.15),
430
+ voc_points=3, isc_points=3, mp_fit_order=4):
431
+ '''
432
+ Extract photovoltaic IV parameters according to ASTM E1036. Assumes that
433
+ the power producing portion of the curve is in the first quadrant.
434
+
435
+ Parameters
436
+ ----------
437
+ v : array-like
438
+ Voltage points
439
+ i : array-like
440
+ Current points
441
+ imax_limits : tuple, default (0.75, 1.15)
442
+ Two-element tuple (low, high) specifying the fraction of estimated
443
+ Imp within which to fit a polynomial for max power calculation
444
+ vmax_limits : tuple, default (0.75, 1.15)
445
+ Two-element tuple (low, high) specifying the fraction of estimated
446
+ Vmp within which to fit a polynomial for max power calculation
447
+ voc_points : int, default 3
448
+ The number of points near open circuit to use for linear fit
449
+ and Voc calculation
450
+ isc_points : int, default 3
451
+ The number of points near short circuit to use for linear fit and
452
+ Isc calculation
453
+ mp_fit_order : int, default 4
454
+ The order of the polynomial fit of power vs. voltage near maximum
455
+ power
456
+
457
+
458
+ Returns
459
+ -------
460
+ dict
461
+ Results. The IV parameters are given by the keys 'voc', 'isc',
462
+ 'vmp', 'imp', 'pmp', and 'ff'. The key 'mp_fit' gives the numpy
463
+ Polynomial object for the fit of power vs voltage near maximum
464
+ power.
465
+
466
+ References
467
+ ----------
468
+ .. [1] Standard Test Methods for Electrical Performance of Nonconcentrator
469
+ Terrestrial Photovoltaic Modules and Arrays Using Reference Cells,
470
+ ASTM E1036-15(2019), :doi:`10.1520/E1036-15R19`
471
+ '''
472
+
473
+ # Adapted from https://github.com/NREL/iv_params
474
+ # Copyright (c) 2022, Alliance for Sustainable Energy, LLC
475
+ # All rights reserved.
476
+
477
+ df = pd.DataFrame()
478
+ df['v'] = v
479
+ df['i'] = i
480
+ df['p'] = df['v'] * df['i']
481
+
482
+ # determine if we can use voc and isc estimates
483
+ i_min_ind = df['i'].abs().idxmin()
484
+ v_min_ind = df['v'].abs().idxmin()
485
+ voc_est = df['v'][i_min_ind]
486
+ isc_est = df['i'][v_min_ind]
487
+
488
+ # accept the estimates if they are close enough
489
+ # if not, perform a linear fit
490
+ if abs(df['i'][i_min_ind]) <= isc_est * 0.001:
491
+ voc = voc_est
492
+ else:
493
+ df['i_abs'] = df['i'].abs()
494
+ voc_df = df.nsmallest(voc_points, 'i_abs')
495
+ voc_fit = Poly.fit(voc_df['i'], voc_df['v'], 1)
496
+ voc = voc_fit(0)
497
+
498
+ if abs(df['v'][v_min_ind]) <= voc_est * 0.005:
499
+ isc = isc_est
500
+ else:
501
+ df['v_abs'] = df['v'].abs()
502
+ isc_df = df.nsmallest(isc_points, 'v_abs')
503
+ isc_fit = Poly.fit(isc_df['v'], isc_df['i'], 1)
504
+ isc = isc_fit(0)
505
+
506
+ # estimate max power point
507
+ max_index = df['p'].idxmax()
508
+ mp_est = df.loc[max_index]
509
+
510
+ # filter around max power
511
+ mask = (
512
+ (df['i'] >= imax_limits[0] * mp_est['i']) &
513
+ (df['i'] <= imax_limits[1] * mp_est['i']) &
514
+ (df['v'] >= vmax_limits[0] * mp_est['v']) &
515
+ (df['v'] <= vmax_limits[1] * mp_est['v'])
516
+ )
517
+ filtered = df[mask]
518
+
519
+ # fit polynomial and find max
520
+ mp_fit = Poly.fit(filtered['v'], filtered['p'], mp_fit_order)
521
+ # Note that this root finding procedure differs from
522
+ # the suggestion in the standard
523
+ roots = mp_fit.deriv().roots()
524
+ # only consider real roots
525
+ roots = roots.real[abs(roots.imag) < 1e-5]
526
+ # only consider roots in the relevant part of the domain
527
+ roots = roots[(roots < filtered['v'].max()) &
528
+ (roots > filtered['v'].min())]
529
+ vmp = roots[np.argmax(mp_fit(roots))]
530
+ pmp = mp_fit(vmp)
531
+ # Imp isn't mentioned for update in the
532
+ # standard, but this seems to be in the intended spirit
533
+ imp = pmp / vmp
534
+
535
+ ff = pmp / (voc * isc)
536
+
537
+ result = {}
538
+ result['voc'] = voc
539
+ result['isc'] = isc
540
+ result['vmp'] = vmp
541
+ result['imp'] = imp
542
+ result['pmp'] = pmp
543
+ result['ff'] = ff
544
+ result['mp_fit'] = mp_fit
545
+
546
+ return result
pvlib/location.py CHANGED
@@ -331,8 +331,9 @@ class Location:
331
331
  method : str, default 'pyephem'
332
332
  'pyephem', 'spa', or 'geometric'
333
333
 
334
- kwargs are passed to the relevant functions. See
335
- solarposition.sun_rise_set_transit_<method> for details.
334
+ kwargs :
335
+ Passed to the relevant functions. See
336
+ solarposition.sun_rise_set_transit_<method> for details.
336
337
 
337
338
  Returns
338
339
  -------
pvlib/modelchain.py CHANGED
@@ -14,8 +14,7 @@ from dataclasses import dataclass, field
14
14
  from typing import Union, Tuple, Optional, TypeVar
15
15
 
16
16
  from pvlib import (atmosphere, clearsky, inverter, pvsystem, solarposition,
17
- temperature, tools)
18
- from pvlib.tracking import SingleAxisTracker
17
+ temperature)
19
18
  import pvlib.irradiance # avoid name conflict with full import
20
19
  from pvlib.pvsystem import _DC_MODEL_PARAMS
21
20
  from pvlib._deprecation import pvlibDeprecationWarning
@@ -254,6 +253,33 @@ def get_orientation(strategy, **kwargs):
254
253
  return surface_tilt, surface_azimuth
255
254
 
256
255
 
256
+ def _getmcattr(self, attr):
257
+ """
258
+ Helper for __repr__ methods, needed to avoid recursion in property
259
+ lookups
260
+ """
261
+ out = getattr(self, attr)
262
+ try:
263
+ out = out.__name__
264
+ except AttributeError:
265
+ pass
266
+ return out
267
+
268
+
269
+ def _mcr_repr(obj):
270
+ '''
271
+ Helper for ModelChainResult.__repr__
272
+ '''
273
+ if isinstance(obj, tuple):
274
+ return "Tuple (" + ", ".join([_mcr_repr(o) for o in obj]) + ")"
275
+ if isinstance(obj, pd.DataFrame):
276
+ return "DataFrame ({} rows x {} columns)".format(*obj.shape)
277
+ if isinstance(obj, pd.Series):
278
+ return "Series (length {})".format(len(obj))
279
+ # scalar, None, other?
280
+ return repr(obj)
281
+
282
+
257
283
  # Type for fields that vary between arrays
258
284
  T = TypeVar('T')
259
285
 
@@ -385,6 +411,33 @@ class ModelChainResult:
385
411
  value = self._result_type(value)
386
412
  super().__setattr__(key, value)
387
413
 
414
+ def __repr__(self):
415
+ mc_attrs = dir(self)
416
+
417
+ def _head(obj):
418
+ try:
419
+ return obj[:3]
420
+ except:
421
+ return obj
422
+
423
+ if type(self.dc) is tuple:
424
+ num_arrays = len(self.dc)
425
+ else:
426
+ num_arrays = 1
427
+
428
+ desc1 = ('=== ModelChainResult === \n')
429
+ desc2 = (f'Number of Arrays: {num_arrays} \n')
430
+ attr = 'times'
431
+ desc3 = ('times (first 3)\n' +
432
+ f'{_head(_getmcattr(self, attr))}' +
433
+ '\n')
434
+ lines = []
435
+ for attr in mc_attrs:
436
+ if not (attr.startswith('_') or attr=='times'):
437
+ lines.append(f' {attr}: ' + _mcr_repr(getattr(self, attr)))
438
+ desc4 = '\n'.join(lines)
439
+ return (desc1 + desc2 + desc3 + desc4)
440
+
388
441
 
389
442
  class ModelChain:
390
443
  """
@@ -465,13 +518,6 @@ class ModelChain:
465
518
  Name of ModelChain instance.
466
519
  """
467
520
 
468
- # list of deprecated attributes
469
- _deprecated_attrs = ['solar_position', 'airmass', 'total_irrad',
470
- 'aoi', 'aoi_modifier', 'spectral_modifier',
471
- 'cell_temperature', 'effective_irradiance',
472
- 'dc', 'ac', 'diode_params', 'tracking',
473
- 'weather', 'times', 'losses']
474
-
475
521
  def __init__(self, system, location,
476
522
  clearsky_model='ineichen',
477
523
  transposition_model='haydavies',
@@ -503,26 +549,6 @@ class ModelChain:
503
549
 
504
550
  self.results = ModelChainResult()
505
551
 
506
- def __getattr__(self, key):
507
- if key in ModelChain._deprecated_attrs:
508
- msg = f'ModelChain.{key} is deprecated and will' \
509
- f' be removed in v0.10. Use' \
510
- f' ModelChain.results.{key} instead'
511
- warnings.warn(msg, pvlibDeprecationWarning)
512
- return getattr(self.results, key)
513
- # __getattr__ is only called if __getattribute__ fails.
514
- # In that case we should check if key is a deprecated attribute,
515
- # and fail with an AttributeError if it is not.
516
- raise AttributeError
517
-
518
- def __setattr__(self, key, value):
519
- if key in ModelChain._deprecated_attrs:
520
- msg = f'ModelChain.{key} is deprecated from v0.9. Use' \
521
- f' ModelChain.results.{key} instead'
522
- warnings.warn(msg, pvlibDeprecationWarning)
523
- setattr(self.results, key, value)
524
- else:
525
- super().__setattr__(key, value)
526
552
 
527
553
  @classmethod
528
554
  def with_pvwatts(cls, system, location,
@@ -560,7 +586,7 @@ class ModelChain:
560
586
  Examples
561
587
  --------
562
588
  >>> module_parameters = dict(gamma_pdc=-0.003, pdc0=4500)
563
- >>> inverter_parameters = dict(pac0=4000)
589
+ >>> inverter_parameters = dict(pdc0=4000)
564
590
  >>> tparams = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
565
591
  >>> system = PVSystem(surface_tilt=30, surface_azimuth=180,
566
592
  ... module_parameters=module_parameters,
@@ -678,18 +704,8 @@ class ModelChain:
678
704
  'airmass_model', 'dc_model', 'ac_model', 'aoi_model',
679
705
  'spectral_model', 'temperature_model', 'losses_model'
680
706
  ]
681
-
682
- def getmcattr(self, attr):
683
- """needed to avoid recursion in property lookups"""
684
- out = getattr(self, attr)
685
- try:
686
- out = out.__name__
687
- except AttributeError:
688
- pass
689
- return out
690
-
691
707
  return ('ModelChain: \n ' + '\n '.join(
692
- f'{attr}: {getmcattr(self, attr)}' for attr in attrs))
708
+ f'{attr}: {_getmcattr(self, attr)}' for attr in attrs))
693
709
 
694
710
  @property
695
711
  def dc_model(self):
@@ -1535,27 +1551,11 @@ class ModelChain:
1535
1551
  self._prep_inputs_solar_pos(weather)
1536
1552
  self._prep_inputs_airmass()
1537
1553
  self._prep_inputs_albedo(weather)
1554
+ self._prep_inputs_fixed()
1538
1555
 
1539
- # PVSystem.get_irradiance and SingleAxisTracker.get_irradiance
1540
- # and PVSystem.get_aoi and SingleAxisTracker.get_aoi
1541
- # have different method signatures. Use partial to handle
1542
- # the differences.
1543
- if isinstance(self.system, SingleAxisTracker):
1544
- self._prep_inputs_tracking()
1545
- get_irradiance = partial(
1546
- self.system.get_irradiance,
1547
- self.results.tracking['surface_tilt'],
1548
- self.results.tracking['surface_azimuth'],
1549
- self.results.solar_position['apparent_zenith'],
1550
- self.results.solar_position['azimuth'])
1551
- else:
1552
- self._prep_inputs_fixed()
1553
- get_irradiance = partial(
1554
- self.system.get_irradiance,
1555
- self.results.solar_position['apparent_zenith'],
1556
- self.results.solar_position['azimuth'])
1557
-
1558
- self.results.total_irrad = get_irradiance(
1556
+ self.results.total_irrad = self.system.get_irradiance(
1557
+ self.results.solar_position['apparent_zenith'],
1558
+ self.results.solar_position['azimuth'],
1559
1559
  _tuple_from_dfs(self.results.weather, 'dni'),
1560
1560
  _tuple_from_dfs(self.results.weather, 'ghi'),
1561
1561
  _tuple_from_dfs(self.results.weather, 'dhi'),
@@ -1636,10 +1636,7 @@ class ModelChain:
1636
1636
  self._prep_inputs_solar_pos(data)
1637
1637
  self._prep_inputs_airmass()
1638
1638
 
1639
- if isinstance(self.system, SingleAxisTracker):
1640
- self._prep_inputs_tracking()
1641
- else:
1642
- self._prep_inputs_fixed()
1639
+ self._prep_inputs_fixed()
1643
1640
 
1644
1641
  return self
1645
1642
 
@@ -1686,7 +1683,7 @@ class ModelChain:
1686
1683
  self.temperature_model()
1687
1684
  return self
1688
1685
 
1689
- def _prepare_temperature(self, data=None):
1686
+ def _prepare_temperature(self, data):
1690
1687
  """
1691
1688
  Sets cell_temperature using inputs in data and the specified
1692
1689
  temperature model.
@@ -1699,7 +1696,7 @@ class ModelChain:
1699
1696
 
1700
1697
  Parameters
1701
1698
  ----------
1702
- data : DataFrame, default None
1699
+ data : DataFrame
1703
1700
  May contain columns ``'cell_temperature'`` or
1704
1701
  ``'module_temperaure'``.
1705
1702
 
@@ -1878,13 +1875,13 @@ class ModelChain:
1878
1875
 
1879
1876
  return self
1880
1877
 
1881
- def _run_from_effective_irrad(self, data=None):
1878
+ def _run_from_effective_irrad(self, data):
1882
1879
  """
1883
1880
  Executes the temperature, DC, losses and AC models.
1884
1881
 
1885
1882
  Parameters
1886
1883
  ----------
1887
- data : DataFrame, or tuple of DataFrame, default None
1884
+ data : DataFrame, or tuple of DataFrame
1888
1885
  If optional column ``'cell_temperature'`` is provided, these values
1889
1886
  are used instead of `temperature_model`. If optional column
1890
1887
  `module_temperature` is provided, `temperature_model` must be
@@ -1907,7 +1904,7 @@ class ModelChain:
1907
1904
 
1908
1905
  return self
1909
1906
 
1910
- def run_model_from_effective_irradiance(self, data=None):
1907
+ def run_model_from_effective_irradiance(self, data):
1911
1908
  """
1912
1909
  Run the model starting with effective irradiance in the plane of array.
1913
1910