Exemple #1
0
def node_parasitics(model):
    """
    Additional variables and constraints for plants with internal parasitics.

    Defines variables:

    * ec_prod: storage -> carrier after parasitics (+ production)
    * ec_con: storage <- carrier after parasitics (- consumption)

    """
    m = model.m

    # Variables
    m.ec_prod = po.Var(m.c, m.y_p, m.x, m.t, within=po.NonNegativeReals)
    m.ec_con = po.Var(m.c, m.y_p, m.x, m.t, within=po.NegativeReals)

    # Constraint rules
    def c_ec_prod_rule(m, c, y, x, t):
        return (m.ec_prod[c, y, x, t] == m.es_prod[c, y, x, t] *
                model.get_option(y + '.constraints.c_eff', x=x))

    def c_ec_con_rule(m, c, y, x, t):
        if y in m.y_trans or y in m.y_conv:
            # Ensure that transmission and conversion technologies
            # do not double count c_eff
            c_eff = 1.0
        else:
            c_eff = model.get_option(y + '.constraints.c_eff', x=x)
        return (m.ec_con[c, y, x, t] == m.es_con[c, y, x, t] / c_eff)

    # Constraints
    m.c_ec_prod = po.Constraint(m.c, m.y_p, m.x, m.t, rule=c_ec_prod_rule)
    m.c_ec_con = po.Constraint(m.c, m.y_p, m.x, m.t, rule=c_ec_con_rule)
Exemple #2
0
def add_buy_sell_price(m):

    # Sets
    m.com_sell = pyomo.Set(within=m.com,
                           initialize=commodity_subset(m.com_tuples, 'Sell'),
                           doc='Commodities that can be sold')
    m.com_buy = pyomo.Set(within=m.com,
                          initialize=commodity_subset(m.com_tuples, 'Buy'),
                          doc='Commodities that can be purchased')

    # Variables
    m.e_co_sell = pyomo.Var(
        m.tm,
        m.com_tuples,
        within=pyomo.NonNegativeReals,
        doc='Use of sell commodity source (MW) per timestep')
    m.e_co_buy = pyomo.Var(m.tm,
                           m.com_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='Use of buy commodity source (MW) per timestep')

    # Rules
    m.res_sell_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_sell_step_rule,
        doc='sell commodity output per step <= commodity.maxperstep')
    m.res_sell_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_sell_total_rule,
        doc='total sell commodity output <= commodity.max')
    m.res_buy_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_buy_step_rule,
        doc='buy commodity output per step <= commodity.maxperstep')
    m.res_buy_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_buy_total_rule,
        doc='total buy commodity output <= commodity.max')

    m.res_sell_buy_symmetry = pyomo.Constraint(
        m.pro_input_tuples,
        rule=res_sell_buy_symmetry_rule,
        doc='power connection capacity must be symmetric in both directions')

    return m
Exemple #3
0
def node_resource(model):
    """
    Defines variables:

    * rs: resource <-> storage (+ production, - consumption)
    * r_area: resource collector area
    * rbs: secondary resource -> storage (+ production)

    """
    m = model.m

    # Variables
    m.rs = po.Var(m.y, m.x, m.t, within=po.Reals)
    m.r_area = po.Var(m.y_def_r, m.x, within=po.NonNegativeReals)
    m.rbs = po.Var(m.y_rb, m.x, m.t, within=po.NonNegativeReals)

    # Constraint rules
    def c_rs_rule(m, y, x, t):
        r_avail = (m.r[y, x, t] *
                   model.get_option(y + '.constraints.r_scale', x=x) *
                   m.r_area[y, x] *
                   model.get_option(y + '.constraints.r_eff', x=x))
        if model.get_option(y + '.constraints.force_r', x=x):
            return m.rs[y, x, t] == r_avail
        # TODO reformulate conditionally once Pyomo supports that:
        # had to remove the following formulation because it is not
        # re-evaluated on model re-construction -- we now check for
        # demand/supply tech instead, which means that `r` can only
        # be ALL negative or ALL positive for a given tech!
        # elif po.value(m.r[y, x, t]) > 0:
        elif (y in model.get_group_members('supply')
              or y in model.get_group_members('unmet_demand')):
            return m.rs[y, x, t] <= r_avail
        elif y in model.get_group_members('demand'):
            return m.rs[y, x, t] >= r_avail

    # Constraints
    m.c_rs = po.Constraint(m.y_def_r, m.x, m.t, rule=c_rs_rule)
Exemple #4
0
def build_variables(backend_model, model_data, variable_definitions):
    for var_name, var_config in variable_definitions.items():
        subset = create_valid_subset(model_data, var_name, var_config)
        if subset is None:
            continue
        if "bounds" in var_config:
            kwargs = {"bounds": get_capacity_bounds(var_config.bounds)}
        else:
            kwargs = {}

        setattr(
            backend_model,
            var_name,
            po.Var(subset, domain=getattr(po, var_config.domain), **kwargs),
        )
Exemple #5
0
def generate_variables(model):
    """
    Defines variables:

    * r: resource -> tech (+ production
    * r_area: resource collector area
    * r2: secondary resource -> storage (+ production)
    * c_prod: tech -> carrier (+ production)
    * c_con: tech <- carrier (- consumption)
    * s_cap: installed storage capacity
    * r_cap: installed resource <-> storage conversion capacity
    * e_cap: installed storage <-> grid conversion capacity (gross)
    * r2_cap: installed secondary resource conversion capacity

    * cost: total costs
    * cost_con: construction costs
    * cost_op_fixed: fixed operation costs
    * cost_op_var: variable operation costs
    * cost_op_fuel: primary resource fuel costs
    * cost_op_r2: secondary resource fuel costs

    """

    m = model.m

    # Capacity
    m.r_area = po.Var(m.y_r_area, m.x_r, within=po.NonNegativeReals)
    m.s_cap = po.Var(m.y_store, m.x_store, within=po.NonNegativeReals)
    m.r_cap = po.Var(
        m.y_sp_finite_r, m.x_r,
        within=po.NonNegativeReals)  # FIXME maybe should be y_finite_r?
    m.e_cap = po.Var(m.y, m.x, within=po.NonNegativeReals)
    m.r2_cap = po.Var(m.y_sp_r2, m.x_r, within=po.NonNegativeReals)

    # Unit commitment
    m.r = po.Var(m.y_sp_finite_r, m.x_r, m.t, within=po.Reals)
    m.r2 = po.Var(m.y_sp_r2, m.x_r, m.t, within=po.NonNegativeReals)
    m.s = po.Var(m.y_store, m.x_store, m.t, within=po.NonNegativeReals)
    m.c_prod = po.Var(m.c, m.y, m.x, m.t, within=po.NonNegativeReals)
    m.c_con = po.Var(m.c, m.y, m.x, m.t, within=po.NegativeReals)
    m.export = po.Var(m.y_export, m.x_export, m.t, within=po.NonNegativeReals)

    # Costs
    m.cost_var = po.Var(m.y, m.x, m.t, m.k, within=po.Reals)
    m.cost_fixed = po.Var(m.y, m.x, m.k, within=po.Reals)
    m.cost = po.Var(m.y, m.x, m.k, within=po.Reals)
Exemple #6
0
def add_transmission(m):

    # tranmission (e.g. hvac, hvdc, pipeline...)
    indexlist = set()
    for key in m.transmission_dict["eff"]:
        indexlist.add(tuple(key)[3])
    m.tra = pyomo.Set(
        initialize=indexlist,
        doc='Set of transmission technologies')

    # transmission tuples
    m.tra_tuples = pyomo.Set(
        within=m.stf * m.sit * m.sit * m.tra * m.com,
        initialize=tuple(m.transmission_dict["eff"].keys()),
        doc='Combinations of possible transmissions, e.g. '
            '(2020,South,Mid,hvac,Elec)')

    if m.mode['int']:
        m.operational_tra_tuples = pyomo.Set(
            within=m.sit * m.sit * m.tra * m.com * m.stf * m.stf,
            initialize=[(sit, sit_, tra, com, stf, stf_later)
                        for (sit, sit_, tra, com, stf, stf_later)
                        in op_tra_tuples(m.tra_tuples, m)],
            doc='Transmissions that are still operational through stf_later'
                '(and the relevant years following), if built in stf'
                'in stf.')
        m.inst_tra_tuples = pyomo.Set(
            within=m.sit * m.sit * m.tra * m.com * m.stf,
            initialize=[(sit, sit_, tra, com, stf)
                        for (sit, sit_, tra, com, stf)
                        in inst_tra_tuples(m)],
            doc='Installed transmissions that are still operational'
                'through stf')

    # Variables
    m.cap_tra_new = pyomo.Var(
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='New transmission capacity (MW)')

    # transmission capacity as expression object
    m.cap_tra = pyomo.Expression(
        m.tra_tuples,
        rule=def_transmission_capacity_rule,
        doc='total transmission capacity')

    m.e_tra_in = pyomo.Var(
        m.tm, m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow into transmission line (MW) per timestep')
    m.e_tra_out = pyomo.Var(
        m.tm, m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow out of transmission line (MW) per timestep')

    # transmission
    m.def_transmission_output = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=def_transmission_output_rule,
        doc='transmission output = transmission input * efficiency')
    m.res_transmission_input_by_capacity = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=res_transmission_input_by_capacity_rule,
        doc='transmission input <= total transmission capacity')
    m.res_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_capacity_rule,
        doc='transmission.cap-lo <= total transmission capacity <= '
            'transmission.cap-up')
    m.res_transmission_symmetry = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_symmetry_rule,
        doc='total transmission capacity must be symmetric in both directions')

    return m
Exemple #7
0
def add_transmission_dc(m):
    # defining transmission tuple sets for transport and DCPF model separately
    tra_tuples = set()
    tra_tuples_dc = set()
    for key in m.transmission_dict['reactance']:
        tra_tuples.add(tuple(key))
    for key in m.transmission_dc_dict['reactance']:
        tra_tuples_dc.add(tuple(key))
    tra_tuples_tp = tra_tuples - tra_tuples_dc
    tra_tuples_dc = remove_duplicate_transmission(tra_tuples_dc)
    tra_tuples = tra_tuples_dc | tra_tuples_tp

    # tranmission (e.g. hvac, hvdc, pipeline...)
    indexlist = set()
    for key in m.transmission_dict["eff"]:
        indexlist.add(tuple(key)[3])
    m.tra = pyomo.Set(
        initialize=indexlist,
        doc='Set of transmission technologies')

    # Transport and DCPF transmission tuples
    m.tra_tuples = pyomo.Set(
        within=m.stf * m.sit * m.sit * m.tra * m.com,
        initialize=tuple(tra_tuples),
        doc='Combinations of possible transmissions,'
            'without duplicate dc transmissions'
            ' e.g. (2020,South,Mid,hvac,Elec)')

    # DCPF transmission tuples
    m.tra_tuples_dc = pyomo.Set(
        within=m.stf * m.sit * m.sit * m.tra * m.com,
        initialize=tuple(tra_tuples_dc),
        doc='Combinations of possible bidirectional dc'
            'transmissions, e.g. (2020,South,Mid,hvac,Elec)')

    # Transport transmission tuples
    m.tra_tuples_tp = pyomo.Set(
        within=m.stf * m.sit * m.sit * m.tra * m.com,
        initialize=tuple(tra_tuples_tp),
        doc='Combinations of possible transport transmissions,'
            'e.g. (2020,South,Mid,hvac,Elec)')

    if m.mode['int']:
        m.operational_tra_tuples = pyomo.Set(
            within=m.sit * m.sit * m.tra * m.com * m.stf * m.stf,
            initialize=[(sit, sit_, tra, com, stf, stf_later)
                        for (sit, sit_, tra, com, stf, stf_later)
                        in op_tra_tuples(m.tra_tuples, m)],
            doc='Transmissions that are still operational through stf_later'
                '(and the relevant years following), if built in stf'
                'in stf.')
        m.inst_tra_tuples = pyomo.Set(
            within=m.sit * m.sit * m.tra * m.com * m.stf,
            initialize=[(sit, sit_, tra, com, stf)
                        for (sit, sit_, tra, com, stf)
                        in inst_tra_tuples(m)],
            doc='Installed transmissions that are still operational'
                'through stf')

    # Variables
    m.cap_tra_new = pyomo.Var(
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='New transmission capacity (MW)')

    # transmission capacity as expression object
    m.cap_tra = pyomo.Expression(
        m.tra_tuples,
        rule=def_transmission_capacity_rule,
        doc='total transmission capacity')

    m.e_tra_abs = pyomo.Var(
        m.tm, m.tra_tuples_dc,
        within=pyomo.NonNegativeReals,
        doc='Absolute power flow on transmission line (MW) per timestep')
    m.e_tra_in = pyomo.Var(
        m.tm, m.tra_tuples,
        within=e_tra_domain_rule,
        doc='Power flow into transmission line (MW) per timestep')
    m.e_tra_out = pyomo.Var(
        m.tm, m.tra_tuples,
        within=e_tra_domain_rule,
        doc='Power flow out of transmission line (MW) per timestep')

    m.voltage_angle = pyomo.Var(
        m.tm, m.stf, m.sit,
        within=pyomo.Reals,
        doc='Voltage angle of a site')

    # transmission
    m.def_transmission_output = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=def_transmission_output_rule,
        doc='transmission output = transmission input * efficiency')
    m.def_dc_power_flow = pyomo.Constraint(
        m.tm, m.tra_tuples_dc,
        rule=def_dc_power_flow_rule,
        doc='transmission output = (angle(in)-angle(out))/ 57.2958 '
            '* -1 *(-1/reactance) * (base voltage)^2')
    m.def_angle_limit = pyomo.Constraint(
        m.tm, m.tra_tuples_dc,
        rule=def_angle_limit_rule,
        doc='-angle limit < angle(in) - angle(out) < angle limit')
    m.e_tra_abs1 = pyomo.Constraint(
        m.tm, m.tra_tuples_dc,
        rule=e_tra_abs_rule1,
        doc='transmission dc input <= absolute transmission dc input')
    m.e_tra_abs2 = pyomo.Constraint(
        m.tm, m.tra_tuples_dc,
        rule=e_tra_abs_rule2,
        doc='-transmission dc input <= absolute transmission dc input')

    m.res_transmission_input_by_capacity = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=res_transmission_input_by_capacity_rule,
        doc='transmission input <= total transmission capacity')
    m.res_transmission_dc_input_by_capacity = pyomo.Constraint(
        m.tm, m.tra_tuples_dc,
        rule=res_transmission_dc_input_by_capacity_rule,
        doc='-dcpf transmission input <= total transmission capacity')
    m.res_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_capacity_rule,
        doc='transmission.cap-lo <= total transmission capacity <= '
            'transmission.cap-up')
    m.res_transmission_symmetry = pyomo.Constraint(
        m.tra_tuples_tp,
        rule=res_transmission_symmetry_rule,
        doc='total transmission capacity must be symmetric in both directions')

    return m
