rosetta-eclab 0.1.2__tar.gz → 0.1.3__tar.gz
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.
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/PKG-INFO +6 -4
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/README.md +4 -2
- rosetta_eclab-0.1.3/namer/galvani_ref.py +67 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/library.py +2 -2
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/mprfile.py +20 -13
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/pyproject.toml +6 -3
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/PKG-INFO +6 -4
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/SOURCES.txt +3 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/corpus.py +2 -2
- rosetta_eclab-0.1.3/spike2/galvani_0.5.0_colmap.json +129 -0
- rosetta_eclab-0.1.3/spike2/galvani_snapshot.py +48 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/tests/test_cli.py +37 -1
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/LICENSE +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/__init__.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/cli.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/data/stats.json +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/data/stats_features.npy +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/debug.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/engine.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/evaluate.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/fallback.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/golden.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/namer/misses.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/dependency_links.txt +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/entry_points.txt +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/requires.txt +1 -1
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/rosetta_eclab.egg-info/top_level.txt +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/setup.cfg +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/__init__.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/agent.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/api_check.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/baselines.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/dsl.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/elim.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/features.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/lock.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/run.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/score.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/tasks.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/spike2/vocab.py +0 -0
- {rosetta_eclab-0.1.2 → rosetta_eclab-0.1.3}/tests/test_relations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rosetta-eclab
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Name the columns of BioLogic EC-Lab .mpr files that your parser does not know - no language model, evidence for every answer
|
|
5
5
|
Author: Nataniel Ataseven
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -18,9 +18,9 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=2.0
|
|
20
20
|
Requires-Dist: yadg==7.0.1
|
|
21
|
-
Requires-Dist: galvani==0.5.0
|
|
22
21
|
Provides-Extra: test
|
|
23
22
|
Requires-Dist: pytest; extra == "test"
|
|
23
|
+
Requires-Dist: galvani==0.5.0; extra == "test"
|
|
24
24
|
Dynamic: license-file
|
|
25
25
|
|
|
26
26
|
# Rosetta
|
|
@@ -52,6 +52,8 @@ Python 3.10 or later:
|
|
|
52
52
|
pip install rosetta-eclab
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
Rosetta does not install galvani. If galvani or datalab-org-galvani is installed, "unknown to galvani" means unknown to the galvani you actually have. Without either, Rosetta uses a frozen copy of galvani 0.5.0's column map and says so. The names Rosetta gives never depend on which galvani is installed.
|
|
56
|
+
|
|
55
57
|
To run the tests or reproduce the benchmark, clone the repository instead:
|
|
56
58
|
|
|
57
59
|
```sh
|
|
@@ -96,7 +98,7 @@ peis.issue_225.with_ece.mpr
|
|
|
96
98
|
every column ID is in galvani's column map; nothing to name
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
101
|
+
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). Rosetta prints the note only when the installed galvani reads the low byte. IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
100
102
|
|
|
101
103
|
## How it works
|
|
102
104
|
|
|
@@ -155,4 +157,4 @@ pytest tests
|
|
|
155
157
|
|
|
156
158
|
## Data and licence
|
|
157
159
|
|
|
158
|
-
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) and [galvani](https://github.com/echemdata/galvani)
|
|
160
|
+
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) (GPL), and `spike2/galvani_0.5.0_colmap.json` is a copy of the column map of [galvani](https://github.com/echemdata/galvani) 0.5.0 (GPL-3.0-or-later). Measurement files are downloaded at run time and never committed. Sources, licences and attributions are listed in [`DATA_SOURCES.md`](https://github.com/Nataniel0212/rosetta-eclab/blob/main/DATA_SOURCES.md). Rosetta is not affiliated with BioLogic.
|
|
@@ -27,6 +27,8 @@ Python 3.10 or later:
|
|
|
27
27
|
pip install rosetta-eclab
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Rosetta does not install galvani. If galvani or datalab-org-galvani is installed, "unknown to galvani" means unknown to the galvani you actually have. Without either, Rosetta uses a frozen copy of galvani 0.5.0's column map and says so. The names Rosetta gives never depend on which galvani is installed.
|
|
31
|
+
|
|
30
32
|
To run the tests or reproduce the benchmark, clone the repository instead:
|
|
31
33
|
|
|
32
34
|
```sh
|
|
@@ -71,7 +73,7 @@ peis.issue_225.with_ece.mpr
|
|
|
71
73
|
every column ID is in galvani's column map; nothing to name
|
|
72
74
|
```
|
|
73
75
|
|
|
74
|
-
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
76
|
+
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). Rosetta prints the note only when the installed galvani reads the low byte. IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
75
77
|
|
|
76
78
|
## How it works
|
|
77
79
|
|
|
@@ -130,4 +132,4 @@ pytest tests
|
|
|
130
132
|
|
|
131
133
|
## Data and licence
|
|
132
134
|
|
|
133
|
-
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) and [galvani](https://github.com/echemdata/galvani)
|
|
135
|
+
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) (GPL), and `spike2/galvani_0.5.0_colmap.json` is a copy of the column map of [galvani](https://github.com/echemdata/galvani) 0.5.0 (GPL-3.0-or-later). Measurement files are downloaded at run time and never committed. Sources, licences and attributions are listed in [`DATA_SOURCES.md`](https://github.com/Nataniel0212/rosetta-eclab/blob/main/DATA_SOURCES.md). Rosetta is not affiliated with BioLogic.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""galvani as a reference parser, without depending on one particular galvani.
|
|
2
|
+
|
|
3
|
+
Two distributions install the module `galvani`: galvani (echemdata, 0.5.0 on PyPI)
|
|
4
|
+
and datalab-org-galvani (0.6.0 reads EC-Lab 11.50+ column IDs in full). Rosetta
|
|
5
|
+
does not install either, so it never overwrites the one a user already has.
|
|
6
|
+
|
|
7
|
+
reference() returns the installed galvani's own column map, and whether that
|
|
8
|
+
galvani reads only the low byte of each column ID in the 11.50+ layout. Without
|
|
9
|
+
galvani installed, the frozen 0.5.0 map (spike2/galvani_snapshot.py) stands in.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import inspect
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
from spike2.galvani_snapshot import snapshot_map
|
|
18
|
+
|
|
19
|
+
LOW_BYTE_IDIOM = "column_types[1::2]" # galvani 0.5.0 keeps every second byte of the ID array
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class Reference:
|
|
24
|
+
colmap: dict[int, tuple[str, str]]
|
|
25
|
+
label: str # e.g. "galvani 0.5.0", "datalab-org-galvani 0.6.0"
|
|
26
|
+
low_byte: bool | None # True: reads only the low byte of 11.50+ column IDs; None: cannot tell
|
|
27
|
+
installed: bool
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _label() -> str:
|
|
31
|
+
from importlib.metadata import PackageNotFoundError, packages_distributions, version
|
|
32
|
+
|
|
33
|
+
parts = []
|
|
34
|
+
for d in sorted(set(packages_distributions().get("galvani", []))):
|
|
35
|
+
try:
|
|
36
|
+
parts.append(f"{d} {version(d)}")
|
|
37
|
+
except PackageNotFoundError:
|
|
38
|
+
parts.append(d)
|
|
39
|
+
if not parts:
|
|
40
|
+
return "galvani"
|
|
41
|
+
if len(parts) == 1:
|
|
42
|
+
return parts[0]
|
|
43
|
+
return f"galvani (installed by both {' and '.join(parts)}; its files are from whichever was installed last)"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _reads_low_byte(biologic) -> bool | None:
|
|
47
|
+
try:
|
|
48
|
+
src = inspect.getsource(biologic.MPRfile.__init__)
|
|
49
|
+
except (OSError, TypeError, AttributeError):
|
|
50
|
+
return None
|
|
51
|
+
return LOW_BYTE_IDIOM in src
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def installed() -> Reference | None:
|
|
55
|
+
try:
|
|
56
|
+
from galvani import BioLogic
|
|
57
|
+
except ImportError:
|
|
58
|
+
return None
|
|
59
|
+
colmap = {int(k): (v[0], v[1]) for k, v in BioLogic.VMPdata_colID_dtype_map.items()}
|
|
60
|
+
return Reference(colmap, _label(), _reads_low_byte(BioLogic), installed=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def reference() -> Reference:
|
|
64
|
+
ref = installed()
|
|
65
|
+
if ref is not None:
|
|
66
|
+
return ref
|
|
67
|
+
return Reference(snapshot_map(), "galvani 0.5.0", low_byte=True, installed=False)
|
|
@@ -451,15 +451,15 @@ def is_code(ctx, lo=0, hi=200, max_distinct=8) -> float:
|
|
|
451
451
|
# --- expected storage types (public parser maps: yadg 7.0.1 and galvani 0.5.0) ---------
|
|
452
452
|
|
|
453
453
|
def expected_dtypes() -> dict[str, set[str]]:
|
|
454
|
-
from galvani import BioLogic
|
|
455
454
|
from yadg.extractors.eclab.mpr_columns import data_columns
|
|
456
455
|
|
|
457
456
|
from spike2 import vocab
|
|
457
|
+
from spike2.galvani_snapshot import snapshot_map # frozen 0.5.0 map, whichever galvani is installed
|
|
458
458
|
|
|
459
459
|
out: dict[str, set[str]] = {}
|
|
460
460
|
for dt, name, _unit in data_columns.values():
|
|
461
461
|
out.setdefault(vocab.canonical(name), set()).add(dt)
|
|
462
|
-
for full, dt in
|
|
462
|
+
for full, dt in snapshot_map().values():
|
|
463
463
|
name = full.split("/")[0] if not full.startswith("Q charge/discharge") else "Q charge/discharge"
|
|
464
464
|
out.setdefault(vocab.canonical(name), set()).add(dt)
|
|
465
465
|
return out
|
|
@@ -16,6 +16,8 @@ from pathlib import Path
|
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
|
|
19
|
+
from . import galvani_ref
|
|
20
|
+
|
|
19
21
|
FLAG_IDS = (1, 2, 3, 21, 31, 65)
|
|
20
22
|
CANDIDATES = {1: ["|u1"], 2: ["<u2", "<i2"], 4: ["<f4", "<u4", "<i4"], 8: ["<f8", "<u8", "<i8"]}
|
|
21
23
|
|
|
@@ -79,25 +81,29 @@ def _plausibility(v: np.ndarray, dtype: str) -> float:
|
|
|
79
81
|
return 0.9 if span < 2 ** (8 * width - 4) else 0.2
|
|
80
82
|
|
|
81
83
|
|
|
82
|
-
def
|
|
83
|
-
"""
|
|
84
|
+
def _galvani_notes(full_ids: list[int], ref: galvani_ref.Reference, layout_11_50: bool) -> list[str]:
|
|
85
|
+
"""What the reference galvani makes of this file, beyond plain unknown IDs.
|
|
84
86
|
|
|
87
|
+
galvani 0.5.0 reads only the low byte of each column ID in the 11.50+ layout.
|
|
85
88
|
IDs above 255 then become other IDs (471 -> 215), which galvani either does not
|
|
86
89
|
know (it stops) or knows as a different column. Its map repeats a few entries at
|
|
87
90
|
the low-byte ID (211 = 467), so only IDs whose low-byte entry differs are listed.
|
|
88
91
|
"""
|
|
89
|
-
|
|
90
|
-
if not
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
notes = []
|
|
93
|
+
if not ref.installed:
|
|
94
|
+
notes.append("galvani is not installed, so 'known to galvani' means the column map of galvani 0.5.0 "
|
|
95
|
+
"(bundled with Rosetta)")
|
|
96
|
+
if layout_11_50 and ref.low_byte:
|
|
97
|
+
gmap = ref.colmap
|
|
98
|
+
misread = [x for x in full_ids if x > 255 and gmap.get(x & 0xFF) != gmap.get(x)]
|
|
99
|
+
if misread:
|
|
100
|
+
pairs = ", ".join(f"{x} as {x & 0xFF}" for x in misread)
|
|
101
|
+
notes.append(f"{ref.label} reads only the low byte of each column ID in this file layout, "
|
|
102
|
+
f"so it misreads {pairs}")
|
|
103
|
+
return notes
|
|
97
104
|
|
|
98
105
|
|
|
99
106
|
def read(path: str | Path, reference: str = "galvani") -> MprData:
|
|
100
|
-
from galvani import BioLogic
|
|
101
107
|
from yadg.extractors.eclab.mpr import parse_columns, process_settings
|
|
102
108
|
|
|
103
109
|
logging.getLogger("yadg").setLevel(logging.CRITICAL)
|
|
@@ -133,8 +139,9 @@ def read(path: str | Path, reference: str = "galvani") -> MprData:
|
|
|
133
139
|
rec = region // n_points
|
|
134
140
|
|
|
135
141
|
names, dtypes, units, _ = parse_columns(yids, technique) # yadg's map: names for known columns
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
gref = galvani_ref.reference() if reference == "galvani" else None
|
|
143
|
+
gmap = gref.colmap if gref else {}
|
|
144
|
+
notes = _galvani_notes(full, gref, version in (10, 11)) if gref else []
|
|
138
145
|
layout = [] # record layout in file order: (raw_id or None for the flags byte, ...)
|
|
139
146
|
for yid, fid in zip(yids, full):
|
|
140
147
|
if yid in FLAG_IDS: # all flag IDs share one byte, at the position of the first one
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rosetta-eclab"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Name the columns of BioLogic EC-Lab .mpr files that your parser does not know - no language model, evidence for every answer"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|
|
@@ -19,10 +19,12 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
20
|
"Topic :: Scientific/Engineering :: Chemistry",
|
|
21
21
|
]
|
|
22
|
+
# galvani is not a dependency: two distributions install the module (galvani and datalab-org-galvani),
|
|
23
|
+
# and requiring one would overwrite the other. Rosetta uses whichever is installed, or a frozen copy
|
|
24
|
+
# of galvani 0.5.0's column map (spike2/galvani_0.5.0_colmap.json).
|
|
22
25
|
dependencies = [
|
|
23
26
|
"numpy>=2.0",
|
|
24
27
|
"yadg==7.0.1",
|
|
25
|
-
"galvani==0.5.0",
|
|
26
28
|
]
|
|
27
29
|
|
|
28
30
|
[project.urls]
|
|
@@ -30,7 +32,7 @@ Repository = "https://github.com/Nataniel0212/rosetta-eclab"
|
|
|
30
32
|
Issues = "https://github.com/Nataniel0212/rosetta-eclab/issues"
|
|
31
33
|
|
|
32
34
|
[project.optional-dependencies]
|
|
33
|
-
test = ["pytest"]
|
|
35
|
+
test = ["pytest", "galvani==0.5.0"]
|
|
34
36
|
|
|
35
37
|
[project.scripts]
|
|
36
38
|
rosetta = "rosetta_eclab.cli:main"
|
|
@@ -45,3 +47,4 @@ rosetta_eclab = "namer"
|
|
|
45
47
|
|
|
46
48
|
[tool.setuptools.package-data]
|
|
47
49
|
rosetta_eclab = ["data/*.json", "data/*.npy"]
|
|
50
|
+
spike2 = ["*.json"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rosetta-eclab
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Name the columns of BioLogic EC-Lab .mpr files that your parser does not know - no language model, evidence for every answer
|
|
5
5
|
Author: Nataniel Ataseven
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -18,9 +18,9 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=2.0
|
|
20
20
|
Requires-Dist: yadg==7.0.1
|
|
21
|
-
Requires-Dist: galvani==0.5.0
|
|
22
21
|
Provides-Extra: test
|
|
23
22
|
Requires-Dist: pytest; extra == "test"
|
|
23
|
+
Requires-Dist: galvani==0.5.0; extra == "test"
|
|
24
24
|
Dynamic: license-file
|
|
25
25
|
|
|
26
26
|
# Rosetta
|
|
@@ -52,6 +52,8 @@ Python 3.10 or later:
|
|
|
52
52
|
pip install rosetta-eclab
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
Rosetta does not install galvani. If galvani or datalab-org-galvani is installed, "unknown to galvani" means unknown to the galvani you actually have. Without either, Rosetta uses a frozen copy of galvani 0.5.0's column map and says so. The names Rosetta gives never depend on which galvani is installed.
|
|
56
|
+
|
|
55
57
|
To run the tests or reproduce the benchmark, clone the repository instead:
|
|
56
58
|
|
|
57
59
|
```sh
|
|
@@ -96,7 +98,7 @@ peis.issue_225.with_ece.mpr
|
|
|
96
98
|
every column ID is in galvani's column map; nothing to name
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
101
|
+
The fix was released in datalab-org-galvani 0.6.0 ([galvani-mirror#11](https://github.com/datalab-org/galvani-mirror/issues/11)). Rosetta prints the note only when the installed galvani reads the low byte. IDs that are missing from the map altogether, such as 880 or 883 and 884, are what Rosetta names.
|
|
100
102
|
|
|
101
103
|
## How it works
|
|
102
104
|
|
|
@@ -155,4 +157,4 @@ pytest tests
|
|
|
155
157
|
|
|
156
158
|
## Data and licence
|
|
157
159
|
|
|
158
|
-
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) and [galvani](https://github.com/echemdata/galvani)
|
|
160
|
+
Rosetta is free software under the **GNU General Public License v3.0 or later** (see `LICENSE`). It uses [yadg](https://github.com/dgbowl/yadg) (GPL), and `spike2/galvani_0.5.0_colmap.json` is a copy of the column map of [galvani](https://github.com/echemdata/galvani) 0.5.0 (GPL-3.0-or-later). Measurement files are downloaded at run time and never committed. Sources, licences and attributions are listed in [`DATA_SOURCES.md`](https://github.com/Nataniel0212/rosetta-eclab/blob/main/DATA_SOURCES.md). Rosetta is not affiliated with BioLogic.
|
|
@@ -7,6 +7,7 @@ namer/debug.py
|
|
|
7
7
|
namer/engine.py
|
|
8
8
|
namer/evaluate.py
|
|
9
9
|
namer/fallback.py
|
|
10
|
+
namer/galvani_ref.py
|
|
10
11
|
namer/golden.py
|
|
11
12
|
namer/library.py
|
|
12
13
|
namer/misses.py
|
|
@@ -27,6 +28,8 @@ spike2/corpus.py
|
|
|
27
28
|
spike2/dsl.py
|
|
28
29
|
spike2/elim.py
|
|
29
30
|
spike2/features.py
|
|
31
|
+
spike2/galvani_0.5.0_colmap.json
|
|
32
|
+
spike2/galvani_snapshot.py
|
|
30
33
|
spike2/lock.py
|
|
31
34
|
spike2/run.py
|
|
32
35
|
spike2/score.py
|
|
@@ -96,9 +96,9 @@ def decode(path: Path) -> FileData:
|
|
|
96
96
|
raise NotImplementedError(f"data module version {data_version}")
|
|
97
97
|
names, dtypes, units, _flags = parse_columns(ids, technique)
|
|
98
98
|
arr = np.frombuffer(data, offset=offset, dtype=np.dtype(list(zip(names, dtypes))), count=n_points)
|
|
99
|
-
from
|
|
99
|
+
from .galvani_snapshot import snapshot_map
|
|
100
100
|
|
|
101
|
-
km =
|
|
101
|
+
km = snapshot_map() # galvani 0.5.0 (frozen copy): full IDs it knows
|
|
102
102
|
cols, pos = [], 0
|
|
103
103
|
# yadg's own flag test (on its reading of the IDs) decides which entries are flags
|
|
104
104
|
data_ids = [(i, f) for i, f in zip(ids, full_ids) if i not in (1, 2, 3, 21, 31, 65)]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "galvani 0.5.0 (GPL-3.0), galvani/BioLogic.py, VMPdata_colID_dtype_map: column ID -> [name/unit, numpy dtype]",
|
|
3
|
+
"columns": {
|
|
4
|
+
"4": ["time/s", "<f8"],
|
|
5
|
+
"5": ["control/V/mA", "<f4"],
|
|
6
|
+
"6": ["Ewe/V", "<f4"],
|
|
7
|
+
"7": ["dq/mA.h", "<f8"],
|
|
8
|
+
"8": ["I/mA", "<f4"],
|
|
9
|
+
"9": ["Ece/V", "<f4"],
|
|
10
|
+
"11": ["<I>/mA", "<f8"],
|
|
11
|
+
"13": ["(Q-Qo)/mA.h", "<f8"],
|
|
12
|
+
"16": ["Analog IN 1/V", "<f4"],
|
|
13
|
+
"17": ["Analog IN 2/V", "<f4"],
|
|
14
|
+
"19": ["control/V", "<f4"],
|
|
15
|
+
"20": ["control/mA", "<f4"],
|
|
16
|
+
"23": ["dQ/mA.h", "<f8"],
|
|
17
|
+
"24": ["cycle number", "<f8"],
|
|
18
|
+
"26": ["Rapp/Ohm", "<f4"],
|
|
19
|
+
"27": ["Ewe-Ece/V", "<f4"],
|
|
20
|
+
"32": ["freq/Hz", "<f4"],
|
|
21
|
+
"33": ["|Ewe|/V", "<f4"],
|
|
22
|
+
"34": ["|I|/A", "<f4"],
|
|
23
|
+
"35": ["Phase(Z)/deg", "<f4"],
|
|
24
|
+
"36": ["|Z|/Ohm", "<f4"],
|
|
25
|
+
"37": ["Re(Z)/Ohm", "<f4"],
|
|
26
|
+
"38": ["-Im(Z)/Ohm", "<f4"],
|
|
27
|
+
"39": ["I Range", "<u2"],
|
|
28
|
+
"69": ["R/Ohm", "<f4"],
|
|
29
|
+
"70": ["P/W", "<f4"],
|
|
30
|
+
"74": ["|Energy|/W.h", "<f8"],
|
|
31
|
+
"75": ["Analog OUT/V", "<f4"],
|
|
32
|
+
"76": ["<I>/mA", "<f4"],
|
|
33
|
+
"77": ["<Ewe>/V", "<f4"],
|
|
34
|
+
"78": ["Cs-2/µF-2", "<f4"],
|
|
35
|
+
"96": ["|Ece|/V", "<f4"],
|
|
36
|
+
"98": ["Phase(Zce)/deg", "<f4"],
|
|
37
|
+
"99": ["|Zce|/Ohm", "<f4"],
|
|
38
|
+
"100": ["Re(Zce)/Ohm", "<f4"],
|
|
39
|
+
"101": ["-Im(Zce)/Ohm", "<f4"],
|
|
40
|
+
"123": ["Energy charge/W.h", "<f8"],
|
|
41
|
+
"124": ["Energy discharge/W.h", "<f8"],
|
|
42
|
+
"125": ["Capacitance charge/µF", "<f8"],
|
|
43
|
+
"126": ["Capacitance discharge/µF", "<f8"],
|
|
44
|
+
"131": ["Ns", "<u2"],
|
|
45
|
+
"163": ["|Estack|/V", "<f4"],
|
|
46
|
+
"168": ["Rcmp/Ohm", "<f4"],
|
|
47
|
+
"169": ["Cs/µF", "<f4"],
|
|
48
|
+
"172": ["Cp/µF", "<f4"],
|
|
49
|
+
"173": ["Cp-2/µF-2", "<f4"],
|
|
50
|
+
"174": ["<Ewe>/V", "<f4"],
|
|
51
|
+
"178": ["(Q-Qo)/C", "<f4"],
|
|
52
|
+
"179": ["dQ/C", "<f4"],
|
|
53
|
+
"211": ["Q charge/discharge/mA.h", "<f8"],
|
|
54
|
+
"212": ["half cycle", "<u4"],
|
|
55
|
+
"213": ["z cycle", "<u4"],
|
|
56
|
+
"217": ["THD Ewe/%", "<f4"],
|
|
57
|
+
"218": ["THD I/%", "<f4"],
|
|
58
|
+
"220": ["NSD Ewe/%", "<f4"],
|
|
59
|
+
"221": ["NSD I/%", "<f4"],
|
|
60
|
+
"223": ["NSR Ewe/%", "<f4"],
|
|
61
|
+
"224": ["NSR I/%", "<f4"],
|
|
62
|
+
"230": ["|Ewe h2|/V", "<f4"],
|
|
63
|
+
"231": ["|Ewe h3|/V", "<f4"],
|
|
64
|
+
"232": ["|Ewe h4|/V", "<f4"],
|
|
65
|
+
"233": ["|Ewe h5|/V", "<f4"],
|
|
66
|
+
"234": ["|Ewe h6|/V", "<f4"],
|
|
67
|
+
"235": ["|Ewe h7|/V", "<f4"],
|
|
68
|
+
"236": ["|I h2|/A", "<f4"],
|
|
69
|
+
"237": ["|I h3|/A", "<f4"],
|
|
70
|
+
"238": ["|I h4|/A", "<f4"],
|
|
71
|
+
"239": ["|I h5|/A", "<f4"],
|
|
72
|
+
"240": ["|I h6|/A", "<f4"],
|
|
73
|
+
"241": ["|I h7|/A", "<f4"],
|
|
74
|
+
"242": ["|E2|/V", "<f4"],
|
|
75
|
+
"271": ["Phase(Z1) / deg", "<f4"],
|
|
76
|
+
"272": ["Phase(Z2) / deg", "<f4"],
|
|
77
|
+
"301": ["|Z1|/Ohm", "<f4"],
|
|
78
|
+
"302": ["|Z2|/Ohm", "<f4"],
|
|
79
|
+
"331": ["Re(Z1)/Ohm", "<f4"],
|
|
80
|
+
"332": ["Re(Z2)/Ohm", "<f4"],
|
|
81
|
+
"361": ["-Im(Z1)/Ohm", "<f4"],
|
|
82
|
+
"362": ["-Im(Z2)/Ohm", "<f4"],
|
|
83
|
+
"391": ["<E1>/V", "<f4"],
|
|
84
|
+
"392": ["<E2>/V", "<f4"],
|
|
85
|
+
"422": ["Phase(Zstack)/deg", "<f4"],
|
|
86
|
+
"423": ["|Zstack|/Ohm", "<f4"],
|
|
87
|
+
"424": ["Re(Zstack)/Ohm", "<f4"],
|
|
88
|
+
"425": ["-Im(Zstack)/Ohm", "<f4"],
|
|
89
|
+
"426": ["<Estack>/V", "<f4"],
|
|
90
|
+
"430": ["Phase(Zwe-ce)/deg", "<f4"],
|
|
91
|
+
"431": ["|Zwe-ce|/Ohm", "<f4"],
|
|
92
|
+
"432": ["Re(Zwe-ce)/Ohm", "<f4"],
|
|
93
|
+
"433": ["-Im(Zwe-ce)/Ohm", "<f4"],
|
|
94
|
+
"434": ["(Q-Qo)/C", "<f4"],
|
|
95
|
+
"435": ["dQ/C", "<f4"],
|
|
96
|
+
"438": ["step time/s", "<f8"],
|
|
97
|
+
"441": ["<Ecv>/V", "<f4"],
|
|
98
|
+
"462": ["Temperature/°C", "<f4"],
|
|
99
|
+
"467": ["Q charge/discharge/mA.h", "<f8"],
|
|
100
|
+
"468": ["half cycle", "<u4"],
|
|
101
|
+
"469": ["z cycle", "<u4"],
|
|
102
|
+
"471": ["<Ece>/V", "<f4"],
|
|
103
|
+
"473": ["THD Ewe/%", "<f4"],
|
|
104
|
+
"474": ["THD I/%", "<f4"],
|
|
105
|
+
"476": ["NSD Ewe/%", "<f4"],
|
|
106
|
+
"477": ["NSD I/%", "<f4"],
|
|
107
|
+
"479": ["NSR Ewe/%", "<f4"],
|
|
108
|
+
"480": ["NSR I/%", "<f4"],
|
|
109
|
+
"486": ["|Ewe h2|/V", "<f4"],
|
|
110
|
+
"487": ["|Ewe h3|/V", "<f4"],
|
|
111
|
+
"488": ["|Ewe h4|/V", "<f4"],
|
|
112
|
+
"489": ["|Ewe h5|/V", "<f4"],
|
|
113
|
+
"490": ["|Ewe h6|/V", "<f4"],
|
|
114
|
+
"491": ["|Ewe h7|/V", "<f4"],
|
|
115
|
+
"492": ["|I h2|/A", "<f4"],
|
|
116
|
+
"493": ["|I h3|/A", "<f4"],
|
|
117
|
+
"494": ["|I h4|/A", "<f4"],
|
|
118
|
+
"495": ["|I h5|/A", "<f4"],
|
|
119
|
+
"496": ["|I h6|/A", "<f4"],
|
|
120
|
+
"497": ["|I h7|/A", "<f4"],
|
|
121
|
+
"498": ["Q charge/mA.h", "<f8"],
|
|
122
|
+
"499": ["Q discharge/mA.h", "<f8"],
|
|
123
|
+
"500": ["step time/s", "<f8"],
|
|
124
|
+
"501": ["Efficiency/%", "<f8"],
|
|
125
|
+
"502": ["Capacity/mA.h", "<f8"],
|
|
126
|
+
"505": ["Rdc/Ohm", "<f4"],
|
|
127
|
+
"509": ["Acir/Dcir Control", "<u1"]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""galvani 0.5.0's column map, frozen in this repository.
|
|
2
|
+
|
|
3
|
+
The benchmarks and the namer's expected storage types use this copy, so their
|
|
4
|
+
answers do not depend on which galvani (if any) is installed. namer/galvani_ref.py
|
|
5
|
+
uses the installed galvani for "known to galvani" instead.
|
|
6
|
+
|
|
7
|
+
python -m spike2.galvani_snapshot --write # regenerate from an installed galvani 0.5.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from functools import lru_cache
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
SNAPSHOT = Path(__file__).resolve().parent / "galvani_0.5.0_colmap.json"
|
|
18
|
+
SOURCE = "galvani 0.5.0 (GPL-3.0), galvani/BioLogic.py, VMPdata_colID_dtype_map: column ID -> [name/unit, numpy dtype]"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@lru_cache(maxsize=1)
|
|
22
|
+
def snapshot_map() -> dict[int, tuple[str, str]]:
|
|
23
|
+
d = json.loads(SNAPSHOT.read_text(encoding="utf-8"))
|
|
24
|
+
return {int(k): (v[0], v[1]) for k, v in d["columns"].items()}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def write() -> int:
|
|
28
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
ok = version("galvani") == "0.5.0"
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
ok = False
|
|
34
|
+
if not ok:
|
|
35
|
+
print("needs galvani 0.5.0 installed (pip install galvani==0.5.0)", file=sys.stderr)
|
|
36
|
+
return 1
|
|
37
|
+
from galvani import BioLogic
|
|
38
|
+
|
|
39
|
+
rows = [f" {json.dumps(str(k))}: {json.dumps([v[0], v[1]], ensure_ascii=False)}"
|
|
40
|
+
for k, v in sorted(BioLogic.VMPdata_colID_dtype_map.items())]
|
|
41
|
+
text = '{\n"source": ' + json.dumps(SOURCE) + ',\n"columns": {\n' + ",\n".join(rows) + "\n}\n}\n"
|
|
42
|
+
SNAPSHOT.write_text(text, encoding="utf-8", newline="\n")
|
|
43
|
+
print(f"wrote {SNAPSHOT} ({len(rows)} columns)")
|
|
44
|
+
return 0
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
raise SystemExit(write() if "--write" in sys.argv else 0)
|
|
@@ -11,8 +11,9 @@ from pathlib import Path
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
import pytest
|
|
13
13
|
|
|
14
|
-
from namer import cli, mprfile
|
|
14
|
+
from namer import cli, galvani_ref, mprfile
|
|
15
15
|
from spike2 import corpus
|
|
16
|
+
from spike2.galvani_snapshot import snapshot_map
|
|
16
17
|
|
|
17
18
|
EXT = corpus.EXT
|
|
18
19
|
pytestmark = pytest.mark.skipif(not (EXT / "gcpl.mpr").exists(), reason="run scripts/fetch_testdata.py first")
|
|
@@ -60,6 +61,41 @@ def test_galvani_low_byte_misreads_are_reported():
|
|
|
60
61
|
assert not any("low byte" in n for n in mprfile.read(EXT / "peis.issue_225.with_ece.mpr", reference="yadg").notes)
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def _galvani_version() -> str | None:
|
|
65
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
return version("galvani")
|
|
69
|
+
except PackageNotFoundError:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_frozen_map_is_galvani_050():
|
|
74
|
+
if _galvani_version() != "0.5.0":
|
|
75
|
+
pytest.skip("needs galvani 0.5.0 (pip install '.[test]')")
|
|
76
|
+
from galvani import BioLogic
|
|
77
|
+
|
|
78
|
+
assert snapshot_map() == {int(k): (v[0], v[1]) for k, v in BioLogic.VMPdata_colID_dtype_map.items()}
|
|
79
|
+
ref = galvani_ref.installed()
|
|
80
|
+
assert ref.label == "galvani 0.5.0" and ref.low_byte is True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_full_id_galvani_gets_no_low_byte_note(monkeypatch):
|
|
84
|
+
"""A galvani that reads column IDs in full (datalab-org-galvani 0.6.0) misreads nothing."""
|
|
85
|
+
ref = galvani_ref.Reference(snapshot_map(), "datalab-org-galvani 0.6.0", low_byte=False, installed=True)
|
|
86
|
+
monkeypatch.setattr(galvani_ref, "reference", lambda: ref)
|
|
87
|
+
m = mprfile.read(EXT / "peis.issue_225.with_ece.mpr", reference="galvani")
|
|
88
|
+
assert m.unknown == [] and not any("low byte" in n for n in m.notes), m.notes
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_without_galvani_the_frozen_map_stands_in(monkeypatch):
|
|
92
|
+
monkeypatch.setattr(galvani_ref, "installed", lambda: None)
|
|
93
|
+
m = mprfile.read(EXT / "gcpl.issue_230.mpr", reference="galvani")
|
|
94
|
+
assert [c.raw_id for c in m.unknown] == [883, 884]
|
|
95
|
+
assert any("not installed" in n for n in m.notes), m.notes
|
|
96
|
+
assert any("galvani 0.5.0 reads only the low byte" in n and "438 as 182" in n for n in m.notes), m.notes
|
|
97
|
+
|
|
98
|
+
|
|
63
99
|
@pytest.mark.parametrize("old_id,expected", [(467, "Q charge or discharge"), (7, "dq"), (4, "time")])
|
|
64
100
|
def test_simulated_drift_is_named(tmp_path, old_id, expected):
|
|
65
101
|
f = tmp_path / "drift.mpr"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|