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

Storage updates #213

Merged
merged 5 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions examples/basic_example.ipynb

Large diffs are not rendered by default.

33 changes: 17 additions & 16 deletions examples/controller_example.ipynb

Large diffs are not rendered by default.

310 changes: 290 additions & 20 deletions examples/sil_example.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/util/example_node/sil_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def _workload_sim(self):
time.sleep(2)

def start(self):
uvicorn.run(self.app, host="0.0.0.0", port=self.port)
uvicorn.run(self.app, host="127.0.0.1", port=self.port)
Copy link
Contributor

Choose a reason for hiding this comment

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

does this matter?



if __name__ == "__main__":
Expand Down
35 changes: 18 additions & 17 deletions tests/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,18 @@ def test_forecast_single_column(self, hist_signal_single):
start_time="2023-01-01T00:00:00",
end_time="2023-01-01T01:00:00",
) == {
np.datetime64("2023-01-01T00:30:00.000000000"): 2.0,
np.datetime64("2023-01-01T01:00:00.000000000"): 3.0,
}

np.datetime64("2023-01-01T00:30:00.000000000"): 2.0,
np.datetime64("2023-01-01T01:00:00.000000000"): 3.0,
}

def test_forecast_static(self, hist_signal_static_forecast):
assert hist_signal_static_forecast.forecast(
start_time="2023-01-01T00:00:00",
end_time="2023-01-01T02:00:00",
) == {
np.datetime64("2023-01-01T01:00:00.000000000"): 2.0,
np.datetime64("2023-01-01T02:00:00.000000000"): 4.0,
}

np.datetime64("2023-01-01T01:00:00.000000000"): 2.0,
np.datetime64("2023-01-01T02:00:00.000000000"): 4.0,
}

@pytest.mark.parametrize(
"start, end, column, expected",
Expand All @@ -122,7 +120,7 @@ def test_forecast_static(self, hist_signal_static_forecast):
{
np.datetime64("2023-01-01T00:10:00.000000000"): 2.0,
np.datetime64("2023-01-01T01:00:00.000000000"): 1.5,
}
},
),
(
"2023-01-01T01:00:00",
Expand Down Expand Up @@ -159,7 +157,7 @@ def test_forecast_static(self, hist_signal_static_forecast):
{
np.datetime64("2023-01-01T02:00:00.000000000"): 3.0,
np.datetime64("2023-01-01T03:00:00.000000000"): 1.5,
}
},
),
],
)
Expand Down Expand Up @@ -259,13 +257,16 @@ def test_forecast(self, hist_signal_forecast, start, end, column, expected):
def test_forecast_with_frequency(
self, hist_signal_forecast, start, end, column, frequency, method, expected
):
assert hist_signal_forecast.forecast(
start,
end,
column=column,
frequency=frequency,
resample_method=method,
) == expected
assert (
hist_signal_forecast.forecast(
start,
end,
column=column,
frequency=frequency,
resample_method=method,
)
== expected
)

def test_forecast_fails_if_column_not_specified(self, hist_signal):
with pytest.raises(ValueError):
Expand Down
191 changes: 96 additions & 95 deletions tests/test_storage.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,74 @@
import pytest
import math

import vessim as vs


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

@pytest.fixture
def battery_c(self) -> vs.SimpleBattery:
"""This battery can only be (dis)charged at 10W max."""
return vs.SimpleBattery(capacity=3600, charge_level=1800, c_rate=10)

def test_soc(self, battery):
assert battery.soc() == 0.8
return vs.SimpleBattery(capacity=10, initial_soc=0.5, c_rate=1)

@pytest.mark.parametrize(
"power, duration, exp_charge_energy, exp_charge_level",
"power, duration, exp_charge_energy, exp_charge_level, exp_soc",
[
# No charge
(0, 1000, 0, 80),
(0, 1000, 0, 80, 0.8),
# Charge
(1, 1, 1, 81),
(10, 2, 20, 100),
(10, 4, 20, 100),
(100, 4, 20, 100),
(1, 1, 1, 81, 0.81),
(10, 2, 20, 100, 1),
(10, 4, 20, 100, 1),
(100, 4, 20, 100, 1),
# Discharge
(-1, 1, -1, 79),
(-10, 7, -70, 10),
(-15, 7, -70, 10),
(-10, 14, -70, 10),
(-1, 1, -1, 79, 0.79),
(-10, 7, -70, 10, 0.1),
(-15, 7, -70, 10, 0.1),
(-10, 14, -70, 10, 0.1),
],
)
def test_update(self, battery, power, duration, exp_charge_energy, exp_charge_level):
charge_energy = battery.update(power=power, duration=duration)
assert charge_energy == exp_charge_energy
assert battery.charge_level == exp_charge_level
def test_update(self, battery, power, duration, exp_charge_energy, exp_charge_level, exp_soc):
# duration in hours and charge_level in Wh
charge_energy = battery.update(power=power, duration=duration * 3600)
assert charge_energy == exp_charge_energy * 3600
assert battery.state()["charge_level"] == exp_charge_level
assert math.isclose(battery.soc(), exp_soc)