Exemple #8
0
def add_storage(m):

    # storage (e.g. hydrogen, pump storage)
    indexlist = set()
    for key in m.storage_dict["eff-in"]:
        indexlist.add(tuple(key)[2])
    m.sto = pyomo.Set(initialize=indexlist, doc='Set of storage technologies')

    # storage tuples
    m.sto_tuples = pyomo.Set(within=m.stf * m.sit * m.sto * m.com,
                             initialize=tuple(m.storage_dict["eff-in"].keys()),
                             doc='Combinations of possible storage by site,'
                             'e.g. (2020,Mid,Bat,Elec)')

    # tuples for intertemporal operation
    if m.mode['int']:
        m.operational_sto_tuples = pyomo.Set(
            within=m.sit * m.sto * m.com * m.stf * m.stf,
            initialize=[(sit, sto, com, stf, stf_later)
                        for (sit, sto, com, stf,
                             stf_later) in op_sto_tuples(m.sto_tuples, m)],
            doc='Processes that are still operational through stf_later'
            '(and the relevant years following), if built in stf'
            'in stf.')
        m.inst_sto_tuples = pyomo.Set(
            within=m.sit * m.sto * m.com * m.stf,
            initialize=[(sit, sto, com, stf)
                        for (sit, sto, com, stf) in inst_sto_tuples(m)],
            doc='Installed storages that are still operational through stf')

    # storage tuples for storages with fixed initial state
    m.sto_init_bound_tuples = pyomo.Set(
        within=m.stf * m.sit * m.sto * m.com,
        initialize=tuple(m.stor_init_bound_dict.keys()),
        doc='storages with fixed initial state')

    # storage tuples for storages with given energy to power ratio
    m.sto_ep_ratio_tuples = pyomo.Set(
        within=m.stf * m.sit * m.sto * m.com,
        initialize=tuple(m.sto_ep_ratio_dict.keys()),
        doc='storages with given energy to power ratio')

    # Variables
    m.cap_sto_c_new = pyomo.Var(m.sto_tuples,
                                within=pyomo.NonNegativeReals,
                                doc='New storage size (MWh)')
    m.cap_sto_p_new = pyomo.Var(m.sto_tuples,
                                within=pyomo.NonNegativeReals,
                                doc='New  storage power (MW)')

    # storage capacities as expression objects
    m.cap_sto_c = pyomo.Expression(m.sto_tuples,
                                   rule=def_storage_capacity_rule,
                                   doc='Total storage size (MWh)')
    m.cap_sto_p = pyomo.Expression(m.sto_tuples,
                                   rule=def_storage_power_rule,
                                   doc='Total storage power (MW)')

    m.e_sto_in = pyomo.Var(m.tm,
                           m.sto_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='Power flow into storage (MW) per timestep')
    m.e_sto_out = pyomo.Var(m.tm,
                            m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Power flow out of storage (MW) per timestep')
    m.e_sto_con = pyomo.Var(m.t,
                            m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Energy content of storage (MWh) in timestep')

    # storage rules
    m.def_storage_state = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=def_storage_state_rule,
        doc='storage[t] = (1 - sd) * storage[t-1] + in * eff_i - out / eff_o')
    m.res_storage_input_by_power = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=res_storage_input_by_power_rule,
        doc='storage input <= storage power')
    m.res_storage_output_by_power = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=res_storage_output_by_power_rule,
        doc='storage output <= storage power')
    m.res_storage_state_by_capacity = pyomo.Constraint(
        m.t,
        m.sto_tuples,
        rule=res_storage_state_by_capacity_rule,
        doc='storage content <= storage capacity')
    m.res_storage_power = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_power_rule,
        doc='storage.cap-lo-p <= storage power <= storage.cap-up-p')
    m.res_storage_capacity = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_capacity_rule,
        doc='storage.cap-lo-c <= storage capacity <= storage.cap-up-c')
    m.def_initial_storage_state = pyomo.Constraint(
        m.sto_init_bound_tuples,
        rule=def_initial_storage_state_rule,
        doc='storage content initial == and final >= storage.init * capacity')
    m.res_storage_state_cyclicity = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_state_cyclicity_rule,
        doc='storage content initial <= final, both variable')
    m.def_storage_energy_power_ratio = pyomo.Constraint(
        m.sto_ep_ratio_tuples,
        rule=def_storage_energy_power_ratio_rule,
        doc='storage capacity = storage power * storage E2P ratio')

    return m
Exemple #9
0
def create_model(data, dt=1, timesteps=None, dual=False):
    """Create a pyomo ConcreteModel urbs object from given input data.

    Args:
        data: a dict of 6 DataFrames with the keys 'commodity', 'process',
            'transmission', 'storage', 'demand' and 'supim'.
        dt: timestep duration in hours (default: 1)
        timesteps: optional list of timesteps, default: demand timeseries
        dual: set True to add dual variables to model (slower); default: False

    Returns:
        a pyomo ConcreteModel object
    """

    # Optional
    if not timesteps:
        timesteps = data['demand'].index.tolist()
    m = pyomo_model_prep(data, timesteps)  # preparing pyomo model
    m.name = 'urbs'
    m.created = datetime.now().strftime('%Y%m%dT%H%M')
    m._data = data

    # Parameters

    # weight = length of year (hours) / length of simulation (hours)
    # weight scales costs and emissions from length of simulation to a full
    # year, making comparisons among cost types (invest is annualized, fixed
    # costs are annual by default, variable costs are scaled by weight) and
    # among different simulation durations meaningful.
    m.weight = pyomo.Param(
        initialize=float(8760) / (len(m.timesteps) * dt),
        doc='Pre-factor for variable costs and emissions for an annual result')

    # dt = spacing between timesteps. Required for storage equation that
    # converts between energy (storage content, e_sto_con) and power (all other
    # quantities that start with "e_")
    m.dt = pyomo.Param(initialize=dt,
                       doc='Time step duration (in hours), default: 1')

    # Sets
    # ====
    # Syntax: m.{name} = Set({domain}, initialize={values})
    # where name: set name
    #       domain: set domain for tuple sets, a cartesian set product
    #       values: set values, a list or array of element tuples

    # generate ordered time step sets
    m.t = pyomo.Set(initialize=m.timesteps,
                    ordered=True,
                    doc='Set of timesteps')

    # modelled (i.e. excluding init time step for storage) time steps
    m.tm = pyomo.Set(within=m.t,
                     initialize=m.timesteps[1:],
                     ordered=True,
                     doc='Set of modelled timesteps')

    # modelled Demand Side Management time steps (downshift):
    # downshift effective in tt to compensate for upshift in t
    m.tt = pyomo.Set(within=m.t,
                     initialize=m.timesteps[1:],
                     ordered=True,
                     doc='Set of additional DSM time steps')

    # site (e.g. north, middle, south...)
    m.sit = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Site').unique(),
        doc='Set of sites')

    # commodity (e.g. solar, wind, coal...)
    m.com = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Commodity').unique(),
        doc='Set of commodities')

    # commodity type (i.e. SupIm, Demand, Stock, Env)
    m.com_type = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Type').unique(),
        doc='Set of commodity types')

    # process (e.g. Wind turbine, Gas plant, Photovoltaics...)
    m.pro = pyomo.Set(
        initialize=m.process.index.get_level_values('Process').unique(),
        doc='Set of conversion processes')

    # tranmission (e.g. hvac, hvdc, pipeline...)
    m.tra = pyomo.Set(initialize=m.transmission.index.get_level_values(
        'Transmission').unique(),
                      doc='Set of transmission technologies')

    # storage (e.g. hydrogen, pump storage)
    m.sto = pyomo.Set(
        initialize=m.storage.index.get_level_values('Storage').unique(),
        doc='Set of storage technologies')

    # cost_type
    m.cost_type = pyomo.Set(initialize=[
        'Invest', 'Fixed', 'Variable', 'Fuel', 'Revenue', 'Purchase',
        'Environmental'
    ],
                            doc='Set of cost types (hard-coded)')

    # tuple sets
    m.com_tuples = pyomo.Set(
        within=m.sit * m.com * m.com_type,
        initialize=m.commodity.index,
        doc='Combinations of defined commodities, e.g. (Mid,Elec,Demand)')
    m.pro_tuples = pyomo.Set(
        within=m.sit * m.pro,
        initialize=m.process.index,
        doc='Combinations of possible processes, e.g. (North,Coal plant)')
    m.tra_tuples = pyomo.Set(
        within=m.sit * m.sit * m.tra * m.com,
        initialize=m.transmission.index,
        doc='Combinations of possible transmissions, e.g. '
        '(South,Mid,hvac,Elec)')
    m.sto_tuples = pyomo.Set(
        within=m.sit * m.sto * m.com,
        initialize=m.storage.index,
        doc='Combinations of possible storage by site, e.g. (Mid,Bat,Elec)')
    m.dsm_site_tuples = pyomo.Set(
        within=m.sit * m.com,
        initialize=m.dsm.index,
        doc='Combinations of possible dsm by site, e.g. (Mid, Elec)')
    m.dsm_down_tuples = pyomo.Set(
        within=m.tm * m.tm * m.sit * m.com,
        initialize=[(t, tt, site, commodity)
                    for (t, tt, site, commodity) in dsm_down_time_tuples(
                        m.timesteps[1:], m.dsm_site_tuples, m)],
        doc='Combinations of possible dsm_down combinations, e.g. '
        '(5001,5003,Mid,Elec)')

    # commodity type subsets
    m.com_supim = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'SupIm'),
        doc='Commodities that have intermittent (timeseries) input')
    m.com_stock = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Stock'),
        doc='Commodities that can be purchased at some site(s)')
    m.com_sell = pyomo.Set(within=m.com,
                           initialize=commodity_subset(m.com_tuples, 'Sell'),
                           doc='Commodities that can be sold')
    m.com_buy = pyomo.Set(within=m.com,
                          initialize=commodity_subset(m.com_tuples, 'Buy'),
                          doc='Commodities that can be purchased')
    m.com_demand = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Demand'),
        doc='Commodities that have a demand (implies timeseries)')
    m.com_env = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Env'),
        doc='Commodities that (might) have a maximum creation limit')

    # process tuples for area rule
    m.pro_area_tuples = pyomo.Set(
        within=m.sit * m.pro,
        initialize=m.proc_area.index,
        doc='Processes and Sites with area Restriction')

    # process input/output
    m.pro_input_tuples = pyomo.Set(
        within=m.sit * m.pro * m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_tuples
                    for (pro, commodity) in m.r_in.index if process == pro],
        doc='Commodities consumed by process by site, e.g. (Mid,PV,Solar)')
    m.pro_output_tuples = pyomo.Set(
        within=m.sit * m.pro * m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_tuples
                    for (pro, commodity) in m.r_out.index if process == pro],
        doc='Commodities produced by process by site, e.g. (Mid,PV,Elec)')

    # process tuples for maximum gradient feature
    m.pro_maxgrad_tuples = pyomo.Set(
        within=m.sit * m.pro,
        initialize=[(sit, pro) for (sit, pro) in m.pro_tuples
                    if m.process.loc[sit, pro]['max-grad'] < 1.0 / dt],
        doc='Processes with maximum gradient smaller than timestep length')

    # process tuples for partial feature
    m.pro_partial_tuples = pyomo.Set(within=m.sit * m.pro,
                                     initialize=[
                                         (site, process)
                                         for (site, process) in m.pro_tuples
                                         for (pro,
                                              _) in m.r_in_min_fraction.index
                                         if process == pro
                                     ],
                                     doc='Processes with partial input')

    m.pro_partial_input_tuples = pyomo.Set(
        within=m.sit * m.pro * m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_partial_tuples
                    for (pro, commodity) in m.r_in_min_fraction.index
                    if process == pro],
        doc='Commodities with partial input ratio, e.g. (Mid,Coal PP,Coal)')

    m.pro_partial_output_tuples = pyomo.Set(
        within=m.sit * m.pro * m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_partial_tuples
                    for (pro, commodity) in m.r_out_min_fraction.index
                    if process == pro],
        doc='Commodities with partial input ratio, e.g. (Mid,Coal PP,CO2)')

    # process tuples for time variable efficiency
    m.pro_timevar_output_tuples = pyomo.Set(
        within=m.sit * m.pro * m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.eff_factor.columns.values
                    for (pro, commodity) in m.r_out.index if process == pro],
        doc='Outputs of processes with time dependent efficiency')

    # Variables

    # costs
    m.costs = pyomo.Var(m.cost_type,
                        within=pyomo.Reals,
                        doc='Costs by type (EUR/a)')

    # commodity
    m.e_co_stock = pyomo.Var(
        m.tm,
        m.com_tuples,
        within=pyomo.NonNegativeReals,
        doc='Use of stock commodity source (MW) per timestep')
    m.e_co_sell = pyomo.Var(
        m.tm,
        m.com_tuples,
        within=pyomo.NonNegativeReals,
        doc='Use of sell commodity source (MW) per timestep')
    m.e_co_buy = pyomo.Var(m.tm,
                           m.com_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='Use of buy commodity source (MW) per timestep')

    # process
    m.cap_pro = pyomo.Var(m.pro_tuples,
                          within=pyomo.NonNegativeReals,
                          doc='Total process capacity (MW)')
    m.cap_pro_new = pyomo.Var(m.pro_tuples,
                              within=pyomo.NonNegativeReals,
                              doc='New process capacity (MW)')
    m.tau_pro = pyomo.Var(m.t,
                          m.pro_tuples,
                          within=pyomo.NonNegativeReals,
                          doc='Power flow (MW) through process')
    m.e_pro_in = pyomo.Var(
        m.tm,
        m.pro_tuples,
        m.com,
        within=pyomo.NonNegativeReals,
        doc='Power flow of commodity into process (MW) per timestep')
    m.e_pro_out = pyomo.Var(m.tm,
                            m.pro_tuples,
                            m.com,
                            within=pyomo.NonNegativeReals,
                            doc='Power flow out of process (MW) per timestep')

    # transmission
    m.cap_tra = pyomo.Var(m.tra_tuples,
                          within=pyomo.NonNegativeReals,
                          doc='Total transmission capacity (MW)')
    m.cap_tra_new = pyomo.Var(m.tra_tuples,
                              within=pyomo.NonNegativeReals,
                              doc='New transmission capacity (MW)')
    m.e_tra_in = pyomo.Var(
        m.tm,
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow into transmission line (MW) per timestep')
    m.e_tra_out = pyomo.Var(
        m.tm,
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow out of transmission line (MW) per timestep')

    # storage
    m.cap_sto_c = pyomo.Var(m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Total storage size (MWh)')
    m.cap_sto_c_new = pyomo.Var(m.sto_tuples,
                                within=pyomo.NonNegativeReals,
                                doc='New storage size (MWh)')
    m.cap_sto_p = pyomo.Var(m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Total storage power (MW)')
    m.cap_sto_p_new = pyomo.Var(m.sto_tuples,
                                within=pyomo.NonNegativeReals,
                                doc='New  storage power (MW)')
    m.e_sto_in = pyomo.Var(m.tm,
                           m.sto_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='Power flow into storage (MW) per timestep')
    m.e_sto_out = pyomo.Var(m.tm,
                            m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Power flow out of storage (MW) per timestep')
    m.e_sto_con = pyomo.Var(m.t,
                            m.sto_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Energy content of storage (MWh) in timestep')

    # demand side management
    m.dsm_up = pyomo.Var(m.tm,
                         m.dsm_site_tuples,
                         within=pyomo.NonNegativeReals,
                         doc='DSM upshift')
    m.dsm_down = pyomo.Var(m.dsm_down_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='DSM downshift')

    # Equation declarations
    # equation bodies are defined in separate functions, referred to here by
    # their name in the "rule" keyword.

    # commodity
    m.res_vertex = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_vertex_rule,
        doc='storage + transmission + process + source + buy - sell == demand')
    m.res_stock_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_stock_step_rule,
        doc='stock commodity input per step <= commodity.maxperstep')
    m.res_stock_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_stock_total_rule,
        doc='total stock commodity input <= commodity.max')
    m.res_sell_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_sell_step_rule,
        doc='sell commodity output per step <= commodity.maxperstep')
    m.res_sell_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_sell_total_rule,
        doc='total sell commodity output <= commodity.max')
    m.res_buy_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_buy_step_rule,
        doc='buy commodity output per step <= commodity.maxperstep')
    m.res_buy_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_buy_total_rule,
        doc='total buy commodity output <= commodity.max')
    m.res_env_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_env_step_rule,
        doc='environmental output per step <= commodity.maxperstep')
    m.res_env_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_env_total_rule,
        doc='total environmental commodity output <= commodity.max')

    # process
    m.def_process_capacity = pyomo.Constraint(
        m.pro_tuples,
        rule=def_process_capacity_rule,
        doc='total process capacity = inst-cap + new capacity')
    m.def_process_input = pyomo.Constraint(
        m.tm,
        m.pro_input_tuples - m.pro_partial_input_tuples,
        rule=def_process_input_rule,
        doc='process input = process throughput * input ratio')
    m.def_process_output = pyomo.Constraint(
        m.tm, (m.pro_output_tuples - m.pro_partial_output_tuples -
               m.pro_timevar_output_tuples),
        rule=def_process_output_rule,
        doc='process output = process throughput * output ratio')
    m.def_intermittent_supply = pyomo.Constraint(
        m.tm,
        m.pro_input_tuples,
        rule=def_intermittent_supply_rule,
        doc='process output = process capacity * supim timeseries')
    m.res_process_throughput_by_capacity = pyomo.Constraint(
        m.tm,
        m.pro_tuples,
        rule=res_process_throughput_by_capacity_rule,
        doc='process throughput <= total process capacity')
    m.res_process_maxgrad_lower = pyomo.Constraint(
        m.tm,
        m.pro_maxgrad_tuples,
        rule=res_process_maxgrad_lower_rule,
        doc='throughput may not decrease faster than maximal gradient')
    m.res_process_maxgrad_upper = pyomo.Constraint(
        m.tm,
        m.pro_maxgrad_tuples,
        rule=res_process_maxgrad_upper_rule,
        doc='throughput may not increase faster than maximal gradient')
    m.res_process_capacity = pyomo.Constraint(
        m.pro_tuples,
        rule=res_process_capacity_rule,
        doc='process.cap-lo <= total process capacity <= process.cap-up')

    m.res_area = pyomo.Constraint(
        m.sit,
        rule=res_area_rule,
        doc='used process area <= total process area')

    m.res_sell_buy_symmetry = pyomo.Constraint(
        m.pro_input_tuples,
        rule=res_sell_buy_symmetry_rule,
        doc='power connection capacity must be symmetric in both directions')

    m.res_throughput_by_capacity_min = pyomo.Constraint(
        m.tm,
        m.pro_partial_tuples,
        rule=res_throughput_by_capacity_min_rule,
        doc='cap_pro * min-fraction <= tau_pro')
    m.def_partial_process_input = pyomo.Constraint(
        m.tm,
        m.pro_partial_input_tuples,
        rule=def_partial_process_input_rule,
        doc='e_pro_in = '
        ' cap_pro * min_fraction * (r - R) / (1 - min_fraction)'
        ' + tau_pro * (R - min_fraction * r) / (1 - min_fraction)')
    m.def_partial_process_output = pyomo.Constraint(
        m.tm, (m.pro_partial_output_tuples -
               (m.pro_partial_output_tuples & m.pro_timevar_output_tuples)),
        rule=def_partial_process_output_rule,
        doc='e_pro_out = '
        ' cap_pro * min_fraction * (r - R) / (1 - min_fraction)'
        ' + tau_pro * (R - min_fraction * r) / (1 - min_fraction)')
    m.def_process_timevar_output = pyomo.Constraint(
        m.tm, (m.pro_timevar_output_tuples -
               (m.pro_partial_output_tuples & m.pro_timevar_output_tuples)),
        rule=def_pro_timevar_output_rule,
        doc='e_pro_out = tau_pro * r_out * eff_factor')
    m.def_process_partial_timevar_output = pyomo.Constraint(
        m.tm,
        m.pro_partial_output_tuples & m.pro_timevar_output_tuples,
        rule=def_pro_partial_timevar_output_rule,
        doc='e_pro_out = tau_pro * r_out * eff_factor')

    # transmission
    m.def_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=def_transmission_capacity_rule,
        doc='total transmission capacity = inst-cap + new capacity')
    m.def_transmission_output = pyomo.Constraint(
        m.tm,
        m.tra_tuples,
        rule=def_transmission_output_rule,
        doc='transmission output = transmission input * efficiency')
    m.res_transmission_input_by_capacity = pyomo.Constraint(
        m.tm,
        m.tra_tuples,
        rule=res_transmission_input_by_capacity_rule,
        doc='transmission input <= total transmission capacity')
    m.res_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_capacity_rule,
        doc='transmission.cap-lo <= total transmission capacity <= '
        'transmission.cap-up')
    m.res_transmission_symmetry = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_symmetry_rule,
        doc='total transmission capacity must be symmetric in both directions')

    # storage
    m.def_storage_state = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=def_storage_state_rule,
        doc='storage[t] = (1 - sd) * storage[t-1] + in * eff_i - out / eff_o')
    m.def_storage_power = pyomo.Constraint(
        m.sto_tuples,
        rule=def_storage_power_rule,
        doc='storage power = inst-cap + new power')
    m.def_storage_capacity = pyomo.Constraint(
        m.sto_tuples,
        rule=def_storage_capacity_rule,
        doc='storage capacity = inst-cap + new capacity')
    m.res_storage_input_by_power = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=res_storage_input_by_power_rule,
        doc='storage input <= storage power')
    m.res_storage_output_by_power = pyomo.Constraint(
        m.tm,
        m.sto_tuples,
        rule=res_storage_output_by_power_rule,
        doc='storage output <= storage power')
    m.res_storage_state_by_capacity = pyomo.Constraint(
        m.t,
        m.sto_tuples,
        rule=res_storage_state_by_capacity_rule,
        doc='storage content <= storage capacity')
    m.res_storage_power = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_power_rule,
        doc='storage.cap-lo-p <= storage power <= storage.cap-up-p')
    m.res_storage_capacity = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_capacity_rule,
        doc='storage.cap-lo-c <= storage capacity <= storage.cap-up-c')
    m.res_initial_and_final_storage_state = pyomo.Constraint(
        m.t,
        m.sto_tuples,
        rule=res_initial_and_final_storage_state_rule,
        doc='storage content initial == and final >= storage.init * capacity')

    # costs
    m.def_costs = pyomo.Constraint(m.cost_type,
                                   rule=def_costs_rule,
                                   doc='main cost function by cost type')
    m.obj = pyomo.Objective(rule=obj_rule,
                            sense=pyomo.minimize,
                            doc='minimize(cost = sum of all cost types)')

    # demand side management
    m.def_dsm_variables = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=def_dsm_variables_rule,
        doc='DSMup * efficiency factor n == DSMdo (summed)')

    m.res_dsm_upward = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_upward_rule,
        doc='DSMup <= Cup (threshold capacity of DSMup)')

    m.res_dsm_downward = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_downward_rule,
        doc='DSMdo (summed) <= Cdo (threshold capacity of DSMdo)')

    m.res_dsm_maximum = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_maximum_rule,
        doc='DSMup + DSMdo (summed) <= max(Cup,Cdo)')

    m.res_dsm_recovery = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_recovery_rule,
        doc='DSMup(t, t + recovery time R) <= Cup * delay time L')

    m.res_global_co2_limit = pyomo.Constraint(
        rule=res_global_co2_limit_rule,
        doc='total co2 commodity output <= Global CO2 limit')

    if dual:
        m.dual = pyomo.Suffix(direction=pyomo.Suffix.IMPORT)
    return m
