steer-core 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- steer_core/Constants/Units.py +36 -0
- steer_core/Constants/Universal.py +2 -0
- steer_core/Constants/__init__.py +0 -0
- steer_core/ContextManagers/__init__.py +0 -0
- steer_core/DataManager.py +316 -0
- steer_core/Decorators/Coordinates.py +46 -0
- steer_core/Decorators/Electrochemical.py +28 -0
- steer_core/Decorators/General.py +30 -0
- steer_core/Decorators/Objects.py +14 -0
- steer_core/Decorators/__init__.py +0 -0
- steer_core/Mixins/Colors.py +41 -0
- steer_core/Mixins/Coordinates.py +338 -0
- steer_core/Mixins/Data.py +40 -0
- steer_core/Mixins/Serializer.py +45 -0
- steer_core/Mixins/__init__.py +0 -0
- steer_core/Mixins/validators.py +420 -0
- steer_core/__init__.py +1 -0
- steer_core-0.1.1.dist-info/METADATA +29 -0
- steer_core-0.1.1.dist-info/RECORD +21 -0
- steer_core-0.1.1.dist-info/WHEEL +5 -0
- steer_core-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,420 @@
|
|
1
|
+
from typing import Type
|
2
|
+
import pandas as pd
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from scipy.interpolate import PchipInterpolator
|
6
|
+
|
7
|
+
|
8
|
+
ALLOWED_REFERENCE = [
|
9
|
+
'Na/Na+',
|
10
|
+
'Li/Li+'
|
11
|
+
]
|
12
|
+
|
13
|
+
|
14
|
+
class ValidationMixin:
|
15
|
+
|
16
|
+
@staticmethod
|
17
|
+
def validate_formulations(value: Type) -> None:
|
18
|
+
"""
|
19
|
+
Validate that a value is an instance of _ElectrodeFormulation.
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
value : Type
|
24
|
+
The value to validate.
|
25
|
+
|
26
|
+
Raises
|
27
|
+
------
|
28
|
+
TypeError
|
29
|
+
If the value is not an instance of _ElectrodeFormulation.
|
30
|
+
"""
|
31
|
+
from steer_opencell_design.Formulations.ElectrodeFormulations import _ElectrodeFormulation
|
32
|
+
|
33
|
+
if not isinstance(value, _ElectrodeFormulation):
|
34
|
+
raise TypeError(f"Expected an instance of _ElectrodeFormulation. Provided: {type(value)}.")
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def validate_insulation_material(value: Type) -> None:
|
38
|
+
"""
|
39
|
+
Validate that a value is an instance of InsulationMaterial.
|
40
|
+
|
41
|
+
Parameters
|
42
|
+
----------
|
43
|
+
value : Type
|
44
|
+
The value to validate.
|
45
|
+
|
46
|
+
Raises
|
47
|
+
------
|
48
|
+
TypeError
|
49
|
+
If the value is not an instance of InsulationMaterial.
|
50
|
+
"""
|
51
|
+
from steer_materials.CellMaterials.Base import InsulationMaterial
|
52
|
+
|
53
|
+
if not isinstance(value, InsulationMaterial):
|
54
|
+
raise TypeError(f"Expected an instance of InsulationMaterial. Provided: {type(value)}.")
|
55
|
+
|
56
|
+
@staticmethod
|
57
|
+
def validate_current_collector(value: Type) -> None:
|
58
|
+
"""
|
59
|
+
Validate that a value is an instance of _CurrentCollector.
|
60
|
+
|
61
|
+
Parameters
|
62
|
+
----------
|
63
|
+
value : Type
|
64
|
+
The value to validate.
|
65
|
+
|
66
|
+
Raises
|
67
|
+
------
|
68
|
+
TypeError
|
69
|
+
If the value is not an instance of _CurrentCollector.
|
70
|
+
"""
|
71
|
+
from steer_opencell_design.Components.CurrentCollectors import _CurrentCollector
|
72
|
+
|
73
|
+
if not isinstance(value, _CurrentCollector):
|
74
|
+
raise TypeError(f"Expected an instance of _CurrentCollector. Provided: {type(value)}.")
|
75
|
+
|
76
|
+
@staticmethod
|
77
|
+
def validate_active_material(value: Type) -> None:
|
78
|
+
"""
|
79
|
+
Validate that a value is an instance of _ActiveMaterial.
|
80
|
+
|
81
|
+
Parameters
|
82
|
+
----------
|
83
|
+
value : Type
|
84
|
+
The value to validate.
|
85
|
+
|
86
|
+
Raises
|
87
|
+
------
|
88
|
+
TypeError
|
89
|
+
If the value is not an instance of _ActiveMaterial.
|
90
|
+
"""
|
91
|
+
from steer_materials.CellMaterials.Electrode import _ActiveMaterial
|
92
|
+
|
93
|
+
if not isinstance(value, _ActiveMaterial):
|
94
|
+
raise TypeError(f"Expected an instance of _ActiveMaterial. Provided: {type(value)}.")
|
95
|
+
|
96
|
+
@staticmethod
|
97
|
+
def validate_binder(value: Type) -> None:
|
98
|
+
"""
|
99
|
+
Validate that a value is an instance of Binder.
|
100
|
+
|
101
|
+
Parameters
|
102
|
+
----------
|
103
|
+
value : Type
|
104
|
+
The value to validate.
|
105
|
+
|
106
|
+
Raises
|
107
|
+
------
|
108
|
+
TypeError
|
109
|
+
If the value is not an instance of Binder.
|
110
|
+
"""
|
111
|
+
from steer_materials.CellMaterials.Electrode import Binder
|
112
|
+
|
113
|
+
if not isinstance(value, Binder):
|
114
|
+
raise TypeError(f"Expected an instance of Binder. Provided: {type(value)}.")
|
115
|
+
|
116
|
+
@staticmethod
|
117
|
+
def validate_conductive_additive(value: Type) -> None:
|
118
|
+
"""
|
119
|
+
Validate that a value is an instance of ConductiveAdditive.
|
120
|
+
|
121
|
+
Parameters
|
122
|
+
----------
|
123
|
+
value : Type
|
124
|
+
The value to validate.
|
125
|
+
|
126
|
+
Raises
|
127
|
+
------
|
128
|
+
TypeError
|
129
|
+
If the value is not an instance of ConductiveAdditive.
|
130
|
+
"""
|
131
|
+
from steer_materials.CellMaterials.Electrode import ConductiveAdditive
|
132
|
+
|
133
|
+
if not isinstance(value, ConductiveAdditive):
|
134
|
+
raise TypeError(f"Expected an instance of ConductiveAdditive. Provided: {type(value)}.")
|
135
|
+
|
136
|
+
@staticmethod
|
137
|
+
def validate_percentage(value: float, name: str) -> None:
|
138
|
+
"""
|
139
|
+
Validate that a value is a percentage (between 0 and 100).
|
140
|
+
|
141
|
+
Parameters
|
142
|
+
----------
|
143
|
+
value : float
|
144
|
+
The value to validate.
|
145
|
+
name : str
|
146
|
+
The name of the parameter for error messages.
|
147
|
+
|
148
|
+
Raises
|
149
|
+
------
|
150
|
+
ValueError
|
151
|
+
If the value is not a percentage.
|
152
|
+
"""
|
153
|
+
if not isinstance(value, (int, float)):
|
154
|
+
raise TypeError(f"{name} must be a number. Provided: {value}.")
|
155
|
+
|
156
|
+
if not (0 <= value <= 100):
|
157
|
+
raise ValueError(f"{name} must be a percentage between 0 and 100. Provided: {value}.")
|
158
|
+
|
159
|
+
@staticmethod
|
160
|
+
def validate_fraction(value: float, name: str) -> None:
|
161
|
+
"""
|
162
|
+
Validate that a value is a fraction (between 0 and 1).
|
163
|
+
|
164
|
+
Parameters
|
165
|
+
----------
|
166
|
+
value : float
|
167
|
+
The value to validate.
|
168
|
+
name : str
|
169
|
+
The name of the parameter for error messages.
|
170
|
+
|
171
|
+
Raises
|
172
|
+
------
|
173
|
+
ValueError
|
174
|
+
If the value is not a fraction.
|
175
|
+
"""
|
176
|
+
if not (0 <= value <= 1):
|
177
|
+
raise ValueError(f"{name} must be a fraction between 0 and 1. Provided: {value}.")
|
178
|
+
|
179
|
+
@staticmethod
|
180
|
+
def validate_pandas_dataframe(
|
181
|
+
df: pd.DataFrame,
|
182
|
+
name: str,
|
183
|
+
column_names: list = None
|
184
|
+
) -> None:
|
185
|
+
"""
|
186
|
+
Validate that the input is a pandas DataFrame.
|
187
|
+
|
188
|
+
Parameters
|
189
|
+
----------
|
190
|
+
df : pd.DataFrame
|
191
|
+
The DataFrame to validate.
|
192
|
+
name : str
|
193
|
+
The name of the DataFrame for error messages.
|
194
|
+
|
195
|
+
Raises
|
196
|
+
------
|
197
|
+
TypeError
|
198
|
+
If the input is not a pandas DataFrame.
|
199
|
+
"""
|
200
|
+
if not isinstance(df, pd.DataFrame):
|
201
|
+
raise TypeError(f"{name} must be a pandas DataFrame. Provided: {type(df)}.")
|
202
|
+
|
203
|
+
if column_names is not None:
|
204
|
+
missing_columns = [col for col in column_names if col not in df.columns]
|
205
|
+
if missing_columns:
|
206
|
+
raise ValueError(f"{name} is missing required columns: {missing_columns}. "
|
207
|
+
f"Available columns: {df.columns.tolist()}.")
|
208
|
+
|
209
|
+
@staticmethod
|
210
|
+
def validate_electrochemical_reference(reference: str) -> None:
|
211
|
+
"""
|
212
|
+
Validate the electrochemical reference electrode.
|
213
|
+
|
214
|
+
Parameters
|
215
|
+
----------
|
216
|
+
reference : str
|
217
|
+
The reference electrode to validate.
|
218
|
+
|
219
|
+
Raises
|
220
|
+
------
|
221
|
+
ValueError
|
222
|
+
If the reference is not a valid electrochemical reference.
|
223
|
+
"""
|
224
|
+
ValidationMixin.validate_string(reference, "Electrochemical reference")
|
225
|
+
|
226
|
+
if reference not in ALLOWED_REFERENCE:
|
227
|
+
raise ValueError(f"Invalid electrochemical reference: {reference}. "
|
228
|
+
f"Must be one of {ALLOWED_REFERENCE}.")
|
229
|
+
|
230
|
+
@staticmethod
|
231
|
+
def validate_datum(datum: np.ndarray) -> None:
|
232
|
+
"""
|
233
|
+
Validate the datum point for extrusion.
|
234
|
+
|
235
|
+
Parameters
|
236
|
+
----------
|
237
|
+
datum : np.ndarray
|
238
|
+
Datum point for extrusion (shape (3,))
|
239
|
+
|
240
|
+
Raises
|
241
|
+
------
|
242
|
+
ValueError
|
243
|
+
If the datum does not have exactly 3 coordinates.
|
244
|
+
"""
|
245
|
+
if type(datum) is not tuple and len(datum) != 3:
|
246
|
+
raise ValueError("Datum must be a 3D point with exactly 3 coordinates.")
|
247
|
+
|
248
|
+
if not all(isinstance(coord, (int, float)) for coord in datum):
|
249
|
+
raise TypeError("All coordinates in datum must be numbers.")
|
250
|
+
|
251
|
+
@staticmethod
|
252
|
+
def validate_current_collector_material(material: Type) -> None:
|
253
|
+
"""
|
254
|
+
Validate the current collector material.
|
255
|
+
|
256
|
+
Parameters
|
257
|
+
----------
|
258
|
+
material : str
|
259
|
+
The material to validate.
|
260
|
+
|
261
|
+
Raises
|
262
|
+
------
|
263
|
+
ValueError
|
264
|
+
If the material is not a valid current collector material.
|
265
|
+
"""
|
266
|
+
from steer_materials.CellMaterials.Base import CurrentCollectorMaterial
|
267
|
+
|
268
|
+
if type(material) is not CurrentCollectorMaterial:
|
269
|
+
|
270
|
+
raise ValueError(f"Invalid current collector material: {material}. "
|
271
|
+
"Must be an instance of CurrentCollectorMaterial.")
|
272
|
+
|
273
|
+
@staticmethod
|
274
|
+
def validate_weld_tab(tab) -> None:
|
275
|
+
"""
|
276
|
+
Validate the weld tab.
|
277
|
+
|
278
|
+
Parameters
|
279
|
+
----------
|
280
|
+
tab : WeldTab
|
281
|
+
The weld tab to validate.
|
282
|
+
|
283
|
+
Raises
|
284
|
+
------
|
285
|
+
ValueError
|
286
|
+
If the weld tab is not valid.
|
287
|
+
"""
|
288
|
+
from steer_opencell_design.Components.CurrentCollectors import WeldTab
|
289
|
+
|
290
|
+
if not isinstance(tab, WeldTab):
|
291
|
+
raise ValueError(f"Invalid weld tab: {tab}. Must be an instance of WeldTab.")
|
292
|
+
|
293
|
+
@staticmethod
|
294
|
+
def validate_positive_float(value: float, name: str) -> None:
|
295
|
+
"""
|
296
|
+
Validate that a value is a positive float.
|
297
|
+
|
298
|
+
Parameters
|
299
|
+
----------
|
300
|
+
value : float
|
301
|
+
The value to validate.
|
302
|
+
name : str
|
303
|
+
The name of the parameter for error messages.
|
304
|
+
|
305
|
+
Raises
|
306
|
+
------
|
307
|
+
ValueError
|
308
|
+
If the value is not a positive float.
|
309
|
+
"""
|
310
|
+
if not isinstance(value, (int, float)):
|
311
|
+
raise ValueError(f"{name} must be a positive float. Provided: {value}.")
|
312
|
+
|
313
|
+
@staticmethod
|
314
|
+
def validate_string(value: str, name: str) -> None:
|
315
|
+
"""
|
316
|
+
Validate that a value is a string.
|
317
|
+
|
318
|
+
Parameters
|
319
|
+
----------
|
320
|
+
value : str
|
321
|
+
The value to validate.
|
322
|
+
name : str
|
323
|
+
The name of the parameter for error messages.
|
324
|
+
|
325
|
+
Raises
|
326
|
+
------
|
327
|
+
TypeError
|
328
|
+
If the value is not a string.
|
329
|
+
"""
|
330
|
+
if not isinstance(value, str):
|
331
|
+
raise TypeError(f"{name} must be a string. Provided: {value}.")
|
332
|
+
|
333
|
+
@staticmethod
|
334
|
+
def validate_two_iterable_of_floats(value: tuple, name: str) -> None:
|
335
|
+
"""
|
336
|
+
Validate that a value is a tuple of two iterables.
|
337
|
+
|
338
|
+
Parameters
|
339
|
+
----------
|
340
|
+
value : tuple
|
341
|
+
The value to validate.
|
342
|
+
name : str
|
343
|
+
The name of the parameter for error messages.
|
344
|
+
|
345
|
+
Raises
|
346
|
+
------
|
347
|
+
TypeError
|
348
|
+
If the value is not a tuple of two floats.
|
349
|
+
"""
|
350
|
+
# Accept both tuples and lists
|
351
|
+
if not isinstance(value, (tuple, list)) or len(value) != 2:
|
352
|
+
raise TypeError(f"{name} must be a tuple or list of two numbers. Provided: {value}.")
|
353
|
+
|
354
|
+
# Check if all values are numeric (int or float)
|
355
|
+
if not all(isinstance(v, (int, float)) for v in value):
|
356
|
+
raise TypeError(f"{name} must be a tuple or list of two numbers. Provided: {value}.")
|
357
|
+
|
358
|
+
# Check if all values are non-negative
|
359
|
+
if not all(v >= 0 for v in value):
|
360
|
+
raise ValueError(f"{name} must be a tuple or list of two non-negative numbers. Provided: {value}.")
|
361
|
+
|
362
|
+
@staticmethod
|
363
|
+
def validate_positive_float_list(value: list, name: str) -> None:
|
364
|
+
"""
|
365
|
+
Validate that a value is a list of positive floats.
|
366
|
+
|
367
|
+
Parameters
|
368
|
+
----------
|
369
|
+
value : list
|
370
|
+
The value to validate.
|
371
|
+
name : str
|
372
|
+
The name of the parameter for error messages.
|
373
|
+
|
374
|
+
Raises
|
375
|
+
------
|
376
|
+
TypeError
|
377
|
+
If the value is not a list of positive floats.
|
378
|
+
"""
|
379
|
+
if not isinstance(value, list) or not all(isinstance(v, (int, float)) and v > 0 for v in value):
|
380
|
+
raise TypeError(f"{name} must be a list of positive floats. Provided: {value}.")
|
381
|
+
|
382
|
+
if len(value) == 0:
|
383
|
+
raise ValueError(f"{name} must not be an empty list. Provided: {value}.")
|
384
|
+
|
385
|
+
|
386
|
+
class DataMixin:
|
387
|
+
"""
|
388
|
+
A mixin class to handle data processing and validation for electrode materials.
|
389
|
+
Provides methods to calculate properties, check curve directions, and process half-cell curves.
|
390
|
+
"""
|
391
|
+
@staticmethod
|
392
|
+
def enforce_monotonicity(array: np.ndarray) -> np.ndarray:
|
393
|
+
"""
|
394
|
+
Enforces a monotonic version of the input array.
|
395
|
+
If the array is not monotonic, it is smoothed using cumulative max/min.
|
396
|
+
"""
|
397
|
+
x = np.arange(len(array))
|
398
|
+
diff = np.diff(array)
|
399
|
+
|
400
|
+
if np.all(diff >= 0):
|
401
|
+
return array # Already monotonic increasing
|
402
|
+
|
403
|
+
if np.all(diff <= 0):
|
404
|
+
return array # Already monotonic decreasing, reverse it
|
405
|
+
|
406
|
+
# Determine general trend (ascending or descending)
|
407
|
+
ascending = array[-1] >= array[0]
|
408
|
+
|
409
|
+
# Sort by x so that PCHIP works (PCHIP requires increasing x)
|
410
|
+
# We'll smooth the array using PCHIP, then enforce monotonicity
|
411
|
+
interpolator = PchipInterpolator(x, array, extrapolate=False)
|
412
|
+
new_array = interpolator(x)
|
413
|
+
|
414
|
+
# Enforce strict monotonicity post-smoothing
|
415
|
+
if ascending:
|
416
|
+
new_array = np.maximum.accumulate(new_array)
|
417
|
+
else:
|
418
|
+
new_array = np.minimum.accumulate(new_array)
|
419
|
+
|
420
|
+
return new_array
|
steer_core/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.1"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: steer-core
|
3
|
+
Version: 0.1.1
|
4
|
+
Summary: Modelling energy storage from cell to site - STEER OpenCell Design
|
5
|
+
Home-page: https://github.com/nicholas9182/steer-core/
|
6
|
+
Author: Nicholas Siemons
|
7
|
+
Author-email: nsiemons@stanford.edu
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.10
|
12
|
+
Requires-Dist: pandas
|
13
|
+
Requires-Dist: numpy
|
14
|
+
Requires-Dist: datetime
|
15
|
+
Requires-Dist: scipy
|
16
|
+
Requires-Dist: shapely
|
17
|
+
Requires-Dist: plotly
|
18
|
+
Requires-Dist: dash
|
19
|
+
Requires-Dist: dash_bootstrap_components
|
20
|
+
Requires-Dist: flask_caching
|
21
|
+
Requires-Dist: nbformat
|
22
|
+
Requires-Dist: scipy
|
23
|
+
Dynamic: author
|
24
|
+
Dynamic: author-email
|
25
|
+
Dynamic: classifier
|
26
|
+
Dynamic: home-page
|
27
|
+
Dynamic: requires-dist
|
28
|
+
Dynamic: requires-python
|
29
|
+
Dynamic: summary
|
@@ -0,0 +1,21 @@
|
|
1
|
+
steer_core/DataManager.py,sha256=r9AmA1Aqmjk66xrUp03GFdVbp0ChFod6NmonUr8SGOw,11094
|
2
|
+
steer_core/__init__.py,sha256=rnObPjuBcEStqSO0S6gsdS_ot8ITOQjVj_-P1LUUYpg,22
|
3
|
+
steer_core/Constants/Units.py,sha256=Bdlv_APY2-kbKva3FW4jA0srTbavqAumrEaaQ6HNKuU,489
|
4
|
+
steer_core/Constants/Universal.py,sha256=5FWdrex5NiI2DResDmwO7GIvGN2B0DNtdlG1l-ysDh8,41
|
5
|
+
steer_core/Constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
steer_core/ContextManagers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
steer_core/Decorators/Coordinates.py,sha256=qo79PlA8ZZ6QY-VvH9YGg27gqpVJ2-Xa3blyoQVCp7A,1436
|
8
|
+
steer_core/Decorators/Electrochemical.py,sha256=fAy89aw3zspBu_8UPa5kEhUpvO-bYpM0xH1r6O6mSiA,985
|
9
|
+
steer_core/Decorators/General.py,sha256=-Wu-kTC9JAokicgt_nvANR7zpbCBPNR1kDmY6jzHfy4,966
|
10
|
+
steer_core/Decorators/Objects.py,sha256=xiQfFgyTY2k8MF4lWojHMWbUMzojpyt8fRGl5bTNze8,503
|
11
|
+
steer_core/Decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
steer_core/Mixins/Colors.py,sha256=rJnXyUb9hdy3SkzrPV9dXvnBFZ1funYRF8Q0EZr3vZA,1152
|
13
|
+
steer_core/Mixins/Coordinates.py,sha256=L5-OqG30lpqLjJd4SDhSxF_W2G4q5vsLMFfxTZjNzcg,12937
|
14
|
+
steer_core/Mixins/Data.py,sha256=wdNedZHS5Q7B-pCIoEbqTWMcxsC91CVn9pbQ2Zszg3w,1363
|
15
|
+
steer_core/Mixins/Serializer.py,sha256=x-aX8BcFSh5egZdkKinYMmIHLqZymCnETNUld3hzgnk,1039
|
16
|
+
steer_core/Mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
|
+
steer_core/Mixins/validators.py,sha256=VmWmv1M8YqTzaIyc95TOvNEJScG7o8rQezQNor84DEc,12933
|
18
|
+
steer_core-0.1.1.dist-info/METADATA,sha256=4egl0gBt2R3wB1iMjWWkUYhZKH8647UiJjseU0zAfTo,826
|
19
|
+
steer_core-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
+
steer_core-0.1.1.dist-info/top_level.txt,sha256=6LFpGCSDE_SqRoT7raeM3Ax7KTBKQnyXLXxM9kXtw5M,11
|
21
|
+
steer_core-0.1.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
steer_core
|