batplot 1.8.30__tar.gz → 1.8.31__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.
Files changed (42) hide show
  1. batplot-1.8.31/MANIFEST.in +2 -0
  2. {batplot-1.8.30/batplot.egg-info → batplot-1.8.31}/PKG-INFO +27 -6
  3. {batplot-1.8.30 → batplot-1.8.31}/README.md +26 -5
  4. {batplot-1.8.30 → batplot-1.8.31}/batplot/__init__.py +1 -1
  5. {batplot-1.8.30 → batplot-1.8.31}/batplot/args.py +25 -10
  6. {batplot-1.8.30 → batplot-1.8.31}/batplot/batch.py +27 -8
  7. {batplot-1.8.30 → batplot-1.8.31}/batplot/batplot.py +98 -57
  8. {batplot-1.8.30 → batplot-1.8.31}/batplot/cpc_interactive.py +146 -8
  9. {batplot-1.8.30 → batplot-1.8.31}/batplot/data/CHANGELOG.md +15 -6
  10. batplot-1.8.31/batplot/data/USER_MANUAL.md +624 -0
  11. {batplot-1.8.30 → batplot-1.8.31}/batplot/electrochem_interactive.py +367 -126
  12. {batplot-1.8.30 → batplot-1.8.31}/batplot/manual.py +12 -1
  13. {batplot-1.8.30 → batplot-1.8.31}/batplot/modes.py +23 -3
  14. {batplot-1.8.30 → batplot-1.8.31}/batplot/operando.py +167 -147
  15. {batplot-1.8.30 → batplot-1.8.31}/batplot/operando_ec_interactive.py +14 -14
  16. {batplot-1.8.30 → batplot-1.8.31}/batplot/readers.py +390 -0
  17. {batplot-1.8.30 → batplot-1.8.31}/batplot/session.py +70 -0
  18. {batplot-1.8.30 → batplot-1.8.31}/batplot/version_check.py +2 -3
  19. {batplot-1.8.30 → batplot-1.8.31/batplot.egg-info}/PKG-INFO +27 -6
  20. {batplot-1.8.30 → batplot-1.8.31}/batplot.egg-info/SOURCES.txt +2 -1
  21. batplot-1.8.31/batplot.egg-info/top_level.txt +2 -0
  22. {batplot-1.8.30 → batplot-1.8.31}/pyproject.toml +1 -1
  23. batplot-1.8.30/MANIFEST.in +0 -2
  24. batplot-1.8.30/batplot.egg-info/top_level.txt +0 -1
  25. {batplot-1.8.30 → batplot-1.8.31}/LICENSE +0 -0
  26. {batplot-1.8.30 → batplot-1.8.31}/NOTICE +0 -0
  27. {batplot-1.8.30 → batplot-1.8.31}/batplot/cif.py +0 -0
  28. {batplot-1.8.30 → batplot-1.8.31}/batplot/cli.py +0 -0
  29. {batplot-1.8.30 → batplot-1.8.31}/batplot/color_utils.py +0 -0
  30. {batplot-1.8.30 → batplot-1.8.31}/batplot/config.py +0 -0
  31. {batplot-1.8.30 → batplot-1.8.31}/batplot/converters.py +0 -0
  32. {batplot-1.8.30 → batplot-1.8.31}/batplot/dev_upgrade.py +0 -0
  33. {batplot-1.8.30 → batplot-1.8.31}/batplot/interactive.py +0 -0
  34. {batplot-1.8.30 → batplot-1.8.31}/batplot/plotting.py +0 -0
  35. {batplot-1.8.30 → batplot-1.8.31}/batplot/style.py +0 -0
  36. {batplot-1.8.30 → batplot-1.8.31}/batplot/ui.py +0 -0
  37. {batplot-1.8.30 → batplot-1.8.31}/batplot/utils.py +0 -0
  38. {batplot-1.8.30 → batplot-1.8.31}/batplot.egg-info/dependency_links.txt +0 -0
  39. {batplot-1.8.30 → batplot-1.8.31}/batplot.egg-info/entry_points.txt +0 -0
  40. {batplot-1.8.30 → batplot-1.8.31}/batplot.egg-info/requires.txt +0 -0
  41. {batplot-1.8.30 → batplot-1.8.31}/setup.cfg +0 -0
  42. {batplot-1.8.30 → batplot-1.8.31}/setup.py +0 -0
@@ -0,0 +1,2 @@
1
+ include README.md LICENSE USER_MANUAL.md
2
+ recursive-include batplot/data *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.30
3
+ Version: 1.8.31
4
4
  Summary: Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing
