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/extractors.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Pluggable per-cycle summary extractors.
|
|
2
|
+
|
|
3
|
+
A *summary extractor* is a callable object that derives one or more per-cycle
|
|
4
|
+
columns from the engine frames (``raw`` / ``steps`` / ``summary``) and returns
|
|
5
|
+
them as a small polars frame keyed by the cycle-number column. The summary
|
|
6
|
+
helpers in :mod:`cellpycore.summarizers` accept an extractor so the *what to
|
|
7
|
+
extract* policy can be swapped without touching the engine plumbing (the join
|
|
8
|
+
onto the summary and null handling stay in the helper).
|
|
9
|
+
|
|
10
|
+
The first user is :func:`cellpycore.summarizers.ir_to_summary`, whose default
|
|
11
|
+
:class:`LastIRExtractor` implements the corrected internal-resistance semantics
|
|
12
|
+
(issue #23): per cycle, the internal resistance of the last datapoint of the
|
|
13
|
+
cycle's last charge / discharge step. Developers building on cellpy-core can
|
|
14
|
+
subclass :class:`SummaryExtractor` to plug in their own logic (for example an
|
|
15
|
+
extractor keyed off the dedicated ``"ir"`` step type) and pass it via the
|
|
16
|
+
``ir_extractor`` keyword argument.
|
|
17
|
+
|
|
18
|
+
This abstraction is intentionally minimal: only the IR extractor is provided
|
|
19
|
+
today. Other per-cycle helpers (C-rate, end-voltage) could adopt it later.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
|
|
26
|
+
import polars as pl
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from cellpycore.config import Schema
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SummaryExtractor:
|
|
33
|
+
"""Base class for callable per-cycle summary extractors.
|
|
34
|
+
|
|
35
|
+
Subclasses implement :meth:`__call__`, deriving per-cycle values from the
|
|
36
|
+
(polars) engine frames and returning a polars frame keyed by the summary
|
|
37
|
+
cycle-number column (``schema.cycle.cycle_num``) carrying one or more summary
|
|
38
|
+
columns. The caller left-joins the result onto the summary, so cycles the
|
|
39
|
+
extractor omits become missing (the helper decides how to fill them).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __call__(
|
|
43
|
+
self,
|
|
44
|
+
*,
|
|
45
|
+
raw: "pl.DataFrame",
|
|
46
|
+
steps: "pl.DataFrame",
|
|
47
|
+
summary: "pl.DataFrame",
|
|
48
|
+
schema: "Schema",
|
|
49
|
+
) -> "pl.DataFrame":
|
|
50
|
+
"""Return a per-cycle frame keyed by ``schema.cycle.cycle_num``.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
raw: The raw datapoint frame (polars, native schema).
|
|
54
|
+
steps: The per-step table (polars, native schema).
|
|
55
|
+
summary: The per-cycle summary built so far (polars, native schema).
|
|
56
|
+
schema: The column-header schema in use.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
A polars frame with a ``schema.cycle.cycle_num`` column plus one or
|
|
60
|
+
more derived per-cycle columns.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
NotImplementedError: Always, unless overridden by a subclass.
|
|
64
|
+
"""
|
|
65
|
+
raise NotImplementedError
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LastIRExtractor(SummaryExtractor):
|
|
69
|
+
"""Default internal-resistance extractor (issue #23).
|
|
70
|
+
|
|
71
|
+
For each cycle it reads the ``internal_resistance`` of the **last raw
|
|
72
|
+
datapoint** of the cycle's **last charge step** (``ir_charge``) and of the
|
|
73
|
+
cycle's **last discharge step** (``ir_discharge``). The value is taken
|
|
74
|
+
literally (no skipping of zero/null readings). Cycles without a charge
|
|
75
|
+
(resp. discharge) step are simply absent from the returned frame, so the
|
|
76
|
+
caller fills them with ``NaN``.
|
|
77
|
+
|
|
78
|
+
This fixes the legacy off-by-one cycle attribution (the old helper read the
|
|
79
|
+
first datapoint of the *first* charge/discharge step) and makes the
|
|
80
|
+
multiple-step case explicit (the *last* step wins instead of a silent
|
|
81
|
+
``[0]``).
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __call__(
|
|
85
|
+
self,
|
|
86
|
+
*,
|
|
87
|
+
raw: "pl.DataFrame",
|
|
88
|
+
steps: "pl.DataFrame",
|
|
89
|
+
summary: "pl.DataFrame",
|
|
90
|
+
schema: "Schema",
|
|
91
|
+
) -> "pl.DataFrame":
|
|
92
|
+
headers_raw = schema.raw
|
|
93
|
+
headers_steps = schema.step
|
|
94
|
+
headers_cycle = schema.cycle
|
|
95
|
+
|
|
96
|
+
# Group per (test, cycle, step) when the frames carry a ``test_id`` key so
|
|
97
|
+
# merged objects never mix steps/cycles across tests; otherwise fall back
|
|
98
|
+
# to (cycle, step), byte-identical to the single-test path.
|
|
99
|
+
use_tid = (
|
|
100
|
+
headers_raw.test_id in raw.columns
|
|
101
|
+
and headers_steps.test_id in steps.columns
|
|
102
|
+
)
|
|
103
|
+
raw_step_keys = (
|
|
104
|
+
[headers_raw.test_id, headers_raw.cycle_num, headers_raw.step_num]
|
|
105
|
+
if use_tid
|
|
106
|
+
else [headers_raw.cycle_num, headers_raw.step_num]
|
|
107
|
+
)
|
|
108
|
+
step_group_keys = (
|
|
109
|
+
[headers_steps.test_id, headers_steps.cycle_num]
|
|
110
|
+
if use_tid
|
|
111
|
+
else [headers_steps.cycle_num]
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# internal resistance of the last raw datapoint of each (cycle, step).
|
|
115
|
+
# maintain_order (no sort) mirrors the raw frame's natural acquisition
|
|
116
|
+
# order, so ``last`` is the chronologically last reading of the step.
|
|
117
|
+
ir_per_step = raw.group_by(raw_step_keys, maintain_order=True).agg(
|
|
118
|
+
pl.col(headers_raw.internal_resistance).last().alias("__ir")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _side(step_type: str, out_name: str) -> "pl.DataFrame":
|
|
122
|
+
last_step = (
|
|
123
|
+
steps.filter(pl.col(headers_steps.step_type) == step_type)
|
|
124
|
+
.group_by(step_group_keys, maintain_order=True)
|
|
125
|
+
.agg(pl.col(headers_steps.step_num).last().alias("__step"))
|
|
126
|
+
)
|
|
127
|
+
left_keys = (
|
|
128
|
+
[headers_steps.test_id, headers_steps.cycle_num, "__step"]
|
|
129
|
+
if use_tid
|
|
130
|
+
else [headers_steps.cycle_num, "__step"]
|
|
131
|
+
)
|
|
132
|
+
select_exprs = [
|
|
133
|
+
pl.col(headers_steps.cycle_num).alias(headers_cycle.cycle_num),
|
|
134
|
+
pl.col("__ir").alias(out_name),
|
|
135
|
+
]
|
|
136
|
+
if use_tid:
|
|
137
|
+
select_exprs.insert(
|
|
138
|
+
0, pl.col(headers_steps.test_id).alias(headers_cycle.test_id)
|
|
139
|
+
)
|
|
140
|
+
return last_step.join(
|
|
141
|
+
ir_per_step,
|
|
142
|
+
left_on=left_keys,
|
|
143
|
+
right_on=raw_step_keys,
|
|
144
|
+
how="left",
|
|
145
|
+
).select(select_exprs)
|
|
146
|
+
|
|
147
|
+
ir_charge = _side("charge", headers_cycle.ir_charge)
|
|
148
|
+
ir_discharge = _side("discharge", headers_cycle.ir_discharge)
|
|
149
|
+
join_on = (
|
|
150
|
+
[headers_cycle.test_id, headers_cycle.cycle_num]
|
|
151
|
+
if use_tid
|
|
152
|
+
else headers_cycle.cycle_num
|
|
153
|
+
)
|
|
154
|
+
return ir_charge.join(
|
|
155
|
+
ir_discharge, on=join_on, how="full", coalesce=True
|
|
156
|
+
)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Authoritative ``config.Cols`` <-> legacy ``Headers*`` column-name mapping.
|
|
2
|
+
|
|
3
|
+
This module is the single source of truth for how the native cellpy-core column
|
|
4
|
+
names (``config.RawCols`` / ``config.StepCols`` / ``config.CycleCols``) translate
|
|
5
|
+
to and from the legacy cellpy names (``legacy.HeadersNormal`` /
|
|
6
|
+
``legacy.HeadersStepTable`` / ``legacy.HeadersSummary``).
|
|
7
|
+
|
|
8
|
+
The legacy bridge (``cell_core.OldCellpyCellCore``) builds all of its native
|
|
9
|
+
<-> legacy rename dictionaries from the declarations here, so the translation
|
|
10
|
+
lives in exactly one place and is covered by ``tests/test_header_mapping.py``
|
|
11
|
+
(round-trip, totality, and bridge-parity tests).
|
|
12
|
+
|
|
13
|
+
Design notes:
|
|
14
|
+
|
|
15
|
+
- The mapping is defined over **column-name strings** (the values of the
|
|
16
|
+
dataclass fields), not over attribute names. This is what DataFrame renames
|
|
17
|
+
act on, and it side-steps the fact that legacy ``HeadersSummary`` has two
|
|
18
|
+
attributes that share a value (``discharge_capacity`` /
|
|
19
|
+
``discharge_capacity_raw``).
|
|
20
|
+
- Pairs include **identity pass-throughs** (native string == legacy string,
|
|
21
|
+
e.g. summary ``ir_charge`` / ``charge_c_rate`` / ``normalized_cycle_index``).
|
|
22
|
+
These columns are intentionally not renamed by the bridge (they already share
|
|
23
|
+
a name), but they are real, mapped columns and must be declared so the
|
|
24
|
+
"total" claim holds.
|
|
25
|
+
- "Lossless and total" is defined **modulo the documented exception sets**
|
|
26
|
+
below: every native column is either mapped or listed in a ``NATIVE_ONLY_*``
|
|
27
|
+
set, and every legacy column is either mapped or listed in a ``LEGACY_ONLY_*``
|
|
28
|
+
set. The exception sets are explicit (not derived) so that adding a new column
|
|
29
|
+
on either side fails the totality test until it is deliberately categorised.
|
|
30
|
+
|
|
31
|
+
Step-table granularity: the step engine produces per-signal statistic columns
|
|
32
|
+
(``<signal>_<stat>``). The native/legacy *base-signal* correspondence is declared
|
|
33
|
+
in :data:`STEP_BASE_PAIRS` and expanded with :data:`STAT_SUFFIXES`; scalar
|
|
34
|
+
(non-statistic) step columns are in :data:`STEP_SCALAR_PAIRS`. Note that the
|
|
35
|
+
``datapoint_num`` and ``test_time`` step signals are declared in ``StepCols`` only
|
|
36
|
+
with ``_first`` / ``_last`` variants (the engine emits just those two stats for
|
|
37
|
+
them), even though they participate in the base-signal mapping.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# --- statistic suffixes (native -> legacy) ----------------------------------
|
|
41
|
+
# The per-step engine names statistics ``<signal>_<native_stat>``; legacy cellpy
|
|
42
|
+
# uses ``<signal>_<legacy_stat>`` (only ``mean`` -> ``avr`` actually differs).
|
|
43
|
+
STAT_SUFFIXES = {
|
|
44
|
+
"mean": "avr",
|
|
45
|
+
"std": "std",
|
|
46
|
+
"min": "min",
|
|
47
|
+
"max": "max",
|
|
48
|
+
"first": "first",
|
|
49
|
+
"last": "last",
|
|
50
|
+
"delta": "delta",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# --- raw frame (native RawCols <-> legacy HeadersNormal) ---------------------
|
|
54
|
+
# Each entry is ``(native, legacy)``. Only these raw columns cross the bridge;
|
|
55
|
+
# everything else is a documented exception below.
|
|
56
|
+
RAW_PAIRS = [
|
|
57
|
+
("datapoint_num", "data_point"),
|
|
58
|
+
("test_time", "test_time"),
|
|
59
|
+
("step_time", "step_time"),
|
|
60
|
+
("cycle_num", "cycle_index"),
|
|
61
|
+
("step_num", "step_index"),
|
|
62
|
+
("current", "current"),
|
|
63
|
+
("potential", "voltage"),
|
|
64
|
+
("cumulative_charge_capacity", "charge_capacity"),
|
|
65
|
+
("cumulative_discharge_capacity", "discharge_capacity"),
|
|
66
|
+
("internal_resistance", "internal_resistance"),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# --- step table (native StepCols <-> legacy HeadersStepTable) ----------------
|
|
70
|
+
# Base signals carry the seven ``STAT_SUFFIXES`` variants; ``(native, legacy)``.
|
|
71
|
+
STEP_BASE_PAIRS = [
|
|
72
|
+
("datapoint_num", "point"),
|
|
73
|
+
("test_time", "test_time"),
|
|
74
|
+
("step_time", "step_time"),
|
|
75
|
+
("current", "current"),
|
|
76
|
+
("potential", "voltage"),
|
|
77
|
+
("charge_capacity", "charge"),
|
|
78
|
+
("discharge_capacity", "discharge"),
|
|
79
|
+
("internal_resistance", "ir"),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
# Scalar (non-statistic) step columns; ``(native, legacy)``.
|
|
83
|
+
STEP_SCALAR_PAIRS = [
|
|
84
|
+
("cycle_num", "cycle"),
|
|
85
|
+
("step_num", "step"),
|
|
86
|
+
("sub_step_num", "sub_step"),
|
|
87
|
+
("step_type", "type"),
|
|
88
|
+
("sub_step_type", "sub_type"),
|
|
89
|
+
("c_rate", "rate_avr"),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# --- cycle / summary (native CycleCols <-> legacy HeadersSummary) ------------
|
|
93
|
+
# ``(native, legacy)``. Includes identity pass-throughs (last block) so the
|
|
94
|
+
# totality claim holds; the bridge treats those as no-op renames.
|
|
95
|
+
CYCLE_PAIRS = [
|
|
96
|
+
("cycle_num", "cycle_index"),
|
|
97
|
+
("datapoint_num_last", "data_point"),
|
|
98
|
+
("last_test_time", "test_time"),
|
|
99
|
+
("charge_capacity", "charge_capacity"),
|
|
100
|
+
("discharge_capacity", "discharge_capacity"),
|
|
101
|
+
("coulombic_efficiency", "coulombic_efficiency"),
|
|
102
|
+
("coulombic_difference", "coulombic_difference"),
|
|
103
|
+
("charge_capacity_loss", "charge_capacity_loss"),
|
|
104
|
+
("discharge_capacity_loss", "discharge_capacity_loss"),
|
|
105
|
+
("test_cumulated_charge_capacity", "cumulated_charge_capacity"),
|
|
106
|
+
("test_cumulated_discharge_capacity", "cumulated_discharge_capacity"),
|
|
107
|
+
("test_cumulated_coulombic_difference", "cumulated_coulombic_difference"),
|
|
108
|
+
("test_cumulated_charge_capacity_loss", "cumulated_charge_capacity_loss"),
|
|
109
|
+
("test_cumulated_discharge_capacity_loss", "cumulated_discharge_capacity_loss"),
|
|
110
|
+
("potential_end_charge", "end_voltage_charge"),
|
|
111
|
+
("potential_end_discharge", "end_voltage_discharge"),
|
|
112
|
+
("temperature_cell_mean", "temperature_mean"),
|
|
113
|
+
("temperature_cell_last", "temperature_last"),
|
|
114
|
+
# Identity pass-throughs (native name already equals the legacy name).
|
|
115
|
+
("ir_charge", "ir_charge"),
|
|
116
|
+
("ir_discharge", "ir_discharge"),
|
|
117
|
+
("charge_c_rate", "charge_c_rate"),
|
|
118
|
+
("discharge_c_rate", "discharge_c_rate"),
|
|
119
|
+
("normalized_cycle_index", "normalized_cycle_index"),
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
# -----------------------------------------------------------------------------
|
|
123
|
+
# Documented exceptions (columns with no counterpart on the other side).
|
|
124
|
+
# These make "lossless/total" well-defined; the totality test asserts that the
|
|
125
|
+
# declared columns of each class equal (mapped columns) ∪ (its exception set).
|
|
126
|
+
# -----------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
# Legacy HeadersNormal column values with no native RawCols counterpart.
|
|
129
|
+
# (``test_id`` exists on both sides with the same name but is intentionally not
|
|
130
|
+
# translated by the raw bridge, so it is listed as an exception on both sides.)
|
|
131
|
+
LEGACY_ONLY_RAW = frozenset({
|
|
132
|
+
"aci_phase_angle", "ref_aci_phase_angle", "ac_impedance", "ref_ac_impedance",
|
|
133
|
+
"charge_energy", "date_time", "discharge_energy", "power", "is_fc_data",
|
|
134
|
+
"sub_step_index", "sub_step_time", "test_id", "reference_voltage", "dv_dt",
|
|
135
|
+
"frequency", "amplitude", "channel_id", "data_flag", "test_name",
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
# Native RawCols column values with no legacy HeadersNormal counterpart.
|
|
139
|
+
# (``ref_potential`` is deliberately not bridged to legacy ``reference_voltage``:
|
|
140
|
+
# legacy HeadersStepTable has no reference-voltage aggregates, so bridging the
|
|
141
|
+
# raw column would grow the legacy step frame and break byte parity. The signal
|
|
142
|
+
# is native-path only; see issue #43.)
|
|
143
|
+
NATIVE_ONLY_RAW = frozenset({
|
|
144
|
+
"source_datapoint_num", "mask", "epoch_time_utc", "source_type",
|
|
145
|
+
"source_uuid", "test_id", "source_step_num", "step_type", "step_type_detail",
|
|
146
|
+
"step_mode", "cycle_type", "cumulative_charge_energy",
|
|
147
|
+
"cumulative_discharge_energy", "step_charge_power", "step_discharge_power",
|
|
148
|
+
"ref_potential",
|
|
149
|
+
"aux_temperature_cell", "aux_temperature_chamber", "aux_pressure_cell",
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
# Legacy HeadersStepTable column values with no native StepCols counterpart.
|
|
153
|
+
# (``ustep`` is emitted by the engine as a literal "ustep" column only when
|
|
154
|
+
# ``usteps=True``; it has no declared StepCols field.)
|
|
155
|
+
LEGACY_ONLY_STEP = frozenset({"test", "ustep", "info", "ir_pct_change"})
|
|
156
|
+
|
|
157
|
+
# Native StepCols *signals* with no legacy counterpart (power / energy
|
|
158
|
+
# statistics, the boolean ``mask``, and the per-test ``test_id`` key). Compared at
|
|
159
|
+
# base-signal granularity, i.e. after stripping the ``STAT_SUFFIXES`` from
|
|
160
|
+
# statistic columns. ``test_id`` is intentionally not step-bridged (it is dropped
|
|
161
|
+
# from the legacy step table), matching the raw-bridge treatment of ``test_id``.
|
|
162
|
+
NATIVE_ONLY_STEP = frozenset(
|
|
163
|
+
{"power", "charge_energy", "discharge_energy", "mask", "test_id",
|
|
164
|
+
"ref_potential"}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Legacy HeadersSummary column values with no native CycleCols counterpart
|
|
168
|
+
# (legacy-only cruft: cumulated CE, shifted / RIC capacities, OCV mins/maxes,
|
|
169
|
+
# normalized capacities, temperatures, levels, passthrough identity columns).
|
|
170
|
+
LEGACY_ONLY_CYCLE = frozenset({
|
|
171
|
+
"date_time", "test_name", "data_flag", "channel_id",
|
|
172
|
+
"cumulated_coulombic_efficiency", "normalized_charge_capacity",
|
|
173
|
+
"normalized_discharge_capacity", "shifted_charge_capacity",
|
|
174
|
+
"shifted_discharge_capacity", "ocv_first_min", "ocv_second_min",
|
|
175
|
+
"ocv_first_max", "ocv_second_max", "cumulated_ric_disconnect",
|
|
176
|
+
"cumulated_ric_sei", "cumulated_ric", "low_level", "high_level",
|
|
177
|
+
"aux_",
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
# Native CycleCols column values with no legacy HeadersSummary counterpart.
|
|
181
|
+
# (``test_id`` is the per-test key; intentionally not summary-bridged, so it is
|
|
182
|
+
# dropped from the legacy summary table like the raw/step ``test_id``.)
|
|
183
|
+
NATIVE_ONLY_CYCLE = frozenset({
|
|
184
|
+
"test_id",
|
|
185
|
+
"mask", "datapoint_num_first", "first_epoch_time_utc", "last_epoch_time_utc",
|
|
186
|
+
"first_test_time", "cycle_duration", "charge_duration", "discharge_duration",
|
|
187
|
+
"rest_duration", "test_net_capacity", "charge_energy", "discharge_energy",
|
|
188
|
+
"cycle_net_energy", "energy_efficiency", "test_cumulated_charge_energy",
|
|
189
|
+
"test_cumulated_discharge_energy", "test_net_energy",
|
|
190
|
+
"current_charge_mean", "current_charge_mean_tw", "current_charge_mean_cw",
|
|
191
|
+
"current_charge_max", "current_charge_min", "current_discharge_mean",
|
|
192
|
+
"current_discharge_mean_tw", "current_discharge_mean_cw",
|
|
193
|
+
"current_discharge_max", "current_discharge_min",
|
|
194
|
+
"potential_charge_mean", "potential_charge_mean_tw", "potential_charge_mean_cw",
|
|
195
|
+
"potential_charge_max", "potential_charge_min", "potential_discharge_mean",
|
|
196
|
+
"potential_discharge_mean_tw", "potential_discharge_mean_cw",
|
|
197
|
+
"potential_discharge_max", "potential_discharge_min",
|
|
198
|
+
"potential_start_charge", "potential_start_discharge", "voltage_efficiency",
|
|
199
|
+
"power_charge_mean", "power_charge_mean_tw", "power_charge_mean_cw",
|
|
200
|
+
"power_charge_max", "power_charge_min", "power_discharge_mean",
|
|
201
|
+
"power_discharge_mean_tw", "power_discharge_mean_cw", "power_discharge_max",
|
|
202
|
+
"power_discharge_min", "ir_start_charge", "ir_end_charge", "ir_start_discharge",
|
|
203
|
+
"ir_end_discharge", "relaxation_potential_charge",
|
|
204
|
+
"relaxation_potential_discharge", "open_circuit_potential_charge",
|
|
205
|
+
"open_circuit_potential_discharge", "cv_share", "cv_charge_capacity",
|
|
206
|
+
"cv_charge_energy", "cv_charge_time", "cc_charge_capacity", "cc_charge_energy",
|
|
207
|
+
"cc_charge_time", "temperature_cell_max", "temperature_cell_min",
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# -----------------------------------------------------------------------------
|
|
212
|
+
# Derivation helpers (the bridge builds its rename dicts from these).
|
|
213
|
+
# -----------------------------------------------------------------------------
|
|
214
|
+
def legacy_to_native_raw(columns=None) -> dict:
|
|
215
|
+
"""Return the legacy -> native rename dict for the raw frame.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
columns: Optional iterable of column names actually present. When given,
|
|
219
|
+
the result is filtered to keys in ``columns`` (so the dict is safe to
|
|
220
|
+
pass straight to ``DataFrame.rename``).
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
dict: Mapping ``legacy_name -> native_name``.
|
|
224
|
+
"""
|
|
225
|
+
mapping = {legacy: native for native, legacy in RAW_PAIRS}
|
|
226
|
+
if columns is not None:
|
|
227
|
+
cols = set(columns)
|
|
228
|
+
mapping = {k: v for k, v in mapping.items() if k in cols}
|
|
229
|
+
return mapping
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def native_to_legacy_step() -> dict:
|
|
233
|
+
"""Return the native -> legacy rename dict for the step table.
|
|
234
|
+
|
|
235
|
+
Expands :data:`STEP_BASE_PAIRS` with every :data:`STAT_SUFFIXES` variant and
|
|
236
|
+
appends the scalar :data:`STEP_SCALAR_PAIRS`.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
dict: Mapping ``native_name -> legacy_name``.
|
|
240
|
+
"""
|
|
241
|
+
rename = {}
|
|
242
|
+
for native_base, legacy_base in STEP_BASE_PAIRS:
|
|
243
|
+
for native_stat, legacy_stat in STAT_SUFFIXES.items():
|
|
244
|
+
rename[f"{native_base}_{native_stat}"] = f"{legacy_base}_{legacy_stat}"
|
|
245
|
+
for native, legacy in STEP_SCALAR_PAIRS:
|
|
246
|
+
rename[native] = legacy
|
|
247
|
+
return rename
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def legacy_to_native_step() -> dict:
|
|
251
|
+
"""Return the inverse of :func:`native_to_legacy_step` (legacy -> native)."""
|
|
252
|
+
return {v: k for k, v in native_to_legacy_step().items()}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def native_to_legacy_summary() -> dict:
|
|
256
|
+
"""Return the native -> legacy rename dict for the per-cycle summary.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
dict: Mapping ``native_name -> legacy_name`` (identity pass-throughs
|
|
260
|
+
included; harmless no-op renames for the bridge).
|
|
261
|
+
"""
|
|
262
|
+
return {native: legacy for native, legacy in CYCLE_PAIRS}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def legacy_to_native_summary() -> dict:
|
|
266
|
+
"""Return the inverse of :func:`native_to_legacy_summary` (legacy -> native)."""
|
|
267
|
+
return {v: k for k, v in native_to_legacy_summary().items()}
|