Exemple #10
0
def add_dsm(m):

    # modelled Demand Side Management time steps (downshift):
    # downshift effective in tt to compensate for upshift in t
    m.tt = pyomo.Set(within=m.t,
                     initialize=m.timesteps[1:],
                     ordered=True,
                     doc='Set of additional DSM time steps')

    # DSM Tuples
    m.dsm_site_tuples = pyomo.Set(
        within=m.stf * m.sit * m.com,
        initialize=tuple(m.dsm_dict["delay"].keys()),
        doc='Combinations of possible dsm by site, e.g. '
        '(2020, Mid, Elec)')
    m.dsm_down_tuples = pyomo.Set(
        within=m.tm * m.tm * m.stf * m.sit * m.com,
        initialize=[(t, tt, stf, site, commodity)
                    for (t, tt, stf, site, commodity) in dsm_down_time_tuples(
                        m.timesteps[1:], m.dsm_site_tuples, m)],
        doc='Combinations of possible dsm_down combinations, e.g. '
        '(5001,5003,2020,Mid,Elec)')

    # Variables
    m.dsm_up = pyomo.Var(m.tm,
                         m.dsm_site_tuples,
                         within=pyomo.NonNegativeReals,
                         doc='DSM upshift')
    m.dsm_down = pyomo.Var(m.dsm_down_tuples,
                           within=pyomo.NonNegativeReals,
                           doc='DSM downshift')

    # DSM rules
    m.def_dsm_variables = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=def_dsm_variables_rule,
        doc='DSMup * efficiency factor n == DSMdo (summed)')

    m.res_dsm_upward = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_upward_rule,
        doc='DSMup <= Cup (threshold capacity of DSMup)')

    m.res_dsm_downward = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_downward_rule,
        doc='DSMdo (summed) <= Cdo (threshold capacity of DSMdo)')

    m.res_dsm_maximum = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_maximum_rule,
        doc='DSMup + DSMdo (summed) <= max(Cup,Cdo)')

    m.res_dsm_recovery = pyomo.Constraint(
        m.tm,
        m.dsm_site_tuples,
        rule=res_dsm_recovery_rule,
        doc='DSMup(t, t + recovery time R) <= Cup * delay time L')

    return m
Exemple #11
0
def lp_unit_factors(ranges, solver_name, solver_io, solver_tolerance):
    '''Solve an auxiliary LP to find optimal scaling factors for given unit ranges:
    Given a set of units {u1, u2, ..., un}
    and the range of absolute values of each unit 
    { u1 -> [l1, r1], ..., un -> [ln, rn]}
    we want to find scaling factors f1, ..., fn that minimize
    max(fi*ri)/min(fj*rj)
    
    A difficulty arises because each unit is a fraction of two base units: ui = bj/bk
    and we need to retain consistency between scaling factors. 
    Thus we optimize the scaling factors Fj of base units bj and compute factors of "composite" units, i.e.
    
    ui = bj/bk --> fi = Fj/Fk'''

    def bound_rule(model, num1, den1, v1, num2, den2, v2):
        '''Link r, the log of the largest gap, to the scaling factor variables.'''
        def g(acc):
            return model.x[acc] if acc != 'const' else 0
    
        return g(num1) - g(den1) + v1 - g(num2) + g(den2) - v2 <= model.r


    def lower_limits_rule(model, num1, den1, v):
        '''Forbid that unit num1/den1 is scaled too low.'''
        def g(acc):
            return model.x[acc] if acc != 'const' else 0
    
        return g(num1) - g(den1) >= v


    model = po.ConcreteModel()

    '''
    variables
    (a) We create one variable for the log of the scaling factor of each unit
    (b) And one variable r to be minimized 
    '''
    unitvars = [unit for unit in filter(
        lambda u: u != 'const',
        list(set(
            list(map(lambda r: r['num'], ranges)) +
            list(map(lambda r: r['den'], ranges))
        )))
    ]

    model.x = po.Var(unitvars, domain=po.Reals)
    model.r = po.Var(domain=po.Reals)

    '''
    set objective to minimize r
    '''
    model.cost = po.Objective(expr = model.r)
    
    '''
    Constraints:
    limit si - sj + (u_hi){i/j} - sk + sl - (u_lo){k/l} <= r 
    for all pairs of units u{i/j}, u{k/l}
    to find max_{{i/j}, {k/l} \in units}(si*sl/sj*sk (u_hi){i/j} / (u_lo){k/l})
    '''
    ranges_wo_const = [rng for rng in ranges if rng['num'] != 'const' or rng['den'] != 'const']
    boundvals = [
        (r1['num'], r1['den'], math.log(r1['max'], 2), r2['num'], r2['den'], math.log(r2['min'], 2))
        for r1 in ranges for r2 in ranges
    ]
    
    model.bounds = po.Constraint(boundvals, rule=bound_rule)

    solver = SolverFactory(solver_name, solver_io=solver_io)
    solver.solve(model)
    temp_facs = {k: 2**model.x[k]() for k in unitvars}
    maxs = [r['max'] * temp_facs.get(r['num'], 1) / temp_facs.get(r['den'], 1) for r in ranges]
    mins = [r['min'] * temp_facs.get(r['num'], 1) / temp_facs.get(r['den'], 1) for r in ranges]
    best_range = max(maxs)/min(mins)
    print('best range: {}'.format(best_range))

    '''
    ensure that absolute values in model are not scaled below a certain threshold.
    this generally limits the objective function -> need to find good tradeoff.

    Practical Guidelines for Solving Difficult Linear Programs suggests we should ensure that user input consists of values larger than the solver tolerances tol, thus limit values to scaling_tolerance_threshold*tol. Or center the values around 0 if this gives an even higher threshold.

    note: our coefficient rounding below may lead to values 2 times smaller than the set limit, thus limit by 2*scaling_tolerance_threshold*tol 
    '''
    lower_limit = 10*solver_tolerance
    print('setting limit {}'.format(lower_limit))


    violating_mins = set()
    while True:
        changed = False
        factors = {k: 2**model.x[k]() for k in unitvars}
        for rng in ranges_wo_const: 
            num = factors[rng['num']] if rng['num'] != 'const' else 1
            den = factors[rng['den']] if rng['den'] != 'const' else 1
            if rng['min']*num/den < solver_tolerance:
                print('violating {}/{}: {} -> add constr'.format(rng['num'], rng['den'], rng['min']*num/den))
                violating_mins.add((rng['num'], rng['den'], math.log(lower_limit/rng['min'], 2)))
                changed = True
            
        if not changed:
            break
                   
        if not model.component('lower_limits') is None:
            model.del_component(model.lower_limits)
            model.del_component(model.lower_limits_index)
                   
        model.add_component('lower_limits', po.Constraint(list(violating_mins), rule=lower_limits_rule))

        solver.solve(model)    

    '''
    we want all factors to be an exponent of 2 in order not to tamper with precision of user values (c.f. tomlin - on scaling linear programming problems)
    we achieve this by rounding the optimal values we just computed to integers before exponentiating them
    '''
    facs = {k: 2**math.floor(model.x[k]()) for k in unitvars}
    return facs
