def earthsun_distance(moment): r'''Calculates the distance between the earth and the sun as a function of date and time. Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Time and date for the calculation, in UTC time (or GMT, which is almost the same thing); not local time, [-] Returns ------- distance : float Distance between the center of the earth and the center of the sun, [m] Examples -------- >>> earthsun_distance(datetime(2003, 10, 17, 13, 30, 30)) 149090925951.18338 The distance at perihelion, which occurs at 4:21 according to this algorithm. The real value is 04:38 (January 2nd). >>> earthsun_distance(datetime(2013, 1, 2, 4, 21, 50)) 147098089490.67123 The distance at aphelion, which occurs at 14:44 according to this algorithm. The real value is dead on - 14:44 (July 5). >>> earthsun_distance(datetime(2013, 7, 5, 14, 44, 51, 0)) 152097354414.36044 Notes ----- This function is quite accurate. The difference comes from the impact of the moon. Note this function is not continuous; the sun-earth distance is not sufficiently accurately modeled for the change to be continuous throughout each day. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) import calendar unixtime = calendar.timegm(moment.timetuple()) # Convert datetime object to unixtime return float(spa.earthsun_distance(unixtime, delta_t=delta_t)) * au
def earthsun_distance(moment): r'''Calculates the distance between the earth and the sun as a function of date and time. Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Time and date for the calculation, in UTC time (or GMT, which is almost the same thing); not local time, [-] Returns ------- distance : float Distance between the center of the earth and the center of the sun, [m] Examples -------- >>> earthsun_distance(datetime(2003, 10, 17, 13, 30, 30)) 149090925951.18338 The distance at perihelion, which occurs at 4:21 according to this algorithm. The real value is 04:38 (January 2nd). >>> earthsun_distance(datetime(2013, 1, 2, 4, 21, 50)) 147098089490.67123 The distance at aphelion, which occurs at 14:44 according to this algorithm. The real value is dead on - 14:44 (July 5). >>> earthsun_distance(datetime(2013, 7, 5, 14, 44, 51, 0)) 152097354414.36044 Notes ----- This function is quite accurate. The difference comes from the impact of the moon. Note this function is not continuous; the sun-earth distance is not sufficiently accurately modeled for the change to be continuous throughout each day. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) import calendar unixtime = calendar.timegm(moment.timetuple()) # Convert datetime object to unixtime return float(spa.earthsun_distance(unixtime, delta_t=delta_t))*au
def test_deltat_astropy(): # Can't do a full range of tests because astropy doesn't have # answers before 1960, after 1999 in this version from astropy.time import Time from datetime import datetime def delta_t_astropy(dt): t = Time(dt, scale='utc') return -(dt - t.tt.value).total_seconds() # years = range(1, 3000, 100) + [3000] years = range(1960, 1999, 1) months = range(1, 13) for year in years: for month in months: delta_t_pvlib = spa.calculate_deltat(year, month) dt = datetime(year, month, 1) delta_t_external = delta_t_astropy(dt) assert_allclose(delta_t_pvlib, delta_t_external, atol=.5, rtol=.01)
def sunrise_sunset(moment, latitude, longitude): r'''Calculates the times at which the sun is at sunset; sunrise; and halfway between sunrise and sunset (transit). Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Date for the calculation; needs to contain only the year, month, and day; if it is timezone-aware, the return values will be localized to this timezone [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Returns ------- sunrise : datetime The time at the specified day when the sun rises **IN UTC IF MOMENT DOES NOT HAVE A TIMEZONE, OTHERWISE THE TIMEZONE GIVEN WITH IT**, [-] sunset : datetime The time at the specified day when the sun sets **IN UTC IF MOMENT DOES NOT HAVE A TIMEZONE, OTHERWISE THE TIMEZONE GIVEN WITH IT**, [-] transit : datetime The time at the specified day when the sun is at solar noon - halfway between sunrise and sunset **IN UTC IF MOMENT DOES NOT HAVE A TIMEZONE, OTHERWISE THE TIMEZONE GIVEN WITH IT**, [-] Examples -------- >>> sunrise, sunset, transit = sunrise_sunset(datetime(2018, 4, 17), ... 51.0486, -114.07) >>> sunrise datetime.datetime(2018, 4, 17, 12, 36, 55, 782660) >>> sunset datetime.datetime(2018, 4, 18, 2, 34, 4, 249326) >>> transit datetime.datetime(2018, 4, 17, 19, 35, 46, 686265) Example with time zone: >>> import pytz >>> sunrise_sunset(pytz.timezone('America/Edmonton').localize(datetime(2018, 4, 17)), 51.0486, -114.07) (datetime.datetime(2018, 4, 16, 6, 39, 1, 570479, tzinfo=<DstTzInfo 'America/Edmonton' MDT-1 day, 18:00:00 DST>), datetime.datetime(2018, 4, 16, 20, 32, 25, 778162, tzinfo=<DstTzInfo 'America/Edmonton' MDT-1 day, 18:00:00 DST>), datetime.datetime(2018, 4, 16, 13, 36, 0, 386341, tzinfo=<DstTzInfo 'America/Edmonton' MDT-1 day, 18:00:00 DST>)) Note that the year/month/day as input with a timezone, is converted to UTC time as well. Notes ----- This functions takes on the order of 2 ms per calculation. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. ''' from fluids.optional import spa import calendar if moment.utcoffset() is not None: moment_utc = moment + moment.utcoffset() else: moment_utc = moment delta_t = spa.calculate_deltat(moment_utc.year, moment_utc.month) # Strip the part of the day ymd_moment_utc = datetime(moment_utc.year, moment_utc.month, moment_utc.day) unixtime = calendar.timegm(ymd_moment_utc.utctimetuple()) unixtime = unixtime - unixtime % (86400) # Remove the remainder of the value, rounding it to the day it is transit, sunrise, sunset = spa.transit_sunrise_sunset(unixtime, lat=latitude, lon=longitude, delta_t=delta_t) transit = datetime.utcfromtimestamp(transit) sunrise = datetime.utcfromtimestamp(sunrise) sunset = datetime.utcfromtimestamp(sunset) if moment.tzinfo is not None: sunrise = moment.tzinfo.fromutc(sunrise) sunset = moment.tzinfo.fromutc(sunset) transit = moment.tzinfo.fromutc(transit) return sunrise, sunset, transit
def solar_position(moment, latitude, longitude, Z=0.0, T=298.15, P=101325.0, atmos_refract=0.5667): r'''Calculate the position of the sun in the sky. It is defined in terms of two angles - the zenith and the azimith. The azimuth tells where a sundial would see the sun as coming from; the zenith tells how high in the sky it is. The solar elevation angle is returned for convinience; it is the complimentary angle of the zenith. The sun's refraction changes how high it appears as though the sun is; so values are returned with an optional conversion to the aparent angle. This impacts only the zenith/elevation. Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime, optionally with pytz info Time and date for the calculation, in UTC time OR in the time zone of the latitude/longitude specified BUT WITH A TZINFO ATTATCHED! Please be careful with this argument, time zones are confusing. [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Z : float, optional Elevation above sea level for the solar position calculation, [m] T : float, optional Temperature of atmosphere at ground level, [K] P : float, optional Pressure of atmosphere at ground level, [Pa] atmos_refract : float, optional Atmospheric refractivity, [degrees] Returns ------- apparent_zenith : float Zenith of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] zenith : float Actual zenith of the sun (ignores atmospheric refraction), [degrees] apparent_altitude : float Altitude of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] altitude : float Actual altitude of the sun (ignores atmospheric refraction), [degrees] azimuth : float The azimuth of the sun, [degrees] equation_of_time : float Equation of time - the number of seconds to be added to the day's mean solar time to obtain the apparent solar noon time, [seconds] Examples -------- >>> import pytz Perth, Australia - sunrise >>> solar_position(pytz.timezone('Australia/Perth').localize(datetime(2020, 6, 6, 7, 10, 57)), -31.95265, 115.85742) [90.89617025931, 90.89617025931, -0.896170259317, -0.896170259317, 63.6016017691, 79.0711232143] Perth, Australia - Comparing against an online source https://www.suncalc.org/#/-31.9526,115.8574,9/2020.06.06/14:30/1/0 >>> solar_position(pytz.timezone('Australia/Perth').localize(datetime(2020, 6, 6, 14, 30, 0)), -31.95265, 115.85742) [63.4080568623, 63.4400018158, 26.59194313766, 26.55999818417, 325.121376246, 75.7467475485] Perth, Australia - time input without timezone; must be converted by user to UTC! >>> solar_position(datetime(2020, 6, 6, 14, 30, 0) - timedelta(hours=8), -31.95265, 115.85742) [63.4080568623, 63.4400018158, 26.59194313766, 26.55999818417, 325.121376246, 75.7467475485] Sunrise occurs when the zenith is 90 degrees (Calgary, AB): >>> local_time = datetime(2018, 4, 15, 6, 43, 5) >>> local_time = pytz.timezone('America/Edmonton').localize(local_time) >>> solar_position(local_time, 51.0486, -114.07)[0] 90.0005468548 Sunset occurs when the zenith is 90 degrees (13.5 hours later in this case): >>> solar_position(pytz.timezone('America/Edmonton').localize(datetime(2018, 4, 15, 20, 30, 28)), 51.0486, -114.07) [89.999569566, 90.5410381216, 0.000430433876, -0.541038121618, 286.831378190, 6.63142952587] Notes ----- If you were standing at the same longitude of the sun such that it was no further east or west than you were, the amount of angle it was south or north of you is the *zenith*. If it were directly overhead it would be 0°; a little north or south and it would be a little positive; near sunset or sunrise, near 90°; and at night, between 90° and 180°. The *solar altitude angle* is defined as 90° -`zenith`. Note the *elevation* angle is just another name for the *altitude* angle. The *azimuth* the angle in degrees that the sun is East of the North angle. It is positive North eastwards 0° to 360°. Other conventions may be used. Note that due to differences in atmospheric refractivity, estimation of sunset and sunrise are accuract to no more than one minute. Refraction conditions truly vary across the atmosphere; so characterizing it by an average value is limiting as well. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. .. [2] "Navigation - What Azimuth Description Systems Are in Use? - Astronomy Stack Exchange." https://astronomy.stackexchange.com/questions/237/what-azimuth-description-systems-are-in-use?rq=1. ''' from fluids.optional import spa import calendar tt = moment.utctimetuple() delta_t = spa.calculate_deltat(tt.tm_year, tt.tm_mon) unixtime = calendar.timegm(tt) # Input pressure in milibar; input temperature in deg C # print(dict(unixtime=unixtime, lat=latitude, lon=longitude, elev=Z, # pressure=P*1E-2, temp=T-273.15, delta_t=delta_t, # atmos_refract=atmos_refract, sst=False)) result = spa.solar_position(unixtime, lat=latitude, lon=longitude, elev=Z, pressure=P*1E-2, temp=T-273.15, delta_t=delta_t, atmos_refract=atmos_refract, sst=False) # confirmed equation of time https://www.minasi.com/figeot.asp # Convert minutes to seconds; sometimes negative, sometimes positive result[-1] = result[-1]*60.0 return result
def sunrise_sunset(moment, latitude, longitude): r'''Calculates the times at which the sun is at sunset; sunrise; and halfway between sunrise and sunset (transit). Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Date for the calculation; needs to contain only the year, month, and day, [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Returns ------- sunrise : datetime The time at the specified day when the sun rises **IN UTC**, [-] sunset : datetime The time at the specified day when the sun sets **IN UTC**, [-] transit : datetime The time at the specified day when the sun is at solar noon - halfway between sunrise and sunset **IN UTC**, [-] Examples -------- >>> sunrise, sunset, transit = sunrise_sunset(datetime(2018, 4, 17), ... 51.0486, -114.07) >>> sunrise datetime.datetime(2018, 4, 17, 12, 36, 55, 782660) >>> sunset datetime.datetime(2018, 4, 18, 2, 34, 4, 249326) >>> transit datetime.datetime(2018, 4, 17, 19, 35, 46, 686265) Notes ----- This functions takes on the order of 2 ms per calculation. The reason the function cannot return the time correct the local timezone is that the function does not know the timezone at the specified lat/long. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) # Strip the part of the day moment = datetime(moment.year, moment.month, moment.day) import calendar unixtime = calendar.timegm(moment.timetuple()) unixtime = unixtime - unixtime % ( 86400 ) # Remove the remainder of the value, rounding it to the day it is transit, sunrise, sunset = spa.transit_sunrise_sunset(np.array([unixtime]), lat=latitude, lon=longitude, delta_t=delta_t, numthreads=1) transit = datetime.utcfromtimestamp(float(transit)) sunrise = datetime.utcfromtimestamp(float(sunrise)) sunset = datetime.utcfromtimestamp(float(sunset)) return sunrise, sunset, transit
def solar_position(moment, latitude, longitude, Z=0.0, T=298.15, P=101325.0, atmos_refract=0.5667): r'''Calculate the position of the sun in the sky. It is defined in terms of two angles - the zenith and the azimith. The azimuth tells where a sundial would see the sun as coming from; the zenith tells how high in the sky it is. The solar elevation angle is returned for convinience; it is the complimentary angle of the zenith. The sun's refraction changes how high it appears as though the sun is; so values are returned with an optional conversion to the aparent angle. This impacts only the zenith/elevation. Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Time and date for the calculation, in local UTC time (not daylight savings time), [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Z : float, optional Elevation above sea level for the solar position calculation, [m] T : float, optional Temperature of atmosphere at ground level, [K] P : float, optional Pressure of atmosphere at ground level, [Pa] atmos_refract : float, optional Atmospheric refractivity, [degrees] Returns ------- apparent_zenith : float Zenith of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] zenith : float Actual zenith of the sun (ignores atmospheric refraction), [degrees] apparent_altitude : float Altitude of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] altitude : float Actual altitude of the sun (ignores atmospheric refraction), [degrees] azimuth : float The azimuth of the sun, [degrees] equation_of_time : float Equation of time - the number of seconds to be added to the day's mean solar time to obtain the apparent solar noon time, [seconds] Examples -------- >>> solar_position(datetime(2003, 10, 17, 13, 30, 30), 45, 45) [140.8367913391112, 140.8367913391112, -50.83679133911118, -50.83679133911118, 329.9096671679604, 878.4902950980904] Sunrise occurs when the zenith is 90 degrees (Calgary, AB): >>> solar_position(datetime(2018, 4, 15, 6, 43, 5), 51.0486, -114.07)[0] 90.00054676987014 Sunrise also occurs when the zenith is 90 degrees (13.5 hours later): >>> solar_position(datetime(2018, 4, 15, 20, 30, 28), 51.0486, -114.07) [89.9995695661236, 90.54103812161853, 0.00043043387640950836, -0.5410381216185247, 286.8313781904518, 6.631429525878048] Notes ----- If you were standing at the same longitude of the sun such that it was no further east or west than you were, the amount of angle it was south or north of you is the *zenith*. If it were directly overhead it would be 0°; a little north or south and it would be a little positive; near sunset or sunrise, near 90°; and at night, between 90° and 180°. The *solar altitude angle* is defined as 90° -`zenith`. Note the *elevation* angle is just another name for the *altitude* angle. The *azimuth* the angle in degrees that the sun is East of the North angle. It is positive North eastwards 0° to 360°. Other conventions may be used. Note that due to differences in atmospheric refractivity, estimation of sunset and sunrise are accuract to no more than one minute. Refraction conditions truly vary across the atmosphere; so characterizing it by an average value is limiting as well. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. .. [2] "Navigation - What Azimuth Description Systems Are in Use? - Astronomy Stack Exchange." https://astronomy.stackexchange.com/questions/237/what-azimuth-description-systems-are-in-use?rq=1. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) unixtime = time.mktime(moment.timetuple()) # Input pressure in milibar; input temperature in deg C result = spa.solar_position_numpy(unixtime, lat=latitude, lon=longitude, elev=Z, pressure=P * 1E-2, temp=T - 273.15, delta_t=delta_t, atmos_refract=atmos_refract, sst=False, esd=False) # confirmed equation of time https://www.minasi.com/figeot.asp # Convert minutes to seconds; sometimes negative, sometimes positive result[-1] = result[-1] * 60.0 return result
def sunrise_sunset(moment, latitude, longitude): r'''Calculates the times at which the sun is at sunset; sunrise; and halfway between sunrise and sunset (transit). Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Date for the calculationl; needs to contain only the year, month, and day, [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Returns ------- sunrise : datetime The time at the specified day when the sun rises **IN UTC**, [-] sunset : datetime The time at the specified day when the sun sets **IN UTC**, [-] transit : datetime The time at the specified day when the sun is at solar noon - halfway between sunrise and sunset **IN UTC**, [-] Examples -------- >>> sunrise, sunset, transit = sunrise_sunset(datetime(2018, 4, 17), ... 51.0486, -114.07) >>> sunrise datetime.datetime(2018, 4, 17, 12, 36, 55, 782660) >>> sunset datetime.datetime(2018, 4, 18, 2, 34, 4, 249326) >>> transit datetime.datetime(2018, 4, 17, 19, 35, 46, 686265) Notes ----- This functions takes on the order of 2 ms per calculation. The reason the function cannot return the time correct the local timezone is that the function does not know the timezone at the specified lat/long. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) # Strip the part of the day moment = datetime(moment.year, moment.month, moment.day) import calendar unixtime = calendar.timegm(moment.timetuple()) unixtime = unixtime - unixtime % (86400) # Remove the remainder of the value, rounding it to the day it is transit, sunrise, sunset = spa.transit_sunrise_sunset(np.array([unixtime]), lat=latitude, lon=longitude, delta_t=delta_t, numthreads=1) transit = datetime.utcfromtimestamp(float(transit)) sunrise = datetime.utcfromtimestamp(float(sunrise)) sunset = datetime.utcfromtimestamp(float(sunset)) return sunrise, sunset, transit
def solar_position(moment, latitude, longitude, Z=0.0, T=298.15, P=101325.0, atmos_refract=0.5667): r'''Calculate the position of the sun in the sky. It is defined in terms of two angles - the zenith and the azimith. The azimuth tells where a sundial would see the sun as coming from; the zenith tells how high in the sky it is. The solar elevation angle is returned for convinience; it is the complimentary angle of the zenith. The sun's refraction changes how high it appears as though the sun is; so values are returned with an optional conversion to the aparent angle. This impacts only the zenith/elevation. Uses the Reda and Andreas (2004) model described in [1]_, originally incorporated into the excellent `pvlib library <https://github.com/pvlib/pvlib-python>`_ Parameters ---------- moment : datetime Time and date for the calculation, in local UTC time (not daylight savings time), [-] latitude : float Latitude, between -90 and 90 [degrees] longitude : float Longitude, between -180 and 180, [degrees] Z : float, optional Elevation above sea level for the solar position calculation, [m] T : float, optional Temperature of atmosphere at ground level, [K] P : float, optional Pressure of atmosphere at ground level, [Pa] atmos_refract : float, optional Atmospheric refractivity, [degrees] Returns ------- apparent_zenith : float Zenith of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] zenith : float Actual zenith of the sun (ignores atmospheric refraction), [degrees] apparent_altitude : float Altitude of the sun as observed from the ground based after accounting for atmospheric refraction, [degrees] altitude : float Actual altitude of the sun (ignores atmospheric refraction), [degrees] azimuth : float The azimuth of the sun, [degrees] equation_of_time : float Equation of time - the number of seconds to be added to the day's mean solar time to obtain the apparent solar noon time, [seconds] Examples -------- >>> solar_position(datetime(2003, 10, 17, 13, 30, 30), 45, 45) [140.8367913391112, 140.8367913391112, -50.83679133911118, -50.83679133911118, 329.9096671679604, 878.4902950980904] Sunrise occurs when the zenith is 90 degrees (Calgary, AB): >>> solar_position(datetime(2018, 4, 15, 6, 43, 5), 51.0486, -114.07)[0] 90.00054676987014 Sunrise also occurs when the zenith is 90 degrees (13.5 hours later): >>> solar_position(datetime(2018, 4, 15, 20, 30, 28), 51.0486, -114.07) [89.9995695661236, 90.54103812161853, 0.00043043387640950836, -0.5410381216185247, 286.8313781904518, 6.631429525878048] Notes ----- If you were standing at the same longitude of the sun such that it was no further east or west than you were, the amount of angle it was south or north of you is the *zenith*. If it were directly overhead it would be 0°; a little north or south and it would be a little positive; near sunset or sunrise, near 90°; and at night, between 90° and 180°. The *solar altitude angle* is defined as 90° -`zenith`. Note the *elevation* angle is just another name for the *altitude* angle. The *azimuth* the angle in degrees that the sun is East of the North angle. It is positive North eastwards 0° to 360°. Other conventions may be used. Note that due to differences in atmospheric refractivity, estimation of sunset and sunrise are accuract to no more than one minute. Refraction conditions truly vary across the atmosphere; so characterizing it by an average value is limiting as well. References ---------- .. [1] Reda, Ibrahim, and Afshin Andreas. "Solar Position Algorithm for Solar Radiation Applications." Solar Energy 76, no. 5 (January 1, 2004): 577-89. https://doi.org/10.1016/j.solener.2003.12.003. .. [2] "Navigation - What Azimuth Description Systems Are in Use? - Astronomy Stack Exchange." https://astronomy.stackexchange.com/questions/237/what-azimuth-description-systems-are-in-use?rq=1. ''' from fluids.optional import spa delta_t = spa.calculate_deltat(moment.year, moment.month) unixtime = time.mktime(moment.timetuple()) # Input pressure in milibar; input temperature in deg C result = spa.solar_position_numpy(unixtime, lat=latitude, lon=longitude, elev=Z, pressure=P*1E-2, temp=T-273.15, delta_t=delta_t, atmos_refract=atmos_refract, sst=False, esd=False) # confirmed equation of time https://www.minasi.com/figeot.asp # Convert minutes to seconds; sometimes negative, sometimes positive result[-1] = result[-1]*60.0 return result