cellpycore 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.
- cellpycore/__init__.py +0 -0
- cellpycore/_helpers.py +199 -0
- cellpycore/cell_core.py +696 -0
- cellpycore/config.py +674 -0
- cellpycore/extractors.py +156 -0
- cellpycore/header_mapping.py +267 -0
- cellpycore/legacy.py +336 -0
- cellpycore/metadata/__init__.py +59 -0
- cellpycore/metadata/io.py +192 -0
- cellpycore/metadata/models.py +238 -0
- cellpycore/selectors.py +470 -0
- cellpycore/settings_base.py +98 -0
- cellpycore/summarizers.py +903 -0
- cellpycore/timestamps.py +128 -0
- cellpycore/units.py +257 -0
- cellpycore-0.1.1.dist-info/METADATA +90 -0
- cellpycore-0.1.1.dist-info/RECORD +19 -0
- cellpycore-0.1.1.dist-info/WHEEL +4 -0
- cellpycore-0.1.1.dist-info/licenses/LICENSE +21 -0
cellpycore/__init__.py
ADDED
|
File without changes
|
cellpycore/_helpers.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Helper functions only intended for development purposes (e.g. for creating mock data)."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
DataFrame = TypeVar("DataFrame")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_raw_data() -> DataFrame:
|
|
9
|
+
"""Create mock raw battery testing data with realistic values.
|
|
10
|
+
|
|
11
|
+
TODO: This function was generated using AI and has not been checked for correctness!
|
|
12
|
+
|
|
13
|
+
Note that this function is called separately for each test that uses the mock_data_with_raw fixture.
|
|
14
|
+
If the test-suite is getting slow, consider caching the result of this function or using a different approach.
|
|
15
|
+
"""
|
|
16
|
+
import polars as pl
|
|
17
|
+
from cellpycore.config import RawCols
|
|
18
|
+
from cellpycore.timestamps import NS_PER_SECOND
|
|
19
|
+
|
|
20
|
+
# Create a RawCols instance to get column names
|
|
21
|
+
raw_cols = RawCols()
|
|
22
|
+
|
|
23
|
+
# Generate realistic battery testing data
|
|
24
|
+
n_points = 1000 # Number of data points
|
|
25
|
+
|
|
26
|
+
# Create time series data
|
|
27
|
+
test_time = pl.Series(raw_cols.test_time, range(n_points), dtype=pl.Float64)
|
|
28
|
+
# epoch_time_utc is int64 nanoseconds since the Unix epoch, UTC (canonical
|
|
29
|
+
# absolute-timestamp dtype; see cellpycore.timestamps). Start at 2021-01-01 and
|
|
30
|
+
# advance one second per row.
|
|
31
|
+
start_epoch_s = 1609459200
|
|
32
|
+
epoch_time_utc = pl.Series(
|
|
33
|
+
raw_cols.epoch_time_utc,
|
|
34
|
+
[(start_epoch_s + i) * NS_PER_SECOND for i in range(n_points)],
|
|
35
|
+
dtype=pl.Int64,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Generate cycle and step data
|
|
39
|
+
cycle_num = pl.Series(
|
|
40
|
+
raw_cols.cycle_num, [i // 100 for i in range(n_points)], dtype=pl.Int64
|
|
41
|
+
)
|
|
42
|
+
step_num = pl.Series(
|
|
43
|
+
raw_cols.step_num, [(i % 100) // 10 for i in range(n_points)], dtype=pl.Int64
|
|
44
|
+
)
|
|
45
|
+
datapoint_num = pl.Series(raw_cols.datapoint_num, range(n_points), dtype=pl.Int64)
|
|
46
|
+
source_datapoint_num = pl.Series(
|
|
47
|
+
raw_cols.source_datapoint_num, range(n_points), dtype=pl.Int64
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Generate step types (charge, discharge, rest)
|
|
51
|
+
step_types = []
|
|
52
|
+
for i in range(n_points):
|
|
53
|
+
step_idx = i % 10
|
|
54
|
+
if step_idx < 3:
|
|
55
|
+
step_types.append("charge")
|
|
56
|
+
elif step_idx < 6:
|
|
57
|
+
step_types.append("discharge")
|
|
58
|
+
else:
|
|
59
|
+
step_types.append("rest")
|
|
60
|
+
|
|
61
|
+
step_type = pl.Series(raw_cols.step_type, step_types, dtype=pl.Utf8)
|
|
62
|
+
step_type_detail = pl.Series(
|
|
63
|
+
raw_cols.step_type_detail, [f"{t}_cc" for t in step_types], dtype=pl.Utf8
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Generate current data (charge: positive, discharge: negative, rest: 0)
|
|
67
|
+
current = []
|
|
68
|
+
for i, st in enumerate(step_types):
|
|
69
|
+
if st == "charge":
|
|
70
|
+
current.append(1.0 + (i % 5) * 0.1) # 1.0 to 1.4 A
|
|
71
|
+
elif st == "discharge":
|
|
72
|
+
current.append(-1.0 - (i % 5) * 0.1) # -1.0 to -1.4 A
|
|
73
|
+
else:
|
|
74
|
+
current.append(0.0)
|
|
75
|
+
|
|
76
|
+
current = pl.Series(raw_cols.current, current, dtype=pl.Float64)
|
|
77
|
+
|
|
78
|
+
# Generate potential data (voltage)
|
|
79
|
+
potential = []
|
|
80
|
+
base_voltage = 3.7 # Typical Li-ion voltage
|
|
81
|
+
for i, st in enumerate(step_types):
|
|
82
|
+
if st == "charge":
|
|
83
|
+
# Voltage increases during charge
|
|
84
|
+
potential.append(base_voltage + (i % 100) * 0.01)
|
|
85
|
+
elif st == "discharge":
|
|
86
|
+
# Voltage decreases during discharge
|
|
87
|
+
potential.append(base_voltage - (i % 100) * 0.01)
|
|
88
|
+
else:
|
|
89
|
+
# Rest voltage
|
|
90
|
+
potential.append(base_voltage + (i % 10) * 0.001)
|
|
91
|
+
|
|
92
|
+
potential = pl.Series(raw_cols.potential, potential, dtype=pl.Float64)
|
|
93
|
+
|
|
94
|
+
# Generate cumulative capacities (cumulative per cycle, per direction).
|
|
95
|
+
cumulative_charge_capacity = []
|
|
96
|
+
cumulative_discharge_capacity = []
|
|
97
|
+
charge_cap = 0.0
|
|
98
|
+
discharge_cap = 0.0
|
|
99
|
+
|
|
100
|
+
for i, (curr, st) in enumerate(zip(current, step_types)):
|
|
101
|
+
if st == "charge":
|
|
102
|
+
charge_cap += abs(curr) * 0.1 # Assuming 0.1 hour time step
|
|
103
|
+
elif st == "discharge":
|
|
104
|
+
discharge_cap += abs(curr) * 0.1
|
|
105
|
+
cumulative_charge_capacity.append(charge_cap)
|
|
106
|
+
cumulative_discharge_capacity.append(discharge_cap)
|
|
107
|
+
|
|
108
|
+
cumulative_charge_capacity = pl.Series(
|
|
109
|
+
raw_cols.cumulative_charge_capacity,
|
|
110
|
+
cumulative_charge_capacity,
|
|
111
|
+
dtype=pl.Float64,
|
|
112
|
+
)
|
|
113
|
+
cumulative_discharge_capacity = pl.Series(
|
|
114
|
+
raw_cols.cumulative_discharge_capacity,
|
|
115
|
+
cumulative_discharge_capacity,
|
|
116
|
+
dtype=pl.Float64,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Step time (seconds since the start of the current step) and instrument IR.
|
|
120
|
+
step_time = pl.Series(
|
|
121
|
+
raw_cols.step_time, [float(i % 10) for i in range(n_points)], dtype=pl.Float64
|
|
122
|
+
)
|
|
123
|
+
internal_resistance = pl.Series(
|
|
124
|
+
raw_cols.internal_resistance, [0.05] * n_points, dtype=pl.Float64
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Reference-electrode potential (3-electrode setup): the cell potential
|
|
128
|
+
# shifted by a small constant offset vs the reference electrode.
|
|
129
|
+
ref_potential = pl.Series(
|
|
130
|
+
raw_cols.ref_potential, [v - 0.2 for v in potential], dtype=pl.Float64
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Generate auxiliary temperature data
|
|
134
|
+
temperature_cell = pl.Series(
|
|
135
|
+
raw_cols.aux_temperature_cell,
|
|
136
|
+
[25.0 + (i % 20) * 0.5 for i in range(n_points)],
|
|
137
|
+
dtype=pl.Float64,
|
|
138
|
+
)
|
|
139
|
+
temperature_chamber = pl.Series(
|
|
140
|
+
raw_cols.aux_temperature_chamber,
|
|
141
|
+
[25.0 + (i % 10) * 0.2 for i in range(n_points)],
|
|
142
|
+
dtype=pl.Float64,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Generate other metadata
|
|
146
|
+
source_type = pl.Series(raw_cols.source_type, ["maccor"] * n_points, dtype=pl.Utf8)
|
|
147
|
+
source_uuid = pl.Series(
|
|
148
|
+
raw_cols.source_uuid, [f"test_{i:06d}" for i in range(n_points)], dtype=pl.Utf8
|
|
149
|
+
)
|
|
150
|
+
step_mode = pl.Series(raw_cols.step_mode, ["CC"] * n_points, dtype=pl.Utf8)
|
|
151
|
+
pressure = pl.Series(
|
|
152
|
+
raw_cols.aux_pressure_cell,
|
|
153
|
+
[101325.0 + (i % 100) * 10 for i in range(n_points)],
|
|
154
|
+
dtype=pl.Float64,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Create the DataFrame using the column names from RawCols
|
|
158
|
+
data = {
|
|
159
|
+
raw_cols.source_type: source_type,
|
|
160
|
+
raw_cols.source_uuid: source_uuid,
|
|
161
|
+
raw_cols.source_datapoint_num: source_datapoint_num,
|
|
162
|
+
raw_cols.datapoint_num: datapoint_num,
|
|
163
|
+
raw_cols.step_num: step_num,
|
|
164
|
+
raw_cols.cycle_num: cycle_num,
|
|
165
|
+
raw_cols.epoch_time_utc: epoch_time_utc,
|
|
166
|
+
raw_cols.test_time: test_time,
|
|
167
|
+
raw_cols.step_time: step_time,
|
|
168
|
+
raw_cols.step_mode: step_mode,
|
|
169
|
+
raw_cols.step_type: step_type,
|
|
170
|
+
raw_cols.step_type_detail: step_type_detail,
|
|
171
|
+
raw_cols.potential: potential,
|
|
172
|
+
raw_cols.current: current,
|
|
173
|
+
raw_cols.internal_resistance: internal_resistance,
|
|
174
|
+
raw_cols.ref_potential: ref_potential,
|
|
175
|
+
raw_cols.aux_temperature_cell: temperature_cell,
|
|
176
|
+
raw_cols.aux_temperature_chamber: temperature_chamber,
|
|
177
|
+
raw_cols.aux_pressure_cell: pressure,
|
|
178
|
+
raw_cols.cumulative_charge_capacity: cumulative_charge_capacity,
|
|
179
|
+
raw_cols.cumulative_discharge_capacity: cumulative_discharge_capacity,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return pl.DataFrame(data)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
import matplotlib.pyplot as plt
|
|
187
|
+
df = create_raw_data()
|
|
188
|
+
df_pandas = df.to_pandas()
|
|
189
|
+
|
|
190
|
+
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
|
|
191
|
+
axs[0, 0].plot(df_pandas["test_time"], df_pandas["potential"])
|
|
192
|
+
axs[0, 0].set_title("Potential")
|
|
193
|
+
axs[0, 1].plot(df_pandas["test_time"], df_pandas["current"])
|
|
194
|
+
axs[0, 1].set_title("Current")
|
|
195
|
+
axs[1, 0].plot(df_pandas["test_time"], df_pandas["aux_temperature_cell"])
|
|
196
|
+
axs[1, 0].set_title("Temperature Cell")
|
|
197
|
+
axs[1, 1].plot(df_pandas["test_time"], df_pandas["aux_temperature_chamber"])
|
|
198
|
+
axs[1, 1].set_title("Temperature Chamber")
|
|
199
|
+
plt.show()
|