Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core tests #119

Merged
merged 11 commits into from
Aug 24, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.ruff_cache
.mypy_cache
.pytest_cache
.coverage
__pycache__
dockerfile

Expand Down
37 changes: 0 additions & 37 deletions tests/test_carbon_api.py

This file was deleted.

61 changes: 61 additions & 0 deletions tests/test_consumer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import pytest

from vessim.core.consumer import MockPowerMeter, ComputingSystem

class TestMockPowerMeter:

@pytest.fixture
def power_meter(self) -> MockPowerMeter:
return MockPowerMeter(p=20, power_config={
"high performance": 1,
"normal": .8,
"power-saving": .4
})

def test_initialize_fails_with_invalid_p_value(self):
with pytest.raises(ValueError):
MockPowerMeter(p=-1.0)

@pytest.mark.parametrize("power_config", [
{
"high performance": 1,
"normal": .7
},
{
"invalid_mode": 1,
"normal": .7,
"power-saving": 0.5
}
])
def test_initialize_fails_with_invalid_power_config(self, power_config):
with pytest.raises(ValueError):
MockPowerMeter(p=10, power_config=power_config)

def test_set_power_mode_fails_with_invalid_power_mode(self, power_meter):
with pytest.raises(ValueError):
power_meter.set_power_mode("invalid_mode")

def test_measure(self, power_meter):
assert power_meter.measure() == 20.0
Comment on lines +38 to +39
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does the power meter know which power mode to chose initially? the first in the dict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial power mode is "high performance" as specified in the docstring of the MockPowerMeter class

power_meter.set_power_mode("normal")
assert power_meter.measure() == 16.0
power_meter.set_power_mode("power-saving")
assert power_meter.measure() == 8.0


class TestComputingSystem:

@pytest.fixture
def computing_system(self) -> ComputingSystem:
return ComputingSystem(
power_meters=[MockPowerMeter(p=5), MockPowerMeter(p=7)], pue=1.5
)

def test_consumption(self, computing_system):
assert computing_system.consumption() == 18.0

def test_finalize(self, computing_system):
try:
computing_system.finalize()
except Exception as err:
pytest.fail(f"Unexpected Error: {err}")
Comment on lines +58 to +61
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is being tested here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is just a test to check if the finalize function throws an error. I just wrote this test to achieve full code coverage, but I agree that it is not very useful.

34 changes: 34 additions & 0 deletions tests/test_microgrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pytest
from typing import Dict

from vessim.core.microgrid import SimpleMicrogrid
from vessim.core.storage import SimpleBattery

class TestSimpleMicrogrid:

@pytest.fixture
def microgrid(self) -> SimpleMicrogrid:
return SimpleMicrogrid()

@pytest.fixture
def microgrid_battery(self) -> SimpleMicrogrid:
return SimpleMicrogrid(
storage=SimpleBattery(capacity=100, charge_level=80, min_soc=0.1)
)

@pytest.fixture
def power_values(self) -> Dict:
return {
"Consumer_0": -15,
"Consumer_1": -10,
"Generator_0": 20,
"Generator_1": 0,
}

def test_power_flow(self, microgrid, power_values):
assert microgrid.power_flow(power_values, 10) == -5

def test_power_flow_with_storage(self, microgrid_battery, power_values):
p_delta = microgrid_battery.power_flow(power_values, 10)
assert p_delta == 0
assert microgrid_battery.storage.charge_level == 30
94 changes: 94 additions & 0 deletions tests/test_simulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import pandas as pd
import pytest

from vessim.core.simulator import CarbonApi, Generator


class TestCarbonApi:

@pytest.fixture
def ci_api(self) -> CarbonApi:
index = [
pd.to_datetime("2023-01-01T00:00:00"),
pd.to_datetime("2023-01-01T00:30:00"),
pd.to_datetime("2023-01-01T01:00:00")
]
data = pd.DataFrame({"a": [1, 2, 3], "b": [0, 3, 0]}, index=index)
return CarbonApi(data)

def test_initialize_fails_if_unit_unsupported(self):
with pytest.raises(ValueError):
CarbonApi(pd.DataFrame(), unit="unsupported_unit")

@pytest.mark.parametrize("dt, expected", [
(pd.to_datetime("2023-01-01T00:00:00"), pd.to_datetime("2023-01-01T00:30:00")),
(pd.to_datetime("2023-01-01T00:10:00"), pd.to_datetime("2023-01-01T00:30:00")),
(pd.to_datetime("2023-01-01T00:29:59"), pd.to_datetime("2023-01-01T00:30:00")),
(pd.to_datetime("2023-01-01T00:40:00"), pd.to_datetime("2023-01-01T01:00:00")),
])
def test_next_update(self, ci_api, dt, expected):
assert ci_api.next_update(dt) == expected

def test_zones(self, ci_api):
assert ci_api.zones() == ["a", "b"]

def test_carbon_intensity_at_single_zone(self):
ci_api = CarbonApi(pd.DataFrame({"a": [1]}))
assert ci_api.carbon_intensity_at(0) == 1

@pytest.mark.parametrize("dt, zone, expected", [
(pd.to_datetime("2023-01-01T00:00:00"), "a", 1),
(pd.to_datetime("2023-01-01T00:00:10"), "a", 1),
(pd.to_datetime("2023-01-01T01:00:00"), "a", 3),
(pd.to_datetime("2023-01-01T10:00:00"), "a", 3),
(pd.to_datetime("2023-01-01T00:29:59"), "b", 0),
(pd.to_datetime("2023-01-01T00:30:00"), "b", 3),
])
def test_carbon_intensity_at(self, ci_api, dt, zone, expected):
assert ci_api.carbon_intensity_at(dt, zone) == expected

