Ejemplo n.º 1
0
 def test_error1(self):
     model = AbstractModel()
     try:
         model.a = BuildAction()
         self.fail("Expected ValueError")
     except ValueError:
         pass
Ejemplo n.º 2
0
    def test_dense_param(self):
        #
        # Create model instance
        #
        model = AbstractModel()
        model.Z = Set(initialize=[1,3])
        model.A = Param(model.Z, initialize=1.3, mutable=True)
        model.action2 = BuildAction(model.Z, rule=action2_fn)
        instance = model.create_instance()
        #
        self.assertEqual( instance.A[1].value, 2.3)
        self.assertEqual( value(instance.A[3]), 4.3)
        #
        buf = StringIO()
        instance.pprint(ostream=buf)
        self.assertEqual(buf.getvalue(),"""1 Set Declarations
    Z : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    2 : {1, 3}

1 Param Declarations
    A : Size=2, Index=Z, Domain=Any, Default=None, Mutable=True
        Key : Value
          1 :   2.3
          3 :   4.3

1 BuildAction Declarations
    action2 : Size=0, Index=Z, Active=True

3 Declarations: Z A action2
""")
Ejemplo n.º 3
0
 def setUp(self):
     #
     # Create model instance
     #
     model = AbstractModel()
     model.A = Param(initialize=3.3, mutable=True)
     model.action1 = BuildAction(rule=action1_fn)
     self.instance = model.create_instance()
Ejemplo n.º 4
0
    def _create(self, group=None):
        """ Creates the linear constraint for the class:`ElectricalLine`
        block.

        Parameters
        ----------
        group : list
            List of oemof.solph.ElectricalLine (eline) objects for which
            the linear relation of inputs and outputs is created
            e.g. group = [eline1, eline2, ...]. The components inside the
            list need to hold a attribute `reactance` of type Sequence
            containing the reactance of the line.
        """
        if group is None:
            return None

        m = self.parent_block()

        # create voltage angle variables
        self.ELECTRICAL_BUSES = Set(
            initialize=[n for n in m.es.nodes if isinstance(n, ElectricalBus)])

        def _voltage_angle_bounds(block, b, t):
            return b.v_min, b.v_max

        self.voltage_angle = Var(self.ELECTRICAL_BUSES,
                                 m.TIMESTEPS,
                                 bounds=_voltage_angle_bounds)

        if True not in [b.slack for b in self.ELECTRICAL_BUSES]:
            # TODO: Make this robust to select the same slack bus for
            # the same problems
            bus = [b for b in self.ELECTRICAL_BUSES][0]
            logging.info(
                "No slack bus set,setting bus {0} as slack bus".format(
                    bus.label))
            bus.slack = True

        def _voltage_angle_relation(block):
            for t in m.TIMESTEPS:
                for n in group:
                    if n.input.slack is True:
                        self.voltage_angle[n.output, t].value = 0
                        self.voltage_angle[n.output, t].fix()
                    try:
                        lhs = m.flow[n.input, n.output, t]
                        rhs = 1 / n.reactance[t] * (
                            self.voltage_angle[n.input, t] -
                            self.voltage_angle[n.output, t])
                    except:
                        raise ValueError("Error in constraint creation",
                                         "of node {}".format(n.label))
                    block.electrical_flow.add((n, t), (lhs == rhs))

        self.electrical_flow = Constraint(group, m.TIMESTEPS, noruleinit=True)

        self.electrical_flow_build = BuildAction(rule=_voltage_angle_relation)
Ejemplo n.º 5
0
    def test_sparse_param_default(self):
        #
        # Create model instance
        #
        model = AbstractModel()
        model.Z = Set(initialize=[1,3])
        model.A = Param(model.Z, initialize={1:1.3}, default=0, mutable=True)
        model.action2 = BuildAction(model.Z, rule=action2_fn)
        instance = model.create_instance()

        tmp = value(instance.A[1])
        self.assertEqual( type(tmp), float)
        self.assertEqual( tmp, 2.3 )
Ejemplo n.º 6
0
    def _create(self, group=None):
        """
        """
        if group is None:
            return None

        m = self.parent_block()

        # create voltage angle variables
        self.ELECTRICAL_BUSES = Set(
            initialize=[n for n in m.es.nodes if isinstance(n, ElectricalBus)])

        def _voltage_angle_bounds(block, b, t):
            return b.v_min, b.v_max

        self.voltage_angle = Var(self.ELECTRICAL_BUSES,
                                 m.TIMESTEPS,
                                 bounds=_voltage_angle_bounds)

        if True not in [b.slack for b in self.ELECTRICAL_BUSES]:
            # TODO: Make this robust to select the same slack bus for
            # the same problems
            bus = [b for b in self.ELECTRICAL_BUSES][0]
            logging.info(
                "No slack bus set,setting bus {0} as slack bus".format(
                    bus.label))
            bus.slack = True

        def _voltage_angle_relation(block):
            for t in m.TIMESTEPS:
                for n in group:
                    if n.input.slack is True:
                        self.voltage_angle[n.output, t].value = 0
                        self.voltage_angle[n.output, t].fix()
                    try:
                        lhs = m.flow[n.input, n.output, t]
                        rhs = 1 / n.reactance[t] * (
                            self.voltage_angle[n.input, t] -
                            self.voltage_angle[n.output, t])
                    except:
                        raise ValueError("Error in constraint creation",
                                         "of node {}".format(n.label))
                    block.electrical_flow.add((n, t), (lhs == rhs))

        self.electrical_flow = Constraint(group, m.TIMESTEPS, noruleinit=True)

        self.electrical_flow_build = BuildAction(rule=_voltage_angle_relation)
Ejemplo n.º 7
0
    def _create(self, group=None):
        """ Creates the relation for the class:`Link`.

        Parameters
        ----------
        group : list
            List of oemof.solph.custom.Link objects for which
            the relation of inputs and outputs is createdBuildAction
            e.g. group = [link1, link2, link3, ...]. The components inside
            the list need to hold an attribute `conversion_factors` of type
            dict containing the conversion factors for all inputs to outputs.
        """
        if group is None:
            return None

        m = self.parent_block()

        all_conversions = {}
        for n in group:
            all_conversions[n] = {
                k: v
                for k, v in n.conversion_factors.items()
            }

        def _input_output_relation(block):
            for t in m.TIMESTEPS:
                for n, conversion in all_conversions.items():
                    for cidx, c in conversion.items():
                        try:
                            expr = (m.flow[n, cidx[1],
                                           t] == c[t] * m.flow[cidx[0], n, t])
                        except ValueError:
                            raise ValueError(
                                "Error in constraint creation",
                                "from: {0}, to: {1}, via: {3}".format(
                                    cidx[0], cidx[1], n))
                        block.relation.add((n, cidx[0], cidx[1], t), (expr))

        self.relation = Constraint(
            [(n, cidx[0], cidx[1], t) for t in m.TIMESTEPS
             for n, conversion in all_conversions.items()
             for cidx, c in conversion.items()],
            noruleinit=True)
        self.relation_build = BuildAction(rule=_input_output_relation)