Exemple #12
0
def create_model(vertex, edge, params={}, timesteps=[]):
    """return a DHMIN model instance from nodes and edges DataFrame
    
    Args:
        vertex: DataFrame of vertex with index and attributes
        edges: DataFrame of edges with (Vertex1, Vertex2) MultiIndex and attributes
        params: dict of cost and technical parameters
        timesteps: list of timestep tuples (duration, scaling factor)
    Returns:
        m: a coopr.pyomo ConcreteModel object
    Usage: 
        see rundh.py
    
    The optional argument params can be used to specify any of the 
    technical and cost parameters.
    
    The optional argument timesteps is given, DHMIN is run in multi-
    seasonal mode that includes a simplified time model. Each (t,p)
    tuple encodes a time interval of length (1 hour)*t and relative
    peak power requirement (peak)*p of all consumers. Note that sum(t)
    must be equal to 8760. The inequalities 0 <= t <= 8760 and 0 <= p <= 1
    are to be respected.

    """
    m = pyomo.ConcreteModel()
    m.name = 'DHMIN'
    
    # DATA PREPARATION
    
    tech_parameters = {
        'c_fix': 600, # (€/m) fixed pipe investment
        'c_var': 0.015, # (€/kW/m) variable pipe investment
        'c_om': 5, # (€/m) operation & maintenance
        'r_heat': 0.07, # (€/kWh) retail price for heat
        'annuity': anf(40, 0.06), # (%) annuity factor (years, interest)
        'thermal_loss_fix': 20e-3, # (kW/m) fixed thermal losses
        'thermal_loss_var': 1e-7, # (kW/kW/m) variable thermal losses
        'concurrence': 1, # (%) concurrence effect
        } 
    
    # Entity edge contains column 'Edge' as index. This model (in contrast to
    # the old GAMS version) does not use the 'Edge' ID on its own, so remove the
    # edge ID from the index ('Edge', 'Vertex1', 'Vertex2')
    edges = edge.reset_index('Edge')
            
    # replace default parameter values with user-defined ones, if specified
    tech_parameters.update(params)
    
    # make edges symmetric by duplicating each row (i,j) to (j,i)
    edges_tmp = edges
    edges_tmp.index.names = ['Vertex2', 'Vertex1']
    edges_tmp = edges_tmp.reorder_levels(['Vertex1', 'Vertex2'])
    edges = edges_tmp.append(edges, verify_integrity=True)
    del edges_tmp
    
    # derive list of neighbours for each vertex
    m.neighbours = {}
    for (i, j) in edges.index:
        m.neighbours.setdefault(i, [])
        m.neighbours[i].append(j)
        
    #
    m.vertices = vertex.copy()
    m.edges = edges.copy()
    
    cost_types = [
        'network', # pipe construction, maintenance
        'heat', # heating plants, operation
        'revenue', # sold heat
        ]
    
    # derive subset of source vertices, i.e. those with column 'init' set to 1
    source_vertex = vertex[vertex.init == 1].index

    # timestep preparation
    if timesteps:
        # extend timesteps with (name, duration, scaling factor) tuples and
        # add a near-zero (here: 1 hour) legnth, nominal power timestep 'Pmax'
        timesteps = [('t{}'.format(t[0]), t[0], t[1]) for t in timesteps]
        timesteps.append(('Pmax', 1 , 1))
        
        # now get a list of all source nodes
        # for each source, add a non-availability timestep ('v0', 1, 1)
        # and set availability matrix so that 'v0' is off in that timestep
        availability = np.ones((len(timesteps) + len(source_vertex), 
                                len(source_vertex)), 
                               dtype=np.int)
        
        for i, v0 in enumerate(source_vertex):
            availability[len(timesteps), i] = 0
            timesteps.append(('v{}'.format(v0), 1, 1))
    else:
        # no timesteps: create single dummy timestep with 100% availability
        timesteps = [('t0', 1, 1)]
        availability = np.ones((1, 
                                len(source_vertex)), 
                               dtype=np.int)
     
    # MODEL
    
    # Sets
    m.vertex = pyomo.Set(initialize=vertex.index)
    m.edge = pyomo.Set(within=m.vertex*m.vertex, initialize=edges.index)
    m.cost_types = pyomo.Set(initialize=cost_types)
    m.tech_params = pyomo.Set(initialize=tech_parameters.keys())
    m.timesteps = pyomo.Set(initialize=[t[0] for t in timesteps])
    m.source_vertex = pyomo.Set(initialize=source_vertex)
    
    # Parameters
    m.tech_parameters = pyomo.Param(m.tech_params, initialize=tech_parameters)
    
    # derive delta and eta from edge attributes
    m.delta = pyomo.Param(m.edge, initialize=dict(
                    edges['peak'] 
                        * edges['cnct_quota']
                        * tech_parameters['concurrence'] +
                    edges['length']
                        * tech_parameters['thermal_loss_fix']
                    ))
    m.eta = pyomo.Param(m.edge, initialize=dict(
                  1 - (edges['length']
                       * tech_parameters['thermal_loss_var'])
                  ))
    
    # cost coefficients for objective function
    
    # k_fix: power-independent investment and operation & maintenance costs for 
    # pipes (EUR/a)
    m.k_fix = pyomo.Param(m.edge, initialize=dict(
                    edges['length']
                        * 0.5 # x and Pmax are forced in both directions (i,j),(j,i)
                        * tech_parameters['c_fix'] 
                        * tech_parameters['annuity']
                        * (1 - edges['pipe_exist']) +
                    edges['length']
                        * 0.5 # x and Pmax are forced in both directions (i,j),(j,i)
                        * tech_parameters['c_om']
                    ))
    
    # k_var: power-dependent pipe investment costs (EUR/kW/a)
    m.k_var = pyomo.Param(m.edge, initialize=dict(
                    edges['length']
                        * 0.5 # x and Pmax are forced in both directions (i,j),(j,i)
                        * tech_parameters['c_var'] 
                        * tech_parameters['annuity']
                        * (1- edges['pipe_exist'])
                    ))
    
    # k_heat: costs for heat generation (EUR/h)
    # as the source-term for power flow is lowered by concurrence effect (cf.
    # m.delta), for conversion to energy integral, it must be removed again
    m.k_heat = pyomo.Param(m.vertex, initialize=dict(
                     vertex['cost_heat']
                        / tech_parameters['concurrence']
                     ))
    
    # r_heat: revenue for heat delivery (EUR/h)
    #
    m.r_heat = pyomo.Param(m.edge, initialize=dict(
                     edges['peak']
                        * 0.5 # x and Pmax are forced in both directions (i,j),(j,i)
                        * edges['cnct_quota']
                        * tech_parameters['r_heat']
                     ))
    m.availability = pyomo.Param(m.source_vertex, m.timesteps, initialize={
            (s,t[0]): availability[x,y]
            for y,s in enumerate(source_vertex)
            for x,t in enumerate(timesteps)
            })
    m.dt = pyomo.Param(m.timesteps, initialize={t[0]:t[1] for t in timesteps})
    m.scaling_factor = pyomo.Param(m.timesteps, initialize={t[0]:t[2] for t in timesteps})
    
    # Variables
    m.costs = pyomo.Var(m.cost_types)
    m.x = pyomo.Var(m.edge, within=pyomo.Binary)
    m.Pmax = pyomo.Var(m.edge, within=pyomo.NonNegativeReals)

    m.Pin = pyomo.Var(m.edge, m.timesteps, within=pyomo.NonNegativeReals)
    m.Pot = pyomo.Var(m.edge, m.timesteps, within=pyomo.NonNegativeReals)
    m.Q = pyomo.Var(m.vertex, m.timesteps, within=pyomo.NonNegativeReals)
    m.y = pyomo.Var(m.edge, m.timesteps, within=pyomo.Binary)
    
    m.energy_conservation = pyomo.Constraint(
        m.vertex, m.timesteps,
        doc='Power flow is conserved in vertex',
        rule=energy_conservation_rule)
    m.demand_satisfaction = pyomo.Constraint(
        m.edge, m.timesteps,
        doc='Peak demand (delta) must be satisfied in edge, if pipe is built',
        rule=demand_satisfaction_rule)
    m.pipe_capacity = pyomo.Constraint(
        m.edge, m.timesteps,
        doc='Power flow is smaller than pipe capacity Pmax',
        rule=pipe_capacity_rule)
    m.pipe_usage = pyomo.Constraint(
        m.edge, m.timesteps,
        doc='Power flow through pipe=0 if y[i,j,t]=0',
        rule=pipe_usage_rule)
    m.must_build = pyomo.Constraint(
        m.edge,
        doc='Pipe must be built if must_build == 1',
        rule=must_build_rule)
    m.build_capacity = pyomo.Constraint(
        m.edge, 
        doc='Pipe capacity Pmax must be smaller than edge attribute cap_max',
        rule=build_capacity_rule)
    m.unidirectionality = pyomo.Constraint(
        m.edge, m.timesteps, 
        doc='Power flow only in one direction per timestep',
        rule=unidirectionality_rule)
    m.symmetry_x = pyomo.Constraint(
        m.edge, 
        doc='Pipe may be used in both directions, if built',
        rule=symmetry_x_rule)
    m.symmetry_Pmax = pyomo.Constraint(
        m.edge, 
        doc='Pipe has same capacity in both directions, if built',
        rule=symmetry_Pmax_rule)
    m.built_then_use = pyomo.Constraint(
        m.edge, m.timesteps, 
        doc='Demand must be satisfied from at least one direction, if built',
        rule=built_then_use_rule)
    m.source_vertices = pyomo.Constraint(
        m.vertex, m.timesteps,
        doc='Non-zero source term Q is only allowed in source vertices',
        rule=source_vertices_rule)
    
    # Objective
    m.def_costs = pyomo.Constraint(
        m.cost_types,
        doc='Cost definitions by type',
        rule=cost_rule)
    m.obj = pyomo.Objective(
        sense=pyomo.minimize,
        doc='Minimize costs = network + heat - revenue',
        rule=obj_rule)

    return m
Exemple #13
0
def node_energy_balance(model):
    """
    Defines variables:

    * s: storage level
    * es_prod: storage -> carrier (+ production)
    * es_con: storage <- carrier (- consumption)

    """
    m = model.m
    d = model.data
    time_res = model.data['_time_res'].to_series()

    # FIXME this is very inefficient for y not in y_def_e_eff
    def get_e_eff(m, y, x, t):
        if y in m.y_def_e_eff:
            e_eff = m.e_eff[y, x, t]
        else:  # This includes transmission technologies
            e_eff = d.e_eff.loc[dict(y=y, x=x)][0]  # Just get first entry
        return e_eff

    def get_e_eff_per_distance(model, y, x):
        try:
            e_loss = model.get_option(y + '.constraints_per_distance.e_loss',
                                      x=x)
            per_distance = model.get_option(y + '.per_distance')
            distance = model.get_option(y + '.distance')
            return 1 - (e_loss * (distance / per_distance))
        except exceptions.OptionNotSetError:
            return 1.0

    # Variables
    m.s = po.Var(m.y_pc, m.x, m.t, within=po.NonNegativeReals)
    m.es_prod = po.Var(m.c, m.y, m.x, m.t, within=po.NonNegativeReals)
    m.es_con = po.Var(m.c, m.y, m.x, m.t, within=po.NegativeReals)

    # Constraint rules
    def transmission_rule(m, y, x, t):
        y_remote, x_remote = transmission.get_remotes(y, x)
        if y_remote in m.y_trans:
            c = model.get_option(y + '.carrier')
            return (m.es_prod[c, y, x,
                              t] == -1 * m.es_con[c, y_remote, x_remote, t] *
                    get_e_eff(m, y, x, t) *
                    get_e_eff_per_distance(model, y, x))
        else:
            return po.Constraint.NoConstraint

    def conversion_rule(m, y, x, t):
        c_prod = model.get_option(y + '.carrier')
        c_source = model.get_option(y + '.source_carrier')
        return (m.es_prod[c_prod, y, x, t] == -1 *
                m.es_con[c_source, y, x, t] * get_e_eff(m, y, x, t))

    def pc_rule(m, y, x, t):
        e_eff = get_e_eff(m, y, x, t)
        # TODO once Pyomo supports it,
        # let this update conditionally on param update!
        if po.value(e_eff) == 0:
            e_prod = 0
        else:
            e_prod = sum(m.es_prod[c, y, x, t] for c in m.c) / e_eff
        e_con = sum(m.es_con[c, y, x, t] for c in m.c) * e_eff

        # If this tech is in the set of techs allowing rb, include it
        if y in m.y_rb:
            rbs = m.rbs[y, x, t]
        else:
            rbs = 0

        # A) Case where no storage allowed
        if (model.get_option(y + '.constraints.s_cap.max', x=x) == 0
                and not model.get_option(y + '.constraints.use_s_time', x=x)):
            return m.rs[y, x, t] == e_prod + e_con - rbs

        # B) Case where storage is allowed
        else:
            # Ensure that storage-only techs have no rs
            if y in model.get_group_members('storage'):
                rs = 0
            else:
                rs = m.rs[y, x, t]
            m.rs[y, x, t]
            # set up s_minus_one
            # NB: From Pyomo 3.5 to 3.6, order_dict became zero-indexed
            if m.t.order_dict[t] == 0:
                s_minus_one = m.s_init[y, x]
            else:
                s_loss = model.get_option(y + '.constraints.s_loss', x=x)
                s_minus_one = (((1 - s_loss)**time_res.at[model.prev_t(t)]) *
                               m.s[y, x, model.prev_t(t)])
            return (m.s[y, x, t] == s_minus_one + rs + rbs - e_prod - e_con)

    # Constraints
    m.c_s_balance_transmission = po.Constraint(m.y_trans,
                                               m.x,
                                               m.t,
                                               rule=transmission_rule)
    m.c_s_balance_conversion = po.Constraint(m.y_conv,
                                             m.x,
                                             m.t,
                                             rule=conversion_rule)
    m.c_s_balance_pc = po.Constraint(m.y_pc, m.x, m.t, rule=pc_rule)
Exemple #14
0
def node_costs(model):
    """
    Defines variables:

    * cost: total costs
    * cost_con: construction costs
    * cost_op_fixed: fixed operation costs
    * cost_op_var: variable operation costs
    * cost_op_fuel: primary resource fuel costs
    * cost_op_rb: secondary resource fuel costs

    """
    m = model.m
    time_res = model.data['_time_res'].to_series()
    weights = model.data['_weights'].to_series()

    cost_getter = utils.cost_getter(model.get_option)
    depreciation_getter = utils.depreciation_getter(model.get_option)
    cost_per_distance_getter = utils.cost_per_distance_getter(model.get_option)

    @utils.memoize
    def _depreciation_rate(y, k):
        return depreciation_getter(y, k)

    @utils.memoize
    def _cost(cost, y, k, x=None):
        return cost_getter(cost, y, k, x=x)

    @utils.memoize
    def _cost_per_distance(cost, y, k, x):
        return cost_per_distance_getter(cost, y, k, x)

    # Variables
    m.cost = po.Var(m.y, m.x, m.k, within=po.NonNegativeReals)
    m.cost_con = po.Var(m.y, m.x, m.k, within=po.NonNegativeReals)
    m.cost_op_fixed = po.Var(m.y, m.x, m.k, within=po.NonNegativeReals)
    m.cost_op_variable = po.Var(m.y, m.x, m.k, within=po.NonNegativeReals)
    m.cost_op_var = po.Var(m.y, m.x, m.t, m.k, within=po.NonNegativeReals)
    m.cost_op_fuel = po.Var(m.y, m.x, m.t, m.k, within=po.NonNegativeReals)
    m.cost_op_rb = po.Var(m.y, m.x, m.t, m.k, within=po.NonNegativeReals)

    # Constraint rules
    def c_cost_rule(m, y, x, k):
        return (m.cost[y, x, k] == m.cost_con[y, x, k] +
                m.cost_op_fixed[y, x, k] + m.cost_op_variable[y, x, k])

    def c_cost_con_rule(m, y, x, k):
        if y in m.y_pc:
            cost_s_cap = _cost('s_cap', y, k, x) * m.s_cap[y, x]
        else:
            cost_s_cap = 0

        if y in m.y_def_r:
            cost_r_cap = _cost('r_cap', y, k, x) * m.r_cap[y, x]
            cost_r_area = _cost('r_area', y, k, x) * m.r_area[y, x]
        else:
            cost_r_cap = 0
            cost_r_area = 0

        if y in m.y_trans:
            # Divided by 2 for transmission techs because construction costs
            # are counted at both ends
            cost_e_cap = (_cost('e_cap', y, k, x) +
                          _cost_per_distance('e_cap', y, k, x)) / 2
        else:
            cost_e_cap = _cost('e_cap', y, k, x)

        if y in m.y_rb:
            cost_rb_cap = _cost('rb_cap', y, k, x) * m.rb_cap[y, x]
        else:
            cost_rb_cap = 0

        return (m.cost_con[y, x, k] == _depreciation_rate(y, k) *
                (sum(time_res * weights) / 8760) *
                (cost_s_cap + cost_r_cap + cost_r_area + cost_rb_cap +
                 cost_e_cap * m.e_cap[y, x]))

    def c_cost_op_fixed_rule(m, y, x, k):
        if y in m.y:
            return (m.cost_op_fixed[y, x, k] == _cost('om_frac', y, k, x) *
                    m.cost_con[y, x, k] +
                    (_cost('om_fixed', y, k, x) * m.e_cap[y, x] *
                     (sum(time_res * weights) / 8760)))
        else:
            return m.cost_op_fixed[y, x, k] == 0

    def c_cost_op_variable_rule(m, y, x, k):
        return (m.cost_op_variable[y, x, k] == sum(
            m.cost_op_var[y, x, t, k] + m.cost_op_fuel[y, x, t, k] +
            m.cost_op_rb[y, x, t, k] for t in m.t))

    def c_cost_op_var_rule(m, y, x, t, k):
        # Note: only counting es_prod for operational costs.
        # This should generally be a reasonable assumption to make.
        if y in m.y:
            carrier = model.get_option(y + '.carrier')
            return (m.cost_op_var[y, x, t, k] == _cost('om_var', y, k, x) *
                    weights.loc[t] * m.es_prod[carrier, y, x, t])
        else:
            return m.cost_op_var[y, x, t, k] == 0

    def c_cost_op_fuel_rule(m, y, x, t, k):
        if y in m.y:
            # Dividing by r_eff here so we get the actual r used, not the rs
            # moved into storage...
            return (m.cost_op_fuel[y, x, t, k] == _cost('om_fuel', y, k, x) *
                    weights.loc[t] *
                    (m.rs[y, x, t] /
                     model.get_option(y + '.constraints.r_eff', x=x)))
        else:
            return m.cost_op_fuel[y, x, t, k] == 0

    def c_cost_op_rb_rule(m, y, x, t, k):
        if y in m.y_rb:
            return (m.cost_op_rb[y, x, t, k] == _cost('om_rb', y, k, x) *
                    weights.loc[t] *
                    (m.rbs[y, x, t] /
                     model.get_option(y + '.constraints.rb_eff', x=x)))
        else:
            return m.cost_op_rb[y, x, t, k] == 0

    # Constraints
    m.c_cost = po.Constraint(m.y, m.x, m.k, rule=c_cost_rule)
    m.c_cost_con = po.Constraint(m.y, m.x, m.k, rule=c_cost_con_rule)
    m.c_cost_op_fixed = po.Constraint(m.y, m.x, m.k, rule=c_cost_op_fixed_rule)
    m.c_cost_op_variable = po.Constraint(m.y,
                                         m.x,
                                         m.k,
                                         rule=c_cost_op_variable_rule)
    m.c_cost_op_var = po.Constraint(m.y,
                                    m.x,
                                    m.t,
                                    m.k,
                                    rule=c_cost_op_var_rule)
    m.c_cost_op_fuel = po.Constraint(m.y,
                                     m.x,
                                     m.t,
                                     m.k,
                                     rule=c_cost_op_fuel_rule)
    m.c_cost_op_rb = po.Constraint(m.y, m.x, m.t, m.k, rule=c_cost_op_rb_rule)
