pvlib 0.10.4__py3-none-any.whl → 0.11.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 (47) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/albedo.py +168 -0
  3. pvlib/bifacial/utils.py +2 -1
  4. pvlib/data/ASTMG173.csv +2004 -0
  5. pvlib/iam.py +28 -28
  6. pvlib/iotools/__init__.py +0 -1
  7. pvlib/iotools/midc.py +15 -10
  8. pvlib/iotools/psm3.py +10 -25
  9. pvlib/iotools/srml.py +1 -61
  10. pvlib/iotools/surfrad.py +1 -1
  11. pvlib/irradiance.py +133 -95
  12. pvlib/location.py +16 -6
  13. pvlib/modelchain.py +2 -165
  14. pvlib/pvarray.py +7 -5
  15. pvlib/pvsystem.py +75 -106
  16. pvlib/scaling.py +4 -2
  17. pvlib/shading.py +350 -0
  18. pvlib/singlediode.py +37 -9
  19. pvlib/snow.py +3 -1
  20. pvlib/spectrum/__init__.py +5 -0
  21. pvlib/spectrum/mismatch.py +573 -43
  22. pvlib/spectrum/spectrl2.py +8 -8
  23. pvlib/tests/bifacial/test_utils.py +6 -5
  24. pvlib/tests/iotools/test_psm3.py +0 -18
  25. pvlib/tests/iotools/test_srml.py +1 -43
  26. pvlib/tests/test_albedo.py +84 -0
  27. pvlib/tests/test_inverter.py +2 -2
  28. pvlib/tests/test_irradiance.py +35 -2
  29. pvlib/tests/test_location.py +26 -18
  30. pvlib/tests/test_modelchain.py +0 -57
  31. pvlib/tests/test_pvsystem.py +73 -128
  32. pvlib/tests/test_shading.py +167 -1
  33. pvlib/tests/test_singlediode.py +68 -29
  34. pvlib/tests/test_spectrum.py +283 -22
  35. pvlib/tests/test_temperature.py +7 -7
  36. pvlib/tests/test_tools.py +24 -0
  37. pvlib/tests/test_transformer.py +60 -0
  38. pvlib/tools.py +27 -0
  39. pvlib/transformer.py +117 -0
  40. pvlib/version.py +1 -5
  41. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/METADATA +3 -4
  42. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/RECORD +46 -42
  43. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
  44. pvlib/data/astm_g173_am15g.csv +0 -2003
  45. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
  46. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
  47. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/top_level.txt +0 -0
pvlib/shading.py CHANGED
@@ -342,3 +342,353 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth,
342
342
  # Eq. (5); angle between sun's beam and surface
343
343
  theta_T = np.degrees(np.arctan2(sx_prime, sz_prime))
344
344
  return theta_T