Ejemplo n.º 8
0
def define_components(m):
    # benders suffix should have 0 for master problem or n for each subproblem;
    # see https://ampl.com/products/solvers/solvers-we-sell/cplex/options/
    # and https://www.ibm.com/support/knowledgecenter/en/SSSA5P_12.7.1/ilog.odms.cplex.help/CPLEX/Parameters/topics/BendersStrategy.html
    # solve with --solver cplexamp --solver-options-string "bendersopt='' benders_strategy=2"
    # but note: subproblems must be strictly continuous or you get error 2002 (bad decomposition):
    # https://www.ibm.com/developerworks/community/forums/html/topic?id=086bfa2b-35fd-4987-aca2-1e31b7fd2413
    # https://orinanobworld.blogspot.com/2013/07/benders-decomposition-with-integer.html
    # https://groups.google.com/forum/#!topic/aimms/OsMHRV-HXhk
    m.benders = Suffix(direction=Suffix.EXPORT)

    def rule(m):
        # place all build vars in the master problem (0), others in subproblem (1)
        for var in m.component_objects(Var):
            suf = 0 if var.name in fix_vars else 1
            for obj in var.values():
                m.benders[obj] = suf

    m.Assign_Benders_Suffixes = BuildAction(rule=rule)
    def _create(self, group=None):
        if group is None:
            return None

        m = self.parent_block()

        # for all DSM components get inflow from bus_elec
        for n in group:
            n.inflow = list(n.inputs)[0]

        #  ************* SETS *********************************

        # Set of DSM Components
        self.DSM = Set(initialize=[n for n in group])

        #  ************* VARIABLES *****************************

        # Variable load shift down (MWh)
        self.DSMdo = Var(self.DSM, m.TIMESTEPS, m.TIMESTEPS, initialize=0, within=NonNegativeReals)

        # Variable load shift up(MWh)
        self.DSMup = Var(self.DSM, m.TIMESTEPS, initialize=0, within=NonNegativeReals)

        #  ************* CONSTRAINTS *****************************

        # Demand Production Relation
        def _input_output_relation_rule(block):
            """
            Relation between input data and pyomo variables. The actual demand after DSM.
            Generator Production == Demand +- DSM
            """
            for t in m.TIMESTEPS:
                for g in group:

                    # first time steps: 0 + delay time
                    if t <= g.delay_time:

                        # Generator loads from bus
                        lhs = m.flow[g.inflow, g, t]
                        # Demand +- DSM
                        rhs = g.demand[t] + self.DSMup[g, t] - sum(
                            self.DSMdo[g, tt, t] for tt in range(t + g.delay_time + 1))
                        # add constraint
                        block.input_output_relation.add((g, t), (lhs == rhs))

                    # main use case
                    elif g.delay_time < t <= m.TIMESTEPS._bounds[1] - g.delay_time:

                        # Generator loads from bus
                        lhs = m.flow[g.inflow, g, t]
                        # Demand +- DSM
                        rhs = g.demand[t] + self.DSMup[g, t] - sum(
                            self.DSMdo[g, tt, t] for tt in range(t - g.delay_time, t + g.delay_time + 1))
                        # add constraint
                        block.input_output_relation.add((g, t), (lhs == rhs))

                    # last time steps: end - delay time
                    else:
                        # Generator loads from bus
                        lhs = m.flow[g.inflow, g, t]
                        # Demand +- DSM
                        rhs = g.demand[t] + self.DSMup[g, t] - sum(
                            self.DSMdo[g, tt, t] for tt in range(t - g.delay_time, m.TIMESTEPS._bounds[1] + 1))
                        # add constraint
                        block.input_output_relation.add((g, t), (lhs == rhs))

        self.input_output_relation = Constraint(group, m.TIMESTEPS, noruleinit=True)
        self.input_output_relation_build = BuildAction(rule=_input_output_relation_rule)

        # Equation 7
        def dsmupdo_constraint_rule(block):
            '''
            Equation 7 by Zerrahn, Schill:
            Every upward load shift has to be compensated by downward load shifts in a defined time frame.
            Slightly modified equations for the first and last time steps due to variable initialization.
            '''

            for t in m.TIMESTEPS:
                for g in group:

                    # first time steps: 0 + delay time
                    if t <= g.delay_time:

                        # DSM up
                        lhs = self.DSMup[g, t]
                        # DSM down
                        rhs = sum(self.DSMdo[g, t, tt] for tt in range(t + g.delay_time + 1))
                        # add constraint
                        block.dsmupdo_constraint.add((g, t), (lhs == rhs))

                    # main use case
                    elif g.delay_time < t <= m.TIMESTEPS._bounds[1] - g.delay_time:

                        # DSM up
                        lhs = self.DSMup[g, t]
                        # DSM down
                        rhs = sum(self.DSMdo[g, t, tt] for tt in range(t - g.delay_time, t + g.delay_time + 1))
                        # add constraint
                        block.dsmupdo_constraint.add((g, t), (lhs == rhs))

                    # last time steps: end - delay time
                    else:

                        # DSM up
                        lhs = self.DSMup[g, t]
                        # DSM down
                        rhs = sum(self.DSMdo[g, t, tt] for tt in range(t - g.delay_time, m.TIMESTEPS._bounds[1] + 1))
                        # add constraint
                        block.dsmupdo_constraint.add((g, t), (lhs == rhs))

        self.dsmupdo_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True)
        self.dsmupdo_constraint_build = BuildAction(rule=dsmupdo_constraint_rule)

        # Equation 8
        def dsmup_constraint_rule(block):
            '''
            Equation 8 by Zerrahn, Schill:
            Realised upward load shift at time t has to be smaller than upward DSM capacity at time t.
            '''

            for t in m.TIMESTEPS:
                for g in group:
                    # DSM up
                    lhs = self.DSMup[g, t]
                    # Capacity DSMup
                    rhs = g.c_do[t]
                    # add constraint
                    block.dsmup_constraint.add((g, t), (lhs <= rhs))

        self.dsmup_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True)
        self.dsmup_constraint_build = BuildAction(rule=dsmup_constraint_rule)

        # Equation 9
        def dsmdo_constraint_rule(block):
            '''
            Equation 9 by Zerrahn, Schill:
            Realised downward load shift at time t has to be smaller than downward DSM capacity at time t.
            '''

            for tt in m.TIMESTEPS:
                for g in group:

                    # first times steps: 0 + delay time
                    if tt <= g.delay_time:

                        # DSM down
                        lhs = sum(self.DSMdo[g, t, tt] for t in range(tt + g.delay_time + 1))
                        # Capacity DSM down
                        rhs = g.c_do[tt]
                        # add constraint
                        block.dsmdo_constraint.add((g, tt), (lhs <= rhs))

                    # main use case
                    elif g.delay_time < tt <= m.TIMESTEPS._bounds[1] - g.delay_time:

                        # DSM down
                        lhs = sum(self.DSMdo[g, t, tt] for t in range(tt - g.delay_time, tt + g.delay_time + 1))
                        # Capacity DSM down
                        rhs = g.c_do[tt]
                        # add constraint
                        block.dsmdo_constraint.add((g, tt), (lhs <= rhs))

                    # last time steps: end - delay time
                    else:

                        # DSM down
                        lhs = sum(self.DSMdo[g, t, tt] for t in range(tt - g.delay_time, m.TIMESTEPS._bounds[1] + 1))
                        # Capacity DSM down
                        rhs = g.c_do[tt]
                        # add constraint
                        block.dsmdo_constraint.add((g, tt), (lhs <= rhs))

        self.dsmdo_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True)
        self.dsmdo_constraint_build = BuildAction(rule=dsmdo_constraint_rule)

        # Equation 10
        def C2_constraint_rule(block):
            '''
            Equation 10 by Zerrahn, Schill:
            The realised DSM up or down at time T has to be smaller than the maximum downward or upward capacity
            at time T. Therefore in total each DSM unit can only be shifted up OR down.
            '''

            for tt in m.TIMESTEPS:
                for g in group:

                    # first times steps: 0 + delay time
                    if tt <= g.delay_time:

                        # DSM up/down
                        lhs = self.DSMup[g, tt] + sum(self.DSMdo[g, t, tt] for t in range(tt + g.delay_time + 1))
                        # max capacity at tt
                        rhs = max(g.c_up[tt], g.c_do[tt])
                        # add constraint
                        block.C2_constraint.add((g, tt), (lhs <= rhs))

                    elif g.delay_time < tt <= m.TIMESTEPS._bounds[1] - g.delay_time:

                        # DSM up/down
                        lhs = self.DSMup[g, tt] + sum(
                            self.DSMdo[g, t, tt] for t in range(tt - g.delay_time, tt + g.delay_time + 1))
                        # max capacity at tt
                        rhs = max(g.c_up[tt], g.c_do[tt])
                        # add constraint
                        block.C2_constraint.add((g, tt), (lhs <= rhs))

                    else:

                        # DSM up/down
                        lhs = self.DSMup[g, tt] + sum(
                            self.DSMdo[g, t, tt] for t in range(tt - g.delay_time, m.TIMESTEPS._bounds[1] + 1))
                        # max capacity at tt
                        rhs = max(g.c_up[tt], g.c_do[tt])
                        # add constraint
                        block.C2_constraint.add((g, tt), (lhs <= rhs))

        self.C2_constraint = Constraint(group, m.TIMESTEPS, noruleinit=True)
        self.C2_constraint_build = BuildAction(rule=C2_constraint_rule)

        def equivalent_power_constraint_rule(block):
            ''' new rule: inactive and doesnt work yet'''

            for t in m.TIMESTEPS:
                for g in group:

                    # first time steps: 0 + delay time
                    if t <= g.delay_time:

                        lhs = 0

                        rhs = value(min(self.DSMup[g, t], sum(self.DSMdo[g, t, tt]
                                                              for tt in range(t + g.delay_time + 1))))

                        block.input_output_relation.add((g, t), (lhs == rhs))

                    # main use case
                    elif g.delay_time < t <= m.TIMESTEPS._bounds[1] - g.delay_time:

                        lhs = 0

                        rhs = value(min(self.DSMup[g, t], sum(self.DSMdo[g, t, tt]
                                                              for tt in range(t - g.delay_time, t + g.delay_time + 1))))

                        # add constraint
                        block.input_output_relation.add((g, t), (lhs == rhs))


                    # last time steps: end - delay time
                    else:

                        lhs = 0

                        rhs = value(min(self.DSMup[g, t], sum(self.DSMdo[g, t, tt]
                                                              for tt in
                                                              range(t - g.delay_time, m.TIMESTEPS._bounds[1] + 1))))

                        block.input_output_relation.add((g, t), (lhs == rhs))
