2SolarPosition - High-level interface for solar position and radiation calculations
4This module provides a Python interface to the SolarPosition Helios plugin,
5offering comprehensive solar angle calculations, radiation modeling, and
6time-dependent solar functions for atmospheric physics and plant modeling.
9from typing
import List, Tuple, Optional, Union
10from .wrappers
import USolarPositionWrapper
as solar_wrapper
11from .Context
import Context
12from .plugins.registry
import get_plugin_registry
13from .exceptions
import HeliosError
14from .wrappers.DataTypes
import Time, Date, vec3, SphericalCoord
18 """Exception raised for SolarPosition-specific errors"""
24 High-level interface for solar position calculations and radiation modeling.
26 SolarPosition provides comprehensive solar angle calculations, radiation flux
27 modeling, sunrise/sunset time calculations, and atmospheric turbidity calibration.
28 The plugin automatically uses Context time/date for calculations or can be
29 initialized with explicit coordinates.
31 This class requires the native Helios library built with SolarPosition support.
32 Use context managers for proper resource cleanup.
35 Basic usage with Context coordinates:
36 >>> with Context() as context:
37 ... context.setDate(2023, 6, 21) # Summer solstice
38 ... context.setTime(12, 0) # Solar noon
39 ... with SolarPosition(context) as solar:
40 ... elevation = solar.getSunElevation()
41 ... print(f"Sun elevation: {elevation:.1f}°")
43 Usage with explicit coordinates:
44 >>> with Context() as context:
45 ... # Davis, California coordinates
46 ... with SolarPosition(context, utc_offset=-8, latitude=38.5, longitude=-121.7) as solar:
47 ... azimuth = solar.getSunAzimuth()
48 ... flux = solar.getSolarFlux(101325, 288.15, 0.6, 0.1)
49 ... print(f"Solar flux: {flux:.1f} W/m²")
52 def __init__(self, context: Context, utc_offset: Optional[float] =
None,
53 latitude: Optional[float] =
None, longitude: Optional[float] =
None):
55 Initialize SolarPosition with a Helios context.
58 context: Active Helios Context instance
59 utc_offset: UTC time offset in hours (-12 to +12). If provided with
60 latitude/longitude, creates plugin with explicit coordinates.
61 latitude: Latitude in degrees (-90 to +90). Required if utc_offset provided.
62 longitude: Longitude in degrees (-180 to +180). Required if utc_offset provided.
65 SolarPositionError: If plugin not available in current build
66 ValueError: If coordinate parameters are invalid or incomplete
67 RuntimeError: If plugin initialization fails
70 If coordinates are not provided, the plugin uses Context location settings.
71 Solar calculations depend on Context time/date - use context.setTime() and
72 context.setDate() to set the simulation time before calculations.
75 registry = get_plugin_registry()
76 if not registry.is_plugin_available(
'solarposition'):
78 "SolarPosition not available in current Helios library. "
79 "SolarPosition plugin availability depends on build configuration.\n"
81 "System requirements:\n"
82 " - Platforms: Windows, Linux, macOS\n"
83 " - Dependencies: None\n"
84 " - GPU: Not required\n"
86 "If you're seeing this error, the SolarPosition plugin may not be "
87 "properly compiled into your Helios library. Please rebuild PyHelios:\n"
88 " build_scripts/build_helios --clean"
92 if utc_offset
is not None or latitude
is not None or longitude
is not None:
94 if utc_offset
is None or latitude
is None or longitude
is None:
96 "If specifying coordinates, all three parameters must be provided: "
97 "utc_offset, latitude, longitude"
101 if utc_offset < -12.0
or utc_offset > 12.0:
102 raise ValueError(f
"UTC offset must be between -12 and +12 hours, got: {utc_offset}")
103 if latitude < -90.0
or latitude > 90.0:
104 raise ValueError(f
"Latitude must be between -90 and +90 degrees, got: {latitude}")
105 if longitude < -180.0
or longitude > 180.0:
106 raise ValueError(f
"Longitude must be between -180 and +180 degrees, got: {longitude}")
110 self.
_solar_pos = solar_wrapper.createSolarPositionWithCoordinates(
111 context.getNativePtr(), utc_offset, latitude, longitude
116 self.
_solar_pos = solar_wrapper.createSolarPosition(context.getNativePtr())
122 """Context manager entry"""
125 def __exit__(self, exc_type, exc_val, exc_tb):
126 """Context manager exit - cleanup resources"""
127 if hasattr(self,
'_solar_pos')
and self.
_solar_pos:
134 Get the sun elevation angle in degrees.
137 Sun elevation angle in degrees (0° = horizon, 90° = zenith)
140 SolarPositionError: If calculation fails
143 >>> elevation = solar.getSunElevation()
144 >>> print(f"Sun is {elevation:.1f}° above horizon")
147 return solar_wrapper.getSunElevation(self.
_solar_pos)
148 except Exception
as e:
153 Get the sun zenith angle in degrees.
156 Sun zenith angle in degrees (0° = zenith, 90° = horizon)
159 SolarPositionError: If calculation fails
162 >>> zenith = solar.getSunZenith()
163 >>> print(f"Sun zenith angle: {zenith:.1f}°")
166 return solar_wrapper.getSunZenith(self.
_solar_pos)
167 except Exception
as e:
172 Get the sun azimuth angle in degrees.
175 Sun azimuth angle in degrees (0° = North, 90° = East, 180° = South, 270° = West)
178 SolarPositionError: If calculation fails
181 >>> azimuth = solar.getSunAzimuth()
182 >>> print(f"Sun azimuth: {azimuth:.1f}° (compass bearing)")
185 return solar_wrapper.getSunAzimuth(self.
_solar_pos)
186 except Exception
as e:
192 Get the sun direction as a 3D unit vector.
195 vec3 representing the sun direction vector (x, y, z)
198 SolarPositionError: If calculation fails
201 >>> direction = solar.getSunDirectionVector()
202 >>> print(f"Sun direction vector: ({direction.x:.3f}, {direction.y:.3f}, {direction.z:.3f})")
205 direction_list = solar_wrapper.getSunDirectionVector(self.
_solar_pos)
206 return vec3(direction_list[0], direction_list[1], direction_list[2])
207 except Exception
as e:
212 Get the sun direction as spherical coordinates.
215 SphericalCoord with radius=1, elevation and azimuth in radians
218 SolarPositionError: If calculation fails
221 >>> spherical = solar.getSunDirectionSpherical()
222 >>> print(f"Spherical: r={spherical.radius}, elev={spherical.elevation:.3f}, az={spherical.azimuth:.3f}")
225 spherical_list = solar_wrapper.getSunDirectionSpherical(self.
_solar_pos)
227 radius=spherical_list[0],
228 elevation=spherical_list[1],
229 azimuth=spherical_list[2]
231 except Exception
as e:
235 def getSolarFlux(self, pressure_Pa: float, temperature_K: float,
236 humidity_rel: float, turbidity: float) -> float:
238 Calculate total solar flux with atmospheric parameters.
241 pressure_Pa: Atmospheric pressure in Pascals (e.g., 101325 for sea level)
242 temperature_K: Temperature in Kelvin (e.g., 288.15 for 15°C)
243 humidity_rel: Relative humidity as fraction (0.0-1.0)
244 turbidity: Atmospheric turbidity coefficient (typically 0.05-0.5)
247 Total solar flux in W/m²
250 ValueError: If atmospheric parameters are out of valid ranges
251 SolarPositionError: If calculation fails
254 >>> # Standard atmospheric conditions
255 >>> flux = solar.getSolarFlux(101325, 288.15, 0.6, 0.1)
256 >>> print(f"Total solar flux: {flux:.1f} W/m²")
259 return solar_wrapper.getSolarFlux(self.
_solar_pos, pressure_Pa, temperature_K, humidity_rel, turbidity)
260 except Exception
as e:
264 humidity_rel: float, turbidity: float) -> float:
266 Calculate PAR (Photosynthetically Active Radiation) solar flux.
269 pressure_Pa: Atmospheric pressure in Pascals
270 temperature_K: Temperature in Kelvin
271 humidity_rel: Relative humidity as fraction (0.0-1.0)
272 turbidity: Atmospheric turbidity coefficient
275 PAR solar flux in W/m² (wavelength range ~400-700 nm)
278 ValueError: If atmospheric parameters are invalid
279 SolarPositionError: If calculation fails
282 >>> par_flux = solar.getSolarFluxPAR(101325, 288.15, 0.6, 0.1)
283 >>> print(f"PAR flux: {par_flux:.1f} W/m²")
286 return solar_wrapper.getSolarFluxPAR(self.
_solar_pos, pressure_Pa, temperature_K, humidity_rel, turbidity)
287 except Exception
as e:
291 humidity_rel: float, turbidity: float) -> float:
293 Calculate NIR (Near-Infrared) solar flux.
296 pressure_Pa: Atmospheric pressure in Pascals
297 temperature_K: Temperature in Kelvin
298 humidity_rel: Relative humidity as fraction (0.0-1.0)
299 turbidity: Atmospheric turbidity coefficient
302 NIR solar flux in W/m² (wavelength range >700 nm)
305 ValueError: If atmospheric parameters are invalid
306 SolarPositionError: If calculation fails
309 >>> nir_flux = solar.getSolarFluxNIR(101325, 288.15, 0.6, 0.1)
310 >>> print(f"NIR flux: {nir_flux:.1f} W/m²")
313 return solar_wrapper.getSolarFluxNIR(self.
_solar_pos, pressure_Pa, temperature_K, humidity_rel, turbidity)
314 except Exception
as e:
318 humidity_rel: float, turbidity: float) -> float:
320 Calculate the diffuse fraction of solar radiation.
323 pressure_Pa: Atmospheric pressure in Pascals
324 temperature_K: Temperature in Kelvin
325 humidity_rel: Relative humidity as fraction (0.0-1.0)
326 turbidity: Atmospheric turbidity coefficient
329 Diffuse fraction as ratio (0.0-1.0) where:
330 - 0.0 = all direct radiation
331 - 1.0 = all diffuse radiation
334 ValueError: If atmospheric parameters are invalid
335 SolarPositionError: If calculation fails
338 >>> diffuse_fraction = solar.getDiffuseFraction(101325, 288.15, 0.6, 0.1)
339 >>> print(f"Diffuse fraction: {diffuse_fraction:.3f} ({diffuse_fraction*100:.1f}%)")
342 return solar_wrapper.getDiffuseFraction(self.
_solar_pos, pressure_Pa, temperature_K, humidity_rel, turbidity)
343 except Exception
as e:
349 Calculate sunrise time for the current date and location.
352 Time object with sunrise time (hour, minute, second)
355 SolarPositionError: If calculation fails
358 >>> sunrise = solar.getSunriseTime()
359 >>> print(f"Sunrise: {sunrise}") # Prints as HH:MM:SS
362 hour, minute, second = solar_wrapper.getSunriseTime(self.
_solar_pos)
363 return Time(hour, minute, second)
364 except Exception
as e:
369 Calculate sunset time for the current date and location.
372 Time object with sunset time (hour, minute, second)
375 SolarPositionError: If calculation fails
378 >>> sunset = solar.getSunsetTime()
379 >>> print(f"Sunset: {sunset}") # Prints as HH:MM:SS
382 hour, minute, second = solar_wrapper.getSunsetTime(self.
_solar_pos)
383 return Time(hour, minute, second)
384 except Exception
as e:
390 Calibrate atmospheric turbidity using timeseries data.
393 timeseries_label: Label of timeseries data in Context
396 ValueError: If timeseries label is invalid
397 SolarPositionError: If calibration fails
400 >>> solar.calibrateTurbidityFromTimeseries("solar_irradiance")
402 if not timeseries_label:
403 raise ValueError(
"Timeseries label cannot be empty")
406 solar_wrapper.calibrateTurbidityFromTimeseries(self.
_solar_pos, timeseries_label)
407 except Exception
as e:
412 Enable cloud calibration using timeseries data.
415 timeseries_label: Label of cloud timeseries data in Context
418 ValueError: If timeseries label is invalid
419 SolarPositionError: If calibration setup fails
422 >>> solar.enableCloudCalibration("cloud_cover")
424 if not timeseries_label:
425 raise ValueError(
"Timeseries label cannot be empty")
428 solar_wrapper.enableCloudCalibration(self.
_solar_pos, timeseries_label)
429 except Exception
as e:
434 Disable cloud calibration.
437 SolarPositionError: If operation fails
440 >>> solar.disableCloudCalibration()
443 solar_wrapper.disableCloudCalibration(self.
_solar_pos)
444 except Exception
as e:
451 Check if SolarPosition is available in current build.
454 True if plugin is available, False otherwise
456 registry = get_plugin_registry()
457 return registry.is_plugin_available(
'solarposition')
462 latitude: Optional[float] =
None, longitude: Optional[float] =
None) -> SolarPosition:
464 Create SolarPosition instance with context and optional coordinates.
467 context: Helios Context
468 utc_offset: UTC time offset in hours (optional)
469 latitude: Latitude in degrees (optional)
470 longitude: Longitude in degrees (optional)
473 SolarPosition instance
476 >>> solar = create_solar_position(context, utc_offset=-8, latitude=38.5, longitude=-121.7)
478 return SolarPosition(context, utc_offset, latitude, longitude)
Exception raised for SolarPosition-specific errors.
High-level interface for solar position calculations and radiation modeling.
enableCloudCalibration(self, str timeseries_label)
Enable cloud calibration using timeseries data.
Time getSunriseTime(self)
Calculate sunrise time for the current date and location.
float getSunZenith(self)
Get the sun zenith angle in degrees.
float getSunAzimuth(self)
Get the sun azimuth angle in degrees.
float getSunElevation(self)
Get the sun elevation angle in degrees.
bool is_available(self)
Check if SolarPosition is available in current build.
SphericalCoord getSunDirectionSpherical(self)
Get the sun direction as spherical coordinates.
Time getSunsetTime(self)
Calculate sunset time for the current date and location.
__enter__(self)
Context manager entry.
__exit__(self, exc_type, exc_val, exc_tb)
Context manager exit - cleanup resources.
calibrateTurbidityFromTimeseries(self, str timeseries_label)
Calibrate atmospheric turbidity using timeseries data.
float getSolarFlux(self, float pressure_Pa, float temperature_K, float humidity_rel, float turbidity)
Calculate total solar flux with atmospheric parameters.
float getDiffuseFraction(self, float pressure_Pa, float temperature_K, float humidity_rel, float turbidity)
Calculate the diffuse fraction of solar radiation.
float getSolarFluxPAR(self, float pressure_Pa, float temperature_K, float humidity_rel, float turbidity)
Calculate PAR (Photosynthetically Active Radiation) solar flux.
disableCloudCalibration(self)
Disable cloud calibration.
float getSolarFluxNIR(self, float pressure_Pa, float temperature_K, float humidity_rel, float turbidity)
Calculate NIR (Near-Infrared) solar flux.
vec3 getSunDirectionVector(self)
Get the sun direction as a 3D unit vector.
Exception classes for PyHelios library.
Helios Time structure for representing time values.
SolarPosition create_solar_position(Context context, Optional[float] utc_offset=None, Optional[float] latitude=None, Optional[float] longitude=None)
Create SolarPosition instance with context and optional coordinates.