steer-core 0.1.35__py3-none-any.whl → 0.1.37__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.
@@ -19,6 +19,19 @@ mG_TO_G = 1e-3
19
19
  G_TO_mG = 1e3
20
20
  CM_TO_UM = 1e4
21
21
  UM_TO_CM = 1e-4
22
+ KG_TO_T = 1e-3
23
+ T_TO_KG = 1e3
24
+ T_TO_MT = 1e-6
25
+ MT_TO_T = 1e6
26
+ LB_TO_KG = 0.453592
27
+ KG_TO_LB = 1 / 0.453592
28
+ T_TO_SHORT_TON = 1.10231
29
+ SHORT_TON_TO_T = 1 / 1.10231
30
+ LB_TO_SHORT_TON = 1 / 2000
31
+ SHORT_TON_TO_LB = 2000
32
+ LB_TO_T = 1 / 2000 * 1/1.10231
33
+
34
+
22
35
 
23
36
  # Current units
24
37
  A_TO_mA = 1e3
@@ -31,6 +44,9 @@ S_TO_MIN = 1 / 60
31
44
  MIN_TO_S = 60
32
45
  S_TO_Y = 1 / (3600 * 24 * 365)
33
46
  Y_TO_S = 3600 * 24 * 365
47
+ H_TO_Y = 1 / 8760
48
+ Y_TO_H = 8760
49
+
34
50
 
35
51
  # Energy units
36
52
  W_TO_KW = 1e-3
@@ -42,7 +58,14 @@ DEG_TO_RAD = 0.017453292519943295
42
58
  # Percentage units
43
59
  PERCENT_TO_FRACTION = 1e-2
44
60
  FRACTION_TO_PERCENT = 1e2
61
+ FRACTION_TO_PPM = 1e6
62
+ PPM_TO_FRACTION = 1e-6
45
63
 
46
64
  # Volume units
47
65
  L_TO_M3 = 1e-3
48
- M3_TO_L = 1e3
66
+ M3_TO_L = 1e3
67
+ GAL_TO_L = 3.78541
68
+ L_TO_GAL = 1 / 3.78541
69
+ MMGAL_TO_GAL = 1e-6
70
+ GAL_TO_MMGAL = 1e6
71
+
@@ -1,2 +1,7 @@
1
1
  ## Constants
2
2
  PI = 3.14159265358979323846
3
+
4
+ # Molar Masses
5
+ M_G_PER_MOL_NO2 = 46.0055
6
+ M_G_PER_MOL_SO2 = 64.0638
7
+ MW_G_PER_MOL_CO2 = 44.0095
@@ -257,7 +257,7 @@ class SerializerMixin:
257
257
  ValueError
258
258
  If the object name is not found in any of the tables.