Ejemplo n.º 10
0
                    initialize=check_inistageon)
amodel.geninion = Param(amodel.generator,
                        within=NonNegativeReals,
                        initialize=enforce_inistageon)
amodel.geninioff = Param(amodel.generator,
                         within=NonNegativeReals,
                         initialize=enforce_inistageoff)

#%% generation cost function
amodel.pgenK0 = Param(amodel.generator, within=NonNegativeReals)
amodel.PCost0 = Param(amodel.generator, within=NonNegativeReals)
amodel.PCost1 = Param(amodel.generator, within=NonNegativeReals)
amodel.PCost2 = Param(amodel.generator, within=NonNegativeReals)
amodel.numcurve = Param(Within=PositiveIntegers, default=3)
amodel.createpoints = {}
amodel.CreatePowerGenerationPiecewisePoints = BuildAction(
    amodel.generator * amodel.nperiods, rule=gen_piecewise_curve)
amodel.ShutdownCostCoefficient = Param(amodel.generator,
                                       within=NonNegativeReals,
                                       default=0.0)

#%% vars
amodel.UnitOn = Var(amodel.generator, amodel.nperiods, within=Binary)
amodel.PowerGenerated = Var(amodel.generator,
                            amodel.nperiods,
                            within=NonNegativeReals)
amodel.MaximumPowerAvailable = Var(amodel.generator,
                                   amodel.nperiods,
                                   within=NonNegativeReals)

