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

pi180 = 0.017453292519943295
def solar_declination(date): View Source

    16def solar_declination( date ):
    17    '''Calculate solar declination (degrees) for specified date
    18
    19    Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling
    20
    21    Argument
    22    --------
    23    date : pandas.Timestamp, date, datetime, or str
    24        date for calculation
    25
    26    Returns
    27    -------
    28    dec : float
    29        solar declination in degrees at the specified date
    30    '''
    31
    32    # Convert to pandas Timestamp, if needed
    33    if not isinstance(date, pd.Timestamp):
    34        date = pd.Timestamp(date)
    35
    36     # Number of days since beginning of 2000
    37    NJD = np.floor( date.to_julian_date() - pd.Timestamp(2000,1,1).to_julian_date() )
    38
    39    # Obliquity, degrees
    40    ob = 23.439 - 4e-7 * NJD
    41
    42    # Parameters for ecliptic, degrees
    43    gm = 357.528 + 0.9856003 * NJD
    44    lm = 280.460 + 0.9856474 * NJD
    45
    46    # Ecliptic longitude of sun, degrees
    47    ec = lm + 1.915 * np.sin( gm * pi180 ) + 0.020 * np.sin( 2 * gm * pi180 )
    48
    49    #Solar declination, degrees
    50    dec = np.arcsin( np.sin( ob * pi180 ) * np.sin( ec * pi180 ) ) / pi180
    51
    52    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 equation_of_time(date): View Source

    54def equation_of_time( date ):
    55    '''Calculate equation of time (degrees) for specified date
    56
    57    Implements the "alternative equation" from Wikipedia, derived from
    58    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
    59    Results checked against NOAA solar calculator and agree within 10 seconds.
    60
    61    Argument
    62    --------
    63    date : pandas.Timestamp, date, or datetime
    64        date for calculation
    65
    66    Returns
    67    -------
    68    eot : float
    69        equation of time in degrees on the specified date
    70    '''
    71    # Convert to pandas Timestamp, if needed
    72    if not isinstance(date, pd.Timestamp):
    73        date = pd.Timestamp(date)
    74
    75    # Equation of time, accounts for the solar day differing slightly from 24 hr
    76    doy = date.dayofyear
    77    W = 360 / 365.24
    78    A = W * (doy+10)
    79    B = A + 1.914 * np.sin( W * (doy-2) * pi180 )
    80    C = ( A - np.arctan2( np.tan(B*pi180), np.cos(23.44*pi180) ) / pi180 ) / 180
    81
    82    # Equation of time in minutes of an hour
    83    eotmin = 720 * ( C - np.round(C) )
    84
    85    # Equation of time, minutes -> degrees
    86    eot = eotmin / 60 * 360 / 24
    87
    88    return eot

Calculate 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 hour_angle(lon, datetimeUTC): View Source

     90def hour_angle( lon, datetimeUTC ):
     91    '''Compute hour angle (degrees) for specified longitude, date and time
     92
     93    Hour angle is the angular displacement of the sun from the local meridian.
     94    It is zero at local noon, negative in the morning, and positive is afternoon.
     95
     96    Parameters
     97    ----------
     98    lon : float
     99        longitude in degrees east
    100    datetimeUTC : pandas.Timestamp or datetime
    101        date and time for calculation, must be UTC
    102
    103    Returns
    104    -------
    105    ha : float
    106        hour angle in degrees at the specified location and time
    107    '''
    108
    109    # Convert to pandas Timestamp, if needed
    110    if not isinstance(datetimeUTC, pd.Timestamp):
    111        datetimeUTC = pd.Timestamp(datetimeUTC)
    112
    113    # Hour angle for mean solar time.
    114    # Actual solar position has a small offset given by the equation of time (below)
    115    Ha = ( datetimeUTC.hour + datetimeUTC.minute / 60 - 12 ) * 15 + lon
    116
    117    # Add equation of time to the hour angle, degrees
    118    Ha += equation_of_time( datetimeUTC )
    119
    120    return Ha

