33#
44import pybamm
55
6+ from .util import check_if_composite
7+
68
79class ElectrodeSOHHalfCell (pybamm .BaseModel ):
810 """Model to calculate electrode-specific SOH for a half-cell, adapted from
@@ -21,17 +23,32 @@ class ElectrodeSOHHalfCell(pybamm.BaseModel):
2123
2224 """
2325
24- def __init__ (self , name = "ElectrodeSOH model" ):
26+ def __init__ (self , name = "ElectrodeSOH model" , options = None ):
2527 pybamm .citations .register ("Mohtat2019" )
2628 super ().__init__ (name )
27- param = pybamm .LithiumIonParameters ({"working electrode" : "positive" })
29+ if options is None :
30+ options = {"working electrode" : "positive" }
31+ is_composite = check_if_composite (options , "positive" )
32+ param = pybamm .LithiumIonParameters (options )
2833
2934 x_100 = pybamm .Variable ("x_100" , bounds = (0 , 1 ))
3035 x_0 = pybamm .Variable ("x_0" , bounds = (0 , 1 ))
36+ if is_composite :
37+ x_100_2 = pybamm .Variable ("x_100_2" , bounds = (0 , 1 ))
38+ x_0_2 = pybamm .Variable ("x_0_2" , bounds = (0 , 1 ))
3139 Q_w = pybamm .InputParameter ("Q_w" )
40+ if is_composite :
41+ Q_w_2 = pybamm .InputParameter ("Q_w_2" )
3242 T_ref = param .T_ref
3343 U_w = param .p .prim .U
34- Q = Q_w * (x_0 - x_100 )
44+ if is_composite :
45+ U_w_2 = param .p .sec .U
46+ Q_1 = Q_w * (x_0 - x_100 )
47+ if is_composite :
48+ Q_2 = Q_w_2 * (x_0_2 - x_100_2 )
49+ else :
50+ Q_2 = pybamm .Scalar (0 )
51+ Q = Q_1 + Q_2
3552
3653 V_max = param .ocp_soc_100
3754 V_min = param .ocp_soc_0
@@ -40,7 +57,13 @@ def __init__(self, name="ElectrodeSOH model"):
4057 x_100 : U_w (x_100 , T_ref ) - V_max ,
4158 x_0 : U_w (x_0 , T_ref ) - V_min ,
4259 }
60+ if is_composite :
61+ self .algebraic [x_100_2 ] = U_w_2 (x_100_2 , T_ref ) - V_max
62+ self .algebraic [x_0_2 ] = U_w_2 (x_0_2 , T_ref ) - V_min
4363 self .initial_conditions = {x_100 : 0.8 , x_0 : 0.2 }
64+ if is_composite :
65+ self .initial_conditions [x_100_2 ] = 0.8
66+ self .initial_conditions [x_0_2 ] = 0.2
4467
4568 self .variables = {
4669 "x_100" : x_100 ,
@@ -50,6 +73,12 @@ def __init__(self, name="ElectrodeSOH model"):
5073 "Uw(x_0)" : U_w (x_0 , T_ref ),
5174 "Q_w" : Q_w ,
5275 }
76+ if is_composite :
77+ self .variables ["x_100_2" ] = x_100_2
78+ self .variables ["x_0_2" ] = x_0_2
79+ self .variables ["Q_w_2" ] = Q_w_2
80+ self .variables ["Uw(x_100_2)" ] = U_w_2 (x_100_2 , T_ref )
81+ self .variables ["Uw(x_0_2)" ] = U_w_2 (x_0_2 , T_ref )
5382
5483 @property
5584 def default_solver (self ):
@@ -94,7 +123,13 @@ def get_initial_stoichiometry_half_cell(
94123 The initial stoichiometry that give the desired initial state of charge
95124 """
96125 param = pybamm .LithiumIonParameters (options )
97- x_0 , x_100 = get_min_max_stoichiometries (parameter_values , inputs = inputs )
126+ x_dict = get_min_max_stoichiometries (
127+ parameter_values , inputs = inputs , options = options
128+ )
129+ x_0 , x_100 = x_dict ["x_0" ], x_dict ["x_100" ]
130+ is_composite = check_if_composite (options , "positive" )
131+ if is_composite :
132+ x_0_2 , x_100_2 = x_dict ["x_0_2" ], x_dict ["x_100_2" ]
98133
99134 if isinstance (initial_value , str ) and initial_value .endswith ("V" ):
100135 V_init = float (initial_value [:- 1 ])
@@ -106,38 +141,67 @@ def get_initial_stoichiometry_half_cell(
106141 f"Initial voltage { V_init } V is outside the voltage limits "
107142 f"({ V_min } , { V_max } )"
108143 )
109-
110144 # Solve simple model for initial soc based on target voltage
111- soc_model = pybamm .BaseModel ()
112- soc = pybamm .Variable ("soc " )
145+ model = pybamm .BaseModel ()
146+ x = pybamm .Variable ("x " )
113147 Up = param .p .prim .U
114148 T_ref = parameter_values ["Reference temperature [K]" ]
115- x = x_0 + soc * (x_100 - x_0 )
116-
117- soc_model .algebraic [soc ] = Up (x , T_ref ) - V_init
118- # initial guess for soc linearly interpolates between 0 and 1
149+ model .variables ["x" ] = x
150+ model .algebraic [x ] = Up (x , T_ref ) - V_init
151+ # initial guess for x linearly interpolates between 0 and 1
119152 # based on V linearly interpolating between V_max and V_min
120- soc_model .initial_conditions [soc ] = (V_init - V_min ) / (V_max - V_min )
121- soc_model .variables ["soc" ] = soc
122- parameter_values .process_model (soc_model )
123- initial_soc = (
124- pybamm .AlgebraicSolver (tol = tol )
125- .solve (soc_model , [0 ], inputs = inputs )["soc" ]
126- .data [0 ]
153+ soc_initial_guess = (V_init - V_min ) / (V_max - V_min )
154+ model .initial_conditions [x ] = 1 - soc_initial_guess
155+ if is_composite :
156+ Up_2 = param .p .sec .U
157+ x_2 = pybamm .Variable ("x_2" )
158+ model .algebraic [x_2 ] = Up_2 (x_2 , T_ref ) - V_init
159+ model .variables ["x_2" ] = x_2
160+ model .initial_conditions [x_2 ] = 1 - soc_initial_guess
161+
162+ parameter_values .process_model (model )
163+ sol = pybamm .AlgebraicSolver ("lsq__trf" , tol = tol ).solve (
164+ model , [0 ], inputs = inputs
127165 )
166+ x = sol ["x" ].data [0 ]
167+ if is_composite :
168+ x_2 = sol ["x_2" ].data [0 ]
128169 elif isinstance (initial_value , int | float ):
129- initial_soc = initial_value
130- if not 0 <= initial_soc <= 1 :
170+ if not 0 <= initial_value <= 1 :
131171 raise ValueError ("Initial SOC should be between 0 and 1" )
132-
172+ if not is_composite :
173+ x = x_0 + initial_value * (x_100 - x_0 )
174+ else :
175+ model = pybamm .BaseModel ()
176+ x = pybamm .Variable ("x" )
177+ x_2 = pybamm .Variable ("x_2" )
178+ U_p = param .p .prim .U
179+ U_p_2 = param .p .sec .U
180+ T_ref = parameter_values ["Reference temperature [K]" ]
181+ model .algebraic [x ] = U_p (x , T_ref ) - U_p_2 (x_2 , T_ref )
182+ model .initial_conditions [x ] = x_0 + initial_value * (x_100 - x_0 )
183+ model .initial_conditions [x_2 ] = x_0_2 + initial_value * (x_100_2 - x_0_2 )
184+ Q_w = parameter_values .evaluate (param .p .prim .Q_init , inputs = inputs )
185+ Q_w_2 = parameter_values .evaluate (param .p .sec .Q_init , inputs = inputs )
186+ Q_min = x_100 * Q_w + x_100_2 * Q_w_2
187+ Q_max = x_0 * Q_w + x_0_2 * Q_w_2
188+ Q_now = Q_w * x + Q_w_2 * x_2
189+ soc = (Q_now - Q_min ) / (Q_max - Q_min )
190+ model .algebraic [x_2 ] = soc - initial_value
191+ model .variables ["x" ] = x
192+ model .variables ["x_2" ] = x_2
193+ parameter_values .process_model (model )
194+ sol = pybamm .AlgebraicSolver (tol = tol ).solve (model , [0 ], inputs = inputs )
195+ x = sol ["x" ].data [0 ]
196+ x_2 = sol ["x_2" ].data [0 ]
133197 else :
134198 raise ValueError (
135199 "Initial value must be a float between 0 and 1, or a string ending in 'V'"
136200 )
137-
138- x = x_0 + initial_soc * ( x_100 - x_0 )
139-
140- return x
201+ ret_dict = { "x" : x }
202+ if is_composite :
203+ ret_dict [ "x_2" ] = x_2
204+ return ret_dict
141205
142206
143207def get_min_max_stoichiometries (parameter_values , options = None , inputs = None ):
@@ -156,12 +220,26 @@ def get_min_max_stoichiometries(parameter_values, options=None, inputs=None):
156220 inputs = inputs or {}
157221 if options is None :
158222 options = {"working electrode" : "positive" }
159- esoh_model = pybamm .lithium_ion .ElectrodeSOHHalfCell ("ElectrodeSOH" )
223+ esoh_model = pybamm .lithium_ion .ElectrodeSOHHalfCell (
224+ "ElectrodeSOH" , options = options
225+ )
160226 param = pybamm .LithiumIonParameters (options )
161- Q_w = parameter_values .evaluate (param .p .Q_init , inputs = inputs )
227+ is_composite = check_if_composite (options , "positive" )
228+ if is_composite :
229+ Q_w = parameter_values .evaluate (param .p .prim .Q_init , inputs = inputs )
230+ Q_w_2 = parameter_values .evaluate (param .p .sec .Q_init , inputs = inputs )
231+ Q_inputs = {"Q_w" : Q_w , "Q_w_2" : Q_w_2 }
232+ else :
233+ Q_w = parameter_values .evaluate (param .p .prim .Q_init , inputs = inputs )
234+ Q_inputs = {"Q_w" : Q_w }
162235 # Add Q_w to input parameters
163- all_inputs = {** inputs , "Q_w" : Q_w }
236+ all_inputs = {** inputs , ** Q_inputs }
164237 esoh_sim = pybamm .Simulation (esoh_model , parameter_values = parameter_values )
165238 esoh_sol = esoh_sim .solve ([0 ], inputs = all_inputs )
166239 x_0 , x_100 = esoh_sol ["x_0" ].data [0 ], esoh_sol ["x_100" ].data [0 ]
167- return x_0 , x_100
240+ if is_composite :
241+ x_0_2 , x_100_2 = esoh_sol ["x_0_2" ].data [0 ], esoh_sol ["x_100_2" ].data [0 ]
242+ ret_dict = {"x_0" : x_0 , "x_100" : x_100 , "x_0_2" : x_0_2 , "x_100_2" : x_100_2 }
243+ else :
244+ ret_dict = {"x_0" : x_0 , "x_100" : x_100 }
245+ return ret_dict
0 commit comments