Example #1
0
def process_techs(config_model):

    default_palette_cycler = itertools.cycle(range(len(_DEFAULT_PALETTE)))

    result = AttrDict()
    errors = []
    debug_comments = AttrDict()

    for tech_id, tech_config in config_model.techs.items():

        # If a tech specifies ``exists: false``, we skip it entirely
        if not tech_config.get("exists", True):
            continue

        tech_result = AttrDict()

        # Add inheritance chain
        tech_result.inheritance = get_parents(tech_id, config_model)

        # CHECK: A tech's parent must lead to one of the built-in tech_groups
        builtin_tech_groups = checks.DEFAULTS.tech_groups.keys()
        if tech_result.inheritance[-1] not in builtin_tech_groups:
            errors.append(
                "tech {} must inherit from a built-in tech group".format(
                    tech_id))

        # Process inheritance
        tech_result.essentials = AttrDict()
        tech_result.constraints = AttrDict()
        for parent in reversed(tech_result.inheritance):
            # Does the parent group have model-wide settings?
            parent_essentials = config_model.tech_groups[parent].essentials
            parent_systemwide_constraints = util.get_systemwide_constraints(
                config_model.tech_groups[parent])
            for k in parent_essentials.as_dict_flat():
                debug_comments.set_key(
                    "{}.essentials.{}".format(tech_id, k),
                    "From parent tech_group `{}`".format(parent),
                )
            tech_result.essentials.union(parent_essentials,
                                         allow_override=True)
            tech_result.constraints.union(parent_systemwide_constraints,
                                          allow_override=True)

        # Add this tech's essentials and constraints, overwriting any essentials from parents
        tech_result.essentials.union(tech_config.essentials,
                                     allow_override=True)
        tech_result.constraints.union(
            util.get_systemwide_constraints(tech_config), allow_override=True)

        # Add allowed_constraints and required_constraints from base tech
        keys_to_add = [
            "required_constraints",
            "allowed_constraints",
            "allowed_costs",
            "allowed_switches",
        ]
        for k in keys_to_add:
            tech_result[k] = config_model.tech_groups[
                tech_result.inheritance[-1]].get(k, [])

        # CHECK: If necessary, populate carrier_in and carrier_out in essentials, but
        # also break on missing carrier data
        if "carrier_in" not in tech_result.essentials:
            if tech_result.inheritance[-1] in ["supply", "supply_plus"]:
                pass
            elif tech_result.inheritance[-1] in [
                    "demand", "transmission", "storage"
            ]:
                try:
                    tech_result.essentials.carrier_in = tech_result.essentials.carrier
                    debug_comments.set_key(
                        "{}.essentials.carrier_in".format(tech_id),
                        "Set from essentials.carrier",
                    )
                except KeyError:
                    errors.append("`carrier` or `carrier_in` must be "
                                  "defined for {}".format(tech_id))
            else:
                errors.append(
                    "`carrier_in` must be defined for {}".format(tech_id))

        if "carrier_out" not in tech_result.essentials:
            if tech_result.inheritance[-1] == "demand":
                pass
            elif tech_result.inheritance[-1] in [
                    "supply",
                    "supply_plus",
                    "transmission",
                    "storage",
            ]:
                try:
                    tech_result.essentials.carrier_out = tech_result.essentials.carrier
                except KeyError:
                    errors.append("`carrier` or `carrier_out` must be "
                                  "defined for {}".format(tech_id))
            else:
                errors.append(
                    "`carrier_out` must be defined for {}".format(tech_id))
        # Deal with primary carrier in/out for conversion_plus techs
        if tech_result.inheritance[-1] == "conversion_plus":
            for direction in ["_in", "_out"]:
                carriers = set(
                    util.flatten_list([
                        v for k, v in tech_result.essentials.items()
                        if k.startswith("carrier" + direction)
                    ]))
                primary_carrier = tech_result.essentials.get(
                    "primary_carrier" + direction, None)
                if primary_carrier is None and len(carriers) == 1:
                    tech_result.essentials["primary_carrier" +
                                           direction] = carriers.pop()
                elif primary_carrier is None and len(carriers) > 1:
                    errors.append(
                        "Primary_carrier{0} must be assigned for tech `{1}` as "
                        "there are multiple carriers{0}".format(
                            direction, tech_id))
                elif primary_carrier not in carriers:
                    errors.append(
                        "Primary_carrier{0} `{1}` not one of the available carriers"
                        "{0} for `{2}`".format(direction, primary_carrier,
                                               tech_id))

        # If necessary, pick a color for the tech, cycling through
        # the hardcoded default palette
        if not tech_result.essentials.get_key("color", None):
            color = _DEFAULT_PALETTE[next(default_palette_cycler)]
            tech_result.essentials.color = color
            debug_comments.set_key("{}.essentials.color".format(tech_id),
                                   "From Calliope default palette")
        result[tech_id] = tech_result

    return result, debug_comments, errors
