import json from pathlib import Path from hybrid.sites import SiteInfo, flatirons_site from hybrid.hybrid_simulation import HybridSimulation from hybrid.log import hybrid_logger as logger from hybrid.keys import set_nrel_key_dot_env examples_dir = Path(__file__).parent.absolute() # Set API key set_nrel_key_dot_env() # Set wind, solar, and interconnection capacities (in MW) solar_size_mw = 50 wind_size_mw = 50 interconnection_size_mw = 50 technologies = {'pv': { 'system_capacity_kw': solar_size_mw * 1000 }, 'wind': { 'num_turbines': 10, 'turbine_rating_kw': 2000 }} # Get resource lat = flatirons_site['lat'] lon = flatirons_site['lon'] prices_file = examples_dir.parent / "resource_files" / "grid" / "pricing-data-2015-IronMtn-002_factors.csv" site = SiteInfo(flatirons_site, grid_resource_file=prices_file)
def __init__(self, data, solar_resource_file="", wind_resource_file="", grid_resource_file="", hub_height=97, capacity_hours=[], desired_schedule=[]): """ Site specific information required by the hybrid simulation class and layout optimization. :param data: dict, containing the following keys: #. ``lat``: float, latitude [decimal degrees] #. ``lon``: float, longitude [decimal degrees] #. ``year``: int, year used to pull solar and/or wind resource data. If not provided, default is 2012 [-] #. ``elev``: float (optional), elevation (metadata purposes only) [m] #. ``tz``: int (optional), timezone code (metadata purposes only) [-] #. ``no_solar``: bool (optional), if ``True`` solar data download for site is skipped, otherwise solar resource is downloaded from NSRDB #. ``no_wind``: bool (optional), if ``True`` wind data download for site is skipped, otherwise wind resource is downloaded from wind-toolkit #. ``site_boundaries``: dict (optional), with the following keys: * ``verts``: list of list [x,y], site boundary vertices [m] * ``verts_simple``: list of list [x,y], simple site boundary vertices [m] #. ``urdb_label``: string (optional), `Link Utility Rate DataBase <https://openei.org/wiki/Utility_Rate_Database>`_ label for REopt runs .. TODO: Can we get rid of verts_simple and simplify site_boundaries :param solar_resource_file: string, location (path) and filename of solar resource file (if not downloading from NSRDB) :param wind_resource_file: string, location (path) and filename of wind resource file (if not downloading from wind-toolkit) :param grid_resource_file: string, location (path) and filename of grid pricing data :param hub_height: int (default = 97), turbine hub height for resource download [m] :param capacity_hours: list of booleans, (8760 length) ``True`` if the hour counts for capacity payments, ``False`` otherwise :param desired_schedule: list of floats, (8760 length) absolute desired load profile [MWe] """ set_nrel_key_dot_env() self.data = data if 'site_boundaries' in data: self.vertices = np.array([np.array(v) for v in data['site_boundaries']['verts']]) self.polygon: Polygon = Polygon(self.vertices) self.valid_region = self.polygon.buffer(1e-8) if 'lat' not in data or 'lon' not in data: raise ValueError("SiteInfo requires lat and lon") self.lat = data['lat'] self.lon = data['lon'] if 'year' not in data: data['year'] = 2012 if 'no_solar' not in data: data['no_solar'] = False if not data['no_solar']: self.solar_resource = SolarResource(data['lat'], data['lon'], data['year'], filepath=solar_resource_file) if 'no_wind' not in data: data['no_wind'] = False if not data['no_wind']: # TODO: allow hub height to be used as an optimization variable self.wind_resource = WindResource(data['lat'], data['lon'], data['year'], wind_turbine_hub_ht=hub_height, filepath=wind_resource_file) self.elec_prices = ElectricityPrices(data['lat'], data['lon'], data['year'], filepath=grid_resource_file) self.n_timesteps = len(self.solar_resource.data['gh']) // 8760 * 8760 self.n_periods_per_day = self.n_timesteps // 365 # TODO: Does not handle leap years well self.interval = int((60*24)/self.n_periods_per_day) self.urdb_label = data['urdb_label'] if 'urdb_label' in data.keys() else None if len(capacity_hours) == self.n_timesteps: self.capacity_hours = capacity_hours else: self.capacity_hours = [False] * self.n_timesteps # Desired load schedule for the system to dispatch against self.desired_schedule = desired_schedule self.follow_desired_schedule = len(desired_schedule) == self.n_timesteps if len(desired_schedule) > 0 and len(desired_schedule) != self.n_timesteps: raise ValueError('The provided desired schedule does not match length of the simulation horizon.') # FIXME: this a hack if 'no_wind' in data: logger.info("Set up SiteInfo with solar resource files: {}".format(self.solar_resource.filename)) else: logger.info( "Set up SiteInfo with solar and wind resource files: {}, {}".format(self.solar_resource.filename, self.wind_resource.filename))