Compute 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

    122def refraction_angle( true_elevation_angle, pressure=101325., temperature_celsius=10. ):
    123    '''Atmospheric refraction angle
    124
    125    apparent elevation angle = (true elevation angle) + (refraction angle)
    126    Equation from Saemundsson/Bennett
    127
    128    Parameters
    129    ----------
    130    true_elevation_angle : float
    131        degrees above horizon of sun or other object
    132    pressure : float
    133        surface atmospheric pressure (Pa)
    134    temperature_celsius : float
    135        surface atmospheric temperature (C)
    136
    137    Returns
    138    -------
    139    angle : float
    140        refraction angle in degrees. Value is zero when apparent elevation is below horizon
    141    '''
    142    # Refraction angle, arcminutes
    143    R = 1.02 / np.tan( ( true_elevation_angle + 10.3 / (true_elevation_angle + 5.11) ) * pi180 )
    144    # Account for temperature and pressure, arcminutes
    145    R = R * pressure / 101325 * 283 / ( 273 + temperature_celsius )
    146    # Convert arcminutes -> degrees
    147    R /= 60
    148
    149    # Result must be positive
    150    R = np.maximum(R,0)
    151
    152    # Refraction defined only when the apparent elevation angle is positive
    153    # Set refraction to zero when the apparent elevation is below horizon
    154    refraction_angle = np.where( true_elevation_angle + R <= 0, 0, R)
    155
    156    return refraction_angle

Atmospheric refraction angle

apparent elevation angle = (true elevation angle) + (refraction angle) Equation from Saemundsson/Bennett

###### Parameters

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

###### Returns

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

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

    158def solar_zenith_angle( lat, lon, datetimeUTC,
    159                       refraction=False, temperature=10., pressure=101325. ):
    160    '''Calculate solar zenith angle for a given latitude, longitude, date and time.
    161
    162    Accounts for equation of time and (optionally) for atmospheric refraction.
    163    Altitude of the observer is not accounted for, which can be important when the sun
    164    is near the horizon.
    165
    166    Results are accurate to tenths of a degree, except where altitude is important
    167    (< 20 degrees solar elevation)
    168
    169    Parameters
    170    ----------
    171    lat : float or ndarray
    172        latitude in degrees
    173    lon : float or ndarray
    174        longitudes in degrees
    175    datetimeUTC : pandas.Timestamp, datetime, or str
    176        date and time in UTC
    177    refraction : bool, optional (default=False)
    178        specifies whether to account for atmospheric refraction
    179    temperature : float or ndarray, optional (default=10)
    180        surface atmospheric temperature (Celsius), only used for refraction calculation
    181    pressure : float or ndarray, optional (default=101325)
    182        surface atmospheric pressure (Pa), only used for refraction calculation
    183
    184    Returns
    185    -------
    186    sza : float or ndarray
    187        solar zenith angle in degrees at the designated locations and times
    188        If refraction=False, this is the true solar zenith angle
    189        If refraction=True, this is the apparent solar zenith angle
    190    '''
    191    # Convert to pandas Timestamp, if needed
    192    if not isinstance(datetimeUTC, pd.Timestamp):
    193        datetimeUTC = pd.Timestamp(datetimeUTC)
    194
    195    # Solar declination, degrees
    196    dec = solar_declination( datetimeUTC )
    197
    198    # Hour angle, degrees
    199    Ha = hour_angle( lon, datetimeUTC )
    200
    201    # True solar zenith angle, radians
    202    sza = np.arccos( np.sin(lat*pi180) * np.sin(dec*pi180) + \
    203          np.cos(lat*pi180) * np.cos(dec*pi180) * np.cos(Ha*pi180) )
    204
    205    # Convert radians -> degrees
    206    sza /= pi180
    207
    208    if refraction:
    209        # Subtract refraction angle (degrees) from zenith angle.
    210        # SZA is always smaller due to refraction.
    211        sza -= refraction_angle( 90-sza, pressure, temperature )
    212
    213    return sza