Example #2
0
def generate_simple_sets(model_run):
    """
    Generate basic sets for a given pre-processed ``model_run``.

    Parameters
    ----------
    model_run : AttrDict

    """
    sets = AttrDict()

    flat_techs = model_run.techs.as_dict(flat=True)
    flat_locations = model_run.locations.as_dict(flat=True)

    sets.resources = set(
        flatten_list(v for k, v in flat_techs.items() if ".carrier" in k))

    sets.carriers = sets.resources - set(["resource"])

    sets.carrier_tiers = set(
        key.split(".carrier_")[1] for key in flat_techs.keys()
        if ".carrier_" in key)

    sets.costs = set(
        k.split("costs.")[-1].split(".")[0] for k in flat_locations.keys()
        if ".costs." in k)

    sets.locs = set(model_run.locations.keys())

    sets.techs_non_transmission = set()
    tech_groups = [
        "demand",
        "supply",
        "supply_plus",
        "conversion",
        "conversion_plus",
        "storage",
    ]
    for tech_group in tech_groups:
        sets["techs_{}".format(tech_group)] = set(
            k for k, v in model_run.techs.items()
            if v.inheritance[-1] == tech_group)
        sets.techs_non_transmission.update(sets["techs_{}".format(tech_group)])

    sets.techs_transmission_names = set(k for k, v in model_run.techs.items()
                                        if v.inheritance[-1] == "transmission")

    # This builds the "tech:loc" expansion of transmission technologies
    techs_transmission = set()
    for loc_name, loc_config in model_run.locations.items():
        for link_name, link_config in loc_config.get("links", {}).items():
            for tech_name in link_config.techs:
                techs_transmission.add("{}:{}".format(tech_name, link_name))
    sets.techs_transmission = techs_transmission

    sets.techs = sets.techs_non_transmission | sets.techs_transmission_names

    # this extracts location coordinate information
    coordinates = set(
        k.split(".")[-1] for k in flat_locations.keys()
        if ".coordinates." in k)

    if coordinates:
        sets.coordinates = coordinates

    # `timesteps` set is built from the results of timeseries_data processing
    sets.timesteps = list(model_run.timesteps.astype(str))
    model_run.del_key("timesteps")

    # `techlists` are strings with comma-separated techs used for grouping in
    # some model-wide constraints
    sets.techlists = set()
    for k in model_run.model.get_key("group_share", {}).keys():
        sets.techlists.add(k)

    # `constraint_groups` are the group names per constraint that is defined
    # at a group level

    sets.group_constraints = set()
    group_constraints = AttrDict({
        name: data
        for name, data in model_run["group_constraints"].items()
        if data.get("exists", True)
    })
    if len(group_constraints.keys()) > 0:
        sets.group_constraints.update(
            i.split(".")[1] for i in group_constraints.as_dict_flat().keys()
            if i.split(".")[1] not in ["techs", "locs"])
        for constr in sets.group_constraints:
            sets["group_names_" + constr] = set(
                k for k, v in group_constraints.items() if constr in v.keys())

    return sets