pvlib 0.10.5__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 (39) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/albedo.py +168 -0
  3. pvlib/data/ASTMG173.csv +2004 -0
  4. pvlib/iam.py +28 -28
  5. pvlib/iotools/__init__.py +0 -1
  6. pvlib/iotools/midc.py +15 -10
  7. pvlib/iotools/psm3.py +10 -25
  8. pvlib/iotools/srml.py +0 -61
  9. pvlib/irradiance.py +133 -95
  10. pvlib/location.py +13 -5
  11. pvlib/modelchain.py +2 -165
  12. pvlib/pvsystem.py +23 -63
  13. pvlib/shading.py +350 -0
  14. pvlib/spectrum/__init__.py +5 -0
  15. pvlib/spectrum/mismatch.py +572 -43
  16. pvlib/spectrum/spectrl2.py +8 -8
  17. pvlib/tests/iotools/test_psm3.py +0 -18
  18. pvlib/tests/iotools/test_srml.py +1 -43
  19. pvlib/tests/test_albedo.py +84 -0
  20. pvlib/tests/test_inverter.py +2 -2
  21. pvlib/tests/test_irradiance.py +35 -2
  22. pvlib/tests/test_location.py +26 -18
  23. pvlib/tests/test_modelchain.py +0 -57
  24. pvlib/tests/test_pvsystem.py +11 -39
  25. pvlib/tests/test_shading.py +167 -1
  26. pvlib/tests/test_singlediode.py +0 -19
  27. pvlib/tests/test_spectrum.py +283 -22
  28. pvlib/tests/test_temperature.py +7 -7
  29. pvlib/tests/test_tools.py +24 -0
  30. pvlib/tests/test_transformer.py +60 -0
  31. pvlib/tools.py +27 -0
  32. pvlib/transformer.py +117 -0
  33. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/METADATA +1 -1
  34. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/RECORD +38 -34
  35. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
  36. pvlib/data/astm_g173_am15g.csv +0 -2003
  37. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
  38. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
  39. {pvlib-0.10.5.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
+ )
@@ -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
  )