Example #1
0
def test__EcoDeclaration__constructor__not_all_emissions_have_same_keys__should_set_default_None_where_missing(
):
    declaration = EcoDeclaration(
        emissions={
            begin1: EmissionValues(CO2=100, NOx=100),
            begin2: EmissionValues(CO2=100, CO=100),
        },
        consumed_amount={
            begin1: 100,
            begin2: 100,
        },
        retired_amount={},
        technologies={
            begin1: EmissionValues(Solar=10, Wind=90),
            begin2: EmissionValues(Solar=20, Wind=80),
        },
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    assert sorted(declaration.emissions[begin1].keys()) == sorted(
        ['CO2', 'CO', 'NOx'])
    assert sorted(declaration.emissions[begin2].keys()) == sorted(
        ['CO2', 'CO', 'NOx'])
    assert declaration.emissions[begin1]['CO'] is None
    assert declaration.emissions[begin2]['NOx'] is None
    def build_general_declaration(self, measurements, general_mix_emissions):
        """
        :param list[Measurement] measurements:
        :param dict[datetime, dict[str, EmissionData]] general_mix_emissions:
        :rtype: EcoDeclaration
        """

        # Emission in gram (mapped by begin)
        # {begin: {key: value}}
        emissions: Dict[datetime, EmissionValues[str, float]] = {}

        # Consumption in Wh (mapped by begin)
        # {begin: amount}
        consumed_amount: Dict[datetime, float] = {}

        # Consumed amount in Wh per technology (mapped by begin)
        # {begin: {technology: amount}}
        technologies: Dict[datetime, EmissionValues[str, float]] = {}

        # Group measurements by their begin
        measurements_sorted_and_grouped = groupby(
            iterable=sorted(measurements, key=lambda m: m.begin),
            key=lambda m: m.begin,
        )

        for begin, measurements in measurements_sorted_and_grouped:
            # unique_sectors_this_begin = set(m.sector for m in measurements)
            unique_sectors = ('DK1', 'DK2')

            for sector in unique_sectors:
                if sector not in general_mix_emissions[begin]:
                    continue
                mix = general_mix_emissions[begin][sector]

                emissions.setdefault(begin, EmissionValues())
                emissions[begin] += mix.emissions

                consumed_amount.setdefault(begin, 0)
                consumed_amount[begin] += mix.amount

                technologies.setdefault(begin, EmissionValues())
                technologies[begin] += mix.technologies

        return EcoDeclaration(
            emissions=emissions,
            consumed_amount=consumed_amount,
            retired_amount={},
            technologies=technologies,
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
Example #3
0
    def emissions(self):
        """
        Returns Emissions in gram

        :rtype: EmissionValues[str, float]
        """
        return sum((part.emissions for part in self.parts), EmissionValues())
Example #4
0
def mix_emissions_to_emission_data(mix_emissions):
    mix_emissions_grouped = groupby(
        iterable=mix_emissions,
        key=lambda row:
        (parse_mix_emissions_timestamp(row['timestamp_utc']), row['sector']),
    )

    for (timestamp_utc, sector), rows in mix_emissions_grouped:
        parts = []

        for row in rows:
            emissions = EmissionValues(
                **{
                    k: v
                    for k, v in row.items()
                    if k not in ('timestamp_utc', 'sector', 'technology',
                                 'amount')
                })

            parts.append(
                EmissionPart(
                    technology=row['technology'],
                    amount=row['amount'],
                    emissions=emissions,
                ))

        yield EmissionData(
            timestamp_utc=timestamp_utc,
            sector=sector,
            parts=parts,
        )
Example #5
0
def test__EcoDeclaration__constructor__consumed_amount_is_not_of_type_dict__should_raise_ValueError(
):
    with pytest.raises(ValueError):
        EcoDeclaration(
            emissions={
                begin1: EmissionValues(),
                begin2: EmissionValues(),
            },
            consumed_amount=123,  # Should be dict
            retired_amount={},
            technologies={
                begin1: EmissionValues(Solar=110, Wind=90),
                begin2: EmissionValues(Solar=120, Wind=80),
            },
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
Example #6
0
    def technologies(self):
        """
        Returns consumed amount per technology

        :rtype: EmissionValues[str, int]
        """
        return EmissionValues(
            **{part.technology: part.amount
               for part in self.parts})
def test__EmissionData__amount__has_parts___should_return_sum_of_parts():
    uut = EmissionData(
        sector='DK1',
        timestamp_utc=datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc),
        parts=[
            EmissionPart(technology='A',
                         amount=111,
                         emissions=EmissionValues()),
            EmissionPart(technology='B',
                         amount=222,
                         emissions=EmissionValues()),
            EmissionPart(technology='C',
                         amount=333,
                         emissions=EmissionValues()),
        ],
    )

    assert uut.amount == 111 + 222 + 333
def test__EmissionData__emissions_per_wh__has_parts_but_amount_is_zero___should_return_empty_EmissionValues(
):
    uut = EmissionData(
        sector='DK1',
        timestamp_utc=datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc),
        parts=[
            EmissionPart(technology='A',
                         amount=0,
                         emissions=EmissionValues(CO2=100, CO=200)),
            EmissionPart(technology='B',
                         amount=0,
                         emissions=EmissionValues(CO2=300, CO=400, NOx=500)),
            EmissionPart(technology='C',
                         amount=0,
                         emissions=EmissionValues(CO=600, NOx=700)),
        ],
    )

    assert isinstance(uut.emissions_per_wh, EmissionValues)
    assert uut.emissions_per_wh == {}
Example #9
0
def test__EcoDeclaration__constructor__technologies_values_are_not_all_of_type_EmissionValues__should_raise_ValueError(
):
    with pytest.raises(ValueError):
        EcoDeclaration(
            emissions={
                begin1: EmissionValues(),
                begin2: EmissionValues(),
            },
            consumed_amount={
                begin1: 100,
                begin2: 100,
            },
            retired_amount={},
            technologies={
                begin1: 123,  # Should be EmissionValues
                begin2: EmissionValues(Solar=20, Wind=80),
            },
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
Example #10
0
def test__EcoDeclaration__constructor__emissions_and_consumed_amount_does_not_have_the_same_keys__should_raise_ValueError(
):
    with pytest.raises(ValueError):
        EcoDeclaration(
            emissions={
                begin1: EmissionValues(),
                begin2: EmissionValues(),
            },
            consumed_amount={
                begin2:
                100,  # Should have begin1 and begin2 as keys, like emissions
                begin3: 100,
            },
            retired_amount={},
            technologies={
                begin1: EmissionValues(Solar=110, Wind=90),
                begin2: EmissionValues(Solar=120, Wind=80),
            },
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
Example #11
0
    def technologies_share(self):
        """
        Returns consumed amount per technology

        :rtype: EmissionValues[str, int]
        """
        amount = self.amount

        if amount > 0:
            return self.technologies / amount
        else:
            return EmissionValues()
Example #12
0
    def emissions_per_wh(self):
        """
        Returns Emissions per Wh (gram/Wh)

        :rtype: EmissionValues[str, float]
        """
        amount = self.amount

        if amount > 0:
            return self.emissions / amount
        else:
            return EmissionValues()
Example #13
0
def test__EcoDeclaration__constructor__sum_of_consumed_amount_is_not_equal_to_sum_of_technologies__should_raise_ValueError(
):
    with pytest.raises(ValueError):
        EcoDeclaration(
            emissions={
                begin1: EmissionValues(),
                begin2: EmissionValues(),
            },
            consumed_amount={
                begin1: 100,
                begin2: 100,
            },
            retired_amount={},
            technologies={
                begin1: EmissionValues(Solar=110,
                                       Wind=90),  # Should be 10 + 90 = 100
                begin2: EmissionValues(Solar=20, Wind=80),
            },
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
Example #14
0
def test__EcoDeclaration__technologies_percentage__technologies_exists__should_return_EmissionValues_with_correct_values(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={
            begin1: EmissionValues(CO2=100, CH4=200),
            begin2: EmissionValues(CO2=300, CH4=400),
            begin3: EmissionValues(CO2=500, CH4=600, NOx=700),
        },
        consumed_amount={
            begin1: 10,
            begin2: 20,
            begin3: 30,
        },
        retired_amount={},
        technologies={
            begin1: EmissionValues(Solar=5, Wind=5),
            begin2: EmissionValues(Solar=15, Wind=5),
            begin3: EmissionValues(Solar=20, Wind=10),
        },
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert isinstance(uut.technologies_percentage, EmissionValues)
    assert uut.technologies_percentage == {
        'Wind': 20 / (10 + 20 + 30) * 100,
        'Solar': 40 / (10 + 20 + 30) * 100,
    }
Example #15
0
def test__EcoDeclaration__total_consumed_amount__consumed_amount_exists__should_return_correct_number(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={
            begin1: EmissionValues(CO2=100, CH4=200),
            begin2: EmissionValues(CO2=300, CH4=400),
            begin3: EmissionValues(CO2=500, CH4=600, NOx=700),
        },
        consumed_amount={
            begin1: 10,
            begin2: 20,
            begin3: 30,
        },
        retired_amount={},
        technologies={
            begin1: EmissionValues(Solar=5, Wind=5),
            begin2: EmissionValues(Solar=15, Wind=5),
            begin3: EmissionValues(Solar=20, Wind=10),
        },
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert uut.total_consumed_amount == 10 + 20 + 30
Example #16
0
def test__EcoDeclaration__total_emissions_per_wh__emissions_exists__should_return_EmissionValues_with_correct_values(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={
            begin1: EmissionValues(CO2=100, CH4=200),
            begin2: EmissionValues(CO2=300, CH4=400),
            begin3: EmissionValues(CO2=500, CH4=600, NOx=700),
        },
        consumed_amount={
            begin1: 0,
            begin2: 20,
            begin3: 30,
        },
        retired_amount={},
        technologies={
            begin1: EmissionValues(),
            begin2: EmissionValues(Solar=15, Wind=5),
            begin3: EmissionValues(Solar=20, Wind=10),
        },
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert isinstance(uut.total_emissions_per_wh, EmissionValues)
    assert uut.total_emissions_per_wh == {
        'CO2': (100 + 300 + 500) / (20 + 30),
        'CH4': (200 + 400 + 600) / (20 + 30),
        'NOx': 700 / (20 + 30),
    }
Example #17
0
def test__EcoDeclaration__total_consumed_amount__NO_consumed_amount_exists__should_return_zero(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={},
        consumed_amount={},
        retired_amount={},
        technologies=EmissionValues(),
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert uut.total_consumed_amount == 0
def test__EmissionData__emissions_per_wh__has_parts___should_return_emissions_per_wh_as_EmissionValues(
):
    uut = EmissionData(
        sector='DK1',
        timestamp_utc=datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc),
        parts=[
            EmissionPart(technology='A',
                         amount=111,
                         emissions=EmissionValues(CO2=100, CO=200)),
            EmissionPart(technology='B',
                         amount=222,
                         emissions=EmissionValues(CO2=300, CO=400, NOx=500)),
            EmissionPart(technology='C',
                         amount=333,
                         emissions=EmissionValues(CO=600, NOx=700)),
        ],
    )

    assert isinstance(uut.emissions_per_wh, EmissionValues)
    assert uut.emissions_per_wh == {
        'CO2': (100 + 300) / (111 + 222 + 333),
        'CO': (200 + 400 + 600) / (111 + 222 + 333),
        'NOx': (500 + 700) / (111 + 222 + 333),
    }
def test__EmissionData__technologies_share__has_parts___should_return_technologies_share_as_EmissionValues(
):
    uut = EmissionData(
        sector='DK1',
        timestamp_utc=datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc),
        parts=[
            EmissionPart(technology='Solar',
                         amount=111,
                         emissions=EmissionValues(CO2=100, CO=200)),
            EmissionPart(technology='Wind',
                         amount=222,
                         emissions=EmissionValues(CO2=300, CO=400, NOx=500)),
            EmissionPart(technology='Coal',
                         amount=333,
                         emissions=EmissionValues(CO=600, NOx=700)),
        ],
    )

    assert isinstance(uut.technologies_share, EmissionValues)
    assert uut.technologies_share == {
        'Solar': 111 / (111 + 222 + 333),
        'Wind': 222 / (111 + 222 + 333),
        'Coal': 333 / (111 + 222 + 333),
    }
Example #20
0
def test__EcoDeclaration__as_resolution__resolution_is_higher_than_current__should_raise_ValueError(
        current_resolution, new_resolution):

    # Arrange
    uut = EcoDeclaration(
        emissions={},
        consumed_amount={},
        retired_amount={},
        technologies=EmissionValues(),
        resolution=current_resolution,
        utc_offset=0,
    )

    # Assert
    with pytest.raises(ValueError):
        uut.as_resolution(new_resolution, 0)
Example #21
0
def test__EcoDeclaration__total_emissions_per_wh__NO_emissions_exists__should_return_empty_EmissionValues(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={},
        consumed_amount={},
        retired_amount={},
        technologies=EmissionValues(),
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert isinstance(uut.total_emissions_per_wh, EmissionValues)
    assert uut.total_emissions_per_wh == {}
Example #22
0
def test__EcoDeclaration__emissions_per_wh__consumed_amount_exists__should_return_EmissionValues_with_correct_values(
):

    # Arrange
    uut = EcoDeclaration(
        emissions={
            begin1: EmissionValues(CO2=100, CH4=200),
            begin2: EmissionValues(CO2=300, CH4=400),
            begin3: EmissionValues(CO2=500, CH4=600, NOx=700),
        },
        consumed_amount={
            begin1: 0,
            begin2: 20,
            begin3: 40,
        },
        retired_amount={},
        technologies={
            begin1: EmissionValues(),
            begin2: EmissionValues(Solar=15, Wind=5),
            begin3: EmissionValues(Solar=20, Wind=20),
        },
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
    )

    # Assert
    assert isinstance(uut.emissions_per_wh, dict)
    assert all(
        isinstance(v, EmissionValues) for v in uut.emissions_per_wh.values())
    assert uut.emissions_per_wh == {
        begin1: {
            'CO2': 0,
            'CH4': 0,
            'NOx': 0
        },
        begin2: {
            'CO2': 300 / 20,
            'CH4': 400 / 20,
            'NOx': 0
        },
        begin3: {
            'CO2': 500 / 40,
            'CH4': 600 / 40,
            'NOx': 700 / 40
        },
    }
import pytest

from origin.common import EmissionValues

ev1 = EmissionValues(
    carbon_dioxide=1,
    methane=2,
    nitrous_oxide=3,
    greenhouse_gasses=4,
    sulfur_dioxide=5,
    nitrogen_dioxide=6,
    carbon_monoxide=7,
    hydrocarbons=8,
    particles=9,
    coal_fly_ash=10,
    coal_slag=11,
    desulfuriazion_products=12,
    slag=13,
    rga=14,
    bioash=15,
    radioactive_waste=16,
)

ev2 = EmissionValues(
    carbon_dioxide=100,
    methane=200,
    nitrous_oxide=300,
    greenhouse_gasses=400,
    sulfur_dioxide=500,
    nitrogen_dioxide=600,
    carbon_monoxide=700,
Example #24
0
import marshmallow
from typing import List
from itertools import groupby
from datetime import datetime, timezone
from dataclasses import dataclass
from marshmallow_dataclass import NewType

from origin.common import EmissionValues

EmissionValuesType = NewType(
    name='EmissionValuesType',
    typ=dict,
    field=marshmallow.fields.Function,
    deserialize=lambda emissions: EmissionValues(**emissions),
)


@dataclass
class EmissionPart:
    technology: str

    # Consumed amount in Wh
    amount: int

    # Emissions in gram
    emissions: EmissionValuesType


@dataclass
class EmissionData:
    sector: str
def test__EcoDeclarationBuilder__integration(energytype_service_mock,
                                             datahub_service_mock):

    # -- Arrange -------------------------------------------------------------

    uut = EcoDeclarationBuilder()

    user = Mock()
    gsrn1 = 'GSRN1'
    gsrn2 = 'GSRN2'
    gsrn3 = 'GSRN3'
    meteringpoints = [
        Mock(gsrn=gsrn1, sector='DK1'),
        Mock(gsrn=gsrn2, sector='DK2'),
        Mock(gsrn=gsrn3, sector='DK2'),
    ]

    begin0 = datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc)
    begin1 = datetime(2020, 1, 1, 1, 0, tzinfo=timezone.utc)
    begin2 = datetime(2020, 1, 1, 2, 0, tzinfo=timezone.utc)
    begin3 = datetime(2020, 1, 1, 3, 0, tzinfo=timezone.utc)
    begin4 = datetime(2020, 1, 1, 4, 0, tzinfo=timezone.utc)

    # datahub_service.get_measurements()
    datahub_service_mock.get_measurements.return_value = GetMeasurementListResponse(
        success=True,
        total=9,
        measurements=[
            Measurement(gsrn=gsrn1,
                        sector='DK1',
                        begin=begin1,
                        amount=100000,
                        end=None,
                        address=None,
                        type=None),
            Measurement(gsrn=gsrn1,
                        sector='DK1',
                        begin=begin2,
                        amount=200000,
                        end=None,
                        address=None,
                        type=None),
            Measurement(gsrn=gsrn1,
                        sector='DK1',
                        begin=begin3,
                        amount=300000,
                        end=None,
                        address=None,
                        type=None),
            Measurement(gsrn=gsrn1,
                        sector='DK1',
                        begin=begin4,
                        amount=400000,
                        end=None,
                        address=None,
                        type=None),
            Measurement(gsrn=gsrn2,
                        sector='DK2',
                        begin=begin3,
                        amount=700000,
                        end=None,
                        address=None,
                        type=None),
            Measurement(gsrn=gsrn2,
                        sector='DK2',
                        begin=begin4,
                        amount=800000,
                        end=None,
                        address=None,
                        type=None),

            # No Retired GGOs exists for GSRN3
            # Two measurements in DK2 at begin6, only one of them should count
            Measurement(gsrn=gsrn3,
                        sector='DK2',
                        begin=begin4,
                        amount=1000000,
                        end=None,
                        address=None,
                        type=None),
        ])

    # energytype_service.get_residual_mix()
    energytype_service_mock.get_residual_mix.return_value = GetMixEmissionsResponse(
        success=True,
        mix_emissions=[
            # DK1
            EmissionData(sector='DK1',
                         timestamp_utc=begin1,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=111,
                                          emissions=EmissionValues(CO2=50,
                                                                   CH4=51)),
                             EmissionPart(technology='Wind',
                                          amount=222,
                                          emissions=EmissionValues(CO2=52,
                                                                   CH4=53)),
                         ]),
            EmissionData(sector='DK1',
                         timestamp_utc=begin2,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=333,
                                          emissions=EmissionValues(CO2=54,
                                                                   CH4=55)),
                             EmissionPart(technology='Wind',
                                          amount=444,
                                          emissions=EmissionValues(CO2=56,
                                                                   CH4=57)),
                         ]),
            EmissionData(sector='DK1',
                         timestamp_utc=begin3,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=555,
                                          emissions=EmissionValues(CO2=58,
                                                                   CH4=59)),
                             EmissionPart(technology='Wind',
                                          amount=666,
                                          emissions=EmissionValues(CO2=60,
                                                                   CH4=61)),
                         ]),
            EmissionData(sector='DK1',
                         timestamp_utc=begin4,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=777,
                                          emissions=EmissionValues(CO2=62,
                                                                   CH4=63)),
                             EmissionPart(technology='Wind',
                                          amount=888,
                                          emissions=EmissionValues(CO2=64,
                                                                   CH4=65)),
                         ]),

            # DK2
            EmissionData(sector='DK2',
                         timestamp_utc=begin3,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=131313,
                                          emissions=EmissionValues(CO2=74,
                                                                   CH4=75)),
                             EmissionPart(technology='Wind',
                                          amount=141414,
                                          emissions=EmissionValues(CO2=76,
                                                                   CH4=77)),
                         ]),
            EmissionData(sector='DK2',
                         timestamp_utc=begin4,
                         parts=[
                             EmissionPart(technology='Solar',
                                          amount=151515,
                                          emissions=EmissionValues(CO2=78,
                                                                   CH4=79)),
                             EmissionPart(technology='Wind',
                                          amount=161616,
                                          emissions=EmissionValues(CO2=80,
                                                                   CH4=81)),
                         ]),
        ])

    # uut.fetch_retired_ggos_from_db()
    uut.fetch_retired_ggos_from_db = Mock(return_value=[
        # GSRN1
        Ggo(retire_gsrn=gsrn1,
            begin=begin1,
            amount=100000,
            emissions={
                'CO2': 1,
                'CH4': 2
            },
            technology=Mock(technology='Nuclear')),
        Ggo(retire_gsrn=gsrn1,
            begin=begin2,
            amount=2222,
            emissions={
                'CO2': 3,
                'CH4': 4
            },
            technology=Mock(technology='Oil')),
        Ggo(retire_gsrn=gsrn1,
            begin=begin2,
            amount=3333,
            emissions=None,
            technology=Mock(technology='Waste')),  # No emissions - is ignored
        Ggo(retire_gsrn=gsrn1,
            begin=begin3,
            amount=4444,
            emissions={
                'CO2': 5,
                'CH4': 6
            },
            technology=Mock(technology='Hydro')),
        Ggo(retire_gsrn=gsrn1,
            begin=begin4,
            amount=5555,
            emissions={
                'CO2': 7,
                'CH4': 8
            },
            technology=Mock(technology='Coal')),

        # GSRN2
        Ggo(retire_gsrn=gsrn2,
            begin=begin3,
            amount=6666,
            emissions={
                'CO2': 9,
                'CH4': 10
            },
            technology=Mock(technology='Biomass')),
        Ggo(retire_gsrn=gsrn2,
            begin=begin4,
            amount=7777,
            emissions={
                'CO2': 11,
                'CH4': 12
            },
            technology=Mock(technology='Naturalgas')),
        Ggo(retire_gsrn=gsrn2,
            begin=begin4,
            amount=8888,
            emissions=None,
            technology=Mock(technology='Biogas')),  # No emissions - is ignored
    ])

    # -- Act -----------------------------------------------------------------

    individual, general = uut.build_eco_declaration(
        user=user,
        meteringpoints=meteringpoints,
        begin_range=DateTimeRange(begin=begin0, end=begin4),
        session=Mock(),
    )

    # -- Assert --------------------------------------------------------------

    # Individual declaration

    assert individual.consumed_amount == {
        begin1: 100000,
        begin2: 200000,
        begin3: 300000 + 700000,
        begin4: 400000 + 800000 + 1000000,
    }

    # TODO assert technologies

    assert individual.emissions[begin1] == {
        'CO2': 100000 * 1,
        'CH4': 100000 * 2,
    }

    assert individual.emissions[begin2] == {
        'CO2': 2222 * 3 + (200000 - 3333 - 2222) * ((54 + 56) / (333 + 444)),
        'CH4': 2222 * 4 + (200000 - 3333 - 2222) * ((55 + 57) / (333 + 444)),
    }

    assert individual.emissions[begin3] == {
        'CO2':
        4444 * 5 + (300000 - 4444) * ((58 + 60) / (555 + 666)) + 6666 * 9 +
        (700000 - 6666) * ((74 + 76) / (131313 + 141414)),
        'CH4':
        4444 * 6 + (300000 - 4444) * ((59 + 61) / (555 + 666)) + 6666 * 10 +
        (700000 - 6666) * ((75 + 77) / (131313 + 141414)),
    }

    assert individual.emissions[begin4] == {
        'CO2':
        5555 * 7 + (400000 - 5555) * ((62 + 64) / (777 + 888)) + 7777 * 11 +
        (800000 - 8888 - 7777) * ((78 + 80) / (151515 + 161616)) + 1000000 *
        ((78 + 80) / (151515 + 161616)),
        'CH4':
        5555 * 8 + (400000 - 5555) * ((63 + 65) / (777 + 888)) + 7777 * 12 +
        (800000 - 8888 - 7777) * ((79 + 81) / (151515 + 161616)) + 1000000 *
        ((79 + 81) / (151515 + 161616)),
    }

    # General declaration

    assert general.consumed_amount == {
        begin1: 111 + 222,
        begin2: 333 + 444,
        begin3: 555 + 666 + 131313 + 141414,
        begin4: 777 + 888 + 151515 + 161616,
    }

    # TODO assert technologies

    assert general.emissions[begin1] == {
        'CO2': 50 + 52,
        'CH4': 51 + 53,
    }

    assert general.emissions[begin2] == {
        'CO2': 54 + 56,
        'CH4': 55 + 57,
    }

    assert general.emissions[begin3] == {
        'CO2': 58 + 60 + 74 + 76,
        'CH4': 59 + 61 + 75 + 77,
    }

    assert general.emissions[begin4] == {
        'CO2': 62 + 64 + 78 + 80,
        'CH4': 63 + 65 + 79 + 81,
    }

    # Correct call parameters to datahub_service_mock.get_measurements()

    datahub_service_mock.get_measurements.assert_called_once()

    get_measurements_call_args = datahub_service_mock.get_measurements.call_args[
        1]

    assert get_measurements_call_args['token'] is user.access_token
    assert get_measurements_call_args[
        'request'].filters.type is MeasurementType.CONSUMPTION
    assert get_measurements_call_args['request'].filters.gsrn == [
        gsrn1, gsrn2, gsrn3
    ]
    assert get_measurements_call_args[
        'request'].filters.begin_range.begin == begin1
    assert get_measurements_call_args[
        'request'].filters.begin_range.end == begin4

    # Correct call parameters to energytype_service.get_residual_mix()

    energytype_service_mock.get_residual_mix.assert_called_once()

    get_residual_mix_call_args = energytype_service_mock.get_residual_mix.call_args[
        1]

    assert sorted(get_residual_mix_call_args['sector']) == ['DK1', 'DK2']
    assert get_residual_mix_call_args['begin_from'] == begin1
    assert get_residual_mix_call_args['begin_to'] == begin4
