def make_matched_dfs(base, **par_value): """Return data frames derived from *base* for multiple parameters. *par_values* maps from parameter names (e.g. 'fix_cost') to values. make_matched_dfs returns a :class:`dict` of :class:`pandas.DataFrame`, one for each parameter in *par_value*. The contents of *base* are used to populate the columns of each data frame, and the values of *par_value* overwrite the 'value' column. Duplicates—which occur when the target parameter has fewer dimensions than *base*—are dropped. Examples -------- >>> input = make_df('input', ...) >>> cf_tl = make_matched_dfs( >>> input, >>> capacity_factor=1, >>> technical_lifetime=1, >>> ) """ data = {col: v for col, v in base.iteritems() if col != "value"} return { par: message_ix.make_df( par, **data, value=value).drop_duplicates().reset_index(drop=True) for par, value in par_value.items() }
def test_make_df(): # DataFrame prepared for the message_ix parameter 'input' has the correct # shape result = make_df("input") assert result.shape == (1, 12) # …and column name(s) assert result.columns[0] == "node_loc" npt.assert_array_equal(result.columns[-2:], ("value", "unit")) # Check correct behaviour when adding key-worded args: defaults = dict(mode="all", time="year", time_origin="year", time_dest="year") result = make_df("output", **defaults) pdt.assert_series_equal(result["mode"], pd.Series("all", name="mode")) pdt.assert_series_equal(result["time"], pd.Series("year", name="time")) pdt.assert_series_equal(result["time_dest"], pd.Series("year", name="time_dest"))
def add_data_func(scenario, dry_run): return dict(demand=make_df( "demand", commodity="cases", level="consumption", node="chicago", time="year", unit="case", value=301.0, year=1963, ))
def test_make_df_deprecated(): # Importing from the old location generates a warning with pytest.warns(DeprecationWarning, match="from 'message_ix.utils' instead of"): from message_ix.utils import make_df as make_df_unused # noqa: F401 base = {"foo": "bar"} exp = pd.DataFrame({"foo": "bar", "baz": [42, 43]}) # Deprecated signature generates a warning with pytest.warns(DeprecationWarning, match="with a mapping or pandas object"): obs = make_df(base, baz=[42, 43]) pdt.assert_frame_equal(obs, exp) with pytest.raises(ValueError): make_df(42, baz=[42, 42]) # Equivalent base.update(baz=[42, 43]) pdt.assert_frame_equal(pd.DataFrame.from_dict(base), exp)
def make_io(src, dest, efficiency, on="input", **kwargs): """Return input and output data frames for a 1-to-1 technology. Parameters ---------- src : tuple (str, str, str) Input commodity, level, unit. dest : tuple (str, str, str) Output commodity, level, unit. efficiency : float Conversion efficiency. on : 'input' or 'output' If 'input', `efficiency` applies to the input, and the output, thus the activity level of the technology, is in dest[2] units. If 'output', the opposite. kwargs Passed to :func:`make_df`. Returns ------- dict (str -> pd.DataFrame) Keys are 'input' and 'output'; values are data frames. """ return dict( input=message_ix.make_df( "input", commodity=src[0], level=src[1], unit=src[2], value=efficiency if on == "input" else 1.0, **kwargs, ), output=message_ix.make_df( "output", commodity=dest[0], level=dest[1], unit=dest[2], value=1.0 if on == "input" else efficiency, **kwargs, ), )
def input(): """Fixture: test data for :func:`.adapt_R11_R14`.""" R11_all = get_codes("node/R11") R11_reg = R11_all[R11_all.index("World")].child df = make_df(PAR, technology="coal_ppl", year_vtg=[2021, 2022], value=[1.2, 3.4], unit="year").pipe(broadcast, node_loc=R11_reg) # Set a specific value for the regions to be broadcast df["value"] = df["value"].where(df["node_loc"] != "R11_FSU", VALUE) return {PAR: df}
def make_source_tech(info, common, **values) -> Dict[str, pd.DataFrame]: """Return parameter data for a ‘source’ technology. The technology has no inputs; its output commodity and/or level are determined by `common`; either single values, or :obj:`None` if the result will be :meth:`~DataFrame.pipe`'d through :func:`broadcast`. Parameters ---------- info : ScenarioInfo common : dict Passed to :func:`make_df`. **values Values for 'capacity_factor' (optional; default 1.0), 'output', 'var_cost', and optionally 'technical_lifetime'. Returns ------- dict Suitable for :func:`add_par_data`. """ # Check arguments values.setdefault("capacity_factor", 1.0) missing = {"capacity_factor", "output", "var_cost"} - set(values.keys()) if len(missing): raise ValueError( f"make_source_tech() needs values for {repr(missing)}") elif "technical_lifetime" not in values: log.debug("No technical_lifetime for source technology") # Create data for "output" result = dict(output=message_ix.make_df( "output", value=values.pop("output"), year_act=info.Y, year_vtg=info.Y, **common, ).pipe(broadcast, node_loc=info.N[1:]).pipe(same_node)) # Add data for other parameters result.update(make_matched_dfs(base=result["output"], **values)) return result
def test_ffill(): years = list(range(6)) df = (make_df( "fix_cost", year_act=[0, 2, 4], year_vtg=[0, 2, 4], technology=["foo", "bar", "baz"], unit="USD", ).pipe(broadcast, node_loc=["A", "B", "C"]).assign(value=list(map(float, range(9))))) # Function completes result = ffill(df, "year_vtg", years, "year_act = year_vtg") assert 2 * len(df) == len(result) assert years == sorted(result["year_vtg"].unique()) # Cannot ffill on "value" and "unit" dimensions with pytest.raises(ValueError, match="value"): ffill(df, "value", [])
def minimal_test_data(scenario): """Generate data for :func:`test_minimal`.""" common = COMMON.copy() common.pop("node_loc") common.update(dict(mode="all")) data = dict() info = ScenarioInfo(scenario) y0 = info.Y[0] y1 = info.Y[1] # Output from t0 and t1 for t in ("t0", "t1"): common.update(dict(technology=t, commodity=f"output of {t}")) merge_data(data, make_source_tech(info, common, output=1.0, var_cost=1.0)) # Disutility input for each combination of (tech) × (group) × (2 years) input_data = pd.DataFrame( [ ["usage of t0 by g0", y0, 0.1], ["usage of t0 by g0", y1, 0.1], ["usage of t1 by g0", y0, 0.1], ["usage of t1 by g0", y1, 0.1], ["usage of t0 by g1", y0, 0.1], ["usage of t0 by g1", y1, 0.1], ["usage of t1 by g1", y0, 0.1], ["usage of t1 by g1", y1, 0.1], ], columns=["technology", "year_vtg", "value"], ) data["input"] = make_df("input", **input_data, commodity="disutility", **COMMON).assign( node_origin=copy_column("node_loc"), year_act=copy_column("year_vtg")) # Demand c, y = zip( *product(["demand of group g0", "demand of group g1"], [y0, y1])) data["demand"] = make_df("demand", commodity=c, year=y, value=1.0, **COMMON) # Constraint on activity in the first period t = sorted(input_data["technology"].unique()) for bound in ("lo", "up"): par = f"bound_activity_{bound}" data[par] = make_df(par, value=0.5, technology=t, year_act=y0, **COMMON) # Constraint on activity growth annual = (1.1**(1.0 / 5.0)) - 1.0 for bound, factor in (("lo", -1.0), ("up", 1.0)): par = f"growth_activity_{bound}" data[par] = make_df(par, value=factor * annual, technology=t, year_act=y1, **COMMON) return data, y0, y1
def make_austria(mp, solve=False, quiet=True): """Return an :class:`message_ix.Scenario` for the Austrian energy system. This is the same model used in the ``austria.ipynb`` tutorial. Parameters ---------- mp : ixmp.Platform Platform on which to create the scenario. solve : bool, optional If True, the scenario is solved. """ mp.add_unit("USD/kW") mp.add_unit("MtCO2") mp.add_unit("tCO2/kWa") scen = Scenario( mp, version="new", **SCENARIO["austria"], annotation= "A stylized energy system model for illustration and testing", ) # Structure year = dict(all=list(range(2010, 2041, 10))) scen.add_horizon(year=year["all"]) year_df = scen.vintage_and_active_years() year["vtg"] = year_df["year_vtg"] year["act"] = year_df["year_act"] country = "Austria" scen.add_spatial_sets({"country": country}) sets = dict( commodity=["electricity", "light", "other_electricity"], emission=["CO2"], level=["secondary", "final", "useful"], mode=["standard"], ) sets["technology"] = AUSTRIA_TECH.index.to_list() plants = sets["technology"][:7] lights = sets["technology"][10:] for name, values in sets.items(): scen.add_set(name, values) scen.add_cat("emission", "GHGs", "CO2") # Parameters name = "interestrate" scen.add_par(name, make_df(name, year=year["all"], value=0.05, unit="-")) common = dict( mode="standard", node_dest=country, node_loc=country, node_origin=country, node=country, time_dest="year", time_origin="year", time="year", year_act=year["act"], year_vtg=year["vtg"], year=year["all"], ) gdp_profile = np.array([1.0, 1.21631, 1.4108, 1.63746]) beta = 0.7 demand_profile = gdp_profile**beta # From IEA statistics, in GW·h, converted to GW·a base_annual_demand = dict(other_electricity=55209.0 / 8760, light=6134.0 / 8760) name = "demand" common.update(level="useful", unit="GWa") for c, base in base_annual_demand.items(): scen.add_par( name, make_df(name, **common, commodity=c, value=base * demand_profile)) common.pop("level") # input, output common.update(unit="-") for name, (tec, info) in product(("input", "output"), AUSTRIA_TECH.iterrows()): value = info[f"{name}_value"] if np.isnan(value): continue scen.add_par( name, make_df( name, **common, technology=tec, commodity=info[f"{name}_commodity"], level=info[f"{name}_level"], value=value, ), ) data = AUSTRIA_PAR # Convert GW·h to GW·a data["activity"] = data["activity"] / 8760.0 # Convert USD / MW·h to USD / GW·a data["var_cost"] = data["var_cost"] * 8760.0 / 1e3 # Convert t / MW·h to t / kw·a data["emission_factor"] = data["emission_factor"] * 8760.0 / 1e3 def _add(): """Add using values from the calling scope.""" scen.add_par(name, make_df(name, **common, technology=tec, value=value)) name = "capacity_factor" for tec, value in data[name].dropna().items(): _add() name = "technical_lifetime" common.update(year_vtg=year["all"], unit="y") for tec, value in data[name].dropna().items(): _add() name = "growth_activity_up" common.update(year_act=year["all"][1:], unit="%") value = 0.05 for tec in plants + lights: _add() name = "initial_activity_up" common.update(year_act=year["all"][1:], unit="%") value = 0.01 * base_annual_demand["light"] * demand_profile[1:] for tec in lights: _add() # bound_activity_lo, bound_activity_up common.update(year_act=year["all"][0], unit="GWa") for (tec, value), kind in product(data["activity"].dropna().items(), ("up", "lo")): name = f"bound_activity_{kind}" _add() name = "bound_activity_up" common.update(year_act=year["all"][1:]) for tec in ("bio_ppl", "hydro_ppl", "import"): value = data.loc[tec, "activity"] _add() name = "bound_new_capacity_up" common.update(year_vtg=year["all"][0], unit="GW") for tec, value in (data["activity"] / data["capacity_factor"]).dropna().items(): _add() name = "inv_cost" common.update(dict(year_vtg=year["all"], unit="USD/kW")) for tec, value in data[name].dropna().items(): _add() # fix_cost, var_cost common.update( dict(year_vtg=year["vtg"], year_act=year["act"], unit="USD/kWa")) for name in ("fix_cost", "var_cost"): for tec, value in data[name].dropna().items(): _add() name = "emission_factor" common.update(year_vtg=year["vtg"], year_act=year["act"], unit="tCO2/kWa", emission="CO2") for tec, value in data[name].dropna().items(): _add() scen.commit("Initial commit for Austria model") scen.set_as_default() if solve: scen.solve(quiet=quiet) return scen
def make_westeros(mp, emissions=False, solve=False, quiet=True): """Return an :class:`message_ix.Scenario` for the Westeros model. This is the same model used in the ``westeros_baseline.ipynb`` tutorial. Parameters ---------- mp : ixmp.Platform Platform on which to create the scenario. emissions : bool, optional If True, the ``emissions_factor`` parameter is also populated for CO2. solve : bool, optional If True, the scenario is solved. """ mp.add_unit("USD/kW") mp.add_unit("tCO2/kWa") scen = Scenario(mp, version="new", **SCENARIO["westeros"]) # Sets history = [690] model_horizon = [700, 710, 720] scen.add_horizon(year=history + model_horizon, firstmodelyear=model_horizon[0]) year_df = scen.vintage_and_active_years() vintage_years, act_years = year_df["year_vtg"], year_df["year_act"] country = "Westeros" scen.add_spatial_sets({"country": country}) for name, values in ( ("technology", ["coal_ppl", "wind_ppl", "grid", "bulb"]), ("mode", ["standard"]), ("level", ["secondary", "final", "useful"]), ("commodity", ["electricity", "light"]), ): scen.add_set(name, values) # Parameters — copy & paste from the tutorial notebook common = dict( mode="standard", node_dest=country, node_loc=country, node_origin=country, node=country, time_dest="year", time_origin="year", time="year", year_act=act_years, year_vtg=vintage_years, year=model_horizon, ) gdp_profile = np.array([1.0, 1.5, 1.9]) demand_per_year = 40 * 12 * 1000 / 8760 scen.add_par( "demand", make_df( "demand", **common, commodity="light", level="useful", # FIXME should use demand_per_year; requires adjustments elsewhere. value=(100 * gdp_profile).round(), unit="GWa", ), ) grid_efficiency = 0.9 common.update(unit="-") for name, tec, c, l, value in [ ("input", "bulb", "electricity", "final", 1.0), ("output", "bulb", "light", "useful", 1.0), ("input", "grid", "electricity", "secondary", 1.0), ("output", "grid", "electricity", "final", grid_efficiency), ("output", "coal_ppl", "electricity", "secondary", 1.0), ("output", "wind_ppl", "electricity", "secondary", 1.0), ]: scen.add_par( name, make_df(name, **common, technology=tec, commodity=c, level=l, value=value), ) # FIXME the value for wind_ppl should be 0.36; requires adjusting other tests. name = "capacity_factor" capacity_factor = dict(coal_ppl=1.0, wind_ppl=1.0, bulb=1.0) for tec, value in capacity_factor.items(): scen.add_par(name, make_df(name, **common, technology=tec, value=value)) name = "technical_lifetime" common.update(year_vtg=model_horizon, unit="y") for tec, value in dict(coal_ppl=20, wind_ppl=20, bulb=1).items(): scen.add_par(name, make_df(name, **common, technology=tec, value=value)) name = "growth_activity_up" common.update(year_act=model_horizon, unit="-") for tec in "coal_ppl", "wind_ppl": scen.add_par(name, make_df(name, **common, technology=tec, value=0.1)) historic_demand = 0.85 * demand_per_year historic_generation = historic_demand / grid_efficiency coal_fraction = 0.6 common.update(year_act=history, year_vtg=history, unit="GWa") for tec, value in ( ("coal_ppl", coal_fraction * historic_generation), ("wind_ppl", (1 - coal_fraction) * historic_generation), ): name = "historical_activity" scen.add_par(name, make_df(name, **common, technology=tec, value=value)) # 20 year lifetime name = "historical_new_capacity" scen.add_par( name, make_df( name, **common, technology=tec, value=value / (2 * 10 * capacity_factor[tec]), ), ) name = "interestrate" scen.add_par(name, make_df(name, year=model_horizon, value=0.05, unit="-")) for name, tec, value in [ ("inv_cost", "coal_ppl", 500), ("inv_cost", "wind_ppl", 1500), ("inv_cost", "bulb", 5), ("fix_cost", "coal_ppl", 30), ("fix_cost", "wind_ppl", 10), ("var_cost", "coal_ppl", 30), ("var_cost", "grid", 50), ]: common.update( dict(year_vtg=model_horizon, unit="USD/kW") if name == "inv_cost" else dict( year_vtg=vintage_years, year_act=act_years, unit="USD/kWa")) scen.add_par(name, make_df(name, **common, technology=tec, value=value)) scen.commit("basic model of Westerosi electrification") scen.set_as_default() if emissions: scen.check_out() # Introduce the emission species CO2 and the emission category GHG scen.add_set("emission", "CO2") scen.add_cat("emission", "GHG", "CO2") # we now add CO2 emissions to the coal powerplant name = "emission_factor" common.update(year_vtg=vintage_years, year_act=act_years, unit="tCO2/kWa") scen.add_par( name, make_df(name, **common, technology="coal_ppl", emission="CO2", value=100.0), ) scen.commit("Added emissions sets/params to Westeros model.") if solve: scen.solve(quiet=quiet) return scen
def make_dantzig(mp, solve=False, multi_year=False, **solve_opts): """Return an :class:`message_ix.Scenario` for Dantzig's canning problem. Parameters ---------- mp : ixmp.Platform Platform on which to create the scenario. solve : bool, optional If True, the scenario is solved. multi_year : bool, optional If True, the scenario has years 1963--1965 inclusive. Otherwise, the scenario has the single year 1963. """ # add custom units and region for timeseries data mp.add_unit("USD/case") mp.add_unit("case") mp.add_region("DantzigLand", "country") # initialize a new (empty) instance of an `ixmp.Scenario` scen = Scenario( mp, model=SCENARIO["dantzig"]["model"], scenario="multi-year" if multi_year else "standard", annotation="Dantzig's canning problem as a MESSAGE-scheme Scenario", version="new", ) # Sets # NB commit() is refused if technology and year are not given t = ["canning_plant", "transport_from_seattle", "transport_from_san-diego"] sets = { "technology": t, "node": "seattle san-diego new-york chicago topeka".split(), "mode": "production to_new-york to_chicago to_topeka".split(), "level": "supply consumption".split(), "commodity": ["cases"], } for name, values in sets.items(): scen.add_set(name, values) scen.add_horizon(year=[1962, 1963], firstmodelyear=1963) # Parameters par = {} # Common values common = dict( commodity="cases", year=1963, year_vtg=1963, year_act=1963, time="year", time_dest="year", time_origin="year", ) par["demand"] = make_df( "demand", **common, node=["new-york", "chicago", "topeka"], level="consumption", value=[325, 300, 275], unit="case", ) par["bound_activity_up"] = make_df( "bound_activity_up", **common, node_loc=["seattle", "san-diego"], mode="production", technology="canning_plant", value=[350, 600], unit="case", ) par["ref_activity"] = par["bound_activity_up"].copy() input = pd.DataFrame( [ ["to_new-york", "seattle", "seattle", t[1]], ["to_chicago", "seattle", "seattle", t[1]], ["to_topeka", "seattle", "seattle", t[1]], ["to_new-york", "san-diego", "san-diego", t[2]], ["to_chicago", "san-diego", "san-diego", t[2]], ["to_topeka", "san-diego", "san-diego", t[2]], ], columns=["mode", "node_loc", "node_origin", "technology"], ) par["input"] = make_df( "input", **input, **common, level="supply", value=1, unit="case", ) output = pd.DataFrame( [ ["supply", "production", "seattle", "seattle", t[0]], ["supply", "production", "san-diego", "san-diego", t[0]], ["consumption", "to_new-york", "new-york", "seattle", t[1]], ["consumption", "to_chicago", "chicago", "seattle", t[1]], ["consumption", "to_topeka", "topeka", "seattle", t[1]], ["consumption", "to_new-york", "new-york", "san-diego", t[2]], ["consumption", "to_chicago", "chicago", "san-diego", t[2]], ["consumption", "to_topeka", "topeka", "san-diego", t[2]], ], columns=["level", "mode", "node_dest", "node_loc", "technology"], ) par["output"] = make_df("output", **output, **common, value=1, unit="case") # Variable cost: cost per kilometre × distance (neither parametrized # explicitly) var_cost = pd.DataFrame( [ ["to_new-york", "seattle", "transport_from_seattle", 0.225], ["to_chicago", "seattle", "transport_from_seattle", 0.153], ["to_topeka", "seattle", "transport_from_seattle", 0.162], ["to_new-york", "san-diego", "transport_from_san-diego", 0.225], ["to_chicago", "san-diego", "transport_from_san-diego", 0.162], ["to_topeka", "san-diego", "transport_from_san-diego", 0.126], ], columns=["mode", "node_loc", "technology", "value"], ) par["var_cost"] = make_df("var_cost", **var_cost, **common, unit="USD/case") for name, value in par.items(): scen.add_par(name, value) if multi_year: scen.add_set("year", [1964, 1965]) scen.add_par("technical_lifetime", ["seattle", "canning_plant", 1964], 3, "y") if solve: # Always read one equation. Used by test_core.test_year_int. scen.init_equ("COMMODITY_BALANCE_GT", ["node", "commodity", "level", "year", "time"]) solve_opts["equ_list"] = solve_opts.get("equ_list", []) + ["COMMODITY_BALANCE_GT"] scen.commit("Created a MESSAGE-scheme version of the transport problem.") scen.set_as_default() if solve: solve_opts.setdefault("quiet", True) scen.solve(**solve_opts) scen.check_out(timeseries_only=True) scen.add_timeseries(HIST_DF, meta=True) scen.add_timeseries(INP_DF) scen.commit("Import Dantzig's transport problem for testing.") return scen
def _add(): """Add using values from the calling scope.""" scen.add_par(name, make_df(name, **common, technology=tec, value=value))
def make_subannual( request, tec_dict, time_steps, demand, time_relative=[], com_dict={"gas_ppl": { "input": "fuel", "output": "electr" }}, capacity={"gas_ppl": { "inv_cost": 0.1, "technical_lifetime": 5 }}, capacity_factor={}, var_cost={}, ): """Return an :class:`message_ix.Scenario` with subannual time resolution. The scenario contains a simple model with two technologies, and a number of time slices. Parameters ---------- request : The pytest ``request`` fixture. tec_dict : dict A dictionary for a technology and required info for time-related parameters. (e.g., ``tec_dict = {"gas_ppl": {"time_origin": ["summer"], "time": ["summer"], "time_dest": ["summer"]}``) time_steps : list of tuples Information about each time slice, packed in a tuple with four elements, including: time slice name, duration relative to "year", "temporal_lvl", and parent time slice (e.g., ``time_steps = [("summer", 1, "season", "year")]``) demand : dict A dictionary for information of "demand" in each time slice. (e.g., 11demand = {"summer": 2.5}``) time_relative: list of str, optional List of parent "time" slices, for which a relative duration time is maintained. This will be used to specify parameter "duration_time_rel" for these "time"s. com_dict : dict, optional A dictionary for specifying "input" and "output" commodities. (e.g., ``com_dict = {"gas_ppl": {"input": "fuel", "output": "electr"}}``) capacity : dict, optional Data for "inv_cost" and "technical_lifetime" per technology. capacity_factor : dict, optional "capacity_factor" with technology as key and "time"/"value" pairs as value. var_cost : dict, optional "var_cost" with technology as key and "time"/"value" pairs as value. """ # Get the `test_mp` fixture for the requesting test function mp = request.getfixturevalue("test_mp") # Build an empty scenario scen = Scenario(mp, request.node.name, scenario="test", version="new") # Add required sets scen.add_set("node", "node") for c in com_dict.values(): scen.add_set("commodity", [x for x in list(c.values()) if x]) # Fixed values y = 2020 unit = "GWa" scen.add_set("level", "level") scen.add_set("year", y) scen.add_set("type_year", y) scen.add_set("mode", "mode") scen.add_set("technology", list(tec_dict.keys())) # Add "time" and "duration_time" to the model for (h, dur, tmp_lvl, parent) in time_steps: scen.add_set("time", h) scen.add_set("time", parent) scen.add_set("lvl_temporal", tmp_lvl) scen.add_set("map_temporal_hierarchy", [tmp_lvl, h, parent]) scen.add_par("duration_time", [h], dur, "-") scen.add_set("time_relative", time_relative) # Common dimensions for parameter data common = dict( node="node", node_loc="node", mode="mode", level="level", year=y, year_vtg=y, year_act=y, ) # Define demand; unpack (key, value) pairs into individual pd.DataFrame rows df = make_df( "demand", **common, commodity="electr", time=demand.keys(), value=demand.values(), unit=unit, ) scen.add_par("demand", df) # Add "input" and "output" parameters of technologies common.update(value=1.0, unit="-") base_output = make_df("output", **common, node_dest="node") base_input = make_df("input", **common, node_origin="node") for tec, times in tec_dict.items(): c = com_dict[tec] for h1, h2 in zip(times["time"], times.get("time_dest", [])): scen.add_par( "output", base_output.assign(technology=tec, commodity=c["output"], time=h1, time_dest=h2), ) for h1, h2 in zip(times["time"], times.get("time_origin", [])): scen.add_par( "input", base_input.assign(technology=tec, commodity=c["input"], time=h1, time_origin=h2), ) # Add capacity related parameters for year, tec in product([y], capacity.keys()): for parname, val in capacity[tec].items(): scen.add_par(parname, ["node", tec, year], val, "-") common.pop("value") # Add capacity factor and variable cost data, both optional for name, arg in [("capacity_factor", capacity_factor), ("var_cost", var_cost)]: for tec, data in arg.items(): df = make_df(name, **common, technology=tec, time=data.keys(), value=data.values()) scen.add_par(name, df) scen.commit( f"Scenario with subannual time resolution for {request.node.name}") scen.solve() return scen