acgc.solar API documentation

# acgc.solar

Module to calculate solar zenith angle, solar declination, and equation of time Results should be accurate to < 0.1 degree, but other modules should be used for high-precision calculations.

C.D. Holmes 9 Nov 2018

View Source

      1#!/usr/local/bin/env python3
      2''' Module to calculate solar zenith angle, solar declination, and equation of time
      3Results should be accurate to < 0.1 degree, but other modules should be used for
      4high-precision calculations.
      5
      6C.D. Holmes 9 Nov 2018
      7'''
      8
      9import warnings
     10import numpy as np
     11import pandas as pd
     12
     13pi180 = np.pi / 180
     14
     15def solar_azimuth_angle( lat, lon, datetimeUTC ):
     16    '''Solar azimuth angle (degrees) for a latitude, longitude, date and time
     17
     18    SAA is degrees clockwise from north.
     19
     20    Parameters
     21    ----------
     22    lat : float or ndarray
     23        latitude in degrees
     24    lon : float or ndarray
     25        longitudes in degrees
     26    datetimeUTC : pandas.Timestamp, datetime, or str
     27        date and time in UTC
     28
     29    Returns
     30    -------
     31    saa : float or ndarray
     32        solar azimuth angle in degrees (clockwise from north)
     33    '''
     34    # Convert to pandas Timestamp, if needed
     35    datetimeUTC = _to_timestamp(datetimeUTC)
     36
     37    # Solar declination, degrees
     38    dec = solar_declination( datetimeUTC )
     39
     40    # Hour angle, degrees
     41    Ha = solar_hour_angle( lon, datetimeUTC )
     42
     43    # Solar zenith angle, degrees
     44    # Use true sza, without refraction
     45    zen = sza( lat, lon, datetimeUTC, refraction=False )
     46
     47    # Solar azimuth angle, degrees
     48    saa = np.arcsin( -np.sin( Ha*pi180 ) * np.cos( dec*pi180 ) /
     49            np.sin( zen*pi180 ) ) / pi180
     50
     51    # Change range [-180,180] to [0,360]
     52    return np.mod( saa+360, 360 )
     53
     54def solar_elevation_angle( lat, lon, alt, datetimeUTC,
     55                       refraction=False, temperature=10., pressure=101325. ):
     56    '''Solar elevation angle (degrees) above the horizon
     57
     58    The altitude parameter should be the vertical distance
     59    above the surrounding terrain that defines the horizon,
     60    not necessarily the altitude above sea level or the altitude above ground level.
     61    For example, on a mountain peak that is 4000 m above sea level and
     62    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
     63    For an observer on the plateau, the relevant altitude is 0 m.
     64
     65    See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.
     66
     67    Parameters
     68    ----------
     69    lat : float or ndarray
     70        latitude in degrees
     71    lon : float or ndarray
     72        longitudes in degrees
     73    alt : float or ndarray
     74        altitude above surrounding terrain that defines the horizon, meters
     75    datetimeUTC : pandas.Timestamp, datetime, or str
     76        date and time in UTC
     77    refraction : bool, optional (default=False)
     78        specifies whether to account for atmospheric refraction
     79    temperature : float or ndarray, optional (default=10)
     80        surface atmospheric temperature (Celsius), only used for refraction calculation
     81    pressure : float or ndarray, optional (default=101325)
     82        surface atmospheric pressure (Pa), only used for refraction calculation
     83
     84    Returns
     85    -------
     86    sea : float or ndarray
     87        solar elevation angle in degrees at the designated locations and times
     88        If refraction=False, this is the true solar elevation angle
     89        If refraction=True, this is the apparent solar elevation angle
     90
     91    '''
     92
     93    if refraction and np.any(alt):
     94        warnings.warn( 'Atmospheric refraction is calculated for surface conditions, '
     95                    + 'but an altitude above the surface was specified',
     96                     category=UserWarning,
     97                     stacklevel=2 )
     98
     99    sea = horizon_zenith_angle( alt ) \
    100         - solar_zenith_angle( lat, lon, datetimeUTC, refraction, temperature, pressure )
    101
    102    return sea
    103
    104def solar_zenith_angle( lat, lon, datetimeUTC,
    105                       refraction=False, temperature=10., pressure=101325. ):
    106    '''Solar zenith angle (degrees) for a given latitude, longitude, date and time.
    107
    108    Accounts for equation of time and (optionally) for atmospheric refraction.
    109    Altitude of the observer is not accounted for, which can be important when the sun
    110    is near the horizon.
    111
    112    Results are accurate to tenths of a degree, except where altitude is important
    113    (< 20 degrees solar elevation)
    114
    115    Parameters
    116    ----------
    117    lat : float or ndarray
    118        latitude in degrees
    119    lon : float or ndarray
    120        longitudes in degrees
    121    datetimeUTC : pandas.Timestamp, datetime, or str
    122        date and time in UTC
    123    refraction : bool, optional (default=False)
    124        specifies whether to account for atmospheric refraction
    125    temperature : float or ndarray, optional (default=10)
    126        surface atmospheric temperature (Celsius), only used for refraction calculation
    127    pressure : float or ndarray, optional (default=101325)
    128        surface atmospheric pressure (Pa), only used for refraction calculation
    129
    130    Returns
    131    -------
    132    sza : float or ndarray
    133        solar zenith angle in degrees at the designated locations and times
    134        If refraction=False, this is the true solar zenith angle
    135        If refraction=True, this is the apparent solar zenith angle
    136    '''
    137    # Convert to pandas Timestamp, if needed
    138    datetimeUTC = _to_timestamp(datetimeUTC)
    139
    140    # Solar declination, degrees
    141    dec = solar_declination( datetimeUTC )
    142
    143    # Hour angle, degrees
    144    Ha = solar_hour_angle( lon, datetimeUTC )
    145
    146    # True solar zenith angle, radians
    147    sza = np.arccos( np.sin(lat*pi180) * np.sin(dec*pi180) + \
    148          np.cos(lat*pi180) * np.cos(dec*pi180) * np.cos(Ha*pi180) )
    149
    150    # Convert radians -> degrees
    151    sza /= pi180
    152
    153    if refraction:
    154        # Subtract refraction angle (degrees) from zenith angle.
    155        # SZA is always smaller due to refraction.
    156        sza -= refraction_angle( 90-sza, pressure, temperature )
    157
    158    return sza
    159
    160def equation_of_time( date ):
    161    '''Equation of time (degrees) for specified date
    162
    163    Implements the "alternative equation" from Wikipedia, derived from
    164    https://web.archive.org/web/20120323231813/http://www.green-life-innovators.org/tiki-index.php?page=The%2BLatitude%2Band%2BLongitude%2Bof%2Bthe%2BSun%2Bby%2BDavid%2BWilliams
    165    Results checked against NOAA solar calculator and agree within 10 seconds.
    166
    167    Argument
    168    --------
    169    date : pandas.Timestamp, date, or datetime
    170        date for calculation
    171
    172    Returns
    173    -------
    174    eot : float
    175        equation of time in degrees on the specified date
    176    '''
    177    # Convert to pandas Timestamp, if needed
    178    date = _to_timestamp(date)
    179
    180    # Equation of time, accounts for the solar day differing slightly from 24 hr
    181    doy = date.dayofyear
    182    W = 360 / 365.24
    183    A = W * (doy+10)
    184    B = A + 1.914 * np.sin( W * (doy-2) * pi180 )
    185    C = ( A - np.arctan2( np.tan(B*pi180), np.cos(23.44*pi180) ) / pi180 ) / 180
    186
    187    # Equation of time in minutes of an hour
    188    eotmin = 720 * ( C - np.round(C) )
    189
    190    # Equation of time, minutes -> degrees
    191    eot = eotmin / 60 * 360 / 24
    192
    193    return eot
    194
    195def horizon_zenith_angle( alt ):
    196    '''Angle from the zenith to the horizon
    197
    198    The horizon is the locii of points where a line from the
    199    observation location to the ellipsoid is tangent to the ellipsoid surface.
    200
    201    The altitude parameter should be the vertical distance
    202    above the surrounding terrain that defines the horizon,
    203    not necessarily the altitude above sea level or the altitude above ground level.
    204    For example, on a mountain peak that is 4000 m above sea level and
    205    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
    206    For an observer on the plateau, the relevant altitude is 0 m.
    207
    208    The implementation below assumes a spherical Earth.
    209    Results using the WGS84 ellipsoid (see commented code below)
    210    differ from the spherical case by << 1°. Terrain,
    211    which is neglected here, has a larger effect on the horizon
    212    location, so the simpler spherical calculation is appropriate.
    213
    214    Parameters
    215    ----------
    216    lat : float or ndarray
    217        latitude in degrees
    218    alt : float or ndarray
    219        altitude above surrounding terrain that defines the horizon, meters
    220
    221    Returns
    222    -------
    223    hza : float or ndarray
    224        horizon zenith angle in degrees
    225    '''
    226
    227    # WGS84 ellipsoid parameters
    228    # semi-major radius, m
    229    r_earth = 6378137.0
    230    # ellipsoidal flattening, unitless
    231    f = 1/298.257223563
    232
    233    # Horizon zenith angle, degrees (spherical earth)
    234    hza = 180 - np.arcsin( r_earth / ( r_earth + alt ) ) / pi180
    235
    236    ## Ellipsoidal Earth
    237    # # Eccentricity of ellipsoid
    238    # ecc = f * (2-f)
    239    # # Local (i.e. prime vertical) radius of curvature at latitude
    240    # N = r_earth / np.sqrt( 1 - ecc**2 * np.sin(lat*pi180)**2 )
    241    # # Horizon zenith angle, degrees
    242    # hza = 180 - np.arcsin( N / (N+alt) ) / pi180
    243
    244    return hza
    245
    246def solar_declination( date ):
    247    '''Calculate solar declination (degrees) for specified date
    248
    249    Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling
    250
    251    Argument
    252    --------
    253    date : pandas.Timestamp, date, datetime, or str
    254        date for calculation
    255
    256    Returns
    257    -------
    258    dec : float
    259        solar declination in degrees at the specified date
    260    '''
    261
    262    # Convert to pandas Timestamp, if needed
    263    date = _to_timestamp(date)
    264
    265     # Number of days since beginning of 2000
    266    NJD = np.floor( date.to_julian_date() - pd.Timestamp(2000,1,1).to_julian_date() )
    267
    268    # Obliquity, degrees
    269    ob = 23.439 - 4e-7 * NJD
    270
    271    # Parameters for ecliptic, degrees
    272    gm = 357.528 + 0.9856003 * NJD
    273    lm = 280.460 + 0.9856474 * NJD
    274
    275    # Ecliptic longitude of sun, degrees
    276    ec = lm + 1.915 * np.sin( gm * pi180 ) + 0.020 * np.sin( 2 * gm * pi180 )
    277
    278    #Solar declination, degrees
    279    dec = np.arcsin( np.sin( ob * pi180 ) * np.sin( ec * pi180 ) ) / pi180
    280
    281    return dec
    282
    283def solar_hour_angle( lon, datetimeUTC ):
    284    '''Solar hour angle (degrees) for specified longitude, date and time
    285
    286    Hour angle is the angular displacement of the sun from the local meridian.
    287    It is zero at local noon, negative in the morning, and positive is afternoon.
    288
    289    Parameters
    290    ----------
    291    lon : float
    292        longitude in degrees east
    293    datetimeUTC : pandas.Timestamp or datetime
    294        date and time for calculation, must be UTC
    295
    296    Returns
    297    -------
    298    ha : float
    299        hour angle in degrees at the specified location and time
    300    '''
    301
    302    # Convert to pandas Timestamp, if needed
    303    datetimeUTC = _to_timestamp(datetimeUTC)
    304
    305    # Hour angle for mean solar time.
    306    # Actual solar position has a small offset given by the equation of time (below)
    307    Ha = ( datetimeUTC.hour + datetimeUTC.minute / 60 - 12 ) * 15 + lon
    308
    309    # Add equation of time to the hour angle, degrees
    310    Ha += equation_of_time( datetimeUTC )
    311
    312    return Ha
    313
    314def refraction_angle( true_elevation_angle, pressure=101325., temperature_celsius=10. ):
    315    '''Atmospheric refraction angle for light passing through Earth's atmosphere
    316
    317    The apparent locations in the sky of objects outsides Earth's atmosphere
    318    differs from their true locations due to atmospheric refraction.
    319    (e.g. sun and moon when they rise and set)
    320    The apparent elevation of an object above the horizon is
    321    apparent elevation angle = (true elevation angle) + (refraction angle)
    322
    323    The equations here are from Saemundsson/Bennett, whose calculations use
    324    a typical vertical profile of atmospheric density (i.e. temperature and pressure).
    325    The profiles can be rescaled to a particular surface temperature and pressure
    326    to approximately account for varying atmospheric conditions.
    327    Accurate refraction calculations should use fully specified vertical profile
    328    of temperature and pressure, which cannot be done here.
    329
    330    Parameters
    331    ----------
    332    true_elevation_angle : float
    333        degrees above horizon of sun or other object
    334    pressure : float (default=101325)
    335        surface atmospheric pressure (Pa)
    336    temperature_celsius : float (default=10)
    337        surface atmospheric temperature (C)
    338
    339    Returns
    340    -------
    341    angle : float
    342        refraction angle in degrees. Value is zero when apparent elevation is below horizon
    343    '''
    344    # Refraction angle, arcminutes
    345    R = 1.02 / np.tan( ( true_elevation_angle + 10.3 / (true_elevation_angle + 5.11) ) * pi180 )
    346    # Account for temperature and pressure, arcminutes
    347    R = R * pressure / 101325 * 283 / ( 273 + temperature_celsius )
    348    # Convert arcminutes -> degrees
    349    R /= 60
    350
    351    # Result must be positive
    352    R = np.maximum(R,0)
    353
    354    # Refraction defined only when the apparent elevation angle is positive
    355    # Set refraction to zero when the apparent elevation is below horizon
    356    refraction_angle = np.where( true_elevation_angle + R <= 0, 0, R)
    357
    358    return refraction_angle
    359
    360def _to_timestamp(time_in):
    361    '''Convert input to Pandas Timestamp or DatetimeIndex
    362
    363    Arguments
    364    ---------
    365    time_in : datetime-like or array
    366        time to be converted
    367
    368    Returns
    369    -------
    370    time_out : pandas.DatetimeIndex or pandas.Timestamp
    371        result will be a DatetimeIndex, if possible, and Timestamp otherwise
    372    '''
    373    if not isinstance(time_in, (pd.Timestamp,pd.DatetimeIndex) ):
    374        try:
    375            time_out = pd.DatetimeIndex(time_in)
    376        except TypeError:
    377            time_in = pd.Timestamp(time_in)
    378    else:
    379        time_out = time_in
    380    return time_out
    381
    382# Aliases for functions
    383sza = solar_zenith_angle
    384saa = solar_azimuth_angle
    385sea = solar_elevation_angle
    386# Additional aliases for backwards compatibility
    387equationOfTime = equation_of_time
    388solarDeclination = solar_declination