Exemple #15
0
def initialize_decision_variables(backend_model):
    """
    Defines decision variables.

    ==================== ========================================
    Variable             Dimensions
    ==================== ========================================
    energy_cap           loc_techs
    carrier_prod         loc_tech_carriers_prod, timesteps
    carrier_con          loc_tech_carriers_con, timesteps
    cost                 costs, loc_techs_cost
    resource_area        loc_techs_area,
    storage_cap          loc_techs_store
    storage              loc_techs_store, timesteps
    resource_con         loc_techs_supply_plus, timesteps
    resource_cap         loc_techs_supply_plus
    carrier_export       loc_tech_carriers_export, timesteps
    cost_var             costs, loc_techs_om_cost, timesteps
    cost_investment      costs, loc_techs_investment_cost
    purchased            loc_techs_purchase
    units                loc_techs_milp
    operating\\_units     loc_techs_milp, timesteps
    unmet\\_demand        loc_carriers, timesteps
    unused\\_supply       loc_carriers, timesteps
    ==================== ========================================

    """
    model_data_dict = backend_model.__calliope_model_data
    run_config = backend_model.__calliope_run_config

    ##
    # Variables which are always assigned
    ##
    if run_config['mode'] != 'operate':
        backend_model.energy_cap = po.Var(backend_model.loc_techs, within=po.NonNegativeReals)
    backend_model.carrier_prod = po.Var(backend_model.loc_tech_carriers_prod, backend_model.timesteps, within=po.NonNegativeReals)
    backend_model.carrier_con = po.Var(backend_model.loc_tech_carriers_con, backend_model.timesteps, within=po.NegativeReals)
    backend_model.cost = po.Var(backend_model.costs, backend_model.loc_techs_cost, within=po.Reals)

    ##
    # Conditionally assigned variables
    ##

    if 'loc_techs_area' in model_data_dict['sets'] and run_config['mode'] != 'operate':
        backend_model.resource_area = po.Var(backend_model.loc_techs_area, within=po.NonNegativeReals)

    if 'loc_techs_store' in model_data_dict['sets']:
        if run_config['mode'] != 'operate':
            backend_model.storage_cap = po.Var(backend_model.loc_techs_store, within=po.NonNegativeReals)
        if hasattr(backend_model, 'clusters') and hasattr(backend_model, 'datesteps'):
            backend_model.storage_inter_cluster = po.Var(backend_model.loc_techs_store, backend_model.datesteps, within=po.NonNegativeReals)
            backend_model.storage_intra_cluster_max = po.Var(backend_model.loc_techs_store, backend_model.clusters, within=po.Reals)
            backend_model.storage_intra_cluster_min = po.Var(backend_model.loc_techs_store, backend_model.clusters, within=po.Reals)
            storage_within = po.Reals
        else:
            storage_within = po.NonNegativeReals
        backend_model.storage = po.Var(backend_model.loc_techs_store, backend_model.timesteps, within=storage_within)

    if 'loc_techs_supply_plus' in model_data_dict['sets']:
        backend_model.resource_con = po.Var(backend_model.loc_techs_supply_plus, backend_model.timesteps, within=po.NonNegativeReals)
        if run_config['mode'] != 'operate':
            backend_model.resource_cap = po.Var(backend_model.loc_techs_supply_plus, within=po.NonNegativeReals)

    if 'loc_techs_export' in model_data_dict['sets']:
        backend_model.carrier_export = po.Var(backend_model.loc_tech_carriers_export, backend_model.timesteps, within=po.NonNegativeReals)

    if 'loc_techs_om_cost' in model_data_dict['sets']:
        backend_model.cost_var = po.Var(backend_model.costs, backend_model.loc_techs_om_cost, backend_model.timesteps, within=po.Reals)

    if 'loc_techs_investment_cost' in model_data_dict['sets'] and run_config['mode'] != 'operate':
        backend_model.cost_investment = po.Var(backend_model.costs, backend_model.loc_techs_investment_cost, within=po.Reals)

    if 'loc_techs_purchase' in model_data_dict['sets'] and run_config['mode'] != 'operate':
        backend_model.purchased = po.Var(backend_model.loc_techs_purchase, within=po.Binary)

    if 'group_demand_share_per_timestep_decision' in model_data_dict['data'] and run_config['mode'] != 'operate':
        backend_model.demand_share_per_timestep_decision = po.Var(backend_model.loc_tech_carriers_prod, within=po.NonNegativeReals)

    if 'loc_techs_milp' in model_data_dict['sets']:
        if run_config['mode'] != 'operate':
            backend_model.units = po.Var(backend_model.loc_techs_milp, within=po.NonNegativeIntegers)
        backend_model.operating_units = po.Var(backend_model.loc_techs_milp, backend_model.timesteps, within=po.NonNegativeIntegers)
        # For any milp tech, we need to update energy_cap, as energy_cap_max and energy_cap_equals
        # are replaced by energy_cap_per_unit
        if run_config['mode'] == 'operate':
            for k, v in backend_model.units.items():
                backend_model.energy_cap[k] = v * backend_model.energy_cap_per_unit[k]

    if 'loc_techs_asynchronous_prod_con' in model_data_dict['sets']:
        backend_model.prod_con_switch = po.Var(backend_model.loc_techs_asynchronous_prod_con, backend_model.timesteps, within=po.Binary)
        backend_model.bigM = run_config.get('bigM', 1e10)

    if run_config.get('ensure_feasibility', False):
        backend_model.unmet_demand = po.Var(backend_model.loc_carriers, backend_model.timesteps, within=po.NonNegativeReals)
        backend_model.unused_supply = po.Var(backend_model.loc_carriers, backend_model.timesteps, within=po.NegativeReals)
        backend_model.bigM = run_config.get('bigM', 1e10)
Exemple #16
0
def initialize_decision_variables(backend_model):
    """
    Defines decision variables.

    ==================== ========================================
    Variable             Dimensions
    ==================== ========================================
    energy_cap           loc_techs
    carrier_prod         loc_tech_carriers_prod, timesteps
    carrier_con          loc_tech_carriers_con, timesteps
    cost                 costs, loc_techs_cost
    resource_area        loc_techs_area,
    storage_cap          loc_techs_store
    storage              loc_techs_store, timesteps
    resource_con         loc_techs_supply_plus, timesteps
    resource_cap         loc_techs_supply_plus
    carrier_export       loc_tech_carriers_export, timesteps
    cost_var             costs, loc_techs_om_cost, timesteps
    cost_investment      costs, loc_techs_investment_cost
    purchased            loc_techs_purchase
    units                loc_techs_milp
    operating\_units     loc_techs_milp, timesteps
    unmet\_demand        loc_carriers, timesteps
    ==================== ========================================

    """
    model_data_dict = backend_model.__calliope_model_data__

    ##
    # Variables which are always assigned
    ##
    if backend_model.mode != 'operate':
        backend_model.energy_cap = po.Var(backend_model.loc_techs,
                                          within=po.NonNegativeReals)
    backend_model.carrier_prod = po.Var(backend_model.loc_tech_carriers_prod,
                                        backend_model.timesteps,
                                        within=po.NonNegativeReals)
    backend_model.carrier_con = po.Var(backend_model.loc_tech_carriers_con,
                                       backend_model.timesteps,
                                       within=po.NegativeReals)
    backend_model.cost = po.Var(backend_model.costs,
                                backend_model.loc_techs_cost,
                                within=po.Reals)

    ##
    # Conditionally assigned variables
    ##

    if 'loc_techs_area' in model_data_dict[
            'sets'] and backend_model.mode != 'operate':
        backend_model.resource_area = po.Var(backend_model.loc_techs_area,
                                             within=po.NonNegativeReals)

    if 'loc_techs_store' in model_data_dict['sets']:
        if backend_model.mode != 'operate':
            backend_model.storage_cap = po.Var(backend_model.loc_techs_store,
                                               within=po.NonNegativeReals)
        backend_model.storage = po.Var(backend_model.loc_techs_store,
                                       backend_model.timesteps,
                                       within=po.NonNegativeReals)

    if 'loc_techs_supply_plus' in model_data_dict['sets']:
        backend_model.resource_con = po.Var(
            backend_model.loc_techs_supply_plus,
            backend_model.timesteps,
            within=po.Reals)
        if backend_model.mode != 'operate':
            backend_model.resource_cap = po.Var(
                backend_model.loc_techs_supply_plus,
                within=po.NonNegativeReals)

    if 'loc_techs_export' in model_data_dict['sets']:
        backend_model.carrier_export = po.Var(
            backend_model.loc_tech_carriers_export,
            backend_model.timesteps,
            within=po.NonNegativeReals)

    if 'loc_techs_om_cost' in model_data_dict['sets']:
        backend_model.cost_var = po.Var(backend_model.costs,
                                        backend_model.loc_techs_om_cost,
                                        backend_model.timesteps,
                                        within=po.Reals)

    if 'loc_techs_investment_cost' in model_data_dict[
            'sets'] and backend_model.mode != 'operate':
        backend_model.cost_investment = po.Var(
            backend_model.costs,
            backend_model.loc_techs_investment_cost,
            within=po.Reals)

    if 'loc_techs_purchase' in model_data_dict[
            'sets'] and backend_model.mode != 'operate':
        backend_model.purchased = po.Var(backend_model.loc_techs_purchase,
                                         within=po.Binary)

    if 'loc_techs_milp' in model_data_dict['sets']:
        if backend_model.mode != 'operate':
            backend_model.units = po.Var(backend_model.loc_techs_milp,
                                         within=po.NonNegativeIntegers)
        backend_model.operating_units = po.Var(backend_model.loc_techs_milp,
                                               backend_model.timesteps,
                                               within=po.NonNegativeIntegers)
        # For any milp tech, we need to update energy_cap, as energy_cap_max and energy_cap_equals
        # are replaced by energy_cap_per_unit
        if backend_model.mode == 'operate':
            for k, v in backend_model.units.items():
                backend_model.energy_cap[
                    k] = v * backend_model.energy_cap_per_unit[k]

    if model_data_dict['attrs'].get('run.ensure_feasibility', False):
        backend_model.unmet_demand = po.Var(backend_model.loc_carriers,
                                            backend_model.timesteps,
                                            within=po.NonNegativeReals)
        backend_model.bigM = model_data_dict['attrs'].get('run.bigM')
