"""
This module contains the Location class.
"""
# Will Holmgren, University of Arizona, 2014-2016.
import datetime
import pandas as pd
import pytz
from pvlib import solarposition
from pvlib import clearsky
from pvlib import atmosphere
[docs]class Location(object):
"""
Location objects are convenient containers for latitude, longitude,
timezone, and altitude data associated with a particular
geographic location. You can also assign a name to a location object.
Location objects have two timezone attributes:
* ``tz`` is a IANA timezone string.
* ``pytz`` is a pytz timezone object.
Location objects support the print method.
Parameters
----------
latitude : float.
Positive is north of the equator.
Use decimal degrees notation.
longitude : float.
Positive is east of the prime meridian.
Use decimal degrees notation.
tz : str, int, float, or pytz.timezone.
See
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
for a list of valid time zones.
pytz.timezone objects will be converted to strings.
ints and floats must be in hours from UTC.
alitude : float.
Altitude from sea level in meters.
name : None or string.
Sets the name attribute of the Location object.
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
See also
--------
pvsystem.PVSystem
"""
def __init__(self, latitude, longitude, tz='UTC', altitude=0,
name=None, **kwargs):
self.latitude = latitude
self.longitude = longitude
if isinstance(tz, str):
self.tz = tz
self.pytz = pytz.timezone(tz)
elif isinstance(tz, datetime.tzinfo):
self.tz = tz.zone
self.pytz = tz
elif isinstance(tz, (int, float)):
self.tz = tz
self.pytz = pytz.FixedOffset(tz*60)
else:
raise TypeError('Invalid tz specification')
self.altitude = altitude
self.name = name
# needed for tying together Location and PVSystem in LocalizedPVSystem
# if LocalizedPVSystem signature is reversed
# super(Location, self).__init__(**kwargs)
def __str__(self):
return ('{}: latitude={}, longitude={}, tz={}, altitude={}'
.format(self.name, self.latitude, self.longitude,
self.tz, self.altitude))
@classmethod
[docs] def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs):
"""
Create an object based on a metadata
dictionary from tmy2 or tmy3 data readers.
Parameters
----------
tmy_metadata : dict
Returned from tmy.readtmy2 or tmy.readtmy3
tmy_data : None or DataFrame
Optionally attach the TMY data to this object.
Returns
-------
Location object (or the child class of Location that you
called this method from).
"""
# not complete, but hopefully you get the idea.
# might need code to handle the difference between tmy2 and tmy3
# determine if we're dealing with TMY2 or TMY3 data
tmy2 = tmy_metadata.get('City', False)
latitude = tmy_metadata['latitude']
longitude = tmy_metadata['longitude']
if tmy2:
name = tmy_metadata['City']
else:
name = tmy_metadata['Name']
tz = tmy_metadata['TZ']
altitude = tmy_metadata['altitude']
new_object = cls(latitude, longitude, tz=tz, altitude=altitude,
name=name, **kwargs)
# not sure if this should be assigned regardless of input.
if tmy_data is not None:
new_object.tmy_data = tmy_data
return new_object
[docs] def get_solarposition(self, times, pressure=None, temperature=12,
**kwargs):
"""
Uses the :py:func:`solarposition.get_solarposition` function
to calculate the solar zenith, azimuth, etc. at this location.
Parameters
----------
times : DatetimeIndex
pressure : None, float, or array-like
If None, pressure will be calculated using
:py:func:`atmosphere.alt2pres` and ``self.altitude``.
temperature : None, float, or array-like
kwargs passed to :py:func:`solarposition.get_solarposition`
Returns
-------
solar_position : DataFrame
Columns depend on the ``method`` kwarg, but always include
``zenith`` and ``azimuth``.
"""
if pressure is None:
pressure = atmosphere.alt2pres(self.altitude)
return solarposition.get_solarposition(times, latitude=self.latitude,
longitude=self.longitude,
altitude=self.altitude,
pressure=pressure,
temperature=temperature,
**kwargs)
[docs] def get_clearsky(self, times, model='ineichen', **kwargs):
"""
Calculate the clear sky estimates of GHI, DNI, and/or DHI
at this location.
Parameters
----------
times : DatetimeIndex
model : str
The clear sky model to use.
kwargs passed to the relevant function(s).
Returns
-------
clearsky : DataFrame
Column names are: ``ghi, dni, dhi``.
"""
if model == 'ineichen':
cs = clearsky.ineichen(times, latitude=self.latitude,
longitude=self.longitude,
altitude=self.altitude,
**kwargs)
elif model == 'haurwitz':
solpos = self.get_solarposition(times, **kwargs)
cs = clearsky.haurwitz(solpos['apparent_zenith'])
else:
raise ValueError('{} is not a valid clear sky model'
.format(model))
return cs
[docs] def get_airmass(self, times=None, solar_position=None,
model='kastenyoung1989'):
"""
Calculate the relative and absolute airmass.
Automatically chooses zenith or apparant zenith
depending on the selected model.
Parameters
----------
times : None or DatetimeIndex
Only used if solar_position is not provided.
solar_position : None or DataFrame
DataFrame with with columns 'apparent_zenith', 'zenith'.
model : str
Relative airmass model
Returns
-------
airmass : DataFrame
Columns are 'airmass_relative', 'airmass_absolute'
"""
if solar_position is None:
solar_position = self.get_solarposition(times)
if model in atmosphere.APPARENT_ZENITH_MODELS:
zenith = solar_position['apparent_zenith']
elif model in atmosphere.TRUE_ZENITH_MODELS:
zenith = solar_position['zenith']
else:
raise ValueError('{} is not a valid airmass model'.format(model))
airmass_relative = atmosphere.relativeairmass(zenith, model)
pressure = atmosphere.alt2pres(self.altitude)
airmass_absolute = atmosphere.absoluteairmass(airmass_relative,
pressure)
airmass = pd.DataFrame()
airmass['airmass_relative'] = airmass_relative
airmass['airmass_absolute'] = airmass_absolute
return airmass