@pytest.mark.parametrize(
"power, duration, exp_delta, exp_charge_level",
"power, duration, exp_charge_energy, exp_charge_level, exp_soc",
[
# No charge
(0, 10, 0, 1800),
(0, 10, 0, 5, 0.5),
# Charge
(10, 10, 100, 1900),
(20, 10, 100, 1900),
(50, 10, 100, 1900),
(10, 6, 1, 6, 0.6),
(20, 6, 1, 6, 0.6),
(50, 6, 1, 6, 0.6),
# Discharge
(-10, 10, -100, 1700),
(-20, 10, -100, 1700),
(-50, 10, -100, 1700),
(-10, 6, -1, 4, 0.4),
(-20, 6, -1, 4, 0.4),
(-50, 6, -1, 4, 0.4),
# Charge over capacity
(10, 180, 1800, 3600),
(10, 200, 1800, 3600),
(15, 200, 1800, 3600),
(10, 30, 5, 10, 1),
(10, 40, 5, 10, 1),
(15, 40, 5, 10, 1),
# Discharge until empty
(-10, 180, -1800, 0),
(-10, 200, -1800, 0),
(-15, 200, -1800, 0),
(-10, 30, -5, 0, 0),
(-10, 40, -5, 0, 0),
(-15, 40, -5, 0, 0),
],
)
def test_update_c_rate(self, battery_c, power, duration, exp_delta, exp_charge_level):
delta = battery_c.update(power=power, duration=duration)
assert delta == exp_delta
assert battery_c.charge_level == exp_charge_level
def test_update_c_rate(
self, battery_c, power, duration, exp_charge_energy, exp_charge_level, exp_soc
):
# duration in minutes and charge_level in Wh
charge_energy = battery_c.update(power=power, duration=duration * 60)
assert charge_energy == exp_charge_energy * 3600
assert battery_c.state()["charge_level"] == exp_charge_level
assert math.isclose(battery_c.soc(), exp_soc)

def test_update_fails_if_duration_not_positive(self, battery):
with pytest.raises(ValueError):
Expand All @@ -74,7 +78,7 @@ def test_update_fails_if_duration_not_positive(self, battery):
class TestDefaultMicrogridPolicy:
@pytest.fixture
def battery(self) -> vs.SimpleBattery:
return vs.SimpleBattery(capacity=100, charge_level=80, min_soc=0.1)
return vs.SimpleBattery(capacity=100, initial_soc=0.8, min_soc=0.1)

@pytest.fixture
def policy(self) -> vs.DefaultMicrogridPolicy:
Expand All @@ -93,92 +97,88 @@ def policy_islanded(self) -> vs.DefaultMicrogridPolicy:
return vs.DefaultMicrogridPolicy(mode="islanded")

@pytest.mark.parametrize(
"power, duration, exp_delta, exp_charge_level",
"power, duration, exp_delta, exp_soc",
[
# No charge
(0, 1000, 0, 80),
(0, 1000, 0, 0.8),
# Charge
(5, 2, 0, 90),
(100, 4, 380, 100),
(5, 2, 0, 0.9),
(10, 4, 20, 1),
# Discharge
(-5, 2, 0, 70),
(-10, 14, -70, 10),
(-5, 2, 0, 0.7),
(-10, 14, -70, 0.1),
],
)
def test_apply_no_charge_mode(
self, battery, policy, power, duration, exp_delta, exp_charge_level
):
delta = policy.apply(power, duration, battery)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level
def test_apply_no_charge_mode(self, battery, policy, power, duration, exp_delta, exp_soc):
# duration in hours and energy delta in Wh
delta = policy.apply(power, duration * 3600, battery)
assert delta == exp_delta * 3600
assert math.isclose(battery.soc(), exp_soc)

