2023-01-09 16:06:22 +00:00
import numpy . testing as npt
2024-06-27 14:55:30 +00:00
import numpy as np
import typing
2023-01-09 16:06:22 +00:00
import pytest
2024-06-20 08:14:38 +00:00
from caimira . calculator . models import models
from caimira . calculator . validators . co2 . co2_validator import CO2FormData
2024-06-27 14:55:30 +00:00
2023-01-09 16:06:22 +00:00
@pytest.fixture
2023-11-29 07:54:58 +00:00
def simple_co2_conc_model ( data_registry ) :
2023-01-09 16:06:22 +00:00
return models . CO2ConcentrationModel (
2023-11-29 07:54:58 +00:00
data_registry = data_registry ,
2023-01-09 16:06:22 +00:00
room = models . Room ( 200 , models . PiecewiseConstant ( ( 0. , 24. ) , ( 293 , ) ) ) ,
ventilation = models . AirChange ( models . PeriodicInterval ( period = 120 , duration = 120 ) , 0.25 ) ,
2023-07-25 11:18:33 +00:00
CO2_emitters = models . SimplePopulation (
2023-01-09 16:06:22 +00:00
number = 5 ,
presence = models . SpecificInterval ( ( ( [ 0. , 4. ] , ) ) ) ,
activity = models . Activity . types [ ' Seated ' ] ,
) ,
)
@pytest.mark.parametrize (
" time, expected_co2_concentration " , [
[ 0. , 440.44 ] ,
[ 1. , 914.2487227 ] ,
[ 2. , 1283.251327 ] ,
[ 3. , 1570.630844 ] ,
[ 4. , 1794.442237 ] ,
]
)
def test_co2_concentration (
simple_co2_conc_model : models . CO2ConcentrationModel ,
time : float ,
expected_co2_concentration : float ,
) :
npt . assert_almost_equal ( simple_co2_conc_model . concentration ( time ) , expected_co2_concentration )
def test_integrated_concentration ( simple_co2_conc_model ) :
c1 = simple_co2_conc_model . integrated_concentration ( 0 , 2 )
c2 = simple_co2_conc_model . integrated_concentration ( 0 , 1 )
c3 = simple_co2_conc_model . integrated_concentration ( 1 , 2 )
assert c1 != 0
npt . assert_almost_equal ( c1 , c2 + c3 )
2024-06-27 14:55:30 +00:00
2024-08-28 15:46:46 +00:00
@pytest.mark.parametrize (
2024-08-29 08:08:30 +00:00
" scenario_data, room_volume, max_total_people, start, finish, state_changes " , [
[ " office_scenario_1_sensor_data " , 102 , 4 , " 14:00 " , " 17:30 " , ( 14.78 , 15.1 , 15.53 , 15.87 , 16.52 , 16.83 ) ] ,
2024-09-02 09:23:52 +00:00
[ " office_scenario_2_sensor_data " , 60 , 2 , " 08:38 " , " 17:30 " , ( 10.17 , 12.45 , 14.5 ) ] , # Second state change should actually be 12.87 - that's the real time at which the ventilation was changed in the room.
2024-08-28 15:46:46 +00:00
[ " meeting_scenario_1_sensor_data " , 83 , 3 , " 09:04 " , " 11:45 " , ( 10.37 , 11.07 ) ] ,
2024-08-29 08:08:30 +00:00
[ " meeting_scenario_2_sensor_data " , 83 , 4 , " 13:40 " , " 16:40 " , ( 14.37 , 14.72 , 15 , 15.33 , 15.68 , 16.03 ) ]
2024-08-28 15:46:46 +00:00
]
)
2024-08-29 08:08:30 +00:00
def test_find_change_points ( scenario_data , room_volume , max_total_people , start , finish , state_changes , request ) :
'''
2024-09-02 09:23:52 +00:00
Specific test of the find_change_points method .
Testing the ventilation state changes only .
2024-08-29 08:08:30 +00:00
'''
2024-08-28 15:46:46 +00:00
CO2_form_model : CO2FormData = CO2FormData (
CO2_data = request . getfixturevalue ( scenario_data ) ,
fitting_ventilation_states = [ ] ,
exposed_start = start ,
exposed_finish = finish ,
2024-08-29 08:08:30 +00:00
total_people = max_total_people ,
2024-08-28 15:46:46 +00:00
room_volume = room_volume ,
)
find_points = CO2_form_model . find_change_points ( )
assert np . allclose ( find_points , state_changes , rtol = 1e-2 )
2024-08-29 08:08:30 +00:00
@pytest.mark.parametrize (
" scenario_data, room_volume, occupancy, presence_interval, all_state_changes " , [
[ " office_scenario_1_sensor_data " , 102 , ( 4 , ) , ( 14 , 17.5 ) , ( 14 , 14.25 , 14.78 , 15.1 , 15.53 , 15.87 , 16.52 , 16.83 , 17.5 ) ] ,
2024-09-02 09:23:52 +00:00
[ " office_scenario_2_sensor_data " , 60 , ( 2 , 0 , 2 ) , ( 8.62 , 11.93 , 12.42 , 17.5 ) , ( 8.62 , 10.17 , 12.45 , 14.5 , 17.5 , 20. ) ] , # Third state change should actually be 12.87 - that's the real time at which the ventilation was changed in the room.
2024-08-29 08:08:30 +00:00
[ " meeting_scenario_1_sensor_data " , 83 , ( 2 , 3 , 2 , 3 ) , ( 9.07 , 9.32 , 9.75 , 10.75 , 11.75 ) , ( 9.07 , 10.37 , 11.07 , 11.75 ) ] ,
[ " meeting_scenario_2_sensor_data " , 83 , ( 2 , 3 , 4 ) , ( 13.67 , 13.75 , 15.87 , 16.67 ) , ( 13.67 , 14.37 , 14.72 , 15.00 , 15.33 , 15.68 , 16.03 , 16.67 ) ]
]
)
def test_predictive_model_accuracy ( data_registry , scenario_data , room_volume , occupancy , presence_interval , all_state_changes , request ) :
2024-06-27 14:55:30 +00:00
'''
2024-09-02 09:23:52 +00:00
Specific test corresponding to the data files of four
different scenarios ( 2 in an office and 2 in a meeting room ) .
2024-08-29 08:08:30 +00:00
The room volume , number of people and ventilation transition times
2024-09-02 09:23:52 +00:00
correspond to the actual state change occurrences during the day .
2024-08-29 08:08:30 +00:00
Note that the last time from the input file is considered as a ventilation
state change .
2024-06-27 14:55:30 +00:00
'''
2024-08-29 08:08:30 +00:00
input_fitting_data = request . getfixturevalue ( scenario_data )
2024-06-27 14:55:30 +00:00
fitting_model : models . CO2DataModel = models . CO2DataModel (
data_registry = data_registry ,
2024-08-30 10:27:47 +00:00
room = models . Room ( volume = room_volume ) ,
occupancy = models . IntPiecewiseConstant (
2024-08-29 08:08:30 +00:00
transition_times = presence_interval ,
values = occupancy
) ,
ventilation_transition_times = all_state_changes ,
times = input_fitting_data [ ' times ' ] ,
CO2_concentrations = input_fitting_data [ ' CO2 ' ] ,
2024-06-27 14:55:30 +00:00
)
# Get fitting results
fitting_results : typing . Dict = fitting_model . CO2_fit_params ( )
predictive_CO2 : typing . List [ float ] = fitting_results [ ' predictive_CO2 ' ]
def root_mean_square_error_percentage ( actual , predicted ) - > float :
return np . sqrt ( np . mean ( ( ( actual - predicted ) / actual ) * * 2 ) ) * 100
# Calculate RMSEP metric
2024-08-29 08:08:30 +00:00
rmsep = root_mean_square_error_percentage ( np . array ( input_fitting_data [ ' CO2 ' ] ) , np . array ( predictive_CO2 ) )
2024-06-27 14:55:30 +00:00
acceptable_rmsep = 10 # Threshold of 10% for the accepted error margin
assert rmsep < = acceptable_rmsep , f " RMSEP { rmsep } exceeds acceptable threshold { acceptable_rmsep } "