5
5
  Author-email: Tian Dai <tianda@uio.no>
6
6
  License: MIT License
@@ -57,9 +57,10 @@ Dynamic: license-file
57
57
 
58
58
  ## Features
59
59
 
60
- - **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
61
- - **Normal xy plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
62
- - **Operando Analysis**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
60
+ With a single line of command to easily plot publication-ready plots with customized, intuitive interactive editing features such as:
61
+ - **Electrochemistry Plot**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
62
+ - **1D XY plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
63
+ - **Operando Contour plot**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
63
64
  - **Interactive plotting**: Real-time editing customized for each type of plottings
64
65
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
65
66
  - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
@@ -249,6 +250,23 @@ batplot custom.mpt --gc --pw 0.01 3 --cd 0.2 --i
249
250
 
250
251
  ## Operando Mode
251
252
 
253
+ Contour plots from a folder of diffraction data, optionally with an electrochemistry side panel.
254
+
255
+ ### Bruker operando (.brml)
256
+
257
+ For Bruker operando XRD (multi-scan .brml files named cyc1, cyc2, cyc3, etc.):
258
+
259
+ ```bash
260
+ # Place .brml files (e.g. RA_O5_cyc1.brml, RA_O5_cyc2.brml) in the folder
261
+ # Use --wl for Q conversion (e.g. synchrotron λ=0.709 Å)
262
+ batplot RA_O5 --operando --wl 0.709 --i
263
+
264
+ # EC side panel: .mpt or Biologic DataLogger CSV (*--DataLogger.csv), sorted by cyc
265
+ # Time vs potential is concatenated across files (continuous time axis)
266
+ ```
267
+
268
+ ### Standard XY files
269
+
252
270
  ```bash
253
271
  # Contour from folder of .xy/.xye/.qye/.dat
254
272
  batplot --operando --wl 1.54 --i
@@ -313,8 +331,9 @@ batplot --all --readcol 2 3 style.bps --xaxis 2theta
313
331
 
314
332
  | Type | Formats |
315
333
  |------|---------|
316
- | **Electrochemistry** | `.csv` (Neware), `.mpt` (Biologic), `.xlsx` (Landt/Lanhe CPC) |
334
+ | **Electrochemistry** | `.csv` (Neware, Biologic DataLogger), `.mpt` (Biologic), `.xlsx` (Landt/Lanhe CPC) |
317
335
  | **XRD / PDF** | `.xye`, `.xy`, `.qye`, `.dat`, `.csv`, `.txt`; Bruker `.brml`, `.raw` |
336
+ | **Operando** | `.xy`, `.xye`, `.qye`, `.dat`; Bruker `.brml` (cyc1/cyc2/cyc3); EC: `.mpt` or DataLogger `.csv` |
318
337
  | **XAS** | `.nor`, `.chik`, `.chir` |
319
338
  | **Generic** | Use `--readcol` and `--xaxis` for custom formats |
320
339
 
@@ -324,9 +343,11 @@ batplot --all --readcol 2 3 style.bps --xaxis 2theta
324
343
 
325
344
  With `--interactive`:
326
345
  - **Cycle/Scan Control**: Toggle visibility, change colors
346
+ - **Multi-file palette (EC/CV/dQdV)**: Press `c`, then type `fall viridis` (all files), `f1-5 viridis` (files 1–5), or `f1 f3 f5 4` (each file gets one color)
347
+ - **CPC file palette**: In `ly`/`ry` color submenu, apply palette to file range: `1-5 viridis`, `1 3 5 4`
327
348
  - **Styling**: Line widths, markers, fonts
328
349
  - **Axes**: Labels, limits, ticks, spine styles
329
- - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images
350
+ - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images. Colors persist via `p` (print style), `i` (import), `s` (save session), `b` (undo)
330
351
  - **Live Preview**: All changes update in real-time
331
352
 
332
353
  ---
@@ -6,9 +6,10 @@
6
6
 
7
7
  ## Features
8
8
 
9
- - **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
10
- - **Normal xy plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
11
- - **Operando Analysis**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
9
+ With a single line of command to easily plot publication-ready plots with customized, intuitive interactive editing features such as:
10
+ - **Electrochemistry Plot**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
11
+ - **1D XY plot**: Designed for XRD, PDF, XAS (XANES/EXAFS) but also support other types
12
+ - **Operando Contour plot**: Correlate in-situ characterizations (XRD/PDF/XAS) with electrochemical data
12
13
  - **Interactive plotting**: Real-time editing customized for each type of plottings
13
14
  - **Session Persistence**: Save and reload complete plot states with `.pkl` files
14
15
  - **Style Management**: Import/export plot styles as `.bps`/`.bpsg` files
@@ -198,6 +199,23 @@ batplot custom.mpt --gc --pw 0.01 3 --cd 0.2 --i
198
199
 
199
200
  ## Operando Mode
200
201
 
202
+ Contour plots from a folder of diffraction data, optionally with an electrochemistry side panel.
203
+
204
+ ### Bruker operando (.brml)
205
+
206
+ For Bruker operando XRD (multi-scan .brml files named cyc1, cyc2, cyc3, etc.):
207
+
208
+ ```bash
209
+ # Place .brml files (e.g. RA_O5_cyc1.brml, RA_O5_cyc2.brml) in the folder
210
+ # Use --wl for Q conversion (e.g. synchrotron λ=0.709 Å)
211
+ batplot RA_O5 --operando --wl 0.709 --i
212
+
213
+ # EC side panel: .mpt or Biologic DataLogger CSV (*--DataLogger.csv), sorted by cyc
214
+ # Time vs potential is concatenated across files (continuous time axis)
215
+ ```
216
+
217
+ ### Standard XY files
218
+
201
219
  ```bash
