From 7506b1508c9f6d561a40ad7d2f6f53412d2d4ded Mon Sep 17 00:00:00 2001 From: "Corey R. Randall" Date: Mon, 9 Dec 2024 13:26:34 -0700 Subject: [PATCH] Working Coulombic efficiency --- images/tests.svg | 8 ++--- src/thevenin/__init__.py | 2 +- src/thevenin/_model.py | 6 +++- src/thevenin/templates/params.yaml | 1 + tests/test_model.py | 47 ++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/images/tests.svg b/images/tests.svg index e295e07..660c846 100644 --- a/images/tests.svg +++ b/images/tests.svg @@ -1,5 +1,5 @@ - - tests: 32 + + tests: 33 @@ -15,7 +15,7 @@ tests - - 32 + + 33 diff --git a/src/thevenin/__init__.py b/src/thevenin/__init__.py index 0ed5d13..9c65d60 100644 --- a/src/thevenin/__init__.py +++ b/src/thevenin/__init__.py @@ -27,7 +27,7 @@ from . import loadfns from . import plotutils -__version__ = '0.1.1' +__version__ = '0.1.2.dev' __all__ = [ 'IDASolver', diff --git a/src/thevenin/_model.py b/src/thevenin/_model.py index 91be780..cd9a226 100644 --- a/src/thevenin/_model.py +++ b/src/thevenin/_model.py @@ -42,6 +42,7 @@ def __init__(self, params: dict | str = 'params.yaml'): num_RC_pairs number of RC pairs *int*, - soc0 initial state of charge *float*, - capacity maximum battery capacity *float*, Ah + ce coulombic efficiency *float*, - mass total battery mass *float*, kg isothermal flag for isothermal model *bool*, - Cp specific heat capacity *float*, J/kg/K @@ -92,6 +93,7 @@ def __init__(self, params: dict | str = 'params.yaml'): 'num_RC_pairs', 'soc0', 'capacity', + 'ce', 'mass', 'isothermal', 'Cp', @@ -103,6 +105,7 @@ def __init__(self, params: dict | str = 'params.yaml'): self.num_RC_pairs = params.pop('num_RC_pairs') self.soc0 = params.pop('soc0') self.capacity = params.pop('capacity') + self.ce = params.pop('ce') self.mass = params.pop('mass') self.isothermal = params.pop('isothermal') self.Cp = params.pop('Cp') @@ -302,7 +305,8 @@ def rhs_funcs(self, t: float, sv: np.ndarray, inputs: dict) -> np.ndarray: power = current*voltage # state of charge (differential) - rhs[self._ptr['soc']] = -current / 3600. / self.capacity + ce = 1. if current >= 0. else self.ce + rhs[self._ptr['soc']] = -ce*current / 3600. / self.capacity # temperature (differential) Q_gen = current*(ocv - voltage) diff --git a/src/thevenin/templates/params.yaml b/src/thevenin/templates/params.yaml index d34bcf1..be6c031 100644 --- a/src/thevenin/templates/params.yaml +++ b/src/thevenin/templates/params.yaml @@ -1,6 +1,7 @@ num_RC_pairs: 1 soc0: 1. capacity: 75. +ce: 1. mass: 1.9 isothermal: False Cp: 745. diff --git a/tests/test_model.py b/tests/test_model.py index e29803e..82ef05e 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -3,6 +3,7 @@ import pytest import numpy as np import thevenin as thev +from scipy.integrate import cumulative_trapezoid @pytest.fixture(scope='function') @@ -14,6 +15,7 @@ def dict_params(): 'num_RC_pairs': 0, 'soc0': 0.5, 'capacity': 1., + 'ce': 1., 'mass': 0.5, 'isothermal': False, 'Cp': 1150., @@ -368,6 +370,51 @@ def test_isothermal_flag(model_2RC, constant_steps): assert np.allclose(soln.vars['temperature_K'], model_2RC.T_inf) +def test_coulombic_efficiency(): + + model_100 = thev.Model() + model_100.soc0 = 1. + model_100.ce = 1. + model_100.pre() + + model_80 = thev.Model() + model_80.soc0 = 1. + model_80.ce = 0.8 + model_80.pre() + + expr = thev.Experiment() + expr.add_step('current_C', 0.05, (3600.*30., 10.), limits=('soc', 0.)) + expr.add_step('current_C', -0.05, (3600.*30., 10.), limits=('soc', 1.)) + + # check discharge capacity / charge capacity ~ 1.0 + soln_100 = model_100.run(expr) + assert all(soln_100.success) + + dis = soln_100.get_steps(0) + chg = soln_100.get_steps(1) + + cap_dis = cumulative_trapezoid(dis.vars['current_A'], dis.vars['time_h'], + initial=0.) + cap_chg = cumulative_trapezoid(chg.vars['current_A'], chg.vars['time_h'], + initial=0.) + + assert round(abs(cap_dis).max() / abs(cap_chg).max(), 1) == 1.0 + + # check discharge capacity / charge capacity ~ 0.8 + soln_80 = model_80.run(expr) + assert all(soln_80.success) + + dis = soln_80.get_steps(0) + chg = soln_80.get_steps(1) + + cap_dis = cumulative_trapezoid(dis.vars['current_A'], dis.vars['time_h'], + initial=0.) + cap_chg = cumulative_trapezoid(chg.vars['current_A'], chg.vars['time_h'], + initial=0.) + + assert round(abs(cap_dis).max() / abs(cap_chg).max(), 1) == 0.8 + + def test_mutable_warning(): from thevenin._model import short_warn