345
+
346
+
347
+ def shaded_fraction1d(
348
+ solar_zenith,
349
+ solar_azimuth,
350
+ axis_azimuth,
351
+ shaded_row_rotation,
352
+ *,
353
+ collector_width,
354
+ pitch,
355
+ axis_tilt=0,
356
+ surface_to_axis_offset=0,
357
+ cross_axis_slope=0,
358
+ shading_row_rotation=None,
359
+ ):
360
+ r"""
361
+ Shaded fraction in the vertical dimension of tilted rows, or perpendicular
362
+ to the axis of horizontal rows.
363
+
364
+ If ``shading_row_rotation`` isn't provided, it is assumed that
365
+ both the shaded row and the shading row (the one blocking the
366
+ direct beam) have the same rotation and azimuth values.
367
+
368
+ .. warning::
369
+ The function assumes that the roles of the shaded and shading rows
370
+ remain the same during the day. In the case where the shading and
371
+ shaded rows change throughout the day, e.g. a N-S single-axis tracker,
372
+ the inputs must be switched depending on the sign of the projected
373
+ solar zenith angle. See the Examples section below.
374
+
375
+ .. versionadded:: 0.11.0
376
+
377
+ Parameters
378
+ ----------
379
+ solar_zenith : numeric
380
+ Solar zenith angle, in degrees.
381
+ solar_azimuth : numeric
382
+ Solar azimuth angle, in degrees.
383
+ axis_azimuth : numeric
384
+ Axis azimuth of the rotation axis of a tracker, in degrees.
385
+ Fixed-tilt arrays can be considered as a particular case of a tracker.
386
+ North=0º, South=180º, East=90º, West=270º.
387
+ shaded_row_rotation : numeric
388
+ Right-handed rotation of the row receiving the shade, with respect
389
+ to ``axis_azimuth``. In degrees :math:`^{\circ}`.
390
+ collector_width : numeric
391
+ Vertical length of a tilted row. The returned ``shaded_fraction``
392
+ is the ratio of the shadow over this value.
393
+ pitch : numeric
394
+ Axis-to-axis horizontal spacing of the row.
395
+ axis_tilt : numeric, default 0
396
+ Tilt of the rows axis from horizontal. In degrees :math:`^{\circ}`.
397
+ surface_to_axis_offset : numeric, default 0
398
+ Distance between the rotating axis and the collector surface.
399
+ May be used to account for a torque tube offset.
400
+ cross_axis_slope : numeric, default 0
401
+ Angle of the plane containing the rows' axes from
402
+ horizontal. Right-handed rotation with respect to the rows axes.
403
+ In degrees :math:`^{\circ}`.
404
+ shading_row_rotation : numeric, optional
405
+ Right-handed rotation of the row casting the shadow, with respect
406
+ to the row axis. In degrees :math:`^{\circ}`.
407
+
408
+ Returns
409
+ -------
410
+ shaded_fraction : numeric
411
+ The fraction of the collector width shaded by an adjacent row. A
412
+ value of 1 is completely shaded and 0 is no shade.
413
+
414
+ Notes
415
+ -----
416
+ All length parameters must have the same units.
417
+
418
+ Parameters are defined as follow:
419
+
420
+ .. figure:: ../../_images/Anderson_Jensen_2024_Fig3.png
421
+ :alt: Diagram showing the two rows and the parameters of the model.
422
+
423
+ Figure 3 of [1]_. See correspondence between this nomenclature and the
424
+ function parameters in the table below.
425
+
426
+ +------------------+----------------------------+---------------------+
427
+ | Symbol | Parameter | Units |
428
+ +==================+============================+=====================+
429
+ | :math:`\theta_1` | ``shading_row_rotation`` | |
430
+ +------------------+----------------------------+ |
431
+ | :math:`\theta_2` | ``shaded_row_rotation`` | Degrees |
432
+ +------------------+----------------------------+ :math:`^{\circ}` |
433
+ | :math:`\beta_c` | ``cross_axis_slope`` | |
434
+ +------------------+----------------------------+---------------------+
435
+ | :math:`p` | ``pitch`` | Any consistent |
436
+ +------------------+----------------------------+ length unit across |
437
+ | :math:`\ell` | ``collector_width`` | all these |
438
+ +------------------+----------------------------+ parameters, e.g. |
439
+ | :math:`z_0` | ``surface_to_axis_offset`` | :math:`m`. |
440
+ +------------------+----------------------------+---------------------+
441
+ | :math:`f_s` | Return value | Dimensionless |
442
+ +------------------+----------------------------+---------------------+
443
+
444
+ Examples
445
+ --------
446
+
447
+ **Fixed-tilt south-facing array on flat terrain**
448
+
449
+ Tilted row with a pitch of 3 m, a collector width of
450
+ 2 m, and row rotations of 30°. In the morning.
451
+
452
+ >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=104.5,
453
+ ... axis_azimuth=90, shaded_row_rotation=30, shading_row_rotation=30,
454
+ ... collector_width=2, pitch=3, axis_tilt=0,
455
+ ... surface_to_axis_offset=0.05, cross_axis_slope=0)
456
+ 0.6827437712114521
457
+
458
+ **Fixed-tilt north-facing array on sloped terrain**
459
+
460
+ Tilted row with a pitch of 4 m, a collector width of
461
+ 2.5 m, and row rotations of 50° for the shaded
462
+ row and 30° for the shading row. The rows are on a
463
+ 10° slope, where their axis is on the most inclined
464
+ direction (zero cross-axis slope). Shaded in the morning.
465
+
466
+ >>> shaded_fraction1d(solar_zenith=65, solar_azimuth=75.5,
467
+ ... axis_azimuth=270, shaded_row_rotation=50, shading_row_rotation=30,
468
+ ... collector_width=2.5, pitch=4, axis_tilt=10,
469
+ ... surface_to_axis_offset=0.05, cross_axis_slope=0)
470
+ 0.6975923460352351
471
+
472
+ **N-S single-axis tracker on sloped terrain**
473
+
474
+ Horizontal trackers with a pitch of 3 m, a collector width of
475
+ 1.4 m, and tracker rotations of 30° pointing east,
476
+ in the morning. Terrain slope is 7° west-east (east-most
477
+ tracker is higher than the west-most tracker).
478
+
479
+ >>> shaded_fraction1d(solar_zenith=50, solar_azimuth=90, axis_azimuth=180,
480
+ ... shaded_row_rotation=-30, collector_width=1.4, pitch=3, axis_tilt=0,
481
+ ... surface_to_axis_offset=0.10, cross_axis_slope=7)
482
+ 0.5828961460616938
483
+
484
+ Note the previous example only is valid for the shaded fraction of the
485
+ west-most tracker in the morning, and assuming it is the
486
+ shaded tracker during all the day is incorrect.
487
+ During the afternoon, it is the one casting the shadow onto the
488
+ east-most tracker.
489
+
490
+ To calculate the shaded fraction for the east-most
491
+ tracker, you must input the corresponding ``shaded_row_rotation``
492
+ in the afternoon.
493
+
494
+ >>> shaded_fraction1d(solar_zenith=50, solar_azimuth=270, axis_azimuth=180,
495
+ ... shaded_row_rotation=30, collector_width=1.4, pitch=3, axis_tilt=0,
496
+ ... surface_to_axis_offset=0.10, cross_axis_slope=7)
497
+ 0.4399034444363955
498
+
499
+ You must switch the input/output depending on the
500
+ sign of the projected solar zenith angle. See
501
+ :py:func:`~pvlib.shading.projected_solar_zenith_angle` and the example
502
+ :ref:`sphx_glr_gallery_shading_plot_shaded_fraction1d_ns_hsat_example.py`
503
+
504
+ See also
505
+ --------
506
+ pvlib.shading.projected_solar_zenith_angle
507
+
508
+ References
509
+ ----------
510
+ .. [1] Kevin S. Anderson, Adam R. Jensen; Shaded fraction and backtracking
511
+ in single-axis trackers on rolling terrain. J. Renewable Sustainable
512
+ Energy 1 March 2024; 16 (2): 023504. :doi:`10.1063/5.0202220`
513
+ """
514
+ # For nomenclature you may refer to [1].
515
+
516
+ # rotation of row casting the shadow defaults to shaded row's one
517
+ if shading_row_rotation is None:
518
+ shading_row_rotation = shaded_row_rotation
519
+
520
+ # projected solar zenith angle
521
+ projected_solar_zenith = projected_solar_zenith_angle(
522
+ solar_zenith,
523
+ solar_azimuth,
524
+ axis_tilt,
525
+ axis_azimuth,
526
+ )
527
+
528
+ # calculate repeated elements
529
+ thetas_1_S_diff = shading_row_rotation - projected_solar_zenith
530
+ thetas_2_S_diff = shaded_row_rotation - projected_solar_zenith
531
+ thetaS_rotation_diff = projected_solar_zenith - cross_axis_slope
532
+
533
+ cos_theta_2_S_diff_abs = np.abs(cosd(thetas_2_S_diff))
534
+
535
+ # Eq. (12) of [1]
536
+ t_asterisk = (
537
+ 0.5
538
+ + np.abs(cosd(thetas_1_S_diff)) / cos_theta_2_S_diff_abs / 2
539
+ + (
540
+ np.sign(projected_solar_zenith)
541
+ * surface_to_axis_offset
542
+ / collector_width
543
+ / cos_theta_2_S_diff_abs
544
+ * (sind(thetas_2_S_diff) - sind(thetas_1_S_diff))
545
+ )
546
+ - (
547
+ pitch
548
+ / collector_width
549
+ * cosd(thetaS_rotation_diff)
550
+ / cos_theta_2_S_diff_abs
551
+ / cosd(cross_axis_slope)
552
+ )
553
+ )
554
+
555
+ return np.clip(t_asterisk, 0, 1)
556
+
557
+
558
+ def direct_martinez(
559
+ poa_global,
560
+ poa_direct,
561
+ shaded_fraction,
562
+ shaded_blocks,
563
+ total_blocks,
564
+ ):
565
+ r"""
566
+ A shading loss power factor for non-monolithic silicon
567
+ modules and arrays with an arbitrary number of bypass diodes.
568
+
569
+ This experimental model reduces the direct and circumsolar
570
+ irradiance reaching the module's cells based on the number of *blocks*
571
+ affected by the shadow.
572
+ More on blocks in the *Notes* section and in [1]_.
573
+
574
+ .. versionadded:: 0.11.0
575
+
576
+ Parameters
577
+ ----------
578
+ poa_global : numeric
579
+ Plane of array global irradiance. [W/m²].
580
+ poa_direct : numeric
581
+ Plane of array direct and circumsolar irradiance. [W/m²].
582
+ shaded_fraction : numeric
583
+ Fraction of module surface area that is shaded. [Unitless].
584
+ shaded_blocks : numeric
585
+ Number of blocks affected by the shadow. [Unitless].
586
+ If a floating point number is provided, it will be rounded up.
587
+ total_blocks : int
588
+ Number of total blocks. Unitless.
589
+
590
+ Returns
591
+ -------
592
+ shading_losses : numeric
593
+ Fraction of DC power lost due to shading. [Unitless]
594
+
595
+ Notes
596
+ -----
597
+ The implemented equations are (6) and (8) from [1]_:
598
+
599
+ .. math::
600
+
601
+ (1 - F_{ES}) = (1 - F_{GS}) \left(1 - \frac{N_{SB}}{N_{TB} + 1}\right)
602
+ \quad \text{(6)}
603
+
604
+ \left(1 - \frac{P_{S}}{P_{NS}}\right) = \left(1 -
605
+ \frac{\left[(B + D^{CIR})(1 - F_{ES}) + D^{ISO} + R\right]}{G}\right)
606
+ \quad \text{(8)}
607
+
608
+ In (6), :math:`(1 - F_{ES})` is the correction factor to be multiplied by
609
+ the direct and circumsolar irradiance, :math:`F_{GS}` is the shaded
610
+ fraction of the collector, :math:`N_{SB}` is the number of shaded blocks
611
+ and :math:`N_{TB}` is the number of total blocks.
612
+
613
+ In (8), :math:`\frac{P_{S}}{P_{NS}}` is the fraction of DC power lost due
614
+ to shading, :math:`P_{S}` is the power output of the shaded module,
615
+ :math:`P_{NS}` is the power output of the non-shaded module,
616
+ :math:`B + D^{CIR}` is the beam and circumsolar irradiance,
617
+ :math:`D^{ISO} + R` is the sum of diffuse and albedo irradiances and
618
+ :math:`G` is the global irradiance.
619
+
620
+ **Blocks terminology:**
621
+
622
+ A *block* is defined in [1]_ as a group of solar cells protected by a
623
+ bypass diode. Also, a *block* is shaded when at least one of its
624
+ cells is partially shaded.
625
+
626
+ The total number of blocks and their layout depend on the module(s) used.
627
+ Many manufacturers don't specify this information explicitly.
628
+ However, these values can be inferred from:
629
+
630
+ - the number of bypass diodes
631
+ - where and how many junction boxes are present on the back of the module
632
+ - whether or not the module is comprised of *half-cut cells*
633
+
634
+ The latter two are heavily correlated.
635
+
636
+ For example:
637
+
638
+ 1. A module with 1 bypass diode behaves as 1 block.
639
+ 2. A module with 3 bypass diodes and 1 junction box is likely to have 3
640
+ blocks.
641
+ 3. A half-cut module with 3 junction boxes (split junction boxes) is
642
+ likely to have 3x2 blocks. The number of blocks along the longest
643
+ side of the module is 2 and along the shortest side is 3.
644
+ 4. A module without bypass diodes doesn't constitute a block, but may be
645
+ part of one.
646
+
647
+ Examples
648
+ --------
649
+ Minimal example. For a complete example, see
650
+ :ref:`sphx_glr_gallery_shading_plot_martinez_shade_loss.py`.
651
+
652
+ >>> import numpy as np
653
+ >>> from pvlib import shading
654
+ >>> total_blocks = 3 # blocks along the vertical of the module
655
+ >>> POA_direct_and_circumsolar, POA_diffuse = 600, 80 # W/m²
656
+ >>> POA_global = POA_direct_and_circumsolar + POA_diffuse
657
+ >>> P_out_unshaded = 3000 # W
658
+ >>> # calculation of the shaded fraction for the collector
659
+ >>> shaded_fraction = shading.shaded_fraction1d(
660
+ >>> solar_zenith=80, solar_azimuth=180,
661
+ >>> axis_azimuth=90, shaded_row_rotation=25,
662
+ >>> collector_width=0.5, pitch=1, surface_to_axis_offset=0,
663
+ >>> cross_axis_slope=5.711, shading_row_rotation=50)
664
+ >>> # calculation of the number of shaded blocks
665
+ >>> shaded_blocks = np.ceil(total_blocks*shaded_fraction)
666
+ >>> # apply the Martinez power losses to the calculated shading
667
+ >>> loss_fraction = shading.direct_martinez(
668
+ >>> POA_global, POA_direct_and_circumsolar,
669
+ >>> shaded_fraction, shaded_blocks, total_blocks)
670
+ >>> P_out_corrected = P_out_unshaded * (1 - loss_fraction)
671
+
672
+ See Also
673
+ --------
674
+ shaded_fraction1d : to calculate 1-dimensional shaded fraction
675
+
676
+ References
677
+ ----------
678
+ .. [1] F. Martínez-Moreno, J. Muñoz, and E. Lorenzo, 'Experimental model
679
+ to estimate shading losses on PV arrays', Solar Energy Materials and
680
+ Solar Cells, vol. 94, no. 12, pp. 2298-2303, Dec. 2010,
681
+ :doi:`10.1016/j.solmat.2010.07.029`.
682
+ """ # Contributed by Echedey Luis, 2024
683
+ beam_factor = ( # Eq. (6) of [1]
684
+ (1 - shaded_fraction)
685
+ * (1 - np.ceil(shaded_blocks) / (1 + total_blocks))
686
+ )
687
+ return ( # Eq. (8) of [1]
688
+ 1
689
+ - (
690
+ poa_direct * beam_factor
691
+ + (poa_global - poa_direct) # diffuse and albedo
692
+ )
693
+ / poa_global
694
+ )
pvlib/singlediode.py CHANGED
@@ -58,7 +58,7 @@ def estimate_voc(photocurrent, saturation_current, nNsVth):
58
58
 