def test__EcoDeclarationBuilder__build_general_declaration():

    # -- Arrange -------------------------------------------------------------

    uut = EcoDeclarationBuilder()

    begin1 = datetime(2020, 1, 1, 1, 0)
    begin2 = datetime(2020, 1, 1, 2, 0)
    begin3 = datetime(2020, 1, 1, 3, 0)

    measurements = [
        Mock(sector='DK1', begin=begin1),
        Mock(sector='DK1', begin=begin2),
        Mock(sector='DK1', begin=begin3),

        # Two measurements in DK2 at begin2, only one of them should count
        Mock(sector='DK2', begin=begin1),
        Mock(sector='DK2', begin=begin2),
        Mock(sector='DK2', begin=begin2),
    ]

    general_mix_emissions = {
        begin1: {
            'DK1':
            Mock(
                amount=110,
                emissions=EmissionValues(CO2=111, CH4=222),
                technologies=EmissionValues(Solar=35, Wind=75),
            ),
            'DK2':
            Mock(
                amount=220,
                emissions=EmissionValues(CO2=333, CH4=444),
                technologies=EmissionValues(Solar=130, Wind=90),
            ),
        },
        begin2: {
            'DK1':
            Mock(
                amount=330,
                emissions=EmissionValues(CO2=555, CH4=666),
                technologies=EmissionValues(Solar=91, Wind=239),
            ),
            'DK2':
            Mock(
                amount=440,
                emissions=EmissionValues(CO2=777, CH4=888),
                technologies=EmissionValues(Solar=250, Wind=190),
            ),
        },
        begin3: {
            'DK1':
            Mock(
                amount=500,
                emissions=EmissionValues(CO2=999, CH4=101010),
                technologies=EmissionValues(Solar=490, Wind=10),
            ),
        },
    }

    # -- Act -----------------------------------------------------------------

    declaration = uut.build_general_declaration(
        measurements=measurements,
        general_mix_emissions=general_mix_emissions,
    )

    # -- Assert --------------------------------------------------------------

    assert declaration.consumed_amount == {
        begin1: 110 + 220,
        begin2: 330 + 440,
        begin3: 500,
    }

    assert declaration.technologies == {
        begin1: {
            'Solar': 35 + 130,
            'Wind': 75 + 90,
        },
        begin2: {
            'Solar': 91 + 250,
            'Wind': 239 + 190,
        },
        begin3: {
            'Solar': 490,
            'Wind': 10,
        },
    }

    assert declaration.emissions[begin1] == {
        'CO2': 111 + 333,
        'CH4': 222 + 444,
    }

    assert declaration.emissions[begin2] == {
        'CO2': 555 + 777,
        'CH4': 666 + 888,
    }

    assert declaration.emissions[begin3] == {
        'CO2': 999,
        'CH4': 101010,
    }