pi180 = 0.017453292519943295
def solar_azimuth_angle(lat, lon, datetimeUTC): View Source

    16def solar_azimuth_angle( lat, lon, datetimeUTC ):
    17    '''Solar azimuth angle (degrees) for a latitude, longitude, date and time
    18
    19    SAA is degrees clockwise from north.
    20
    21    Parameters
    22    ----------
    23    lat : float or ndarray
    24        latitude in degrees
    25    lon : float or ndarray
    26        longitudes in degrees
    27    datetimeUTC : pandas.Timestamp, datetime, or str
    28        date and time in UTC
    29
    30    Returns
    31    -------
    32    saa : float or ndarray
    33        solar azimuth angle in degrees (clockwise from north)
    34    '''
    35    # Convert to pandas Timestamp, if needed
    36    datetimeUTC = _to_timestamp(datetimeUTC)
    37
    38    # Solar declination, degrees
    39    dec = solar_declination( datetimeUTC )
    40
    41    # Hour angle, degrees
    42    Ha = solar_hour_angle( lon, datetimeUTC )
    43
    44    # Solar zenith angle, degrees
    45    # Use true sza, without refraction
    46    zen = sza( lat, lon, datetimeUTC, refraction=False )
    47
    48    # Solar azimuth angle, degrees
    49    saa = np.arcsin( -np.sin( Ha*pi180 ) * np.cos( dec*pi180 ) /
    50            np.sin( zen*pi180 ) ) / pi180
    51
    52    # Change range [-180,180] to [0,360]
    53    return np.mod( saa+360, 360 )

