diff --git a/HISTORY.md b/HISTORY.md index 34836b17..d2ad18ce 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,12 @@ ## [Unreleased] +* cellpycore 0.2.2 sync: the legacy bridge now carries `test_id` on steps and + summary for all objects (core #136), so the #507 re-stamp workaround in + `make_step_table` is removed and the campaign merger remaps the right-hand + summary's `test_id` onto its new ids (previously the merged, pre-recompute + summary showed `test_id=0` everywhere). + * Cellpy-file format v9 (#510, V2-13/14): default `save()` writes zip-of-parquet + `meta.json` (``.cellpy``); `load()` still reads v4–v8 HDF5 and sniffs v9. Full `TestMetaCollection` (+ units/limits) persists on v9; diff --git a/cellpy/readers/cellreader.py b/cellpy/readers/cellreader.py index 76a5e4e8..891947d0 100644 --- a/cellpy/readers/cellreader.py +++ b/cellpy/readers/cellreader.py @@ -2290,23 +2290,6 @@ def make_step_table( if from_data_point is not None: return result - # Campaign objects only (issue #507): re-stamp test_id onto the step - # table. The core bridge groups steps by (test_id, cycle, step) using - # the raw test_id column but strips the column from the returned - # legacy frame; putting it back makes the subsequent summary use - # per-test windowing (use_tid) instead of cycle-only grouping. Gated - # on _extra_tests so single-test and continuation objects (including - # multi-tester-id Arbin raw) are untouched. - if self.data._extra_tests and not self.data.steps.empty: - from cellpy.readers import merger - - test_id_hdr = self.headers_normal.test_id_txt - if test_id_hdr in self.data.raw.columns: - point_first = f"{self.headers_step_table.point}_first" - self.data.steps[test_id_hdr] = merger._steps_test_id_from_raw( - self.data, self.data.steps, point_first - ) - return self def _select_usteps(self, cycle: int, steps: Union[list, np.ndarray]): diff --git a/cellpy/readers/merger.py b/cellpy/readers/merger.py index 69d935ae..d01f8094 100644 --- a/cellpy/readers/merger.py +++ b/cellpy/readers/merger.py @@ -176,6 +176,15 @@ def campaign_fold(left, right, *, merge_steps=True, merge_summary=True) -> None: right_summary[hs.cycle_index] = right_summary[hs.cycle_index] + cycle_offset if hs.data_point in right_summary.columns: right_summary[hs.data_point] = right_summary[hs.data_point] + dp_offset + # test_id: bridge summaries carry it since cellpycore 0.2.2 (#136); + # remap the right side onto its new ids (mirrors the raw handling). + if test_id_hdr in right_summary.columns: + if right._extra_tests: + right_summary[test_id_hdr] = right_summary[test_id_hdr].map( + lambda v: id_map[int(v)] + ) + else: + right_summary[test_id_hdr] = id_map[right.active_test_id] left.summary = externals.pandas.concat( [left.summary, right_summary], ignore_index=True ) diff --git a/tests/test_cell_readers.py b/tests/test_cell_readers.py index e3731404..1508d3b2 100644 --- a/tests/test_cell_readers.py +++ b/tests/test_cell_readers.py @@ -894,9 +894,9 @@ def test_save_cellpyfile_auto_extension(cellpy_data_instance, parameters): cellpy_data_instance.make_step_table() tmp_file = next(tempfile._get_candidate_names()) cellpy_data_instance.save(tmp_file) - assert os.path.isfile(tmp_file + ".h5") - os.remove(tmp_file + ".h5") - assert not os.path.isfile(tmp_file + ".h5") + assert os.path.isfile(tmp_file + ".cellpy") + os.remove(tmp_file + ".cellpy") + assert not os.path.isfile(tmp_file + ".cellpy") def test_save_cellpyfile_auto_extension_pathlib(cellpy_data_instance, parameters): @@ -905,7 +905,7 @@ def test_save_cellpyfile_auto_extension_pathlib(cellpy_data_instance, parameters cellpy_data_instance.make_step_table() tmp_file = pathlib.Path(next(tempfile._get_candidate_names())) cellpy_data_instance.save(tmp_file) - tmp_file = tmp_file.with_suffix(".h5") + tmp_file = tmp_file.with_suffix(".cellpy") assert tmp_file.is_file() os.remove(tmp_file) assert not tmp_file.is_file() diff --git a/tests/test_merge_campaign.py b/tests/test_merge_campaign.py index 35dba52e..3a19622f 100644 --- a/tests/test_merge_campaign.py +++ b/tests/test_merge_campaign.py @@ -228,12 +228,33 @@ def test_campaign_recompute_stamps_steps_and_windows_summary(campaign_cell): ) -def test_single_test_steps_have_no_test_id_column(parameters): - """Safety pin: the re-stamp gate stays off for non-campaign objects.""" +def test_campaign_merge_remaps_summary_test_id(parameters): + """The merged (pre-recompute) summary keeps per-test ids: the bridge + summaries carry test_id since cellpycore 0.2.2 (#136), so the merger must + remap the right side instead of concatenating two test_id=0 blocks.""" + left = cellreader.CellpyCell() + left.from_raw(parameters.res_file_path) + left.make_step_table() + left.make_summary(find_ir=False, find_end_voltage=False) + right = cellreader.CellpyCell() + right.from_raw(parameters.res_file_path2) + right.make_step_table() + right.make_summary(find_ir=False, find_end_voltage=False) + left.merge(right) + + summary = left.data.summary + assert HS.test_id in summary.columns + assert set(summary[HS.test_id].astype(int).unique()) == {0, 1} + + +def test_single_test_steps_carry_test_id_zero(parameters): + """Since cellpycore 0.2.2 (#136) the bridge carries test_id through the + legacy step table; single-test objects get the injected id 0.""" c = cellreader.CellpyCell() c.from_raw(parameters.res_file_path) c.make_step_table() - assert HN.test_id_txt not in c.data.steps.columns + assert HN.test_id_txt in c.data.steps.columns + assert set(c.data.steps[HN.test_id_txt].unique()) == {0} def test_campaign_save_warns_and_reloads_single(campaign_cell, tmp_path, caplog):