202
220
  # Contour from folder of .xy/.xye/.qye/.dat
203
221
  batplot --operando --wl 1.54 --i
@@ -262,8 +280,9 @@ batplot --all --readcol 2 3 style.bps --xaxis 2theta
262
280
 
263
281
  | Type | Formats |
264
282
  |------|---------|
265
- | **Electrochemistry** | `.csv` (Neware), `.mpt` (Biologic), `.xlsx` (Landt/Lanhe CPC) |
283
+ | **Electrochemistry** | `.csv` (Neware, Biologic DataLogger), `.mpt` (Biologic), `.xlsx` (Landt/Lanhe CPC) |
266
284
  | **XRD / PDF** | `.xye`, `.xy`, `.qye`, `.dat`, `.csv`, `.txt`; Bruker `.brml`, `.raw` |
285
+ | **Operando** | `.xy`, `.xye`, `.qye`, `.dat`; Bruker `.brml` (cyc1/cyc2/cyc3); EC: `.mpt` or DataLogger `.csv` |
267
286
  | **XAS** | `.nor`, `.chik`, `.chir` |
268
287
  | **Generic** | Use `--readcol` and `--xaxis` for custom formats |
269
288
 
@@ -273,9 +292,11 @@ batplot --all --readcol 2 3 style.bps --xaxis 2theta
273
292
 
274
293
  With `--interactive`:
275
294
  - **Cycle/Scan Control**: Toggle visibility, change colors
295
+ - **Multi-file palette (EC/CV/dQdV)**: Press `c`, then type `fall viridis` (all files), `f1-5 viridis` (files 1–5), or `f1 f3 f5 4` (each file gets one color)
296
+ - **CPC file palette**: In `ly`/`ry` color submenu, apply palette to file range: `1-5 viridis`, `1 3 5 4`
276
297
  - **Styling**: Line widths, markers, fonts
277
298
  - **Axes**: Labels, limits, ticks, spine styles
278
- - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images
299
+ - **Export**: Sessions (`.pkl`), styles (`.bps`/`.bpsg`), high-res images. Colors persist via `p` (print style), `i` (import), `s` (save session), `b` (undo)
279
300
  - **Live Preview**: All changes update in real-time
280
301
 
281
302
  ---
@@ -1,5 +1,5 @@
1
1
  """batplot: Interactive plotting for battery data visualization."""
2
2
 
3
- __version__ = "1.8.30"
3
+ __version__ = "1.8.31"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -135,7 +135,7 @@ def _print_general_help() -> None:
135
135
  "What it does:\n"
136
136
  " • XY: XRD/PDF/XAS/User defined curves\n"
137
137
  " • EC: Galvanostatic cycling(GC)/Capacity per cycle(CPC)/Diffrential capacity(dQdV)/Cyclic Voltammetry(CV) from Neware (.csv) or Biologic (.mpt)\n"
138
- " • Operando: contour maps from a folder of .xy/.xye/.dat/.txt and optional .mpt file as side panel\n"
138
+ " • Operando: contour from .xy/.xye/.dat/.brml; Bruker .brml (cyc1/cyc2/cyc3) with optional .mpt or DataLogger CSV side panel\n"
139
139
  " • Batch: export vector plots for all files in a directory\n"
