def __init__(self, site: SiteInfo, interconnect_kw): """ Class that houses the hybrid system performance and financials. Enforces interconnection and curtailment limits based on PySAM's Grid module :param site: Power source site information (SiteInfo object) :param interconnect_kw: Interconnection limit [kW] """ system_model = GridModel.default("GenericSystemSingleOwner") financial_model: Singleowner.Singleowner = Singleowner.from_existing(system_model, "GenericSystemSingleOwner") super().__init__("Grid", site, system_model, financial_model) self._system_model.GridLimits.enable_interconnection_limit = 1 self._system_model.GridLimits.grid_interconnection_limit_kwac = interconnect_kw # financial calculations set up self._financial_model.value("add_om_num_types", 1) self._dispatch: GridDispatch = None # TODO: figure out if this is the best place for these self.missed_load = [0.] self.missed_load_percentage = 0.0 self.schedule_curtailed = [0.] self.schedule_curtailed_percentage = 0.0
def __init__(self, site: SiteInfo, battery_config: dict, chemistry: str = 'lfpgraphite', system_voltage_volts: float = 500): """ Battery Storage class based on PySAM's BatteryStateful Model :param site: Power source site information (SiteInfo object) :param battery_config: Battery configuration with the following keys: #. ``system_capacity_kwh``: float, Battery energy capacity [kWh] #. ``system_capacity_kw``: float, Battery rated power capacity [kW] :param chemistry: Battery storage chemistry, options include: #. ``LFPGraphite``: Lithium Iron Phosphate (Lithium Ion) #. ``LMOLTO``: LMO/Lithium Titanate (Lithium Ion) #. ``LeadAcid``: Lead Acid #. ``NMCGraphite``: Nickel Manganese Cobalt Oxide (Lithium Ion) :param system_voltage_volts: Battery system voltage [VDC] """ for key in ('system_capacity_kwh', 'system_capacity_kw'): if key not in battery_config.keys(): raise ValueError system_model = BatteryModel.default(chemistry) financial_model = Singleowner.from_existing(system_model, "StandaloneBatterySingleOwner") super().__init__("Battery", site, system_model, financial_model) self.Outputs = BatteryOutputs(n_timesteps=site.n_timesteps) self.system_capacity_kw: float = battery_config['system_capacity_kw'] self.chemistry = chemistry BatteryTools.battery_model_sizing(self._system_model, battery_config['system_capacity_kw'], battery_config['system_capacity_kwh'], system_voltage_volts, module_specs=Battery.module_specs) self._system_model.ParamsPack.h = 20 self._system_model.ParamsPack.Cp = 900 self._system_model.ParamsCell.resistance = 0.001 self._system_model.ParamsCell.C_rate = battery_config['system_capacity_kw'] / battery_config['system_capacity_kwh'] # Minimum set of parameters to set to get statefulBattery to work self._system_model.value("control_mode", 0.0) self._system_model.value("input_current", 0.0) self._system_model.value("dt_hr", 1.0) self._system_model.value("minimum_SOC", 10.0) self._system_model.value("maximum_SOC", 90.0) self._system_model.value("initial_SOC", 10.0) self._dispatch = None logger.info("Initialized battery with parameters and state {}".format(self._system_model.export()))
def __init__(self, site: SiteInfo, pv_config: dict, detailed_not_simple: bool = False): """ :param pv_config: dict, with keys ('system_capacity_kw', 'layout_params') where 'layout_params' is of the SolarGridParameters type :param detailed_not_simple: Detailed model uses Pvsamv1, simple uses PVWatts """ if 'system_capacity_kw' not in pv_config.keys(): raise ValueError self._detailed_not_simple: bool = detailed_not_simple if not detailed_not_simple: system_model = Pvwatts.default("PVWattsSingleOwner") financial_model = Singleowner.from_existing(system_model, "PVWattsSingleOwner") else: system_model = Pvsam.default("FlatPlatePVSingleOwner") financial_model = Singleowner.from_existing(system_model, "FlatPlatePVSingleOwner") super().__init__("SolarPlant", site, system_model, financial_model) self._system_model.SolarResource.solar_resource_data = self.site.solar_resource.data self.dc_degradation = [0] params: Optional[PVGridParameters] = None if 'layout_params' in pv_config.keys(): params: PVGridParameters = pv_config['layout_params'] self._layout = PVLayout(site, system_model, params) self._dispatch: PvDispatch = None self.system_capacity_kw: float = pv_config['system_capacity_kw']
Most recently tested against PySAM 2.2.3 @author: frohro """ import json import PySAM.GenericSystem as GenericSystem import PySAM.Grid as Grid import PySAM.Singleowner as Singleowner import PySAM.PySSC as pssc ssc = pssc.PySSC() with open("Examples/100mW_Generic.json") as f: dic = json.load(f) gs_dat = pssc.dict_to_ssc_table(dic, "generic_system") grid_dat = pssc.dict_to_ssc_table(dic, "grid") so_dat = pssc.dict_to_ssc_table(dic, "singleowner") gs = GenericSystem.wrap(gs_dat) grid = Grid.from_existing(gs) grid.assign(Grid.wrap(grid_dat).export()) # to create GenericSystem and Singleowner combined simulation, sharing the same data so = Singleowner.from_existing(gs) so.assign(Singleowner.wrap(so_dat).export()) gs.execute() grid.execute() so.execute() print('Made it past execute.') print(gs.Outputs.export()) # as dictionary
import PySAM.ResourceTools as tools import PySAM.Windpower as wp import PySAM.Singleowner as so # --- Initialize Wind Fetcher --- wtkfetcher = tools.FetchResourceFiles( tech='wind', workers=1, #thread workers if fetching multiple files nrel_api_key=<NREL_API_KEY>, nrel_api_email=<NREL_API_EMAIL>) # --- Pass a list of (lon, lat) tuples or Shapely points to fetch the nearest resource data --- lon_lats = [(-105.1800775, 39.7383155)] # golden CO wtkfetcher.fetch(lon_lats) # --- Get resource data file path --- wtk_path_dict = wtkfetcher.resource_file_paths_dict wtk_fp = wtk_path_dict[lon_lats[0]] # --- Initialize Generator --- generator = wp.default('WindPowerSingleOwner') generator.Resource.assign({'wind_resource_model_choice': 0}) generator.Resource.assign({'wind_resource_filename': wtk_fp}) #pass path to resource file # --- Initialize Financial Model --- financial = so.from_existing(generator, "WindPowerSingleOwner") # --- Execute Models --- generator.execute() financial.execute()
def __init__( self, site: SiteInfo, farm_config: dict, rating_range_kw: tuple = (1000, 3000), ): """ Set up a WindPlant :param farm_config: dict, with keys ('num_turbines', 'turbine_rating_kw', 'rotor_diameter', 'hub_height', 'layout_mode', 'layout_params') where layout_mode can be selected from the following: - 'boundarygrid': regular grid with boundary turbines, requires WindBoundaryGridParameters as 'params' - 'grid': regular grid with dx, dy distance, 0 angle; does not require 'params' :param rating_range_kw: allowable kw range of turbines, default is 1000 - 3000 kW """ self._rating_range_kw = rating_range_kw if 'model_name' in farm_config.keys(): if farm_config['model_name'] == 'floris': print('FLORIS is the system model...') system_model = Floris(farm_config, site, timestep=farm_config['timestep']) financial_model = Singleowner.default("WindPowerSingleOwner") else: raise NotImplementedError else: system_model = Windpower.default("WindPowerSingleOwner") financial_model = Singleowner.from_existing( system_model, "WindPowerSingleOwner") super().__init__("WindPlant", site, system_model, financial_model) self._system_model.value("wind_resource_data", self.site.wind_resource.data) if 'layout_mode' not in farm_config.keys(): layout_mode = 'grid' else: layout_mode = farm_config['layout_mode'] params: Optional[WindBoundaryGridParameters] = None if layout_mode == 'boundarygrid': if 'layout_params' not in farm_config.keys(): raise ValueError( "Parameters of WindBoundaryGridParameters required for boundarygrid layout mode" ) else: params: WindBoundaryGridParameters = farm_config[ 'layout_params'] self._layout = WindLayout(site, system_model, layout_mode, params) self._dispatch: WindDispatch = None if 'turbine_rating_kw' not in farm_config.keys(): raise ValueError("Turbine rating required for WindPlant") if 'num_turbines' not in farm_config.keys(): raise ValueError("Num Turbines required for WindPlant") self.turb_rating = farm_config['turbine_rating_kw'] self.num_turbines = farm_config['num_turbines'] if 'hub_height' in farm_config.keys(): self._system_model.Turbine.wind_turbine_hub_ht = farm_config[ 'hub_height'] if 'rotor_diameter' in farm_config.keys(): self.rotor_diameter = farm_config['rotor_diameter']
# --- List of (lon, lat) tuples or Shapely points --- lon_lats = [(lon, lat)] wtkfetcher.fetch(lon_lats) # --- Get resource data file path --- wtk_path_dict = wtkfetcher.resource_file_paths_dict wtk_fp = wtk_path_dict[lon_lats[0]] # --- Initialize generator --- if wtk_fp is not None: generator = wp.default('WindPowerSingleOwner') generator.Resource.assign({'wind_resource_model_choice': 0}) generator.Resource.assign({'wind_resource_filename': wtk_fp}) # --- Initialize financial model --- financial = so.from_existing(generator, 'WindPowerSingleOwner') print('Wind Power - Single Owner Results') generator.execute() print('capacity factor = {:.3f}'.format(generator.Outputs.capacity_factor)) financial.execute() print('npv = ${:,.2f}'.format( financial.Outputs.project_return_aftertax_npv)) else: print('Wind resource file does not exist. Skipping wind model simulation.') # --- Solar Example --- # --- Initialize Solar Resource Fetcher with minimum parameters --- # See function documentation for full parameter list