Example #27
0
def test__EcoDeclaration__as_resolution__group_by_year():

    # Arrange
    month1_day1_begin1 = datetime(2020, 1, 1, 0, 0)
    month1_day1_begin2 = datetime(2020, 1, 1, 1, 0)
    month1_day2_begin1 = datetime(2020, 1, 2, 0, 0)
    month1_day2_begin2 = datetime(2020, 1, 2, 1, 0)
    month2_day1_begin1 = datetime(2020, 2, 1, 0, 0)
    month2_day1_begin2 = datetime(2020, 2, 1, 1, 0)
    month2_day2_begin1 = datetime(2020, 2, 2, 0, 0)
    month2_day2_begin2 = datetime(2020, 2, 2, 1, 0)

    uut = EcoDeclaration(
        resolution=EcoDeclarationResolution.hour,
        utc_offset=0,
        emissions={
            month1_day1_begin1: EmissionValues(CO2=1, NO2=2),
            month1_day1_begin2: EmissionValues(CO2=3, NO2=4),
            month1_day2_begin1: EmissionValues(CO2=5, NO2=6),
            month1_day2_begin2: EmissionValues(CO2=7, NO2=8),
            month2_day1_begin1: EmissionValues(CO2=9, NO2=10),
            month2_day1_begin2: EmissionValues(CO2=11, NO2=12),
            month2_day2_begin1: EmissionValues(CO2=13, NO2=14),
            month2_day2_begin2: EmissionValues(CO2=15, NO2=16),
        },
        consumed_amount={
            month1_day1_begin1: 10,
            month1_day1_begin2: 20,
            month1_day2_begin1: 30,
            month1_day2_begin2: 40,
            month2_day1_begin1: 50,
            month2_day1_begin2: 60,
            month2_day2_begin1: 70,
            month2_day2_begin2: 80,
        },
        retired_amount={},
        technologies={
            month1_day1_begin1: EmissionValues(Solar=5, Wind=5),
            month1_day1_begin2: EmissionValues(Solar=15, Wind=5),
            month1_day2_begin1: EmissionValues(Solar=15, Wind=15),
            month1_day2_begin2: EmissionValues(Solar=35, Wind=5),
            month2_day1_begin1: EmissionValues(Solar=25, Wind=25),
            month2_day1_begin2: EmissionValues(Solar=55, Wind=5),
            month2_day2_begin1: EmissionValues(Solar=65, Wind=5),
            month2_day2_begin2: EmissionValues(Solar=75, Wind=5),
        },
    )

    # Act
    new_declaration = uut.as_resolution(EcoDeclarationResolution.year, 0)

    # Assert
    assert new_declaration.emissions == {
        datetime(2020, 1, 1, 0, 0): {
            'CO2': 1 + 3 + 5 + 7 + 9 + 11 + 13 + 15,
            'NO2': 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16,
        },
    }

    assert new_declaration.consumed_amount == {
        datetime(2020, 1, 1, 0, 0): 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80,
    }

    assert new_declaration.technologies == {
        datetime(2020, 1, 1, 0, 0): {
            'Solar': 5 + 15 + 15 + 35 + 25 + 55 + 65 + 75,
            'Wind': 5 + 5 + 15 + 5 + 25 + 5 + 5 + 5,
        },
    }
