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.
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ steer_core