Exemple #17
0
def create_model(ems_local):
    """ create one optimization instance and parameterize it with the input data in ems model
    Args:
        - ems_local:  ems model which has been parameterized

    Return:
        - m: optimization model instance created according to ems model
    """
    # record the time
    t0 = tm.time()
    # get all the data from the external file
    # ems_local = ems_loc(initialize=True, path='C:/Users/ge57vam/emsflex/opentumflex/ems01_ems.txt')
    devices = ems_local['devices']

    # read data from excel file

    # print('Data Read. time: ' + "{:.1f}".format(tm.time() - t0) + ' s\n')
    # print('Prepare Data ...\n')
    t = tm.time()
    time_interval = ems_local['time_data'][
        't_inval']  # x minutes for one time step
    # write in the time series from the data
    df_time_series = ems_local['fcst']
    time_series = pd.DataFrame.from_dict(df_time_series)
    # time = time_series.index.values

    # print('Data Prepared. time: ' + "{:.1f}".format(tm.time() - t0) + ' s\n')

    # system
    # get the initial time step
    # time_step_initial = parameter.loc['System']['value']
    time_step_initial = ems_local['time_data']['isteps']
    # time_step_end = int(60 / time_interval * 24)
    time_step_end = ems_local['time_data']['nsteps']
    timesteps = np.arange(time_step_initial, time_step_end)
    # timestep_1 = timesteps[0]

    # timesteps = timesteps_all[time_step_initial:time_step_end]
    t_dn = 4
    # 6*time_step_end/96
    t_up = 4
    # 6*time_step_end/96
    timesteps_dn = timesteps[time_step_initial + 1:time_step_end - t_dn]
    timesteps_up = timesteps[time_step_initial + 1:time_step_end - t_up]

    # 15 min for every timestep/ timestep by one hour
    # create the concrete model
    p2e = time_interval / 60

    # create the model object m
    m = pyen.ConcreteModel()

    # create the parameter
    # print('Define Model ...\n')

    m.t = pyen.Set(ordered=True, initialize=timesteps)
    m.t_DN = pyen.Set(ordered=True, initialize=timesteps_dn)
    m.t_UP = pyen.Set(ordered=True, initialize=timesteps_up)

    # heat_storage
    sto_param = devices['sto']
    m.sto_max_cont = pyen.Param(initialize=sto_param['stocap'])
    m.SOC_init = pyen.Param(initialize=sto_param['initSOC'])
    m.temp_min = pyen.Param(initialize=sto_param['mintemp'])
    m.temp_max = pyen.Param(initialize=sto_param['maxtemp'])

    # battery
    bat_param = devices['bat']
    m.bat_cont_max = pyen.Param(initialize=bat_param['stocap'])
    m.bat_SOC_init = pyen.Param(initialize=bat_param['initSOC'])
    m.bat_power_max = pyen.Param(initialize=bat_param['maxpow'])
    m.bat_eta = pyen.Param(initialize=bat_param['eta'])

    # heat pump
    hp_param = devices['hp']
    hp_elec_cap = pd.DataFrame.from_dict(hp_param['powmap'])
    hp_cop = pd.DataFrame.from_dict(hp_param['COP'])
    hp_supply_temp = hp_param['supply_temp']
    m.hp_ther_pow = pyen.Param(m.t,
                               initialize=1,
                               mutable=True,
                               within=pyen.NonNegativeReals)
    m.hp_COP = pyen.Param(m.t,
                          initialize=1,
                          mutable=True,
                          within=pyen.NonNegativeReals)
    m.hp_elec_pow = pyen.Param(m.t,
                               initialize=1,
                               mutable=True,
                               within=pyen.NonNegativeReals)
    m.T_DN = pyen.Param(initialize=t_dn, mutable=True)
    m.T_UP = pyen.Param(initialize=t_up, mutable=True)
    m.hp_themInertia = pyen.Param(initialize=hp_param['thermInertia'])
    m.hp_minTemp = pyen.Param(initialize=hp_param['minTemp'])
    m.hp_maxTemp = pyen.Param(initialize=hp_param['maxTemp'])
    m.hp_heatgain = pyen.Param(initialize=hp_param['heatgain'])

    # elec_vehicle
    ev_param = devices['ev']
    ev_aval = ev_param['aval']
    m.ev_min_pow = pyen.Param(initialize=ev_param['minpow'])
    m.ev_max_pow = pyen.Param(initialize=ev_param['maxpow'])
    m.ev_sto_cap = pyen.Param(initialize=ev_param['stocap'])
    m.ev_eta = pyen.Param(initialize=ev_param['eta'])
    m.ev_aval = pyen.Param(m.t, initialize=1, mutable=True)
    m.ev_charg_amount = m.ev_sto_cap * (ev_param['endSOC'][-1] -
                                        ev_param['initSOC'][0]) / 100
    ev_soc_init = ev_param['initSOC']
    ev_soc_end = ev_param['endSOC']
    ev_init_soc_check = ev_param['init_soc_check']
    ev_end_soc_check = ev_param['end_soc_check']

    # boilder
    boil_param = devices['boiler']
    m.boiler_max_cap = pyen.Param(initialize=boil_param['maxpow'])
    m.boiler_eff = pyen.Param(initialize=boil_param['eta'])

    # CHP
    chp_param = devices['chp']
    m.chp_elec_effic = pyen.Param(m.t, initialize=chp_param['eta'][0])
    m.chp_ther_effic = pyen.Param(m.t, initialize=chp_param['eta'][1])
    m.chp_elec_run = pyen.Param(m.t, initialize=chp_param['maxpow'])
    m.chp_heat_run = pyen.Param(m.t, initialize=0, mutable=True)
    m.chp_gas_run = pyen.Param(m.t, initialize=0, mutable=True)

    # solar
    pv_param = devices['pv']
    # m.pv_effic = pyen.Param(initialize=pv_param['eta'])
    m.pv_peak_power = pyen.Param(initialize=pv_param['maxpow'])
    m.solar = pyen.Param(m.t, initialize=1, mutable=True)

    #    for t in m.t_UP:
    #        m.t_dn[t] = t_dn
    #        m.t_up[t] = t_dn

    # price
    m.ele_price_in, m.ele_price_out, m.gas_price = (pyen.Param(m.t,
                                                               initialize=1,
                                                               mutable=True)
                                                    for i in range(3))

    # lastprofil
    m.lastprofil_heat, m.lastprofil_elec = (pyen.Param(m.t,
                                                       initialize=1,
                                                       mutable=True)
                                            for i in range(2))

    for t in m.t:
        # weather data
        m.ele_price_in[t] = time_series.loc[t]['ele_price_in']
        m.gas_price[t] = time_series.loc[t]['gas_price']
        m.ele_price_out[t] = time_series.loc[t]['ele_price_out']
        m.lastprofil_heat[t] = time_series.loc[t]['load_heat']
        m.lastprofil_elec[t] = time_series.loc[t]['load_elec']
        m.solar[t] = time_series.loc[t]['solar_power']
        # fill the ev availability
        m.ev_aval[t] = ev_aval[t]
        # calculate the spline function for thermal power of heat pump
        spl_elec_pow = UnivariateSpline(
            list(map(float, hp_elec_cap.columns.values)),
            list(hp_elec_cap.loc[hp_supply_temp, :]))
        m.hp_elec_pow[t] = spl_elec_pow(time_series.loc[t]['temperature'] +
                                        273.15).item(0)
        # calculate the spline function for COP of heat pump
        spl_cop = UnivariateSpline(list(map(float, hp_cop.columns.values)),
                                   list(hp_cop.loc[hp_supply_temp, :]))
        m.hp_COP[t] = spl_cop(time_series.loc[t]['temperature'] +
                              273.15).item(0)
        m.hp_ther_pow[t] = m.hp_elec_pow[t] * m.hp_COP[t]
        # calculate the chp electric and thermal power when it's running
        m.chp_heat_run[
            t] = m.chp_elec_run[t] / m.chp_elec_effic[t] * m.chp_ther_effic[t]
        m.chp_gas_run[t] = m.chp_elec_run[t] / m.chp_elec_effic[t]

    # m.ele_price = ele_price

    # Variables

    m.hp_run = pyen.Var(m.t,
                        within=pyen.Boolean,
                        doc='operation of the heat pump')
    m.CHP_run = pyen.Var(m.t, within=pyen.Boolean, doc='operation of the CHP')

    m.ev_power = pyen.Var(m.t,
                          within=pyen.NonNegativeReals,
                          bounds=(ev_param['minpow'], ev_param['maxpow']),
                          doc='power of the EV')
    m.boiler_cap, m.PV_cap, m.elec_import, m.elec_export, m.bat_cont, m.sto_e_cont, m.bat_pow_pos, m.bat_pow_neg, \
    m.ev_cont, m.ev_var_pow, m.soc_diff, m.roomtemp = (pyen.Var(m.t, within=pyen.NonNegativeReals) for i in range(12))
    m.sto_e_pow, m.costs, m.heatextra = (pyen.Var(m.t, within=pyen.Reals)
                                         for i in range(3))

    # Constrains

    # heat_storage
    def sto_e_cont_def_rule(m, t):
        if t > m.t[1]:
            return m.sto_e_cont[t] == m.sto_e_cont[t -
                                                   1] + m.sto_e_pow[t] * p2e
        else:
            return m.sto_e_cont[
                t] == m.sto_max_cont * m.SOC_init / 100 + m.sto_e_pow[t] * p2e

    m.sto_e_cont_def = pyen.Constraint(m.t,
                                       rule=sto_e_cont_def_rule,
                                       doc='heat_storage_balance')

    def heat_balance_rule(m, t):
        return m.boiler_cap[t] + m.CHP_run[t] * m.chp_heat_run[t] + \
               m.hp_run[t] * m.hp_ther_pow[t] - m.lastprofil_heat[t] - m.sto_e_pow[t] == 0

    m.heat_power_balance = pyen.Constraint(m.t,
                                           rule=heat_balance_rule,
                                           doc='heat_storage_balance')

    # the room in the building
    # def heat_room_rule(m, t):
    #     if t > m.t[1]:
    #         return m.roomtemp[t] == m.roomtemp[t - 1] + (m.heatextra[t] + m.hp_heatgain) / m.hp_themInertia
    #     else:
    #         return m.roomtemp[t] == 23
    #
    # m.heat_room_balance = pyen.Constraint(m.t, rule=heat_room_rule, doc='heat_room_balance')
    #
    # def heat_room_maxtemp_rule(m, t):
    #     return m.roomtemp[t] <= m.hp_maxTemp
    #
    # m.heat_room_maxtemp = pyen.Constraint(m.t, rule=heat_room_maxtemp_rule)
    #
    # def heat_room_mintemp_rule(m, t):
    #     return m.roomtemp[t] >= m.hp_minTemp
    #
    # m.heat_room_mintemp = pyen.Constraint(m.t, rule=heat_room_mintemp_rule)
    #
    # def heat_room_end_rule(m, t):
    #     if t == m.t[-1]:
    #         return m.roomtemp[t] == 23
    #     else:
    #         return Constraint.Skip
    #
    # m.heat_room_end = pyen.Constraint(m.t, rule=heat_room_end_rule)

    # battery
    def battery_e_cont_def_rule(m, t):
        if t > m.t[1]:
            return m.bat_cont[t] == m.bat_cont[
                t - 1] + (m.bat_pow_pos[t] * m.bat_eta -
                          m.bat_pow_neg[t] / m.bat_eta) * p2e
        else:
            return m.bat_cont[t] == m.bat_cont_max * m.bat_SOC_init / 100 + (
                m.bat_pow_pos[t] * m.bat_eta -
                m.bat_pow_neg[t] / m.bat_eta) * p2e

    m.bat_e_cont_def = pyen.Constraint(m.t,
                                       rule=battery_e_cont_def_rule,
                                       doc='battery_balance')

    def elec_balance_rule(m, t):
        return m.elec_import[t] + m.CHP_run[t] * m.chp_elec_run[t] + m.PV_cap[t] * m.solar[t] - \
               m.elec_export[t] - m.hp_run[t] * m.hp_elec_pow[t] - m.lastprofil_elec[t] - \
               (m.bat_pow_pos[t] - m.bat_pow_neg[t]) - m.ev_power[t] == 0

    m.elec_power_balance = pyen.Constraint(m.t,
                                           rule=elec_balance_rule,
                                           doc='elec_balance')

    def cost_sum_rule(m, t):
        return m.costs[t] == p2e * (
            m.boiler_cap[t] / m.boiler_eff * m.gas_price[t] +
            m.CHP_run[t] * m.chp_gas_run[t] * m.gas_price[t] +
            m.elec_import[t] * m.ele_price_in[t] -
            m.elec_export[t] * m.ele_price_out[t]) + m.soc_diff[t] * 1000

    m.cost_sum = pyen.Constraint(m.t, rule=cost_sum_rule)

    # ev battery balance
    def ev_cont_def_rule(m, t):
        if t > m.t[1]:
            return m.ev_cont[t] == m.ev_cont[
                t - 1] + m.ev_power[t] * p2e * m.ev_eta - m.ev_var_pow[t]
        else:
            return m.ev_cont[t] == m.ev_sto_cap * ev_soc_init[
                0] / 100 + m.ev_power[t] * p2e * m.ev_eta

    m.ev_cont_def = pyen.Constraint(m.t,
                                    rule=ev_cont_def_rule,
                                    doc='EV_balance')

    def EV_end_soc_rule(m, t):
        return m.ev_cont[
            t] >= m.ev_sto_cap * ev_end_soc_check[t] / 100 - m.soc_diff[t]

    m.EV_end_soc_def = pyen.Constraint(m.t, rule=EV_end_soc_rule)

    def EV_init_soc_rule(m, t):
        return m.ev_cont[t] <= m.ev_sto_cap * ev_init_soc_check[t] / 100

    m.EV_init_soc_def = pyen.Constraint(m.t, rule=EV_init_soc_rule)

    def EV_aval_rule(m, t):
        return m.ev_power[t] <= m.ev_aval[t] * m.ev_max_pow

    m.EV_aval_def = pyen.Constraint(m.t, rule=EV_aval_rule)

    # hp
    def hp_min_still_t_rule(m, t):
        return (m.hp_run[t - 1] - m.hp_run[t]) * m.T_DN <= m.T_DN - (
            m.hp_run[t] + m.hp_run[t + 1] + m.hp_run[t + 2] + m.hp_run[t + 3])

    # m.hp_min_still_t_def = pyen.Constraint(m.t_DN, rule=hp_min_still_t_rule)

    def hp_min_lauf_t_rule(m, t):
        return (m.hp_run[t] - m.hp_run[t - 1]) * m.T_UP <= m.hp_run[t] + m.hp_run[t + 1] \
               + m.hp_run[t + 2] + m.hp_run[t + 3]

    # m.hp_min_lauf_t_def = pyen.Constraint(m.t_UP, rule=hp_min_lauf_t_rule)

    def chp_min_still_t_rule(m, t):
        return (m.CHP_run[t - 1] - m.CHP_run[t]) * m.T_DN <= m.T_DN - (
            m.CHP_run[t] + m.CHP_run[t + 1])

    # m.chp_min_still_t_def = pyen.Constraint(m.t_DN, rule=chp_min_still_t_rule)

    def chp_min_lauf_t_rule(m, t):
        return (m.CHP_run[t] -
                m.CHP_run[t - 1]) * m.T_UP <= m.CHP_run[t] + m.CHP_run[t + 1]

    # m.chp_min_lauf_t_def = pyen.Constraint(m.t_UP, rule=chp_min_lauf_t_rule)

    # boiler
    def boiler_max_cap_rule(m, t):
        return m.boiler_cap[t] <= m.boiler_max_cap

    m.boiler_max_cap_def = pyen.Constraint(m.t, rule=boiler_max_cap_rule)

    # PV
    def pv_max_cap_rule(m, t):
        return m.PV_cap[t] <= m.pv_peak_power

    m.pv_max_cap_def = pyen.Constraint(m.t, rule=pv_max_cap_rule)

    # elec_import
    def elec_import_rule(m, t):
        return m.elec_import[t] <= 50 * 5000

    m.elec_import_def = pyen.Constraint(m.t, rule=elec_import_rule)

    # elec_export
    def elec_export_rule(m, t):
        return m.elec_export[t] <= 50 * 5000

    m.elec_export_def = pyen.Constraint(m.t, rule=elec_export_rule)

    # storage
    # storage content
    if m.sto_max_cont > 0:

        def sto_e_cont_min_rule(m, t):
            return m.sto_e_cont[t] / m.sto_max_cont >= 0.1

        m.sto_e_cont_min = pyen.Constraint(m.t, rule=sto_e_cont_min_rule)

        def sto_e_cont_max_rule(m, t):
            return m.sto_e_cont[t] / m.sto_max_cont <= 0.9

        m.sto_e_cont_max = pyen.Constraint(m.t, rule=sto_e_cont_max_rule)
    if m.bat_cont_max > 0:

        def bat_e_cont_min_rule(m, t):
            return m.bat_cont[t] / m.bat_cont_max >= 0.1

        m.bat_e_cont_min = pyen.Constraint(m.t, rule=bat_e_cont_min_rule)

        def bat_e_cont_max_rule(m, t):
            return m.bat_cont[t] / m.bat_cont_max <= 0.9

        m.bat_e_cont_max = pyen.Constraint(m.t, rule=bat_e_cont_max_rule)

    # storage power

    def sto_e_max_pow_rule_1(m, t):
        return m.sto_e_pow[t] <= m.sto_max_cont

    m.sto_e_pow_max_1 = pyen.Constraint(m.t, rule=sto_e_max_pow_rule_1)

    def sto_e_max_pow_rule_2(m, t):
        return m.sto_e_pow[t] >= -m.sto_max_cont

    m.sto_e_pow_max_2 = pyen.Constraint(m.t, rule=sto_e_max_pow_rule_2)

    def bat_e_max_pow_rule_1(m, t):
        return m.bat_pow_pos[t] <= min(m.bat_power_max, m.bat_cont_max)

    m.bat_e_pow_max_1 = pyen.Constraint(m.t, rule=bat_e_max_pow_rule_1)

    def bat_e_max_pow_rule_2(m, t):
        return m.bat_pow_neg[t] <= min(m.bat_power_max, m.bat_cont_max)

    m.bat_e_pow_max_2 = pyen.Constraint(m.t, rule=bat_e_max_pow_rule_2)

    # end state of storage and battery
    m.sto_e_cont_end = pyen.Constraint(
        expr=(m.sto_e_cont[m.t[-1]] >= 0.5 * m.sto_max_cont))
    m.bat_e_cont_end = pyen.Constraint(
        expr=(m.bat_cont[m.t[-1]] >= 0.5 * m.bat_cont_max))

    def obj_rule(m):
        # Return sum of total costs over all cost types.
        # Simply calculates the sum of m.costs over all m.cost_types.
        return pyen.summation(m.costs)

    m.obj = pyen.Objective(sense=pyen.minimize,
                           rule=obj_rule,
                           doc='Sum costs by cost type')

    return m