def test_carbon_intensity_at_fails_if_zone_not_specified(self, ci_api):
with pytest.raises(ValueError):
ci_api.carbon_intensity_at(pd.to_datetime("2023-01-01T00:00:00"))

def test_carbon_intensity_at_fails_if_now_too_early(self, ci_api):
with pytest.raises(ValueError):
ci_api.carbon_intensity_at(pd.to_datetime("2022-12-30T23:59:59"), "a")

def test_carbon_intensity_at_fails_if_zone_does_not_exist(self, ci_api):
with pytest.raises(ValueError):
ci_api.carbon_intensity_at(pd.to_datetime("2023-01-01T00:00:00"), "c")

def test_carbon_intensity_at_with_lb_per_mwh(self):
data = pd.DataFrame({"a": [1, 2]})
ci_api = CarbonApi(data, unit="lb_per_MWh")
assert 0.45 < ci_api.carbon_intensity_at(0) < 0.46
assert 0.9 < ci_api.carbon_intensity_at(1) < 0.91


class TestGenerator:

@pytest.fixture
def generator(self) -> Generator:
index = [
pd.to_datetime("2023-01-01T00:00:00"),
pd.to_datetime("2023-01-01T00:30:00"),
pd.to_datetime("2023-01-01T01:00:00")
]
data = pd.Series([1, 2, 3], index=index)
return Generator(data)

@pytest.mark.parametrize("dt, expected", [
(pd.to_datetime("2023-01-01T00:00:00"), 1),
(pd.to_datetime("2023-01-01T00:00:10"), 1),
(pd.to_datetime("2023-01-01T01:00:00"), 3),
(pd.to_datetime("2023-01-01T10:00:00"), 3),
(pd.to_datetime("2023-01-01T00:29:59"), 1),
(pd.to_datetime("2023-01-01T00:30:00"), 2),
])
def test_carbon_intensity_at(self, generator, dt, expected):
assert generator.power_at(dt) == expected

def test_power_at_fails_if_now_too_early(self, generator):
with pytest.raises(ValueError):
generator.power_at(pd.to_datetime("2022-12-30T23:59:59"))
94 changes: 93 additions & 1 deletion tests/test_storage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from vessim.core.storage import SimpleBattery
from vessim.core.storage import SimpleBattery, DefaultStoragePolicy


class TestSimpleBattery:
Expand Down Expand Up @@ -60,3 +60,95 @@ def test_update_c_rate(self, battery_c, power, duration, exp_delta, exp_charge_l
delta = battery_c.update(power=power, duration=duration)
assert delta == exp_delta
assert battery_c.charge_level == exp_charge_level

def test_update_fails_if_duration_not_positive(self, battery):
with pytest.raises(ValueError):
battery.update(10, -5)


class TestDefaultStoragePolicy:

@pytest.fixture
def battery(self) -> SimpleBattery:
return SimpleBattery(capacity=100, charge_level=80, min_soc=0.1)

@pytest.fixture
def policy(self) -> DefaultStoragePolicy:
return DefaultStoragePolicy()

@pytest.fixture
def policy_charge(self) -> DefaultStoragePolicy:
return DefaultStoragePolicy(grid_power=10)

@pytest.fixture
def policy_discharge(self) -> DefaultStoragePolicy:
return DefaultStoragePolicy(grid_power=-10)

@pytest.mark.parametrize("power, duration, exp_delta, exp_charge_level", [
# No charge
(0, 1000, 0, 80),
# Charge
(100, 4, 95, 100),
# Discharge
(-10, 14, -5, 10),
])
def test_apply_no_charge_mode(
self, battery, policy, power, duration, exp_delta,exp_charge_level
):
delta = policy.apply(
storage=battery, p_delta=power, time_since_last_step=duration
)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level

@pytest.mark.parametrize("power, duration, exp_delta, exp_charge_level", [
# Charge from grid without power-delta
(0, 1, -10, 90),
(0, 2, -10, 100),
(0, 10, -2, 100),
(0, 20, -1, 100),
# Charge from grid with positive power-delta
(5, 1, -5, 90),
(5, 2, -5, 100),
(5, 10, 3, 100),
(5, 20, 4, 100),
# Charge from grid with negative power-delta
(-5, 1, -15, 90),
(-5, 2, -15, 100),
(-5, 10, -7, 100),
(-5, 20, -6, 100),
])
def test_apply_charge(
self, battery, policy_charge, power, duration, exp_delta, exp_charge_level
):
delta = policy_charge.apply(
storage=battery, p_delta=power, time_since_last_step=duration
)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level

@pytest.mark.parametrize("power, duration, exp_delta, exp_charge_level", [
# Discharge to grid without power-delta
(0, 1, 10, 70),
(0, 7, 10, 10),
(0, 10, 7, 10),
(0, 70, 1, 10),
# Discharge to grid with positive power-delta
(5, 1, 15, 70),
(5, 7, 15, 10),
(5, 10, 12, 10),
(5, 70, 6, 10),
# Discharge to grid with negative power-delta
(-5, 1, 5, 70),
(-5, 7, 5, 10),
(-5, 10, 2, 10),
(-5, 70, -4, 10),
])
def test_apply_discharge(
self, battery, policy_discharge, power, duration, exp_delta, exp_charge_level
):
delta = policy_discharge.apply(
storage=battery, p_delta=power, time_since_last_step=duration
)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level
Loading