Solar azimuth angle (degrees) for a latitude, longitude, date and time

SAA is degrees clockwise from north.

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC

###### Returns

- saa (float or ndarray): solar azimuth angle in degrees (clockwise from north)

def solar_elevation_angle( lat, lon, alt, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

     55def solar_elevation_angle( lat, lon, alt, datetimeUTC,
     56                       refraction=False, temperature=10., pressure=101325. ):
     57    '''Solar elevation angle (degrees) above the horizon
     58
     59    The altitude parameter should be the vertical distance
     60    above the surrounding terrain that defines the horizon,
     61    not necessarily the altitude above sea level or the altitude above ground level.
     62    For example, on a mountain peak that is 4000 m above sea level and
     63    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
     64    For an observer on the plateau, the relevant altitude is 0 m.
     65
     66    See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.
     67
     68    Parameters
     69    ----------
     70    lat : float or ndarray
     71        latitude in degrees
     72    lon : float or ndarray
     73        longitudes in degrees
     74    alt : float or ndarray
     75        altitude above surrounding terrain that defines the horizon, meters
     76    datetimeUTC : pandas.Timestamp, datetime, or str
     77        date and time in UTC
     78    refraction : bool, optional (default=False)
     79        specifies whether to account for atmospheric refraction
     80    temperature : float or ndarray, optional (default=10)
     81        surface atmospheric temperature (Celsius), only used for refraction calculation
     82    pressure : float or ndarray, optional (default=101325)
     83        surface atmospheric pressure (Pa), only used for refraction calculation
     84
     85    Returns
     86    -------
     87    sea : float or ndarray
     88        solar elevation angle in degrees at the designated locations and times
     89        If refraction=False, this is the true solar elevation angle
     90        If refraction=True, this is the apparent solar elevation angle
     91
     92    '''
     93
     94    if refraction and np.any(alt):
     95        warnings.warn( 'Atmospheric refraction is calculated for surface conditions, '
     96                    + 'but an altitude above the surface was specified',
     97                     category=UserWarning,
     98                     stacklevel=2 )
     99
    100    sea = horizon_zenith_angle( alt ) \
    101         - solar_zenith_angle( lat, lon, datetimeUTC, refraction, temperature, pressure )
    102
    103    return sea

Solar elevation angle (degrees) above the horizon

The altitude parameter should be the vertical distance above the surrounding terrain that defines the horizon, not necessarily the altitude above sea level or the altitude above ground level. For example, on a mountain peak that is 4000 m above sea level and 1500 m above the surrounding plateau, the relevant altitude is 1500 m. For an observer on the plateau, the relevant altitude is 0 m.

See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- alt (float or ndarray): altitude above surrounding terrain that defines the horizon, meters
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC
- refraction (bool, optional (default=False)): specifies whether to account for atmospheric refraction
- temperature (float or ndarray, optional (default=10)): surface atmospheric temperature (Celsius), only used for refraction calculation
- pressure (float or ndarray, optional (default=101325)): surface atmospheric pressure (Pa), only used for refraction calculation

###### Returns

- sea (float or ndarray): solar elevation angle in degrees at the designated locations and times If refraction=False, this is the true solar elevation angle If refraction=True, this is the apparent solar elevation angle

def solar_zenith_angle( lat, lon, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

    105def solar_zenith_angle( lat, lon, datetimeUTC,
    106                       refraction=False, temperature=10., pressure=101325. ):
    107    '''Solar zenith angle (degrees) for a given latitude, longitude, date and time.
    108
    109    Accounts for equation of time and (optionally) for atmospheric refraction.
    110    Altitude of the observer is not accounted for, which can be important when the sun
    111    is near the horizon.
    112
    113    Results are accurate to tenths of a degree, except where altitude is important
    114    (< 20 degrees solar elevation)
    115
    116    Parameters
    117    ----------
    118    lat : float or ndarray
    119        latitude in degrees
    120    lon : float or ndarray
    121        longitudes in degrees
    122    datetimeUTC : pandas.Timestamp, datetime, or str
    123        date and time in UTC
    124    refraction : bool, optional (default=False)
    125        specifies whether to account for atmospheric refraction
    126    temperature : float or ndarray, optional (default=10)
    127        surface atmospheric temperature (Celsius), only used for refraction calculation
    128    pressure : float or ndarray, optional (default=101325)
    129        surface atmospheric pressure (Pa), only used for refraction calculation
    130
    131    Returns
    132    -------
    133    sza : float or ndarray
    134        solar zenith angle in degrees at the designated locations and times
    135        If refraction=False, this is the true solar zenith angle
    136        If refraction=True, this is the apparent solar zenith angle
    137    '''
    138    # Convert to pandas Timestamp, if needed
    139    datetimeUTC = _to_timestamp(datetimeUTC)
    140
    141    # Solar declination, degrees
    142    dec = solar_declination( datetimeUTC )
    143
    144    # Hour angle, degrees
    145    Ha = solar_hour_angle( lon, datetimeUTC )
    146
    147    # True solar zenith angle, radians
    148    sza = np.arccos( np.sin(lat*pi180) * np.sin(dec*pi180) + \
    149          np.cos(lat*pi180) * np.cos(dec*pi180) * np.cos(Ha*pi180) )
    150
    151    # Convert radians -> degrees
    152    sza /= pi180
    153
    154    if refraction:
    155        # Subtract refraction angle (degrees) from zenith angle.
    156        # SZA is always smaller due to refraction.
    157        sza -= refraction_angle( 90-sza, pressure, temperature )
    158
    159    return sza

Solar zenith angle (degrees) for a given latitude, longitude, date and time.

Accounts for equation of time and (optionally) for atmospheric refraction. Altitude of the observer is not accounted for, which can be important when the sun is near the horizon.

Results are accurate to tenths of a degree, except where altitude is important (< 20 degrees solar elevation)

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC
- refraction (bool, optional (default=False)): specifies whether to account for atmospheric refraction
- temperature (float or ndarray, optional (default=10)): surface atmospheric temperature (Celsius), only used for refraction calculation
- pressure (float or ndarray, optional (default=101325)): surface atmospheric pressure (Pa), only used for refraction calculation

###### Returns

- sza (float or ndarray): solar zenith angle in degrees at the designated locations and times If refraction=False, this is the true solar zenith angle If refraction=True, this is the apparent solar zenith angle

def equation_of_time(date): View Source

    161def equation_of_time( date ):
    162    '''Equation of time (degrees) for specified date
    163
    164    Implements the "alternative equation" from Wikipedia, derived from
    165    https://web.archive.org/web/20120323231813/http://www.green-life-innovators.org/tiki-index.php?page=The%2BLatitude%2Band%2BLongitude%2Bof%2Bthe%2BSun%2Bby%2BDavid%2BWilliams
    166    Results checked against NOAA solar calculator and agree within 10 seconds.
    167
    168    Argument
    169    --------
    170    date : pandas.Timestamp, date, or datetime
    171        date for calculation
    172
    173    Returns
    174    -------
    175    eot : float
    176        equation of time in degrees on the specified date
    177    '''
    178    # Convert to pandas Timestamp, if needed
    179    date = _to_timestamp(date)
    180
    181    # Equation of time, accounts for the solar day differing slightly from 24 hr
    182    doy = date.dayofyear
    183    W = 360 / 365.24
    184    A = W * (doy+10)
    185    B = A + 1.914 * np.sin( W * (doy-2) * pi180 )
    186    C = ( A - np.arctan2( np.tan(B*pi180), np.cos(23.44*pi180) ) / pi180 ) / 180
    187
    188    # Equation of time in minutes of an hour
    189    eotmin = 720 * ( C - np.round(C) )
    190
    191    # Equation of time, minutes -> degrees
    192    eot = eotmin / 60 * 360 / 24
    193
    194    return eot

Equation of time (degrees) for specified date

Implements the "alternative equation" from Wikipedia, derived from https://web.archive.org/web/20120323231813/http://www.green-life-innovators.org/tiki-index.php?page=The%2BLatitude%2Band%2BLongitude%2Bof%2Bthe%2BSun%2Bby%2BDavid%2BWilliams Results checked against NOAA solar calculator and agree within 10 seconds.

###### Argument

date : pandas.Timestamp, date, or datetime date for calculation

###### Returns

- eot (float): equation of time in degrees on the specified date

def horizon_zenith_angle(alt): View Source

    196def horizon_zenith_angle( alt ):
    197    '''Angle from the zenith to the horizon
    198
    199    The horizon is the locii of points where a line from the
    200    observation location to the ellipsoid is tangent to the ellipsoid surface.
    201
    202    The altitude parameter should be the vertical distance
    203    above the surrounding terrain that defines the horizon,
    204    not necessarily the altitude above sea level or the altitude above ground level.
    205    For example, on a mountain peak that is 4000 m above sea level and
    206    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
    207    For an observer on the plateau, the relevant altitude is 0 m.
    208
    209    The implementation below assumes a spherical Earth.
    210    Results using the WGS84 ellipsoid (see commented code below)
    211    differ from the spherical case by << 1°. Terrain,
    212    which is neglected here, has a larger effect on the horizon
    213    location, so the simpler spherical calculation is appropriate.
    214
    215    Parameters
    216    ----------
    217    lat : float or ndarray
    218        latitude in degrees
    219    alt : float or ndarray
    220        altitude above surrounding terrain that defines the horizon, meters
    221
    222    Returns
    223    -------
    224    hza : float or ndarray
    225        horizon zenith angle in degrees
    226    '''
    227
    228    # WGS84 ellipsoid parameters
    229    # semi-major radius, m
    230    r_earth = 6378137.0
    231    # ellipsoidal flattening, unitless
    232    f = 1/298.257223563
    233
    234    # Horizon zenith angle, degrees (spherical earth)
    235    hza = 180 - np.arcsin( r_earth / ( r_earth + alt ) ) / pi180
    236
    237    ## Ellipsoidal Earth
    238    # # Eccentricity of ellipsoid
    239    # ecc = f * (2-f)
    240    # # Local (i.e. prime vertical) radius of curvature at latitude
    241    # N = r_earth / np.sqrt( 1 - ecc**2 * np.sin(lat*pi180)**2 )
    242    # # Horizon zenith angle, degrees
    243    # hza = 180 - np.arcsin( N / (N+alt) ) / pi180
    244
    245    return hza

Angle from the zenith to the horizon

The horizon is the locii of points where a line from the observation location to the ellipsoid is tangent to the ellipsoid surface.

The altitude parameter should be the vertical distance above the surrounding terrain that defines the horizon, not necessarily the altitude above sea level or the altitude above ground level. For example, on a mountain peak that is 4000 m above sea level and 1500 m above the surrounding plateau, the relevant altitude is 1500 m. For an observer on the plateau, the relevant altitude is 0 m.

The implementation below assumes a spherical Earth. Results using the WGS84 ellipsoid (see commented code below) differ from the spherical case by << 1°. Terrain, which is neglected here, has a larger effect on the horizon location, so the simpler spherical calculation is appropriate.

###### Parameters

- lat (float or ndarray): latitude in degrees
- alt (float or ndarray): altitude above surrounding terrain that defines the horizon, meters

###### Returns

- hza (float or ndarray): horizon zenith angle in degrees

def solar_declination(date): View Source

    247def solar_declination( date ):
    248    '''Calculate solar declination (degrees) for specified date
    249
    250    Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling
    251
    252    Argument
    253    --------
    254    date : pandas.Timestamp, date, datetime, or str
    255        date for calculation
    256
    257    Returns
    258    -------
    259    dec : float
    260        solar declination in degrees at the specified date
    261    '''
    262
    263    # Convert to pandas Timestamp, if needed
    264    date = _to_timestamp(date)
    265
    266     # Number of days since beginning of 2000
    267    NJD = np.floor( date.to_julian_date() - pd.Timestamp(2000,1,1).to_julian_date() )
    268
    269    # Obliquity, degrees
    270    ob = 23.439 - 4e-7 * NJD
    271
    272    # Parameters for ecliptic, degrees
    273    gm = 357.528 + 0.9856003 * NJD
    274    lm = 280.460 + 0.9856474 * NJD
    275
    276    # Ecliptic longitude of sun, degrees
    277    ec = lm + 1.915 * np.sin( gm * pi180 ) + 0.020 * np.sin( 2 * gm * pi180 )
    278
    279    #Solar declination, degrees
    280    dec = np.arcsin( np.sin( ob * pi180 ) * np.sin( ec * pi180 ) ) / pi180
    281
    282    return dec

Calculate solar declination (degrees) for specified date

Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling

###### Argument

date : pandas.Timestamp, date, datetime, or str date for calculation

###### Returns

- dec (float): solar declination in degrees at the specified date

def solar_hour_angle(lon, datetimeUTC): View Source

    284def solar_hour_angle( lon, datetimeUTC ):
    285    '''Solar hour angle (degrees) for specified longitude, date and time
    286
    287    Hour angle is the angular displacement of the sun from the local meridian.
    288    It is zero at local noon, negative in the morning, and positive is afternoon.
    289
    290    Parameters
    291    ----------
    292    lon : float
    293        longitude in degrees east
    294    datetimeUTC : pandas.Timestamp or datetime
    295        date and time for calculation, must be UTC
    296
    297    Returns
    298    -------
    299    ha : float
    300        hour angle in degrees at the specified location and time
    301    '''
    302
    303    # Convert to pandas Timestamp, if needed
    304    datetimeUTC = _to_timestamp(datetimeUTC)
    305
    306    # Hour angle for mean solar time.
    307    # Actual solar position has a small offset given by the equation of time (below)
    308    Ha = ( datetimeUTC.hour + datetimeUTC.minute / 60 - 12 ) * 15 + lon
    309
    310    # Add equation of time to the hour angle, degrees
    311    Ha += equation_of_time( datetimeUTC )
    312
    313    return Ha

Solar hour angle (degrees) for specified longitude, date and time

Hour angle is the angular displacement of the sun from the local meridian. It is zero at local noon, negative in the morning, and positive is afternoon.

###### Parameters

- lon (float): longitude in degrees east
- datetimeUTC (pandas.Timestamp or datetime): date and time for calculation, must be UTC

###### Returns

- ha (float): hour angle in degrees at the specified location and time

def refraction_angle(true_elevation_angle, pressure=101325.0, temperature_celsius=10.0): View Source

    315def refraction_angle( true_elevation_angle, pressure=101325., temperature_celsius=10. ):
    316    '''Atmospheric refraction angle for light passing through Earth's atmosphere
    317
    318    The apparent locations in the sky of objects outsides Earth's atmosphere
    319    differs from their true locations due to atmospheric refraction.
    320    (e.g. sun and moon when they rise and set)
    321    The apparent elevation of an object above the horizon is
    322    apparent elevation angle = (true elevation angle) + (refraction angle)
    323
    324    The equations here are from Saemundsson/Bennett, whose calculations use
    325    a typical vertical profile of atmospheric density (i.e. temperature and pressure).
    326    The profiles can be rescaled to a particular surface temperature and pressure
    327    to approximately account for varying atmospheric conditions.
    328    Accurate refraction calculations should use fully specified vertical profile
    329    of temperature and pressure, which cannot be done here.
    330
    331    Parameters
    332    ----------
    333    true_elevation_angle : float
    334        degrees above horizon of sun or other object
    335    pressure : float (default=101325)
    336        surface atmospheric pressure (Pa)
    337    temperature_celsius : float (default=10)
    338        surface atmospheric temperature (C)
    339
    340    Returns
    341    -------
    342    angle : float
    343        refraction angle in degrees. Value is zero when apparent elevation is below horizon
    344    '''
    345    # Refraction angle, arcminutes
    346    R = 1.02 / np.tan( ( true_elevation_angle + 10.3 / (true_elevation_angle + 5.11) ) * pi180 )
    347    # Account for temperature and pressure, arcminutes
    348    R = R * pressure / 101325 * 283 / ( 273 + temperature_celsius )
    349    # Convert arcminutes -> degrees
    350    R /= 60
    351
    352    # Result must be positive
    353    R = np.maximum(R,0)
    354
    355    # Refraction defined only when the apparent elevation angle is positive
    356    # Set refraction to zero when the apparent elevation is below horizon
    357    refraction_angle = np.where( true_elevation_angle + R <= 0, 0, R)
    358
    359    return refraction_angle

Atmospheric refraction angle for light passing through Earth's atmosphere

The apparent locations in the sky of objects outsides Earth's atmosphere differs from their true locations due to atmospheric refraction. (e.g. sun and moon when they rise and set) The apparent elevation of an object above the horizon is apparent elevation angle = (true elevation angle) + (refraction angle)

The equations here are from Saemundsson/Bennett, whose calculations use a typical vertical profile of atmospheric density (i.e. temperature and pressure). The profiles can be rescaled to a particular surface temperature and pressure to approximately account for varying atmospheric conditions. Accurate refraction calculations should use fully specified vertical profile of temperature and pressure, which cannot be done here.

###### Parameters

- true_elevation_angle (float): degrees above horizon of sun or other object
- pressure (float (default=101325)): surface atmospheric pressure (Pa)
- temperature_celsius (float (default=10)): surface atmospheric temperature (C)

###### Returns

- angle (float): refraction angle in degrees. Value is zero when apparent elevation is below horizon

def sza( lat, lon, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

    105def solar_zenith_angle( lat, lon, datetimeUTC,
    106                       refraction=False, temperature=10., pressure=101325. ):
    107    '''Solar zenith angle (degrees) for a given latitude, longitude, date and time.
    108
    109    Accounts for equation of time and (optionally) for atmospheric refraction.
    110    Altitude of the observer is not accounted for, which can be important when the sun
    111    is near the horizon.
    112
    113    Results are accurate to tenths of a degree, except where altitude is important
    114    (< 20 degrees solar elevation)
    115
    116    Parameters
    117    ----------
    118    lat : float or ndarray
    119        latitude in degrees
    120    lon : float or ndarray
    121        longitudes in degrees
    122    datetimeUTC : pandas.Timestamp, datetime, or str
    123        date and time in UTC
    124    refraction : bool, optional (default=False)
    125        specifies whether to account for atmospheric refraction
    126    temperature : float or ndarray, optional (default=10)
    127        surface atmospheric temperature (Celsius), only used for refraction calculation
    128    pressure : float or ndarray, optional (default=101325)
    129        surface atmospheric pressure (Pa), only used for refraction calculation
    130
    131    Returns
    132    -------
    133    sza : float or ndarray
    134        solar zenith angle in degrees at the designated locations and times
    135        If refraction=False, this is the true solar zenith angle
    136        If refraction=True, this is the apparent solar zenith angle
    137    '''
    138    # Convert to pandas Timestamp, if needed
    139    datetimeUTC = _to_timestamp(datetimeUTC)
    140
    141    # Solar declination, degrees
    142    dec = solar_declination( datetimeUTC )
    143
    144    # Hour angle, degrees
    145    Ha = solar_hour_angle( lon, datetimeUTC )
    146
    147    # True solar zenith angle, radians
    148    sza = np.arccos( np.sin(lat*pi180) * np.sin(dec*pi180) + \
    149          np.cos(lat*pi180) * np.cos(dec*pi180) * np.cos(Ha*pi180) )
    150
    151    # Convert radians -> degrees
    152    sza /= pi180
    153
    154    if refraction:
    155        # Subtract refraction angle (degrees) from zenith angle.
    156        # SZA is always smaller due to refraction.
    157        sza -= refraction_angle( 90-sza, pressure, temperature )
    158
    159    return sza

Solar zenith angle (degrees) for a given latitude, longitude, date and time.

Accounts for equation of time and (optionally) for atmospheric refraction. Altitude of the observer is not accounted for, which can be important when the sun is near the horizon.

Results are accurate to tenths of a degree, except where altitude is important (< 20 degrees solar elevation)

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC
- refraction (bool, optional (default=False)): specifies whether to account for atmospheric refraction
- temperature (float or ndarray, optional (default=10)): surface atmospheric temperature (Celsius), only used for refraction calculation
- pressure (float or ndarray, optional (default=101325)): surface atmospheric pressure (Pa), only used for refraction calculation

###### Returns

- sza (float or ndarray): solar zenith angle in degrees at the designated locations and times If refraction=False, this is the true solar zenith angle If refraction=True, this is the apparent solar zenith angle

def saa(lat, lon, datetimeUTC): View Source

    16def solar_azimuth_angle( lat, lon, datetimeUTC ):
    17    '''Solar azimuth angle (degrees) for a latitude, longitude, date and time
    18
    19    SAA is degrees clockwise from north.
    20
    21    Parameters
    22    ----------
    23    lat : float or ndarray
    24        latitude in degrees
    25    lon : float or ndarray
    26        longitudes in degrees
    27    datetimeUTC : pandas.Timestamp, datetime, or str
    28        date and time in UTC
    29
    30    Returns
    31    -------
    32    saa : float or ndarray
    33        solar azimuth angle in degrees (clockwise from north)
    34    '''
    35    # Convert to pandas Timestamp, if needed
    36    datetimeUTC = _to_timestamp(datetimeUTC)
    37
    38    # Solar declination, degrees
    39    dec = solar_declination( datetimeUTC )
    40
    41    # Hour angle, degrees
    42    Ha = solar_hour_angle( lon, datetimeUTC )
    43
    44    # Solar zenith angle, degrees
    45    # Use true sza, without refraction
    46    zen = sza( lat, lon, datetimeUTC, refraction=False )
    47
    48    # Solar azimuth angle, degrees
    49    saa = np.arcsin( -np.sin( Ha*pi180 ) * np.cos( dec*pi180 ) /
    50            np.sin( zen*pi180 ) ) / pi180
    51
    52    # Change range [-180,180] to [0,360]
    53    return np.mod( saa+360, 360 )

Solar azimuth angle (degrees) for a latitude, longitude, date and time

SAA is degrees clockwise from north.

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC

###### Returns

- saa (float or ndarray): solar azimuth angle in degrees (clockwise from north)

def sea( lat, lon, alt, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

     55def solar_elevation_angle( lat, lon, alt, datetimeUTC,
     56                       refraction=False, temperature=10., pressure=101325. ):
     57    '''Solar elevation angle (degrees) above the horizon
     58
     59    The altitude parameter should be the vertical distance
     60    above the surrounding terrain that defines the horizon,
     61    not necessarily the altitude above sea level or the altitude above ground level.
     62    For example, on a mountain peak that is 4000 m above sea level and
     63    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
     64    For an observer on the plateau, the relevant altitude is 0 m.
     65
     66    See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.
     67
     68    Parameters
     69    ----------
     70    lat : float or ndarray
     71        latitude in degrees
     72    lon : float or ndarray
     73        longitudes in degrees
     74    alt : float or ndarray
     75        altitude above surrounding terrain that defines the horizon, meters
     76    datetimeUTC : pandas.Timestamp, datetime, or str
     77        date and time in UTC
     78    refraction : bool, optional (default=False)
     79        specifies whether to account for atmospheric refraction
     80    temperature : float or ndarray, optional (default=10)
     81        surface atmospheric temperature (Celsius), only used for refraction calculation
     82    pressure : float or ndarray, optional (default=101325)
     83        surface atmospheric pressure (Pa), only used for refraction calculation
     84
     85    Returns
     86    -------
     87    sea : float or ndarray
     88        solar elevation angle in degrees at the designated locations and times
     89        If refraction=False, this is the true solar elevation angle
     90        If refraction=True, this is the apparent solar elevation angle
     91
     92    '''
     93
     94    if refraction and np.any(alt):
     95        warnings.warn( 'Atmospheric refraction is calculated for surface conditions, '
     96                    + 'but an altitude above the surface was specified',
     97                     category=UserWarning,
     98                     stacklevel=2 )
     99
    100    sea = horizon_zenith_angle( alt ) \
    101         - solar_zenith_angle( lat, lon, datetimeUTC, refraction, temperature, pressure )
    102
    103    return sea

Solar elevation angle (degrees) above the horizon

The altitude parameter should be the vertical distance above the surrounding terrain that defines the horizon, not necessarily the altitude above sea level or the altitude above ground level. For example, on a mountain peak that is 4000 m above sea level and 1500 m above the surrounding plateau, the relevant altitude is 1500 m. For an observer on the plateau, the relevant altitude is 0 m.

See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.

###### Parameters

- lat (float or ndarray): latitude in degrees
- lon (float or ndarray): longitudes in degrees
- alt (float or ndarray): altitude above surrounding terrain that defines the horizon, meters
- datetimeUTC (pandas.Timestamp, datetime, or str): date and time in UTC
- refraction (bool, optional (default=False)): specifies whether to account for atmospheric refraction
- temperature (float or ndarray, optional (default=10)): surface atmospheric temperature (Celsius), only used for refraction calculation
- pressure (float or ndarray, optional (default=101325)): surface atmospheric pressure (Pa), only used for refraction calculation

###### Returns

- sea (float or ndarray): solar elevation angle in degrees at the designated locations and times If refraction=False, this is the true solar elevation angle If refraction=True, this is the apparent solar elevation angle

def equationOfTime(date): View Source

    161def equation_of_time( date ):
    162    '''Equation of time (degrees) for specified date
    163
    164    Implements the "alternative equation" from Wikipedia, derived from
    165    https://web.archive.org/web/20120323231813/http://www.green-life-innovators.org/tiki-index.php?page=The%2BLatitude%2Band%2BLongitude%2Bof%2Bthe%2BSun%2Bby%2BDavid%2BWilliams
    166    Results checked against NOAA solar calculator and agree within 10 seconds.
    167
    168    Argument
    169    --------
    170    date : pandas.Timestamp, date, or datetime
    171        date for calculation
    172
    173    Returns
    174    -------
    175    eot : float
    176        equation of time in degrees on the specified date
    177    '''
    178    # Convert to pandas Timestamp, if needed
    179    date = _to_timestamp(date)
    180
    181    # Equation of time, accounts for the solar day differing slightly from 24 hr
    182    doy = date.dayofyear
    183    W = 360 / 365.24
    184    A = W * (doy+10)
    185    B = A + 1.914 * np.sin( W * (doy-2) * pi180 )
    186    C = ( A - np.arctan2( np.tan(B*pi180), np.cos(23.44*pi180) ) / pi180 ) / 180
    187
    188    # Equation of time in minutes of an hour
    189    eotmin = 720 * ( C - np.round(C) )
    190
    191    # Equation of time, minutes -> degrees
    192    eot = eotmin / 60 * 360 / 24
    193
    194    return eot

Equation of time (degrees) for specified date

Implements the "alternative equation" from Wikipedia, derived from https://web.archive.org/web/20120323231813/http://www.green-life-innovators.org/tiki-index.php?page=The%2BLatitude%2Band%2BLongitude%2Bof%2Bthe%2BSun%2Bby%2BDavid%2BWilliams Results checked against NOAA solar calculator and agree within 10 seconds.

###### Argument

date : pandas.Timestamp, date, or datetime date for calculation

###### Returns

- eot (float): equation of time in degrees on the specified date

def solarDeclination(date): View Source

    247def solar_declination( date ):
    248    '''Calculate solar declination (degrees) for specified date
    249
    250    Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling
    251
    252    Argument
    253    --------
    254    date : pandas.Timestamp, date, datetime, or str
    255        date for calculation
    256
    257    Returns
    258    -------
    259    dec : float
    260        solar declination in degrees at the specified date
    261    '''
    262
    263    # Convert to pandas Timestamp, if needed
    264    date = _to_timestamp(date)
    265
    266     # Number of days since beginning of 2000
    267    NJD = np.floor( date.to_julian_date() - pd.Timestamp(2000,1,1).to_julian_date() )
    268
    269    # Obliquity, degrees
    270    ob = 23.439 - 4e-7 * NJD
    271
    272    # Parameters for ecliptic, degrees
    273    gm = 357.528 + 0.9856003 * NJD
    274    lm = 280.460 + 0.9856474 * NJD
    275
    276    # Ecliptic longitude of sun, degrees
    277    ec = lm + 1.915 * np.sin( gm * pi180 ) + 0.020 * np.sin( 2 * gm * pi180 )
    278
    279    #Solar declination, degrees
    280    dec = np.arcsin( np.sin( ob * pi180 ) * np.sin( ec * pi180 ) ) / pi180
    281
    282    return dec

Calculate solar declination (degrees) for specified date

Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling

###### Argument

date : pandas.Timestamp, date, datetime, or str date for calculation

###### Returns

- dec (float): solar declination in degrees at the specified date