Exemple #18
0
def create_model(data, dt=1, timesteps=None, objective='cost', dual=True):
    """Create a pyomo ConcreteModel urbs object from given input data.

    Args:
        - data: a dict of up to 12
        - dt: timestep duration in hours (default: 1)
        - timesteps: optional list of timesteps, default: demand timeseries
        - objective: Either "cost" or "CO2" for choice of objective function,
          default: "cost"
        - dual: set True to add dual variables to model output
          (marginally slower), default: True

    Returns:
        a pyomo ConcreteModel object
    """

    # Optional
    if not timesteps:
        timesteps = data['demand'].index.tolist()
    m = pyomo_model_prep(data, timesteps)  # preparing pyomo model
    m.name = 'urbs'
    m.created = datetime.now().strftime('%Y%m%dT%H%M')
    m._data = data

    # Parameters

    # weight = length of year (hours) / length of simulation (hours)
    # weight scales costs and emissions from length of simulation to a full
    # year, making comparisons among cost types (invest is annualized, fixed
    # costs are annual by default, variable costs are scaled by weight) and
    # among different simulation durations meaningful.
    m.weight = pyomo.Param(
        initialize=float(8760) / (len(m.timesteps) * dt),
        doc='Pre-factor for variable costs and emissions for an annual result')

    # dt = spacing between timesteps. Required for storage equation that
    # converts between energy (storage content, e_sto_con) and power (all other
    # quantities that start with "e_")
    m.dt = pyomo.Param(initialize=dt,
                       doc='Time step duration (in hours), default: 1')

    # import objective function information
    m.obj = pyomo.Param(
        initialize=objective,
        doc='Specification of minimized quantity, default: "cost"')

    # Sets
    # ====
    # Syntax: m.{name} = Set({domain}, initialize={values})
    # where name: set name
    #       domain: set domain for tuple sets, a cartesian set product
    #       values: set values, a list or array of element tuples

    # generate ordered time step sets
    m.t = pyomo.Set(initialize=m.timesteps,
                    ordered=True,
                    doc='Set of timesteps')

    # modelled (i.e. excluding init time step for storage) time steps
    m.tm = pyomo.Set(within=m.t,
                     initialize=m.timesteps[1:],
                     ordered=True,
                     doc='Set of modelled timesteps')

    # support timeframes (e.g. 2020, 2030...)
    indexlist = set()
    for key in m.commodity_dict["price"]:
        indexlist.add(tuple(key)[0])
    m.stf = pyomo.Set(initialize=indexlist,
                      doc='Set of modeled support timeframes (e.g. years)')

    # site (e.g. north, middle, south...)
    indexlist = set()
    for key in m.commodity_dict["price"]:
        indexlist.add(tuple(key)[1])
    m.sit = pyomo.Set(initialize=indexlist, doc='Set of sites')

    # commodity (e.g. solar, wind, coal...)
    indexlist = set()
    for key in m.commodity_dict["price"]:
        indexlist.add(tuple(key)[2])
    m.com = pyomo.Set(initialize=indexlist, doc='Set of commodities')

    # commodity type (i.e. SupIm, Demand, Stock, Env)
    indexlist = set()
    for key in m.commodity_dict["price"]:
        indexlist.add(tuple(key)[3])
    m.com_type = pyomo.Set(initialize=indexlist, doc='Set of commodity types')

    # process (e.g. Wind turbine, Gas plant, Photovoltaics...)
    indexlist = set()
    for key in m.process_dict["inv-cost"]:
        indexlist.add(tuple(key)[2])
    m.pro = pyomo.Set(initialize=indexlist, doc='Set of conversion processes')

    # cost_type
    m.cost_type = pyomo.Set(initialize=m.cost_type_list,
                            doc='Set of cost types (hard-coded)')

    # tuple sets
    m.sit_tuples = pyomo.Set(
        within=m.stf * m.sit,
        initialize=tuple(m.site_dict["area"].keys()),
        doc='Combinations of support timeframes and sites')
    m.com_tuples = pyomo.Set(
        within=m.stf * m.sit * m.com * m.com_type,
        initialize=tuple(m.commodity_dict["price"].keys()),
        doc='Combinations of defined commodities, e.g. (2018,Mid,Elec,Demand)')
    m.pro_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro,
        initialize=tuple(m.process_dict["inv-cost"].keys()),
        doc='Combinations of possible processes, e.g. (2018,North,Coal plant)')
    m.com_stock = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Stock'),
        doc='Commodities that can be purchased at some site(s)')

    if m.mode['int']:
        # tuples for operational status of technologies
        m.operational_pro_tuples = pyomo.Set(
            within=m.sit * m.pro * m.stf * m.stf,
            initialize=[(sit, pro, stf, stf_later)
                        for (sit, pro, stf,
                             stf_later) in op_pro_tuples(m.pro_tuples, m)],
            doc='Processes that are still operational through stf_later'
            '(and the relevant years following), if built in stf'
            'in stf.')

        # tuples for rest lifetime of installed capacities of technologies
        m.inst_pro_tuples = pyomo.Set(
            within=m.sit * m.pro * m.stf,
            initialize=[(sit, pro, stf)
                        for (sit, pro, stf) in inst_pro_tuples(m)],
            doc='Installed processes that are still operational through stf')

    # commodity type subsets
    m.com_supim = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'SupIm'),
        doc='Commodities that have intermittent (timeseries) input')
    m.com_demand = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Demand'),
        doc='Commodities that have a demand (implies timeseries)')
    m.com_env = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Env'),
        doc='Commodities that (might) have a maximum creation limit')

    # process tuples for area rule
    m.pro_area_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro,
        initialize=tuple(m.proc_area_dict.keys()),
        doc='Processes and Sites with area Restriction')

    # process input/output
    m.pro_input_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro * m.com,
        initialize=[(stf, site, process, commodity)
                    for (stf, site, process) in m.pro_tuples
                    for (s, pro, commodity) in tuple(m.r_in_dict.keys())
                    if process == pro and s == stf],
        doc='Commodities consumed by process by site,'
        'e.g. (2020,Mid,PV,Solar)')
    m.pro_output_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro * m.com,
        initialize=[(stf, site, process, commodity)
                    for (stf, site, process) in m.pro_tuples
                    for (s, pro, commodity) in tuple(m.r_out_dict.keys())
                    if process == pro and s == stf],
        doc='Commodities produced by process by site, e.g. (2020,Mid,PV,Elec)')

    # process tuples for maximum gradient feature
    m.pro_maxgrad_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro,
        initialize=[(stf, sit, pro) for (stf, sit, pro) in m.pro_tuples
                    if m.process_dict['max-grad'][stf, sit, pro] < 1.0 / dt],
        doc='Processes with maximum gradient smaller than timestep length')

    # process tuples for partial feature
    m.pro_partial_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro,
        initialize=[(stf, site, process)
                    for (stf, site, process) in m.pro_tuples
                    for (s, pro, _) in tuple(m.r_in_min_fraction_dict.keys())
                    if process == pro and s == stf],
        doc='Processes with partial input')

    m.pro_partial_input_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro * m.com,
        initialize=[(stf, site, process, commodity)
                    for (stf, site, process) in m.pro_partial_tuples
                    for (s, pro,
                         commodity) in tuple(m.r_in_min_fraction_dict.keys())
                    if process == pro and s == stf],
        doc='Commodities with partial input ratio,'
        'e.g. (2020,Mid,Coal PP,Coal)')

    m.pro_partial_output_tuples = pyomo.Set(
        within=m.stf * m.sit * m.pro * m.com,
        initialize=[(stf, site, process, commodity)
                    for (stf, site, process) in m.pro_partial_tuples
                    for (s, pro,
                         commodity) in tuple(m.r_out_min_fraction_dict.keys())
                    if process == pro and s == stf],
        doc='Commodities with partial input ratio, e.g. (Mid,Coal PP,CO2)')

    # Variables

    # costs
    m.costs = pyomo.Var(m.cost_type,
                        within=pyomo.Reals,
                        doc='Costs by type (EUR/a)')

    # commodity
    m.e_co_stock = pyomo.Var(
        m.tm,
        m.com_tuples,
        within=pyomo.NonNegativeReals,
        doc='Use of stock commodity source (MW) per timestep')

    # process
    m.cap_pro_new = pyomo.Var(m.pro_tuples,
                              within=pyomo.NonNegativeReals,
                              doc='New process capacity (MW)')

    # process capacity as expression object
    # (variable if expansion is possible, else static)
    m.cap_pro = pyomo.Expression(m.pro_tuples,
                                 rule=def_process_capacity_rule,
                                 doc='total process capacity')

    m.tau_pro = pyomo.Var(m.t,
                          m.pro_tuples,
                          within=pyomo.NonNegativeReals,
                          doc='Power flow (MW) through process')
    m.e_pro_in = pyomo.Var(
        m.tm,
        m.pro_input_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow of commodity into process (MW) per timestep')
    m.e_pro_out = pyomo.Var(m.tm,
                            m.pro_output_tuples,
                            within=pyomo.NonNegativeReals,
                            doc='Power flow out of process (MW) per timestep')

    # Add additional features
    # called features are declared in distinct files in features folder
    if m.mode['tra']:
        if m.mode['dpf']:
            m = transmission.add_transmission_dc(m)
        else:
            m = add_transmission(m)
    if m.mode['sto']:
        m = add_storage(m)
    if m.mode['dsm']:
        m = add_dsm(m)
    if m.mode['bsp']:
        m = add_buy_sell_price(m)
    if m.mode['tve']:
        m = add_time_variable_efficiency(m)
    else:
        m.pro_timevar_output_tuples = pyomo.Set(
            within=m.stf * m.sit * m.pro * m.com,
            doc='empty set needed for (partial) process output')

    # Equation declarations
    # equation bodies are defined in separate functions, referred to here by
    # their name in the "rule" keyword.

    # commodity
    m.res_vertex = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_vertex_rule,
        doc='storage + transmission + process + source + buy - sell == demand')
    m.res_stock_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_stock_step_rule,
        doc='stock commodity input per step <= commodity.maxperstep')
    m.res_stock_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_stock_total_rule,
        doc='total stock commodity input <= commodity.max')
    m.res_env_step = pyomo.Constraint(
        m.tm,
        m.com_tuples,
        rule=res_env_step_rule,
        doc='environmental output per step <= commodity.maxperstep')
    m.res_env_total = pyomo.Constraint(
        m.com_tuples,
        rule=res_env_total_rule,
        doc='total environmental commodity output <= commodity.max')

    # process
    m.def_process_input = pyomo.Constraint(
        m.tm,
        m.pro_input_tuples - m.pro_partial_input_tuples,
        rule=def_process_input_rule,
        doc='process input = process throughput * input ratio')
    m.def_process_output = pyomo.Constraint(
        m.tm, (m.pro_output_tuples - m.pro_partial_output_tuples -
               m.pro_timevar_output_tuples),
        rule=def_process_output_rule,
        doc='process output = process throughput * output ratio')
    m.def_intermittent_supply = pyomo.Constraint(
        m.tm,
        m.pro_input_tuples,
        rule=def_intermittent_supply_rule,
        doc='process output = process capacity * supim timeseries')
    m.res_process_throughput_by_capacity = pyomo.Constraint(
        m.tm,
        m.pro_tuples,
        rule=res_process_throughput_by_capacity_rule,
        doc='process throughput <= total process capacity')
    m.res_process_maxgrad_lower = pyomo.Constraint(
        m.tm,
        m.pro_maxgrad_tuples,
        rule=res_process_maxgrad_lower_rule,
        doc='throughput may not decrease faster than maximal gradient')
    m.res_process_maxgrad_upper = pyomo.Constraint(
        m.tm,
        m.pro_maxgrad_tuples,
        rule=res_process_maxgrad_upper_rule,
        doc='throughput may not increase faster than maximal gradient')
    m.res_process_capacity = pyomo.Constraint(
        m.pro_tuples,
        rule=res_process_capacity_rule,
        doc='process.cap-lo <= total process capacity <= process.cap-up')

    m.res_area = pyomo.Constraint(
        m.sit_tuples,
        rule=res_area_rule,
        doc='used process area <= total process area')

    m.res_throughput_by_capacity_min = pyomo.Constraint(
        m.tm,
        m.pro_partial_tuples,
        rule=res_throughput_by_capacity_min_rule,
        doc='cap_pro * min-fraction <= tau_pro')
    m.def_partial_process_input = pyomo.Constraint(
        m.tm,
        m.pro_partial_input_tuples,
        rule=def_partial_process_input_rule,
        doc='e_pro_in = '
        ' cap_pro * min_fraction * (r - R) / (1 - min_fraction)'
        ' + tau_pro * (R - min_fraction * r) / (1 - min_fraction)')
    m.def_partial_process_output = pyomo.Constraint(
        m.tm, (m.pro_partial_output_tuples -
               (m.pro_partial_output_tuples & m.pro_timevar_output_tuples)),
        rule=def_partial_process_output_rule,
        doc='e_pro_out = '
        ' cap_pro * min_fraction * (r - R) / (1 - min_fraction)'
        ' + tau_pro * (R - min_fraction * r) / (1 - min_fraction)')

    if m.mode['int']:
        m.res_global_co2_limit = pyomo.Constraint(
            m.stf,
            rule=res_global_co2_limit_rule,
            doc='total co2 commodity output <= global.prop CO2 limit')

    # costs
    m.def_costs = pyomo.Constraint(m.cost_type,
                                   rule=def_costs_rule,
                                   doc='main cost function by cost type')

    # objective and global constraints
    if m.obj.value == 'cost':

        if m.mode['int']:
            m.res_global_co2_budget = pyomo.Constraint(
                rule=res_global_co2_budget_rule,
                doc='total co2 commodity output <= global.prop CO2 budget')
        else:
            m.res_global_co2_limit = pyomo.Constraint(
                m.stf,
                rule=res_global_co2_limit_rule,
                doc='total co2 commodity output <= Global CO2 limit')

        m.objective_function = pyomo.Objective(
            rule=cost_rule,
            sense=pyomo.minimize,
            doc='minimize(cost = sum of all cost types)')

    elif m.obj.value == 'CO2':

        m.res_global_cost_limit = pyomo.Constraint(
            rule=res_global_cost_limit_rule,
            doc='total costs <= Global cost limit')

        m.objective_function = pyomo.Objective(
            rule=co2_rule,
            sense=pyomo.minimize,
            doc='minimize total CO2 emissions')

    else:
        raise NotImplementedError("Non-implemented objective quantity. Set "
                                  "either 'cost' or 'CO2' as the objective in "
                                  "runme.py!")

    if dual:
        m.dual = pyomo.Suffix(direction=pyomo.Suffix.IMPORT)

    return m