amodel.ProductionCost = Var(amodel.generator,
                            amodel.nperiods,
Ejemplo n.º 11
0
    def _create(self, group=None):
        """ Creates the linear constraint for the
        :class:`oemof.solph.Transformer` block.

        Parameters
        ----------
        group : list
            List of :class:`oemof.solph.ExtractionTurbineCHP` (trsf) objects
            for which the linear relation of inputs and outputs is created
            e.g. group = [trsf1, trsf2, trsf3, ...]. Note that the relation
            is created for all existing relations of the inputs and all outputs
            of the transformer. The components inside the list need to hold
            all needed attributes.
        """
        if group is None:
            return None

        m = self.parent_block()

        for n in group:
            n.inflow = list(n.inputs)[0]
            n.label_main_flow = str([
                k for k, v in n.conversion_factor_full_condensation.items()
            ][0])
            n.main_output = [
                o for o in n.outputs if n.label_main_flow == o.label
            ][0]
            n.tapped_output = [
                o for o in n.outputs if n.label_main_flow != o.label
            ][0]
            n.conversion_factor_full_condensation_sq = (
                n.conversion_factor_full_condensation[m.es.groups[
                    n.main_output.label]])
            n.flow_relation_index = [
                n.conversion_factors[m.es.groups[n.main_output.label]][t] /
                n.conversion_factors[m.es.groups[n.tapped_output.label]][t]
                for t in m.TIMESTEPS
            ]
            n.main_flow_loss_index = [
                (n.conversion_factor_full_condensation_sq[t] -
                 n.conversion_factors[m.es.groups[n.main_output.label]][t]) /
                n.conversion_factors[m.es.groups[n.tapped_output.label]][t]
                for t in m.TIMESTEPS
            ]

        def _input_output_relation_rule(block):
            """Connection between input, main output and tapped output.
            """
            for t in m.TIMESTEPS:
                for g in group:
                    lhs = m.flow[g.inflow, g, t]
                    rhs = ((m.flow[g, g.main_output, t] +
                            m.flow[g, g.tapped_output, t] *
                            g.main_flow_loss_index[t]) /
                           g.conversion_factor_full_condensation_sq[t])
                    block.input_output_relation.add((g, t), (lhs == rhs))

        self.input_output_relation = Constraint(group,
                                                m.TIMESTEPS,
                                                noruleinit=True)
        self.input_output_relation_build = BuildAction(
            rule=_input_output_relation_rule)

        def _out_flow_relation_rule(block):
            """Relation between main and tapped output in full chp mode.
            """
            for t in m.TIMESTEPS:
                for g in group:
                    lhs = m.flow[g, g.main_output, t]
                    rhs = (m.flow[g, g.tapped_output, t] *
                           g.flow_relation_index[t])
                    block.out_flow_relation.add((g, t), (lhs >= rhs))

        self.out_flow_relation = Constraint(group,
                                            m.TIMESTEPS,
                                            noruleinit=True)
        self.out_flow_relation_build = BuildAction(
            rule=_out_flow_relation_rule)
Ejemplo n.º 12
0
def create_model(namestr,
                 unslim=False,
                 emitlimit=False,
                 nem_ret_ratio=False,
                 nem_ret_gwh=False,
                 region_ret_ratio=False,
                 nem_disp_ratio=False,
                 nem_re_disp_ratio=False):
    """Creates an instance of the pyomo definition of openCEM"""
    m = AbstractModel(name=namestr)
    # Sets
    m.regions = Set(initialize=cemo.const.REGION.keys())  # Set of NEM regions
    m.zones = Set(
        initialize=cemo.const.ZONE.keys())  # Set of NTNDP planning zones
    m.all_tech = Set(
        initialize=cemo.const.ALL_TECH)  # Set of generation technologies
    # set of fuel based gen technologies
    m.fuel_gen_tech = Set(initialize=cemo.const.FUEL_TECH) & m.all_tech
    # set of gen techs that obey linearised unit commitment constraints
    m.commit_gen_tech = Set(initialize=cemo.const.COMMIT_TECH) & m.all_tech
    # set of retireable technologies
    m.retire_gen_tech = Set(initialize=cemo.const.RETIRE_TECH) & m.all_tech
    # set of retireable technologies
    m.nobuild_gen_tech = Set(initialize=cemo.const.NOBUILD_TECH) & m.all_tech
    # Set of storage technologies
    m.stor_tech = Set(initialize=cemo.const.STOR_TECH) & m.all_tech
    # set of hybrid (gen+storage) technologies
    m.hyb_tech = Set(initialize=cemo.const.HYB_TECH) & m.all_tech
    # Set of dispatch intervals
    m.t = Set(ordered=True)

    # Sparse set of zones per region
    m.zones_in_regions = Set(dimen=2, initialize=init_zones_in_regions)
    # Set listing technologies avaialable per zone (like a sparsity pattern)
    m.gen_tech_in_zones = Set(dimen=2)
    # Retirable technologies avaialable per zone (like a sparsity pattern)
    m.retire_gen_tech_in_zones = Set(dimen=2)
    # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern)
    m.fuel_gen_tech_in_zones = Set(dimen=2)
    # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern)
    m.commit_gen_tech_in_zones = Set(dimen=2)
    # Renewable technologies avaialable per zone (like a sparsity pattern)
    m.re_gen_tech_in_zones = Set(dimen=2)
    # Dispatchable technologies avaialable per zone (like a sparsity pattern)
    m.disp_gen_tech_in_zones = Set(dimen=2)
    # Renewable Dispatchable technologies avaialable per zone (like a sparsity pattern)
    m.re_disp_gen_tech_in_zones = Set(dimen=2)
    # Set listing storage avaialable per zone (like a sparsity pattern)
    m.hyb_tech_in_zones = Set(dimen=2)
    # Set listing storage avaialable per zone (like a sparsity pattern)
    m.stor_tech_in_zones = Set(dimen=2)
    # Set listing transmission lines to other regions in each region
    m.intercons_in_zones = Set(dimen=2, initialize=init_intercons_in_zones)

    # sparse sets built by build actions
    # Returns a list of planning zones for each region in R
    m.zones_per_region = Set(m.regions, within=m.zones, initialize=[])
    # Returns a tuple with generating techs in each zone
    m.gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    # Returns a tuple with emitting techs in each zone
    m.fuel_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    # tuple for techs that obey linearised unit commitment constraints
    m.commit_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    m.re_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    m.disp_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    m.re_disp_gen_tech_per_zone = Set(m.zones,
                                      within=m.all_tech,
                                      initialize=[])
    # Returns a tuple with retirable techs in each zone
    m.retire_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    # Returns a tuple with storage techs in each zone
    m.stor_tech_per_zone = Set(m.zones, within=m.stor_tech, initialize=[])
    # Returns a tuple with emitting techs in each zone
    m.hyb_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[])
    # returns a tuple with transmission links in each region
    m.intercon_per_zone = Set(m.zones, initialize=[])

    # @@ Build actions
    # Scan TechinZones and populate ?_gen_tech_per_zone
    m.TpZ_build = BuildAction(rule=ScanForTechperZone)
    # Scan HybTechinZones and populate hyb_tech_per_zone
    m.HpZ_build = BuildAction(rule=ScanForHybridperZone)
    # Scan ZinR and populate ZperR
    m.ZpR_build = BuildAction(rule=ScanForZoneperRegion)
    # Scan TransLines and populate intercon_per_zone
    m.intercon_build = BuildAction(rule=build_intercon_per_zone)
    # Scan StorinZones and populate stor_tech_per_zone
    m.SpZ_build = BuildAction(rule=ScanForStorageperZone)

    # @@ Parameters
    # Capital costs generators
    # Build costs for generators
    m.cost_gen_build = Param(m.gen_tech_in_zones, default=9e7)
    m.cost_stor_build = Param(m.stor_tech_in_zones)  # Capital costs storage
    m.cost_hyb_build = Param(m.hyb_tech_in_zones)  # Capital costs hybrid
    # Capital costs $/MW/km trans
    m.cost_intercon_build = Param(m.intercons_in_zones,
                                  initialize=init_intercon_build_cost)

    m.cost_fuel = Param(m.fuel_gen_tech_in_zones,
                        initialize=init_default_fuel_price)  # Fuel cost

    # Fixed operating costs generators
    m.cost_gen_fom = Param(m.all_tech)
    # Variable operating costs generators
    m.cost_gen_vom = Param(m.all_tech)
    # Fixed operating costs storage
    m.cost_stor_fom = Param(m.stor_tech)
    # Variable operating costs storage
    m.cost_stor_vom = Param(m.stor_tech)
    # Fixed operating costs hybrid
    m.cost_hyb_fom = Param(m.hyb_tech)
    # Variable operating costs hybrid
    m.cost_hyb_vom = Param(m.hyb_tech)
    # Technology lifetime in years
    m.all_tech_lifetime = Param(m.all_tech, initialize=init_default_lifetime)
    # Project discount rate
    m.all_tech_discount_rate = Param(default=0.05)

    # Generator tech fixed charge rate
    m.fixed_charge_rate = Param(m.all_tech, initialize=init_fcr)
    # Transmission tech fixed charge rate
    m.intercon_fixed_charge_rate = Param(initialize=init_intercon_fcr)
    # Per year cost adjustment for sims shorter than 1 year of dispatch
    m.year_correction_factor = Param(initialize=init_year_correction_factor)

    m.cost_retire = Param(m.retire_gen_tech, initialize=init_cost_retire)
    m.cost_unserved = Param(initialize=cemo.const.DEFAULT_COSTS["unserved"]
                            )  # cost of unserved power
    # cost in $/kg of total emissions
    m.cost_emit = Param(initialize=cemo.const.DEFAULT_COSTS["emit"])
    m.cost_trans = Param(
        initialize=cemo.const.DEFAULT_COSTS["trans"])  # cost of transmission

    # Round trip efficiency of storage technology
    m.stor_rt_eff = Param(m.stor_tech, initialize=init_stor_rt_eff)
    # Number of hours of storage technology
    m.stor_charge_hours = Param(m.stor_tech, initialize=init_stor_charge_hours)

    # Collector multiple of hybrid technology
    m.hyb_col_mult = Param(m.hyb_tech, initialize=init_hyb_col_mult)
    # Number of hours of storage technology
    m.hyb_charge_hours = Param(m.hyb_tech, initialize=init_hyb_charge_hours)

    m.fuel_heat_rate = Param(m.fuel_gen_tech_in_zones,
                             initialize=init_default_heat_rate)
    # Emission rates
    m.fuel_emit_rate = Param(m.fuel_gen_tech,
                             initialize=init_default_fuel_emit_rate)
    # proportioning factors for notional interconnectors
    m.intercon_loss_factor = Param(m.intercons_in_zones,
                                   initialize=init_intercon_loss_factor)

    m.gen_cap_factor = Param(
        m.gen_tech_in_zones, m.t,
        initialize=init_cap_factor)  # Capacity factors for generators
    m.hyb_cap_factor = Param(
        m.hyb_tech_in_zones, m.t,
        initialize=init_cap_factor)  # Capacity factors for generators

    # Maximum capacity per generating technology per zone
    m.gen_build_limit = Param(m.gen_tech_in_zones,
                              initialize=init_gen_build_limit)
    m.gen_cap_initial = Param(m.gen_tech_in_zones,
                              default=0)  # operating capacity
    m.stor_cap_initial = Param(m.stor_tech_in_zones,
                               default=0)  # operating capacity
    m.hyb_cap_initial = Param(m.hyb_tech_in_zones,
                              default=0)  # operating capacity
    m.intercon_cap_initial = Param(
        m.intercons_in_zones,
        initialize=init_intercon_cap_initial)  # operating capacity
    # exogenous new capacity
    m.gen_cap_exo = Param(m.gen_tech_in_zones, default=0)
    # exogenous new storage capacity
    m.stor_cap_exo = Param(m.stor_tech_in_zones, default=0)
    # exogenous new hybrid capacity
    m.hyb_cap_exo = Param(m.hyb_tech_in_zones, default=0)
    # exogenous transmission capacity
    m.intercon_cap_exo = Param(m.intercons_in_zones, default=0)
    m.ret_gen_cap_exo = Param(m.retire_gen_tech_in_zones, default=0)
    # Net Electrical load (may include rooftop and EV)
    m.region_net_demand = Param(m.regions, m.t)
    # Zone load distribution factors as a pct of region demand
    m.zone_demand_factor = Param(m.zones,
                                 m.t,
                                 initialize=init_zone_demand_factors)
    # NEM operating reserve margin
    m.nem_operating_reserve = Param(default=0.075)
    # carry forward capital costs calculated
    m.cost_cap_carry_forward_sim = Param(m.zones, default=0)
    # carry forward capital costs NEM historical estimate
    m.cost_cap_carry_forward_hist = Param(m.zones, default=0)
    # carry forward capital costs total
    m.cost_cap_carry_forward = Param(m.zones, mutable=True)

    # Build action to Compute carry forward costs
    m.cost_carry_forward_build = BuildAction(
        rule=build_carry_fwd_cost_per_zone)

    # @@ Variables
    m.gen_cap_new = Var(m.gen_tech_in_zones,
                        within=NonNegativeReals)  # New capacity
    m.gen_cap_op = Var(m.gen_tech_in_zones,
                       within=NonNegativeReals)  # Total generation capacity
    m.stor_cap_new = Var(m.stor_tech_in_zones,
                         within=NonNegativeReals)  # New storage capacity
    m.stor_cap_op = Var(m.stor_tech_in_zones,
                        within=NonNegativeReals)  # Total storage capacity
    m.hyb_cap_new = Var(m.hyb_tech_in_zones, within=NonNegativeReals)
    m.hyb_cap_op = Var(m.hyb_tech_in_zones, within=NonNegativeReals)
    m.intercon_cap_new = Var(m.intercons_in_zones, within=NonNegativeReals)
    m.intercon_cap_op = Var(m.intercons_in_zones, within=NonNegativeReals)
    m.gen_cap_ret = Var(m.retire_gen_tech_in_zones,
                        within=NonNegativeReals)  # retireable capacity
    m.gen_cap_ret_neg = Var(m.retire_gen_tech_in_zones,
                            within=NonNegativeReals
                            )  # slack for exogenous retires beyond gen_op_cap
    m.gen_cap_exo_neg = Var(
        m.gen_tech_in_zones, within=NonNegativeReals
    )  # slack for exogenous builds exceeding gen_build_limit
    m.gen_disp = Var(m.gen_tech_in_zones, m.t,
                     within=NonNegativeReals)  # dispatched power
    # Variables for committed power constraints
    m.gen_disp_com = Var(m.commit_gen_tech_in_zones,
                         m.t,
                         within=NonNegativeReals)
    m.gen_disp_com_p = Var(m.commit_gen_tech_in_zones,
                           m.t,
                           within=NonNegativeReals)
    m.gen_disp_com_m = Var(m.commit_gen_tech_in_zones,
                           m.t,
                           within=NonNegativeReals)
    m.gen_disp_com_s = Var(m.commit_gen_tech_in_zones,
                           m.t,
                           within=NonNegativeReals)

    m.stor_disp = Var(m.stor_tech_in_zones, m.t,
                      within=NonNegativeReals)  # dispatched power from storage
    m.stor_reserve = Var(
        m.stor_tech_in_zones, m.t,
        within=NonNegativeReals)  # dispatched power from storage
    m.stor_charge = Var(m.stor_tech_in_zones, m.t,
                        within=NonNegativeReals)  # power to charge storage

    m.hyb_disp = Var(m.hyb_tech_in_zones, m.t,
                     within=NonNegativeReals)  # dispatched power from hybrid

    m.hyb_reserve = Var(
        m.hyb_tech_in_zones, m.t,
        within=NonNegativeReals)  # reserve capacity for hybrids

    m.hyb_charge = Var(m.hyb_tech_in_zones, m.t,
                       within=NonNegativeReals)  # charging power from hybrid

    m.stor_level = Var(m.stor_tech_in_zones, m.t,
                       within=NonNegativeReals)  # Charge level for storage

    m.hyb_level = Var(m.hyb_tech_in_zones, m.t,
                      within=NonNegativeReals)  # Charge level for storage

    m.unserved = Var(m.zones, m.t, within=NonNegativeReals)  # unserved power
    m.surplus = Var(m.zones, m.t,
                    within=NonNegativeReals)  # surplus power (if any)

    # Interconnector flow
    m.intercon_disp = Var(m.intercons_in_zones, m.t, within=NonNegativeReals)

    # @@ Constraints
    # Transmission limits
    m.con_max_trans = Constraint(m.intercons_in_zones, m.t, rule=con_max_trans)
    # Transmission capacity balance
    m.con_intercon_cap = Constraint(m.intercons_in_zones,
                                    rule=con_intercon_cap)
    # Load balance
    m.ldbal = Constraint(m.zones, m.t, rule=con_ldbal)
    # Dispatch to be within capacity, RE have variable capacity factors
    m.caplim = Constraint(m.gen_tech_in_zones, m.t, rule=con_caplim)
    # Limit maximum capacity to be built in each region and each technology
    m.maxcap = Constraint(m.gen_tech_in_zones, rule=con_maxcap)
    # gen_cap_op in existing period is previous gen_cap_op plus gen_cap_new
    m.con_gen_cap = Constraint(m.gen_tech_in_zones, rule=con_gen_cap)
    # MaxMWh limit
    m.con_max_mwh_per_zone = Constraint(m.gen_tech_in_zones,
                                        rule=con_max_mhw_per_zone)
    # MaxMWh limit (currently only for hydro)
    m.con_max_mwh_nem_wide = Constraint(m.all_tech, rule=con_max_mwh_nem_wide)
    # Slack constraint on exogenous retirement to prevent it to go nevative
    m.con_slackretire = Constraint(m.retire_gen_tech_in_zones,
                                   rule=con_slackretire)
    # Slack constraint on exogenous retirement to prevent it to go nevative
    m.con_slackbuild = Constraint(m.gen_tech_in_zones, rule=con_slackbuild)

    # linearised unit commitment constraints
    m.con_min_load_commit = Constraint(m.commit_gen_tech_in_zones,
                                       m.t,
                                       rule=con_min_load_commit)
    m.con_disp_ramp_down = Constraint(m.commit_gen_tech_in_zones,
                                      m.t,
                                      rule=con_disp_ramp_down)
    m.con_disp_ramp_up = Constraint(m.commit_gen_tech_in_zones,
                                    m.t,
                                    rule=con_disp_ramp_up)
    m.con_ramp_down_uptime = Constraint(m.commit_gen_tech_in_zones,
                                        m.t,
                                        rule=con_ramp_down_uptime)
    m.con_uptime_commitment = Constraint(m.commit_gen_tech_in_zones,
                                         m.t,
                                         rule=con_uptime_commitment)
    m.con_committed_cap = Constraint(m.commit_gen_tech_in_zones,
                                     m.t,
                                     rule=con_committed_cap)
    # NEM operating reserve constraint
    m.con_operating_reserve = Constraint(m.regions,
                                         m.t,
                                         rule=con_operating_reserve)
    # Hard constraint on unserved energy
    if unslim:
        m.con_uns = Constraint(m.regions, rule=con_uns)
    # Emmissions constraint
    if emitlimit:
        m.con_emissions = Constraint(rule=con_emissions)
        # maximum kg/MWh rate of total emissions
        m.nem_year_emit_limit = Param()
    # NEM wide RET constraint as a ratio
    if nem_ret_ratio:
        # NEM wide renewable energy target for current year
        m.nem_ret_ratio = Param(default=0)
        # NEM wide renewable energy constraint
        m.con_nem_ret_ratio = Constraint(rule=con_nem_ret_ratio)

