Irradiance Models

Overview

Introduction Irradiance modeling is the process of converting measured or estimated solar radiation into the specific irradiance components needed for photovoltaic (PV) design, forecasting, and performance analysis. In practical terms, engineers rarely get all the irradiance variables they need from sensors. A site might measure only Global Horizontal Irradiance (GHI), while a bankability model needs Direct Normal Irradiance (DNI), Diffuse Horizontal Irradiance (DHI), extraterrestrial irradiance, and finally Plane-of-Array (POA) irradiance on the tilted modules. Irradiance models bridge that gap.

At the physics level, irradiance is the instantaneous radiant power per area, usually in W/m^2. For PV systems, the core decomposition identity on a horizontal plane is:

\mathrm{GHI} = \mathrm{DHI} + \mathrm{DNI}\cos(\theta_z)

where \theta_z is solar zenith angle. Once values are transposed to the module plane, POA irradiance is typically represented as:

\mathrm{POA}_{\mathrm{global}} = \mathrm{POA}_{\mathrm{direct}} + \mathrm{POA}_{\mathrm{sky\ diffuse}} + \mathrm{POA}_{\mathrm{ground\ diffuse}}

These terms are the foundation for downstream PV calculations such as cell temperature, DC power, clipping, curtailment analysis, and financial metrics (P50/P90 energy yield). A small bias in irradiance decomposition can propagate into large annual energy and revenue errors. That is why modern workflows rely on validated transposition and decomposition models instead of ad hoc spreadsheet assumptions.

Boardflare’s irradiance model calculators are built around the pvlib irradiance module, a widely used open-source standard in PV engineering. The category combines empirical decomposition methods (DIRINT, DISC, ERBS), reference solar geometry/radiation support (GET_EXTRA_RADIATION), transposition subcomponents (HAYDAVIES, PEREZ, GET_GROUND_DIFFUSE), and full POA assembly (TOTAL_IRRADIANCE). Together, they support both lightweight feasibility checks and production-grade simulation pipelines.

For readers who want a conceptual primer, the solar irradiance and air mass articles provide useful context for the geometry and atmospheric effects these models encode.

When to Use It Irradiance models are most useful when the data available from weather files, plant SCADA, or onsite sensors does not directly match what your PV model requires. Instead of treating this as a data limitation, engineers use decomposition and transposition steps to reconstruct missing components in a physically consistent way.

One common job-to-be-done is utility-scale performance assessment with incomplete meteorological measurements. Many plants measure GHI reliably, but DNI or DHI may be unavailable or noisy. In that case, a team can use DIRINT, DISC, or ERBS to infer DNI (and in some cases DHI), then evaluate tracker POA through TOTAL_IRRADIANCE. This workflow enables PR analysis, underperformance triage, and expected-vs-actual reconciliation without waiting for a complete sensor retrofit.

A second job-to-be-done is pre-construction design tradeoff analysis. During early engineering, analysts frequently compare fixed-tilt and tracker options, albedo assumptions, and orientation scenarios. Here, GET_EXTRA_RADIATION provides extraterrestrial normalization inputs used by anisotropic sky models, HAYDAVIES and PEREZ provide sky diffuse estimates on tilted planes, GET_GROUND_DIFFUSE captures reflected ground contribution, and TOTAL_IRRADIANCE combines everything into POA terms that can be fed into energy models.

A third job-to-be-done is operational forecasting and what-if planning. Grid operators and plant operators need short-term estimates of available solar resource under changing cloud conditions. Empirical models such as DIRINT are useful for time-series applications because they can leverage temporal variability terms, while DISC and ERBS provide robust baseline alternatives for comparison. Running multiple models side by side helps quantify model spread and forecast uncertainty.

These tools are also valuable in data quality workflows. If measured DNI is intermittently missing or implausible, a modeled DNI series from DISC or DIRINT can serve as a reference check. If measured POA appears inconsistent with expected sun position and weather state, reconstructed POA from TOTAL_IRRADIANCE can highlight sensor drift, shading events, or metadata issues (e.g., incorrect tilt/azimuth settings).

In finance and asset management settings, irradiance models support portfolio-level normalization. Different sites may have different sensor configurations and data completeness. Standardizing decomposition and transposition methods across assets improves comparability of availability-adjusted performance, degradation studies, and warranty claim analyses. Even if each site has imperfect data, a consistent modeling chain reduces methodological noise.

Finally, these models are useful in engineering communication. Non-technical stakeholders often ask why simulated and measured energy differ by a few percent. A transparent irradiance workflow shows where differences can emerge: decomposition choice, airmass treatment, albedo assumptions, coefficient set selection, and treatment of high-zenith angles. This makes risk conversations concrete and actionable.

How It Works Irradiance workflows in this category follow a staged pipeline: (1) decompose horizontal irradiance into beam and diffuse components, (2) compute extraterrestrial references and geometry terms, (3) transpose irradiance onto the module plane, and (4) aggregate POA components.

Step 1 is decomposition from GHI to DNI/DHI. The baseline relationship is:

\mathrm{DNI} = \frac{\mathrm{GHI} - \mathrm{DHI}}{\cos(\theta_z)}