@pytest.mark.parametrize(
"power, duration, exp_delta, exp_charge_level",
"power, duration, exp_delta, exp_soc",
[
# Charge from grid without power-delta
(0, 1, -10, 90),
(0, 2, -20, 100),
(0, 10, -20, 100),
(0, 1, -10, 0.9),
(0, 2, -20, 1),
(0, 10, -20, 1),
# Charge from grid with positive power-delta
(5, 1, -5, 90),
(5, 2, -10, 100),
(5, 10, 30, 100),
(5, 1, -5, 0.9),
(5, 2, -10, 1),
(5, 10, 30, 1),
# Charge from grid with negative power-delta
(-5, 1, -15, 90),
(-5, 2, -30, 100),
(-5, 10, -70, 100),
(-5, 1, -15, 0.9),
(-5, 2, -30, 1),
(-5, 10, -70, 1),
],
)
def test_apply_charge(
self, battery, policy_charge, power, duration, exp_delta, exp_charge_level
):
delta = policy_charge.apply(power, duration, battery)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level
def test_apply_charge(self, battery, policy_charge, power, duration, exp_delta, exp_soc):
# duration in hours and energy delta in Wh
delta = policy_charge.apply(power, duration * 3600, battery)
assert delta == exp_delta * 3600
assert math.isclose(battery.soc(), exp_soc)

@pytest.mark.parametrize(
"power, duration, exp_delta, exp_charge_level",
"power, duration, exp_delta, exp_soc",
[
# Discharge to grid without power-delta
(0, 1, 10, 70),
(0, 7, 70, 10),
(0, 10, 70, 10),
(0, 1, 10, 0.7),
(0, 7, 70, 0.1),
(0, 10, 70, 0.1),
# Discharge to grid with positive power-delta
(5, 1, 15, 70),
(5, 7, 105, 10),
(5, 10, 120, 10),
(5, 1, 15, 0.7),
(5, 7, 105, 0.1),
(5, 10, 120, 0.1),
# Discharge to grid with negative power-delta
(-5, 1, 5, 70),
(-5, 7, 35, 10),
(-5, 20, -30, 10),
(-5, 1, 5, 0.7),
(-5, 7, 35, 0.1),
(-5, 20, -30, 0.1),
],
)
def test_apply_discharge(
self, battery, policy_discharge, power, duration, exp_delta, exp_charge_level
):
delta = policy_discharge.apply(power, duration, battery)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level
def test_apply_discharge(self, battery, policy_discharge, power, duration, exp_delta, exp_soc):
# duration in hours and energy delta in Wh
delta = policy_discharge.apply(power, duration * 3600, battery)
assert delta == exp_delta * 3600
assert math.isclose(battery.soc(), exp_soc)

@pytest.mark.parametrize(
"power, duration, exp_delta, exp_charge_level",
"power, duration, exp_delta, exp_soc",
[
# No charge
(0, 1000, 0, 80),
(0, 1000, 0, 0.8),
# Charge
(5, 2, 0, 90),
(100, 4, 0, 100),
(5, 2, 0, 0.9),
(100, 4, 0, 1),
# Discharge
(-5, 2, 0, 70),
(-10, 7, 0, 10),
(-5, 2, 0, 0.7),
(-10, 7, 0, 0.1),
],
)
def test_apply_islanded(
self, battery, policy_islanded, power, duration, exp_delta, exp_charge_level
):
delta = policy_islanded.apply(power, duration, battery)
assert delta == exp_delta
assert battery.charge_level == exp_charge_level
def test_apply_islanded(self, battery, policy_islanded, power, duration, exp_delta, exp_soc):
# duration in hours and energy delta in Wh
delta = policy_islanded.apply(power, duration * 3600, battery)
assert delta == exp_delta * 3600
assert math.isclose(battery.soc(), exp_soc)

@pytest.mark.parametrize(
"power, duration, exp_delta",
Expand All @@ -194,12 +194,13 @@ def test_apply_islanded(
],
)
def test_apply_no_storage(self, policy, power, duration, exp_delta):
# duration in seconds and energy delta in Ws
delta = policy.apply(power, duration)
assert delta == exp_delta

def test_apply_fails_if_no_power_in_islanded_with_battery(self, battery, policy_islanded):
with pytest.raises(RuntimeError):
policy_islanded.apply(-1, 71, battery)
policy_islanded.apply(-1, 71 * 3600, battery)

def test_apply_fails_if_no_power_in_islanded_without_battery(self, policy_islanded):
with pytest.raises(RuntimeError):
Expand Down
Loading
Loading