# NEM wide RET constraint as a ratio
    if nem_ret_gwh:
        # NEM wide renewable energy target for current year
        m.nem_ret_gwh = Param(default=0)
        # NEM wide renewable energy constraint
        m.con_nem_ret_gwh = Constraint(rule=con_nem_ret_gwh)

    if region_ret_ratio:
        # Regional RET targets for current year
        m.region_ret_ratio = Param(m.regions, default=0)
        # Regional RET constraint
        m.con_region_ret = Constraint(m.regions, rule=con_region_ret_ratio)
    if nem_re_disp_ratio:
        # NEM wide minimum hour by our generation from "dispatchable" sources
        m.nem_re_disp_ratio = Param(default=0)
        # NEM wide minimum hourly dispatch from dispatchable sources constraint
        m.con_nem_re_disp_ratio = Constraint(m.regions,
                                             m.t,
                                             rule=con_nem_re_disp_ratio)

    # Storage charge/discharge dynamic
    m.StCharDis = Constraint(m.stor_tech_in_zones, m.t, rule=con_storcharge)
    # Maxiumum rate of storage charge
    m.con_stor_flow_lim = Constraint(m.stor_tech_in_zones,
                                     m.t,
                                     rule=con_stor_flow_lim)
    # Maxiumum rate of storage discharge
    m.con_stor_reserve_lim = Constraint(m.stor_tech_in_zones,
                                        m.t,
                                        rule=con_stor_reserve_lim)
    # Maxiumum charge capacity of storage
    m.MaxCharge = Constraint(m.stor_tech_in_zones, m.t, rule=con_maxcharge)
    # StCap in existing period is previous stor_cap_op plus stor_cap_new
    m.con_stor_cap = Constraint(m.stor_tech_in_zones, rule=con_stor_cap)

    # Hybrid charge/discharge dynamic
    m.HybCharDis = Constraint(m.hyb_tech_in_zones, m.t, rule=con_hybcharge)
    # Maxiumum level of hybrid storage discharge
    m.con_hyb_level_max = Constraint(m.hyb_tech_in_zones,
                                     m.t,
                                     rule=con_hyb_level_max)
    # Maxiumum rate of hybrid storage charge/discharge
    m.con_hyb_flow_lim = Constraint(m.hyb_tech_in_zones,
                                    m.t,
                                    rule=con_hyb_flow_lim)
    # Limit hybrid reserve capacity to be within storage level
    m.con_hyb_reserve_lim = Constraint(m.hyb_tech_in_zones,
                                       m.t,
                                       rule=con_hyb_reserve_lim)
    # Maxiumum charge capacity of storage
    m.MaxChargehy = Constraint(m.hyb_tech_in_zones, m.t, rule=con_maxchargehy)
    # HyCap in existing period is previous stor_cap_op plus stor_cap_new
    m.con_hyb_cap = Constraint(m.hyb_tech_in_zones, rule=con_hyb_cap)

    # @@ Objective
    # Minimise capital, variable and fixed costs of system
    m.FSCost = Expression(expr=0)
    m.SSCost = Expression(rule=obj_cost)
    # objective: minimise all other objectives
    m.Obj = Objective(expr=m.FSCost + m.SSCost)

    # Short run marginal prices
    m.dual = Suffix(direction=Suffix.IMPORT)
    return m