but DHI is usually unknown, so empirical models estimate diffuse fraction and clearness relationships from observed atmospheric behavior.

  • DISC uses correlations between clearness index and direct transmittance to estimate DNI from GHI and solar geometry. It is widely used as a simple and stable baseline.
  • DIRINT is a modified DISC variant that adds time-series context (through stability terms such as \Delta k_t') and can optionally use dew point to improve atmospheric state representation.
  • ERBS estimates diffuse fraction from clearness index regimes and returns both DNI and DHI, making it useful when both components are missing.

Most decomposition methods depend on the clearness index:

k_t = \frac{\mathrm{GHI}}{\mathrm{E}_{0}\cos(\theta_z)}

where \mathrm{E}_{0} is extraterrestrial normal irradiance projected onto the horizontal. In practice, this is why GET_EXTRA_RADIATION matters: it provides the top-of-atmosphere reference that normalizes seasonal Earth–Sun distance changes. Different methods (spencer, asce, nrel) are available, with generally small but sometimes material differences in specific pipelines.

Step 2 is geometric and atmospheric context. Solar zenith and azimuth define incidence geometry between sun rays and module orientation. Airmass describes the optical path length through the atmosphere and influences anisotropic diffuse behavior, especially in models like PEREZ. Relative airmass is often sufficient for transposition, while pressure-corrected forms can be used when higher fidelity is required.

Step 3 is transposition to tilted surfaces. The beam component is projected by incidence angle factors. Diffuse sky radiation can be modeled in increasing complexity:

  • Isotropic assumption: sky diffuse is uniform across the sky dome.
  • Anisotropic assumptions: brightness is non-uniform, with circumsolar and horizon effects.

HAYDAVIES is an anisotropic model emphasizing circumsolar brightening and is often a practical middle ground between simplicity and realism. PEREZ is more flexible, parameterized by empirical coefficient sets (for example, allsitescomposite1990), and frequently preferred in bankability-grade studies for its performance across diverse sky conditions.

Ground-reflected irradiance is handled separately by GET_GROUND_DIFFUSE, which uses tilt and albedo (or surface type presets like snow/concrete) to compute the reflected term. This component can be modest for dark surfaces but material for high-albedo environments (snow, bright concrete, desert conditions).

Step 4 is POA assembly via TOTAL_IRRADIANCE, which combines direct, sky diffuse, and ground diffuse into a single coherent result set:

\mathrm{POA}_{\mathrm{global}} = \mathrm{POA}_{\mathrm{direct}} + \mathrm{POA}_{\mathrm{sky\ diffuse}} + \mathrm{POA}_{\mathrm{ground\ diffuse}}

The tool exposes intermediate components (poa_direct, poa_diffuse, poa_sky_diffuse, poa_ground_diffuse) so users can audit model behavior instead of treating POA as a black box.

From an assumptions perspective, these models are empirical and condition-dependent. They generally assume accurate timestamps, consistent units (W/m^2, degrees), and physically plausible irradiance values (non-negative, realistic zenith limits). High-zenith periods near sunrise/sunset are especially sensitive because division by small \cos(\theta_z) can amplify noise. That is why functions expose controls like min_cos_zenith and max_zenith in decomposition tools.

This category relies on pvlib, which itself implements peer-reviewed methods used across the solar industry and academia. In practical engineering, best practice is not to rely on one model blindly; instead, compare at least two decomposition/transposition combinations for sensitivity analysis and document the chosen rationale.

Practical Example Consider a commercial/industrial portfolio analyst evaluating expected monthly energy for a 5 MW fixed-tilt site where only GHI and weather metadata are available. The analyst needs POA irradiance for a DC model, but DNI and DHI are missing.

Step 1: Prepare time-series inputs. The analyst collects hourly timestamps, GHI, solar zenith/azimuth (from site coordinates and time), module tilt/azimuth, and a reasonable albedo estimate from local land cover. Input hygiene matters: timezone consistency, no duplicated timestamps, and explicit treatment for nighttime values.

Step 2: Compute extraterrestrial irradiance. Using GET_EXTRA_RADIATION, the analyst calculates \mathrm{DNI}_{extra} for each timestamp. This supports clearness-index calculations and anisotropic diffuse models.

Step 3: Decompose GHI into DNI/DHI candidate sets. The analyst runs ERBS to obtain both DNI and DHI directly, then runs DIRINT as a second DNI estimate and reconstructs DHI via:

\mathrm{DHI} = \mathrm{GHI} - \mathrm{DNI}\cos(\theta_z)

clipped at zero when needed for physical plausibility. In parallel, DISC can be run as a third benchmark to understand model spread under variable cloud regimes.

Step 4: Compute sky diffuse on the tilted plane. For sensitivity, the analyst evaluates HAYDAVIES and PEREZ. Perez is configured with allsitescomposite1990 as the default coefficient set unless site-specific validation suggests another set performs better.

Step 5: Add ground-reflected irradiance. GET_GROUND_DIFFUSE is evaluated using site tilt and baseline albedo (for example, 0.2 for mixed terrain). A winter scenario is also run with elevated albedo to represent intermittent snow.

Step 6: Assemble POA components. The analyst feeds the selected decomposition and transposition inputs into TOTAL_IRRADIANCE, which returns POA global and component breakdown. This supports both final DC modeling and explainability (how much comes from beam vs sky vs ground).

Step 7: Validate and calibrate. If short periods of measured POA exist, the analyst compares modeled and measured values, checking bias and RMSE by month and sky condition. If high bias appears in clear-sky afternoons, geometry metadata is reviewed first (tilt/azimuth). If diffuse periods are biased, the team compares Hay-Davies versus Perez and reassesses decomposition choice.

Step 8: Operationalize. The selected workflow is documented as a repeatable standard for the portfolio: preferred decomposition model, preferred transposition model, fallback logic for missing inputs, and seasonal albedo assumptions. This standardization improves consistency across assets and reporting cycles.

Why this is more effective than traditional spreadsheet modeling: spreadsheet-only methods often hide unit issues, use hard-coded constants, and do not expose component diagnostics. Boardflare’s calculators preserve explicit parameters and return structured outputs, enabling transparent QA, scenario analysis, and traceable handoffs between engineering, analytics, and finance teams.

Figure 1: Irradiance modeling concepts: (left) decomposition of GHI into DNI and DHI across solar zenith; (right) POA global irradiance versus module tilt showing beam and diffuse tradeoffs.

How to Choose Choosing the right irradiance calculator is mainly a question of available inputs, required outputs, and the fidelity needed for the decision at hand. A practical approach is to choose the simplest model that satisfies your accuracy and explainability requirements, then stress-test with at least one alternative.

graph TD
    A[Start with available data] --> B{Need POA on tilted modules?}
    B -- Yes --> C[Use TOTAL_IRRADIANCE]
    C --> D{Have DNI and DHI already?}
    D -- No --> E[Estimate with DIRINT / DISC / ERBS]
    D -- Yes --> F[Select diffuse model: Hay-Davies or Perez]
    E --> F
    F --> G{Need detailed sky anisotropy?}
    G -- Moderate --> H[Use HAYDAVIES]
    G -- Higher fidelity --> I[Use PEREZ]
    C --> J[Add ground term via GET_GROUND_DIFFUSE]
    E --> K[Need extraterrestrial reference?]
    K -- Yes --> L[Use GET_EXTRA_RADIATION]
    K -- No --> M[Proceed with known reference inputs]

Use this comparison as a decision guide:

Function Primary purpose Best when Tradeoffs
DIRINT Estimate DNI from GHI with time-series enhancements You have high-quality time-indexed GHI and want stronger dynamic response Slightly more input/parameter complexity than basic models
DISC Estimate DNI from GHI using established empirical relation You need a robust baseline DNI quickly Less adaptive to short-term dynamics than DIRINT
ERBS Estimate both DNI and DHI from GHI You need complete beam/diffuse decomposition from minimal inputs Empirical diffuse-fraction regimes may vary by climate
GET_EXTRA_RADIATION Compute extraterrestrial normal irradiance You need clearness normalization or anisotropic inputs Method choice usually small but should be standardized
HAYDAVIES Sky diffuse transposition (anisotropic) You want a practical anisotropic model with moderate complexity Less configurable than Perez family
PEREZ Sky diffuse transposition with selectable coefficient sets You need high-fidelity diffuse treatment across diverse sky states Requires careful model/airmass input handling
GET_GROUND_DIFFUSE Ground-reflected POA contribution Ground reflectance materially affects yield (snow/bright surfaces) Sensitive to albedo assumptions
TOTAL_IRRADIANCE Assemble POA global and components You want end-to-end POA components in one step Output quality depends on upstream DNI/DHI and model choices

A practical selection strategy for most teams:

  1. Start with ERBS + TOTAL_IRRADIANCE for a transparent baseline.
  2. Add DIRINT and DISC as decomposition alternatives and quantify spread.
  3. Compare HAYDAVIES vs PEREZ for diffuse sensitivity.
  4. Always include GET_GROUND_DIFFUSE when albedo may vary seasonally.
  5. Standardize GET_EXTRA_RADIATION method and coefficient-set choices in model governance documents.

If the use case is early-stage screening, a simpler chain is often enough. If the use case is contractual performance guarantees, debt sizing, or warranty disputes, choose a higher-fidelity stack with documented sensitivity bounds and validation against measured POA where available. In both cases, the most important practice is consistency: once a model chain is selected for a decision process, keep it stable across scenarios so comparisons remain meaningful.

DIRINT

This function implements the modified DISC model known as “DIRINT” to predict DNI from measured Global Horizontal Irradiance (GHI).

DIRINT calculates DNI using time-series GHI data and solar zenith angles. It can optionally incorporate dew point temperature for improved accuracy. The stability index is utilized by default to adjust DNI in response to dynamics in the time series.

Excel Usage

=DIRINT(ghi, solar_zenith, times, pressure, use_delta_kt_prime, temp_dew, min_cos_zenith, max_zenith)
  • ghi (list[list], required): Global horizontal irradiance (W/m^2).
  • solar_zenith (list[list], required): Solar zenith angles (degrees).
  • times (list[list], required): Timestamps corresponding to the observations in ISO8601 format.
  • pressure (float, optional, default: 101325): Site air pressure (Pa).
  • use_delta_kt_prime (bool, optional, default: true): Include stability index delta_kt_prime in the model.
  • temp_dew (list[list], optional, default: null): Optional dew point temperature time series (°C) aligned to ghi observations.
  • min_cos_zenith (float, optional, default: 0.065): Minimum value of cos(zenith) when calculating clearness index.
  • max_zenith (float, optional, default: 87): Maximum value of zenith to allow in DNI calculation (degrees).

Returns (list[list]): 2D list of estimated DNI values (W/m^2), or an error string.

Example 1: Calculate DNI from GHI with default parameters

Inputs:

ghi solar_zenith times pressure use_delta_kt_prime
800 40 2024-01-01T12:00:00Z 101325 true
800 40 2024-01-01T13:00:00Z
800 40 2024-01-01T14:00:00Z
800 40 2024-01-01T15:00:00Z

Excel formula:

=DIRINT({800;800;800;800}, {40;40;40;40}, {"2024-01-01T12:00:00Z";"2024-01-01T13:00:00Z";"2024-01-01T14:00:00Z";"2024-01-01T15:00:00Z"}, 101325, TRUE)

Expected output:

Result
857.709
857.709
857.709
857.709
Example 2: DIRINT using dew point corrections

Inputs:

ghi solar_zenith times temp_dew pressure use_delta_kt_prime
500 65 2024-06-21T08:00:00Z 12 100500 true
700 45 2024-06-21T10:00:00Z 14
850 25 2024-06-21T12:00:00Z 16
600 50 2024-06-21T14:00:00Z 15

Excel formula:

=DIRINT({500;700;850;600}, {65;45;25;50}, {"2024-06-21T08:00:00Z";"2024-06-21T10:00:00Z";"2024-06-21T12:00:00Z";"2024-06-21T14:00:00Z"}, {12;14;16;15}, 100500, TRUE)

Expected output:

Result
534.527
776.165
578.504
750.125
Example 3: DIRINT without stability index adjustment

Inputs:

ghi solar_zenith times use_delta_kt_prime min_cos_zenith
300 70 2024-09-22T07:00:00Z false 0.08
550 55 2024-09-22T09:00:00Z
780 35 2024-09-22T11:00:00Z
520 60 2024-09-22T13:00:00Z

Excel formula:

=DIRINT({300;550;780;520}, {70;55;35;60}, {"2024-09-22T07:00:00Z";"2024-09-22T09:00:00Z";"2024-09-22T11:00:00Z";"2024-09-22T13:00:00Z"}, FALSE, 0.08)

Expected output:

Result
702.838
780.042
653.316
889.943
Example 4: DIRINT with tighter zenith cutoff

Inputs:

ghi solar_zenith times max_zenith pressure
200 80 2024-03-21T06:30:00Z 80 95000
450 68 2024-03-21T08:30:00Z
650 52 2024-03-21T10:30:00Z
400 78 2024-03-21T16:30:00Z

Excel formula:

=DIRINT({200;450;650;400}, {80;68;52;78}, {"2024-03-21T06:30:00Z";"2024-03-21T08:30:00Z";"2024-03-21T10:30:00Z";"2024-03-21T16:30:00Z"}, 80, 95000)

Expected output:

Result
630.356
745.445
761.993
420.681

Python Code

Show Code
import pandas as pd
import numpy as np
from pvlib.irradiance import dirint as result_func

def dirint(ghi, solar_zenith, times, pressure=101325, use_delta_kt_prime=True, temp_dew=None, min_cos_zenith=0.065, max_zenith=87):
    """
    Estimate Direct Normal Irradiance (DNI) from GHI using the DIRINT model.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.dirint.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        ghi (list[list]): Global horizontal irradiance (W/m^2).
        solar_zenith (list[list]): Solar zenith angles (degrees).
        times (list[list]): Timestamps corresponding to the observations in ISO8601 format.
        pressure (float, optional): Site air pressure (Pa). Default is 101325.
        use_delta_kt_prime (bool, optional): Include stability index delta_kt_prime in the model. Default is True.
        temp_dew (list[list], optional): Optional dew point temperature time series (°C) aligned to ghi observations. Default is None.
        min_cos_zenith (float, optional): Minimum value of cos(zenith) when calculating clearness index. Default is 0.065.
        max_zenith (float, optional): Maximum value of zenith to allow in DNI calculation (degrees). Default is 87.

    Returns:
        list[list]: 2D list of estimated DNI values (W/m^2), or an error string.
    """
    try:
        def flatten_num(data):
            if not isinstance(data, list): return [float(data)]
            flat = []
            for row in data:
                row = row if isinstance(row, list) else [row]
                for val in row:
                    if val == "": flat.append(float('nan'))
                    else: flat.append(float(val))
            return flat

        def flatten_str(data):
            if not isinstance(data, list): return [str(data)]
            return [str(val) for row in data for val in (row if isinstance(row, list) else [row]) if val != ""]

        ghi_list = flatten_num(ghi)
        zen_list = flatten_num(solar_zenith)
        time_list = flatten_str(times)
        temp_dew_list = None
        if temp_dew is not None and temp_dew != "":
            td = flatten_num(temp_dew)
            if len(td) != len(ghi_list):
                return "Error: temp_dew must match the length of ghi"
            temp_dew_list = pd.Series(td, index=pd.DatetimeIndex(time_list))

        n = len(ghi_list)
        if n == 0 or len(zen_list) != n or len(time_list) != n:
            return "Error: All input arrays must have the same non-zero length"

        idx = pd.DatetimeIndex(time_list)
        ghi_s = pd.Series(ghi_list, index=idx)
        zen_s = pd.Series(zen_list, index=idx)

        p = float(pressure) if pressure is not None else 101325.0
        use_kt = bool(use_delta_kt_prime) if use_delta_kt_prime is not None else True
        min_cz = float(min_cos_zenith) if min_cos_zenith is not None else 0.065
        max_z = float(max_zenith) if max_zenith is not None else 87.0

        # dirint requires pandas Series if use_delta_kt_prime is True
        dni = result_func(ghi_s, zen_s, idx, pressure=p, use_delta_kt_prime=use_kt, temp_dew=temp_dew_list, min_cos_zenith=min_cz, max_zenith=max_z)

        return [[float(v) if not pd.isna(v) else ""] for v in dni]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Global horizontal irradiance (W/m^2).
Solar zenith angles (degrees).
Timestamps corresponding to the observations in ISO8601 format.
Site air pressure (Pa).
Include stability index delta_kt_prime in the model.
Optional dew point temperature time series (°C) aligned to ghi observations.
Minimum value of cos(zenith) when calculating clearness index.
Maximum value of zenith to allow in DNI calculation (degrees).

DISC

This function calculates Direct Normal Irradiance (DNI) from Global Horizontal Irradiance (GHI) and solar zenith angle using the DISC model.

The model estimates DNI through analytical correlations between the global and direct clearness indices. It returns a 2D array where each row contains the modeled DNI, the global clearness index (Kt), and the absolute airmass.

Excel Usage

=DISC(ghi, solar_zenith, times, pressure, min_cos_zenith, max_zenith)
  • ghi (list[list], required): Global horizontal irradiance (W/m^2).
  • solar_zenith (list[list], required): True (not refraction-corrected) solar zenith angles (degrees).
  • times (list[list], required): Timestamps representing day of year in ISO8601 format.
  • pressure (float, optional, default: 101325): Site air pressure (Pa). Default 101325. Use blank for relative airmass.
  • min_cos_zenith (float, optional, default: 0.065): Minimum value of cos(zenith) when calculating clearness index.
  • max_zenith (float, optional, default: 87): Maximum value of zenith to allow in DNI calculation (degrees).

Returns (list[list]): 2D list containing [[dni, kt, airmass]], or an error string.

Example 1: Calculate DNI using DISC model

Inputs:

ghi solar_zenith times pressure
800 40 2024-06-21T12:00:00Z 101325
500 60 2024-06-21T16:00:00Z

Excel formula:

=DISC({800;500}, {40;60}, {"2024-06-21T12:00:00Z";"2024-06-21T16:00:00Z"}, 101325)

Expected output:

Result
907.719 0.788033 1.30368
864.913 0.754585 1.99276
Example 2: DISC using relative airmass with blank pressure

Inputs:

ghi solar_zenith times pressure
650 35 2024-05-15T11:00:00Z
720 50 2024-05-15T13:00:00Z
540 65 2024-05-15T15:00:00Z

Excel formula:

=DISC({650;720;540}, {35;50;65}, {"2024-05-15T11:00:00Z";"2024-05-15T13:00:00Z";"2024-05-15T15:00:00Z"}, "")

Expected output:

Result
349.405 0.592522 1.21942
940.511 0.836412 1.55255
795.77 0.954116 2.35385
Example 3: DISC at high altitude site pressure

Inputs:

ghi solar_zenith times pressure min_cos_zenith
550 55 2024-07-10T09:00:00Z 82000 0.07
780 30 2024-07-10T12:00:00Z
610 48 2024-07-10T14:00:00Z

Excel formula:

=DISC({550;780;610}, {55;30;48}, {"2024-07-10T09:00:00Z";"2024-07-10T12:00:00Z";"2024-07-10T14:00:00Z"}, 82000, 0.07)

Expected output:

Result
770.347 0.723975 1.40718
428.427 0.680012 0.933588
584.044 0.68829 1.20721
Example 4: DISC with a stricter zenith threshold

Inputs:

ghi solar_zenith times max_zenith
250 78 2024-03-15T07:30:00Z 80
420 72 2024-03-15T08:30:00Z
580 66 2024-03-15T09:30:00Z

Excel formula:

=DISC({250;420;580}, {78;72;66}, {"2024-03-15T07:30:00Z";"2024-03-15T08:30:00Z";"2024-03-15T09:30:00Z"}, 80)

Expected output:

Result
688.312 0.868316 4.70361
702.336 0.981485 3.20352
739.511 1 2.44466

Python Code

Show Code
import pandas as pd
import numpy as np
from pvlib.irradiance import disc as result_func

def disc(ghi, solar_zenith, times, pressure=101325, min_cos_zenith=0.065, max_zenith=87):
    """
    Convert GHI to DNI using the Direct Insolation Simulation Code (DISC) model.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.disc.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        ghi (list[list]): Global horizontal irradiance (W/m^2).
        solar_zenith (list[list]): True (not refraction-corrected) solar zenith angles (degrees).
        times (list[list]): Timestamps representing day of year in ISO8601 format.
        pressure (float, optional): Site air pressure (Pa). Default 101325. Use blank for relative airmass. Default is 101325.
        min_cos_zenith (float, optional): Minimum value of cos(zenith) when calculating clearness index. Default is 0.065.
        max_zenith (float, optional): Maximum value of zenith to allow in DNI calculation (degrees). Default is 87.

    Returns:
        list[list]: 2D list containing [[dni, kt, airmass]], or an error string.
    """
    try:
        def flatten_num(data):
            if not isinstance(data, list): return [float(data)]
            flat = []
            for row in data:
                row = row if isinstance(row, list) else [row]
                for val in row:
                    if val == "": flat.append(float('nan'))
                    else: flat.append(float(val))
            return flat

        def flatten_str(data):
            if not isinstance(data, list): return [str(data)]
            return [str(val) for row in data for val in (row if isinstance(row, list) else [row]) if val != ""]

        ghi_list = flatten_num(ghi)
        zen_list = flatten_num(solar_zenith)
        time_list = flatten_str(times)

        n = len(ghi_list)
        if n == 0 or len(zen_list) != n or len(time_list) != n:
            return "Error: All input arrays must have the same non-zero length"

        idx = pd.DatetimeIndex(time_list)

        if pressure == "":
            p = None
        else:
            p = float(pressure) if pressure is not None else 101325.0
        min_cz = float(min_cos_zenith) if min_cos_zenith is not None else 0.065
        max_z = float(max_zenith) if max_zenith is not None else 87.0

        res = result_func(np.array(ghi_list), np.array(zen_list), idx, pressure=p, min_cos_zenith=min_cz, max_zenith=max_z)

        output = []
        # Res returns an OrderedDict with 'dni', 'kt', 'airmass'
        dnia = res['dni']
        kta = res['kt']
        ama = res['airmass']

        for i in range(n):
            d = float(dnia[i])
            k = float(kta[i])
            a = float(ama[i])
            output.append([d if not pd.isna(d) else "", k if not pd.isna(k) else "", a if not pd.isna(a) else ""])

        return output
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Global horizontal irradiance (W/m^2).
True (not refraction-corrected) solar zenith angles (degrees).
Timestamps representing day of year in ISO8601 format.
Site air pressure (Pa). Default 101325. Use blank for relative airmass.
Minimum value of cos(zenith) when calculating clearness index.
Maximum value of zenith to allow in DNI calculation (degrees).

ERBS

This function decomposes Global Horizontal Irradiance (GHI) into Direct Normal Irradiance (DNI) and Diffuse Horizontal Irradiance (DHI) using the Erbs empirical model.

The model estimates the diffuse fraction from GHI and computing the ratio of GHI to extraterrestrial irradiance. The result is returned as a 2D array where each row contains the computed DNI, DHI, and the clearness index (Kt).

Excel Usage

=ERBS(ghi, solar_zenith, times, min_cos_zenith, max_zenith)
  • ghi (list[list], required): Global horizontal irradiance (W/m^2).
  • solar_zenith (list[list], required): True (not refraction-corrected) solar zenith angles (degrees).
  • times (list[list], required): Timestamps in ISO8601 format.
  • min_cos_zenith (float, optional, default: 0.065): Minimum value of cos(zenith) when calculating clearness index.
  • max_zenith (float, optional, default: 87): Maximum value of zenith to allow in DNI calculation (degrees).

Returns (list[list]): 2D list containing [[dni, dhi, kt]], or an error string.

Example 1: Perform Erbs decomposition

Inputs:

ghi solar_zenith times
800 40 2024-06-21T12:00:00Z
500 60 2024-06-21T16:00:00Z

Excel formula:

=ERBS({800;500}, {40;60}, {"2024-06-21T12:00:00Z";"2024-06-21T16:00:00Z"})

Expected output:

Result
872.408 131.696 0.790283
822.17 88.9148 0.75674
Example 2: Erbs decomposition for a morning ramp

Inputs:

ghi solar_zenith times
250 75 2024-04-10T07:00:00Z
500 55 2024-04-10T09:00:00Z
720 35 2024-04-10T11:00:00Z

Excel formula:

=ERBS({250;500;720}, {75;55;35}, {"2024-04-10T07:00:00Z";"2024-04-10T09:00:00Z";"2024-04-10T11:00:00Z"})

Expected output:

Result
744.997 57.1806 0.710242
565.004 175.927 0.640975
579.196 245.55 0.646295
Example 3: Erbs decomposition with custom minimum cosine

Inputs:

ghi solar_zenith times min_cos_zenith
300 70 2024-08-01T08:00:00Z 0.08
620 45 2024-08-01T10:00:00Z
450 62 2024-08-01T14:00:00Z

Excel formula:

=ERBS({300;620;450}, {70;45;62}, {"2024-08-01T08:00:00Z";"2024-08-01T10:00:00Z";"2024-08-01T14:00:00Z"}, 0.08)

Expected output:

Result
604.64 93.2011 0.661743
603.997 192.91 0.661495
755.897 95.128 0.723141
Example 4: Erbs decomposition with stricter zenith cutoff

Inputs:

ghi solar_zenith times max_zenith
180 82 2024-02-20T07:30:00Z 80
340 74 2024-02-20T08:30:00Z
520 68 2024-02-20T09:30:00Z

Excel formula:

=ERBS({180;340;520}, {82;74;68}, {"2024-02-20T07:30:00Z";"2024-02-20T08:30:00Z";"2024-02-20T09:30:00Z"}, 80)

Expected output:

Result
0 180 0.92514
1029.98 56.1 0.88233
1159.08 85.8 0.992929

Python Code

Show Code
import pandas as pd
import numpy as np
from pvlib.irradiance import erbs as result_func

def erbs(ghi, solar_zenith, times, min_cos_zenith=0.065, max_zenith=87):
    """
    Estimate DNI and DHI from GHI and solar zenith using the Erbs model.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.erbs.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        ghi (list[list]): Global horizontal irradiance (W/m^2).
        solar_zenith (list[list]): True (not refraction-corrected) solar zenith angles (degrees).
        times (list[list]): Timestamps in ISO8601 format.
        min_cos_zenith (float, optional): Minimum value of cos(zenith) when calculating clearness index. Default is 0.065.
        max_zenith (float, optional): Maximum value of zenith to allow in DNI calculation (degrees). Default is 87.

    Returns:
        list[list]: 2D list containing [[dni, dhi, kt]], or an error string.
    """
    try:
        def flatten_num(data):
            if not isinstance(data, list): return [float(data)]
            flat = []
            for row in data:
                row = row if isinstance(row, list) else [row]
                for val in row:
                    if val == "": flat.append(float('nan'))
                    else: flat.append(float(val))
            return flat

        def flatten_str(data):
            if not isinstance(data, list): return [str(data)]
            return [str(val) for row in data for val in (row if isinstance(row, list) else [row]) if val != ""]

        ghi_list = flatten_num(ghi)
        zen_list = flatten_num(solar_zenith)
        time_list = flatten_str(times)

        n = len(ghi_list)
        if n == 0 or len(zen_list) != n or len(time_list) != n:
            return "Error: All input arrays must have the same non-zero length"

        idx = pd.DatetimeIndex(time_list)

        min_cz = float(min_cos_zenith) if min_cos_zenith is not None else 0.065
        max_z = float(max_zenith) if max_zenith is not None else 87.0

        res = result_func(np.array(ghi_list), np.array(zen_list), idx, min_cos_zenith=min_cz, max_zenith=max_z)

        output = []
        # Res returns an OrderedDict with 'dni', 'dhi', 'kt'
        dnia = res['dni']
        dhia = res['dhi']
        kta = res['kt']

        for i in range(n):
            d = float(dnia[i])
            dh = float(dhia[i])
            k = float(kta[i])
            output.append([d if not pd.isna(d) else "", dh if not pd.isna(dh) else "", k if not pd.isna(k) else ""])

        return output
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Global horizontal irradiance (W/m^2).
True (not refraction-corrected) solar zenith angles (degrees).
Timestamps in ISO8601 format.
Minimum value of cos(zenith) when calculating clearness index.
Maximum value of zenith to allow in DNI calculation (degrees).

GET_EXTRA_RADIATION

This function computes extraterrestrial radiation normal to the sun using standard algorithms.

The model returns the estimated global solar constant modulated by earth-sun distance. Users can provide a specific ISO8601 timestamp string and choose the desired estimation method.

Excel Usage

=GET_EXTRA_RADIATION(times, solar_constant, radiation_method, epoch_year)
  • times (list[list], required): Timestamps representing days of the year (ISO8601 format).
  • solar_constant (float, optional, default: 1366.1): The solar constant (W/m^2).
  • radiation_method (str, optional, default: “spencer”): The method by which extraterrestrial radiation should be calculated.
  • epoch_year (int, optional, default: 2014): The reference year. Only applies to DOY input used with numpy/nrel methods.

Returns (list[list]): 2D list of estimated DNI extra values (W/m^2), or an error string.

Example 1: Extraterrestrial radiation at the solstice

Inputs:

times solar_constant radiation_method epoch_year
2024-06-21T12:00:00Z 1366.1 spencer 2014

Excel formula:

=GET_EXTRA_RADIATION({"2024-06-21T12:00:00Z"}, 1366.1, "spencer", 2014)

Expected output:

1321.46

Example 2: Extraterrestrial radiation using the ASCE method

Inputs:

times radiation_method solar_constant
2024-03-20T12:00:00Z asce 1366.1

Excel formula:

=GET_EXTRA_RADIATION({"2024-03-20T12:00:00Z"}, "asce", 1366.1)

Expected output:

1374.78

Example 3: Extraterrestrial radiation using the NREL method

Inputs:

times radiation_method epoch_year
2024-12-21T12:00:00Z nrel 2024

Excel formula:

=GET_EXTRA_RADIATION({"2024-12-21T12:00:00Z"}, "nrel", 2024)

Expected output:

1411.68

Example 4: Extraterrestrial radiation over three representative dates

Inputs:

times radiation_method
2024-01-15T12:00:00Z spencer
2024-06-21T12:00:00Z
2024-09-22T12:00:00Z

Excel formula:

=GET_EXTRA_RADIATION({"2024-01-15T12:00:00Z";"2024-06-21T12:00:00Z";"2024-09-22T12:00:00Z"}, "spencer")

Expected output:

Result
1412.98
1321.46
1356.6

Python Code

Show Code
import pandas as pd
from pvlib.irradiance import get_extra_radiation as result_func

def get_extra_radiation(times, solar_constant=1366.1, radiation_method='spencer', epoch_year=2014):
    """
    Determine extraterrestrial radiation (DNI_extra) for a given day of the year.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.get_extra_radiation.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        times (list[list]): Timestamps representing days of the year (ISO8601 format).
        solar_constant (float, optional): The solar constant (W/m^2). Default is 1366.1.
        radiation_method (str, optional): The method by which extraterrestrial radiation should be calculated. Valid options: Spencer, ASCE, NREL. Default is 'spencer'.
        epoch_year (int, optional): The reference year. Only applies to DOY input used with numpy/nrel methods. Default is 2014.

    Returns:
        list[list]: 2D list of estimated DNI extra values (W/m^2), or an error string.
    """
    try:
        def flatten_str(data):
            if not isinstance(data, list): return [str(data)]
            return [str(val) for row in data for val in (row if isinstance(row, list) else [row]) if val != ""]

        time_list = flatten_str(times)

        if len(time_list) == 0:
            return "Error: input array cannot be empty"

        idx = pd.DatetimeIndex(time_list)

        sc = float(solar_constant) if solar_constant is not None else 1366.1
        meth = str(radiation_method) if radiation_method is not None else "spencer"
        ey = int(epoch_year) if epoch_year is not None else 2014

        if meth not in ["spencer", "asce", "nrel"]:
            return "Error: Invalid method selection"

        res = result_func(idx, solar_constant=sc, method=meth, epoch_year=ey)

        return [[float(v) if not pd.isna(v) else ""] for v in res]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Timestamps representing days of the year (ISO8601 format).
The solar constant (W/m^2).
The method by which extraterrestrial radiation should be calculated.
The reference year. Only applies to DOY input used with numpy/nrel methods.

GET_GROUND_DIFFUSE

This function computes diffuse irradiance caused by ground reflection.

It estimates ground reflection irradiance using the panel’s surface tilt, Global Horizontal Irradiance (GHI), and surface albedo.

Excel Usage

=GET_GROUND_DIFFUSE(surface_tilt, ghi, albedo, surface_type)
  • surface_tilt (float, required): Panel tilt from horizontal (degrees).
  • ghi (float, required): Global horizontal irradiance (W/m^2).
  • albedo (float, optional, default: 0.25): Ground surface reflection coefficient (0 to 1). Overridden by surface_type if provided.
  • surface_type (str, optional, default: null): Overrides albedo with specific constants (e.g. ‘snow’, ‘concrete’). Leave blank to use custom albedo.

Returns (float): Ground reflected irradiance (W/m^2), or an error string.

Example 1: Reflected irradiance on summer afternoon

Inputs:

surface_tilt ghi albedo surface_type
30 800 0.2

Excel formula:

=GET_GROUND_DIFFUSE(30, 800, 0.2, "")

Expected output:

10.718

Example 2: Reflected irradiance overriding albedo with snow

Inputs:

surface_tilt ghi albedo surface_type
45 600 0.25 snow

Excel formula:

=GET_GROUND_DIFFUSE(45, 600, 0.25, "snow")

Expected output:

57.1142

Example 3: Ground diffuse on a horizontal surface

Inputs:

surface_tilt ghi albedo surface_type
0 900 0.3

Excel formula:

=GET_GROUND_DIFFUSE(0, 900, 0.3, "")

Expected output:

0

Example 4: Ground diffuse using concrete surface type

Inputs:

surface_tilt ghi albedo surface_type
60 500 0.1 concrete

Excel formula:

=GET_GROUND_DIFFUSE(60, 500, 0.1, "concrete")

Expected output:

37.5

Python Code

Show Code
from pvlib.irradiance import get_ground_diffuse as result_func

def get_ground_diffuse(surface_tilt, ghi, albedo=0.25, surface_type=None):
    """
    Estimate diffuse irradiance on a tilted surface from ground reflections.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.get_ground_diffuse.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        surface_tilt (float): Panel tilt from horizontal (degrees).
        ghi (float): Global horizontal irradiance (W/m^2).
        albedo (float, optional): Ground surface reflection coefficient (0 to 1). Overridden by surface_type if provided. Default is 0.25.
        surface_type (str, optional): Overrides albedo with specific constants (e.g. 'snow', 'concrete'). Leave blank to use custom albedo. Default is None.

    Returns:
        float: Ground reflected irradiance (W/m^2), or an error string.
    """
    try:
        tilt = float(surface_tilt)
        gl = float(ghi)
        a = float(albedo) if albedo is not None else 0.25

        st = str(surface_type).strip() if surface_type is not None else ""
        if not st:
            st = None
            if not (0 <= a <= 1):
                return "Error: Albedo must be between 0 and 1"

        res = result_func(
            surface_tilt=tilt,
            ghi=gl,
            albedo=a,
            surface_type=st
        )

        return float(res)
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Panel tilt from horizontal (degrees).
Global horizontal irradiance (W/m^2).
Ground surface reflection coefficient (0 to 1). Overridden by surface_type if provided.
Overrides albedo with specific constants (e.g. 'snow', 'concrete'). Leave blank to use custom albedo.

HAYDAVIES

This function computes the diffuse irradiance on a tilted surface using the Hay and Davies (1980) transposition model.

The model estimates the sky diffuse irradiance using surface orientation, sun angles, and irradiance components. Note that ground-reflected irradiance is not included in this output.

Excel Usage

=HAYDAVIES(surface_tilt, surface_azimuth, dhi, dni, dni_extra, solar_zenith, solar_azimuth)
  • surface_tilt (float, required): Panel tilt from the horizontal (degrees).
  • surface_azimuth (float, required): Panel azimuth (degrees).
  • dhi (float, required): Diffuse horizontal irradiance (W/m^2).
  • dni (float, required): Direct normal irradiance (W/m^2).
  • dni_extra (float, required): Extraterrestrial normal irradiance (W/m^2).
  • solar_zenith (float, required): Solar apparent zenith angle (degrees).
  • solar_azimuth (float, required): Solar azimuth angle (degrees).

Returns (float): The sky diffuse component (W/m^2), or an error string.

Example 1: Calculate sky diffuse for basic inputs

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth
30 180 100 800 1367 40 180

Excel formula:

=HAYDAVIES(30, 180, 100, 800, 1367, 40, 180)

Expected output:

113.934

Example 2: Hay-Davies sky diffuse for east-facing array

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth
20 90 120 650 1321 50 110

Excel formula:

=HAYDAVIES(20, 90, 120, 650, 1321, 50, 110)

Expected output:

137.217

Example 3: Hay-Davies sky diffuse for steep winter tilt

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth
60 180 80 500 1412 65 170

Excel formula:

=HAYDAVIES(60, 180, 80, 500, 1412, 65, 170)

Expected output:

104.73

Example 4: Hay-Davies sky diffuse for west-facing afternoon case

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth
35 270 140 700 1335 45 250

Excel formula:

=HAYDAVIES(35, 270, 140, 700, 1335, 45, 250)

Expected output:

160.269

Python Code

Show Code
from pvlib.irradiance import haydavies as result_func

def haydavies(surface_tilt, surface_azimuth, dhi, dni, dni_extra, solar_zenith, solar_azimuth):
    """
    Determine diffuse irradiance from the sky on a tilted surface using the Hay and Davies model.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.haydavies.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        surface_tilt (float): Panel tilt from the horizontal (degrees).
        surface_azimuth (float): Panel azimuth (degrees).
        dhi (float): Diffuse horizontal irradiance (W/m^2).
        dni (float): Direct normal irradiance (W/m^2).
        dni_extra (float): Extraterrestrial normal irradiance (W/m^2).
        solar_zenith (float): Solar apparent zenith angle (degrees).
        solar_azimuth (float): Solar azimuth angle (degrees).

    Returns:
        float: The sky diffuse component (W/m^2), or an error string.
    """
    try:
        tilt = float(surface_tilt)
        azim = float(surface_azimuth)
        dh = float(dhi)
        dn = float(dni)
        dn_e = float(dni_extra)
        zen = float(solar_zenith)
        s_azim = float(solar_azimuth)

        res = result_func(
            surface_tilt=tilt,
            surface_azimuth=azim,
            dhi=dh,
            dni=dn,
            dni_extra=dn_e,
            solar_zenith=zen,
            solar_azimuth=s_azim,
            return_components=False
        )

        return float(res)
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Panel tilt from the horizontal (degrees).
Panel azimuth (degrees).
Diffuse horizontal irradiance (W/m^2).
Direct normal irradiance (W/m^2).
Extraterrestrial normal irradiance (W/m^2).
Solar apparent zenith angle (degrees).
Solar azimuth angle (degrees).

PEREZ

This function computes the diffuse irradiance on a tilted surface using the Perez model.

The model estimates sky diffuse irradiance (excluding ground-reflected irradiance) from surface orientation, sun angles, relative airmass, and irradiance components. Users can optionally select different sets of empirical coefficients via the model argument.

Excel Usage

=PEREZ(surface_tilt, surface_azimuth, dhi, dni, dni_extra, solar_zenith, solar_azimuth, airmass, perez_model)
  • surface_tilt (float, required): Panel tilt from the horizontal (degrees).
  • surface_azimuth (float, required): Panel azimuth (degrees).
  • dhi (float, required): Diffuse horizontal irradiance, must be >=0 (W/m^2).
  • dni (float, required): Direct normal irradiance, must be >=0 (W/m^2).
  • dni_extra (float, required): Extraterrestrial normal irradiance (W/m^2).
  • solar_zenith (float, required): Solar apparent zenith angle (degrees).
  • solar_azimuth (float, required): Solar azimuth angle (degrees).
  • airmass (float, required): Relative (not pressure-corrected) airmass values. Must be >=0 (unitless).
  • perez_model (str, optional, default: “allsitescomposite1990”): Name of the Perez coefficient set to use.

Returns (float): The sky diffuse component (W/m^2), or an error string.

Example 1: Calculate sky diffuse for basic inputs

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth airmass perez_model
30 180 100 800 1367 40 180 1.5 allsitescomposite1990

Excel formula:

=PEREZ(30, 180, 100, 800, 1367, 40, 180, 1.5, "allsitescomposite1990")

Expected output:

118.793

Example 2: Perez sky diffuse using the 1988 composite coefficients

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth airmass perez_model
25 180 110 750 1330 42 175 1.35 allsitescomposite1988

Excel formula:

=PEREZ(25, 180, 110, 750, 1330, 42, 175, 1.35, "allsitescomposite1988")

Expected output:

135.151

Example 3: Perez sky diffuse using the Sandia 1988 coefficients

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth airmass perez_model
40 135 90 680 1325 55 140 1.8 sandiacomposite1988

Excel formula:

=PEREZ(40, 135, 90, 680, 1325, 55, 140, 1.8, "sandiacomposite1988")

Expected output:

115.115

Example 4: Perez sky diffuse for a higher-airmass morning case

Inputs:

surface_tilt surface_azimuth dhi dni dni_extra solar_zenith solar_azimuth airmass perez_model
50 90 130 520 1390 70 100 2.7 usacomposite1988

Excel formula:

=PEREZ(50, 90, 130, 520, 1390, 70, 100, 2.7, "usacomposite1988")

Expected output:

198.361

Python Code

Show Code
from pvlib.irradiance import perez as result_func

def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra, solar_zenith, solar_azimuth, airmass, perez_model='allsitescomposite1990'):
    """
    Determine diffuse irradiance from the sky on a tilted surface using one of the Perez models.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.perez.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        surface_tilt (float): Panel tilt from the horizontal (degrees).
        surface_azimuth (float): Panel azimuth (degrees).
        dhi (float): Diffuse horizontal irradiance, must be >=0 (W/m^2).
        dni (float): Direct normal irradiance, must be >=0 (W/m^2).
        dni_extra (float): Extraterrestrial normal irradiance (W/m^2).
        solar_zenith (float): Solar apparent zenith angle (degrees).
        solar_azimuth (float): Solar azimuth angle (degrees).
        airmass (float): Relative (not pressure-corrected) airmass values. Must be >=0 (unitless).
        perez_model (str, optional): Name of the Perez coefficient set to use. Valid options: 1990 Composite, 1988 Composite, Sandia 1988, USA 1988, France 1988, Phoenix 1988, El Monte 1988, Osage 1988, Albuquerque 1988, Cape Canaveral 1988, Albany 1988. Default is 'allsitescomposite1990'.

    Returns:
        float: The sky diffuse component (W/m^2), or an error string.
    """
    try:
        tilt = float(surface_tilt)
        azim = float(surface_azimuth)
        dh = float(dhi)
        dn = float(dni)
        dn_e = float(dni_extra)
        zen = float(solar_zenith)
        s_azim = float(solar_azimuth)
        am = float(airmass)

        mod = str(perez_model) if perez_model is not None else "allsitescomposite1990"

        if am < 0:
            return "Error: airmass must be >= 0"
        if dh < 0 or dn < 0:
            return "Error: Irradiance values (dhi and dni) must be >= 0"

        res = result_func(
            surface_tilt=tilt,
            surface_azimuth=azim,
            dhi=dh,
            dni=dn,
            dni_extra=dn_e,
            solar_zenith=zen,
            solar_azimuth=s_azim,
            airmass=am,
            model=mod,
            return_components=False
        )

        return float(res)
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Panel tilt from the horizontal (degrees).
Panel azimuth (degrees).
Diffuse horizontal irradiance, must be >=0 (W/m^2).
Direct normal irradiance, must be >=0 (W/m^2).
Extraterrestrial normal irradiance (W/m^2).
Solar apparent zenith angle (degrees).
Solar azimuth angle (degrees).
Relative (not pressure-corrected) airmass values. Must be >=0 (unitless).
Name of the Perez coefficient set to use.

