Source code for prepshot._model.co2

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""This module contains the constraints and expressions related to
carbon emissions. The model computes the carbon emissions for each year, 
based on the sum of carbon emissions from each zone, and from each technology
as follows:

.. math::

    {\\rm{carbon}}_y=\\sum_{e\\in\\mathcal{E}}\\sum_{z\\in\mathcal{Z}}
    \\sum_{m\\in\mathcal{M}}\\sum_{h\\in\mathcal{H}}
    \\left({{\\rm{CARBON}}}_{y,z,e}\\times 
    {\\rm{gen}}_{h,m,y,z,e}\\right)\\quad\\forall y

The calculated carbon emission for each year lower than its upper bound, as follows:

.. math::
    
    {\\rm{carbon}}_y\\le{\\overline{{\\rm{CARBON}}}}_y\\quad\\forall y
    
"""
import pyoptinterface as poi
import numpy as np

[docs]class AddCo2EmissionConstraints: """Class for carbon emission constraints and calculations. """
[docs] def __init__(self, model : object) -> None: """Initialize the class. Parameters ---------- model : object Model object depending on the solver. """ self.model = model model.carbon_offset = model.add_variables( model.year, model.zone, lb=0 ) model.carbon_breakdown = poi.make_tupledict( model.year, model.zone, model.tech, rule=self.carbon_breakdown ) model.carbon_capacity = poi.make_tupledict( model.year, model.zone, rule=self.emission_calc_by_zone_rule ) model.carbon = poi.make_tupledict( model.year, rule=self.emission_calc_rule ) # Carbon emission limits: one row per (limit_id, year) in # policy_carbon_emission_limit.csv with cols # limit_id, year, unit, value, zones # where `zones` is a comma-separated list of member zone codes. # Each row becomes one LP constraint: # sum(carbon_capacity[year, z] for z in zones) <= value emission_limits_df = model.params['carbon_emission_limit'] for _, lr in emission_limits_df.iterrows(): if lr['value'] == np.inf: continue member_zones = [z.strip() for z in str(lr['zones']).split(',') if z.strip()] if not member_zones: continue lhs = poi.quicksum( model.carbon_capacity[lr['year'], z] for z in member_zones ) - lr['value'] model.add_linear_constraint( lhs, poi.Leq, 0, name=f"emission_limit_{lr['limit_id']}_{lr['year']}", ) model.carbon_offset_limit_cons = poi.make_tupledict( model.year, model.zone, rule=self.carbon_offset_limit_rule )
[docs] def emission_calc_rule(self, y : int) -> poi.ConstraintIndex: """Calculation of annual carbon emission across all zones and technologies. Parameters ---------- y : int Planned year. Returns ------- poi.ConstraintIndex A constraint of the model. """ model = self.model return poi.quicksum(model.carbon_capacity.select(y, '*'))
[docs] def emission_calc_by_zone_rule( self, y : int, z : str ) -> poi.ExprBuilder: """Calculation of annual net carbon emissions by zone, after subtracting purchased carbon offsets. Parameters ---------- y : int Planned year. z : str Zone. Returns ------- poi.ExprBuilder An expression for net zonal emissions. """ model = self.model return poi.quicksum(model.carbon_breakdown.select(y, z, '*')) \ - model.carbon_offset[y, z]
[docs] def carbon_offset_limit_rule( self, y : int, z : str ) -> poi.ConstraintIndex: """Limit purchased carbon offsets to a fraction of raw zonal emissions. ``carbon_offset[y,z] <= rate * raw_emissions[y,z]`` where ``raw_emissions = carbon_capacity + carbon_offset`` (i.e. the emissions before offsets are deducted). Parameters ---------- y : int Planned year. z : str Zone. Returns ------- poi.ConstraintIndex A constraint of the model. """ model = self.model rate = model.params['carbon_offset_limit'][z, y] raw_emissions = poi.quicksum(model.carbon_breakdown.select(y, z, '*')) lhs = model.carbon_offset[y, z] - rate * raw_emissions return model.add_linear_constraint(lhs, poi.Leq, 0)
[docs] def carbon_breakdown( self, y : int, z : str, te : str ) -> poi.ExprBuilder: """Carbon emission cost breakdown. Parameters ---------- y : int Year. z : str Zone. te : str Technology. Returns ------- poi.ExprBuilder The expression of the model. """ model = self.model ef = model.params['emission_factor'][te, y] w = model.params['weight'] return 1 / w * ef * poi.quicksum(model.gen.select('*', '*', y, z, te))