Example #28
0
    def build_individual_declaration(self, measurements, retired_ggos,
                                     general_mix_emissions):
        """
        :param list[Measurement] measurements:
        :param dict[str, dict[datetime, list[Ggo]]] retired_ggos:
        :param dict[datetime, dict[str, EmissionData]] general_mix_emissions:
        :rtype: EcoDeclaration
        """

        # Emission in gram (mapped by begin)
        # {begin: {key: value}}
        emissions: Dict[datetime, EmissionValues[str, float]] = {}

        # Consumption in Wh (mapped by begin)
        # {begin: amount}
        consumed_amount: Dict[datetime, float] = {}

        # TODO
        retired_amount: Dict[datetime, float] = {}

        # Consumed amount in Wh per technology (mapped by begin)
        # {begin: {technology: amount}}
        technologies: Dict[datetime, EmissionValues[str, float]] = {}

        for m in measurements:
            ggos = retired_ggos.get(m.gsrn, {}).get(m.begin, [])
            ggos_total_amount = sum(ggo.amount for ggo in ggos)
            remaining_amount = m.amount - ggos_total_amount

            assert 0 <= ggos_total_amount <= m.amount
            assert 0 <= remaining_amount <= m.amount
            assert ggos_total_amount + remaining_amount == m.amount

            # Consumed amount
            consumed_amount.setdefault(m.begin, 0)
            consumed_amount[m.begin] += m.amount

            # Consumed amount
            retired_amount.setdefault(m.begin, 0)
            retired_amount[m.begin] += ggos_total_amount

            # Set default (empty) emission values for this begin
            emissions.setdefault(m.begin, EmissionValues())

            # Set default (empty) emission values for this begin
            technologies.setdefault(m.begin, EmissionValues())

            # Emission from retired GGOs
            for ggo in ggos:
                if ggo.emissions:
                    emissions[m.begin] += \
                        EmissionValues(**ggo.emissions) * ggo.amount
                else:
                    emissions[m.begin] += EmissionValues()

                technologies[m.begin].setdefault(ggo.technology_label, 0)
                technologies[m.begin][ggo.technology_label] += ggo.amount

            # Remaining emission from General mix
            # Assume there exists mix emissions for each
            # begin in the period, otherwise fail hard
            if remaining_amount:
                mix = general_mix_emissions[m.begin][m.sector]

                emissions[m.begin] += \
                    mix.emissions_per_wh * remaining_amount

                technologies[m.begin] += \
                    mix.technologies_share * remaining_amount

        return EcoDeclaration(
            emissions=emissions,
            consumed_amount=consumed_amount,
            retired_amount=retired_amount,
            technologies=technologies,
            resolution=EcoDeclarationResolution.hour,
            utc_offset=0,
        )