TOTAL_IRRADIANCE

This function calculates the total irradiance incident on a tilted surface (Plane of Array, POA) by summing its component parts: beam, sky diffuse, and ground reflected irradiance.

It supports multiple sky diffuse models (e.g., Isotropic, Klucher, Hay-Davies, Perez) and allows specifying ground albedo or surface types.

Excel Usage

=TOTAL_IRRADIANCE(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra, airmass, albedo, surface_type, diffuse_model, perez_set)
  • surface_tilt (float, required): Panel tilt from horizontal (degrees).
  • surface_azimuth (float, required): Panel azimuth (degrees).
  • solar_zenith (float, required): Solar zenith angle (degrees).
  • solar_azimuth (float, required): Solar azimuth angle (degrees).
  • dni (float, required): Direct normal irradiance (W/m^2).
  • ghi (float, required): Global horizontal irradiance (W/m^2).
  • dhi (float, required): Diffuse horizontal irradiance (W/m^2).
  • dni_extra (float, optional, default: 0): Extraterrestrial direct normal irradiance (W/m^2).
  • airmass (float, optional, default: 0): Relative airmass (unitless).
  • albedo (float, optional, default: 0.25): Ground surface albedo (0 to 1).
  • surface_type (str, optional, default: ““): Surface type (e.g. ‘snow’, ‘grass’). Overrides albedo.
  • diffuse_model (str, optional, default: “isotropic”): Sky diffuse irradiance model.
  • perez_set (str, optional, default: “allsitescomposite1990”): Perez coefficient set (used only if model=‘perez’).