Calculate solar zenith angle 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 solar_azimuth_angle(lat, lon, datetimeUTC): View Source

    215def solar_azimuth_angle( lat, lon, datetimeUTC ):
    216    '''Solar azimuth angle (degrees) for a latitude, longitude, date and time
    217
    218    SAA is degrees clockwise from north.
    219
    220    Parameters
    221    ----------
    222    lat : float or ndarray
    223        latitude in degrees
    224    lon : float or ndarray
    225        longitudes in degrees
    226    datetimeUTC : pandas.Timestamp, datetime, or str
    227        date and time in UTC
    228
    229    Returns
    230    -------
    231    saa : float or ndarray
    232        solar azimuth angle in degrees (clockwise from north)
    233    '''
    234    # Convert to pandas Timestamp, if needed
    235    if not isinstance(datetimeUTC, pd.Timestamp):
    236        datetimeUTC = pd.Timestamp(datetimeUTC)
    237
    238    # Solar declination, degrees
    239    dec = solar_declination( datetimeUTC )
    240
    241    # Hour angle, degrees
    242    Ha = hour_angle( lon, datetimeUTC )
    243
    244    # Solar zenith angle, degrees
    245    # Use true sza, without refraction
    246    zen = sza( lat, lon, datetimeUTC, refraction=False )
    247
    248    # Solar azimuth angle, degrees
    249    saa = np.arcsin( -np.sin( Ha*pi180 ) * np.cos( dec*pi180 ) /
    250            np.sin( zen*pi180 ) ) / pi180
    251
    252    # Change range [-180,180] to [0,360]
    253    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 horizon_zenith_angle(lat, alt): View Source

    255def horizon_zenith_angle( lat, alt ):
    256    '''Angle from the zenith to the horizon
    257
    258    The horizon is the locii of points where a line from the
    259    observation location to the ellipsoid is tangent to the ellipsoid surface.
    260
    261    The altitude parameter should be the vertical distance
    262    above the surrounding terrain that defines the horizon,
    263    not necessarily the altitude above sea level or the altitude above ground level.
    264    For example, on a mountain peak that is 4000 m above sea level and
    265    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
    266    For an observer on the plateau, the relevant altitude is 0 m.
    267
    268    The implementation below assumes a spherical Earth.
    269    Results using the WGS84 ellipsoid (see commented code below)
    270    differ from the spherical case by << 1°. Terrain,
    271    which is neglected here, has a larger effect on the horizon
    272    location, so the simpler spherical calculation is appropriate.
    273
    274    Parameters
    275    ----------
    276    lat : float or ndarray
    277        latitude in degrees
    278    alt : float or ndarray
    279        altitude above surrounding terrain that defines the horizon, meters
    280
    281    Returns
    282    -------
    283    hza : float or ndarray
    284        horizon zenith angle in degrees
    285    '''
    286
    287    # WGS84 ellipsoid parameters
    288    # semi-major radius, m
    289    r_earth = 6378137.0
    290    # ellipsoidal flattening, unitless
    291    f = 1/298.257223563
    292
    293    # Horizon zenith angle, degrees (spherical earth)
    294    hza = 180 - np.arcsin( r_earth / ( r_earth + alt ) ) / pi180
    295
    296    ## Ellipsoidal Earth
    297    # # Eccentricity of ellipsoid
    298    # ecc = f * (2-f)
    299    # # Local (i.e. prime vertical) radius of curvature at latitude
    300    # N = r_earth / np.sqrt( 1 - ecc**2 * np.sin(lat*pi180)**2 )
    301    # # Horizon zenith angle, degrees
    302    # hza = 180 - np.arcsin( N / (N+alt) ) / pi180
    303
    304    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_elevation_angle( lat, lon, alt, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

    306def solar_elevation_angle( lat, lon, alt, datetimeUTC,
    307                       refraction=False, temperature=10., pressure=101325. ):
    308    '''Solar elevation angle above the horizon
    309
    310    The altitude parameter should be the vertical distance
    311    above the surrounding terrain that defines the horizon,
    312    not necessarily the altitude above sea level or the altitude above ground level.
    313    For example, on a mountain peak that is 4000 m above sea level and
    314    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
    315    For an observer on the plateau, the relevant altitude is 0 m.
    316
    317    See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.
    318
    319    Parameters
    320    ----------
    321    lat : float or ndarray
    322        latitude in degrees
    323    lon : float or ndarray
    324        longitudes in degrees
    325    alt : float or ndarray
    326        altitude above surrounding terrain that defines the horizon, meters
    327    datetimeUTC : pandas.Timestamp, datetime, or str
    328        date and time in UTC
    329    refraction : bool, optional (default=False)
    330        specifies whether to account for atmospheric refraction
    331    temperature : float or ndarray, optional (default=10)
    332        surface atmospheric temperature (Celsius), only used for refraction calculation
    333    pressure : float or ndarray, optional (default=101325)
    334        surface atmospheric pressure (Pa), only used for refraction calculation
    335
    336    Returns
    337    -------
    338    sea : float or ndarray
    339        solar elevation angle in degrees at the designated locations and times
    340        If refraction=False, this is the true solar elevation angle
    341        If refraction=True, this is the apparent solar elevation angle
    342
    343    '''
    344
    345    if refraction and np.any(alt):
    346        warnings.warn( 'Atmospheric refraction is calculated for surface conditions, '
    347                    + 'but an altitude above the surface was specified',
    348                     category=UserWarning,
    349                     stacklevel=2 )
    350
    351    sea = horizon_zenith_angle( lat, alt ) \
    352         - solar_zenith_angle( lat, lon, datetimeUTC, refraction, temperature, pressure )
    353
    354    return sea

Solar elevation angle 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 sza( lat, lon, datetimeUTC, refraction=False, temperature=10.0, pressure=101325.0): View Source

    158def solar_zenith_angle( lat, lon, datetimeUTC,
    159                       refraction=False, temperature=10., pressure=101325. ):
    160    '''Calculate solar zenith angle for a given latitude, longitude, date and time.
    161
    162    Accounts for equation of time and (optionally) for atmospheric refraction.
    163    Altitude of the observer is not accounted for, which can be important when the sun
    164    is near the horizon.
    165
    166    Results are accurate to tenths of a degree, except where altitude is important
    167    (< 20 degrees solar elevation)
    168
    169    Parameters
    170    ----------
    171    lat : float or ndarray
    172        latitude in degrees
    173    lon : float or ndarray
    174        longitudes in degrees
    175    datetimeUTC : pandas.Timestamp, datetime, or str
    176        date and time in UTC
    177    refraction : bool, optional (default=False)
    178        specifies whether to account for atmospheric refraction
    179    temperature : float or ndarray, optional (default=10)
    180        surface atmospheric temperature (Celsius), only used for refraction calculation
    181    pressure : float or ndarray, optional (default=101325)
    182        surface atmospheric pressure (Pa), only used for refraction calculation
    183
    184    Returns
    185    -------
    186    sza : float or ndarray
    187        solar zenith angle in degrees at the designated locations and times
    188        If refraction=False, this is the true solar zenith angle
    189        If refraction=True, this is the apparent solar zenith angle
    190    '''
    191    # Convert to pandas Timestamp, if needed
    192    if not isinstance(datetimeUTC, pd.Timestamp):
    193        datetimeUTC = pd.Timestamp(datetimeUTC)
    194
    195    # Solar declination, degrees
    196    dec = solar_declination( datetimeUTC )
    197
    198    # Hour angle, degrees
    199    Ha = hour_angle( lon, datetimeUTC )
    200
    201    # True solar zenith angle, radians
    202    sza = np.arccos( np.sin(lat*pi180) * np.sin(dec*pi180) + \
    203          np.cos(lat*pi180) * np.cos(dec*pi180) * np.cos(Ha*pi180) )
    204
    205    # Convert radians -> degrees
    206    sza /= pi180
    207
    208    if refraction:
    209        # Subtract refraction angle (degrees) from zenith angle.
    210        # SZA is always smaller due to refraction.
    211        sza -= refraction_angle( 90-sza, pressure, temperature )
    212
    213    return sza

Calculate solar zenith angle 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

    215def solar_azimuth_angle( lat, lon, datetimeUTC ):
    216    '''Solar azimuth angle (degrees) for a latitude, longitude, date and time
    217
    218    SAA is degrees clockwise from north.
    219
    220    Parameters
    221    ----------
    222    lat : float or ndarray
    223        latitude in degrees
    224    lon : float or ndarray
    225        longitudes in degrees
    226    datetimeUTC : pandas.Timestamp, datetime, or str
    227        date and time in UTC
    228
    229    Returns
    230    -------
    231    saa : float or ndarray
    232        solar azimuth angle in degrees (clockwise from north)
    233    '''
    234    # Convert to pandas Timestamp, if needed
    235    if not isinstance(datetimeUTC, pd.Timestamp):
    236        datetimeUTC = pd.Timestamp(datetimeUTC)
    237
    238    # Solar declination, degrees
    239    dec = solar_declination( datetimeUTC )
    240
    241    # Hour angle, degrees
    242    Ha = hour_angle( lon, datetimeUTC )
    243
    244    # Solar zenith angle, degrees
    245    # Use true sza, without refraction
    246    zen = sza( lat, lon, datetimeUTC, refraction=False )
    247
    248    # Solar azimuth angle, degrees
    249    saa = np.arcsin( -np.sin( Ha*pi180 ) * np.cos( dec*pi180 ) /
    250            np.sin( zen*pi180 ) ) / pi180
    251
    252    # Change range [-180,180] to [0,360]
    253    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

    306def solar_elevation_angle( lat, lon, alt, datetimeUTC,
    307                       refraction=False, temperature=10., pressure=101325. ):
    308    '''Solar elevation angle above the horizon
    309
    310    The altitude parameter should be the vertical distance
    311    above the surrounding terrain that defines the horizon,
    312    not necessarily the altitude above sea level or the altitude above ground level.
    313    For example, on a mountain peak that is 4000 m above sea level and
    314    1500 m above the surrounding plateau, the relevant altitude is 1500 m.
    315    For an observer on the plateau, the relevant altitude is 0 m.
    316
    317    See documentation for `solar_zenith_angle` and `horizon_zenith_angle`.
    318
    319    Parameters
    320    ----------
    321    lat : float or ndarray
    322        latitude in degrees
    323    lon : float or ndarray
    324        longitudes in degrees
    325    alt : float or ndarray
    326        altitude above surrounding terrain that defines the horizon, meters
    327    datetimeUTC : pandas.Timestamp, datetime, or str
    328        date and time in UTC
    329    refraction : bool, optional (default=False)
    330        specifies whether to account for atmospheric refraction
    331    temperature : float or ndarray, optional (default=10)
    332        surface atmospheric temperature (Celsius), only used for refraction calculation
    333    pressure : float or ndarray, optional (default=101325)
    334        surface atmospheric pressure (Pa), only used for refraction calculation
    335
    336    Returns
    337    -------
    338    sea : float or ndarray
    339        solar elevation angle in degrees at the designated locations and times
    340        If refraction=False, this is the true solar elevation angle
    341        If refraction=True, this is the apparent solar elevation angle
    342
    343    '''
    344
    345    if refraction and np.any(alt):
    346        warnings.warn( 'Atmospheric refraction is calculated for surface conditions, '
    347                    + 'but an altitude above the surface was specified',
    348                     category=UserWarning,
    349                     stacklevel=2 )
    350
    351    sea = horizon_zenith_angle( lat, alt ) \
    352         - solar_zenith_angle( lat, lon, datetimeUTC, refraction, temperature, pressure )
    353
    354    return sea

Solar elevation angle 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

    54def equation_of_time( date ):
    55    '''Calculate equation of time (degrees) for specified date
    56
    57    Implements the "alternative equation" from Wikipedia, derived from
    58    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
    59    Results checked against NOAA solar calculator and agree within 10 seconds.
    60
    61    Argument
    62    --------
    63    date : pandas.Timestamp, date, or datetime
    64        date for calculation
    65
    66    Returns
    67    -------
    68    eot : float
    69        equation of time in degrees on the specified date
    70    '''
    71    # Convert to pandas Timestamp, if needed
    72    if not isinstance(date, pd.Timestamp):
    73        date = pd.Timestamp(date)
    74
    75    # Equation of time, accounts for the solar day differing slightly from 24 hr
    76    doy = date.dayofyear
    77    W = 360 / 365.24
    78    A = W * (doy+10)
    79    B = A + 1.914 * np.sin( W * (doy-2) * pi180 )
    80    C = ( A - np.arctan2( np.tan(B*pi180), np.cos(23.44*pi180) ) / pi180 ) / 180
    81
    82    # Equation of time in minutes of an hour
    83    eotmin = 720 * ( C - np.round(C) )
    84
    85    # Equation of time, minutes -> degrees
    86    eot = eotmin / 60 * 360 / 24
    87
    88    return eot

Calculate 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

    16def solar_declination( date ):
    17    '''Calculate solar declination (degrees) for specified date
    18
    19    Implements Eq. 9.68-9.72 from M.Z. Jacobson, Fundamentals of Atmospheric Modeling
    20
    21    Argument
    22    --------
    23    date : pandas.Timestamp, date, datetime, or str
    24        date for calculation
    25
    26    Returns
    27    -------
    28    dec : float
    29        solar declination in degrees at the specified date
    30    '''
    31
    32    # Convert to pandas Timestamp, if needed
    33    if not isinstance(date, pd.Timestamp):
    34        date = pd.Timestamp(date)
    35
    36     # Number of days since beginning of 2000
    37    NJD = np.floor( date.to_julian_date() - pd.Timestamp(2000,1,1).to_julian_date() )
    38
    39    # Obliquity, degrees
    40    ob = 23.439 - 4e-7 * NJD
    41
    42    # Parameters for ecliptic, degrees
    43    gm = 357.528 + 0.9856003 * NJD
    44    lm = 280.460 + 0.9856474 * NJD
    45
    46    # Ecliptic longitude of sun, degrees
    47    ec = lm + 1.915 * np.sin( gm * pi180 ) + 0.020 * np.sin( 2 * gm * pi180 )
    48
    49    #Solar declination, degrees
    50    dec = np.arcsin( np.sin( ob * pi180 ) * np.sin( ec * pi180 ) ) / pi180
    51
    52    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