59
59
  def bishop88(diode_voltage, photocurrent, saturation_current,
60
60
  resistance_series, resistance_shunt, nNsVth, d2mutau=0,
61
- NsVbi=np.Inf, breakdown_factor=0., breakdown_voltage=-5.5,
61
+ NsVbi=np.inf, breakdown_factor=0., breakdown_voltage=-5.5,
62
62
  breakdown_exp=3.28, gradients=False):
63
63
  r"""
64
64
  Explicit calculation of points on the IV curve described by the single
@@ -206,7 +206,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
206
206
 
207
207
  def bishop88_i_from_v(voltage, photocurrent, saturation_current,
208
208
  resistance_series, resistance_shunt, nNsVth,
209
- d2mutau=0, NsVbi=np.Inf, breakdown_factor=0.,
209
+ d2mutau=0, NsVbi=np.inf, breakdown_factor=0.,
210
210
  breakdown_voltage=-5.5, breakdown_exp=3.28,
211
211
  method='newton', method_kwargs=None):
212
212
  """
@@ -285,6 +285,12 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
285
285
 
286
286
  >>> i, method_output = bishop88_i_from_v(0.0, **args, method='newton',
287
287
  ... method_kwargs={'full_output': True})
288
+
289
+ References
290
+ ----------
291
+ .. [1] "Computer simulation of the effects of electrical mismatches in
292
+ photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
293
+ :doi:`10.1016/0379-6787(88)90059-2`
288
294
  """
289
295
  # collect args
290
296
  args = (photocurrent, saturation_current,
@@ -304,6 +310,9 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
304
310
  if method == 'brentq':
305
311
  # first bound the search using voc
306
312
  voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
313
+ # start iteration slightly less than NsVbi when voc_est > NsVbi, to
314
+ # avoid the asymptote at NsVbi
315
+ xp = np.where(voc_est < NsVbi, voc_est, 0.9999*NsVbi)
307
316
 
308
317
  # brentq only works with scalar inputs, so we need a set up function
309
318
  # and np.vectorize to repeatedly call the optimizer with the right
@@ -317,7 +326,7 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
317
326
  **method_kwargs)
318
327
 
319
328
  vd_from_brent_vectorized = np.vectorize(vd_from_brent)
320
- vd = vd_from_brent_vectorized(voc_est, voltage, *args)
329
+ vd = vd_from_brent_vectorized(xp, voltage, *args)
321
330
  elif method == 'newton':
322
331
  x0, (voltage, *args), method_kwargs = \
323
332
  _prepare_newton_inputs(voltage, (voltage, *args), method_kwargs)
@@ -338,7 +347,7 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
338
347
 
339
348
  def bishop88_v_from_i(current, photocurrent, saturation_current,
340
349
  resistance_series, resistance_shunt, nNsVth,
341
- d2mutau=0, NsVbi=np.Inf, breakdown_factor=0.,
350
+ d2mutau=0, NsVbi=np.inf, breakdown_factor=0.,
342
351
  breakdown_voltage=-5.5, breakdown_exp=3.28,
343
352
  method='newton', method_kwargs=None):
344
353
  """