Returns (list[list]): 2D list [[poa_global, poa_direct, poa_diffuse, poa_sky_diffuse, poa_ground_diffuse]], or an error string.

Example 1: Isotropic POA components

Inputs:

surface_tilt surface_azimuth solar_zenith solar_azimuth dni ghi dhi albedo diffuse_model
30 180 40 180 800 800 100 0.2 isotropic

Excel formula:

=TOTAL_IRRADIANCE(30, 180, 40, 180, 800, 800, 100, 0.2, "isotropic")

Expected output:

Result
891.865 787.846 104.019 93.3013 10.718
Example 2: POA components with the Hay-Davies transposition model

Inputs:

surface_tilt surface_azimuth solar_zenith solar_azimuth dni ghi dhi dni_extra albedo diffuse_model
25 180 35 170 850 900 120 1330 0.25 haydavies

Excel formula:

=TOTAL_IRRADIANCE(25, 180, 35, 170, 850, 900, 120, 1330, 0.25, "haydavies")

Expected output:

Result
977.632 833.956 143.676 133.136 10.5404
Example 3: POA components with the Perez transposition model

Inputs:

surface_tilt surface_azimuth solar_zenith solar_azimuth dni ghi dhi dni_extra airmass albedo diffuse_model perez_set
35 180 50 190 700 780 140 1325 1.6 0.2 perez allsitescomposite1990

