diff --git a/examples/pumped_hydro_simple/README.md b/examples/pumped_hydro_simple/README.md new file mode 100644 index 000000000..318cc24a0 --- /dev/null +++ b/examples/pumped_hydro_simple/README.md @@ -0,0 +1,7 @@ +SYNOPSIS: + + switch solve --verbose --log-run + +This example illustrates modeling pumped hydro storage by using the hydro_simple module in concert with the storage module. + +This adds pumped hydro to the hydro_simple example, and illustrates pumped hydro being used for arbitrage within each example day. diff --git a/examples/pumped_hydro_simple/inputs/financials.csv b/examples/pumped_hydro_simple/inputs/financials.csv new file mode 100644 index 000000000..c162f5372 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/financials.csv @@ -0,0 +1,2 @@ +base_financial_year,interest_rate,discount_rate +2015,0.07,0.05 diff --git a/examples/pumped_hydro_simple/inputs/fuel_cost.csv b/examples/pumped_hydro_simple/inputs/fuel_cost.csv new file mode 100644 index 000000000..7783a8744 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/fuel_cost.csv @@ -0,0 +1,2 @@ +load_zone,fuel,period,fuel_cost +South,NaturalGas,2020,4 diff --git a/examples/pumped_hydro_simple/inputs/fuels.csv b/examples/pumped_hydro_simple/inputs/fuels.csv new file mode 100644 index 000000000..54dfca062 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/fuels.csv @@ -0,0 +1,2 @@ +fuel,co2_intensity,upstream_co2_intensity +NaturalGas,0.05306,0 diff --git a/examples/pumped_hydro_simple/inputs/gen_build_costs.csv b/examples/pumped_hydro_simple/inputs/gen_build_costs.csv new file mode 100644 index 000000000..52c360d46 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/gen_build_costs.csv @@ -0,0 +1,10 @@ +GENERATION_PROJECT,build_year,gen_overnight_cost,gen_fixed_om,gen_storage_energy_overnight_cost +S-NG_CC,2000.0,1143900.0,5868.3,. +S-Central_PV-1,2000.0,2334300.0,41850.0,. +S-Geothermal,1998.0,5524200.0,0.0,. +Hydro,2000.0,10000000.0,100000.0,. +Hydro_RoR,2000.0,1000000.0,100000.0,. +Hydro_Pumped,2000.0,1000000.0,100000.0,0.0 +S-Geothermal,2020.0,5524200.0,0.0,. +S-NG_CC,2020.0,1143900.0,5868.3,. +S-Central_PV-1,2020.0,2334300.0,41850.0,. diff --git a/examples/pumped_hydro_simple/inputs/gen_build_predetermined.csv b/examples/pumped_hydro_simple/inputs/gen_build_predetermined.csv new file mode 100644 index 000000000..3dfba0315 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/gen_build_predetermined.csv @@ -0,0 +1,7 @@ +GENERATION_PROJECT,build_year,gen_predetermined_cap +S-NG_CC,2000,5.0 +S-Central_PV-1,2000,1.0 +S-Geothermal,1998,1.0 +Hydro,2000,1.0 +Hydro_RoR,2000,1.0 +Hydro_Pumped,2000,5.0 diff --git a/examples/pumped_hydro_simple/inputs/generation_projects_info.csv b/examples/pumped_hydro_simple/inputs/generation_projects_info.csv new file mode 100644 index 000000000..08f59f315 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/generation_projects_info.csv @@ -0,0 +1,7 @@ +GENERATION_PROJECT,gen_dbid,gen_tech,gen_load_zone,gen_connect_cost_per_mw,gen_capacity_limit_mw,gen_variable_om,gen_max_age,gen_min_build_capacity,gen_scheduled_outage_rate,gen_forced_outage_rate,gen_is_variable,gen_is_baseload,gen_is_cogen,gen_energy_source,gen_full_load_heat_rate,gen_is_pumped_hydro,gen_storage_efficiency +S-Geothermal,33.0,Geothermal,South,134222.0,10.0,28.83,30,0,0.0075,0.0241,0,1,0,Geothermal,.,.,. +S-NG_CC,34.0,NG_CC,South,57566.6,.,3.4131,20,0,0.04,0.06,0,0,0,NaturalGas,6.705,.,. +S-Central_PV-1,41.0,Central_PV,South,74881.9,2.0,0.0,20,0,0.0,0.02,1,0,0,Solar,.,.,. +Hydro,.,Hydro,South,0.0,1.0,0.1,100,0,0.019,0.05,0,0,0,Water,.,.,. +Hydro_RoR,.,Hydro_RoR,South,0.0,1.0,0.0,30,0,0.019,0.05,1,0,0,Water,.,.,. +Hydro_Pumped,.,Hydro_Pumped,South,0.0,5.0,0.1,100,0,0.019,0.05,0,0,0,Water,.,1.0,0.75 diff --git a/examples/pumped_hydro_simple/inputs/hydro_timeseries.csv b/examples/pumped_hydro_simple/inputs/hydro_timeseries.csv new file mode 100644 index 000000000..235674f49 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/hydro_timeseries.csv @@ -0,0 +1,5 @@ +hydro_project,timeseries,hydro_min_flow_mw,hydro_avg_flow_mw +Hydro,2020_winter,0.6,0.75 +Hydro,2020_summer,0.2,0.6 +Hydro_Pumped,2020_winter,0.6,0.75 +Hydro_Pumped,2020_summer,0.2,0.6 diff --git a/examples/pumped_hydro_simple/inputs/load_zones.csv b/examples/pumped_hydro_simple/inputs/load_zones.csv new file mode 100644 index 000000000..9810665f2 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/load_zones.csv @@ -0,0 +1,2 @@ +LOAD_ZONE,cost_multipliers,ccs_distance_km,dbid +South,1,0,3 diff --git a/examples/pumped_hydro_simple/inputs/loads.csv b/examples/pumped_hydro_simple/inputs/loads.csv new file mode 100644 index 000000000..ede325070 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/loads.csv @@ -0,0 +1,5 @@ +LOAD_ZONE,TIMEPOINT,zone_demand_mw +South,1,9.0 +South,2,2.5 +South,3,10.0 +South,4,4.0 diff --git a/examples/pumped_hydro_simple/inputs/modules.txt b/examples/pumped_hydro_simple/inputs/modules.txt new file mode 100644 index 000000000..b2530fc71 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/modules.txt @@ -0,0 +1,14 @@ +# Core Modules +switch_model +switch_model.timescales +switch_model.financials +switch_model.balancing.load_zones +switch_model.energy_sources.properties +switch_model.generators.core.build +switch_model.generators.core.dispatch +switch_model.reporting +# Custom Modules +switch_model.generators.core.no_commit +switch_model.energy_sources.fuel_costs.simple +switch_model.generators.extensions.hydro_simple +switch_model.generators.extensions.storage diff --git a/examples/pumped_hydro_simple/inputs/non_fuel_energy_sources.csv b/examples/pumped_hydro_simple/inputs/non_fuel_energy_sources.csv new file mode 100644 index 000000000..512d1b81e --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/non_fuel_energy_sources.csv @@ -0,0 +1,4 @@ +energy_source +Solar +Geothermal +Water diff --git a/examples/pumped_hydro_simple/inputs/periods.csv b/examples/pumped_hydro_simple/inputs/periods.csv new file mode 100644 index 000000000..27c58e07f --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/periods.csv @@ -0,0 +1,2 @@ +INVESTMENT_PERIOD,period_start,period_end +2020,2017,2026 diff --git a/examples/pumped_hydro_simple/inputs/switch_inputs_version.txt b/examples/pumped_hydro_simple/inputs/switch_inputs_version.txt new file mode 100644 index 000000000..e01025862 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/switch_inputs_version.txt @@ -0,0 +1 @@ +2.0.5 diff --git a/examples/pumped_hydro_simple/inputs/timepoints.csv b/examples/pumped_hydro_simple/inputs/timepoints.csv new file mode 100644 index 000000000..bcbd3da19 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/timepoints.csv @@ -0,0 +1,5 @@ +timepoint_id,timestamp,timeseries +1,2025011512,2020_winter +2,2025011600,2020_winter +3,2025071512,2020_summer +4,2025071600,2020_summer diff --git a/examples/pumped_hydro_simple/inputs/timeseries.csv b/examples/pumped_hydro_simple/inputs/timeseries.csv new file mode 100644 index 000000000..d6638f534 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/timeseries.csv @@ -0,0 +1,3 @@ +TIMESERIES,ts_period,ts_duration_of_tp,ts_num_tps,ts_scale_to_period +2020_winter,2020,12,2,1826 +2020_summer,2020,12,2,1826 diff --git a/examples/pumped_hydro_simple/inputs/variable_capacity_factors.csv b/examples/pumped_hydro_simple/inputs/variable_capacity_factors.csv new file mode 100644 index 000000000..3b55edf84 --- /dev/null +++ b/examples/pumped_hydro_simple/inputs/variable_capacity_factors.csv @@ -0,0 +1,9 @@ +GENERATION_PROJECT,timepoint,gen_max_capacity_factor +S-Central_PV-1,1,0.61 +S-Central_PV-1,2,0.0 +S-Central_PV-1,3,0.81 +S-Central_PV-1,4,0.0 +Hydro_RoR,1,0.25 +Hydro_RoR,2,0.5 +Hydro_RoR,3,0.2 +Hydro_RoR,4,0.4 diff --git a/examples/pumped_hydro_simple/outputs/total_cost.txt b/examples/pumped_hydro_simple/outputs/total_cost.txt new file mode 100644 index 000000000..00b93007f --- /dev/null +++ b/examples/pumped_hydro_simple/outputs/total_cost.txt @@ -0,0 +1 @@ +30423392.46 diff --git a/switch_model/generators/extensions/hydro_simple.py b/switch_model/generators/extensions/hydro_simple.py index f5260483b..123d00cf7 100644 --- a/switch_model/generators/extensions/hydro_simple.py +++ b/switch_model/generators/extensions/hydro_simple.py @@ -22,11 +22,23 @@ but the advanced framework would take longer to read and understand. To really take advantage of it, you'll also need more data than we usually have available. + +This module can model pumped hydro systems if the storage module is listed +after simple_hydro in modules.txt, and pumped hydro generation projects are +flagged via gen_is_pumped_hydro. The current implementation of pumped_hydro +implicitly assumes that the lower reservoir always has sufficient water in it +for pumping uphill into storage. Existing resevoir energy capacity can be +constrained via gen_predetermined_storage_energy_mwh as needed. If existing +reservoir energy capacity is never a binding constraint for day-to-day +operations of pumped hydro, leave gen_predetermined_storage_energy_mwh +unspecified, and set gen_storage_energy_overnight_cost to 0 and the +optimization will set the energy value to a conveniently large value. + """ # ToDo: Refactor this code to move the core components into a # switch_model.hydro.core module, the simplest components into # switch_model.hydro.simple, and the advanced components into -# switch_model.hydro.water_network. That should set a good example +# switch_model.hydro.water_network. That could set a good example # for other people who want to do other custom handling of hydro. from __future__ import division @@ -51,9 +63,19 @@ def define_components(mod): GENERATION_PROJECTS, and is determined by the inputs file hydro_timeseries.csv. + gen_is_pumped_hydro[g in GENERATION_PROJECTS] is an optional parameter + that denotes whether a hydro project includes pumped storage. To use this + pumped hydro implementation, you must include the storage module after + hydro_simple in modules.txt. The storage module will look for storage + generators flagged as pumped hydro and will define custom constraints for + their dispatch & storage that takes into account stream flow. + HYDRO_GEN_TS is the set of Hydro projects and timeseries for which minimum and average flow are specified. + HYDRO_NONPUMPED_GEN_TS is a subset of HYDRO_GEN_TS for hydro projects that + do not include pumped storage, and is used to establish flow constraints. + HYDRO_GEN_TPS is the set of Hydro projects and available dispatch points. This is a filtered version of GEN_TPS that only includes hydro projects. @@ -76,7 +98,11 @@ def define_components(mod): Enforce_Hydro_Avg_Flow[(g, ts) in HYDRO_NONPUMPED_GEN_TS] is a constraint that enforces average flow levels across each timeseries. It requires the average of dispatched and spilled hydro over the course of a timeseries - must equal to the corresponding hydro_avg_flow_mw parameter. + must equal to the corresponding hydro_avg_flow_mw parameter. The + corresponding constraint for pumped hydro is defined in the storage + module (after storage decision variables are available), via an augmented + version of the Track_State_Of_Charge constraint that adds average incoming + streamflow and subtracts any spilled power. """ mod.HYDRO_GEN_TS_RAW = Set( @@ -87,13 +113,23 @@ def define_components(mod): ) mod.HYDRO_GENS = Set( initialize=lambda m: set(g for (g, ts) in m.HYDRO_GEN_TS_RAW), - doc="Dispatchable hydro projects") + doc="Dispatchable hydro projects (both pumped & non-pumped)") + mod.gen_is_pumped_hydro = Param( + mod.GENERATION_PROJECTS, + within=Boolean, + default=False, + validate=lambda m, value, g: (value == False) or (g in m.HYDRO_GENS)) + mod.HYDRO_GEN_TS = Set( dimen=2, initialize=lambda m: set( (g, m.tp_ts[tp]) for g in m.HYDRO_GENS for tp in m.TPS_FOR_GEN[g])) + mod.HYDRO_NONPUMPED_GEN_TS = Set( + dimen=2, + initialize=mod.HYDRO_GEN_TS, + filter=lambda m, g, ts: m.gen_is_pumped_hydro[g] == False) mod.HYDRO_GEN_TPS = Set( initialize=mod.GEN_TPS, filter=lambda m, g, t: g in m.HYDRO_GENS) @@ -125,8 +161,8 @@ def _warn_on_extra_HYDRO_GEN_TS(m): "could indicate a benign issue where the process that built " "the dataset used simplified logic and/or didn't know the " "scheduled operating dates. If you expect those datapoints to " - "be useful, then those plants need to either come online earlier " - ", have longer lifetimes, or have options to build new capacity " + "be useful, then those plants need to either come online earlier, " + "have longer lifetimes, or have options to build new capacity " "when the old capacity reaches the provided end-of-life date." "\n".format(num_impacted_generators)) if extra_indexes: @@ -153,7 +189,7 @@ def _warn_on_extra_HYDRO_GEN_TS(m): mod.HYDRO_GEN_TPS, within=NonNegativeReals) mod.Enforce_Hydro_Avg_Flow = Constraint( - mod.HYDRO_GEN_TS, + mod.HYDRO_NONPUMPED_GEN_TS, rule=lambda m, g, ts: ( sum(m.DispatchGen[g, t] + m.SpillHydro[g,t] for t in m.TPS_IN_TS[ts] @@ -161,6 +197,18 @@ def _warn_on_extra_HYDRO_GEN_TS(m): mod.min_data_check('hydro_min_flow_mw', 'hydro_avg_flow_mw') + def storage_module_avail_for_pumped_hydro_check(m): + no_pumped_hydro = all( + value(m.gen_is_pumped_hydro[g]) == False + for g in m.GENERATION_PROJECTS) + has_storage = ( + 'switch_model.generators.extensions.storage' in m.module_list) + return (no_pumped_hydro or has_storage) + mod.storage_module_avail_for_pumped_hydro = BuildCheck( + rule=storage_module_avail_for_pumped_hydro_check, + doc="Ensure that the user has included the storage module if they are" + "attempting to model pumped hydro.") + def load_inputs(mod, switch_data, inputs_dir): """ @@ -177,6 +225,15 @@ def load_inputs(mod, switch_data, inputs_dir): hydro_generation_project, timeseries, hydro_min_flow_mw, hydro_avg_flow_mw + To model pumped hydro projects that use both river flows and pumped + storage, include the storage module in modules.txt after the hydro_simple + module. You will also need to populate the gen_is_pumped_hydro & + gen_storage_efficiency columns of generation_projects_info.tab for the + pumped hydro projects. + + generation_projects_info.csv + GENERATION_PROJECT, ..., gen_is_pumped_hydro + """ switch_data.load_aug( optional=True, @@ -185,3 +242,8 @@ def load_inputs(mod, switch_data, inputs_dir): index=mod.HYDRO_GEN_TS_RAW, param=(mod.hydro_min_flow_mw, mod.hydro_avg_flow_mw) ) + switch_data.load_aug( + filename=os.path.join(inputs_dir, 'generation_projects_info.csv'), + auto_select=True, + optional_params=['gen_is_pumped_hydro'], + param=(mod.gen_is_pumped_hydro,)) diff --git a/switch_model/generators/extensions/storage.py b/switch_model/generators/extensions/storage.py index e3e4fc191..a3ba85450 100644 --- a/switch_model/generators/extensions/storage.py +++ b/switch_model/generators/extensions/storage.py @@ -5,15 +5,28 @@ This module defines storage technologies. It builds on top of generic generators, adding components for deciding how much energy to build into storage, when to charge, energy accounting, etc. + +To do: + +* Add optional ability to exogenously constrain StateOfCharge based on +input files. See notes under the Track_State_Of_Charge documentation below. +* Update the PumpedHydro features to work with the hydro_system module, +and use full mass balance accounting. """ from pyomo.environ import * import os, collections from switch_model.financials import capital_recovery_factor as crf -dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\ - 'switch_model.financials', 'switch_model.energy_sources.properties', \ - 'switch_model.generators.core.build', 'switch_model.generators.core.dispatch' +dependencies = ( + 'switch_model.timescales', + 'switch_model.balancing.load_zones', + 'switch_model.financials', + 'switch_model.energy_sources.properties', + 'switch_model.generators.core.build', + 'switch_model.generators.core.dispatch' +) +optional_prerequisites = ('switch_model.generators.extensions.hydro_simple',) def define_components(mod): """ @@ -35,11 +48,11 @@ def define_components(mod): for extended time perios, then those behaviors will need to be modeled in more detail. - gen_store_to_release_ratio[STORAGE_GENS] describes the maximum rate - that energy can be stored, expressed as a ratio of discharge power - capacity. This is an optional parameter and will default to 1. If a - storage project has 1 MW of dischage capacity and a gen_store_to_release_ratio - of 1.2, then it can consume up to 1.2 MW of power while charging. + gen_store_to_release_ratio[STORAGE_GENS] describes the maximum rate that + energy can be stored, expressed as a ratio of discharge power capacity. + This is an optional parameter and will default to 1. If a storage project + has 1 MW of dischage capacity and a gen_store_to_release_ratio of 1.2, + then it can consume up to 1.2 MW of power while charging. gen_storage_energy_to_power_ratio[STORAGE_GENS], if specified, restricts the storage capacity (in MWh) to be a fixed multiple of the output @@ -66,37 +79,60 @@ def define_components(mod): decision variable. This is analogous to gen_predetermined_cap, but in units of energy of storage capacity (MWh) rather than power (MW). - BuildStorageEnergy[(g, bld_yr) in STORAGE_GEN_BLD_YRS] - is a decision of how much energy capacity to build onto a storage - project. This is analogous to BuildGen, but for energy rather than power. + BuildStorageEnergy[(g, bld_yr) in STORAGE_GEN_BLD_YRS] is a decision of + how much energy capacity to build onto a storage project in units of MWh. + This is analogous to BuildGen, but for energy rather than power. - StorageEnergyInstallCosts[PERIODS] is an expression of the - annual costs incurred by the BuildStorageEnergy decision. + StorageEnergyInstallCosts[PERIODS] is an expression of the annual costs + incurred by the BuildStorageEnergy decision, in units of $/MWh. StorageEnergyCapacity[g, period] is an expression describing the - cumulative available energy capacity of BuildStorageEnergy. This is - analogous to GenCapacity. + cumulative available energy capacity of BuildStorageEnergy in units of + MWh. This is analogous to GenCapacity. STORAGE_GEN_TPS is the subset of GEN_TPS, restricted to storage projects. - ChargeStorage[(g, t) in STORAGE_GEN_TPS] is a dispatch - decision of how much to charge a storage project in each timepoint. + ChargeStorage[(g, t) in STORAGE_GEN_TPS] is a dispatch decision of how + much to charge a storage project in each timepoint in units of MW. StorageNetCharge[LOAD_ZONE, TIMEPOINT] is an expression describing the - aggregate impact of ChargeStorage in each load zone and timepoint. + aggregate impact of ChargeStorage in each load zone and timepoint in units + of MW. Charge_Storage_Upper_Limit[(g, t) in STORAGE_GEN_TPS] constrains ChargeStorage to available power capacity (accounting for gen_store_to_release_ratio) - StateOfCharge[(g, t) in STORAGE_GEN_TPS] is a variable - for tracking state of charge. This value stores the state of charge at - the end of each timepoint for each storage project. - - Track_State_Of_Charge[(g, t) in STORAGE_GEN_TPS] constrains - StateOfCharge based on the StateOfCharge in the previous timepoint, - ChargeStorage and DispatchGen. + StateOfCharge[(g, t) in STORAGE_GEN_TPS] is a variable for tracking state + of charge, in units of MWh. This value stores the state of charge at the + end of each timepoint for each storage project. + + Track_State_Of_Charge[(g, t) in STORAGE_GEN_TPS] constrains StateOfCharge + based on the StateOfCharge in the previous timepoint, ChargeStorage and + DispatchGen. If pumped hydro projects are available (defined in the + optional prerequisite simple_hydro module), this constraint will also + include average stream inflow and spilled water for pumped hydro projects. + + With the current circular implementation of each timeseries, + StateOfCharge[first] = StateOfCharge[last] + net_energy. This effectively + allows each timeseries to begin and end with any given charge level (0 to + 100% of energy capacity), and the optimization process will choose a value + that is convenient. + + For some applications, you may wish to constrain the StateOfCharge values + to pre-determined values, which would require writing a new module that + reads data and applies a constraint to StateOfCharge at the beginning of + each day - either as a fixed amount of energy (MWh) or as a fraction of + energy storage capacity (%). Or alternately, updating this file to include + that behavior as an optional feature. + + Other applications may require linking consecutive timeseries so that the + StateOfCharge carries over from one timeseries to the next. That would + require writing a new version of the timescales module and redefining + tp_previous to use sequential indexing rather than consecutive. Note, that + strategy is not required for 8760 hourly/annual production cost models; + those problems can define a single timeseries of an entire year. State_Of_Charge_Upper_Limit[(g, t) in STORAGE_GEN_TPS] constrains StateOfCharge based on installed energy capacity. @@ -214,10 +250,23 @@ def Charge_Storage_Upper_Limit_rule(m, g, t): within=NonNegativeReals) def Track_State_Of_Charge_rule(m, g, t): - return m.StateOfCharge[g, t] == \ - m.StateOfCharge[g, m.tp_previous[t]] + \ + new_energy = ( + m.StateOfCharge[g, m.tp_previous[t]] + (m.ChargeStorage[g, t] * m.gen_storage_efficiency[g] - m.DispatchGen[g, t]) * m.tp_duration_hrs[t] + ) + # Try to update the energy balance for pumped hydro from the + # hydro_simple module, but skip it if hydro terms haven't been + # defined. + try: + if m.gen_is_pumped_hydro[g]: + ts = m.tp_ts[t] + new_energy += ( + m.hydro_avg_flow_mw[g, ts] - m.SpillHydro[g, t] + ) * m.tp_duration_hrs[t] + except AttributeError: + pass + return m.StateOfCharge[g, t] == new_energy mod.Track_State_Of_Charge = Constraint( mod.STORAGE_GEN_TPS, rule=Track_State_Of_Charge_rule) @@ -257,25 +306,40 @@ def load_inputs(mod, switch_data, inputs_dir): GENERATION_PROJECT, build_year, ... gen_storage_energy_overnight_cost - gen_build_predetermined.tab + gen_build_predetermined.csv GENERATION_PROJECT, build_year, ..., gen_predetermined_storage_energy_mwh* """ - # TODO: maybe move these columns to a storage_gen_info file to avoid the weird index - # reading and avoid having to create these extra columns for all projects; - # Alternatively, say that these values are specified for _all_ projects (maybe with None - # as default) and then define STORAGE_GENS as the subset of projects for which - # gen_storage_efficiency has been specified, then require valid settings for all - # STORAGE_GENS. + # TODO: consider moving these columns to a storage_gen_info file to avoid + # avoid having to create these extra columns for all projects, and to + # allow unambiguous marking of storage projects without looking for + # non-empty gen_storage_efficiency columns. This is similar to how + # simple_hydro specifies input files. Pro: less empty values for + # non-storage projects. Con: another input file to keep track of. + # Alternatively, say that these values are specified for _all_ projects + # (maybe with None as default) and then define STORAGE_GENS as the subset + # of projects for which gen_storage_efficiency has been specified, then + # require valid settings for all STORAGE_GENS. + # Note: The current implementation is similar to the 2nd option above, but + # requires checking `g in mod.gen_storage_efficiency` rather than checking + # `mod.gen_storage_efficiency[g] is not None`. switch_data.load_aug( filename=os.path.join(inputs_dir, 'generation_projects_info.csv'), auto_select=True, - optional_params=['gen_store_to_release_ratio', 'gen_storage_energy_to_power_ratio', 'gen_storage_max_cycles_per_year'], - param=(mod.gen_storage_efficiency, mod.gen_store_to_release_ratio, mod.gen_storage_energy_to_power_ratio, mod.gen_storage_max_cycles_per_year)) + optional_params=[ + 'gen_store_to_release_ratio', + 'gen_storage_energy_to_power_ratio', + 'gen_storage_max_cycles_per_year'], + param=( + mod.gen_storage_efficiency, + mod.gen_store_to_release_ratio, + mod.gen_storage_energy_to_power_ratio, + mod.gen_storage_max_cycles_per_year)) # Base the set of storage projects on storage efficiency being specified. - # TODO: define this in a more normal way + # TODO: define this in a more normal way, possibly with a binary flag + # gen_is_storage. switch_data.data()['STORAGE_GENS'] = { None: list(switch_data.data(name='gen_storage_efficiency').keys())} switch_data.load_aug( @@ -284,7 +348,7 @@ def load_inputs(mod, switch_data, inputs_dir): param=(mod.gen_storage_energy_overnight_cost)) switch_data.load_aug( optional=True, - filename=os.path.join(inputs_dir, 'gen_build_predetermined.tab'), + filename=os.path.join(inputs_dir, 'gen_build_predetermined.csv'), auto_select=True, param=(mod.gen_predetermined_storage_energy_mwh))