From 4c5ff38899c9f8ed67f5cea5916f6a14ae040482 Mon Sep 17 00:00:00 2001 From: jepegit Date: Wed, 23 Sep 2026 09:06:36 +0200 Subject: [PATCH] Forward optional nominal_capacity from load_cell to cellpy.get. Scenario 2 needs the user's 320 mAh/g on the same path as mass_mg; omitting it still leaves cellpy's default. --- README.md | 2 +- src/cellpy_mcp/cells.py | 9 +++++ tests/test_cell_tools.py | 76 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c233c28..770edae 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Cells and figures: |---|---| | `list_instruments` | loaders, and whether each can actually run on this machine | | `find_cells` | local files by project + number range; empty cellpy → `offer_raw` | -| `load_cell` | a handle, cycle count, mass, summary column names | +| `load_cell` | a handle, cycle count, mass, optional nominal_capacity, summary column names | | `list_cells` | what is loaded | | `describe_plot_families` | the 20 summary families, marked available or missing-columns | | `collect` | a handle, row count, columns, `is_grouped`, direction counts | diff --git a/src/cellpy_mcp/cells.py b/src/cellpy_mcp/cells.py index 8c60d5e..4d482a7 100644 --- a/src/cellpy_mcp/cells.py +++ b/src/cellpy_mcp/cells.py @@ -124,12 +124,17 @@ def load_cell( path: str, instrument: str | None = None, mass_mg: float | None = None, + nominal_capacity: float | str | None = None, ) -> dict: """Load one cell file and return a handle plus what the data can support. `mass_mg` is the active-material mass. Without it every `*_gravimetric` column is computed against a default of 1.0 mg — the numbers still appear, they are simply wrong, so supply it when you know it. + + `nominal_capacity` is passed to `cellpy.get` as-is: a number uses + cellpy units (default mAh/g); a string may carry a unit. There is no + lab default — omit it to keep whatever cellpy already uses. """ import cellpy @@ -139,6 +144,8 @@ def load_cell( kwargs["instrument"] = instrument if mass_mg is not None: kwargs["mass"] = mass_mg + if nominal_capacity is not None: + kwargs["nominal_capacity"] = nominal_capacity cell = cellpy.get(**kwargs) handle = state.handle("cell") @@ -153,6 +160,8 @@ def load_cell( "last_cycle": cycles[-1] if cycles else None, "mass_mg": cell.mass, "mass_was_supplied": mass_mg is not None, + "nominal_capacity": cell.nominal_capacity, + "nominal_capacity_was_supplied": nominal_capacity is not None, # Names only. The frame itself stays here. "summary_columns": sorted(cell.data.summary.columns), } diff --git a/tests/test_cell_tools.py b/tests/test_cell_tools.py index 9389a25..19aab9e 100644 --- a/tests/test_cell_tools.py +++ b/tests/test_cell_tools.py @@ -87,6 +87,82 @@ async def steps(call): assert "cellpy" in drive(steps)["refused"] +class _FakeLoadedCell: + cell_name = "demo" + mass = 2.1 + nominal_capacity = 320 + + class data: + class summary: + columns = [] + + def get_cycle_numbers(self): + return [1] + + +def test_load_cell_forwards_nominal_capacity(drive, root, monkeypatch): + (root / "demo.cellpy").write_text("x") + seen = {} + + def fake_get(**kwargs): + seen.update(kwargs) + return _FakeLoadedCell() + + monkeypatch.setattr("cellpy.get", fake_get) + + async def steps(call): + return await call( + "load_cell", path="demo.cellpy", mass_mg=2.1, nominal_capacity=320 + ) + + out = drive(steps) + assert seen["mass"] == 2.1 + assert seen["nominal_capacity"] == 320 + assert out["nominal_capacity"] == 320 + assert out["nominal_capacity_was_supplied"] is True + + +def test_load_cell_omits_nominal_capacity_when_not_given(drive, root, monkeypatch): + (root / "demo.cellpy").write_text("x") + seen = {} + + def fake_get(**kwargs): + seen.update(kwargs) + return _FakeLoadedCell() + + monkeypatch.setattr("cellpy.get", fake_get) + + async def steps(call): + return await call("load_cell", path="demo.cellpy", mass_mg=2.1) + + out = drive(steps) + assert "nominal_capacity" not in seen + assert out["nominal_capacity_was_supplied"] is False + + +def test_load_cell_forwards_nominal_capacity_string_with_unit(drive, root, monkeypatch): + (root / "demo.cellpy").write_text("x") + seen = {} + + def fake_get(**kwargs): + seen.update(kwargs) + return _FakeLoadedCell() + + monkeypatch.setattr("cellpy.get", fake_get) + + async def steps(call): + return await call( + "load_cell", + path="demo.cellpy", + mass_mg=2.1, + nominal_capacity="320 mAh/g", + ) + + out = drive(steps) + assert seen["nominal_capacity"] == "320 mAh/g" + assert out["nominal_capacity_was_supplied"] is True + + def test_the_whole_arc(drive, demo_cell, root): """Load -> collect -> render -> export, as a client actually does it."""