SOLARPOSITION

Overview

The SOLARPOSITION function calculates the position of the sun in the sky—specifically the azimuth, elevation, and apparent zenith angles—for a given time and geographic location. These values are essential for photovoltaic system design, solar tracking, shading analysis, and solar resource assessment.

This implementation uses the pvlib Python library, which provides a comprehensive set of tools for simulating photovoltaic energy systems. The underlying solar position calculations are based on the NREL Solar Position Algorithm (SPA), developed by the National Renewable Energy Laboratory. The SPA algorithm calculates solar zenith and azimuth angles with uncertainties of ±0.0003 degrees for dates between the years -2000 and 6000. For more details, see the pvlib solar position documentation and the NREL SPA website.

The algorithm is based on the astronomical algorithms published in:

Reda, I.; Andreas, A. (2004). Solar Position Algorithm for Solar Radiation Applications. Solar Energy, Vol. 76(5), pp. 577-589. NREL Technical Report

The function returns three key angles:

  • Azimuth: The compass direction of the sun measured clockwise from true north (0° = North, 90° = East, 180° = South, 270° = West)
  • Elevation: The angle of the sun above the horizon (0° at horizon, 90° at zenith)
  • Apparent Zenith: The angle from the vertical to the sun’s position, corrected for atmospheric refraction (90° - elevation, adjusted)

The function supports multiple calculation methods including nrel_numpy (default, recommended), nrel_numba (compiled for speed), pyephem, ephemeris, and nrel_c. Atmospheric refraction corrections use the provided temperature and pressure parameters to improve accuracy for low solar elevation angles.

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

Excel Usage

=SOLARPOSITION(time, latitude, longitude, altitude, pressure, method, temperature)
  • time (str, required): The time value
  • latitude (float, required): The latitude value
  • longitude (float, required): The longitude value
  • altitude (float, optional, default: 0): The altitude value
  • pressure (float, optional, default: 101325): The pressure value
  • method (str, optional, default: “nrel_numpy”): The method value
  • temperature (float, optional, default: 12): The temperature value

Returns (list[list]): 2D list [[azimuth, elevation, apparent_zenith]], or error string.

Examples

Example 1: Demo case 1

Inputs:

time latitude longitude altitude pressure method temperature
2024-06-20T12:00:00Z 35 -120 0 101325 nrel_numpy 12

Excel formula:

=SOLARPOSITION("2024-06-20T12:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 12)

Expected output:

Result
53.2 -8.8 98.8

Example 2: Demo case 2

Inputs:

time latitude longitude altitude pressure method temperature
2024-06-20T06:00:00Z 35 -120 0 101325 nrel_numpy 12

Excel formula:

=SOLARPOSITION("2024-06-20T06:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 12)

Expected output:

Result
329.2 -24.8 114.8

Example 3: Demo case 3

Inputs:

time latitude longitude altitude pressure method temperature
2024-12-21T12:00:00Z 35 -120 100 90000 nrel_numpy 12

Excel formula:

=SOLARPOSITION("2024-12-21T12:00:00Z", 35, -120, 100, 90000, "nrel_numpy", 12)

Expected output:

Result
94.7 -36.8 126.8

Example 4: Demo case 4

Inputs:

time latitude longitude altitude pressure method temperature
2024-06-20T12:00:00Z 35 -120 0 101325 nrel_numpy 25

Excel formula:

=SOLARPOSITION("2024-06-20T12:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 25)

Expected output:

Result
53.2 -8.8 98.8

Python Code

import micropip
await micropip.install(["pvlib"])
import pandas as pd
from pvlib.solarposition import get_solarposition as pvlib_get_solarposition

def solarposition(time, latitude, longitude, altitude=0, pressure=101325, method='nrel_numpy', temperature=12):
    """
    Calculate solar azimuth, elevation, and apparent zenith for given times and location.

    See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.get_solarposition.html

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

    Args:
        time (str): The time value
        latitude (float): The latitude value
        longitude (float): The longitude value
        altitude (float, optional): The altitude value Default is 0.
        pressure (float, optional): The pressure value Default is 101325.
        method (str, optional): The method value Valid options: NREL NumPy, NREL Numba, PyEphem, Ephemeris, NREL C. Default is 'nrel_numpy'.
        temperature (float, optional): The temperature value Default is 12.

    Returns:
        list[list]: 2D list [[azimuth, elevation, apparent_zenith]], or error string.
    """
    # Helper function to normalize inputs
    def to2d(value):
        if not isinstance(value, list):
            return [[value]]
        return value

    # Normalize time input
    time_2d = to2d(time)

    # Validate time input format
    if not (isinstance(time_2d, list) and all(isinstance(row, list) and len(row) == 1 and isinstance(row[0], str) for row in time_2d)):
        return [["Error: Time must be a single string or a 2D list of single string rows."]]

    try:
        times = [row[0] for row in time_2d]
        dt_index = pd.DatetimeIndex(times)
    except Exception:
        return [["Error: Time values must be ISO8601 datetime strings."]]

    try:
        lat = float(latitude)
        lon = float(longitude)
        alt = float(altitude)
        pres = float(pressure)
        temp = float(temperature)
        meth = str(method)
    except Exception:
        return [["Error: Latitude, longitude, altitude, pressure, temperature must be numbers."]]

    try:
        df = pvlib_get_solarposition(
            time=dt_index,
            latitude=lat,
            longitude=lon,
            altitude=alt,
            pressure=pres,
            method=meth,
            temperature=temp
        )
        # Extract azimuth, elevation, apparent_zenith
        result = []
        for i in range(len(dt_index)):
            az = float(df.iloc[i]["azimuth"])
            el = float(df.iloc[i]["elevation"])
            zen = float(df.iloc[i]["apparent_zenith"])
            result.append([az, el, zen])  # Don't round as per guidelines
        return result
    except Exception as e:
        return [[f"Error: pvlib - {e}"]]

Online Calculator