Source code for tests.test_consumer_actors

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

"""
Unit tests for the module consumer_actors.py, defining constraints and
objectives for consumer actor type.

Overview
========

This test suite verifies the behaviour of the :class:`Consumer` actor and
its interaction with different consumption unit models. The tests ensure that
consumer objectives and constraints are correctly translated into the
optimization model.

The test suite covers:

* Objective creation
    * Minimize total consumption.
    * Maximize total consumption.
    * Minimize operating cost.
    * Minimize CO₂ emissions.

* Constraint creation
    * Minimum and maximum total energy consumption.
    * Minimum and maximum total power consumption.
    * Minimum and maximum power consumption for individual units.

* Input validation
    * Correct handling of scalar and time-dependent inputs.
    * Appropriate exceptions for invalid input types or dimensions.

Each test constructs a minimal optimization model consisting of:

* a :class:`~omegalpes.general.time.TimeUnit`,
* one or more consumption units,
* a :class:`~omegalpes.actor.operator_actors.consumer_actors.Consumer`,
* an :class:`~omegalpes.energy.energy_nodes.EnergyNode`, and
* an :class:`~omegalpes.general.optimisation.model.OptimisationModel`.

After solving the model, the tests verify that the expected objectives,
constraints, and optimization expressions have been generated.


..

    Copyright 2018 G2Elab / MAGE

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

import unittest
from omegalpes.actor.operator_actors.consumer_actors import Consumer
from omegalpes.energy.units.consumption_units import ConsumptionUnit, VariableConsumptionUnit, ShiftableConsumptionUnit
from omegalpes.general.time import TimeUnit
from omegalpes.general.optimisation.model import OptimisationModel
from omegalpes.energy.energy_nodes import EnergyNode

_docformat__ = "restructuredtext en"

[docs] class TestConsumerActorObjectives(unittest.TestCase): """ Test the objective functions implemented by the ``Consumer`` actor. These tests verify that each objective method correctly registers the corresponding optimization objective on all operated consumption units and that the optimization model contains the expected objective names. """
[docs] def setUp(self): self.time = TimeUnit(periods=2, dt=1) self.conso0 = ShiftableConsumptionUnit(time=self.time, name='conso0', power_values=[1]) self.conso1 = VariableConsumptionUnit(time=self.time, name='conso1') self.ca = Consumer(name='ca', operated_unit_list=[self.conso0, self.conso1]) self.node = EnergyNode(time=self.time, name='node') self.model = OptimisationModel(time=self.time, name='OM')
[docs] def test_minimize_consumption(self): """ Test the minimize_consumption objective on two consumption units""" self.ca.minimize_consumption() self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.conso0.get_objectives_name_list(), ['min_consumption']) self.assertEqual(self.conso1.get_objectives_name_list(), ['min_consumption']) self.assertEqual(self.model.get_model_objectives_name_list(), ['conso0_min_consumption', 'conso1_min_consumption'])
[docs] def test_maximize_consumption(self): """ Test the maximize_consumption objective on two consumption units""" self.ca.maximize_consumption() self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.conso0.get_objectives_name_list(), ['max_consumption']) self.assertEqual(self.conso1.get_objectives_name_list(), ['max_consumption']) self.assertEqual(self.model.get_model_objectives_name_list(), ['conso0_max_consumption', 'conso1_max_consumption'])
[docs] def test_minimize_consumption_cost(self): """ Test the minimize_consumption_cost objective on two consumption units""" self.conso0._add_operating_cost([1, 1]) self.conso1._add_operating_cost([2, 2]) self.ca.minimize_consumption_cost() self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.conso0.get_objectives_name_list(), ['min_consumption_cost']) self.assertEqual(self.conso1.get_objectives_name_list(), ['min_consumption_cost']) self.assertEqual(self.model.get_model_objectives_name_list(), ['conso0_min_consumption_cost', 'conso1_min_consumption_cost'])
[docs] def test_minimize_co2_consumption(self): """ Test the minimize_co2_consumption objective on two consumption units""" self.conso0._add_co2_emissions([1, 1]) self.conso1._add_co2_emissions([2, 2]) self.ca.minimize_co2_consumption() self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.conso0.get_objectives_name_list(), ['min_CO2_emissions']) self.assertEqual(self.conso1.get_objectives_name_list(), ['min_CO2_emissions']) self.assertEqual(self.model.get_model_objectives_name_list(), ['conso0_min_CO2_emissions', 'conso1_min_CO2_emissions'])
[docs] class TestConsumerActorConstraints(unittest.TestCase): """ Test the constraints implemented by the ``Consumer`` actor. These tests verify that energy and power constraints are correctly generated for individual and aggregated consumption units. They also ensure that invalid user inputs raise the expected exceptions. """
[docs] def setUp(self): self.time = TimeUnit(periods=2, dt=1) self.conso0 = ConsumptionUnit(time=self.time, name='conso0') self.conso1 = ConsumptionUnit(time=self.time, name='conso1') self.ca = Consumer(name='ca', operated_unit_list=[self.conso0, self.conso1]) self.node = EnergyNode(time=self.time, name='node') self.model = OptimisationModel(time=self.time, name='OM')
[docs] def test_energy_consumption_minimum(self): """ Test the energy_consumption_minimum constraint on two consumption units """ self.ca.add_energy_consumption_minimum(min_e_tot=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_min_energy_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'lpSum(conso0_p[t] + conso1_p[t] for t in time.I) ' '>= ' '10')
[docs] def test_energy_consumption_minimum_without_int_or_float(self): """ Test it raises an error if min_e_tot is a list""" with self.assertRaises(TypeError): self.ca.add_energy_consumption_minimum(min_e_tot=[10, 10])
[docs] def test_energy_consumption_maximum(self): """ Test the energy_consumption_maximum constraint on two consumption units """ self.ca.add_energy_consumption_maximum(max_e_tot=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_max_energy_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'lpSum(conso0_p[t] + conso1_p[t] for t in time.I) ' '<= 10')
[docs] def test_energy_consumption_maximum_without_int_or_float(self): """ Test it raises an error if max_e_tot is a list""" with self.assertRaises(TypeError): self.ca.add_energy_consumption_maximum(max_e_tot=[10, 10])
[docs] def test_power_consumption_total_minimum(self): """ Test the add_power_consumption_minimum constraint on two consumption units """ self.ca.add_power_consumption_total_minimum(time=self.time, min_p=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_min_total_power_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] + conso1_p[t] >= 10')
[docs] def test_power_consumption_total_minimum_with_list(self): """ Test the add_power_consumption_total_minimum constraint on two consumption units if min_p is a list""" self.ca.add_power_consumption_total_minimum(time=self.time, min_p=[10, 10]) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_min_total_power_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] + conso1_p[t] >= [10, 10][t] for t in ' 'time.I')
[docs] def test_power_consumption_total_minimum_with_wrong_list(self): """ Test it raises an error if min_p is a list with a wrong dimension considering the unit time""" with self.assertRaises(IndexError): self.ca.add_power_consumption_total_minimum(time=self.time, min_p=[10])
[docs] def test_power_consumption_total_minimum_with_dic(self): """ Test it raises an error if min_p is a dictionary""" with self.assertRaises(TypeError): self.ca.add_power_consumption_total_minimum(time=self.time, min_p={10})
[docs] def test_power_consumption_by_unit_minimum(self): """ Test the add_power_consumption_by_unit_minimum constraint on two consumption units """ self.ca.add_power_consumption_by_unit_minimum(time=self.time, min_p=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_min_by_unit_power_conso_conso0', 'ca_min_by_unit_power_conso_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] >= 10 for t in time.I') self.assertEqual(self.model._model_constraints_list[4].exp, 'conso1_p[t] >= 10 for t in time.I')
[docs] def test_power_consumption_by_unit_minimum_with_list(self): """ Test the add_power_consumption_by_unit_minimum constraint on two consumption units if min_p is a list""" self.ca.add_power_consumption_by_unit_minimum(time=self.time, min_p=[10, 10]) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_min_by_unit_power_conso_conso0', 'ca_min_by_unit_power_conso_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] >= [10, 10][t] for t in time.I') self.assertEqual(self.model._model_constraints_list[4].exp, 'conso1_p[t] >= [10, 10][t] for t in time.I')
[docs] def test_power_consumption_by_unit_minimum_with_wrong_list(self): """ Test it raises an error if min_p is a list with a wrong dimension considering the unit time""" with self.assertRaises(IndexError): self.ca.add_power_consumption_by_unit_minimum(time=self.time, min_p=[10])
[docs] def test_power_consumption_by_unit_minimum_with_dic(self): """ Test it raises an error if min_p is a dictionary""" with self.assertRaises(TypeError): self.ca.add_power_consumption_by_unit_minimum(time=self.time, min_p={10})
[docs] def test_power_consumption_total_maximum(self): """ Test the power_consumption_maximum constraint on two consumption units""" self.ca.add_power_consumption_total_maximum(time=self.time, max_p=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_max_total_power_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] + conso1_p[t] <= 10')
[docs] def test_power_consumption_total_maximum_with_list(self): """ Test the power_consumption_maximum constraint on two consumption units if max_p is a list""" self.ca.add_power_consumption_total_maximum(time=self.time, max_p=[10, 11]) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_max_total_power_conso_conso0_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] + conso1_p[t] <= [10, 11][t] for t in ' 'time.I')
[docs] def test_power_consumption_total_maximum_with_wrong_list(self): """ Test it raises an error if max_p is a list with a wrong dimension considering the unit time""" with self.assertRaises(IndexError): self.ca.add_power_consumption_total_maximum(time=self.time, max_p=[ 10])
[docs] def test_power_consumption_total_maximum_with_dic(self): """ Test it raises an error if max_p is a list with dictionary""" with self.assertRaises(TypeError): self.ca.add_power_consumption_total_maximum(time=self.time, max_p={ 10})
[docs] def test_power_consumption_by_unit_maximum(self): """ Test the power_consumption_by_unit_maximum constraint on two consumption units""" self.ca.add_power_consumption_by_unit_maximum(time=self.time, max_p=10) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_max_by_unit_power_conso_conso0', 'ca_max_by_unit_power_conso_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] <= 10 for t in ' 'time.I') self.assertEqual(self.model._model_constraints_list[4].exp, 'conso1_p[t] <= 10 for t in ' 'time.I')
[docs] def test_power_consumption_by_unit_maximum_with_list(self): """ Test the power_consumption_by_unit_maximum constraint on two consumption units if max_p is a list""" self.ca.add_power_consumption_by_unit_maximum(time=self.time, max_p=[10, 11]) self.node.connect_units(self.conso0, self.conso1) self.model.add_nodes_and_actors(self.node, self.ca) self.model.solve_and_update() self.assertEqual(self.model.get_model_constraints_name_list(), ['node_power_balance', 'conso0_calc_e_tot', 'conso1_calc_e_tot', 'ca_max_by_unit_power_conso_conso0', 'ca_max_by_unit_power_conso_conso1']) self.assertEqual(self.model._model_constraints_list[3].exp, 'conso0_p[t] <= [10, 11][t] for t in ' 'time.I') self.assertEqual(self.model._model_constraints_list[4].exp, 'conso1_p[t] <= [10, 11][t] for t in ' 'time.I')
[docs] def test_power_consumption_by_unit_maximum_with_wrong_list(self): """ Test it raises an error if max_p is a list with a wrong dimension considering the unit time""" with self.assertRaises(IndexError): self.ca.add_power_consumption_by_unit_maximum(time=self.time, max_p=[ 10])
[docs] def test_power_consumption_by_unit_maximum_with_dic(self): """ Test it raises an error if max_p is a list with dictionary""" with self.assertRaises(TypeError): self.ca.add_power_consumption_by_unit_maximum(time=self.time, max_p={ 10})