batplot 1.1.0__tar.gz → 1.1.2__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.
- {batplot-1.1.0 → batplot-1.1.2}/PKG-INFO +7 -4
- {batplot-1.1.0 → batplot-1.1.2}/README.md +6 -3
- {batplot-1.1.0 → batplot-1.1.2}/batplot/args.py +38 -22
- {batplot-1.1.0 → batplot-1.1.2}/batplot/batch.py +13 -9
- {batplot-1.1.0 → batplot-1.1.2}/batplot/batplot.py +151 -71
- batplot-1.1.2/batplot/batplot_new.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/cpc_interactive.py +608 -71
- {batplot-1.1.0 → batplot-1.1.2}/batplot/electrochem_interactive.py +9 -5
- {batplot-1.1.0 → batplot-1.1.2}/batplot/interactive.py +2 -5
- {batplot-1.1.0 → batplot-1.1.2}/batplot/modes.py +9 -5
- {batplot-1.1.0 → batplot-1.1.2}/batplot/readers.py +234 -105
- {batplot-1.1.0 → batplot-1.1.2}/batplot/session.py +40 -5
- {batplot-1.1.0 → batplot-1.1.2}/batplot/style.py +2 -5
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/PKG-INFO +7 -4
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/SOURCES.txt +1 -0
- {batplot-1.1.0 → batplot-1.1.2}/pyproject.toml +1 -1
- {batplot-1.1.0 → batplot-1.1.2}/LICENSE +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/__init__.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/cif.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/cli.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/converters.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/operando.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/operando_ec_interactive.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/plotting.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/ui.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot/utils.py +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/batplot.egg-info/top_level.txt +0 -0
- {batplot-1.1.0 → batplot-1.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Interactive plotting for XRD, PDF, and XAS data (.xye, .xy, .qye, .dat, .csv, .gr, .nor, .chik, .chir)
|
|
5
5
|
Author-email: Tian Dai <tianda@uio.no>
|
|
6
6
|
License: MIT License
|
|
@@ -55,10 +55,10 @@ Dynamic: license-file
|
|
|
55
55
|
|
|
56
56
|
## Features
|
|
57
57
|
|
|
58
|
-
- **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV),
|
|
58
|
+
- **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
|
|
59
59
|
- **Structural Characterization**: XRD, PDF, XAS (XANES/EXAFS)
|
|
60
60
|
- **Operando Analysis**: Correlate in-situ structural changes with electrochemical data
|
|
61
|
-
- **Interactive Menu**: Real-time styling, cycle visibility control, color customization
|
|
61
|
+
- **Interactive Menu**: Real-time styling, cycle visibility control, individual color customization for multi-file plots
|
|
62
62
|
- **Session Persistence**: Save and reload complete plot states with `.pkl` files
|
|
63
63
|
- **Style Management**: Import/export plot styles as `.bpcfg` files
|
|
64
64
|
- **Batch Processing**: Plot multiple files simultaneously with automatic SVG export
|
|
@@ -96,9 +96,12 @@ batplot cyclic.mpt --cv --interactive
|
|
|
96
96
|
# Differential capacity
|
|
97
97
|
batplot battery.csv --dqdv
|
|
98
98
|
|
|
99
|
-
#
|
|
99
|
+
# Capacity per cycle - single file
|
|
100
100
|
batplot stability.mpt --cpc --mass 5.4 --interactive
|
|
101
101
|
|
|
102
|
+
# Capacity per cycle - multiple files with individual color control
|
|
103
|
+
batplot file1.csv file2.csv file3.mpt --cpc --mass 5.4 --interactive
|
|
104
|
+
|
|
102
105
|
# Batch processing: export all EC files to SVG
|
|
103
106
|
batplot --gc all # All .mpt/.csv files (.mpt needs --mass, .csv doesn't)
|
|
104
107
|
batplot --cv all # All .mpt files (CV mode)
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV),
|
|
9
|
+
- **Electrochemistry Modes**: Galvanostatic cycling (GC), cyclic voltammetry (CV), differential capacity (dQdV), capacity per cycle (CPC) with multi-file support
|
|
10
10
|
- **Structural Characterization**: XRD, PDF, XAS (XANES/EXAFS)
|
|
11
11
|
- **Operando Analysis**: Correlate in-situ structural changes with electrochemical data
|
|
12
|
-
- **Interactive Menu**: Real-time styling, cycle visibility control, color customization
|
|
12
|
+
- **Interactive Menu**: Real-time styling, cycle visibility control, individual color customization for multi-file plots
|
|
13
13
|
- **Session Persistence**: Save and reload complete plot states with `.pkl` files
|
|
14
14
|
- **Style Management**: Import/export plot styles as `.bpcfg` files
|
|
15
15
|
- **Batch Processing**: Plot multiple files simultaneously with automatic SVG export
|
|
@@ -47,9 +47,12 @@ batplot cyclic.mpt --cv --interactive
|
|
|
47
47
|
# Differential capacity
|
|
48
48
|
batplot battery.csv --dqdv
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# Capacity per cycle - single file
|
|
51
51
|
batplot stability.mpt --cpc --mass 5.4 --interactive
|
|
52
52
|
|
|
53
|
+
# Capacity per cycle - multiple files with individual color control
|
|
54
|
+
batplot file1.csv file2.csv file3.mpt --cpc --mass 5.4 --interactive
|
|
55
|
+
|
|
53
56
|
# Batch processing: export all EC files to SVG
|
|
54
57
|
batplot --gc all # All .mpt/.csv files (.mpt needs --mass, .csv doesn't)
|
|
55
58
|
batplot --cv all # All .mpt files (CV mode)
|
|
@@ -14,23 +14,28 @@ def _print_general_help() -> None:
|
|
|
14
14
|
" • EC: GC/CPC/dQdV/CV (from .csv or .mpt)\n"
|
|
15
15
|
" • Operando: contour maps from a folder of normalized XY and .mpt files\n"
|
|
16
16
|
" • Batch: export SVG plots for all files in a directory\n\n"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
" • Interactive mode: --interactive flag opens a menu for styling, ranges, fonts, export, sessions\n\n"
|
|
18
|
+
"How to run (basics):\n"
|
|
19
|
+
" [XY curves]\n"
|
|
20
|
+
" batplot file1.xy file2.qye [option1] [option2] # XY curves\n"
|
|
21
|
+
" batplot all # Batch mode: all XY files in current directory\n\n"
|
|
22
|
+
" [Electrochemistry]\n"
|
|
23
|
+
" batplot --gc FILE.mpt --mass 7.0 # EC GC from .mpt (requires --mass mg)\n"
|
|
24
|
+
" batplot --gc FILE.csv # EC GC from supported .csv (no mass required)\n"
|
|
25
|
+
" batplot --gc all --mass 7.0 # Batch: all .mpt/.csv in directory (.mpt needs --mass)\n"
|
|
26
|
+
" batplot --dqdv FILE.csv # EC dQ/dV from supported .csv\n"
|
|
27
|
+
" batplot --dqdv all # Batch: all .csv in directory (dQdV mode)\n"
|
|
28
|
+
" batplot --cv FILE.mpt # EC CV (cyclic voltammetry) from .mpt\n"
|
|
29
|
+
" batplot --cv FILE.txt # EC CV (cyclic voltammetry) from .txt\n"
|
|
30
|
+
" batplot --cv all # Batch: all .mpt/.txt in directory (CV mode)\n\n"
|
|
31
|
+
" [Operando]\n"
|
|
32
|
+
" batplot --operando [FOLDER] # Operando contour from folder\n\n"
|
|
29
33
|
"Features:\n"
|
|
30
34
|
" • Quick plotting with sensible defaults, no config files needed\n"
|
|
31
35
|
" • Supports many common file formats (see -h xy/ec/op)\n"
|
|
32
36
|
" • Interactive menus (--interactive): styling, ranges, fonts, export, sessions\n"
|
|
33
37
|
" • Batch processing: use 'all' or directory path with any mode\n\n"
|
|
38
|
+
|
|
34
39
|
"More help:\n"
|
|
35
40
|
" batplot -h xy # XY file plotting guide\n"
|
|
36
41
|
" batplot -h ec # Electrochemistry (GC/dQdV/CV/CPC) guide\n"
|
|
@@ -47,12 +52,19 @@ def _print_xy_help() -> None:
|
|
|
47
52
|
"If mixing 2θ data in Q, give wavelength per-file (file.xye:1.5406) or global --wl.\n\n"
|
|
48
53
|
"Examples:\n"
|
|
49
54
|
" batplot a.xye:1.5406 b.qye --stack --interactive\n"
|
|
50
|
-
" batplot a.dat b.xy --
|
|
55
|
+
" batplot a.dat b.xy --wl 1.54 --out fig.svg\n"
|
|
51
56
|
" batplot pattern.qye ticks.cif --interactive\n\n"
|
|
52
|
-
"Tips:\n"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
"Tips and options:\n"
|
|
58
|
+
"[XY plot]\n"
|
|
59
|
+
" --interactive : open interactive menu for styling, ranges, fonts, export, sessions\n"
|
|
60
|
+
" --delta/-d <float> : spacing between curves, e.g. --delta 0.1\n"
|
|
61
|
+
" --raw : plot raw intensity (normalized by default)\n"
|
|
62
|
+
" --xrange/-r <min> <max> : set x-axis range, e.g. --xrange 0 10\n"
|
|
63
|
+
" --out/-o <filename> : save figure to file, e.g. --out file.svg\n"
|
|
64
|
+
" --xaxis <type> : set x-axis type (Q, 2theta, r, k, energy, rft, or user defined), e.g. --xaxis 2theta\n"
|
|
65
|
+
" --wl <float> : set wavelength for Q conversion for all files, e.g. --wl 1.5406\n"
|
|
66
|
+
" --fullprof <args> : FullProf overlay options\n"
|
|
67
|
+
" --stack : stack curves vertically\n"
|
|
56
68
|
)
|
|
57
69
|
print(msg)
|
|
58
70
|
|
|
@@ -60,20 +72,24 @@ def _print_xy_help() -> None:
|
|
|
60
72
|
def _print_ec_help() -> None:
|
|
61
73
|
msg = (
|
|
62
74
|
"Electrochemistry (GC, dQ/dV, CV, and CPC)\n\n"
|
|
75
|
+
"Use --interactive for styling, colors, line widths, axis scales, etc.\n"
|
|
63
76
|
"GC from .mpt: requires active mass in mg to compute mAh g⁻¹.\n"
|
|
64
77
|
" batplot --gc file.mpt --mass 6.5 --interactive\n\n"
|
|
65
78
|
"GC from supported .csv: specific capacity is read directly (no --mass).\n"
|
|
66
79
|
" batplot --gc file.csv\n\n"
|
|
67
80
|
"dQ/dV from supported .csv:\n"
|
|
68
81
|
" batplot --dqdv file.csv\n\n"
|
|
69
|
-
"Cyclic voltammetry (CV) from .mpt: plots voltage vs current for each cycle.\n"
|
|
70
|
-
" batplot --cv file.mpt\n
|
|
71
|
-
"
|
|
82
|
+
"Cyclic voltammetry (CV) from .mpt or .txt: plots voltage vs current for each cycle.\n"
|
|
83
|
+
" batplot --cv file.mpt\n"
|
|
84
|
+
" batplot --cv file.txt\n\n"
|
|
85
|
+
"Capacity-per-cycle (CPC) with coulombic efficiency from .csv or .mpt.\n"
|
|
86
|
+
"Supports multiple files with individual color customization:\n"
|
|
72
87
|
" batplot --cpc file.csv\n"
|
|
73
|
-
" batplot --cpc file.mpt --mass 1.2\n
|
|
88
|
+
" batplot --cpc file.mpt --mass 1.2\n"
|
|
89
|
+
" batplot --cpc file1.csv file2.csv file3.mpt --mass 1.2 --interactive\n"
|
|
74
90
|
"Batch mode: Process all files in a directory and export to SVG.\n"
|
|
75
91
|
" batplot --gc all --mass 7.0 # All .mpt/.csv files (.mpt requires --mass)\n"
|
|
76
|
-
" batplot --cv all # All .mpt files (CV mode)\n"
|
|
92
|
+
" batplot --cv all # All .mpt/.txt files (CV mode)\n"
|
|
77
93
|
" batplot --dqdv all # All .csv files (dQdV mode)\n"
|
|
78
94
|
" batplot --cpc all --mass 5.4 # All .mpt/.csv files (.mpt requires --mass)\n"
|
|
79
95
|
" batplot --gc /path/to/folder --mass 6 # Process specific directory\n\n"
|
|
@@ -184,7 +184,7 @@ def batch_process_ec(directory: str, args):
|
|
|
184
184
|
supported_ext = {'.mpt', '.csv'}
|
|
185
185
|
elif getattr(args, 'cv', False):
|
|
186
186
|
mode = 'cv'
|
|
187
|
-
supported_ext = {'.mpt'}
|
|
187
|
+
supported_ext = {'.mpt', '.txt'}
|
|
188
188
|
elif getattr(args, 'dqdv', False):
|
|
189
189
|
mode = 'dqdv'
|
|
190
190
|
supported_ext = {'.csv'}
|
|
@@ -230,11 +230,11 @@ def batch_process_ec(directory: str, args):
|
|
|
230
230
|
specific_capacity, voltage, cycle_numbers, charge_mask, discharge_mask = \
|
|
231
231
|
read_mpt_file(fpath, mode='gc', mass_mg=mass_mg)
|
|
232
232
|
cap_x = specific_capacity
|
|
233
|
-
x_label = 'Specific Capacity (mAh g
|
|
233
|
+
x_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
234
234
|
elif ext == '.csv':
|
|
235
235
|
cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
|
|
236
236
|
read_ec_csv_file(fpath, prefer_specific=True)
|
|
237
|
-
x_label = 'Specific Capacity (mAh g
|
|
237
|
+
x_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
238
238
|
else:
|
|
239
239
|
raise ValueError(f"Unsupported file type for GC: {ext}")
|
|
240
240
|
|
|
@@ -274,10 +274,14 @@ def batch_process_ec(directory: str, args):
|
|
|
274
274
|
|
|
275
275
|
# ---- CV Mode ----
|
|
276
276
|
elif mode == 'cv':
|
|
277
|
-
if ext
|
|
278
|
-
|
|
277
|
+
if ext == '.txt':
|
|
278
|
+
from .readers import read_biologic_txt_file
|
|
279
|
+
voltage, current, cycles = read_biologic_txt_file(fpath, mode='cv')
|
|
280
|
+
elif ext == '.mpt':
|
|
281
|
+
voltage, current, cycles = read_mpt_file(fpath, mode='cv')
|
|
282
|
+
else:
|
|
283
|
+
raise ValueError("CV mode requires .mpt or .txt file")
|
|
279
284
|
|
|
280
|
-
voltage, current, cycles = read_mpt_file(fpath, mode='cv')
|
|
281
285
|
cyc_int_raw = np.array(np.rint(cycles), dtype=int)
|
|
282
286
|
if cyc_int_raw.size:
|
|
283
287
|
min_c = int(np.min(cyc_int_raw))
|
|
@@ -307,7 +311,7 @@ def batch_process_ec(directory: str, args):
|
|
|
307
311
|
voltage, dqdv = read_ec_csv_dqdv_file(fpath)
|
|
308
312
|
ax_b.plot(voltage, dqdv, '-', color='#1f77b4', linewidth=1.5)
|
|
309
313
|
ax_b.set_xlabel('Voltage (V)')
|
|
310
|
-
ax_b.set_ylabel('dQ/dV (mAh g
|
|
314
|
+
ax_b.set_ylabel(r'dQ/dV (mAh g$^{-1}$ V$^{-1}$)')
|
|
311
315
|
ax_b.set_title(f"{fname}")
|
|
312
316
|
|
|
313
317
|
# ---- CPC Mode ----
|
|
@@ -320,7 +324,7 @@ def batch_process_ec(directory: str, args):
|
|
|
320
324
|
continue
|
|
321
325
|
cyc_nums, cap_charge, cap_discharge, eff = \
|
|
322
326
|
read_mpt_file(fpath, mode='cpc', mass_mg=mass_mg)
|
|
323
|
-
x_label = 'Specific Capacity (mAh g
|
|
327
|
+
x_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
324
328
|
elif ext == '.csv':
|
|
325
329
|
# For CSV CPC, read as GC-like data
|
|
326
330
|
cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
|
|
@@ -349,7 +353,7 @@ def batch_process_ec(directory: str, args):
|
|
|
349
353
|
cyc_nums = np.array([1])
|
|
350
354
|
cap_charge = np.array([0])
|
|
351
355
|
cap_discharge = np.array([0])
|
|
352
|
-
x_label = 'Specific Capacity (mAh g
|
|
356
|
+
x_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
353
357
|
else:
|
|
354
358
|
raise ValueError(f"Unsupported file type for CPC: {ext}")
|
|
355
359
|
|
|
@@ -112,14 +112,19 @@ def batplot_main() -> int:
|
|
|
112
112
|
import os as _os
|
|
113
113
|
import matplotlib.pyplot as _plt
|
|
114
114
|
if len(args.files) != 1:
|
|
115
|
-
print("CV mode: provide exactly one .mpt
|
|
115
|
+
print("CV mode: provide exactly one file (.mpt or .txt).")
|
|
116
116
|
exit(1)
|
|
117
117
|
ec_file = args.files[0]
|
|
118
118
|
if not _os.path.isfile(ec_file):
|
|
119
119
|
print(f"File not found: {ec_file}")
|
|
120
120
|
exit(1)
|
|
121
121
|
try:
|
|
122
|
-
|
|
122
|
+
# Support both .mpt and .txt formats
|
|
123
|
+
if ec_file.lower().endswith('.txt'):
|
|
124
|
+
from .readers import read_biologic_txt_file
|
|
125
|
+
voltage, current, cycles = read_biologic_txt_file(ec_file, mode='cv')
|
|
126
|
+
else:
|
|
127
|
+
voltage, current, cycles = read_mpt_file(ec_file, mode='cv')
|
|
123
128
|
# Normalize cycle indices to start at 1
|
|
124
129
|
# Find the first cycle with at least 2 data points (needed for plotting)
|
|
125
130
|
cyc_int_raw = np.array(np.rint(cycles), dtype=int)
|
|
@@ -330,12 +335,12 @@ def batplot_main() -> int:
|
|
|
330
335
|
print("Example: batplot file.mpt --gc --mass 7.0")
|
|
331
336
|
exit(1)
|
|
332
337
|
specific_capacity, voltage, cycle_numbers, charge_mask, discharge_mask = read_mpt_file(ec_file, mode='gc', mass_mg=mass_mg)
|
|
333
|
-
x_label_gc = 'Specific Capacity (mAh g
|
|
338
|
+
x_label_gc = r'Specific Capacity (mAh g$^{-1}$)'
|
|
334
339
|
cap_x = specific_capacity
|
|
335
340
|
elif ec_file.lower().endswith('.csv'):
|
|
336
341
|
# For supported CSV export, use specific capacity directly when available (no mass required)
|
|
337
342
|
cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = read_ec_csv_file(ec_file, prefer_specific=True)
|
|
338
|
-
x_label_gc = 'Specific Capacity (mAh g
|
|
343
|
+
x_label_gc = r'Specific Capacity (mAh g$^{-1}$)'
|
|
339
344
|
else:
|
|
340
345
|
print("GC mode: file must be .mpt or .csv")
|
|
341
346
|
exit(1)
|
|
@@ -573,81 +578,143 @@ def batplot_main() -> int:
|
|
|
573
578
|
import os as _os
|
|
574
579
|
import numpy as _np
|
|
575
580
|
|
|
576
|
-
if len(args.files)
|
|
577
|
-
print("CPC mode: provide
|
|
578
|
-
exit(1)
|
|
579
|
-
ec_file = args.files[0]
|
|
580
|
-
if not _os.path.isfile(ec_file):
|
|
581
|
-
print(f"File not found: {ec_file}")
|
|
581
|
+
if len(args.files) < 1:
|
|
582
|
+
print("CPC mode: provide at least one file (.csv or .mpt).")
|
|
582
583
|
exit(1)
|
|
584
|
+
|
|
585
|
+
# Process multiple files
|
|
586
|
+
file_data = [] # List of dicts with file info and data
|
|
587
|
+
# Use Viridis colormap for capacity (charge/discharge) - spreads from purple to yellow
|
|
588
|
+
# Use Plasma colormap for efficiency - spreads from purple to yellow-pink
|
|
589
|
+
import matplotlib.cm as cm
|
|
590
|
+
import matplotlib.colors as mcolors
|
|
591
|
+
n_files = len(args.files)
|
|
592
|
+
viridis = cm.get_cmap('viridis', n_files)
|
|
593
|
+
plasma = cm.get_cmap('plasma', n_files)
|
|
594
|
+
|
|
595
|
+
# Generate colors from colormaps
|
|
596
|
+
capacity_colors = [mcolors.rgb2hex(viridis(i)[:3]) for i in range(n_files)]
|
|
597
|
+
efficiency_colors = [mcolors.rgb2hex(plasma(i)[:3]) for i in range(n_files)]
|
|
598
|
+
|
|
599
|
+
for file_idx, ec_file in enumerate(args.files):
|
|
600
|
+
if not _os.path.isfile(ec_file):
|
|
601
|
+
print(f"File not found: {ec_file}")
|
|
602
|
+
continue
|
|
583
603
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
cap_x, voltage, cycles, chg_mask, dchg_mask = _bp_read_ec_csv(ec_file, prefer_specific=True)
|
|
588
|
-
except Exception as e:
|
|
589
|
-
print(f"Failed to read EC CSV: {e}")
|
|
590
|
-
exit(1)
|
|
591
|
-
cyc = _np.array(cycles, dtype=int)
|
|
592
|
-
unique_cycles = _np.unique(cyc)
|
|
593
|
-
unique_cycles = unique_cycles[_np.isfinite(unique_cycles)]
|
|
594
|
-
unique_cycles = [int(x) for x in unique_cycles]
|
|
595
|
-
if not unique_cycles:
|
|
596
|
-
unique_cycles = [1]
|
|
597
|
-
cyc_nums = []
|
|
598
|
-
cap_charge = []
|
|
599
|
-
cap_discharge = []
|
|
600
|
-
eff = []
|
|
601
|
-
for c in sorted(unique_cycles):
|
|
602
|
-
m_c = (cyc == c)
|
|
603
|
-
qchg = _np.nanmax(cap_x[m_c & chg_mask]) if _np.any(m_c & chg_mask) else _np.nan
|
|
604
|
-
qdch = _np.nanmax(cap_x[m_c & dchg_mask]) if _np.any(m_c & dchg_mask) else _np.nan
|
|
605
|
-
eta = (qdch / qchg * 100.0) if (_np.isfinite(qchg) and qchg > 0 and _np.isfinite(qdch)) else _np.nan
|
|
606
|
-
cyc_nums.append(c)
|
|
607
|
-
cap_charge.append(qchg)
|
|
608
|
-
cap_discharge.append(qdch)
|
|
609
|
-
eff.append(eta)
|
|
610
|
-
cyc_nums = _np.array(cyc_nums, dtype=float)
|
|
611
|
-
cap_charge = _np.array(cap_charge, dtype=float)
|
|
612
|
-
cap_discharge = _np.array(cap_discharge, dtype=float)
|
|
613
|
-
eff = _np.array(eff, dtype=float)
|
|
614
|
-
elif ext == '.mpt':
|
|
615
|
-
mass_mg = getattr(args, 'mass', None)
|
|
616
|
-
if mass_mg is None:
|
|
617
|
-
print("CPC mode (.mpt): --mass parameter is required (active material mass in mg).")
|
|
618
|
-
print("Example: batplot data.mpt --cpc --mass 1.2 --interactive")
|
|
619
|
-
exit(1)
|
|
604
|
+
ext = _os.path.splitext(ec_file)[1].lower()
|
|
605
|
+
file_basename = _os.path.basename(ec_file)
|
|
606
|
+
|
|
620
607
|
try:
|
|
621
|
-
|
|
608
|
+
if ext == '.csv':
|
|
609
|
+
cap_x, voltage, cycles, chg_mask, dchg_mask = read_ec_csv_file(ec_file, prefer_specific=True)
|
|
610
|
+
cyc = _np.array(cycles, dtype=int)
|
|
611
|
+
unique_cycles = _np.unique(cyc)
|
|
612
|
+
unique_cycles = unique_cycles[_np.isfinite(unique_cycles)]
|
|
613
|
+
unique_cycles = [int(x) for x in unique_cycles]
|
|
614
|
+
if not unique_cycles:
|
|
615
|
+
unique_cycles = [1]
|
|
616
|
+
cyc_nums = []
|
|
617
|
+
cap_charge = []
|
|
618
|
+
cap_discharge = []
|
|
619
|
+
eff = []
|
|
620
|
+
for c in sorted(unique_cycles):
|
|
621
|
+
m_c = (cyc == c)
|
|
622
|
+
qchg = _np.nanmax(cap_x[m_c & chg_mask]) if _np.any(m_c & chg_mask) else _np.nan
|
|
623
|
+
qdch = _np.nanmax(cap_x[m_c & dchg_mask]) if _np.any(m_c & dchg_mask) else _np.nan
|
|
624
|
+
eta = (qdch / qchg * 100.0) if (_np.isfinite(qchg) and qchg > 0 and _np.isfinite(qdch)) else _np.nan
|
|
625
|
+
cyc_nums.append(c)
|
|
626
|
+
cap_charge.append(qchg)
|
|
627
|
+
cap_discharge.append(qdch)
|
|
628
|
+
eff.append(eta)
|
|
629
|
+
cyc_nums = _np.array(cyc_nums, dtype=float)
|
|
630
|
+
cap_charge = _np.array(cap_charge, dtype=float)
|
|
631
|
+
cap_discharge = _np.array(cap_discharge, dtype=float)
|
|
632
|
+
eff = _np.array(eff, dtype=float)
|
|
633
|
+
elif ext == '.mpt':
|
|
634
|
+
mass_mg = getattr(args, 'mass', None)
|
|
635
|
+
if mass_mg is None:
|
|
636
|
+
print(f"Skipped {file_basename}: CPC mode (.mpt) requires --mass parameter.")
|
|
637
|
+
continue
|
|
638
|
+
cyc_nums, cap_charge, cap_discharge, eff = read_mpt_file(ec_file, mode='cpc', mass_mg=mass_mg)
|
|
639
|
+
else:
|
|
640
|
+
print(f"Skipped {file_basename}: unsupported format (must be .csv or .mpt)")
|
|
641
|
+
continue
|
|
642
|
+
|
|
643
|
+
# Assign colors: distinct hue for each file
|
|
644
|
+
capacity_color = capacity_colors[file_idx % len(capacity_colors)]
|
|
645
|
+
efficiency_color = efficiency_colors[file_idx % len(efficiency_colors)]
|
|
646
|
+
|
|
647
|
+
file_data.append({
|
|
648
|
+
'filename': file_basename,
|
|
649
|
+
'filepath': ec_file,
|
|
650
|
+
'cyc_nums': cyc_nums,
|
|
651
|
+
'cap_charge': cap_charge,
|
|
652
|
+
'cap_discharge': cap_discharge,
|
|
653
|
+
'eff': eff,
|
|
654
|
+
'color': capacity_color,
|
|
655
|
+
'eff_color': efficiency_color,
|
|
656
|
+
'visible': True
|
|
657
|
+
})
|
|
658
|
+
|
|
622
659
|
except Exception as e:
|
|
623
|
-
print(f"Failed to read
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
660
|
+
print(f"Failed to read {file_basename}: {e}")
|
|
661
|
+
continue
|
|
662
|
+
|
|
663
|
+
if not file_data:
|
|
664
|
+
print("No valid CPC data files to plot.")
|
|
627
665
|
exit(1)
|
|
628
666
|
|
|
629
667
|
# Plot (same figsize as GC)
|
|
630
668
|
fig, ax = plt.subplots(figsize=(10, 6))
|
|
631
|
-
col_chg = '#1f77b4' # blue
|
|
632
|
-
col_dch = '#d62728' # red
|
|
633
|
-
sc_charge = ax.scatter(cyc_nums, cap_charge, color=col_chg, label='Charge capacity', s=32, zorder=3)
|
|
634
|
-
sc_discharge = ax.scatter(cyc_nums, cap_discharge, color=col_dch, label='Discharge capacity', s=32, zorder=3)
|
|
635
669
|
ax.set_xlabel('Cycle number', labelpad=8.0)
|
|
636
|
-
ax.set_ylabel('Specific Capacity (mAh g
|
|
670
|
+
ax.set_ylabel(r'Specific Capacity (mAh g$^{-1}$)', labelpad=8.0)
|
|
637
671
|
ax.grid(True, alpha=0.25, linestyle='--', linewidth=0.8)
|
|
638
|
-
ax.legend(loc='best')
|
|
639
672
|
|
|
640
673
|
ax2 = ax.twinx()
|
|
641
|
-
sc_eff = ax2.scatter(cyc_nums, eff, color='#2ca02c', marker='^', label='Coulombic efficiency', s=40, alpha=0.85, zorder=3)
|
|
642
674
|
ax2.set_ylabel('Efficiency (%)', labelpad=8.0)
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
675
|
+
|
|
676
|
+
# Create scatter plots for each file
|
|
677
|
+
for file_info in file_data:
|
|
678
|
+
cyc_nums = file_info['cyc_nums']
|
|
679
|
+
cap_charge = file_info['cap_charge']
|
|
680
|
+
cap_discharge = file_info['cap_discharge']
|
|
681
|
+
eff = file_info['eff']
|
|
682
|
+
color = file_info['color'] # Warm color for capacity
|
|
683
|
+
eff_color = file_info['eff_color'] # Cold color for efficiency
|
|
684
|
+
label = file_info['filename']
|
|
685
|
+
|
|
686
|
+
# For single file, use simple labels; for multiple files, prefix with filename
|
|
687
|
+
if len(file_data) == 1:
|
|
688
|
+
label_chg = 'Charge capacity'
|
|
689
|
+
label_dch = 'Discharge capacity'
|
|
690
|
+
label_eff = 'Coulombic efficiency'
|
|
691
|
+
else:
|
|
692
|
+
label_chg = f'{label} (Chg)'
|
|
693
|
+
label_dch = f'{label} (Dch)'
|
|
694
|
+
label_eff = f'{label} (Eff)'
|
|
695
|
+
|
|
696
|
+
# Use slightly different shades for charge/discharge from same file
|
|
697
|
+
from matplotlib.colors import to_rgb
|
|
698
|
+
rgb = to_rgb(color)
|
|
699
|
+
# Discharge: darker shade of the warm color
|
|
700
|
+
discharge_color = tuple(max(0, c * 0.7) for c in rgb)
|
|
701
|
+
|
|
702
|
+
sc_charge = ax.scatter(cyc_nums, cap_charge, color=color, label=label_chg,
|
|
703
|
+
s=32, zorder=3, alpha=0.8, marker='o')
|
|
704
|
+
sc_discharge = ax.scatter(cyc_nums, cap_discharge, color=discharge_color, label=label_dch,
|
|
705
|
+
s=32, zorder=3, alpha=0.8, marker='s')
|
|
706
|
+
sc_eff = ax2.scatter(cyc_nums, eff, color=eff_color, marker='^', label=label_eff,
|
|
707
|
+
s=40, alpha=0.7, zorder=3)
|
|
708
|
+
|
|
709
|
+
# Store scatter artists in file_info for interactive menu
|
|
710
|
+
file_info['sc_charge'] = sc_charge
|
|
711
|
+
file_info['sc_discharge'] = sc_discharge
|
|
712
|
+
file_info['sc_eff'] = sc_eff
|
|
713
|
+
|
|
714
|
+
# Set efficiency y-range to 0-120 by default
|
|
715
|
+
ax2.set_ylim(0, 120)
|
|
716
|
+
|
|
717
|
+
# Compose a combined legend
|
|
651
718
|
try:
|
|
652
719
|
h1, l1 = ax.get_legend_handles_labels()
|
|
653
720
|
h2, l2 = ax2.get_legend_handles_labels()
|
|
@@ -655,7 +722,7 @@ def batplot_main() -> int:
|
|
|
655
722
|
except Exception:
|
|
656
723
|
pass
|
|
657
724
|
|
|
658
|
-
# Adjust layout to ensure top and bottom labels/titles are visible
|
|
725
|
+
# Adjust layout to ensure top and bottom labels/titles are visible
|
|
659
726
|
fig.subplots_adjust(left=0.12, right=0.88, top=0.88, bottom=0.15)
|
|
660
727
|
|
|
661
728
|
if args.interactive and cpc_interactive_menu is not None:
|
|
@@ -679,7 +746,20 @@ def batplot_main() -> int:
|
|
|
679
746
|
pass
|
|
680
747
|
plt.show(block=False)
|
|
681
748
|
try:
|
|
682
|
-
|
|
749
|
+
# Pass file_data for multi-file support, but keep backward compatibility
|
|
750
|
+
if len(file_data) == 1:
|
|
751
|
+
# Single file: use original signature
|
|
752
|
+
cpc_interactive_menu(fig, ax, ax2,
|
|
753
|
+
file_data[0]['sc_charge'],
|
|
754
|
+
file_data[0]['sc_discharge'],
|
|
755
|
+
file_data[0]['sc_eff'])
|
|
756
|
+
else:
|
|
757
|
+
# Multiple files: pass file_data list
|
|
758
|
+
cpc_interactive_menu(fig, ax, ax2,
|
|
759
|
+
file_data[0]['sc_charge'],
|
|
760
|
+
file_data[0]['sc_discharge'],
|
|
761
|
+
file_data[0]['sc_eff'],
|
|
762
|
+
file_data=file_data)
|
|
683
763
|
except Exception as _ie:
|
|
684
764
|
print(f"CPC interactive menu failed: {_ie}")
|
|
685
765
|
# Keep window open after menu
|
|
@@ -1185,9 +1265,9 @@ def batplot_main() -> int:
|
|
|
1185
1265
|
else:
|
|
1186
1266
|
# Keep canvas size as current; avoid surprising resize on load
|
|
1187
1267
|
pass
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1268
|
+
# Don't restore saved DPI - use system default to avoid display-dependent issues
|
|
1269
|
+
# (Retina displays, Windows scaling, etc. can cause saved DPI to differ)
|
|
1270
|
+
# Keeping figure size in inches ensures consistent appearance across platforms
|
|
1191
1271
|
except Exception:
|
|
1192
1272
|
pass
|
|
1193
1273
|
# Restore spines (linewidth, color, visibility) and subplot margins/tick widths (for CLI .pkl load)
|
|
File without changes
|