140
140
  " • Interactive mode: --i flag opens a menu for styling, ranges, export, and save\n\n"
141
141
  "How to run (basics):\n"
@@ -156,7 +156,8 @@ def _print_general_help() -> None:
156
156
  " batplot --cv file.mpt --i # Cyclic voltammetry\n"
157
157
  " batplot --cpc file.csv --mass 3.52 --i # Capacity per cycle\n\n"
158
158
  " [Operando]\n"
159
- " batplot --operando --i [FOLDER] # Contour from folder\n\n"
159
+ " batplot --operando --i [FOLDER] # Contour from folder\n"
160
+ " batplot Path/to/file --operando --wl 0.709 --i # Bruker .brml, Q conversion\n\n"
160
161
  "Features:\n"
161
162
  " • Interactive (--i): styling, ranges, fonts, export, sessions\n"
162
163
  " • XRD wavelength: --wl 1.54 or file.xye:1.5406 for Q conversion\n"
@@ -313,8 +314,11 @@ def _print_ec_help() -> None:
313
314
  " batplot file1.csv file2.csv ./Style/style.bps --dqdv # Style from relative path\n"
314
315
  " batplot file1.csv file2.mpt style.bpsg --cpc --mass 6 # CPC mode\n"
315
316
  " batplot file1.csv file2.mpt ./Style/style.bpsg --cpc --mass 6 # Style+geom from relative path\n\n"
317
+ "Multi-file (EC/CV/dQdV): Press c, then type fall viridis (all files), f1-5 viridis (files 1–5), or f1 f3 f5 4.\n"
318
+ "CPC (ly/ry): Type 1-5 viridis or 1 3 5 4 for file range. Exported via p, restored via i/s/b.\n\n"
316
319
  "Interactive (--i): choose cycles, colors/palettes, line widths, axis scales (linear/log/symlog),\n"
317
320
  "rename axes, toggle ticks/titles/spines, print/export/import style (.bps/.bpsg), save session (.pkl).\n"
321
+ "Multi-file: In c (cycles/colors), type fall viridis (all files), f1-5 viridis (files 1–5), or f1 f3 f5 4 (files 1,3,5).\n"
318
322
  "Note: Batch mode (--all) exports SVG files automatically; --i is for single-file plotting only.\n\n"
319
323
  "Axis swapping:\n"
320
324
  " --ro : swap x and y axes (exchange x and y values before plotting)\n"
@@ -333,7 +337,14 @@ def _print_op_help() -> None:
333
337
  " batplot --operando --xaxis 2theta # Using 2theta axis\n"
334
338
  " batplot --operando --1d --i # Plot derivatives as contour with interactive menu\n"
335
339
  " batplot --operando --2d --i # Plot derivatives (alias for --1d)\n\n"
336
- " Folder should contain XY files (.xy/.xye/.qye/.dat).\n"
340
+ "Bruker operando (.brml):\n"
341
+ " • Place .brml files (e.g. XX_cyc1.brml, XX_cyc2.brml) in the folder.\n"
342
+ " • Each .brml is expanded into per-scan rows; files sorted by cyc1/cyc2/cyc3.\n"
343
+ " • Use --wl for Q conversion: batplot RA_O5 --operando --wl 0.709 --i\n"
344
+ " • EC side panel: .mpt or Biologic DataLogger CSV (*--DataLogger.csv), sorted by cyc.\n"
345
+ " • Time vs potential is concatenated across files (continuous time axis).\n\n"
346
+ "Standard XY files:\n"
347
+ " • Folder should contain .xy/.xye/.qye/.dat files.\n"
337
348
  " • Intensity scale is auto-adjusted between min/max values.\n"
338
349
  " • If no .qye present, provide --xaxis 2theta or set --wl for Q conversion.\n"
339
350
  " • If a .mpt file is present, a side panel is added for dual-panel mode (time/potential/temp/etc.).\n"
@@ -425,6 +436,8 @@ def build_parser() -> argparse.ArgumentParser:
425
436
  parser.add_argument("--xaxis", type=str, help=argparse.SUPPRESS)
426
437
  parser.add_argument("--convert", nargs=2, metavar=("FROM", "TO"),
427
438
  help="Convert XRD data: wavelength-to-wavelength (e.g., 1.54 0.25), wavelength-to-Q (e.g., 1.54 q), or Q-to-wavelength (e.g., q 1.54). Exports to 'converted' subfolder.")