259
259
  """
260
- from steer_core.Data.DataManager import DataManager
260
+ from steer_opencell_data.DataManager import DataManager
261
261
 
262
262
  database = DataManager()
263
263
 
steer_core/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.35"
1
+ __version__ = "0.1.37"
2
2
 
3
3
  from .Mixins.Colors import ColorMixin
4
4
  from .Mixins.Coordinates import CoordinateMixin
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steer-core
3
- Version: 0.1.35
3
+ Version: 0.1.37
4
4
  Summary: Modelling energy storage from cell to site - STEER OpenCell Design
5
5
  Author-email: Nicholas Siemons <nsiemons@stanford.edu>
6
6
  Maintainer-email: Nicholas Siemons <nsiemons@stanford.edu>
@@ -21,14 +21,11 @@ Classifier: Topic :: Scientific/Engineering
21
21
  Classifier: Topic :: Scientific/Engineering :: Physics
22
22
  Requires-Python: >=3.10
23
23
  Description-Content-Type: text/markdown
24
- Requires-Dist: pandas==2.1.4
25
- Requires-Dist: numpy==1.26.4
24
+ Requires-Dist: pandas==2.3.3
25
+ Requires-Dist: numpy==2.2.6
26
26
  Requires-Dist: datetime==5.5
27
27
  Requires-Dist: plotly==6.2.0
28
28
  Requires-Dist: scipy==1.15.3
29
- Requires-Dist: msgpack==1.1.2
30
- Requires-Dist: msgpack-numpy==0.4.8
31
- Requires-Dist: nbformat==5.10.4
32
29
  Provides-Extra: dev
33
30
  Requires-Dist: pytest>=7.0; extra == "dev"
34
31
  Requires-Dist: pytest-cov; extra == "dev"
@@ -1,11 +1,9 @@
1
- steer_core/__init__.py,sha256=IC03czBN7PlRAriVyRNwltNF1GKfFm4LVWJ43B1SCtA,321
2
- steer_core/Constants/Units.py,sha256=QIV_lDX7rANH-MKP90jOyiXbGueL15LILKMNr5dSWoI,714
3
- steer_core/Constants/Universal.py,sha256=5FWdrex5NiI2DResDmwO7GIvGN2B0DNtdlG1l-ysDh8,41
1
+ steer_core/__init__.py,sha256=iv1Zv2edpDkh91ynlce2_TditJjHT03A-Lt6ECkaBIA,321
2
+ steer_core/Constants/Units.py,sha256=wfbR52HHhFyeYBYyiJTTbFISuSQNzxnnfj-ifeDHDts,1114
3
+ steer_core/Constants/Universal.py,sha256=_9FlNWGdGyjDx5zI8J-4M7B4_ThqXD4xauCNytv2KdY,135
4
4
  steer_core/Constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  steer_core/ContextManagers/ContextManagers.py,sha256=4rSeBdBi6xtKLMAbERklrYmZlbFr0zceAiwu-gjTR38,1869
6
6
  steer_core/ContextManagers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- steer_core/Data/DataManager.py,sha256=AUbuK1lmibTeY5oZ_RSmfof9FyFd9HQ5J2vMoLWsuAo,14707
8
- steer_core/Data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
7
  steer_core/Decorators/Coordinates.py,sha256=MxUWXQNrR9Q0_p4gGAywS4qnPAztajJzSay1Cu6lCRQ,1441
10
8
  steer_core/Decorators/General.py,sha256=lc7YdvxU-JDo8b4kunVzSjxcB3_8C185458HrXQq-lk,970
11
9
  steer_core/Decorators/Objects.py,sha256=aYaRQBFgdSE0IB4QgBVfb6GhEPagoU6TRNrW_pOaqQI,506
@@ -15,10 +13,10 @@ steer_core/Mixins/Coordinates.py,sha256=DEPKvySoiDT8JhQSEQDiQgAxGjEJK7MezuZysSvf
15
13
  steer_core/Mixins/Data.py,sha256=c313F85muxlBHQ6yl6AKrifNyV2toHvVwEy35fNNUNE,4434
16
14
  steer_core/Mixins/Dunder.py,sha256=cIwh1VhcwzlwelUo2eM1KllVtxZddrkr6g0a5CkXea8,7146
17
15
  steer_core/Mixins/Plotter.py,sha256=wRRF0C5fz_6polCKKRVnZ07UFL4HBohZ5K2BSm_ULsg,13505
18
- steer_core/Mixins/Serializer.py,sha256=a3-J1ESM0UkO7b8Ctzjj605-p9bfzoFjJydM8k2grEQ,10458
16
+ steer_core/Mixins/Serializer.py,sha256=1kqoY1U-SpGb-x3f46V4flhD6-PE19RjgLft2uJG19A,10462
19
17
  steer_core/Mixins/TypeChecker.py,sha256=eXgu1G4d2_btNg5AJ3UkTXpoFJ79bc6tSasInCnXqc0,10539
20
18
  steer_core/Mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- steer_core-0.1.35.dist-info/METADATA,sha256=4LOmW1xfFIctfghEvREPZzvUZyvhbUWXg219_dctDic,1699
22
- steer_core-0.1.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- steer_core-0.1.35.dist-info/top_level.txt,sha256=6LFpGCSDE_SqRoT7raeM3Ax7KTBKQnyXLXxM9kXtw5M,11
24
- steer_core-0.1.35.dist-info/RECORD,,
19
+ steer_core-0.1.37.dist-info/METADATA,sha256=OgqOn6S5ae7gjAJMbb9iqkqLj72lWd8hPX8LsJFs-cI,1600
20
+ steer_core-0.1.37.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
21
+ steer_core-0.1.37.dist-info/top_level.txt,sha256=6LFpGCSDE_SqRoT7raeM3Ax7KTBKQnyXLXxM9kXtw5M,11
22
+ steer_core-0.1.37.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,432 +0,0 @@
1
- import sqlite3 as sql
2
- from pathlib import Path
3
- from typing import TypeVar
4
- import pandas as pd
5
- import importlib.resources
6
-
7
- from steer_core.Constants.Units import *
8
- from steer_core.Mixins.Serializer import SerializerMixin
9
-
10
-
11
- T = TypeVar('T', bound='SerializerMixin')
12
-
13
-
14
- class DataManager:
15
-
16
- def __init__(self):
17
- with importlib.resources.path("steer_opencell_data", "database.db") as db_path:
18
- self._db_path = db_path
19
- self._connection = sql.connect(self._db_path)
20
- self._cursor = self._connection.cursor()
21
-
22
- def create_table(self, table_name: str, columns: dict):
23
- """
24
- Function to create a table in the database.
25
-
26
- :param table_name: Name of the table.
27
- :param columns: Dictionary of columns and their types.
28
- """
29
- columns_str = ", ".join([f"{k} {v}" for k, v in columns.items()])
30
- self._cursor.execute(f"CREATE TABLE IF NOT EXISTS {table_name} ({columns_str})")
31
- self._connection.commit()
32
-
33
- def drop_table(self, table_name: str):
34
- """
35
- Function to drop a table from the database.
36
-
37
- :param table_name: Name of the table.
38
- """
39
- self._cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
40
- self._connection.commit()
41
-
42
- def get_table_names(self):
43
- """
44
- Function to get the names of all tables in the database.
45
-
46
- :return: List of table names.
47
- """
48
- self._cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
49
- return [row[0] for row in self._cursor.fetchall()]
50
-
51
- def insert_data(self, table_name: str, data: pd.DataFrame):
52
- """
53
- Inserts data into the database only if it doesn’t already exist.
54
-
55
- :param table_name: Name of the table.
56
- :param data: DataFrame containing the data to insert.
57
- """
58
- for _, row in data.iterrows():
59
- conditions = " AND ".join([f"{col} = ?" for col in data.columns])
60
- check_query = f"SELECT COUNT(*) FROM {table_name} WHERE {conditions}"
61
-
62
- self._cursor.execute(check_query, tuple(row))
63
- if self._cursor.fetchone()[0] == 0: # If the row does not exist, insert it
64
- insert_query = f"INSERT INTO {table_name} ({', '.join(data.columns)}) VALUES ({', '.join(['?'] * len(row))})"
65
- self._cursor.execute(insert_query, tuple(row))
66
-
67
- self._connection.commit()
68
-
69
- def get_data(
70
- self,
71
- table_name: str,
72
- columns: list = None,
73
- condition: str | list[str] = None,
74
- latest_column: str = None,
75
- ):
76
- """
77
- Retrieve data from the database.
78
-
79
- :param table_name: Name of the table.
80
- :param columns: List of columns to retrieve. If None, retrieves all columns.
81
- :param condition: Optional condition (single string or list of conditions).
82
- :param latest_column: Column name to find the most recent row.
83
- """
84
- # If columns is not provided, get all columns from the table
85
- if columns is None:
86
- self._cursor.execute(f"PRAGMA table_info({table_name})")
87
- columns_info = self._cursor.fetchall()
88
- columns = [col[1] for col in columns_info] # Extract column names
89
- if not columns:
90
- raise ValueError(
91
- f"Table '{table_name}' does not exist or has no columns."
92
- )
93
-
94
- columns_str = ", ".join(columns)
95
- query = f"SELECT {columns_str} FROM {table_name}"
96
-
97
- # Add condition if specified
98
- if condition:
99
- if isinstance(condition, list):
100
- condition_str = " AND ".join(condition)
101
- else:
102
- condition_str = condition
103
- query += f" WHERE {condition_str}"
104
-
105
- # If latest_column is provided, get the most recent entry
106
- if latest_column:
107
- query += f" ORDER BY {latest_column} DESC LIMIT 1"
108
-
109
- # Execute and return the result
110
- self._cursor.execute(query)
111
- data = self._cursor.fetchall()
112
-
113
- return pd.DataFrame(data, columns=columns)
114
-
115
- def get_unique_values(self, table_name: str, column_name: str):
116
- """
117
- Retrieves all unique values from a specified column.
118
-
119
- :param table_name: The name of the table.
120
- :param column_name: The column to retrieve unique values from.
121
- :return: A list of unique values.
122
- """
123
- query = f"SELECT DISTINCT {column_name} FROM {table_name}"
124
- self._cursor.execute(query)
125
- return [row[0] for row in self._cursor.fetchall()]
126
-
127
- def get_current_collector_materials(self, most_recent: bool = True) -> pd.DataFrame:
128
- """
129
- Retrieves current collector materials from the database.
130
-
131
- :param most_recent: If True, returns only the most recent entry.
132
- :return: DataFrame with current collector materials.
133
- """
134
- data = (
135
- self.get_data(table_name="current_collector_materials")
136
- .groupby("name", group_keys=False)
137
- .apply(
138
- lambda x: x.sort_values("date", ascending=False).head(1)
139
- if most_recent
140
- else x
141
- )
142
- .reset_index(drop=True)
143
- )
144
-
145
- return data
146
-
147
- def get_insulation_materials(self, most_recent: bool = True) -> pd.DataFrame:
148
- """
149
- Retrieves insulation materials from the database.
150
-
151
- :param most_recent: If True, returns only the most recent entry.
152
- :return: DataFrame with insulation materials.
153
- """
154
- data = (
155
- self.get_data(table_name="insulation_materials")
156
- .groupby("name", group_keys=False)
157
- .apply(
158
- lambda x: x.sort_values("date", ascending=False).head(1)
159
- if most_recent
160
- else x
161
- )
162
- .reset_index(drop=True)
163
- )
164
-
165
- return data
166
-
167
- def get_cathode_materials(self, most_recent: bool = True) -> pd.DataFrame:
168
- """
169
- Retrieves cathode materials from the database.
170
-
171
- :param most_recent: If True, returns only the most recent entry.
172
- :return: DataFrame with cathode materials.
173
- """
174
- data = (
175
- self.get_data(table_name="cathode_materials")
176
- .groupby("name", group_keys=False)
177
- .apply(
178
- lambda x: x.sort_values("date", ascending=False).head(1)
179
- if most_recent
180
- else x
181
- )
182
- .reset_index(drop=True)
183
- )
184
-
185
- return data
186
-
187
- def get_anode_materials(self, most_recent: bool = True) -> pd.DataFrame:
188
- """
189
- Retrieves anode materials from the database.
190
-
191
- :param most_recent: If True, returns only the most recent entry.
192
- :return: DataFrame with anode materials.
193
- """
194
- data = (
195
- self.get_data(table_name="anode_materials")
196
- .groupby("name", group_keys=False)
197
- .apply(
198
- lambda x: x.sort_values("date", ascending=False).head(1)
199
- if most_recent
200
- else x
201
- )
202
- .reset_index(drop=True)
203
- )
204
-
205
- return data
206
-
207
- def get_binder_materials(self, most_recent: bool = True) -> pd.DataFrame:
208
- """
209
- Retrieves binder materials from the database.
210
-
211
- :param most_recent: If True, returns only the most recent entry.
212
- :return: DataFrame with binder materials.
213
- """
214
- data = (
215
- self.get_data(table_name="binder_materials")
216
- .groupby("name", group_keys=False)
217
- .apply(
218
- lambda x: x.sort_values("date", ascending=False).head(1)
219
- if most_recent
220
- else x
221
- )
222
- .reset_index(drop=True)
223
- )
224
-
225
- return data
226
-
227
- def get_conductive_additive_materials(
228
- self, most_recent: bool = True
229
- ) -> pd.DataFrame:
230
- """
231
- Retrieves conductive additives from the database.
232
-
233
- :param most_recent: If True, returns only the most recent entry.
234
- :return: DataFrame with conductive additives.
235
- """
236
- data = (
237
- self.get_data(table_name="conductive_additive_materials")
238
- .groupby("name", group_keys=False)
239
- .apply(
240
- lambda x: x.sort_values("date", ascending=False).head(1)
241
- if most_recent
242
- else x
243
- )
244
- .reset_index(drop=True)
245
- )
246
-
247
- return data
248
-
249
- def get_separator_materials(self, most_recent: bool = True) -> pd.DataFrame:
250
- """
251
- Retrieves separator materials from the database.
252
-
253
- :param most_recent: If True, returns only the most recent entry.
254
- :return: DataFrame with separator materials.
255
- """
256
- data = (
257
- self.get_data(table_name="separator_materials")
258
- .groupby("name", group_keys=False)
259
- .apply(
260
- lambda x: x.sort_values("date", ascending=False).head(1)
261
- if most_recent
262
- else x
263
- )
264
- .reset_index(drop=True)
265
- )
266
-
267
- return data
268
-
269
- def get_tape_materials(self, most_recent: bool = True) -> pd.DataFrame:
270
- """
271
- Retrieves tape materials from the database.
272
-
273
- :param most_recent: If True, returns only the most recent entry.
274
- :return: DataFrame with tape materials.
275
- """
276
- data = (
277
- self.get_data(table_name="tape_materials")
278
- .groupby("name", group_keys=False)
279
- .apply(
280
- lambda x: x.sort_values("date", ascending=False).head(1)
281
- if most_recent
282
- else x
283
- )
284
- .reset_index(drop=True)
285
- )
286
-
287
- return data
288
-
289
- def get_prismatic_container_materials(self, most_recent: bool = True) -> pd.DataFrame:
290
- """
291
- Retrieves prismatic container materials from the database.
292
-
293
- :param most_recent: If True, returns only the most recent entry.
294
- :return: DataFrame with prismatic container materials.
295
- """
296
- data = (
297
- self.get_data(table_name="prismatic_container_materials")
298
- .groupby("name", group_keys=False)
299
- .apply(
300
- lambda x: x.sort_values("date", ascending=False).head(1)
301
- if most_recent
302
- else x
303
- )
304
- .reset_index(drop=True)
305
- )
306
-
307
- return data
308
-
309
- @staticmethod
310
- def read_half_cell_curve(half_cell_path) -> pd.DataFrame:
311
- """
312
- Function to read in a half cell curve for this active material
313
-
314
- :param half_cell_path: Path to the half cell data file.
315
- :return: DataFrame with the specific capacity and voltage.
316
- """
317
- try:
318
- data = pd.read_csv(half_cell_path)
319
- except:
320
- raise FileNotFoundError(f"Could not find the file at {half_cell_path}")
321
-
322
- if "Specific Capacity (mAh/g)" not in data.columns:
323
- raise ValueError(
324
- "The file must have a column named 'Specific Capacity (mAh/g)'"
325
- )
326
-
327
- if "Voltage (V)" not in data.columns:
328
- raise ValueError("The file must have a column named 'Voltage (V)'")
329
-
330
- if "Step_ID" not in data.columns:
331
- raise ValueError("The file must have a column named 'Step_ID'")
332
-
333
- data = (
334
- data.rename(
335
- columns={
336
- "Specific Capacity (mAh/g)": "specific_capacity",
337
- "Voltage (V)": "voltage",
338
- "Step_ID": "step_id",
339
- }
340
- )
341
- .assign(
342
- specific_capacity=lambda x: x["specific_capacity"]
343
- * (H_TO_S * mA_TO_A / G_TO_KG)
344
- )
345
- .filter(["specific_capacity", "voltage", "step_id"])
346
- .groupby(["specific_capacity", "step_id"], group_keys=False)["voltage"]
347
- .max()
348
- .reset_index()
349
- .sort_values(["step_id", "specific_capacity"])
350
- )
351
-
352
- return data
353
-
354
- def remove_data(self, table_name: str, condition: str):
355
- """
356
- Function to remove data from the database.
357
-
358
- :param table_name: Name of the table.
359
- :param condition: Condition to remove rows.
360
- """
361
- self._cursor.execute(f"DELETE FROM {table_name} WHERE {condition}")
362
- self._connection.commit()
363
-
364
- @classmethod
365
- def from_database(cls: type[T], name: str, table_name: str = None) -> T:
366
- """
367
- Pull object from the database by name.
368
-
369
- Subclasses must define a '_table_name' class variable (str or list of str)
370
- unless table_name is explicitly provided.
371
-
372
- Parameters
373
- ----------
374
- name : str
375
- Name of the object to retrieve.
376
- table_name : str, optional
377
- Specific table to search. If provided, '_table_name' is not required.
378
- If None, uses class's _table_name.
379
-
380
- Returns
381
- -------
382
- T
383
- Instance of the class.
384
-
385
- Raises
386
- ------
387
- NotImplementedError
388
- If the subclass doesn't define '_table_name' and table_name is not provided.
389
- ValueError
390
- If the object name is not found in any of the tables.
391
- """
392
- database = cls()
393
-
394
- # Get list of tables to search
395
- if table_name:
396
- tables_to_search = [table_name]
397
- else:
398
- # Only check for _table_name if table_name wasn't provided
399
- if not hasattr(cls, '_table_name'):
400
- raise NotImplementedError(
401
- f"{cls.__name__} must define a '_table_name' class variable "
402
- "or provide 'table_name' argument"
403
- )
404
-
405
- if isinstance(cls._table_name, (list, tuple)):
406
- tables_to_search = cls._table_name
407
- else:
408
- tables_to_search = [cls._table_name]
409
-
410
- # Try each table until found
411
- for table in tables_to_search:
412
- available_materials = database.get_unique_values(table, "name")
413
-
414
- if name in available_materials:
415
- data = database.get_data(table, condition=f"name = '{name}'")
416
- serialized_bytes = data["object"].iloc[0]
417
- return cls.deserialize(serialized_bytes)
418
-
419
- # Not found in any table
420
- all_available = []
421
- for table in tables_to_search:
422
- all_available.extend(database.get_unique_values(table, "name"))
423
-
424
- raise ValueError(
425
- f"'{name}' not found in tables {tables_to_search}. "
426
- f"Available: {all_available}"
427
- )
428
-
429
- def __del__(self):
430
- self._connection.close()
431
-
432
-
File without changes