Ejemplo n.º 1
0
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()
    }
Ejemplo n.º 2
0
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"))
Ejemplo n.º 3
0
 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,
     ))
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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,
        ),
    )
Ejemplo n.º 6
0
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}
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
 def _add():
     """Add using values from the calling scope."""
     scen.add_par(name, make_df(name, **common, technology=tec,
                                value=value))
Ejemplo n.º 14
0
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