439
+ parser.add_argument("--extract-brml-scans", nargs="?", const="", metavar="OUT_DIR",
440
+ help="Extract each XRD scan from .brml file to separate .xy files. Optional OUT_DIR (default: <brml_stem>_scans).")
428
441
  parser.add_argument("--wl", type=float, help=argparse.SUPPRESS)
429
442
  parser.add_argument("--fullprof", nargs="+", type=float, help=argparse.SUPPRESS)
430
443
  parser.add_argument("--norm", action="store_true", help=argparse.SUPPRESS)
@@ -530,9 +543,11 @@ def parse_args(argv=None):
530
543
  if argv is None:
531
544
  argv = sys.argv[1:]
532
545
 
533
- # Normalize single-dash short forms to double-dash long forms (backward compatibility)
546
+ # Normalize short forms to long forms (both -x and --x for common flags)
534
547
  _SHORT_TO_LONG = {
535
- '-h': '--help', '-v': '--version', '-V': '--version', '-m': '--manual',
548
+ '-h': '--help', '--h': '--help',
549
+ '-v': '--version', '-V': '--version', '--v': '--version',
550
+ '-m': '--manual', '--m': '--manual',
536
551
  '-i': '--i', '-d': '--delta', '-r': '--xrange', '-o': '--out', '-c': '--convert',
537
552
  '-b': '--b',
538
553
  }
@@ -674,13 +689,13 @@ def parse_args(argv=None):
674
689
  ns, _unknown = parser.parse_known_args(argv)
675
690
  if getattr(ns, "manual", False):
676
691
  try:
677
- from .manual import show_manual # Lazy import avoids matplotlib startup unless needed
678
- pdf_path = show_manual(open_viewer=True)
692
+ from .manual import open_manual_url # Lazy import avoids matplotlib startup unless needed
693
+ open_manual_url()
679
694
  if _HAS_RICH and _console:
680
- _console.print(f"\n[green]Opened manual:[/green] {pdf_path}")
695
+ _console.print("\n[green]Opened manual in browser[/green]")
681
696
  else:
682
- print(f"\nOpened manual: {pdf_path}")
683
- except Exception as exc: # pragma: no cover - rendering is best effort
697
+ print("\nOpened manual in browser")
698
+ except Exception as exc: # pragma: no cover - best effort
684
699
  if _HAS_RICH and _console:
685
700
  _console.print(f"\n[red]Failed to open manual:[/red] {exc}")
686
701
  else:
@@ -21,6 +21,9 @@ from .readers import (
21
21
  read_ec_csv_dqdv_file,
22
22
  compute_dqdv_numerical,
23
23
  is_cs_b_format,
24
+ is_biologic_datalogger_csv,
25
+ read_biologic_datalogger_csv,
26
+ read_biologic_datalogger_dqdv_file,
24
27
  _load_csv_header_and_rows,
25
28
  read_biologic_txt_file,
26
29
  )
@@ -1013,8 +1016,16 @@ def batch_process_ec(directory: str, args):
1013
1016
  cap_x = specific_capacity
1014
1017
  x_label = r'Specific Capacity (mAh g$^{-1}$)'
1015
1018
  elif ext == '.csv':
1016
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
1017
- read_ec_csv_file(fpath, prefer_specific=True)
1019
+ if is_biologic_datalogger_csv(fpath):
1020
+ if mass_mg is None:
1021
+ print(f" Skipped {fname}: GC mode (Biologic DataLogger CSV) requires --mass parameter")
1022
+ plt.close(fig_b)
1023
+ continue
1024
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
1025
+ read_biologic_datalogger_csv(fpath, mass_mg=mass_mg)
1026
+ else:
1027
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
1028
+ read_ec_csv_file(fpath, prefer_specific=True)
1018
1029
  x_label = r'Specific Capacity (mAh g$^{-1}$)'
1019
1030
  else:
1020
1031
  raise ValueError(f"Unsupported file type for GC: {ext}")
@@ -1116,13 +1127,21 @@ def batch_process_ec(directory: str, args):
1116
1127
 
1117
1128
  # Try to load pre-calculated dQ/dV columns; fall back to numerical computation
1118
1129
  _b_dqdv_header = None
1119
- try:
1120
- _b_dqdv_header, _, _ = _load_csv_header_and_rows(fpath)
1121
- except Exception:
1122
- pass
1123
-
1124
1130
  _b_loaded = False