Ejemplo n.º 13
0
    def create_sets(self):
        # Sets
        # Set of NEM regions
        self.m.regions = Set(initialize=cemo.const.REGION.keys())
        self.m.zones = Set(
            initialize=cemo.const.ZONE.keys())  # Set of NTNDP planning zones
        self.m.all_tech = Set(
            initialize=cemo.const.ALL_TECH)  # Set of generation technologies
        # set of fuel based gen technologies
        self.m.fuel_gen_tech = Set(
            initialize=cemo.const.FUEL_TECH) & self.m.all_tech
        # set of gen techs that obey linearised unit commitment constraints
        self.m.commit_gen_tech = Set(
            initialize=cemo.const.COMMIT_TECH) & self.m.all_tech
        # set of retireable technologies
        self.m.retire_gen_tech = Set(
            initialize=cemo.const.RETIRE_TECH) & self.m.all_tech
        # set of retireable technologies
        self.m.nobuild_gen_tech = Set(
            initialize=cemo.const.NOBUILD_TECH) & self.m.all_tech
        # Set of storage technologies
        self.m.stor_tech = Set(
            initialize=cemo.const.STOR_TECH) & self.m.all_tech
        # set of hybrid (gen+storage) technologies
        self.m.hyb_tech = Set(initialize=cemo.const.HYB_TECH) & self.m.all_tech
        self.m.ev_tech = Set(initialize=cemo.const.EV_TECH) & self.m.all_tech

        # Set of dispatch intervals
        self.m.t = Set(ordered=True)

        # Sparse set of zones per region
        self.m.zones_in_regions = Set(
            dimen=2, initialize=init_zones_in_regions)
        # Set listing technologies avaialable per zone (like a sparsity pattern)
        self.m.gen_tech_in_zones = Set(dimen=2)
        # Retirable technologies avaialable per zone (like a sparsity pattern)
        self.m.retire_gen_tech_in_zones = Set(dimen=2)
        # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern)
        self.m.fuel_gen_tech_in_zones = Set(dimen=2)
        # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern)
        self.m.commit_gen_tech_in_zones = Set(dimen=2)
        # Renewable technologies avaialable per zone (like a sparsity pattern)
        self.m.re_gen_tech_in_zones = Set(dimen=2)
        # Dispatchable technologies avaialable per zone (like a sparsity pattern)
        self.m.disp_gen_tech_in_zones = Set(dimen=2)
        # Renewable Dispatchable technologies avaialable per zone (like a sparsity pattern)
        self.m.re_disp_gen_tech_in_zones = Set(dimen=2)
        # Set listing storage avaialable per zone (like a sparsity pattern)
        self.m.hyb_tech_in_zones = Set(dimen=2)
        # Set listing ev avaialable per zone (like a sparsity pattern) #KP_ADDED
        self.m.ev_tech_in_zones = Set(dimen=2)
        # Set listing storage avaialable per zone (like a sparsity pattern)
        self.m.stor_tech_in_zones = Set(dimen=2)
        # Set listing transmission lines to other regions in each region
        self.m.intercons_in_zones = Set(
            dimen=2, initialize=init_intercons_in_zones)

        # sparse sets built by build actions
        # Returns a list of planning zones for each region in R
        self.m.zones_per_region = Set(
            self.m.regions, within=self.m.zones, initialize=[])
        # Returns a tuple with generating techs in each zone
        self.m.gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # Returns a tuple with emitting techs in each zone
        self.m.fuel_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # tuple for techs that obey linearised unit commitment constraints
        self.m.commit_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        self.m.re_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        self.m.disp_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        self.m.re_disp_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # Returns a tuple with retirable techs in each zone
        self.m.retire_gen_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # Returns a tuple with storage techs in each zone
        self.m.stor_tech_per_zone = Set(
            self.m.zones, within=self.m.stor_tech, initialize=[])
        # Returns a tuple with emitting techs in each zone
        self.m.hyb_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # Returns a tuple with emitting techs in each zone
        self.m.ev_tech_per_zone = Set(
            self.m.zones, within=self.m.all_tech, initialize=[])
        # returns a tuple with transmission links in each region
        self.m.intercon_per_zone = Set(self.m.zones, initialize=[])

        self.m.smart_charge_tech = Set(initialize=cemo.const.SMART_CHARGE_EV_TECH) & self.m.all_tech
        # set of ev tech WITHOUT V2G enabled
        self.m.v2g_tech = Set(initialize=cemo.const.V2G_EV_TECH) & self.m.all_tech


        # @@ Build actions
        # Scan TechinZones and populate ?_gen_tech_per_zone
        self.m.TpZ_build = BuildAction(rule=ScanForTechperZone)
        # Scan HybTechinZones and populate hyb_tech_per_zone
        self.m.HpZ_build = BuildAction(rule=ScanForHybridperZone)
        # Scan EVTechinZones and populate ev_tech_per_zone
        self.m.EpZ_build = BuildAction(rule=ScanForEVperZone)
        # Scan ZinR and populate ZperR
        self.m.ZpR_build = BuildAction(rule=ScanForZoneperRegion)
        # Scan TransLines and populate intercon_per_zone
        self.m.intercon_build = BuildAction(rule=build_intercon_per_zone)
        # Scan StorinZones and populate stor_tech_per_zone
        self.m.SpZ_build = BuildAction(rule=ScanForStorageperZone)