def test__EcoDeclarationBuilder__build_individual_declaration():
    """
    begin1: One measurement with 100% of emission from retired GGO

    begin2: One measurement with 50% of emission from retired GGO
            and 50% emission from general mix emissions

    begin3: Two measurements with 100% of emission from retired GGOs

    begin4: Two measurements with 50% of emission from retired GGOs
            and 50% emission from general mix emissions

    begin5: One measurement with 100% of emission from general mix emissions

    begin6: Two measurements with 100% of emission from general mix emissions
    """

    # -- Arrange -------------------------------------------------------------

    uut = EcoDeclarationBuilder()

    gsrn1 = 'GSRN1'
    gsrn2 = 'GSRN2'
    gsrn3 = 'GSRN3'

    begin1 = datetime(2020, 1, 1, 1, 0)
    begin2 = datetime(2020, 1, 1, 2, 0)
    begin3 = datetime(2020, 1, 1, 3, 0)
    begin4 = datetime(2020, 1, 1, 4, 0)
    begin5 = datetime(2020, 1, 1, 5, 0)
    begin6 = datetime(2020, 1, 1, 6, 0)

    measurements = [
        Mock(gsrn=gsrn1, sector='DK1', begin=begin1, amount=100),
        Mock(gsrn=gsrn1, sector='DK1', begin=begin2, amount=200),
        Mock(gsrn=gsrn1, sector='DK1', begin=begin3, amount=300),
        Mock(gsrn=gsrn1, sector='DK1', begin=begin4, amount=400),
        Mock(gsrn=gsrn1, sector='DK1', begin=begin5, amount=500),
        Mock(gsrn=gsrn1, sector='DK1', begin=begin6, amount=600),
        Mock(gsrn=gsrn2, sector='DK2', begin=begin3, amount=700),
        Mock(gsrn=gsrn2, sector='DK2', begin=begin4, amount=800),
        Mock(gsrn=gsrn2, sector='DK2', begin=begin6, amount=900),

        # No Retired GGOs exists for GSRN3
        Mock(gsrn=gsrn3, sector='DK2', begin=begin6, amount=1000),
    ]

    retired_ggos = {
        gsrn1: {
            begin1: [
                Mock(amount=100,
                     technology_label='Coal',
                     emissions={
                         'CO2': 1,
                         'CH4': 2
                     })
            ],
            begin2: [
                Mock(amount=25,
                     technology_label='Coal',
                     emissions={
                         'CO2': 3,
                         'CH4': 4
                     }),
                Mock(amount=50, technology_label='Wind', emissions=None)
            ],
            begin3: [
                Mock(amount=300,
                     technology_label='Coal',
                     emissions={
                         'CO2': 5,
                         'CH4': 6
                     })
            ],
            begin4: [
                Mock(amount=60,
                     technology_label='Coal',
                     emissions={
                         'CO2': 7,
                         'CH4': 8
                     })
            ],
        },
        gsrn2: {
            begin3: [
                Mock(amount=700,
                     technology_label='Coal',
                     emissions={
                         'CO2': 9,
                         'CH4': 10
                     })
            ],
            begin4: [
                Mock(amount=50,
                     technology_label='Coal',
                     emissions={
                         'CO2': 11,
                         'CH4': 12
                     }),
                Mock(amount=50, technology_label='Wind', emissions=None)
            ],
        },
    }

    general_mix_emissions = {
        begin1: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=13, CH4=14),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
        begin2: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=15, CH4=16),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
        begin3: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=17, CH4=18),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
            'DK2':
            Mock(
                emissions_per_wh=EmissionValues(CO2=25, CH4=26),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
        begin4: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=19, CH4=20),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
            'DK2':
            Mock(
                emissions_per_wh=EmissionValues(CO2=27, CH4=28),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
        begin5: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=21, CH4=22),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
        begin6: {
            'DK1':
            Mock(
                emissions_per_wh=EmissionValues(CO2=23, CH4=24),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
            'DK2':
            Mock(
                emissions_per_wh=EmissionValues(CO2=29, CH4=30),
                technologies_share=EmissionValues(Solar=0.5, Wind=0.5),
            ),
        },
    }

    # -- Act -----------------------------------------------------------------

    declaration = uut.build_individual_declaration(
        measurements=measurements,
        retired_ggos=retired_ggos,
        general_mix_emissions=general_mix_emissions,
    )

    # -- Assert --------------------------------------------------------------

    assert declaration.consumed_amount == {
        begin1: 100,
        begin2: 200,
        begin3: 300 + 700,
        begin4: 400 + 800,
        begin5: 500,
        begin6: 600 + 900 + 1000,
    }

    # TODO Assert declaration.technologies

    assert declaration.emissions[begin1] == {
        'CO2': 100 * 1,
        'CH4': 100 * 2,
    }

    assert declaration.emissions[begin2] == {
        'CO2': 25 * 3 + (200 - 50 - 25) * 15,
        'CH4': 25 * 4 + (200 - 50 - 25) * 16,
    }

    assert declaration.emissions[begin3] == {
        'CO2': 300 * 5 + 700 * 9,
        'CH4': 300 * 6 + 700 * 10,
    }

    assert declaration.emissions[begin4] == {
        'CO2': 60 * 7 + (400 - 60) * 19 + 50 * 11 + (800 - 50 - 50) * 27,
        'CH4': 60 * 8 + (400 - 60) * 20 + 50 * 12 + (800 - 50 - 50) * 28,
    }

    assert declaration.emissions[begin5] == {
        'CO2': 500 * 21,
        'CH4': 500 * 22,
    }

    assert declaration.emissions[begin6] == {
        'CO2': 600 * 23 + 900 * 29 + 1000 * 29,
        'CH4': 600 * 24 + 900 * 30 + 1000 * 30,
    }