1125
- if not _b_loaded:
1131
+ if is_biologic_datalogger_csv(fpath):
1132
+ if mass_mg is None or mass_mg <= 0:
1133
+ print(f" Skipped {fname}: dQ/dV (Biologic DataLogger CSV) requires --mass parameter")
1134
+ plt.close(fig_b)
1135
+ continue
1136
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = \
1137
+ read_biologic_datalogger_dqdv_file(fpath, mass_mg=mass_mg, prefer_specific=True)
1138
+ _b_loaded = True
1139
+ else:
1140
+ try:
1141
+ _b_dqdv_header, _, _ = _load_csv_header_and_rows(fpath)
1142
+ except Exception:
1143
+ pass
1144
+
1126
1145
  try:
1127
1146
  voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = \
1128
1147
  read_ec_csv_dqdv_file(fpath, prefer_specific=True)
@@ -40,6 +40,10 @@ from .readers import (
40
40
  read_mpt_time_voltage,
41
41
  read_cs_b_csv_file,
42
42
  is_cs_b_format,
43
+ is_biologic_datalogger_csv,
44
+ read_biologic_datalogger_csv,
45
+ read_biologic_datalogger_dqdv_file,
46
+ read_biologic_datalogger_time_voltage,
43
47
  _load_csv_header_and_rows,
44
48
  read_biologic_txt_file,
45
49
  read_batx_file,
@@ -549,7 +553,7 @@ def batplot_main() -> int: # type: ignore
549
553
  # If no files AND no special flags, nothing to do
550
554
  if not args.files and not has_special_flag:
551
555
  print("No input provided, nothing to do.")
552
- print("Use 'batplot --version' for version and release info, 'batplot --help' for CLI help, or 'batplot --manual' to open the txt manual.")
556
+ print("Use 'batplot --v' for version and release info, 'batplot --h' for CLI help, or 'batplot --m' to open the user manual.")
553
557
  return 0 # Exit successfully (not an error, just nothing to do)
554
558
 
555
559
  # ====================================================================
@@ -797,15 +801,23 @@ def batplot_main() -> int: # type: ignore
797
801
  specific_capacity, voltage, cycle_numbers, charge_mask, discharge_mask = read_mpt_file(ec_file, mode='gc', mass_mg=mass_mg)
798
802
  cap_x = specific_capacity
799
803
  elif ec_file.lower().endswith('.csv'):
800
- try:
801
- header, _, _ = _load_csv_header_and_rows(ec_file)
802
- if is_cs_b_format(header):
803
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_cs_b_csv_file(ec_file, mode='gc')
804
- else:
805
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
806
- except Exception:
804
+ if is_biologic_datalogger_csv(ec_file):
805
+ if mass_mg is None:
806
+ continue
807
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_biologic_datalogger_csv(
808
+ ec_file, mass_mg=mass_mg
809
+ )
807
810
  header = None
808
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
811
+ else:
812
+ try:
813
+ header, _, _ = _load_csv_header_and_rows(ec_file)
814
+ if is_cs_b_format(header):
815
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_cs_b_csv_file(ec_file, mode='gc')
816
+ else:
817
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
818
+ except Exception:
819
+ header = None
820
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
809
821
  # If we only have absolute capacity and the user supplied --mass,
810
822
  # convert Capacity(mAh) → Specific Capacity (mAh g⁻¹).
811
823
  if header is not None:
@@ -1004,19 +1016,27 @@ def batplot_main() -> int: # type: ignore
1004
1016
  x_label_gc = r'Specific Capacity (mAh g$^{-1}$)'
1005
1017
  cap_x = specific_capacity
1006
1018
  elif ec_file.lower().endswith('.csv'):
1007
- # Check if this is CS-B format
1008
1019
  header = None
1009
- try:
1010
- header, _, _ = _load_csv_header_and_rows(ec_file)
1011
- if is_cs_b_format(header):
1012
- # Use CS-B format reader
1013
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_cs_b_csv_file(ec_file, mode='gc')
1014
- else:
1015
- # Use standard CSV reader
1020
+ if is_biologic_datalogger_csv(ec_file):
1021
+ if mass_mg is None:
1022
+ print("GC mode (Biologic DataLogger CSV): --mass parameter is required (active material mass in milligrams).")
1023
+ print("Example: batplot file.csv --gc --mass 7.0")
1024
+ if len(data_files) > 1:
1025
+ continue
1026
+ else:
1027
+ exit(1)
1028
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_biologic_datalogger_csv(
1029
+ ec_file, mass_mg=mass_mg
1030
+ )
1031
+ else:
1032
+ try:
1033
+ header, _, _ = _load_csv_header_and_rows(ec_file)
1034
+ if is_cs_b_format(header):
1035
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_cs_b_csv_file(ec_file, mode='gc')
1036
+ else:
1037
+ cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
1038
+ except Exception:
1016
1039
  cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
1017
- except Exception:
1018
- # Fallback to standard reader
1019
- cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
1020
1040
  # Decide whether we should treat cap_x as absolute or specific capacity.
1021
1041
  x_label_gc = r'Specific Capacity (mAh g$^{-1}$)'
1022
1042
  # If the CSV only has absolute capacity and no specific capacity, rescale
@@ -1893,38 +1913,46 @@ def batplot_main() -> int: # type: ignore
1893
1913
  voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_mpt_dqdv_file(ec_file, mass_mg=_mf_mass, prefer_specific=True)
1894
1914
  else:
1895
1915
  _mf_dqdv_header = None
1896
- try:
1897
- _mf_dqdv_header, _, _ = _load_csv_header_and_rows(ec_file)
1898
- except Exception:
1899
- pass
1900
-
1901
1916
  _mf_loaded = False
1902
- if _mf_dqdv_header is not None and is_cs_b_format(_mf_dqdv_header):
1903
- voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_cs_b_csv_file(ec_file, mode='dqdv')
1917
+ if is_biologic_datalogger_csv(ec_file):
1918
+ if _mf_mass is None or _mf_mass <= 0:
1919
+ continue
1920
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_biologic_datalogger_dqdv_file(
1921
+ ec_file, mass_mg=_mf_mass, prefer_specific=True
1922
+ )
1904
1923
  _mf_loaded = True
1905
-
1906
- if not _mf_loaded:
1924
+ else:
1907
1925
  try:
1908
- voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_ec_csv_dqdv_file(ec_file, prefer_specific=True)
1909
- _mf_loaded = True
1910
- except ValueError:
1926
+ _mf_dqdv_header, _, _ = _load_csv_header_and_rows(ec_file)
1927
+ except Exception:
1911
1928
  pass
1912
1929
 
1913
- if not _mf_loaded:
1914
- _mf_gc_cap, _mf_gc_volt, _mf_gc_cyc, _mf_gc_chgm, _mf_gc_dchm = read_ec_csv_file(ec_file, prefer_specific=True)
1915
- if _mf_dqdv_header is not None:
1916
- _mf_hdrs = [h.strip().replace('\t', '') for h in _mf_dqdv_header]
1917
- _mf_has_spec = any('Spec. Cap.(mAh/g)' in h for h in _mf_hdrs)
1918
- _mf_has_abs = any(h == 'Capacity(mAh)' for h in _mf_hdrs)
1919
- if _mf_has_abs and not _mf_has_spec:
1920
- if _mf_mass and _mf_mass > 0:
1921
- _mf_gc_cap = _mf_gc_cap * (1000.0 / float(_mf_mass))
1922
- else:
1923
- print(f"dQ/dV mode: {os.path.basename(ec_file)!r} contains only Capacity(mAh) — pass --mass <mg>.")
1924
- voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = compute_dqdv_numerical(
1925
- _mf_gc_cap, _mf_gc_volt, _mf_gc_cyc, _mf_gc_chgm, _mf_gc_dchm
1926
- )
1927
- print(f"dQ/dV mode: computing numerically from GC data for {os.path.basename(ec_file)!r}.")
1930
+ if _mf_dqdv_header is not None and is_cs_b_format(_mf_dqdv_header):
1931
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_cs_b_csv_file(ec_file, mode='dqdv')
1932
+ _mf_loaded = True
1933
+
1934
+ if not _mf_loaded:
1935
+ try:
1936
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_ec_csv_dqdv_file(ec_file, prefer_specific=True)
1937
+ _mf_loaded = True
1938
+ except ValueError:
1939
+ pass
1940
+
1941
+ if not _mf_loaded:
1942
+ _mf_gc_cap, _mf_gc_volt, _mf_gc_cyc, _mf_gc_chgm, _mf_gc_dchm = read_ec_csv_file(ec_file, prefer_specific=True)
1943
+ if _mf_dqdv_header is not None:
1944
+ _mf_hdrs = [h.strip().replace('\t', '') for h in _mf_dqdv_header]
1945
+ _mf_has_spec = any('Spec. Cap.(mAh/g)' in h for h in _mf_hdrs)
1946
+ _mf_has_abs = any(h == 'Capacity(mAh)' for h in _mf_hdrs)
1947
+ if _mf_has_abs and not _mf_has_spec:
1948
+ if _mf_mass and _mf_mass > 0:
1949
+ _mf_gc_cap = _mf_gc_cap * (1000.0 / float(_mf_mass))
1950
+ else:
1951
+ print(f"dQ/dV mode: {os.path.basename(ec_file)!r} contains only Capacity(mAh) — pass --mass <mg>.")
1952
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = compute_dqdv_numerical(
1953
+ _mf_gc_cap, _mf_gc_volt, _mf_gc_cyc, _mf_gc_chgm, _mf_gc_dchm
1954
+ )
1955
+ print(f"dQ/dV mode: computing numerically from GC data for {os.path.basename(ec_file)!r}.")
1928
1956
  if y_label_used is None:
1929
1957
  y_label_used = y_label
1930
1958
  segments = _mask_segments(charge_mask, 'charge') + _mask_segments(discharge_mask, 'discharge')
@@ -2058,15 +2086,25 @@ def batplot_main() -> int: # type: ignore
2058
2086
  else:
2059
2087
  # Load header for format detection and mass-scaling check
2060
2088
  _dqdv_header = None
2061
- try:
2062
- _dqdv_header, _, _ = _load_csv_header_and_rows(ec_file)
2063
- except Exception:
2064
- pass
2065
-
2066
2089
  _loaded_dqdv = False
2067
- if _dqdv_header is not None and is_cs_b_format(_dqdv_header):
2068
- voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_cs_b_csv_file(ec_file, mode='dqdv')
2090
+ if is_biologic_datalogger_csv(ec_file):
2091
+ if _dqdv_mass is None or _dqdv_mass <= 0:
2092
+ print(f"dQ/dV mode (Biologic DataLogger CSV): --mass parameter is required.")
2093
+ print(f"Example: batplot {ec_file} --dqdv --mass 7.0")
2094
+ continue
2095
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_biologic_datalogger_dqdv_file(
2096
+ ec_file, mass_mg=_dqdv_mass, prefer_specific=True
2097
+ )
2069
2098
  _loaded_dqdv = True
2099
+ else:
2100
+ try:
2101
+ _dqdv_header, _, _ = _load_csv_header_and_rows(ec_file)
2102
+ except Exception:
2103
+ pass
2104
+
2105
+ if _dqdv_header is not None and is_cs_b_format(_dqdv_header):
2106
+ voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = read_cs_b_csv_file(ec_file, mode='dqdv')
2107
+ _loaded_dqdv = True
2070
2108
 
2071
2109
  if not _loaded_dqdv:
2072
2110
  try:
@@ -2086,11 +2124,11 @@ def batplot_main() -> int: # type: ignore
2086
2124
  if _dqdv_mass and _dqdv_mass > 0:
2087
2125
  _gc_cap = _gc_cap * (1000.0 / float(_dqdv_mass))
2088
2126
  else:
2089
- print(f"dQ/dV mode: {file_basename!r} contains only Capacity(mAh) — pass --mass <mg> for specific dQ/dV.")
2127
+ print(f"dQ/dV mode: {os.path.basename(ec_file)!r} contains only Capacity(mAh) — pass --mass <mg> for specific dQ/dV.")
2090
2128
  voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = compute_dqdv_numerical(
2091
2129
  _gc_cap, _gc_volt, _gc_cyc, _gc_chgm, _gc_dchm
2092
2130
  )
2093
- print(f"dQ/dV mode: no dQ/dV column found — computing numerically from GC data for {file_basename!r}.")
2131
+ print(f"dQ/dV mode: no dQ/dV column found — computing numerically from GC data for {os.path.basename(ec_file)!r}.")
2094
2132
 
2095
2133
  # Create the plot
2096
2134
  fig, ax = plt.subplots(figsize=(10, 6))
@@ -3689,7 +3727,10 @@ def batplot_main() -> int: # type: ignore
3689
3727
  # Time mode: read time (h) vs potential (V) for electrochemistry files
3690
3728
  try:
3691
3729
  if file_ext == '.csv':
3692
- x, y = read_csv_time_voltage(fname)
3730
+ if is_biologic_datalogger_csv(fname):
3731
+ x, y = read_biologic_datalogger_time_voltage(fname)
3732
+ else:
3733
+ x, y = read_csv_time_voltage(fname)
3693
3734
  elif file_ext == '.mpt':
3694
3735
  x, y = read_mpt_time_voltage(fname)
3695
3736
  e = None