ladybugdynamo.ladybug.sunpath module
import math
import core
import euclid
# NOTE:
# Dear developers,
# If classes will be used as base class in geometries use LB in front of class names
# This will avoid name confusions between the libraries and all the geometry libraries
# can use the same class name (e.g Sunpath)
class LBSunpath(object):
"""
Calculates sun path
Attributes:
latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator.
northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0)
longitude: The longitude of the location (Default: 0)
timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot. The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West.
daylightSavingPeriod: An analysis period for daylight saving. (Default = None)
Usage:
import ladybug.sunpath as sunpath
# initiate sunpath
sp = sunpath.LBSunpath(50)
sun = sp.calculateSun(1, 1, 12) # calculate sun data for Jan 1 at noon
print sun.azimuth, sun.altitude
"""
def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
daylightSavingPeriod = None):
self.__latitude = math.radians(float(latitude))
self.__longitude = math.radians(float(longitude))
self.northAngle = northAngle
self.timeZone = timeZone
self.daylightSavingPeriod = daylightSavingPeriod
@classmethod
def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None):
return cls(location.latitude, northAngle, location.longitude, \
location.timeZone, daylightSavingPeriod)
@property
def latitude(self):
"""get latitude in degrees"""
return math.degrees(self.__latitude)
@latitude.setter
def latitude(self, value):
"""set latitude value in degrees"""
self.__latitude = math.radians(float(value))
@property
def longitude(self):
"""get latitude in degrees"""
return math.degrees(self.__latitude)
@longitude.setter
def longitude(self, value):
"""set latitude value in degrees"""
self.__longitude = math.radians(float(value))
def isDaylightSavingHour(self, datetime):
if not self.daylightSavingPeriod: return False
return self.daylightSavingPeriod.isTimeIncluded(datetime.HOY)
def calculateSun(self, month, day, hour, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
month: An integer between 1-12
day: An integer between 1-31
hour: A positive number <= 24
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime(month, day, hour)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunFromHOY(self, HOY, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime.fromHOY(HOY)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunFromDataTime(self, datetime, isSolarTime = False):
"""Get Sun data for an hour of the year
This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com)
based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
month, day, hour = datetime.month, datetime.day, datetime.floatHour
isDaylightSaving = self.isDaylightSavingHour(datetime.HOY)
if isDaylightSaving: hour += 1
#hours
solTime = self.__calculateSolarTime(hour, eqOfTime, isSolarTime)
#degrees
hourAngle = (solTime*15 + 180) if (solTime*15 < 0) else (solTime*15 - 180)
#RADIANS
zenith = math.acos(math.sin(self.__latitude)*math.sin(solDec) \
+ math.cos(self.__latitude)*math.cos(solDec)*math.cos(math.radians(hourAngle)))
altitude = (math.pi/2) - zenith
if hourAngle == 0.0 or hourAngle == -180.0 or hourAngle == 180.0:
if solDec < self.__latitude: azimuth = math.pi
else: azimuth = 0.0
else:
azimuth = ((math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith))) + math.pi) % (2*math.pi)) \
if (hourAngle > 0) else \
((3*math.pi - math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith)))) % (2*math.pi))
# create the sun for this hour
return LBSun(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, self.northAngle)
def calculateSunriseSunset(self, month, day, depression = 0.833, isSolarTime = False):
datetime = core.LBDateTime(month, day, hour = 12)
return self.calculateSunriseSunsetFromDateTime(datetime, depression, isSolarTime)
# TODO: implement solar time
def calculateSunriseSunsetFromDateTime(self, datetime, depression = 0.833, isSolarTime = False):
"""Calculate sunrise, sunset and noon for a day of year"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
# calculate sunrise and sunset hour
#if isSolarTime:
# noon = .5
#else:
noon = (720 - 4 * math.degrees(self.__longitude) - eqOfTime + self.timeZone * 60) / 1440
sunRiseHourAngle = self.__calculateSunriseHourAngle(solDec, depression)
sunrise = noon - sunRiseHourAngle * 4 / 1440
sunset = noon + sunRiseHourAngle * 4 / 1440
# convert demical hour to solar hour
# noon = self.__calculateSolarTime(24*noon, eqOfTime, isSolarTime)
# sunrise = self.__calculateSolarTime(24*sunrise, eqOfTime, isSolarTime)
# sunset = self.__calculateSolarTime(24*sunset, eqOfTime, isSolarTime)
return {
"sunrise" : core.LBDateTime(datetime.month, datetime.day, 24 * sunrise),
"noon" : core.LBDateTime(datetime.month, datetime.day, 24 * noon),
"sunset" : core.LBDateTime(datetime.month, datetime.day, 24 * sunset)}
def __calculateSolarGeometry(self, datetime, year = 2015):
"""Calculate Solar geometry for an hour of the year
Attributes:
datetime: A Ladybug datetime
Returns:
Solar declination: Solar declination in radians
eqOfTime: Equation of time as minutes
"""
month, day, hour = datetime.month, datetime.day, datetime.floatHour
a = 1 if (month < 3) else 0
y = year + 4800 - a
m = month + 12*a - 3
julianDay = day + math.floor((153*m + 2)/5) + 59
julianDay += (hour - self.timeZone)/24.0 + 365*y + math.floor(y/4) \
- math.floor(y/100) + math.floor(y/400) - 32045.5 - 59
julianCentury = (julianDay - 2451545) / 36525
#degrees
geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury*0.0003032)) % 360
#degrees
geomMeanAnomSun = 357.52911 + julianCentury*(35999.05029 - 0.0001537*julianCentury)
eccentOrbit = 0.016708634 - julianCentury*(0.000042037 + 0.0000001267*julianCentury)
sunEqOfCtr = math.sin(math.radians(geomMeanAnomSun))*(1.914602 - julianCentury*(0.004817+0.000014*julianCentury)) + \
math.sin(math.radians(2*geomMeanAnomSun))*(0.019993-0.000101*julianCentury) + \
math.sin(math.radians(3*geomMeanAnomSun))*0.000289
#degrees
sunTrueLong = geomMeanLongSun + sunEqOfCtr
#AUs
sunTrueAnom = geomMeanAnomSun + sunEqOfCtr
#AUs
sunRadVector = (1.000001018*(1 - eccentOrbit**2))/ \
(1 + eccentOrbit*math.cos(math.radians(sunTrueLong)))
#degrees
sunAppLong = sunTrueLong - 0.00569 - 0.00478*math.sin(math.radians(125.04-1934.136*julianCentury))
#degrees
meanObliqEcliptic = 23 + (26 + ((21.448 - julianCentury*(46.815 + \
julianCentury*(0.00059 - julianCentury*0.001813))))/60)/60
#degrees
obliqueCorr = meanObliqEcliptic + 0.00256*math.cos(math.radians(125.04 - 1934.136*julianCentury))
#degrees
sunRightAscen = math.degrees(math.atan2(math.cos(math.radians(obliqueCorr))* \
math.sin(math.radians(sunAppLong)), math.cos(math.radians(sunAppLong))))
#RADIANS
solDec = math.asin(math.sin(math.radians(obliqueCorr))*math.sin(math.radians(sunAppLong)))
varY = math.tan(math.radians(obliqueCorr/2))*math.tan(math.radians(obliqueCorr/2))
#minutes
eqOfTime = 4*math.degrees(varY*math.sin(2*math.radians(geomMeanLongSun)) \
- 2*eccentOrbit*math.sin(math.radians(geomMeanAnomSun)) \
+ 4*eccentOrbit*varY*math.sin(math.radians(geomMeanAnomSun))*math.cos(2*math.radians(geomMeanLongSun)) \
- 0.5*(varY**2)*math.sin(4*math.radians(geomMeanLongSun)) \
- 1.25*(eccentOrbit**2)*math.sin(2*math.radians(geomMeanAnomSun)))
return solDec, eqOfTime
def __calculateSunriseHourAngle(self, solarDec, depression = 0.833):
"""Calculate hour angle for sunrise time in degrees"""
hourAngleArg = math.cos(math.radians(90 + depression)) \
/(math.cos(self.__latitude) * math.cos(solarDec)) \
- math.tan(self.__latitude) * math.tan(solarDec)
return math.degrees(math.acos(hourAngleArg))
def __calculateSolarTime(self, hour, eqOfTime, isSolarTime):
"""Calculate Solar time for an hour"""
if isSolarTime: return hour
return ((hour*60 + eqOfTime + 4*math.degrees(self.__longitude) - 60*self.timeZone) % 1440)/60
class LBSun(object):
"""Create a sun
Attributes:
datetime: A Ladybug datetime that represents the datetime for this sunVector
altitude: Solar Altitude in radians
azimuth: Solar Azimuth in radians
isSolarTime: A Boolean that indicates if datetime represents the solar time
isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period
northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.
"""
def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle):
self.__datetime = datetime
self.__altitude = altitude
self.__azimuth = azimuth
self.__isSolarTime = isSolarTime
self.__isDaylightSaving = isDaylightSaving
self.__northAngle = northAngle # useful to calculate sun vector - sun angle is in degrees
self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet
@property
def datetime(self):
"""Return datetime"""
return self.__datetime
@property
def northAngle(self):
"""Return north angle for +YAxis"""
return self.__northAngle
@property
def HOY(self):
"""Return Hour of the year"""
return self.__datetime.floatHOY
@property
def altitude(self):
"""Return solar altitude in degrees"""
return math.degrees(self.__altitude)
@property
def azimuth(self):
"""Return solar azimuth in degrees"""
return math.degrees(self.__azimuth)
@property
def altitudeInRadians(self):
"""Return solar altitude in radians"""
return self.__altitude
@property
def azimuthInRadians(self):
"""Return solar azimuth in radians"""
return self.__azimuth
@property
def isSolarTime(self):
"""Return a Boolean that indicates is datetime is solar time"""
return self.__isSolarTime
@property
def isDaylightSaving(self):
"""Return a Boolean that indicates is datetime is solar time"""
return self.__isDaylightSaving
@property
def hourlyData(self):
return self.__hourlyData
def appendHourlyData(self, data):
"""Append Ladybug hourly data to this sun"""
assert data.datetime.HOY == self.HOY
self.__hourlyData.append(data)
return True
@property
def isDuringDay(self):
# sun vector is flipped to look to the center
return self.sunVector.z <= 0
@property
def sunVector(self):
"""Return sun vector for this sun
Sun vector will face
"""
zAxis = euclid.Vector3(0., 0., -1.)
xAxis = euclid.Vector3(1., 0., 0.)
northVector = euclid.Vector3(0., 1., 0.)
# rotate north vector based on azimuth, altitude, and north
sunvector = northVector \
.rotate_around(xAxis, self.__altitude) \
.rotate_around(zAxis, self.__azimuth) \
.rotate_around(zAxis, math.radians(-self.__northAngle))
sunvector.normalize().flip()
return sunvector
Classes
class LBSun
Create a sun
Attributes: datetime: A Ladybug datetime that represents the datetime for this sunVector altitude: Solar Altitude in radians azimuth: Solar Azimuth in radians isSolarTime: A Boolean that indicates if datetime represents the solar time isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.
class LBSun(object):
"""Create a sun
Attributes:
datetime: A Ladybug datetime that represents the datetime for this sunVector
altitude: Solar Altitude in radians
azimuth: Solar Azimuth in radians
isSolarTime: A Boolean that indicates if datetime represents the solar time
isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period
northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.
"""
def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle):
self.__datetime = datetime
self.__altitude = altitude
self.__azimuth = azimuth
self.__isSolarTime = isSolarTime
self.__isDaylightSaving = isDaylightSaving
self.__northAngle = northAngle # useful to calculate sun vector - sun angle is in degrees
self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet
@property
def datetime(self):
"""Return datetime"""
return self.__datetime
@property
def northAngle(self):
"""Return north angle for +YAxis"""
return self.__northAngle
@property
def HOY(self):
"""Return Hour of the year"""
return self.__datetime.floatHOY
@property
def altitude(self):
"""Return solar altitude in degrees"""
return math.degrees(self.__altitude)
@property
def azimuth(self):
"""Return solar azimuth in degrees"""
return math.degrees(self.__azimuth)
@property
def altitudeInRadians(self):
"""Return solar altitude in radians"""
return self.__altitude
@property
def azimuthInRadians(self):
"""Return solar azimuth in radians"""
return self.__azimuth
@property
def isSolarTime(self):
"""Return a Boolean that indicates is datetime is solar time"""
return self.__isSolarTime
@property
def isDaylightSaving(self):
"""Return a Boolean that indicates is datetime is solar time"""
return self.__isDaylightSaving
@property
def hourlyData(self):
return self.__hourlyData
def appendHourlyData(self, data):
"""Append Ladybug hourly data to this sun"""
assert data.datetime.HOY == self.HOY
self.__hourlyData.append(data)
return True
@property
def isDuringDay(self):
# sun vector is flipped to look to the center
return self.sunVector.z <= 0
@property
def sunVector(self):
"""Return sun vector for this sun
Sun vector will face
"""
zAxis = euclid.Vector3(0., 0., -1.)
xAxis = euclid.Vector3(1., 0., 0.)
northVector = euclid.Vector3(0., 1., 0.)
# rotate north vector based on azimuth, altitude, and north
sunvector = northVector \
.rotate_around(xAxis, self.__altitude) \
.rotate_around(zAxis, self.__azimuth) \
.rotate_around(zAxis, math.radians(-self.__northAngle))
sunvector.normalize().flip()
return sunvector
Ancestors (in MRO)
- LBSun
- __builtin__.object
Instance variables
var HOY
Return Hour of the year
var altitude
Return solar altitude in degrees
var altitudeInRadians
Return solar altitude in radians
var azimuth
Return solar azimuth in degrees
var azimuthInRadians
Return solar azimuth in radians
var datetime
Return datetime
var hourlyData
var isDaylightSaving
Return a Boolean that indicates is datetime is solar time
var isDuringDay
var isSolarTime
Return a Boolean that indicates is datetime is solar time
var northAngle
Return north angle for +YAxis
var sunVector
Return sun vector for this sun Sun vector will face
Methods
def __init__(
self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle):
self.__datetime = datetime
self.__altitude = altitude
self.__azimuth = azimuth
self.__isSolarTime = isSolarTime
self.__isDaylightSaving = isDaylightSaving
self.__northAngle = northAngle # useful to calculate sun vector - sun angle is in degrees
self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet
def appendHourlyData(
self, data)
Append Ladybug hourly data to this sun
def appendHourlyData(self, data):
"""Append Ladybug hourly data to this sun"""
assert data.datetime.HOY == self.HOY
self.__hourlyData.append(data)
return True
class LBSunpath
Calculates sun path
Attributes: latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator. northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0) longitude: The longitude of the location (Default: 0) timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot. The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West. daylightSavingPeriod: An analysis period for daylight saving. (Default = None)
Usage: import ladybug.sunpath as sunpath # initiate sunpath sp = sunpath.LBSunpath(50) sun = sp.calculateSun(1, 1, 12) # calculate sun data for Jan 1 at noon print sun.azimuth, sun.altitude
class LBSunpath(object):
"""
Calculates sun path
Attributes:
latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator.
northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0)
longitude: The longitude of the location (Default: 0)
timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot. The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West.
daylightSavingPeriod: An analysis period for daylight saving. (Default = None)
Usage:
import ladybug.sunpath as sunpath
# initiate sunpath
sp = sunpath.LBSunpath(50)
sun = sp.calculateSun(1, 1, 12) # calculate sun data for Jan 1 at noon
print sun.azimuth, sun.altitude
"""
def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
daylightSavingPeriod = None):
self.__latitude = math.radians(float(latitude))
self.__longitude = math.radians(float(longitude))
self.northAngle = northAngle
self.timeZone = timeZone
self.daylightSavingPeriod = daylightSavingPeriod
@classmethod
def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None):
return cls(location.latitude, northAngle, location.longitude, \
location.timeZone, daylightSavingPeriod)
@property
def latitude(self):
"""get latitude in degrees"""
return math.degrees(self.__latitude)
@latitude.setter
def latitude(self, value):
"""set latitude value in degrees"""
self.__latitude = math.radians(float(value))
@property
def longitude(self):
"""get latitude in degrees"""
return math.degrees(self.__latitude)
@longitude.setter
def longitude(self, value):
"""set latitude value in degrees"""
self.__longitude = math.radians(float(value))
def isDaylightSavingHour(self, datetime):
if not self.daylightSavingPeriod: return False
return self.daylightSavingPeriod.isTimeIncluded(datetime.HOY)
def calculateSun(self, month, day, hour, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
month: An integer between 1-12
day: An integer between 1-31
hour: A positive number <= 24
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime(month, day, hour)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunFromHOY(self, HOY, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime.fromHOY(HOY)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunFromDataTime(self, datetime, isSolarTime = False):
"""Get Sun data for an hour of the year
This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com)
based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
month, day, hour = datetime.month, datetime.day, datetime.floatHour
isDaylightSaving = self.isDaylightSavingHour(datetime.HOY)
if isDaylightSaving: hour += 1
#hours
solTime = self.__calculateSolarTime(hour, eqOfTime, isSolarTime)
#degrees
hourAngle = (solTime*15 + 180) if (solTime*15 < 0) else (solTime*15 - 180)
#RADIANS
zenith = math.acos(math.sin(self.__latitude)*math.sin(solDec) \
+ math.cos(self.__latitude)*math.cos(solDec)*math.cos(math.radians(hourAngle)))
altitude = (math.pi/2) - zenith
if hourAngle == 0.0 or hourAngle == -180.0 or hourAngle == 180.0:
if solDec < self.__latitude: azimuth = math.pi
else: azimuth = 0.0
else:
azimuth = ((math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith))) + math.pi) % (2*math.pi)) \
if (hourAngle > 0) else \
((3*math.pi - math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith)))) % (2*math.pi))
# create the sun for this hour
return LBSun(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, self.northAngle)
def calculateSunriseSunset(self, month, day, depression = 0.833, isSolarTime = False):
datetime = core.LBDateTime(month, day, hour = 12)
return self.calculateSunriseSunsetFromDateTime(datetime, depression, isSolarTime)
# TODO: implement solar time
def calculateSunriseSunsetFromDateTime(self, datetime, depression = 0.833, isSolarTime = False):
"""Calculate sunrise, sunset and noon for a day of year"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
# calculate sunrise and sunset hour
#if isSolarTime:
# noon = .5
#else:
noon = (720 - 4 * math.degrees(self.__longitude) - eqOfTime + self.timeZone * 60) / 1440
sunRiseHourAngle = self.__calculateSunriseHourAngle(solDec, depression)
sunrise = noon - sunRiseHourAngle * 4 / 1440
sunset = noon + sunRiseHourAngle * 4 / 1440
# convert demical hour to solar hour
# noon = self.__calculateSolarTime(24*noon, eqOfTime, isSolarTime)
# sunrise = self.__calculateSolarTime(24*sunrise, eqOfTime, isSolarTime)
# sunset = self.__calculateSolarTime(24*sunset, eqOfTime, isSolarTime)
return {
"sunrise" : core.LBDateTime(datetime.month, datetime.day, 24 * sunrise),
"noon" : core.LBDateTime(datetime.month, datetime.day, 24 * noon),
"sunset" : core.LBDateTime(datetime.month, datetime.day, 24 * sunset)}
def __calculateSolarGeometry(self, datetime, year = 2015):
"""Calculate Solar geometry for an hour of the year
Attributes:
datetime: A Ladybug datetime
Returns:
Solar declination: Solar declination in radians
eqOfTime: Equation of time as minutes
"""
month, day, hour = datetime.month, datetime.day, datetime.floatHour
a = 1 if (month < 3) else 0
y = year + 4800 - a
m = month + 12*a - 3
julianDay = day + math.floor((153*m + 2)/5) + 59
julianDay += (hour - self.timeZone)/24.0 + 365*y + math.floor(y/4) \
- math.floor(y/100) + math.floor(y/400) - 32045.5 - 59
julianCentury = (julianDay - 2451545) / 36525
#degrees
geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury*0.0003032)) % 360
#degrees
geomMeanAnomSun = 357.52911 + julianCentury*(35999.05029 - 0.0001537*julianCentury)
eccentOrbit = 0.016708634 - julianCentury*(0.000042037 + 0.0000001267*julianCentury)
sunEqOfCtr = math.sin(math.radians(geomMeanAnomSun))*(1.914602 - julianCentury*(0.004817+0.000014*julianCentury)) + \
math.sin(math.radians(2*geomMeanAnomSun))*(0.019993-0.000101*julianCentury) + \
math.sin(math.radians(3*geomMeanAnomSun))*0.000289
#degrees
sunTrueLong = geomMeanLongSun + sunEqOfCtr
#AUs
sunTrueAnom = geomMeanAnomSun + sunEqOfCtr
#AUs
sunRadVector = (1.000001018*(1 - eccentOrbit**2))/ \
(1 + eccentOrbit*math.cos(math.radians(sunTrueLong)))
#degrees
sunAppLong = sunTrueLong - 0.00569 - 0.00478*math.sin(math.radians(125.04-1934.136*julianCentury))
#degrees
meanObliqEcliptic = 23 + (26 + ((21.448 - julianCentury*(46.815 + \
julianCentury*(0.00059 - julianCentury*0.001813))))/60)/60
#degrees
obliqueCorr = meanObliqEcliptic + 0.00256*math.cos(math.radians(125.04 - 1934.136*julianCentury))
#degrees
sunRightAscen = math.degrees(math.atan2(math.cos(math.radians(obliqueCorr))* \
math.sin(math.radians(sunAppLong)), math.cos(math.radians(sunAppLong))))
#RADIANS
solDec = math.asin(math.sin(math.radians(obliqueCorr))*math.sin(math.radians(sunAppLong)))
varY = math.tan(math.radians(obliqueCorr/2))*math.tan(math.radians(obliqueCorr/2))
#minutes
eqOfTime = 4*math.degrees(varY*math.sin(2*math.radians(geomMeanLongSun)) \
- 2*eccentOrbit*math.sin(math.radians(geomMeanAnomSun)) \
+ 4*eccentOrbit*varY*math.sin(math.radians(geomMeanAnomSun))*math.cos(2*math.radians(geomMeanLongSun)) \
- 0.5*(varY**2)*math.sin(4*math.radians(geomMeanLongSun)) \
- 1.25*(eccentOrbit**2)*math.sin(2*math.radians(geomMeanAnomSun)))
return solDec, eqOfTime
def __calculateSunriseHourAngle(self, solarDec, depression = 0.833):
"""Calculate hour angle for sunrise time in degrees"""
hourAngleArg = math.cos(math.radians(90 + depression)) \
/(math.cos(self.__latitude) * math.cos(solarDec)) \
- math.tan(self.__latitude) * math.tan(solarDec)
return math.degrees(math.acos(hourAngleArg))
def __calculateSolarTime(self, hour, eqOfTime, isSolarTime):
"""Calculate Solar time for an hour"""
if isSolarTime: return hour
return ((hour*60 + eqOfTime + 4*math.degrees(self.__longitude) - 60*self.timeZone) % 1440)/60
Ancestors (in MRO)
- LBSunpath
- __builtin__.object
Instance variables
var daylightSavingPeriod
var latitude
get latitude in degrees
var longitude
get latitude in degrees
var northAngle
var timeZone
Methods
def __init__(
self, latitude=0, northAngle=0, longitude=0, timeZone=0, daylightSavingPeriod=None)
def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
daylightSavingPeriod = None):
self.__latitude = math.radians(float(latitude))
self.__longitude = math.radians(float(longitude))
self.northAngle = northAngle
self.timeZone = timeZone
self.daylightSavingPeriod = daylightSavingPeriod
def calculateSun(
self, month, day, hour, isSolarTime=False)
Get Sun data for an hour of the year
Args: month: An integer between 1-12 day: An integer between 1-31 hour: A positive number <= 24 isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns: A sun object for this particular time
def calculateSun(self, month, day, hour, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
month: An integer between 1-12
day: An integer between 1-31
hour: A positive number <= 24
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime(month, day, hour)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunFromDataTime(
self, datetime, isSolarTime=False)
Get Sun data for an hour of the year This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com) based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari
Args: datetime: Ladybug datetime isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns: A sun object for this particular time
def calculateSunFromDataTime(self, datetime, isSolarTime = False):
"""Get Sun data for an hour of the year
This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com)
based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
month, day, hour = datetime.month, datetime.day, datetime.floatHour
isDaylightSaving = self.isDaylightSavingHour(datetime.HOY)
if isDaylightSaving: hour += 1
#hours
solTime = self.__calculateSolarTime(hour, eqOfTime, isSolarTime)
#degrees
hourAngle = (solTime*15 + 180) if (solTime*15 < 0) else (solTime*15 - 180)
#RADIANS
zenith = math.acos(math.sin(self.__latitude)*math.sin(solDec) \
+ math.cos(self.__latitude)*math.cos(solDec)*math.cos(math.radians(hourAngle)))
altitude = (math.pi/2) - zenith
if hourAngle == 0.0 or hourAngle == -180.0 or hourAngle == 180.0:
if solDec < self.__latitude: azimuth = math.pi
else: azimuth = 0.0
else:
azimuth = ((math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith))) + math.pi) % (2*math.pi)) \
if (hourAngle > 0) else \
((3*math.pi - math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
- math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith)))) % (2*math.pi))
# create the sun for this hour
return LBSun(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, self.northAngle)
def calculateSunFromHOY(
self, HOY, isSolarTime=False)
Get Sun data for an hour of the year
Args: datetime: Ladybug datetime isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns: A sun object for this particular time
def calculateSunFromHOY(self, HOY, isSolarTime = False):
"""Get Sun data for an hour of the year
Args:
datetime: Ladybug datetime
isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
Returns:
A sun object for this particular time
"""
datetime = core.LBDateTime.fromHOY(HOY)
return self.calculateSunFromDataTime(datetime, isSolarTime)
def calculateSunriseSunset(
self, month, day, depression=0.833, isSolarTime=False)
def calculateSunriseSunset(self, month, day, depression = 0.833, isSolarTime = False):
datetime = core.LBDateTime(month, day, hour = 12)
return self.calculateSunriseSunsetFromDateTime(datetime, depression, isSolarTime)
def calculateSunriseSunsetFromDateTime(
self, datetime, depression=0.833, isSolarTime=False)
Calculate sunrise, sunset and noon for a day of year
def calculateSunriseSunsetFromDateTime(self, datetime, depression = 0.833, isSolarTime = False):
"""Calculate sunrise, sunset and noon for a day of year"""
solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
# calculate sunrise and sunset hour
#if isSolarTime:
# noon = .5
#else:
noon = (720 - 4 * math.degrees(self.__longitude) - eqOfTime + self.timeZone * 60) / 1440
sunRiseHourAngle = self.__calculateSunriseHourAngle(solDec, depression)
sunrise = noon - sunRiseHourAngle * 4 / 1440
sunset = noon + sunRiseHourAngle * 4 / 1440
# convert demical hour to solar hour
# noon = self.__calculateSolarTime(24*noon, eqOfTime, isSolarTime)
# sunrise = self.__calculateSolarTime(24*sunrise, eqOfTime, isSolarTime)
# sunset = self.__calculateSolarTime(24*sunset, eqOfTime, isSolarTime)
return {
"sunrise" : core.LBDateTime(datetime.month, datetime.day, 24 * sunrise),
"noon" : core.LBDateTime(datetime.month, datetime.day, 24 * noon),
"sunset" : core.LBDateTime(datetime.month, datetime.day, 24 * sunset)}
def fromLocation(
cls, location, northAngle=0, daylightSavingPeriod=None)
@classmethod
def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None):
return cls(location.latitude, northAngle, location.longitude, \
location.timeZone, daylightSavingPeriod)
def isDaylightSavingHour(
self, datetime)
def isDaylightSavingHour(self, datetime):
if not self.daylightSavingPeriod: return False
return self.daylightSavingPeriod.isTimeIncluded(datetime.HOY)