Ejemplo n.º 14
0
    def create_params(self):
        # @@ Parameters
        # Capital costs generators
        # Build costs for generators
        self.m.cost_gen_build = Param(
            self.m.gen_tech_in_zones, initialize=init_default_capex)
        self.m.cost_stor_build = Param(
            self.m.stor_tech_in_zones)  # Capital costs storage
        self.m.cost_hyb_build = Param(
            self.m.hyb_tech_in_zones)  # Capital costs hybrid
        self.m.cost_ev_build = Param(
            self.m.ev_tech_in_zones) #,  initialize=init_default_capex)
        # Capital costs $/MW/km trans
        self.m.cost_intercon_build = Param(
            self.m.intercons_in_zones, initialize=init_intercon_build_cost)

        self.m.cost_fuel = Param(
            self.m.fuel_gen_tech_in_zones,
            initialize=init_default_fuel_price)  # Fuel cost

        # Fixed operating costs generators
        self.m.cost_gen_fom = Param(self.m.all_tech)
        # Variable operating costs generators
        self.m.cost_gen_vom = Param(self.m.all_tech)
        # Fixed operating costs storage
        self.m.cost_stor_fom = Param(self.m.stor_tech)
        # Variable operating costs storage
        self.m.cost_stor_vom = Param(self.m.stor_tech)
        # Fixed operating costs hybrid
        self.m.cost_hyb_fom = Param(self.m.hyb_tech)
        # Variable operating costs hybrid
        self.m.cost_hyb_vom = Param(self.m.hyb_tech)

        # Fixed operating costs ev
        self.m.cost_ev_fom = Param(self.m.ev_tech)
        # Variable operating costs ev
        self.m.cost_ev_vom = Param(self.m.ev_tech)

        # Technology lifetime in years
        self.m.all_tech_lifetime = Param(
            self.m.all_tech, initialize=init_default_lifetime)
        # Project discount rate
        self.m.all_tech_discount_rate = Param(default=0.05)
        # % Fleet V2G EV Discharging enabled
        self.m.percent_v2g_enabled = Param(default=0)
        # % Fleet Smart EV Charging enabled
        self.m.percent_smart_enabled = Param(default=0.5)
        # EV Fleet Batt Level Floor
        self.m.ev_level_floor = Param(default=0.2)

        # Generator tech fixed charge rate
        self.m.fixed_charge_rate = Param(self.m.all_tech, initialize=init_fcr)
        # Transmission tech fixed charge rate
        self.m.intercon_fixed_charge_rate = Param(initialize=init_intercon_fcr)
        # Per year cost adjustment for sims shorter than 1 year of dispatch
        self.m.year_correction_factor = Param(
            initialize=init_year_correction_factor)

        self.m.cost_retire = Param(
            self.m.retire_gen_tech, initialize=init_cost_retire)
        self.m.cost_unserved = Param(
            initialize=cemo.const.
            DEFAULT_COSTS["unserved"])  # cost of unserved power
        # cost in $/kg of total emissions
        self.m.cost_emit = Param(initialize=cemo.const.DEFAULT_COSTS["emit"])
        self.m.cost_trans = Param(
            initialize=cemo.const.DEFAULT_COSTS["trans"])  # cost of transmission

        # Round trip efficiency of storage technology
        self.m.stor_rt_eff = Param(
            self.m.stor_tech, initialize=init_stor_rt_eff)
        # Number of hours of storage technology
        self.m.stor_charge_hours = Param(
            self.m.stor_tech, initialize=init_stor_charge_hours)

        # Round trip efficiency of ev technology
        self.m.ev_rt_eff = Param(self.m.ev_tech, initialize=init_ev_rt_eff)
        self.m.ev_connected =  Param(self.m.ev_tech_in_zones, self.m.t, default=0) #KP_MODIFIED_180820_2 from  ev_tech_in_zones #KP_MODIFIED_030920 changed back to ev_tech_in_zones from ev_tech
        # Max charge rate & v2g discharge rate
        self.m.ev_max_charge_rate = Param(self.m.ev_tech, initialize=init_ev_charge_rate)
        # Battery size per vehicle
        self.m.ev_batt_size = Param(self.m.ev_tech, initialize=init_ev_batt_size)
        self.m.ev_trans_trace = Param(self.m.ev_tech_in_zones, self.m.t, default=0)  # Trnasport energy trace read in #KP_MODIFIED_180820_2 from self.m.ev_tech_in_zones  #KP_MODIFIED_030920 changed back to ev_tech_in_zones from ev_tech
        self.m.ev_charge_dumb_trace = Param(self.m.ev_tech_in_zones,  self.m.t, default=0)


        # Collector multiple of hybrid technology
        self.m.hyb_col_mult = Param(
            self.m.hyb_tech, initialize=init_hyb_col_mult)
        # Number of hours of storage technology
        self.m.hyb_charge_hours = Param(
            self.m.hyb_tech, initialize=init_hyb_charge_hours)

        self.m.fuel_heat_rate = Param(
            self.m.fuel_gen_tech_in_zones, initialize=init_default_heat_rate)
        # Emission rates
        self.m.fuel_emit_rate = Param(
            self.m.fuel_gen_tech, initialize=init_default_fuel_emit_rate)
        # proportioning factors for notional interconnectors
        self.m.intercon_loss_factor = Param(
            self.m.intercons_in_zones, initialize=init_intercon_loss_factor)

        self.m.gen_cap_factor = Param(
            self.m.gen_tech_in_zones, self.m.t,
            initialize=init_cap_factor, mutable=True)  # Capacity factors for generators
        self.m.hyb_cap_factor = Param(
            self.m.hyb_tech_in_zones, self.m.t,
            initialize=init_cap_factor, mutable=True)  # Capacity factors for generators
        self.m.ev_cap_factor = Param(
            self.m.ev_tech_in_zones, self.m.t,
            initialize=init_cap_factor, mutable=True)
        # Revise cap factors for numbers below threshold of 1e-5
        self.m.build_cap_factor_thres = BuildAction(rule=build_cap_factor_thres)

        # Maximum capacity per generating technology per zone
        self.m.gen_build_limit = Param(
            self.m.gen_tech_in_zones, initialize=init_gen_build_limit)
        self.m.gen_cap_initial = Param(
            self.m.gen_tech_in_zones, default=0, mutable=True)  # operating capacity
        self.m.stor_cap_initial = Param(
            self.m.stor_tech_in_zones, default=0)  # operating capacity
        self.m.hyb_cap_initial = Param(
            self.m.hyb_tech_in_zones, default=0)  # operating capacity
        self.m.ev_cap_initial = Param(
            self.m.ev_tech_in_zones, default=0)
        self.m.intercon_cap_initial = Param(
            self.m.intercons_in_zones, initialize=init_intercon_cap_initial)  # operating capacity
        # exogenous new capacity
        self.m.gen_cap_exo = Param(self.m.gen_tech_in_zones, default=0, mutable=True)
        # exogenous new storage capacity
        self.m.stor_cap_exo = Param(self.m.stor_tech_in_zones, default=0)
        # exogenous new hybrid capacity
        self.m.hyb_cap_exo = Param(self.m.hyb_tech_in_zones, default=0)
        self.m.ev_cap_exo = Param(self.m.ev_tech_in_zones, default=0)

        # exogenous transmission capacity
        self.m.intercon_cap_exo = Param(self.m.intercons_in_zones, default=0)
        self.m.ret_gen_cap_exo = Param(
            self.m.retire_gen_tech_in_zones, default=0, mutable=True)
        # Net Electrical load (may include rooftop and EV)
        self.m.region_net_demand = Param(self.m.regions, self.m.t)
        # Zone load distribution factors as a pct of region demand
        self.m.zone_demand_factor = Param(
            self.m.zones, self.m.t, initialize=init_zone_demand_factors)
        # carry forward capital costs calculated
        self.m.cost_cap_carry_forward_sim = Param(self.m.zones, default=0)
        # carry forward capital costs NEM historical estimate
        self.m.cost_cap_carry_forward_hist = Param(self.m.zones, default=0)
        # carry forward capital costs total
        self.m.cost_cap_carry_forward = Param(self.m.zones, mutable=True)

        # Build action to Compute carry forward costs
        self.m.cost_carry_forward_build = BuildAction(
            rule=build_carry_fwd_cost_per_zone)
        # Create params for model options
        for option in self.model_options._fields:
            if getattr(self.model_options, option):
                if cemo.const.DEFAULT_MODEL_OPT.get(option, {}).get("index", None) is None:
                    setattr(self.m,
                            option,
                            Param(default=cemo.const.DEFAULT_MODEL_OPT.get(option, {}).get('value', 0))
                            )
                else:
                    setattr(self.m,
                            option,
                            Param(eval(cemo.const.DEFAULT_MODEL_OPT[option].get("index", None)),
                                  default=cemo.const.DEFAULT_MODEL_OPT.get(option, {}).get('value', 0))
                            )
        # Build action to prevent exogenous buids to exceed build limits
        self.m.build_adjust_exo_cap = BuildAction(rule=build_adjust_exo_cap)
        # Build action to prevent exogenous retires to make capacity negative
        self.m.build_adjust_exo_ret = BuildAction(rule=build_adjust_exo_ret)
