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

Feature/battery cell model #91

Merged
merged 16 commits into from
Mar 28, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ hercules/local_amr_wind_demo/sample_copy.nc
# Some output files to ignore
t_00*
logdummy
loghelics
loghercules
logstandin
logfloris
Expand Down
52 changes: 52 additions & 0 deletions docs/battery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Battery

There are two battery models currently implemented in Hercules: `SimpleBattery` and `LIB`. Both interact with Hercules through a simple wrapper class: `Battery`.

### Parameters

Battery parameters are defined in the hercules input yaml file used to initialize `emulator`.

- `py_sim_type`: `"SimpleBattery"` or `"LIB"`
- `energy_capacity`: [kWh]
- `charge_rate`: [kW]
- `max_SOC`: between 0 and 1
- `min_SOC`: between 0 and 1
- `initial_conditions`
- `SOC`: between `min_SOC` and `max_SOC`


Once initialized, the battery is only interacted with using the `step` method.

### Inputs
Inputs are passed to `step()` as a dict named `inputs`, which must have the following fields:

```
{py_sims:{inputs:{battery_signal: ____,
available_power: ____
}}}
```

### Outputs
Outputs are returned as a dict containing the following values
- `power` The charging/discharging power of the battery
- `reject` The amount of charging/discharging requested of the battery that it could not fulfill. Can be positive or negative.
- `soc` The battery state of charge


## `SimpleBattery`

`SimpleBattery` is defined by $E_t = \sum_{k=0}^t P_k \Delta t$, where $E_t$ is the energy stored and $P_t$ is the charging/discharging power at time $t$. Both $E$ and $P$ are constrained by upper and lower limits.

$\underline{E} \leq E \leq \overline{E}$
$\underline{P} \leq P \leq \overline{P}$


## `LIB`

`LIB` models a lithium ion battery based on the lithium ion cell model presented in [1.]. The main difference between `LIB` and `SimpleBattery` is that `LIB` includes diffusion transients and losses both of which are modeled as an equivalent circuit model following the approach in [1.].



### References

1. M.-K. Tran et al., “A comprehensive equivalent circuit model for lithium-ion batteries, incorporating the effects of state of health, state of charge, and temperature on model parameters,” Journal of Energy Storage, vol. 43, p. 103252, Nov. 2021, doi: 10.1016/j.est.2021.103252.
198 changes: 198 additions & 0 deletions example_case_folders/02_amr_wind_standin_only/loghelics
ZackTully marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

conda activate hercules

export HELICS_PORT=32000

helics_broker -f 2 --consoleloglevel=trace --loglevel=debug --local_port=$HELICS_PORT >>loghelics &

# helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT &

# cd example_case_folders/06_amr_wind_standin_and_battery

python hercules_runscript.py hercules_input_000.yaml >> loghercules 2>&1 &

python hercules_runscript_amr_standin.py amr_input.inp amr_standin_data.csv >> logstandin 2>&1



Large diffs are not rendered by default.

10,901 changes: 10,901 additions & 0 deletions example_case_folders/06_amr_wind_standin_and_battery/loghelics
ZackTully marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions hercules/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def receive_amrwind_data(self):
print("AMRWindSpeed:", wind_speed_amr_wind)
print("AMRWindDirection:", wind_direction_amr_wind)
print("AMRWindTurbinePowers:", turbine_power_array)
print(" AMRWIND number of turbines here: ", self.num_turbines)
print("AMRWIND number of turbines here: ", self.num_turbines)
print("AMRWindTurbineWD:", turbine_wd_array)
print("=======================================")

Expand Down Expand Up @@ -302,7 +302,7 @@ def recursive_flatten_main_dict(self, nested_dict, prefix=""):
self.main_dict_flat[prefix + k + ".%03d" % i] = vi

# If v is a string, int, or float, enter it directly
if isinstance(v, (int, float)):
if isinstance(v, (int, np.integer, float)):
self.main_dict_flat[prefix + k] = v

def log_main_dict(self):
Expand Down
6 changes: 3 additions & 3 deletions hercules/py_sims.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np

from hercules.python_simulators.battery import Battery
from hercules.python_simulators.electrolyzer_plant import ElectrolyzerPlant
from hercules.python_simulators.simple_battery import SimpleBattery
from hercules.python_simulators.simple_solar import SimpleSolar
from hercules.python_simulators.solar_pysam import SolarPySAM

Expand Down Expand Up @@ -53,8 +53,8 @@ def get_py_sim(self, py_sim_obj_dict):
if py_sim_obj_dict["py_sim_type"] == "SolarPySAM":
return SolarPySAM(py_sim_obj_dict, self.dt)

if py_sim_obj_dict["py_sim_type"] == "SimpleBattery":
return SimpleBattery(py_sim_obj_dict, self.dt)
if py_sim_obj_dict["py_sim_type"] in [ "SimpleBattery", "LIB"]:
return Battery(py_sim_obj_dict, self.dt)

if py_sim_obj_dict["py_sim_type"] == "ElectrolyzerPlant":
return ElectrolyzerPlant(py_sim_obj_dict, self.dt)
Expand Down
Loading
Loading