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
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_model.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'] 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']: tech_result.essentials.carrier_in = 'resource' 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': tech_result.essentials.carrier_out = 'resource' 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