Exemple #19
0
def create_model(data, dt=1, timesteps=None, dual=False):
    """Create a pyomo ConcreteModel urbs object from given input data.

    Args:
        data: a dict of 6 DataFrames with the keys 'commodity', 'process',
            'transmission', 'storage', 'demand' and 'supim'.
        dt: timestep duration in hours (default: 1)
        timesteps: optional list of timesteps, default: demand timeseries
        dual: set True to add dual variables to model (slower); default: False

    Returns:
        a pyomo ConcreteModel object
    """

    # Optional
    if not timesteps:
        timesteps = data['demand'].index.tolist()
    m = pyomo_model_prep(data, timesteps)  # preparing pyomo model
    m.name = 'urbs'
    m.created = datetime.now().strftime('%Y%m%dT%H%M')
    m._data = data

    # Parameters

    # weight = length of year (hours) / length of simulation (hours)
    # weight scales costs and emissions from length of simulation to a full
    # year, making comparisons among cost types (invest is annualized, fixed
    # costs are annual by default, variable costs are scaled by weight) and
    # among different simulation durations meaningful.
    m.weight = pyomo.Param(
        initialize=float(8760) / (len(m.timesteps) * dt),
        doc='Pre-factor for variable costs and emissions for an annual result')

    # dt = spacing between timesteps. Required for storage equation that
    # converts between energy (storage content, e_sto_con) and power (all other
    # quantities that start with "e_")
    m.dt = pyomo.Param(
        initialize=dt,
        doc='Time step duration (in hours), default: 1')

    # Sets
    # ====
    # Syntax: m.{name} = Set({domain}, initialize={values})
    # where name: set name
    #       domain: set domain for tuple sets, a cartesian set product
    #       values: set values, a list or array of element tuples

    # generate ordered time step sets
    m.t = pyomo.Set(
        initialize=m.timesteps,
        ordered=True,
        doc='Set of timesteps')

    # modelled (i.e. excluding init time step for storage) time steps
    m.tm = pyomo.Set(
        within=m.t,
        initialize=m.timesteps[1:],
        ordered=True,
        doc='Set of modelled timesteps')

    # site (e.g. north, middle, south...)
    m.sit = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Site').unique(),
        doc='Set of sites')

    # commodity (e.g. solar, wind, coal...)
    m.com = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Commodity').unique(),
        doc='Set of commodities')

    # commodity type (i.e. SupIm, Demand, Stock, Env)
    m.com_type = pyomo.Set(
        initialize=m.commodity.index.get_level_values('Type').unique(),
        doc='Set of commodity types')

    # process (e.g. Wind turbine, Gas plant, Photovoltaics...)
    m.pro = pyomo.Set(
        initialize=m.process.index.get_level_values('Process').unique(),
        doc='Set of conversion processes')

    # tranmission (e.g. hvac, hvdc, pipeline...)
    m.tra = pyomo.Set(
        initialize=m.transmission.index.get_level_values('Transmission')
                                       .unique(),
        doc='Set of transmission technologies')

    # storage (e.g. hydrogen, pump storage)
    m.sto = pyomo.Set(
        initialize=m.storage.index.get_level_values('Storage').unique(),
        doc='Set of storage technologies')

    # cost_type
    m.cost_type = pyomo.Set(
        initialize=['Invest', 'Fixed', 'Variable', 'Fuel',
                    'Environmental'],
        doc='Set of cost types (hard-coded)')

    # tuple sets
    m.com_tuples = pyomo.Set(
        within=m.sit*m.com*m.com_type,
        initialize=m.commodity.index,
        doc='Combinations of defined commodities, e.g. (Mid,Elec,Demand)')
    m.pro_tuples = pyomo.Set(
        within=m.sit*m.pro,
        initialize=m.process.index,
        doc='Combinations of possible processes, e.g. (North,Coal plant)')
    m.tra_tuples = pyomo.Set(
        within=m.sit*m.sit*m.tra*m.com,
        initialize=m.transmission.index,
        doc='Combinations of possible transmissions, e.g. '
            '(South,Mid,hvac,Elec)')
    m.sto_tuples = pyomo.Set(
        within=m.sit*m.sto*m.com,
        initialize=m.storage.index,
        doc='Combinations of possible storage by site, e.g. (Mid,Bat,Elec)')

    # commodity type subsets
    m.com_supim = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'SupIm'),
        doc='Commodities that have intermittent (timeseries) input')
    m.com_stock = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Stock'),
        doc='Commodities that can be purchased at some site(s)')
    m.com_demand = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Demand'),
        doc='Commodities that have a demand (implies timeseries)')
    m.com_env = pyomo.Set(
        within=m.com,
        initialize=commodity_subset(m.com_tuples, 'Env'),
        doc='Commodities that (might) have a maximum creation limit')

    # process input/output
    m.pro_input_tuples = pyomo.Set(
        within=m.sit*m.pro*m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_tuples
                    for (pro, commodity) in m.r_in.index
                    if process == pro],
        doc='Commodities consumed by process by site, e.g. (Mid,PV,Solar)')
    m.pro_output_tuples = pyomo.Set(
        within=m.sit*m.pro*m.com,
        initialize=[(site, process, commodity)
                    for (site, process) in m.pro_tuples
                    for (pro, commodity) in m.r_out.index
                    if process == pro],
        doc='Commodities produced by process by site, e.g. (Mid,PV,Elec)')

    # storage tuples for storages with fixed initial state
    m.sto_init_bound_tuples = pyomo.Set(
        within=m.sit*m.sto*m.com,
        initialize=m.stor_init_bound.index,
        doc='storages with fixed initial state')

    # storage tuples for storages with given energy to power ratio
    m.sto_ep_ratio_tuples = pyomo.Set(
        within=m.sit*m.sto*m.com,
        initialize=m.sto_ep_ratio.index,
        doc='storages with given energy to power ratio')

    # Variables

    # costs
    m.costs = pyomo.Var(
        m.cost_type,
        within=pyomo.Reals,
        doc='Costs by type (EUR/a)')

    # commodity
    m.e_co_stock = pyomo.Var(
        m.tm, m.com_tuples,
        within=pyomo.NonNegativeReals,
        doc='Use of stock commodity source (MW) per timestep')

    # process
    m.cap_pro = pyomo.Var(
        m.pro_tuples,
        within=pyomo.NonNegativeReals,
        doc='Total process capacity (MW)')
    m.cap_pro_new = pyomo.Var(
        m.pro_tuples,
        within=pyomo.NonNegativeReals,
        doc='New process capacity (MW)')
    m.tau_pro = pyomo.Var(
        m.t, m.pro_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow (MW) through process')
    m.e_pro_in = pyomo.Var(
        m.tm, m.pro_tuples, m.com,
        within=pyomo.NonNegativeReals,
        doc='Power flow of commodity into process (MW) per timestep')
    m.e_pro_out = pyomo.Var(
        m.tm, m.pro_tuples, m.com,
        within=pyomo.NonNegativeReals,
        doc='Power flow out of process (MW) per timestep')

    # transmission
    m.cap_tra = pyomo.Var(
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Total transmission capacity (MW)')
    m.cap_tra_new = pyomo.Var(
        m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='New transmission capacity (MW)')
    m.e_tra_in = pyomo.Var(
        m.tm, m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow into transmission line (MW) per timestep')
    m.e_tra_out = pyomo.Var(
        m.tm, m.tra_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow out of transmission line (MW) per timestep')

    # storage
    m.cap_sto_c = pyomo.Var(
        m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='Total storage size (MWh)')
    m.cap_sto_c_new = pyomo.Var(
        m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='New storage size (MWh)')
    m.cap_sto_p = pyomo.Var(
        m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='Total storage power (MW)')
    m.cap_sto_p_new = pyomo.Var(
        m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='New  storage power (MW)')
    m.e_sto_in = pyomo.Var(
        m.tm, m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow into storage (MW) per timestep')
    m.e_sto_out = pyomo.Var(
        m.tm, m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='Power flow out of storage (MW) per timestep')
    m.e_sto_con = pyomo.Var(
        m.t, m.sto_tuples,
        within=pyomo.NonNegativeReals,
        doc='Energy content of storage (MWh) in timestep')

    # Equation declarations
    # equation bodies are defined in separate functions, referred to here by
    # their name in the "rule" keyword.

    # commodity
    m.res_vertex = pyomo.Constraint(
        m.tm, m.com_tuples,
        rule=res_vertex_rule,
        doc='storage + transmission + process + source == demand')

    # process
    m.def_process_capacity = pyomo.Constraint(
        m.pro_tuples,
        rule=def_process_capacity_rule,
        doc='total process capacity = inst-cap + new capacity')
    m.def_process_input = pyomo.Constraint(
        m.tm, m.pro_input_tuples,
        rule=def_process_input_rule,
        doc='process input = process throughput * input ratio')
    m.def_process_output = pyomo.Constraint(
        m.tm, m.pro_output_tuples,
        rule=def_process_output_rule,
        doc='process output = process throughput * output ratio')
    m.def_intermittent_supply = pyomo.Constraint(
        m.tm, m.pro_input_tuples,
        rule=def_intermittent_supply_rule,
        doc='process output = process capacity * supim timeseries')
    m.res_process_throughput_by_capacity = pyomo.Constraint(
        m.tm, m.pro_tuples,
        rule=res_process_throughput_by_capacity_rule,
        doc='process throughput <= total process capacity')
    m.res_process_capacity = pyomo.Constraint(
        m.pro_tuples,
        rule=res_process_capacity_rule,
        doc='process.cap-lo <= total process capacity <= process.cap-up')

    # transmission
    m.def_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=def_transmission_capacity_rule,
        doc='total transmission capacity = inst-cap + new capacity')
    m.def_transmission_output = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=def_transmission_output_rule,
        doc='transmission output = transmission input * efficiency')
    m.res_transmission_input_by_capacity = pyomo.Constraint(
        m.tm, m.tra_tuples,
        rule=res_transmission_input_by_capacity_rule,
        doc='transmission input <= total transmission capacity')
    m.res_transmission_capacity = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_capacity_rule,
        doc='transmission.cap-lo <= total transmission capacity <= '
            'transmission.cap-up')
    m.res_transmission_symmetry = pyomo.Constraint(
        m.tra_tuples,
        rule=res_transmission_symmetry_rule,
        doc='total transmission capacity must be symmetric in both directions')

    # storage
    m.def_storage_state = pyomo.Constraint(
        m.tm, m.sto_tuples,
        rule=def_storage_state_rule,
        doc='storage[t] = (1 - sd) * storage[t-1] + in * eff_i - out / eff_o')
    m.def_storage_power = pyomo.Constraint(
        m.sto_tuples,
        rule=def_storage_power_rule,
        doc='storage power = inst-cap + new power')
    m.def_storage_capacity = pyomo.Constraint(
        m.sto_tuples,
        rule=def_storage_capacity_rule,
        doc='storage capacity = inst-cap + new capacity')
    m.res_storage_input_by_power = pyomo.Constraint(
        m.tm, m.sto_tuples,
        rule=res_storage_input_by_power_rule,
        doc='storage input <= storage power')
    m.res_storage_output_by_power = pyomo.Constraint(
        m.tm, m.sto_tuples,
        rule=res_storage_output_by_power_rule,
        doc='storage output <= storage power')
    m.res_storage_state_by_capacity = pyomo.Constraint(
        m.t, m.sto_tuples,
        rule=res_storage_state_by_capacity_rule,
        doc='storage content <= storage capacity')
    m.res_storage_power = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_power_rule,
        doc='storage.cap-lo-p <= storage power <= storage.cap-up-p')
    m.res_storage_capacity = pyomo.Constraint(
        m.sto_tuples,
        rule=res_storage_capacity_rule,
        doc='storage.cap-lo-c <= storage capacity <= storage.cap-up-c')
    m.res_initial_and_final_storage_state = pyomo.Constraint(
        m.t, m.sto_init_bound_tuples,
        rule=res_initial_and_final_storage_state_rule,
        doc='storage content initial == and final >= storage.init * capacity')
    m.res_initial_and_final_storage_state_var = pyomo.Constraint(
        m.t, m.sto_tuples - m.sto_init_bound_tuples,
        rule=res_initial_and_final_storage_state_var_rule,
        doc='storage content initial <= final, both variable')
    m.def_storage_energy_power_ratio = pyomo.Constraint(
        m.sto_ep_ratio_tuples,
        rule=def_storage_energy_power_ratio_rule,
        doc='storage capacity = storage power * storage E2P ratio')

    # costs
    m.def_costs = pyomo.Constraint(
        m.cost_type,
        rule=def_costs_rule,
        doc='main cost function by cost type')
    m.obj = pyomo.Objective(
        rule=obj_rule,
        sense=pyomo.minimize,
        doc='minimize(cost = sum of all cost types)')

    # global
    m.res_global_co2_limit = pyomo.Constraint(
            rule=res_global_co2_limit_rule,
            doc='total co2 commodity output <= Global CO2 limit')

    if dual:
        m.dual = pyomo.Suffix(direction=pyomo.Suffix.IMPORT)
    return m
Exemple #20
0
    def test_collect_mutable_parameters(self):
        model = pc.ConcreteModel()
        model.p = pc.Param(mutable=True)
        model.q = pc.Param([1], mutable=True, initialize=1.0)
        model.r = pc.Param(initialize=1.1, mutable=False)
        model.x = pc.Var()
        for obj in [model.p, model.q[1]]:

            result = EmbeddedSP._collect_mutable_parameters(obj)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(obj + 1)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(2 * (obj + 1))
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(2 * obj)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(2 * obj + 1)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(2 * obj + 1 +
                                                            model.x)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(obj * model.x)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(model.x / obj)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(model.x /
                                                            (2 * obj))
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(
                obj * pc.log(2 * model.x))
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(
                obj * pc.sin(model.r)**model.x)
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

            result = EmbeddedSP._collect_mutable_parameters(
                model.x**(obj * pc.sin(model.r)))
            self.assertTrue(id(obj) in result)
            self.assertEqual(len(result), 1)
            del result

        result = EmbeddedSP._collect_mutable_parameters(1.0)
        self.assertEqual(len(result), 0)
        del result

        result = EmbeddedSP._collect_mutable_parameters(model.p + model.q[1] +
                                                        model.r)
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(model.p + 1 + model.r +
                                                        model.q[1])
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)

        result = EmbeddedSP._collect_mutable_parameters(model.q[1] * 2 *
                                                        (model.p + model.r) +
                                                        model.r)
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(2 * model.x * model.p *
                                                        model.q[1] * model.r)
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(2 * obj * model.q[1] *
                                                        model.r + 1)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 1)
        del result

        result = EmbeddedSP._collect_mutable_parameters(2 * model.q[1] + 1 +
                                                        model.x - model.p)
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(model.r * model.x)
        self.assertEqual(len(result), 0)
        del result

        result = EmbeddedSP._collect_mutable_parameters(model.x / obj)
        self.assertTrue(id(obj) in result)
        self.assertEqual(len(result), 1)
        del result

        result = EmbeddedSP._collect_mutable_parameters(
            model.x / (2 * model.q[1] / model.p))
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(
            (model.p / model.q[1]) * pc.log(2 * model.x))
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(
            model.q[1] * pc.sin(model.p)**(model.x + model.r))
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result

        result = EmbeddedSP._collect_mutable_parameters(
            (model.p + model.x)**(model.q[1] * pc.sin(model.r)))
        self.assertTrue(id(model.p) in result)
        self.assertTrue(id(model.q[1]) in result)
        self.assertEqual(len(result), 2)
        del result
Exemple #21
0
def node_constraints_build(model):
    """
    Defines variables:

    * s_cap: installed storage capacity
    * r_cap: installed resource <-> storage conversion capacity
    * e_cap: installed storage <-> grid conversion capacity (gross)
    * e_cap_net: installed storage <-> grid conversion capacity (net)
    * rb_cap: installed secondary resource conversion capacity

    """
    m = model.m
    d = model.data

    def get_var_constraint(model_var,
                           y,
                           var,
                           x,
                           _equals=None,
                           _max=None,
                           _min=None,
                           scale=None):

        if not _equals:
            _equals = model.get_option(y + '.constraints.' + var + '.equals',
                                       x=x)
        if not _max:
            _max = model.get_option(y + '.constraints.' + var + '.max', x=x)
        if not _min:
            _min = model.get_option(y + '.constraints.' + var + '.min', x=x)
        if scale:
            _equals = scale * _equals
            _min = scale * _min
            _max = scale * _max
        if _equals:
            if np.isinf(_equals):
                e = exceptions.ModelError
                raise e('Cannot use inf in operational mode, for value of '
                        '{}.{}.equals.{}'.format(y, var, x))
            return model_var == _equals
        elif model.mode == 'operate':
            # Operational mode but 'equals' constraint not set, we use 'max'
            # instead
            # FIXME this should be logged
            if np.isinf(_max):
                return po.Constraint.NoConstraint
            else:
                return model_var == _max
        else:
            if np.isinf(_max):
                _max = None  # to disable upper bound
            if _min == 0 and _max is None:
                return po.Constraint.NoConstraint
            else:
                return (_min, model_var, _max)

    # Variables
    m.s_cap = po.Var(m.y_pc, m.x, within=po.NonNegativeReals)
    m.r_cap = po.Var(m.y_def_r, m.x, within=po.NonNegativeReals)
    m.e_cap = po.Var(m.y, m.x, within=po.NonNegativeReals)
    m.e_cap_net = po.Var(m.y, m.x, within=po.NonNegativeReals)
    m.rb_cap = po.Var(m.y_rb, m.x, within=po.NonNegativeReals)

    # Constraint rules
    def c_s_cap_rule(m, y, x):
        if model.get_option(y + '.constraints.use_s_time', x=x):
            scale = model.get_option(y + '.constraints.e_cap_scale', x=x)
            s_time_max = model.get_option(y + '.constraints.s_time.max', x=x)
            e_cap = model.get_option(y + '.constraints.e_cap.equals', x=x)
            if not e_cap:
                e_cap = model.get_option(y + '.constraints.e_cap.max', x=x)
            e_eff_ref = model.get_eff_ref('e', y)
            s_cap_max = s_time_max * e_cap * scale / e_eff_ref
        else:
            s_cap_max = None

        return get_var_constraint(m.s_cap[y, x], y, 's_cap', x, _max=s_cap_max)

    def c_r_cap_rule(m, y, x):
        if model.get_option(y + '.constraints.r_cap_equals_e_cap', x=x):
            return m.r_cap[y, x] == m.e_cap[y, x]
        else:
            return get_var_constraint(m.r_cap[y, x], y, 'r_cap', x)

    def c_r_area_rule(m, y, x):
        area_per_cap = model.get_option(y + '.constraints.r_area_per_e_cap',
                                        x=x)
        if area_per_cap:
            return m.r_area[y, x] == m.e_cap[y, x] * area_per_cap
        else:
            e_cap_max = model.get_option(y + '.constraints.e_cap.max', x=x)
            if e_cap_max == 0:
                # If a technology has no e_cap here, we force r_area to zero,
                # so as not to accrue spurious costs
                return m.r_area[y, x] == 0
            elif model.get_option(y + '.constraints.r_area.max', x=x) is False:
                return m.r_area[y, x] == 1
            else:
                return get_var_constraint(m.r_area[y, x], y, 'r_area', x)

    def c_e_cap_rule(m, y, x):
        # First check whether this tech is allowed at this location
        if not model._locations.at[x, y] == 1:
            return m.e_cap[y, x] == 0
        else:
            e_cap_scale = model.get_option(y + '.constraints.e_cap_scale', x=x)
            return get_var_constraint(m.e_cap[y, x],
                                      y,
                                      'e_cap',
                                      x,
                                      scale=e_cap_scale)

    def c_e_cap_gross_net_rule(m, y, x):
        c_eff = model.get_option(y + '.constraints.c_eff', x=x)
        return m.e_cap[y, x] * c_eff == m.e_cap_net[y, x]

    def c_rb_cap_rule(m, y, x):
        follow = model.get_option(y + '.constraints.rb_cap_follow', x=x)
        mode = model.get_option(y + '.constraints.rb_cap_follow_mode', x=x)

        # First deal with the special case of ``rb_cap_follow`` being set
        if follow:
            if follow == 'r_cap':
                rb_cap_val = m.r_cap[y, x]
            elif follow == 'e_cap':
                rb_cap_val = m.e_cap[y, x]
            elif follow is not False:
                # Raise an error to make sure follows isn't accidentally set to
                # something invalid
                e = exceptions.ModelError
                raise e('rb_cab_follow set to invalid value at '
                        '({}, {}): {}'.format(y, x, follow))

            if mode == 'max':
                return m.rb_cap[y, x] <= rb_cap_val
            elif mode == 'equals':
                return m.rb_cap[y, x] == rb_cap_val

        else:  # If ``rb_cap_follow`` not set, set up standard constraints
            return get_var_constraint(m.rb_cap[y, x], y, 'rb_cap', x)

    # Constraints
    m.c_s_cap = po.Constraint(m.y_pc, m.x, rule=c_s_cap_rule)
    m.c_r_cap = po.Constraint(m.y_def_r, m.x, rule=c_r_cap_rule)
    m.c_r_area = po.Constraint(m.y_def_r, m.x, rule=c_r_area_rule)
    m.c_e_cap = po.Constraint(m.y, m.x, rule=c_e_cap_rule)
    m.c_e_cap_gross_net = po.Constraint(m.y, m.x, rule=c_e_cap_gross_net_rule)
    m.c_rb_cap = po.Constraint(m.y_rb, m.x, rule=c_rb_cap_rule)