@@ -417,6 +426,12 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
417
426
 
418
427
  >>> v, method_output = bishop88_v_from_i(0.0, **args, method='newton',
419
428
  ... method_kwargs={'full_output': True})
429
+
430
+ References
431
+ ----------
432
+ .. [1] "Computer simulation of the effects of electrical mismatches in
433
+ photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
434
+ :doi:`10.1016/0379-6787(88)90059-2`
420
435
  """
421
436
  # collect args
422
437
  args = (photocurrent, saturation_current,
@@ -431,6 +446,9 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
431
446
 
432
447
  # first bound the search using voc
433
448
  voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
449
+ # start iteration slightly less than NsVbi when voc_est > NsVbi, to avoid
450
+ # the asymptote at NsVbi
451
+ xp = np.where(voc_est < NsVbi, voc_est, 0.9999*NsVbi)
434
452
 
435
453
  def fi(x, i, *a):
436
454
  # calculate current residual given diode voltage "x"
@@ -449,10 +467,10 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
449
467
  **method_kwargs)
450
468
 
451
469
  vd_from_brent_vectorized = np.vectorize(vd_from_brent)
452
- vd = vd_from_brent_vectorized(voc_est, current, *args)
470
+ vd = vd_from_brent_vectorized(xp, current, *args)
453
471
  elif method == 'newton':
454
472
  x0, (current, *args), method_kwargs = \
455
- _prepare_newton_inputs(voc_est, (current, *args), method_kwargs)
473
+ _prepare_newton_inputs(xp, (current, *args), method_kwargs)
456
474
  vd = newton(func=lambda x, *a: fi(x, current, *a), x0=x0,
457
475
  fprime=lambda x, *a: bishop88(x, *a, gradients=True)[3],
458
476
  args=args, **method_kwargs)
@@ -469,7 +487,7 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
469
487
 
470
488
 
471
489
  def bishop88_mpp(photocurrent, saturation_current, resistance_series,
472
- resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.Inf,
490
+ resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.inf,
473
491
  breakdown_factor=0., breakdown_voltage=-5.5,
474
492
  breakdown_exp=3.28, method='newton', method_kwargs=None):
475
493
  """