Ejemplo n.º 15
0
    def _create(self, group=None):
        if group is None:
            return None

        m = self.parent_block()

        # for all DSM components get inflow from bus_elec
        for n in group:
            n.inflow = list(n.inputs)[0]

        #  ************* SETS *********************************

        # Set of DSM Components
        self.DSM = Set(initialize=[n for n in group])

        #  ************* VARIABLES *****************************

        def dsm_capacity_bound_rule(block):
            """Rule definition for bounds(capacity) of DSM - Variable g in timestep t"""
            for t in m.TIMESTEPS:
                for g in group:
                    bounds = (-g.c_do[t], g.c_up[t])
                    return bounds

        # Variable load shift down (MWh)
        self.DSMupdown = Var(self.DSM,
                             m.TIMESTEPS,
                             initialize=0,
                             within=Reals,
                             bounds=dsm_capacity_bound_rule)

        #  ************* CONSTRAINTS *****************************

        # Demand Production Relation
        def _input_output_relation_rule(block):
            """
            Relation between input data and pyomo variables. The actual demand after DSM.
            Generator Production == Demand_el +- DSM
            """
            for t in m.TIMESTEPS:
                for g in group:
                    # Generator loads directly from bus
                    lhs = m.flow[g.inflow, g, t]

                    # Demand +- DSM
                    rhs = g.demand[t] + self.DSMupdown[g, t]

                    # add constraint
                    block.input_output_relation.add((g, t), (lhs == rhs))

        self.input_output_relation = Constraint(group,
                                                m.TIMESTEPS,
                                                noruleinit=True)
        self.input_output_relation_build = BuildAction(
            rule=_input_output_relation_rule)

        # Equation 7
        def dsm_sum_constraint_rule(block):
            """
            Relation to compensate the total amount of positive and negative DSM in between the shift_interval.
            2 Cases: A full interval is optimised or an incomplete one.
            """
            for t in m.TIMESTEPS:
                for g in group:

                    shft_intvl = g.shift_interval

                    # full interval
                    if (t // shft_intvl) < (m.TIMESTEPS._bounds[1] //
                                            shft_intvl):
                        # DSM up/down
                        lhs = sum(
                            self.DSMupdown[g, tt]
                            for tt in range((t // shft_intvl) *
                                            shft_intvl, (t // shft_intvl + 1) *
                                            shft_intvl, 1))
                        # value
                        rhs = 0
                        # add constraint
                        block.dsm_sum_constraint.add((g, t), (lhs == rhs))

                    # incomplete interval
                    else:
                        # DSM up/down
                        lhs = sum(
                            self.DSMupdown[g, tt]
                            for tt in range((t // shft_intvl) * shft_intvl,
                                            m.TIMESTEPS._bounds[1] + 1, 1))
                        # value
                        rhs = 0
                        # add constraint
                        block.dsm_sum_constraint.add((g, t), (lhs == rhs))

        self.dsm_sum_constraint = Constraint(group,
                                             m.TIMESTEPS,
                                             noruleinit=True)
        self.dsm_sum_constraint_build = BuildAction(
            rule=dsm_sum_constraint_rule)