graph TD
A[Start: What do you need?] --> B{Need sun angles\n(zenith/azimuth/elevation)?}
B -- Yes --> C{Have timestamps + site coords?}
C -- Yes --> D[Use SOLARPOSITION]
C -- No --> E{Have hour angle + declination?}
E -- Yes --> F[Use SOLAR_ZEN_AN and SOLAR_AZIM_AN]
E -- No --> G[Compute DECLINATION_SPENCER + EOT_SPENCER\nthen derive hour angle]
B -- No --> H{Need sunrise/sunset/transit times?}
H -- Yes --> I{Need high timing accuracy?}
I -- Yes --> J[Use SUN_RISE_SET_SPA]
I -- No --> K[Use SUN_RISE_SET_GEOM]
A --> L{Need Earth-Sun irradiance scaling?}
L -- Yes --> M[Use EARTHSUN_DISTANCE]
Solar Position
Overview
Introduction The category of solar position methods covers the set of calculations used to determine where the sun appears in the sky, and when key daily events such as sunrise, solar noon, and sunset occur. In mathematical terms, solar position converts time and site coordinates into geometric angles and event timestamps using celestial mechanics and spherical trigonometry. In practical terms, these calculations are foundational for photovoltaic (PV) system design, solar resource assessment, daylighting studies, thermal load modeling, and any workflow where irradiance depends on sun angle. A concise astronomical framing is available in the Solar zenith angle and Equation of time references, both of which map directly to the functions in this category.
For engineering users, solar position is not an academic side topic; it is the first dependency in a long modeling chain. Plane-of-array irradiance, incidence angle modifiers, tracker backtracking, row-to-row shading, and even inverter clipping diagnostics all depend on correct sun geometry. A one-degree bias in zenith or azimuth can propagate into energy-yield error, and a few minutes of sunrise/sunset drift can affect production windows, operating schedules, and dispatch assumptions. That is why this category includes both fast analytical approximations and higher-accuracy astronomical methods: different project phases need different speed-versus-fidelity tradeoffs.
Boardflare’s solar-position calculators are built on pvlib, specifically the pvlib.solarposition module, which is a standard open-source toolkit in PV engineering. The functions in this category are designed as interoperable pieces rather than isolated utilities. For example, DECLINATION_SPENCER and EOT_SPENCER provide fast geometric inputs that can feed SUN_RISE_SET_GEOM, while SOLARPOSITION and SUN_RISE_SET_SPA provide event and angle outputs from the NREL SPA family where higher precision is needed. This dual-path design supports both spreadsheet-scale throughput and engineering-grade validation.
Another important conceptual distinction is coordinate and unit handling. Some functions in this category expect radians for angular inputs (for example SOLAR_ZEN_AN and SOLAR_AZIM_AN), while others accept geographic latitude/longitude in degrees and timestamps with timezone context (for example SOLARPOSITION, SUN_RISE_SET_GEOM, and SUN_RISE_SET_SPA). Successful deployment therefore depends on treating data preparation as part of the model, not just pre-processing: consistent time zones, verified day-of-year mapping, and explicit unit conversion are core quality controls.
When to Use It Solar-position tools are best selected by the job to be done, not by familiarity with one formula. The first common job is PV energy simulation and performance diagnostics. In this workflow, analysts need a timestamped sun vector to transform global horizontal irradiance (GHI) into direct and diffuse components on a tilted plane. SOLARPOSITION is typically the starting point because it returns azimuth, elevation, and apparent zenith in one call and supports multiple algorithm methods. When high-accuracy event boundaries are required for clipping-window checks or curtailment accounting, SUN_RISE_SET_SPA becomes the preferred event-timing function. If analysts need fast annual sweeps or sensitivity studies before final validation, DECLINATION_SPENCER plus EOT_SPENCER can support coarse screening.
The second common job is controls and operations for tracking or sun-following systems. Tracker control logic often requires rapid updates of sun direction under strict compute constraints, especially in edge devices or digital twins running many site scenarios. In this context, SOLAR_ZEN_AN and SOLAR_AZIM_AN are useful when the model already has hour angle and declination available, because they are direct analytical evaluations with low overhead. These are particularly useful in deterministic simulations where the engineering team controls all geometric inputs and wants transparent formula-level behavior. For production supervisory logic where atmospheric refraction and robust timestamp handling matter, SOLARPOSITION provides a safer operational interface.
The third job is planning and permitting workflows that need sunrise/sunset and solar-noon events for specific geographies. Examples include defining maintenance windows, estimating daylight construction periods, or validating expected generation windows in power-purchase analysis. SUN_RISE_SET_SPA is preferred when contractual, compliance, or settlement decisions depend on timing precision. SUN_RISE_SET_GEOM is appropriate when users need a lightweight approximation, understand its assumptions, and mainly care about comparative trends across many dates rather than minute-level absolute accuracy.
The fourth job is resource normalization and extraterrestrial irradiance scaling. Seasonal Earth–Sun distance variation changes top-of-atmosphere irradiance by a few percent, which is material in calibration and clear-sky normalization tasks. EARTHSUN_DISTANCE provides this correction using SPA-based distance estimates in astronomical units, making it useful in clear-sky benchmarking and quality-control pipelines where expected irradiance envelopes are date dependent.
A final job is education, model explainability, and audit trails. Teams that must explain why a modeled outcome changed between datasets often need decomposed geometry terms. Here, DECLINATION_SPENCER, EOT_SPENCER, SOLAR_ZEN_AN, and SOLAR_AZIM_AN are especially valuable because each quantity is explicit and traceable. This decomposition supports peer review, validation notebooks, and clear handoff between data, controls, and performance teams.
How It Works Solar-position modeling starts with Earth–Sun geometry. At a high level, the apparent sun position depends on date, time, observer latitude/longitude, and the Earth’s orbital/rotational configuration. Two core intermediate terms are solar declination \delta and hour angle h. Declination expresses the apparent tilt of the sun relative to the equatorial plane. Hour angle converts local solar time into angular displacement from solar noon. In many engineering pipelines, these terms are sufficient to compute zenith and azimuth analytically.
For fast declination and time-correction estimates, this category includes Spencer-1971 approximations. DECLINATION_SPENCER maps day-of-year n to \delta, and EOT_SPENCER provides equation-of-time offsets used to convert clock time to apparent solar time. The equation of time captures the seasonal mismatch between mean solar time and true apparent solar time due to orbital eccentricity and axial tilt. A common correction structure is:
t_{\text{solar}} = t_{\text{clock}} + \frac{4(\lambda_{\text{std}}-\lambda)}{60} + \frac{\mathrm{EOT}}{60}
where longitude terms are in degrees and EOT is in minutes. These approximations are computationally efficient and often adequate for screening, comparative studies, or as inputs into simplified sunrise/sunset methods.
Given \delta, h, and latitude \phi, solar zenith follows from spherical geometry. SOLAR_ZEN_AN evaluates:
\cos(\theta_z)=\sin(\phi)\sin(\delta)+\cos(\phi)\cos(\delta)\cos(h)
where \theta_z is the solar zenith angle. This relation is central to irradiance transposition because incidence on a tilted surface depends directly on sun-vector orientation. Solar azimuth is then resolved with SOLAR_AZIM_AN, which uses analytical trigonometric relations and quadrant handling to place the horizontal direction correctly. In production models, azimuth conventions (clockwise from north, or alternate definitions) must be verified before integrating with tracker or shading logic.
The category also supports full timestamp-to-angle computation through SOLARPOSITION, which wraps pvlib’s get_solarposition interface. This function accepts time, site coordinates, and atmospheric inputs and returns azimuth, elevation, and apparent zenith. Apparent zenith includes atmospheric refraction correction, which can matter near the horizon and in event boundary calculations. Because SOLARPOSITION exposes method selection (nrel_numpy, nrel_numba, and others), teams can choose a runtime profile that balances speed and deployment constraints while preserving a consistent API.
Event timing functions solve a related but distinct problem: rather than “where is the sun now?”, they ask “when does the sun cross key geometric thresholds today?”. SUN_RISE_SET_GEOM uses a geometric approximation with declination and equation-of-time inputs. It is fast and transparent, and it is useful when upstream inputs are already available from DECLINATION_SPENCER and EOT_SPENCER. The canonical sunrise/sunset condition is based on the event hour angle H_0:
\cos(H_0) = -\tan(\phi)\tan(\delta)
from which sunrise and sunset times are inferred after applying longitude and time corrections.
For higher fidelity, SUN_RISE_SET_SPA computes sunrise, sunset, and transit with the NREL Solar Position Algorithm (SPA), a widely used astronomical method in PV engineering. This is generally preferred when minute-level timing differences matter, especially at high latitudes, near solstice conditions, or in reporting contexts where reproducibility and known algorithmic pedigree are required. EARTHSUN_DISTANCE complements these calculations by quantifying Earth–Sun distance in AU, supporting extraterrestrial irradiance scaling:
E_{0n} = E_{sc}\left(\frac{1}{d^2}\right)
where E_{sc} is the solar constant and d is Earth–Sun distance in AU.
Across all functions, assumptions and data quality boundaries should be explicit. Key assumptions include: accurate timestamp parsing with timezone awareness; correct unit discipline (degrees vs radians); physically realistic latitude/longitude ranges; and valid day-of-year indexing for leap years when applicable. In addition, users should separate approximation error from input error: coarse formulas may be acceptable for trend analysis but not for contractual timing, while even high-accuracy methods fail if time zones or offsets are wrong.
Practical Example Consider a utility-scale PV analyst building a one-day production diagnostics workflow for a site at latitude 39.742^\circ and longitude -105.179^\circ. The analyst needs to answer three questions: (1) what are the sunrise/sunset windows used for operations planning, (2) what is the sun path during plant operating hours, and (3) whether a quick approximation workflow is close enough to the high-accuracy benchmark for daily reporting.
Step 1 is event-window setup. The analyst computes sunrise, sunset, and transit with SUN_RISE_SET_SPA using timezone-aware timestamps and a suitable \Delta T value. This establishes the benchmark day boundary for dispatch and reporting. In parallel, the analyst computes SUN_RISE_SET_GEOM using declination and equation-of-time inputs to get a fast approximation. The difference between these two outputs provides an immediate error envelope for “fast mode” operations.
Step 2 is geometry decomposition for explainability. The analyst evaluates DECLINATION_SPENCER and EOT_SPENCER for the date sequence and then computes SOLAR_ZEN_AN and SOLAR_AZIM_AN over the hour-angle grid. This path gives transparent intermediate values that are easy to audit and communicate. If a control engineer asks why a tracker orientation changed at a specific hour, the team can trace the exact chain from day-of-year to declination to hour angle to azimuth/zenith.
Step 3 is production-model alignment. The analyst runs SOLARPOSITION on the same timestamps to get azimuth, elevation, and apparent zenith in one consistent API call. These outputs feed irradiance transposition and incidence-angle modeling. Because SOLARPOSITION includes atmospheric/refraction-aware quantities, it usually becomes the operational source of truth for energy simulation, while analytical outputs remain useful for QA and sensitivity checks.
Step 4 is extraterrestrial scaling. The analyst computes EARTHSUN_DISTANCE for the day and applies the 1/d^2 correction to normalize expected top-of-atmosphere irradiance. This helps distinguish geometry-driven seasonal effects from sensor bias or cloud impacts when comparing observed and modeled performance.
Step 5 is acceptance criteria. The team defines practical tolerances: for example, approximate event times may be acceptable for early-stage planning if they remain within an internal threshold versus SPA outputs, while monthly performance reporting always uses SUN_RISE_SET_SPA and SOLARPOSITION. This dual workflow preserves speed for iterative analysis and rigor for external-facing results.
Operationally, this approach replaces fragile manual spreadsheet logic with explicit, reusable calculator calls. The key gain is not only numerical consistency but also model governance: each output can be tied to a known algorithm, documented assumptions, and reproducible inputs.
How to Choose Choosing the right calculator in this category is primarily a question of output type (angles vs events), required accuracy, and available inputs. A robust selection process starts with the decision tree below and then uses function-level guidance.
For direct angle outputs from timestamped data, SOLARPOSITION is generally the default. It is the best fit when teams need azimuth, elevation, and apparent zenith together, especially in bankable or operational simulations. It reduces integration risk because it handles timestamp parsing and algorithm selection in one interface. The tradeoff is slightly more computational overhead than purely analytical formulas.
When users already have hour angle and declination (for example from a custom astronomical preprocessing pipeline), SOLAR_ZEN_AN and SOLAR_AZIM_AN are efficient and explicit. Their strength is transparency and speed; their limitation is that input preparation burden shifts to the user. They are excellent for deterministic studies and educational audits, but they require strict unit discipline and correct quadrant handling assumptions.
For fast date-dependent primitives, DECLINATION_SPENCER and EOT_SPENCER are the right pair. They support quick-turn studies, Monte Carlo-like sweeps, and analytical pipelines where approximate geometry is sufficient. Their limitation is approximation fidelity versus SPA-based methods. In practice, they are often used upstream of SUN_RISE_SET_GEOM or custom hour-angle calculations.
For sunrise/sunset/transit events, select between SUN_RISE_SET_GEOM and SUN_RISE_SET_SPA based on consequence of error. SUN_RISE_SET_GEOM is lightweight and useful for comparative planning across large date ranges. SUN_RISE_SET_SPA is preferred for compliance, contracts, performance guarantees, and high-latitude edge cases. A common governance pattern is to prototype with geometric events and finalize with SPA events.
Use EARTHSUN_DISTANCE whenever extraterrestrial irradiance normalization matters. It is not a substitute for sun-angle or event calculations; it is a complementary correction that improves seasonal consistency in clear-sky and benchmarking workflows.
A practical selection matrix is: choose SOLARPOSITION for timestamped angle products; choose SOLAR_ZEN_AN/SOLAR_AZIM_AN for formula-driven geometry with known intermediate terms; choose SUN_RISE_SET_SPA for precise events; choose SUN_RISE_SET_GEOM for rapid approximations; choose DECLINATION_SPENCER and EOT_SPENCER for lightweight date corrections; and choose EARTHSUN_DISTANCE for top-of-atmosphere scaling. This mapping keeps models both efficient and fit for purpose.
DECLINATION_SPENCER
This function computes solar declination, the angle between the Earth-sun line and the equatorial plane, from day-of-year values using the Spencer (1971) Fourier approximation.
Declination is a core geometric input for solar zenith, azimuth, sunrise/sunset timing, and plane-of-array irradiance models.
In solar-geometry notation, declination enters relationships such as:
\cos(\theta_z) = \sin(\phi)\sin(\delta) + \cos(\phi)\cos(\delta)\cos(h)
where \theta_z is solar zenith, \phi is latitude, \delta is declination, and h is hour angle.
Excel Usage
=DECLINATION_SPENCER(dayofyear)
dayofyear(list[list], required): Day of the year (1 to 366).
Returns (list[list]): 2D list of declination angles (radians), or an error string.
Example 1: Declination at summer solstice
Inputs:
| dayofyear |
|---|
| 172 |
Excel formula:
=DECLINATION_SPENCER({172})
Expected output:
0.409315
Example 2: Declination near March equinox
Inputs:
| dayofyear |
|---|
| 80 |
Excel formula:
=DECLINATION_SPENCER({80})
Expected output:
-0.00115059
Example 3: Declination near December solstice
Inputs:
| dayofyear |
|---|
| 355 |
Excel formula:
=DECLINATION_SPENCER({355})
Expected output:
-0.408754
Example 4: Scalar day-of-year input
Inputs:
| dayofyear |
|---|
| 1 |
Excel formula:
=DECLINATION_SPENCER(1)
Expected output:
-0.402449
Python Code
Show Code
import pandas as pd
import numpy as np
from pvlib.solarposition import declination_spencer71 as result_func
def declination_spencer(dayofyear):
"""
Compute the solar declination angle using Spencer's (1971) formula.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.declination_spencer71.html
This example function is provided as-is without any representation of accuracy.
Args:
dayofyear (list[list]): Day of the year (1 to 366).
Returns:
list[list]: 2D list of declination angles (radians), 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
doy_list = flatten_num(dayofyear)
if len(doy_list) == 0:
return "Error: input array cannot be empty"
res = result_func(np.array(doy_list))
return [[float(v) if not pd.isna(v) else ""] for v in res]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
EARTHSUN_DISTANCE
This function computes the Earth-sun distance for one or more timestamps using the NREL SPA model.
The result is returned in astronomical units (AU) and is commonly used to scale extraterrestrial irradiance in clear-sky and transposition calculations.
A common relation is:
E_{0n} = E_{sc}\left(\frac{1}{d^2}\right)
where E_{0n} is extraterrestrial normal irradiance, E_{sc} is the solar constant, and d is Earth-sun distance in AU.
Excel Usage
=EARTHSUN_DISTANCE(times, delta_t)
times(list[list], required): Timestamps for the distance calculation (ISO8601 format).delta_t(float, optional, default: 67): Difference between terrestrial time and UT1 (seconds).
Returns (list[list]): 2D list of distances (AU), or an error string.
Example 1: Earth-Sun distance at winter solstice
Inputs:
| times | delta_t |
|---|---|
| 2024-12-21T12:00:00Z | 67 |
Excel formula:
=EARTHSUN_DISTANCE({"2024-12-21T12:00:00Z"}, 67)
Expected output:
0.983724
Example 2: Earth-Sun distance near perihelion
Inputs:
| times | delta_t |
|---|---|
| 2024-01-03T12:00:00Z | 67 |
Excel formula:
=EARTHSUN_DISTANCE({"2024-01-03T12:00:00Z"}, 67)
Expected output:
0.983307
Example 3: Earth-Sun distance near aphelion
Inputs:
| times | delta_t |
|---|---|
| 2024-07-04T12:00:00Z | 67 |
Excel formula:
=EARTHSUN_DISTANCE({"2024-07-04T12:00:00Z"}, 67)
Expected output:
1.01672
Example 4: Scalar timestamp input
Inputs:
| times | delta_t |
|---|---|
| 2024-03-20T12:00:00Z | 67 |
Excel formula:
=EARTHSUN_DISTANCE("2024-03-20T12:00:00Z", 67)
Expected output:
0.995965
Python Code
Show Code
import pandas as pd
from pvlib.solarposition import nrel_earthsun_distance as result_func
def earthsun_distance(times, delta_t=67):
"""
Calculate the Earth-Sun distance in AU using the NREL SPA algorithm.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.nrel_earthsun_distance.html
This example function is provided as-is without any representation of accuracy.
Args:
times (list[list]): Timestamps for the distance calculation (ISO8601 format).
delta_t (float, optional): Difference between terrestrial time and UT1 (seconds). Default is 67.
Returns:
list[list]: 2D list of distances (AU), 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: times array cannot be empty"
dt = float(delta_t) if delta_t is not None else 67.0
idx = pd.DatetimeIndex(time_list)
res = result_func(
time=idx,
delta_t=dt
)
return [[float(v)] for v in res]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
EOT_SPENCER
This function computes the equation of time (EOT), which quantifies the offset between apparent solar time and mean solar time as a function of day of year.
EOT is a key correction used to map civil clock time into solar geometry calculations.
Solar time can be expressed as:
t_{solar} = t_{clock} + \frac{4(\lambda_{std}-\lambda)}{60} + \frac{\mathrm{EOT}}{60}
where longitudes are in degrees and EOT is in minutes.
Excel Usage
=EOT_SPENCER(dayofyear)
dayofyear(list[list], required): Day of the year (1 to 366).
Returns (list[list]): 2D list of equation of time offsets (minutes), or an error string.
Example 1: EOT at summer solstice
Inputs:
| dayofyear |
|---|
| 172 |
Excel formula:
=EOT_SPENCER({172})
Expected output:
-1.34372
Example 2: EOT near March equinox
Inputs:
| dayofyear |
|---|
| 80 |
Excel formula:
=EOT_SPENCER({80})
Expected output:
-7.87367
Example 3: EOT near December solstice
Inputs:
| dayofyear |
|---|
| 355 |
Excel formula:
=EOT_SPENCER({355})
Expected output:
2.15509
Example 4: Scalar day-of-year input
Inputs:
| dayofyear |
|---|
| 1 |
Excel formula:
=EOT_SPENCER(1)
Expected output:
-2.91968
Python Code
Show Code
import pandas as pd
import numpy as np
from pvlib.solarposition import equation_of_time_spencer71 as result_func
def eot_spencer(dayofyear):
"""
Compute the equation of time (EOT) using Spencer's (1971) formula.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.equation_of_time_spencer71.html
This example function is provided as-is without any representation of accuracy.
Args:
dayofyear (list[list]): Day of the year (1 to 366).
Returns:
list[list]: 2D list of equation of time offsets (minutes), 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
doy_list = flatten_num(dayofyear)
if len(doy_list) == 0:
return "Error: input array cannot be empty"
res = result_func(np.array(doy_list))
return [[float(v) if not pd.isna(v) else ""] for v in res]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SOLAR_AZIM_AN
This function computes solar azimuth from latitude, hour angle, declination, and zenith using an analytical spherical-trigonometry relation.
Azimuth describes the sun’s horizontal direction and is useful for tracker control, façade shading, and incidence-angle calculations.
A common form is:
\sin(\gamma_s)=\frac{\cos(\delta)\sin(h)}{\sin(\theta_z)}
with quadrant handling applied to obtain the final azimuth angle.
Excel Usage
=SOLAR_AZIM_AN(latitude, hourangle, declination, zenith)
latitude(float, required): Site latitude (radians).hourangle(list[list], required): Solar hour angle (radians).declination(list[list], required): Solar declination angle (radians).zenith(list[list], required): Solar zenith angle (radians).
Returns (list[list]): 2D list of solar azimuth angles (radians), or an error string.
Example 1: Solar azimuth at solar noon in radians
Inputs:
| latitude | hourangle | declination | zenith |
|---|---|---|---|
| 0.6936 | 0 | 0.4091 | 0.2845 |
Excel formula:
=SOLAR_AZIM_AN(0.6936, {0}, {0.4091}, {0.2845})
Expected output:
3.14159
Example 2: Morning hour angle gives eastward azimuth
Inputs:
| latitude | hourangle | declination | zenith |
|---|---|---|---|
| 0.6936 | -0.5 | 0.2 | 0.8 |
Excel formula:
=SOLAR_AZIM_AN(0.6936, {-0.5}, {0.2}, {0.8})
Expected output:
2.03455
Example 3: Afternoon hour angle gives westward azimuth
Inputs:
| latitude | hourangle | declination | zenith |
|---|---|---|---|
| 0.6936 | 0.5 | 0.2 | 0.8 |
Excel formula:
=SOLAR_AZIM_AN(0.6936, {0.5}, {0.2}, {0.8})
Expected output:
4.24863
Example 4: Multiple solar geometry rows
Inputs:
| latitude | hourangle | declination | zenith |
|---|---|---|---|
| 0.6936 | -0.3 | 0.1 | 0.9 |
| 0.3 | 0.1 | 0.9 |
Excel formula:
=SOLAR_AZIM_AN(0.6936, {-0.3;0.3}, {0.1;0.1}, {0.9;0.9})
Expected output:
| Result |
|---|
| 2.0875 |
| 4.19568 |
Python Code
Show Code
import pandas as pd
import numpy as np
from pvlib.solarposition import solar_azimuth_analytical as result_func
def solar_azim_an(latitude, hourangle, declination, zenith):
"""
Calculate the solar azimuth angle using an analytical expression.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.solar_azimuth_analytical.html
This example function is provided as-is without any representation of accuracy.
Args:
latitude (float): Site latitude (radians).
hourangle (list[list]): Solar hour angle (radians).
declination (list[list]): Solar declination angle (radians).
zenith (list[list]): Solar zenith angle (radians).
Returns:
list[list]: 2D list of solar azimuth angles (radians), 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
ha_list = flatten_num(hourangle)
dec_list = flatten_num(declination)
zen_list = flatten_num(zenith)
n = len(ha_list)
if n == 0 or len(dec_list) != n or len(zen_list) != n:
return "Error: All input arrays must have the same non-zero length"
lat = float(latitude)
res = result_func(
latitude=lat,
hourangle=np.array(ha_list),
declination=np.array(dec_list),
zenith=np.array(zen_list)
)
return [[float(v) if not pd.isna(v) else ""] for v in res]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SOLAR_ZEN_AN
This function computes solar zenith from latitude, hour angle, and declination using an analytical spherical-geometry expression.
Zenith is the angle between the sun vector and local vertical and is a primary quantity for irradiance decomposition and transposition.
The analytical relation is:
\cos(\theta_z)=\sin(\phi)\sin(\delta)+\cos(\phi)\cos(\delta)\cos(h)
where \phi is latitude, \delta is declination, and h is hour angle, all in radians.
Excel Usage
=SOLAR_ZEN_AN(latitude, hourangle, declination)
latitude(float, required): Site latitude (radians).hourangle(list[list], required): Solar hour angle (radians).declination(list[list], required): Solar declination angle (radians).
Returns (list[list]): 2D list of solar zenith angles (radians), or an error string.
Example 1: Solar zenith at solar noon in radians
Inputs:
| latitude | hourangle | declination |
|---|---|---|
| 0.6936 | 0 | 0.4091 |
Excel formula:
=SOLAR_ZEN_AN(0.6936, {0}, {0.4091})
Expected output:
0.2845
Example 2: Zenith angle for morning hour angle
Inputs:
| latitude | hourangle | declination |
|---|---|---|
| 0.6936 | -0.5 | 0.2 |
Excel formula:
=SOLAR_ZEN_AN(0.6936, {-0.5}, {0.2})
Expected output:
0.662631
Example 3: Zenith angle for afternoon hour angle
Inputs:
| latitude | hourangle | declination |
|---|---|---|
| 0.6936 | 0.5 | 0.2 |
Excel formula:
=SOLAR_ZEN_AN(0.6936, {0.5}, {0.2})
Expected output:
0.662631
Example 4: Multiple hour angles in one call
Inputs:
| latitude | hourangle | declination |
|---|---|---|
| 0.6936 | -0.3 | 0.1 |
| 0.3 | 0.1 |
Excel formula:
=SOLAR_ZEN_AN(0.6936, {-0.3;0.3}, {0.1;0.1})
Expected output:
| Result |
|---|
| 0.652184 |
| 0.652184 |
Python Code
Show Code
import pandas as pd
import numpy as np
from pvlib.solarposition import solar_zenith_analytical as result_func
def solar_zen_an(latitude, hourangle, declination):
"""
Calculate the solar zenith angle using an analytical expression.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.solar_zenith_analytical.html
This example function is provided as-is without any representation of accuracy.
Args:
latitude (float): Site latitude (radians).
hourangle (list[list]): Solar hour angle (radians).
declination (list[list]): Solar declination angle (radians).
Returns:
list[list]: 2D list of solar zenith angles (radians), 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
ha_list = flatten_num(hourangle)
dec_list = flatten_num(declination)
n = len(ha_list)
if n == 0 or len(dec_list) != n:
return "Error: hourangle and declination arrays must have the same non-zero length"
lat = float(latitude)
res = result_func(
latitude=lat,
hourangle=np.array(ha_list),
declination=np.array(dec_list)
)
return [[float(v) if not pd.isna(v) else ""] for v in res]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SOLARPOSITION
This function computes solar position angles for one or more timestamps at a specified site.
For each timestamp, it returns azimuth, elevation, and apparent zenith using one of pvlib’s solar-position algorithms.
Elevation and zenith are related by:
\mathrm{elevation} = 90^\circ - \mathrm{zenith}
Apparent zenith includes atmospheric refraction and is commonly preferred over true zenith for irradiance modeling.
Excel Usage
=SOLARPOSITION(time, latitude, longitude, altitude, pressure, solarpos_meth, temperature)
time(list[list], required): Timestamp or 2D range of timestamps in ISO8601 format.latitude(float, required): Latitude north of equator (deg).longitude(float, required): Longitude east of prime meridian (deg).altitude(float, optional, default: 0): Site elevation above sea level (m).pressure(float, optional, default: 101325): Atmospheric pressure (Pa).solarpos_meth(str, optional, default: “nrel_numpy”): Solar position algorithm method.temperature(float, optional, default: 12): Ambient temperature used in refraction calculation (deg C).
Returns (list[list]): 2D list with one row per timestamp containing azimuth, elevation, and apparent zenith in degrees, or an error string.
Example 1: Summer midday position at sea level
Inputs:
| time | latitude | longitude | altitude | pressure | solarpos_meth | 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.1775 | -8.77273 | 98.7727 |
Example 2: Summer morning position at sea level
Inputs:
| time | latitude | longitude | altitude | pressure | solarpos_meth | 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.216 | -24.8381 | 114.838 |
Example 3: Winter midday position at higher altitude
Inputs:
| time | latitude | longitude | altitude | pressure | solarpos_meth | 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.7359 | -36.8049 | 126.805 |
Example 4: Row vector of timestamps
Inputs:
| time | latitude | longitude | altitude | pressure | solarpos_meth | temperature | |
|---|---|---|---|---|---|---|---|
| 2024-06-20T12:00:00Z | 2024-06-20T18:00:00Z | 35 | -120 | 0 | 101325 | nrel_numpy | 12 |
Excel formula:
=SOLARPOSITION({"2024-06-20T12:00:00Z","2024-06-20T18:00:00Z"}, 35, -120, 0, 101325, "nrel_numpy", 12)
Expected output:
| Result | ||
|---|---|---|
| 53.1775 | -8.77273 | 98.7727 |
| 105.383 | 61.176 | 28.8148 |
Example 5: Winter morning position at sea level
Inputs:
| time | latitude | longitude | altitude | pressure | solarpos_meth | temperature |
|---|---|---|---|---|---|---|
| 2024-12-21T09:00:00Z | 35 | -120 | 0 | 101325 | nrel_numpy | 12 |
Excel formula:
=SOLARPOSITION("2024-12-21T09:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 12)
Expected output:
| Result | ||
|---|---|---|
| 53.4021 | -72.2847 | 162.285 |
Python Code
Show Code
import pandas as pd
from pvlib.solarposition import get_solarposition as pvlib_get_solarposition
def solarposition(time, latitude, longitude, altitude=0, pressure=101325, solarpos_meth='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 (list[list]): Timestamp or 2D range of timestamps in ISO8601 format.
latitude (float): Latitude north of equator (deg).
longitude (float): Longitude east of prime meridian (deg).
altitude (float, optional): Site elevation above sea level (m). Default is 0.
pressure (float, optional): Atmospheric pressure (Pa). Default is 101325.
solarpos_meth (str, optional): Solar position algorithm method. Valid options: NREL NumPy, NREL Numba, PyEphem, Ephemeris, NREL C. Default is 'nrel_numpy'.
temperature (float, optional): Ambient temperature used in refraction calculation (deg C). Default is 12.
Returns:
list[list]: 2D list with one row per timestamp containing azimuth, elevation, and apparent zenith in degrees, or an error string.
"""
try:
def flatten_str(data):
if not isinstance(data, list):
return [str(data)]
flat = []
for row in data:
row = row if isinstance(row, list) else [row]
for value in row:
if value == "":
continue
flat.append(str(value))
return flat
times = flatten_str(time)
if len(times) == 0:
return "Error: time array cannot be empty"
dt_index = pd.DatetimeIndex(times)
lat = float(latitude)
lon = float(longitude)
alt = float(altitude)
pres = float(pressure)
temp = float(temperature)
meth = str(solarpos_meth)
valid_methods = ['nrel_numpy', 'nrel_numba', 'pyephem', 'ephemeris', 'nrel_c']
if meth not in valid_methods:
return "Error: solarpos_meth must be one of nrel_numpy, nrel_numba, pyephem, ephemeris, nrel_c"
df = pvlib_get_solarposition(
time=dt_index,
latitude=lat,
longitude=lon,
altitude=alt,
pressure=pres,
method=meth,
temperature=temp
)
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])
return result
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SUN_RISE_SET_GEOM
This function estimates sunrise, sunset, and solar transit times using a geometric approximation based on declination and equation of time.
The method assumes a circular orbit and neglects atmospheric refraction, so it is faster but less accurate than SPA-based event timing.
Event hour-angle geometry is based on relations such as:
\cos(H_0) = -\tan(\phi)\tan(\delta)
where H_0 is the sunrise/sunset hour angle, \phi is latitude, and \delta is declination.
Excel Usage
=SUN_RISE_SET_GEOM(times, latitude, longitude, declination, equation_of_time)
times(list[list], required): Timestamps for the events (ISO8601 format).latitude(float, required): Site latitude (degrees).longitude(float, required): Site longitude (degrees).declination(list[list], required): Solar declination angle (radians).equation_of_time(list[list], required): Equation of time offset (minutes).
Returns (list[list]): 2D list [[sunrise, sunset, transit]] as ISO8601 strings, or an error string.
Example 1: Geometric events for basic inputs
Inputs:
| times | latitude | longitude | declination | equation_of_time |
|---|---|---|---|---|
| 2024-06-21T12:00:00-06:00 | 39.742 | -105.179 | 0.4091 | -1.5 |
Excel formula:
=SUN_RISE_SET_GEOM({"2024-06-21T12:00:00-06:00"}, 39.742, -105.179, {0.4091}, {-1.5})
Expected output:
| Result | ||
|---|---|---|
| 2024-06-21T05:37:41.734449192-06:00 | 2024-06-21T20:26:44.185550809-06:00 | 2024-06-21T13:02:12.959999998-06:00 |
Example 2: Geometric events for winter solstice
Inputs:
| times | latitude | longitude | declination | equation_of_time |
|---|---|---|---|---|
| 2024-12-21T12:00:00-07:00 | 39.742 | -105.179 | -0.4091 | 2 |
Excel formula:
=SUN_RISE_SET_GEOM({"2024-12-21T12:00:00-07:00"}, 39.742, -105.179, {-0.4091}, {2})
Expected output:
| Result | ||
|---|---|---|
| 2024-12-21T07:23:14.185550806-07:00 | 2024-12-21T16:34:11.734449193-07:00 | 2024-12-21T11:58:42.960000-07:00 |
Example 3: Geometric events near equinox
Inputs:
| times | latitude | longitude | declination | equation_of_time |
|---|---|---|---|---|
| 2024-03-20T12:00:00-06:00 | 39.742 | -105.179 | 0 | -7.5 |
Excel formula:
=SUN_RISE_SET_GEOM({"2024-03-20T12:00:00-06:00"}, 39.742, -105.179, {0}, {-7.5})
Expected output:
| Result | ||
|---|---|---|
| 2024-03-20T07:08:12.959999998-06:00 | 2024-03-20T19:08:12.959999998-06:00 | 2024-03-20T13:08:12.959999998-06:00 |
Example 4: Scalar timestamp geometric events
Inputs:
| times | latitude | longitude | declination | equation_of_time |
|---|---|---|---|---|
| 2024-06-21T12:00:00-06:00 | 39.742 | -105.179 | 0.4091 | -1.5 |
Excel formula:
=SUN_RISE_SET_GEOM("2024-06-21T12:00:00-06:00", 39.742, -105.179, {0.4091}, {-1.5})
Expected output:
| Result | ||
|---|---|---|
| 2024-06-21T05:37:41.734449192-06:00 | 2024-06-21T20:26:44.185550809-06:00 | 2024-06-21T13:02:12.959999998-06:00 |
Python Code
Show Code
import pandas as pd
import numpy as np
from pvlib.solarposition import sun_rise_set_transit_geometric as result_func
def sun_rise_set_geom(times, latitude, longitude, declination, equation_of_time):
"""
Geometric calculation of solar sunrise, sunset, and transit.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.sun_rise_set_transit_geometric.html
This example function is provided as-is without any representation of accuracy.
Args:
times (list[list]): Timestamps for the events (ISO8601 format).
latitude (float): Site latitude (degrees).
longitude (float): Site longitude (degrees).
declination (list[list]): Solar declination angle (radians).
equation_of_time (list[list]): Equation of time offset (minutes).
Returns:
list[list]: 2D list [[sunrise, sunset, transit]] as ISO8601 strings, 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 != ""]
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
time_list = flatten_str(times)
dec_list = flatten_num(declination)
eot_list = flatten_num(equation_of_time)
n = len(time_list)
if n == 0 or len(dec_list) != n or len(eot_list) != n:
return "Error: All input arrays must have the same non-zero length"
lat = float(latitude)
lon = float(longitude)
idx = pd.DatetimeIndex(time_list)
res = result_func(
times=idx,
latitude=lat,
longitude=lon,
declination=np.array(dec_list),
equation_of_time=np.array(eot_list)
)
# res is a tuple (sunrise, sunset, transit) which are DatetimeIndex-like Series
sr = res[0]
ss = res[1]
tr = res[2]
out = []
for i in range(n):
out.append([
sr[i].isoformat() if not pd.isna(sr[i]) else "",
ss[i].isoformat() if not pd.isna(ss[i]) else "",
tr[i].isoformat() if not pd.isna(tr[i]) else ""
])
return out
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SUN_RISE_SET_SPA
This function computes sunrise, sunset, and solar transit times for localized timestamps using the NREL Solar Position Algorithm (SPA).
SPA is a high-accuracy astronomical method used widely in PV and solar-resource workflows where event-timing precision matters.
Conceptually, sunrise and sunset occur near the condition:
\mathrm{zenith} \approx 90^\circ
with corrections from detailed SPA geometry, refraction, and time-scale terms.
Excel Usage
=SUN_RISE_SET_SPA(times, latitude, longitude, delta_t)
times(list[list], required): Timestamps for which to calculate events (ISO8601 format).latitude(float, required): Site latitude (degrees).longitude(float, required): Site longitude (degrees).delta_t(float, optional, default: 67): Difference between terrestrial time and UT1 (seconds).
Returns (list[list]): 2D list [[sunrise, sunset, transit]] as ISO8601 strings, or an error string.
Example 1: Solstice events in Golden, CO
Inputs:
| times | latitude | longitude | delta_t |
|---|---|---|---|
| 2024-06-21T12:00:00-06:00 | 39.742 | -105.179 | 67 |
Excel formula:
=SUN_RISE_SET_SPA({"2024-06-21T12:00:00-06:00"}, 39.742, -105.179, 67)
Expected output:
| Result | ||
|---|---|---|
| 2024-06-21T05:33:02.276770560-06:00 | 2024-06-21T20:32:09.189224704-06:00 | 2024-06-21T13:02:42.069548544-06:00 |
Example 2: Winter solstice events in Golden, CO
Inputs:
| times | latitude | longitude | delta_t |
|---|---|---|---|
| 2024-12-21T12:00:00-07:00 | 39.742 | -105.179 | 67 |
Excel formula:
=SUN_RISE_SET_SPA({"2024-12-21T12:00:00-07:00"}, 39.742, -105.179, 67)
Expected output:
| Result | ||
|---|---|---|
| 2024-12-21T07:18:31.492916224-07:00 | 2024-12-21T16:39:47.010416128-07:00 | 2024-12-21T11:59:09.093026048-07:00 |
Example 3: Equinox events in Golden, CO
Inputs:
| times | latitude | longitude | delta_t |
|---|---|---|---|
| 2024-03-20T12:00:00-06:00 | 39.742 | -105.179 | 67 |
Excel formula:
=SUN_RISE_SET_SPA({"2024-03-20T12:00:00-06:00"}, 39.742, -105.179, 67)
Expected output:
| Result | ||
|---|---|---|
| 2024-03-20T07:03:07.839649792-06:00 | 2024-03-20T19:12:23.359081216-06:00 | 2024-03-20T13:07:56.168577536-06:00 |
Example 4: Scalar timestamp SPA events
Inputs:
| times | latitude | longitude | delta_t |
|---|---|---|---|
| 2024-06-21T12:00:00-06:00 | 39.742 | -105.179 | 67 |
Excel formula:
=SUN_RISE_SET_SPA("2024-06-21T12:00:00-06:00", 39.742, -105.179, 67)
Expected output:
| Result | ||
|---|---|---|
| 2024-06-21T05:33:02.276770560-06:00 | 2024-06-21T20:32:09.189224704-06:00 | 2024-06-21T13:02:42.069548544-06:00 |
Python Code
Show Code
import pandas as pd
from pvlib.solarposition import sun_rise_set_transit_spa as result_func
def sun_rise_set_spa(times, latitude, longitude, delta_t=67):
"""
Calculate sunrise, sunset, and solar transit times using the NREL SPA algorithm.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.sun_rise_set_transit_spa.html
This example function is provided as-is without any representation of accuracy.
Args:
times (list[list]): Timestamps for which to calculate events (ISO8601 format).
latitude (float): Site latitude (degrees).
longitude (float): Site longitude (degrees).
delta_t (float, optional): Difference between terrestrial time and UT1 (seconds). Default is 67.
Returns:
list[list]: 2D list [[sunrise, sunset, transit]] as ISO8601 strings, 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: times array cannot be empty"
lat = float(latitude)
lon = float(longitude)
dt = float(delta_t) if delta_t is not None else 67.0
idx = pd.DatetimeIndex(time_list)
res = result_func(
times=idx,
latitude=lat,
longitude=lon,
delta_t=dt
)
# res is a DataFrame with columns: sunrise, sunset, transit
out = []
for i in range(len(idx)):
sr = res['sunrise'].iloc[i]
ss = res['sunset'].iloc[i]
tr = res['transit'].iloc[i]
out.append([
sr.isoformat() if not pd.isna(sr) else "",
ss.isoformat() if not pd.isna(ss) else "",
tr.isoformat() if not pd.isna(tr) else ""
])
return out
except Exception as e:
return f"Error: {str(e)}"Online Calculator