Excel formula:

=TOTAL_IRRADIANCE(35, 180, 50, 190, 700, 780, 140, 1325, 1.6, 0.2, "perez", "allsitescomposite1990")

Expected output:

Result
874.761 671.475 203.285 189.179 14.1061
Example 4: POA components using a snow surface override

Inputs:

surface_tilt surface_azimuth solar_zenith solar_azimuth dni ghi dhi surface_type diffuse_model
45 180 55 200 600 650 160 snow isotropic

Excel formula:

=TOTAL_IRRADIANCE(45, 180, 55, 200, 600, 650, 160, "snow", "isotropic")

Expected output:

Result
768.368 569.926 198.442 136.569 61.8737

Python Code

Show Code
from pvlib.irradiance import get_total_irradiance as result_func

def total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=0, airmass=0, albedo=0.25, surface_type='', diffuse_model='isotropic', perez_set='allsitescomposite1990'):
    """
    Determine total in-plane irradiance and its beam, sky diffuse and ground reflected components.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.irradiance.get_total_irradiance.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        surface_tilt (float): Panel tilt from horizontal (degrees).
        surface_azimuth (float): Panel azimuth (degrees).
        solar_zenith (float): Solar zenith angle (degrees).
        solar_azimuth (float): Solar azimuth angle (degrees).
        dni (float): Direct normal irradiance (W/m^2).
        ghi (float): Global horizontal irradiance (W/m^2).
        dhi (float): Diffuse horizontal irradiance (W/m^2).
        dni_extra (float, optional): Extraterrestrial direct normal irradiance (W/m^2). Default is 0.
        airmass (float, optional): Relative airmass (unitless). Default is 0.
        albedo (float, optional): Ground surface albedo (0 to 1). Default is 0.25.
        surface_type (str, optional): Surface type (e.g. 'snow', 'grass'). Overrides albedo. Default is ''.
        diffuse_model (str, optional): Sky diffuse irradiance model. Valid options: Isotropic, Klucher, Hay-Davies, Reindl, King, Perez, Perez-Driesse. Default is 'isotropic'.
        perez_set (str, optional): Perez coefficient set (used only if model='perez'). Valid options: 1990 Composite, 1988 Composite, Sandia 1988, USA 1988, France 1988, Phoenix 1988, El Monte 1988, Osage 1988, Albuquerque 1988, Cape Canaveral 1988, Albany 1988. Default is 'allsitescomposite1990'.

    Returns:
        list[list]: 2D list [[poa_global, poa_direct, poa_diffuse, poa_sky_diffuse, poa_ground_diffuse]], or an error string.
    """
    try:
        tilt = float(surface_tilt)
        azim = float(surface_azimuth)
        zen = float(solar_zenith)
        s_azim = float(solar_azimuth)
        dn = float(dni)
        gh = float(ghi)
        dh = float(dhi)

        dn_e = float(dni_extra) if dni_extra not in [None, "", 0] else None
        am = float(airmass) if airmass not in [None, "", 0] else None
        alb = float(albedo) if albedo not in [None, ""] else 0.25
        st = str(surface_type).strip() if surface_type not in [None, ""] else ""
        if not st: st = None

        mod = str(diffuse_model) if diffuse_model is not None else 'isotropic'
        mod_p = str(perez_set) if perez_set is not None else 'allsitescomposite1990'

        res = result_func(
            surface_tilt=tilt,
            surface_azimuth=azim,
            solar_zenith=zen,
            solar_azimuth=s_azim,
            dni=dn,
            ghi=gh,
            dhi=dh,
            dni_extra=dn_e,
            airmass=am,
            albedo=alb,
            surface_type=st,
            model=mod,
            model_perez=mod_p
        )

        out = [
            float(res['poa_global']),
            float(res['poa_direct']),
            float(res['poa_diffuse']),
            float(res['poa_sky_diffuse']),
            float(res['poa_ground_diffuse'])
        ]
        return [out]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Panel tilt from horizontal (degrees).
Panel azimuth (degrees).
Solar zenith angle (degrees).
Solar azimuth angle (degrees).
Direct normal irradiance (W/m^2).
Global horizontal irradiance (W/m^2).
Diffuse horizontal irradiance (W/m^2).
Extraterrestrial direct normal irradiance (W/m^2).
Relative airmass (unitless).
Ground surface albedo (0 to 1).
Surface type (e.g. 'snow', 'grass'). Overrides albedo.
Sky diffuse irradiance model.
Perez coefficient set (used only if model='perez').