diff --git a/CHANGELOG.md b/CHANGELOG.md index 559da41..dcf9180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +------------------------------------------------------------------------------- +Commmit 2021.08.26 (Version 0.2.0) +------------------------------------------------------------------------------- +Fixes #25 +Updates the code to work with Pyomo 6.1, from Pyomo 5.6.8, so that the code is compatible with CPLEX as a solver. +The environment.yml file has been updated to require use of Pyomo>=6.0.0 and pyutilib>=6.0.0, so continued use will require updating your environment. + +Specific updates include: + - pyomo elements with "Simple" in their name were renamed to "Scalar" + - For any params with string elements, needed to manually specify `within=Any` when defining the param in order to silence a deprecation warning + - Needed to manually specify `dimen=1` for Sets that are used to index parameters when loading data + - When loading data, you can no longer specify the index as a cross product of multiple sets (e.g. `index=mod.PERIODS*mod.MONTHS`). Instead, they must be specified as a list of Sets (e.g. `index=[mod.PERIODS, mod.MONTHS]`) + +Other cleanup included: + - when loading data, replaced specifying multiple parameters as tuples with specifying them as lists, to speed the data loading process + - Removed `PERIODS_FOR_GEN_BLD_YR`, and `TPS_FOR_GEN_IN_PERIOD` because they are not used in the model + - changed the `gen_capacity_value` to `elcc` for Effective Load Carrying Capacity + + ------------------------------------------------------------------------------- Commmit 2021.08.11 ------------------------------------------------------------------------------- diff --git a/MODEL_RUNS/generic_office_example/model_inputs.xlsx b/MODEL_RUNS/generic_office_example/model_inputs.xlsx index 0e8095f..0615ec8 100644 Binary files a/MODEL_RUNS/generic_office_example/model_inputs.xlsx and b/MODEL_RUNS/generic_office_example/model_inputs.xlsx differ diff --git a/MODEL_RUNS/model_inputs.xlsx b/MODEL_RUNS/model_inputs.xlsx index cab931d..aa1d39b 100644 Binary files a/MODEL_RUNS/model_inputs.xlsx and b/MODEL_RUNS/model_inputs.xlsx differ diff --git a/environment.yml b/environment.yml index 4a03b3c..8e3e23a 100644 --- a/environment.yml +++ b/environment.yml @@ -17,9 +17,9 @@ dependencies: - pandas - pint - plotly - - pyomo<=5.6.8 + - pyomo>=6.0.0 - python>=2.7.12 - pytz - - pyutilib<=5.7.3 + - pyutilib>=6.0.0 - requests - xlrd \ No newline at end of file diff --git a/setup.py b/setup.py index 9e68060..5494798 100644 --- a/setup.py +++ b/setup.py @@ -28,12 +28,12 @@ def read(*rnames): setup( name='switch_model', version=__version__, - maintainer='Switch Authors', - maintainer_email='authors@switch-model.org', + maintainer='Gregory Miller', + maintainer_email='grmiller@ucdavis.edu', url='http://switch-model.org', license='Apache License 2.0', platforms=["any"], - description='Switch Power System Planning Model', + description='Switch 24x7 Planning Model', long_description=read('README.md'), long_description_content_type="text/markdown", classifiers=[ @@ -62,11 +62,11 @@ def read(*rnames): ], python_requires='>=2.7.12', install_requires=[ - 'Pyomo>=4.4.1, <=5.6.8', # We need a version that works with glpk 4.60+ + 'Pyomo', # We need a version that works with glpk 4.60+ 'pint', # needed by Pyomo when we run our tests, but not included 'testfixtures', # used for standard tests 'pandas', # used for input upgrades and testing that functionality - 'pyutilib <=5.7.3', + 'pyutilib', ], extras_require={ # packages used for advanced demand response, progressive hedging diff --git a/switch_model/balancing/demand_response/simple.py b/switch_model/balancing/demand_response/simple.py index e6f5abb..e61b012 100644 --- a/switch_model/balancing/demand_response/simple.py +++ b/switch_model/balancing/demand_response/simple.py @@ -102,10 +102,10 @@ def load_inputs(mod, switch_data, inputs_dir): optional=True, filename=os.path.join(inputs_dir, 'dr_data.csv'), autoselect=True, - param=(mod.dr_shift_down_limit, mod.dr_shift_up_limit)) + param=[mod.dr_shift_down_limit, mod.dr_shift_up_limit]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'days.csv'), select=('timepoint_id','tp_day'), index=mod.TIMEPOINTS, - param=(mod.tp_day)) + param=[mod.tp_day]) diff --git a/switch_model/balancing/load_zones.py b/switch_model/balancing/load_zones.py index 3b9e09c..69f9485 100644 --- a/switch_model/balancing/load_zones.py +++ b/switch_model/balancing/load_zones.py @@ -150,7 +150,7 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'loads.csv'), auto_select=True, index=mod.ZONE_TIMEPOINTS, - param=(mod.zone_demand_mw)) + param=[mod.zone_demand_mw]) def post_solve(instance, outdir): diff --git a/switch_model/balancing/renewable_target.py b/switch_model/balancing/renewable_target.py index d08b798..d5d7c36 100644 --- a/switch_model/balancing/renewable_target.py +++ b/switch_model/balancing/renewable_target.py @@ -156,8 +156,7 @@ def load_inputs(mod, switch_data, inputs_dir): switch_data.load_aug( filename=os.path.join(inputs_dir, 'renewable_target.csv'), autoselect=True, - index=mod.PERIODS, - param=(mod.renewable_target,)) + param=[mod.renewable_target]) #load inputs which include costs for each timepoint in each zone switch_data.load_aug( @@ -171,7 +170,7 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'days.csv'), select=('timepoint_id','tp_in_subset'), index=mod.TIMEPOINTS, - param=(mod.tp_in_subset)) + param=[mod.tp_in_subset]) def post_solve(instance, outdir): """ diff --git a/switch_model/energy_sources/properties.py b/switch_model/energy_sources/properties.py index a0fe823..dbec261 100644 --- a/switch_model/energy_sources/properties.py +++ b/switch_model/energy_sources/properties.py @@ -91,7 +91,7 @@ def define_components(mod): """ mod.NON_FUEL_ENERGY_SOURCES = Set() - mod.FUELS = Set() + mod.FUELS = Set(dimen=1) mod.f_co2_intensity = Param(mod.FUELS, within=NonNegativeReals) mod.f_upstream_co2_intensity = Param( mod.FUELS, within=Reals, default=0) @@ -103,7 +103,7 @@ def define_components(mod): # ENERGY_SOURCES is the union of fuel and non-fuels sets. Pipe | is # the union operator for Pyomo sets. mod.ENERGY_SOURCES = Set( - initialize=mod.NON_FUEL_ENERGY_SOURCES | mod.FUELS) + initialize=mod.NON_FUEL_ENERGY_SOURCES | mod.FUELS, dimen=1) mod.min_data_check('ENERGY_SOURCES') @@ -145,4 +145,4 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'fuels.csv'), select=('fuel', 'co2_intensity', 'upstream_co2_intensity'), index=mod.FUELS, - param=(mod.f_co2_intensity, mod.f_upstream_co2_intensity)) + param=[mod.f_co2_intensity, mod.f_upstream_co2_intensity]) diff --git a/switch_model/explore_model_instance.ipynb b/switch_model/explore_model_instance.ipynb index fe12108..2819e69 100644 --- a/switch_model/explore_model_instance.ipynb +++ b/switch_model/explore_model_instance.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "source": [ "from pathlib import Path\r\n", "import pickle\r\n", @@ -24,7 +24,7 @@ "import pandas as pd\r\n", "\r\n", "# specify where the pickle file is located\r\n", - "model_path = '../MODEL_RUNS/generic_office_example/outputs/hourly_95/'" + "model_path = '../MODEL_RUNS/generic_office_example/outputs/annual_goal/'" ], "outputs": [], "metadata": {} @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "source": [ "# read instance file\r\n", "with open((Path.cwd() / model_path / 'instance.pickle'), mode='rb') as file:\r\n", @@ -47,6 +47,26 @@ "outputs": [], "metadata": {} }, + { + "cell_type": "code", + "execution_count": 13, + "source": [ + "len(instance.GENERATION_PROJECTS)" + ], + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "9" + ] + }, + "metadata": {}, + "execution_count": 13 + } + ], + "metadata": {} + }, { "cell_type": "markdown", "source": [ @@ -228,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "source": [ "# read results file\r\n", "with open((Path.cwd() / model_path / 'results.pickle'), 'rb') as file:\r\n", @@ -236,16 +256,81 @@ "\r\n", "print(results.problem)" ], - "outputs": [], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "- Name: unknown\n", + " Lower bound: 726748.0428\n", + " Upper bound: 726748.0428\n", + " Number of objectives: 1\n", + " Number of constraints: 43803\n", + " Number of variables: 35051\n", + " Number of binary variables: \n", + " Number of integer variables: \n", + " Number of continuous variables: \n", + " Number of nonzeros: 17479\n", + " Sense: minimize\n", + "\n" + ] + } + ], "metadata": {} }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "source": [ "print(results.solver)" ], - "outputs": [], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "- Name: \n", + " Status: ok\n", + " Return code: \n", + " Message: \n", + " User time: -1.0\n", + " System time: 28.21\n", + " Wallclock time: 28.21\n", + " Termination condition: optimal\n", + " Termination message: Model was solved to optimality (subject to tolerances), and an optimal solution is available.\n", + " Statistics: \n", + " Branch and bound: \n", + " Number of bounded subproblems: None\n", + " Number of created subproblems: None\n", + " Black box: \n", + " Number of function evaluations: \n", + " Number of gradient evaluations: \n", + " Number of iterations: 33511\n", + " Error rc: 0\n", + " Time: 28.228245973587036\n", + "\n" + ] + } + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 10, + "source": [ + "print(results.pyomo_solve_time)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "26.032098054885864\n" + ] + } + ], "metadata": {} }, { diff --git a/switch_model/financials.py b/switch_model/financials.py index 8274db7..44e1dda 100644 --- a/switch_model/financials.py +++ b/switch_model/financials.py @@ -314,7 +314,7 @@ def load_inputs(mod, switch_data, inputs_dir): switch_data.load_aug( filename=os.path.join(inputs_dir, 'financials.csv'), optional=False, auto_select=True, - param=(mod.base_financial_year, mod.interest_rate, mod.discount_rate) + param=[mod.base_financial_year, mod.interest_rate, mod.discount_rate] ) def post_solve(instance, outdir): diff --git a/switch_model/generate_input_files.py b/switch_model/generate_input_files.py index 2ff0c84..8644c2b 100644 --- a/switch_model/generate_input_files.py +++ b/switch_model/generate_input_files.py @@ -116,12 +116,12 @@ def generate_inputs(model_workspace): # ra_requirement.csv xl_ra_req = pd.read_excel(io=model_inputs, sheet_name='RA_requirements').dropna(axis=1, how='all') - ra_requirement = xl_ra_req[xl_ra_req['RA_RESOURCE'] != 'flexible_RA'] + ra_requirement = xl_ra_req.copy()[xl_ra_req['RA_RESOURCE'] != 'flexible_RA'] ra_requirement['period'] = year ra_requirement = ra_requirement[['period','RA_RESOURCE','tp_month','ra_requirement','ra_cost','ra_resell_value']] # flexible_ra_requirement.csv - flexible_ra_requirement = xl_ra_req[xl_ra_req['RA_RESOURCE'] == 'flexible_RA'] + flexible_ra_requirement = xl_ra_req.copy()[xl_ra_req['RA_RESOURCE'] == 'flexible_RA'] flexible_ra_requirement['period'] = year flexible_ra_requirement = flexible_ra_requirement.drop(columns=['RA_RESOURCE']) flexible_ra_requirement = flexible_ra_requirement.rename(columns={'ra_requirement':'flexible_ra_requirement','ra_cost':'flexible_ra_cost', 'ra_resell_value':'flexible_ra_resell_value'}) @@ -130,7 +130,7 @@ def generate_inputs(model_workspace): # ra_capacity_value.csv ra_capacity_value = pd.read_excel(io=model_inputs, sheet_name='RA_capacity_value').dropna(axis=1, how='all') ra_capacity_value['period'] = year - ra_capacity_value = ra_capacity_value[['period','gen_energy_source','tp_month','gen_capacity_value']] + ra_capacity_value = ra_capacity_value[['period','gen_energy_source','tp_month','elcc']] # ra_requirement_areas.csv ra_requirement_areas = pd.read_excel(io=model_inputs, sheet_name='RA_areas') diff --git a/switch_model/generators/core/build.py b/switch_model/generators/core/build.py index 5782c2b..f2e06ee 100644 --- a/switch_model/generators/core/build.py +++ b/switch_model/generators/core/build.py @@ -214,14 +214,15 @@ def define_components(mod): This aggregation is performed for the benefit of the objective function. """ - mod.GENERATION_PROJECTS = Set() + mod.GENERATION_PROJECTS = Set(dimen=1) - mod.gen_tech = Param(mod.GENERATION_PROJECTS) + mod.gen_tech = Param(mod.GENERATION_PROJECTS, within=Any) mod.GENERATION_TECHNOLOGIES = Set(initialize=lambda m: - {m.gen_tech[g] for g in m.GENERATION_PROJECTS} + {m.gen_tech[g] for g in m.GENERATION_PROJECTS}, + ordered=False ) mod.gen_energy_source = Param(mod.GENERATION_PROJECTS, - validate=lambda m,val,g: val in m.ENERGY_SOURCES or val == "multiple") + validate=lambda m,val,g: val in m.ENERGY_SOURCES or val == "multiple", within=Any) mod.gen_load_zone = Param(mod.GENERATION_PROJECTS, within=mod.LOAD_ZONES) mod.gen_max_age = Param(mod.GENERATION_PROJECTS, within=PositiveIntegers, default=25) mod.gen_is_variable = Param(mod.GENERATION_PROJECTS, within=Boolean) @@ -322,14 +323,6 @@ def gen_build_can_operate_in_period(m, g, build_year, period): # mid_period = m.period_start[period] + 0.5 * m.period_length_years[period] # return online <= m.period_start[period] and mid_period <= retirement - # The set of periods when a project built in a certain year will be online - mod.PERIODS_FOR_GEN_BLD_YR = Set( - mod.GEN_BLD_YRS, - within=mod.PERIODS, - ordered=True, - initialize=lambda m, g, bld_yr: set( - period for period in m.PERIODS - if gen_build_can_operate_in_period(m, g, bld_yr, period))) # The set of build years that could be online in the given period # for the given project. mod.BLD_YRS_FOR_GEN_PERIOD = Set( @@ -337,7 +330,8 @@ def gen_build_can_operate_in_period(m, g, build_year, period): initialize=lambda m, g, period: set( bld_yr for (gen, bld_yr) in m.GEN_BLD_YRS if gen == g and - gen_build_can_operate_in_period(m, g, bld_yr, period))) + gen_build_can_operate_in_period(m, g, bld_yr, period)), + ordered=False) # The set of periods when a generator is available to run mod.PERIODS_FOR_GEN = Set( mod.GENERATION_PROJECTS, @@ -549,13 +543,13 @@ def load_inputs(mod, switch_data, inputs_dir): 'gen_forced_outage_rate', 'gen_capacity_limit_mw', 'gen_unit_size', 'gen_min_build_capacity', 'gen_is_cogen', 'gen_variant_group'], index=mod.GENERATION_PROJECTS, - param=(mod.gen_tech, mod.gen_energy_source, + param=[mod.gen_tech, mod.gen_energy_source, mod.gen_load_zone, mod.gen_is_variable, mod.gen_is_storage, mod.gen_is_baseload, mod.gen_scheduled_outage_rate, mod.gen_forced_outage_rate, mod.gen_capacity_limit_mw, mod.gen_unit_size, mod.ppa_energy_cost, mod.gen_min_build_capacity, mod.ppa_capacity_cost, mod.gen_is_cogen, - mod.gen_variant_group, mod.gen_pricing_node)) + mod.gen_variant_group, mod.gen_pricing_node]) # Construct sets of capacity-limited, ccs-capable and unit-size-specified # projects. These sets include projects for which these parameters have # a value @@ -575,7 +569,7 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'gen_build_predetermined.csv'), auto_select=True, index=mod.PREDETERMINED_GEN_BLD_YRS, - param=(mod.gen_predetermined_cap)) + param=[mod.gen_predetermined_cap]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'gen_build_years.csv'), set=mod.GEN_BLD_YRS) diff --git a/switch_model/generators/core/dispatch.py b/switch_model/generators/core/dispatch.py index 0f14291..efcc875 100644 --- a/switch_model/generators/core/dispatch.py +++ b/switch_model/generators/core/dispatch.py @@ -118,7 +118,7 @@ def period_active_gen_rule(m, period): if len(m.period_active_gen_dict) == 0: delattr(m, 'period_active_gen_dict') return result - mod.GENS_IN_PERIOD = Set(mod.PERIODS, initialize=period_active_gen_rule, + mod.GENS_IN_PERIOD = Set(mod.PERIODS, initialize=period_active_gen_rule, ordered=False, doc="The set of projects active in a given period.") mod.TPS_FOR_GEN = Set( @@ -129,21 +129,6 @@ def period_active_gen_rule(m, period): ) ) - def init(m, gen, period): - try: - d = m._TPS_FOR_GEN_IN_PERIOD_dict - except AttributeError: - d = m._TPS_FOR_GEN_IN_PERIOD_dict = dict() - for _gen in m.GENERATION_PROJECTS: - for t in m.TPS_FOR_GEN[_gen]: - d.setdefault((_gen, m.tp_period[t]), set()).add(t) - result = d.pop((gen, period), set()) - if not d: # all gone, delete the attribute - del m._TPS_FOR_GEN_IN_PERIOD_dict - return result - mod.TPS_FOR_GEN_IN_PERIOD = Set( - mod.GENERATION_PROJECTS, mod.PERIODS, - within=mod.TIMEPOINTS, initialize=init) mod.GEN_TPS = Set( dimen=2, @@ -179,7 +164,8 @@ def init(m, gen, period): mod.gen_pricing_node = Param( mod.GENERATION_PROJECTS, - validate=lambda m,val,g: val in m.PRICING_NODES) + validate=lambda m,val,g: val in m.PRICING_NODES, + within=Any) mod.nodal_price = Param( mod.NODE_TIMEPOINTS, within=Reals) @@ -275,7 +261,7 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'variable_capacity_factors.csv'), autoselect=True, index=mod.VARIABLE_GEN_TPS_RAW, - param=(mod.gen_max_capacity_factor)) + param=[mod.gen_max_capacity_factor]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'pricing_nodes.csv'), diff --git a/switch_model/generators/extensions/resource_adequacy.py b/switch_model/generators/extensions/resource_adequacy.py index 47bf504..49f26ba 100644 --- a/switch_model/generators/extensions/resource_adequacy.py +++ b/switch_model/generators/extensions/resource_adequacy.py @@ -56,7 +56,7 @@ def define_components(mod): ############# #set of months - mod.MONTHS = Set(ordered=True, initialize=[1,2,3,4,5,6,7,8,9,10,11,12]) + mod.MONTHS = Set(ordered=True, initialize=[1,2,3,4,5,6,7,8,9,10,11,12], dimen=1) #set of RA resource (RAR) types mod.RA_REQUIREMENT_CATEGORIES = Set( @@ -131,7 +131,7 @@ def GENS_IN_AREA_init(m, a): within=NonNegativeReals, default=0) - mod.gen_capacity_value = Param( + mod.elcc = Param( mod.PERIODS, mod.ENERGY_SOURCES, mod.MONTHS, within=NonNegativeReals) @@ -142,7 +142,7 @@ def GENS_IN_AREA_init(m, a): mod.RAValueByArea = Expression ( mod.PERIODS, mod.LOCAL_RELIABILITY_AREAS, mod.MONTHS, rule=lambda m, p, a, mo: sum( - m.GenCapacity[g,p] * m.gen_capacity_value[p, m.gen_energy_source[g], mo] + m.GenCapacity[g,p] * m.elcc[p, m.gen_energy_source[g], mo] for g in m.GENS_IN_AREA[a])) def areas_for_rar(m,r): @@ -257,7 +257,7 @@ def AvailableFlexRACapacity_rule(m,p): def load_inputs(mod, switch_data, inputs_dir): """ reserve_capacity_value.csv - GEN, TIMEPOINT, gen_capacity_value + GEN, TIMEPOINT, elcc planning_reserve_requirement_zones.csv PLANNING_RESERVE_REQUIREMENTS, prr_cap_reserve_margin, prr_enforcement_timescale @@ -275,7 +275,7 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'generation_projects_info.csv'), auto_select=True, index=mod.GENERATION_PROJECTS, - param=(mod.gen_reliability_area)) + param=[mod.gen_reliability_area]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'ra_requirement_categories.csv'), set=mod.RA_REQUIREMENT_CATEGORIES) @@ -287,21 +287,21 @@ def load_inputs(mod, switch_data, inputs_dir): set=mod.RAR_AREAS) switch_data.load_aug( filename=os.path.join(inputs_dir, 'ra_requirement.csv'), - auto_select=True, - index=mod.PERIODS*mod.RA_MONTHS, + select=('period','RA_RESOURCE','tp_month', 'ra_requirement', 'ra_cost', 'ra_resell_value'), + index=[mod.PERIODS, mod.RA_MONTHS], optional_params=['ra_resell_value'], - param=(mod.ra_requirement, mod.ra_cost, mod.ra_resell_value)) + param=[mod.ra_requirement, mod.ra_cost, mod.ra_resell_value]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'flexible_ra_requirement.csv'), - auto_select=True, - index=mod.PERIODS*mod.MONTHS, + select=('period','tp_month', 'flexible_ra_requirement','flexible_ra_cost','flexible_ra_resell_value'), + index=[mod.PERIODS, mod.MONTHS], optional_params=['flexible_ra_resell_value'], - param=(mod.flexible_ra_requirement, mod.flexible_ra_cost, mod.flexible_ra_resell_value)) + param=[mod.flexible_ra_requirement, mod.flexible_ra_cost, mod.flexible_ra_resell_value]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'ra_capacity_value.csv'), - auto_select=True, - index=mod.PERIODS*mod.ENERGY_SOURCES*mod.MONTHS, - param=(mod.gen_capacity_value,)) + select=('period','gen_energy_source', 'tp_month','elcc'), + index=[mod.PERIODS, mod.ENERGY_SOURCES, mod.MONTHS], + param=[mod.elcc]) @@ -361,7 +361,7 @@ def areas_for_rar(instance,r): "Generation_Project": g, "Local_Reliability_Area": value(instance.gen_reliability_area[g]), "RA_Requirement": r, - "RA_Value": value(instance.GenCapacity[g,p] * instance.gen_capacity_value[p, instance.gen_energy_source[g], mo]) + "RA_Value": value(instance.GenCapacity[g,p] * instance.elcc[p, instance.gen_energy_source[g], mo]) if instance.gen_reliability_area[g] in areas_for_rar(instance,r) else 0 } for g in instance.GENERATION_PROJECTS for (r,mo) in instance.RA_MONTHS for p in instance.PERIODS] gen_df = pd.DataFrame(gen_dat) diff --git a/switch_model/generators/extensions/storage.py b/switch_model/generators/extensions/storage.py index e9b5147..b53e54f 100644 --- a/switch_model/generators/extensions/storage.py +++ b/switch_model/generators/extensions/storage.py @@ -151,7 +151,7 @@ def period_active_gen_rule(m, period): if len(m.period_active_gen_dict) == 0: delattr(m, 'period_active_gen_dict') return result - mod.STORAGE_GENS_IN_PERIOD = Set(mod.PERIODS, initialize=period_active_gen_rule, + mod.STORAGE_GENS_IN_PERIOD = Set(mod.PERIODS, initialize=period_active_gen_rule, ordered=False, doc="The set of projects active in a given period.") # DEFINE PARAMETERS @@ -175,7 +175,8 @@ def period_active_gen_rule(m, period): default=float('inf')) mod.storage_hybrid_generation_project = Param( mod.HYBRID_STORAGE_GENS, - validate= lambda m,val,g: val in m.GENERATION_PROJECTS and val not in m.STORAGE_GENS) #validate the paired generator is in the generator list and isnt another storage project + validate= lambda m,val,g: val in m.GENERATION_PROJECTS and val not in m.STORAGE_GENS, #validate the paired generator is in the generator list and isnt another storage project + within=Any) mod.storage_hybrid_capacity_ratio = Param( mod.STORAGE_GENS, within=NonNegativeReals, @@ -372,7 +373,10 @@ def load_inputs(mod, switch_data, inputs_dir): auto_select=True, index=mod.GENERATION_PROJECTS, optional_params=['storage_charge_to_discharge_ratio', 'storage_energy_to_power_ratio', 'storage_max_annual_cycles'], - param=(mod.storage_roundtrip_efficiency, mod.storage_charge_to_discharge_ratio, mod.storage_energy_to_power_ratio, mod.storage_max_annual_cycles, mod.storage_hybrid_generation_project, mod.storage_hybrid_capacity_ratio, mod.storage_leakage_loss)) + param=[mod.storage_roundtrip_efficiency, mod.storage_charge_to_discharge_ratio, + mod.storage_energy_to_power_ratio, mod.storage_max_annual_cycles, + mod.storage_hybrid_generation_project, mod.storage_hybrid_capacity_ratio, + mod.storage_leakage_loss]) diff --git a/switch_model/reporting/__init__.py b/switch_model/reporting/__init__.py index f9017ae..666ba8f 100644 --- a/switch_model/reporting/__init__.py +++ b/switch_model/reporting/__init__.py @@ -144,10 +144,13 @@ def save_generic_results(instance, outdir, sorted_output): writer = csv.writer(fh, dialect='switch-csv') if var.is_indexed(): index_name = var.index_set().name - # Write column headings - writer.writerow(['%s_%d' % (index_name, i + 1) - for i in range(var.index_set().dimen)] + - [var.name]) + # Write column headings + if isinstance(var.index_set().dimen, int): + writer.writerow(['%s_%d' % (index_name, i + 1) + for i in range(var.index_set().dimen)] + + [var.name]) + else: # the Var was not used + pass # Results are saved in a random order by default for # increased speed. Sorting is available if wanted. items = sorted(var.items()) if sorted_output else list(var.items()) diff --git a/switch_model/run_scenarios.ipynb b/switch_model/run_scenarios.ipynb index b11e79b..da00224 100644 --- a/switch_model/run_scenarios.ipynb +++ b/switch_model/run_scenarios.ipynb @@ -10,15 +10,15 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.6" }, "orig_nbformat": 2, "kernelspec": { "name": "python3", - "display_name": "Python 3.8.10 64-bit ('switch_247': conda)" + "display_name": "Python 3.9.6 64-bit ('switch_pyomo_update': conda)" }, "interpreter": { - "hash": "b5cd7b7571e6318f5fd6adc9cee12d73eccc93ada83e657cc8f1c05b47d15a2e" + "hash": "ff6a17ac238744b540d27fccff62995fc5aaf3aa453e839e2267df7f5be6aa37" } }, "nbformat": 4, diff --git a/switch_model/timescales.py b/switch_model/timescales.py index 82a41b9..66e97be 100644 --- a/switch_model/timescales.py +++ b/switch_model/timescales.py @@ -227,12 +227,12 @@ def define_components(mod): """ - mod.PERIODS = Set(ordered=True) + mod.PERIODS = Set(ordered=True, dimen=1) mod.period_start = Param(mod.PERIODS, within=NonNegativeReals) mod.period_end = Param(mod.PERIODS, within=NonNegativeReals) mod.min_data_check('PERIODS', 'period_start', 'period_end') - mod.TIMESERIES = Set(ordered=True) + mod.TIMESERIES = Set(ordered=True, dimen=1) mod.ts_period = Param(mod.TIMESERIES, within=mod.PERIODS) mod.ts_duration_of_tp = Param(mod.TIMESERIES, within=PositiveReals) mod.ts_num_tps = Param(mod.TIMESERIES, within=PositiveIntegers) @@ -241,10 +241,10 @@ def define_components(mod): 'TIMESERIES', 'ts_period', 'ts_duration_of_tp', 'ts_num_tps', 'ts_scale_to_period') - mod.TIMEPOINTS = Set(ordered=True) + mod.TIMEPOINTS = Set(ordered=True, dimen=1) mod.tp_ts = Param(mod.TIMEPOINTS, within=mod.TIMESERIES) mod.min_data_check('TIMEPOINTS', 'tp_ts') - mod.tp_timestamp = Param(mod.TIMEPOINTS, default=lambda m, t: t) + mod.tp_timestamp = Param(mod.TIMEPOINTS, default=lambda m, t: t, within=Any) # Derived sets and parameters # note: the first five are calculated early so they @@ -402,16 +402,15 @@ def load_inputs(mod, switch_data, inputs_dir): filename=os.path.join(inputs_dir, 'periods.csv'), select=('INVESTMENT_PERIOD', 'period_start', 'period_end'), index=mod.PERIODS, - param=(mod.period_start, mod.period_end)) + param=[mod.period_start, mod.period_end]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'timeseries.csv'), - select=('TIMESERIES', 'ts_period', 'ts_duration_of_tp', - 'ts_num_tps', 'ts_scale_to_period'), + autoselect=True, index=mod.TIMESERIES, - param=(mod.ts_period, mod.ts_duration_of_tp, - mod.ts_num_tps, mod.ts_scale_to_period)) + param=[mod.ts_period, mod.ts_duration_of_tp, + mod.ts_num_tps, mod.ts_scale_to_period]) switch_data.load_aug( filename=os.path.join(inputs_dir, 'timepoints.csv'), select=('timepoint_id', 'timestamp', 'timeseries'), index=mod.TIMEPOINTS, - param=(mod.tp_timestamp, mod.tp_ts)) + param=[mod.tp_timestamp, mod.tp_ts]) diff --git a/switch_model/utilities.py b/switch_model/utilities.py index 49a516a..f601c38 100644 --- a/switch_model/utilities.py +++ b/switch_model/utilities.py @@ -197,7 +197,7 @@ def save_inputs_as_dat(model, instance, save_path="inputs/complete_inputs.dat", component = getattr(model, component_name) comp_class = type(component).__name__ component_data = instance.DataPortal.data(name=component_name) - if comp_class == 'SimpleSet' or comp_class == 'OrderedSimpleSet': + if comp_class == 'ScalarSet' or comp_class == 'OrderedScalarSet': f.write( "set {} := {};\n" .format(component_name, join_space(component_data)) @@ -212,7 +212,7 @@ def save_inputs_as_dat(model, instance, save_path="inputs/complete_inputs.dat", ): f.write(" {} {}\n".format(join_space(key), quote_str(value))) f.write(";\n") - elif comp_class == 'SimpleParam': + elif comp_class == 'ScalarParam': f.write("param {} := {};\n".format(component_name, component_data)) elif comp_class == 'IndexedSet': for key, vals in iteritems(component_data): @@ -339,7 +339,7 @@ def check_mandatory_components(model, *mandatory_model_components): for component_name in mandatory_model_components: obj = getattr(model, component_name) o_class = type(obj).__name__ - if o_class == 'SimpleSet' or o_class == 'OrderedSimpleSet': + if o_class == 'ScalarSet' or o_class == 'OrderedScalarSet': if len(obj) == 0: raise ValueError( "No data is defined for the mandatory set '{}'.". @@ -358,7 +358,7 @@ def check_mandatory_components(model, *mandatory_model_components): raise ValueError( ("Sets are not defined for every index of " + "the mandatory indexed set '{}'").format(component_name)) - elif o_class == 'SimpleParam': + elif o_class == 'ScalarParam': if obj.value is None: raise ValueError( "Value not provided for mandatory parameter '{}'". @@ -460,9 +460,14 @@ def load_aug(switch_data, optional=False, auto_select=False, # Grab the dimensionality of the index param if it was provided. if 'index' in kwds: # if no dimensions are defined, default to 1 - if isinstance(kwds['index'].dimen, int): - num_indexes = kwds['index'].dimen + if isinstance(kwds['index'], list): + num_indexes = 0 + for i in kwds['index']: + num_indexes = num_indexes + i.dimen + elif isinstance((kwds['index']).dimen, int): + num_indexes = (kwds['index']).dimen else: + print(f'WARNING: Please manually set the number of dimensions for Set {kwds["index"]}') num_indexes = 1 # Next try the first parameter's index. elif len(params) > 0: diff --git a/switch_model/version.py b/switch_model/version.py index 9acf20d..4c1f3c8 100644 --- a/switch_model/version.py +++ b/switch_model/version.py @@ -9,4 +9,4 @@ NOTE: This software was forked from Switch version 2.0.5 """ -__version__='0.1.0' +__version__='0.2.0' diff --git a/time_coincident.yml b/time_coincident.yml deleted file mode 100644 index 8429250..0000000 --- a/time_coincident.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: time-coincident -channels: - - nrel - - conda-forge - - defaults -dependencies: - - appdirs=1.4.3=py_1 - - argon2-cffi=20.1.0=py37h4ab8f01_1 - - async_generator=1.10=py_0 - - attrs=20.1.0=pyh9f0ad1d_0 - - backcall=0.2.0=py_0 - - bleach=3.1.5=pyh9f0ad1d_0 - - brotlipy=0.7.0=py37h4ab8f01_1000 - - ca-certificates=2020.6.20=hecda079_0 - - certifi=2020.6.20=py37hc8dfbb8_0 - - cffi=1.14.1=py37h26f1ce3_0 - - chardet=3.0.4=py37hc8dfbb8_1006 - - colorama=0.4.3=py_0 - - cryptography=3.0=py37h26f1ce3_0 - - decorator=4.4.2=py_0 - - defusedxml=0.6.0=py_0 - - entrypoints=0.3=py37_0 - - flake8=3.8.3=py_0 - - git=2.23.0=h6bb4b03_0 - - glpk=4.65=h2fa13f4_1002 - - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=1.7.0=py37hc8dfbb8_0 - - importlib_metadata=1.7.0=0 - - intel-openmp=2020.1=216 - - ipykernel=5.3.0=py37h5ca1d4c_0 - - ipython=7.16.1=py37h5ca1d4c_0 - - ipython_genutils=0.2.0=py37_0 - - jedi=0.17.1=py37_0 - - jinja2=2.11.2=pyh9f0ad1d_0 - - jsonschema=3.2.0=py37hc8dfbb8_1 - - jupyter_client=6.1.5=py_0 - - jupyter_core=4.6.3=py37_0 - - jupyterlab_pygments=0.1.1=pyh9f0ad1d_0 - - libblas=3.8.0=16_mkl - - libcblas=3.8.0=16_mkl - - liblapack=3.8.0=16_mkl - - libsodium=1.0.18=h62dcd97_0 - - m2w64-gcc-libgfortran=5.3.0=6 - - m2w64-gcc-libs=5.3.0=7 - - m2w64-gcc-libs-core=5.3.0=7 - - m2w64-gmp=6.1.0=2 - - m2w64-libwinpthread-git=5.0.0.4634.697f757=2 - - markupsafe=1.1.1=py37h8055547_1 - - mccabe=0.6.1=py37_1 - - mistune=0.8.4=py37h8055547_1001 - - mkl=2020.1=216 - - msys2-conda-epoch=20160418=1 - - nbclient=0.5.0=py_0 - - nbconvert=6.0.2=py37hc8dfbb8_0 - - nbformat=5.0.7=py_0 - - nest-asyncio=1.4.0=py_0 - - nose=1.3.7=py37hc8dfbb8_1004 - - notebook=6.1.4=py37hc8dfbb8_0 - - nrel-pysam=2.1.4=py37_0 - - nrel-pysam-stubs=2.1.4=py37_0 - - numpy=1.18.5=py37hae9e721_0 - - openssl=1.1.1g=he774522_1 - - packaging=20.4=pyh9f0ad1d_0 - - pandas=1.0.5=py37h3bbf574_0 - - pandoc=2.10.1=he774522_0 - - pandocfilters=1.4.2=py_1 - - parso=0.7.0=py_0 - - pickleshare=0.7.5=py37_1001 - - pint=0.14=py_0 - - pip=20.1.1=py37_1 - - plotly=4.9.0=pyh9f0ad1d_0 - - ply=3.11=py_1 - - prometheus_client=0.8.0=pyh9f0ad1d_0 - - prompt-toolkit=3.0.5=py_0 - - pycodestyle=2.6.0=py_0 - - pycparser=2.20=pyh9f0ad1d_2 - - pyflakes=2.2.0=py_0 - - pygments=2.6.1=py_0 - - pyomo=5.6.8=py37_0 - - pyopenssl=19.1.0=py_1 - - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.16.0=py37h8055547_0 - - pysocks=1.7.1=py37hc8dfbb8_1 - - python=3.7.7=h81c818b_4 - - python-dateutil=2.8.1=py_0 - - python_abi=3.7=1_cp37m - - pytz=2020.1=pyh9f0ad1d_0 - - pyutilib=5.8.0=pyh9f0ad1d_1 - - pywin32=227=py37he774522_1 - - pywinpty=0.5.7=py37_0 - - pyzmq=19.0.1=py37ha925a31_1 - - requests=2.24.0=pyh9f0ad1d_0 - - retrying=1.3.3=py_2 - - send2trash=1.5.0=py_0 - - setuptools=49.1.0=py37_0 - - six=1.15.0=pyh9f0ad1d_0 - - sqlite=3.32.3=h2a8f88b_0 - - terminado=0.8.3=py37hc8dfbb8_1 - - testfixtures=6.14.1=pyh9f0ad1d_0 - - testpath=0.4.4=py_0 - - tornado=6.0.4=py37he774522_1 - - traitlets=4.3.3=py37_0 - - urllib3=1.25.10=py_0 - - vc=14.1=h0510ff6_4 - - vs2015_runtime=14.16.27012=hf0eaf9b_3 - - wcwidth=0.2.5=py_0 - - webencodings=0.5.1=py_1 - - wheel=0.34.2=py37_0 - - win_inet_pton=1.1.0=py37_0 - - wincertstore=0.2=py37_0 - - winpty=0.4.3=4 - - xlrd=1.2.0=pyh9f0ad1d_1 - - zeromq=4.3.2=ha925a31_2 - - zipp=3.1.0=py_0 - - zlib=1.2.11=h62dcd97_4 -prefix: C:\Users\gmiller\anaconda3\envs\time-coincident -