@@ -547,6 +565,12 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
547
565
 
548
566
  >>> (i_mp, v_mp, p_mp), method_output = bishop88_mpp(**args,
549
567
  ... method='newton', method_kwargs={'full_output': True})
568
+
569
+ References
570
+ ----------
571
+ .. [1] "Computer simulation of the effects of electrical mismatches in
572
+ photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
573
+ :doi:`10.1016/0379-6787(88)90059-2`
550
574
  """
551
575
  # collect args
552
576
  args = (photocurrent, saturation_current,
@@ -561,6 +585,9 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
561
585
 
562
586
  # first bound the search using voc
563
587
  voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
588
+ # start iteration slightly less than NsVbi when voc_est > NsVbi, to avoid
589
+ # the asymptote at NsVbi
590
+ xp = np.where(voc_est < NsVbi, voc_est, 0.9999*NsVbi)
564
591
 
565
592
  def fmpp(x, *a):
566
593
  return bishop88(x, *a, gradients=True)[6]
@@ -574,12 +601,13 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
574
601
  vbr_a, vbr, vbr_exp),
575
602
  **method_kwargs)
576
603
  )
577
- vd = vec_fun(voc_est, *args)
604
+ vd = vec_fun(xp, *args)
578
605
  elif method == 'newton':
579
606
  # make sure all args are numpy arrays if max size > 1
580
607
  # if voc_est is an array, then make a copy to use for initial guess, v0
608
+
581
609
  x0, args, method_kwargs = \
582
- _prepare_newton_inputs(voc_est, args, method_kwargs)
610
+ _prepare_newton_inputs(xp, args, method_kwargs)
583
611
  vd = newton(func=fmpp, x0=x0,
584
612
  fprime=lambda x, *a: bishop88(x, *a, gradients=True)[7],
585
613
  args=args, **method_kwargs)
pvlib/snow.py CHANGED
@@ -222,7 +222,9 @@ def loss_townsend(snow_total, snow_events, surface_tilt, relative_humidity,
222
222
  string_factor=1.0, angle_of_repose=40):
223
223
  '''
224
224
  Calculates monthly snow loss based on the Townsend monthly snow loss
225
- model [1]_.
225
+ model.
226
+
227
+ This model is described in [1]_.
226
228
 
227
229
  Parameters
228
230
  ----------
@@ -2,8 +2,13 @@ from pvlib.spectrum.spectrl2 import spectrl2 # noqa: F401
2
2
  from pvlib.spectrum.mismatch import ( # noqa: F401
3
3
  calc_spectral_mismatch_field,
4
4
  get_am15g,
5
+ get_reference_spectra,
5
6
  get_example_spectral_response,
6
7
  spectral_factor_caballero,
7
8
  spectral_factor_firstsolar,
8
9
  spectral_factor_sapm,
10
+ spectral_factor_pvspec,
11
+ spectral_factor_jrc,
12
+ sr_to_qe,
13
+ qe_to_sr
9
14
  )