batplot 1.8.25__tar.gz → 1.8.27__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.8.25/batplot.egg-info → batplot-1.8.27}/PKG-INFO +1 -1
- {batplot-1.8.25 → batplot-1.8.27}/batplot/__init__.py +1 -1
- {batplot-1.8.25 → batplot-1.8.27}/batplot/args.py +18 -7
- {batplot-1.8.25 → batplot-1.8.27}/batplot/batch.py +145 -40
- {batplot-1.8.25 → batplot-1.8.27}/batplot/batplot.py +345 -76
- {batplot-1.8.25 → batplot-1.8.27}/batplot/cif.py +40 -23
- {batplot-1.8.25 → batplot-1.8.27}/batplot/color_utils.py +9 -4
- {batplot-1.8.25 → batplot-1.8.27}/batplot/cpc_interactive.py +915 -358
- {batplot-1.8.25 → batplot-1.8.27}/batplot/data/CHANGELOG.md +18 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/data/USER_MANUAL.md +37 -1
- {batplot-1.8.25 → batplot-1.8.27}/batplot/electrochem_interactive.py +693 -341
- {batplot-1.8.25 → batplot-1.8.27}/batplot/interactive.py +159 -147
- {batplot-1.8.25 → batplot-1.8.27}/batplot/modes.py +53 -13
- {batplot-1.8.25 → batplot-1.8.27}/batplot/operando.py +10 -10
- {batplot-1.8.25 → batplot-1.8.27}/batplot/operando_ec_interactive.py +243 -227
- {batplot-1.8.25 → batplot-1.8.27}/batplot/plotting.py +1 -1
- {batplot-1.8.25 → batplot-1.8.27}/batplot/readers.py +114 -12
- {batplot-1.8.25 → batplot-1.8.27}/batplot/session.py +161 -111
- {batplot-1.8.25 → batplot-1.8.27}/batplot/style.py +22 -12
- {batplot-1.8.25 → batplot-1.8.27}/batplot/utils.py +42 -15
- {batplot-1.8.25 → batplot-1.8.27}/batplot/version_check.py +3 -2
- {batplot-1.8.25 → batplot-1.8.27/batplot.egg-info}/PKG-INFO +1 -1
- batplot-1.8.27/batplot.egg-info/top_level.txt +1 -0
- {batplot-1.8.25 → batplot-1.8.27}/pyproject.toml +1 -1
- batplot-1.8.25/batplot.egg-info/top_level.txt +0 -2
- {batplot-1.8.25 → batplot-1.8.27}/LICENSE +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/MANIFEST.in +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/NOTICE +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/README.md +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/USER_MANUAL.md +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/cli.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/config.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/converters.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/dev_upgrade.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/manual.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot/ui.py +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot.egg-info/SOURCES.txt +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot.egg-info/dependency_links.txt +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot.egg-info/entry_points.txt +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/batplot.egg-info/requires.txt +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/setup.cfg +0 -0
- {batplot-1.8.25 → batplot-1.8.27}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: batplot
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.27
|
|
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
|
|
@@ -296,18 +296,28 @@ def _print_ec_help() -> None:
|
|
|
296
296
|
"Use --interactive for styling, colors, line widths, axis scales, etc.\n"
|
|
297
297
|
"GC from .mpt: requires active mass in mg to compute mAh g⁻¹.\n"
|
|
298
298
|
" batplot --gc file.mpt --mass 6.5 --interactive\n\n"
|
|
299
|
-
"GC from supported .csv: specific capacity
|
|
300
|
-
"
|
|
301
|
-
"
|
|
302
|
-
" batplot --
|
|
299
|
+
"GC from supported .csv: specific capacity read directly when available; use --mass for\n"
|
|
300
|
+
" Neware absolute-capacity files (Cycle Index / Step Index / DataPoint format).\n"
|
|
301
|
+
" batplot --gc file.csv\n"
|
|
302
|
+
" batplot --gc file.csv --mass 3.52 # Neware absolute-capacity CSV\n\n"
|
|
303
|
+
"Per-file mass: repeat --mass once per file that needs it, in file order.\n"
|
|
304
|
+
" batplot f1.mpt --mass 6.5 f2.csv f3.mpt --mass 7.0 --gc\n"
|
|
305
|
+
" batplot f1.csv --mass 3.52 f2.mpt --mass 5.0 --cpc\n"
|
|
306
|
+
" # Files without --mass between them use the global --mass value (or none)\n"
|
|
307
|
+
" # Single --mass applies to all files: batplot f1.mpt f2.mpt --gc --mass 7.0\n\n"
|
|
308
|
+
"dQ/dV from supported .csv (pre-calculated column or computed from GC data):\n"
|
|
309
|
+
" batplot --dqdv file.csv\n"
|
|
310
|
+
" batplot --dqdv file.csv --mass 3.52 # Neware absolute-capacity CSV\n\n"
|
|
303
311
|
"Cyclic voltammetry (CV) from .mpt or .txt: plots voltage vs current for each cycle.\n"
|
|
304
312
|
" batplot --cv file.mpt\n"
|
|
305
313
|
" batplot --cv file.txt\n\n"
|
|
306
314
|
"Capacity-per-cycle (CPC) with coulombic efficiency from .csv, .xlsx, or .mpt.\n"
|
|
307
315
|
"Supports multiple files with individual color customization:\n"
|
|
308
|
-
" batplot --cpc file.csv # Neware CSV\n"
|
|
316
|
+
" batplot --cpc file.csv # Neware CSV (specific capacity)\n"
|
|
317
|
+
" batplot --cpc file.csv --mass 3.52 # Neware absolute-capacity CSV\n"
|
|
309
318
|
" batplot --cpc file.xlsx # Landt/Lanhe Excel (Chinese tester)\n"
|
|
310
|
-
" batplot --cpc file.mpt --mass 1.2
|
|
319
|
+
" batplot --cpc file.mpt --mass 1.2 # Biologic MPT\n"
|
|
320
|
+
" batplot file1.csv --mass 3.52 file2.mpt --mass 1.2 --cpc # Per-file mass\n"
|
|
311
321
|
" batplot --cpc file1.csv file2.xlsx file3.mpt --mass 1.2 --interactive\n\n"
|
|
312
322
|
"Excel support: Landt/Lanhe (蓝电/蓝河) .xlsx files with Chinese headers:\n"
|
|
313
323
|
" Expected structure: Row 1=filename, Row 2=headers, Row 3+=data\n"
|
|
@@ -457,10 +467,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
457
467
|
parser.add_argument("--operando", "--contour", action="store_true", dest="operando", help=argparse.SUPPRESS)
|
|
458
468
|
parser.add_argument("--debug", action="store_true", help=argparse.SUPPRESS)
|
|
459
469
|
parser.add_argument("--gc", action="store_true", help=argparse.SUPPRESS)
|
|
460
|
-
parser.add_argument("--mass", type=float, help=argparse.SUPPRESS)
|
|
470
|
+
parser.add_argument("--mass", type=float, action='append', help=argparse.SUPPRESS)
|
|
461
471
|
parser.add_argument("--dqdv", action="store_true", help=argparse.SUPPRESS)
|
|
462
472
|
parser.add_argument("--cv", action="store_true", help=argparse.SUPPRESS)
|
|
463
473
|
parser.add_argument("--cpc", action="store_true", help=argparse.SUPPRESS)
|
|
474
|
+
parser.add_argument("--epc", action="store_true", help=argparse.SUPPRESS)
|
|
464
475
|
parser.add_argument("--pw", nargs=2, type=float, metavar=('V_MIN', 'V_MAX'),
|
|
465
476
|
help=argparse.SUPPRESS)
|
|
466
477
|
parser.add_argument("--cd", type=float, help=argparse.SUPPRESS)
|
|
@@ -4,9 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import json
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
import matplotlib.
|
|
7
|
+
from typing import Tuple, cast
|
|
8
|
+
|
|
9
|
+
import matplotlib.cm as cm # type: ignore[import]
|
|
10
|
+
import numpy as np # type: ignore[import]
|
|
11
|
+
import matplotlib.pyplot as plt # type: ignore[import]
|
|
10
12
|
|
|
11
13
|
from .readers import (
|
|
12
14
|
read_gr_file,
|
|
@@ -16,8 +18,26 @@ from .readers import (
|
|
|
16
18
|
read_mpt_file,
|
|
17
19
|
read_ec_csv_file,
|
|
18
20
|
read_ec_csv_dqdv_file,
|
|
21
|
+
compute_dqdv_numerical,
|
|
22
|
+
is_cs_b_format,
|
|
23
|
+
_load_csv_header_and_rows,
|
|
19
24
|
read_biologic_txt_file,
|
|
20
25
|
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_mass(mass_arg, file_idx: int = 0):
|
|
29
|
+
"""Return mass (mg) for file at file_idx from a --mass list or single value."""
|
|
30
|
+
if mass_arg is None:
|
|
31
|
+
return None
|
|
32
|
+
if isinstance(mass_arg, (int, float)):
|
|
33
|
+
return float(mass_arg)
|
|
34
|
+
if isinstance(mass_arg, list):
|
|
35
|
+
if len(mass_arg) == 1:
|
|
36
|
+
return float(mass_arg[0])
|
|
37
|
+
if file_idx < len(mass_arg):
|
|
38
|
+
return float(mass_arg[file_idx])
|
|
39
|
+
return float(mass_arg[-1])
|
|
40
|
+
return None
|
|
21
41
|
from .utils import _confirm_overwrite, natural_sort_key, ensure_subdirectory
|
|
22
42
|
|
|
23
43
|
|
|
@@ -886,6 +906,9 @@ def batch_process_ec(directory: str, args):
|
|
|
886
906
|
elif getattr(args, 'cpc', False):
|
|
887
907
|
mode = 'cpc'
|
|
888
908
|
supported_ext = {'.mpt', '.csv'}
|
|
909
|
+
elif getattr(args, 'epc', False):
|
|
910
|
+
mode = 'epc'
|
|
911
|
+
supported_ext = {'.mpt', '.csv'}
|
|
889
912
|
else:
|
|
890
913
|
print("EC batch mode requires one of: --gc, --cv, --dqdv, or --cpc")
|
|
891
914
|
return
|
|
@@ -964,9 +987,10 @@ def batch_process_ec(directory: str, args):
|
|
|
964
987
|
|
|
965
988
|
return colors[:n_colors] # Ensure exact count
|
|
966
989
|
|
|
967
|
-
for fname in files:
|
|
990
|
+
for _batch_file_idx, fname in enumerate(files):
|
|
968
991
|
fpath = os.path.join(directory, fname)
|
|
969
992
|
ext = os.path.splitext(fname)[1].lower()
|
|
993
|
+
mass_mg = _resolve_mass(getattr(args, 'mass', None), _batch_file_idx)
|
|
970
994
|
|
|
971
995
|
try:
|
|
972
996
|
fig_b, ax_b = plt.subplots(figsize=(6, 4))
|
|
@@ -974,13 +998,14 @@ def batch_process_ec(directory: str, args):
|
|
|
974
998
|
# ---- GC Mode ----
|
|
975
999
|
if mode == 'gc':
|
|
976
1000
|
if ext == '.mpt':
|
|
977
|
-
mass_mg = getattr(args, 'mass', None)
|
|
978
1001
|
if mass_mg is None:
|
|
979
1002
|
print(f" Skipped {fname}: GC mode (.mpt) requires --mass parameter")
|
|
980
1003
|
plt.close(fig_b)
|
|
981
1004
|
continue
|
|
982
|
-
specific_capacity, voltage, cycle_numbers, charge_mask, discharge_mask =
|
|
983
|
-
|
|
1005
|
+
specific_capacity, voltage, cycle_numbers, charge_mask, discharge_mask = cast(
|
|
1006
|
+
Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray],
|
|
1007
|
+
read_mpt_file(fpath, mode='gc', mass_mg=mass_mg),
|
|
1008
|
+
)
|
|
984
1009
|
cap_x = specific_capacity
|
|
985
1010
|
x_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
986
1011
|
elif ext == '.csv':
|
|
@@ -1005,14 +1030,19 @@ def batch_process_ec(directory: str, args):
|
|
|
1005
1030
|
|
|
1006
1031
|
# Generate color palette for the number of cycles
|
|
1007
1032
|
cycle_colors = get_color_palette(len(cycles_present))
|
|
1033
|
+
|
|
1034
|
+
# Ensure masks are boolean numpy arrays for safe logical operations
|
|
1035
|
+
charge_mask_arr = np.asarray(charge_mask, dtype=bool)
|
|
1036
|
+
discharge_mask_arr = np.asarray(discharge_mask, dtype=bool)
|
|
1008
1037
|
|
|
1009
1038
|
for idx, cyc in enumerate(cycles_present): # Plot all cycles
|
|
1010
1039
|
if cycle_numbers is not None:
|
|
1011
|
-
|
|
1012
|
-
|
|
1040
|
+
cyc_eq = (cyc_int == cyc)
|
|
1041
|
+
mask_c = cyc_eq & charge_mask_arr
|
|
1042
|
+
mask_d = cyc_eq & discharge_mask_arr
|
|
1013
1043
|
else:
|
|
1014
|
-
mask_c =
|
|
1015
|
-
mask_d =
|
|
1044
|
+
mask_c = charge_mask_arr
|
|
1045
|
+
mask_d = discharge_mask_arr
|
|
1016
1046
|
|
|
1017
1047
|
color = cycle_colors[idx]
|
|
1018
1048
|
|
|
@@ -1042,7 +1072,10 @@ def batch_process_ec(directory: str, args):
|
|
|
1042
1072
|
if ext == '.txt':
|
|
1043
1073
|
voltage, current, cycles = read_biologic_txt_file(fpath, mode='cv')
|
|
1044
1074
|
elif ext == '.mpt':
|
|
1045
|
-
voltage, current, cycles =
|
|
1075
|
+
voltage, current, cycles = cast(
|
|
1076
|
+
Tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
1077
|
+
read_mpt_file(fpath, mode='cv'),
|
|
1078
|
+
)
|
|
1046
1079
|
else:
|
|
1047
1080
|
raise ValueError("CV mode requires .mpt or .txt file")
|
|
1048
1081
|
|
|
@@ -1076,10 +1109,39 @@ def batch_process_ec(directory: str, args):
|
|
|
1076
1109
|
elif mode == 'dqdv':
|
|
1077
1110
|
if ext != '.csv':
|
|
1078
1111
|
raise ValueError("dQdV mode requires .csv file")
|
|
1079
|
-
|
|
1080
|
-
#
|
|
1081
|
-
|
|
1082
|
-
|
|
1112
|
+
|
|
1113
|
+
# Try to load pre-calculated dQ/dV columns; fall back to numerical computation
|
|
1114
|
+
_b_dqdv_header = None
|
|
1115
|
+
try:
|
|
1116
|
+
_b_dqdv_header, _, _ = _load_csv_header_and_rows(fpath)
|
|
1117
|
+
except Exception:
|
|
1118
|
+
pass
|
|
1119
|
+
|
|
1120
|
+
_b_loaded = False
|
|
1121
|
+
if not _b_loaded:
|
|
1122
|
+
try:
|
|
1123
|
+
voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = \
|
|
1124
|
+
read_ec_csv_dqdv_file(fpath, prefer_specific=True)
|
|
1125
|
+
_b_loaded = True
|
|
1126
|
+
except ValueError:
|
|
1127
|
+
pass
|
|
1128
|
+
|
|
1129
|
+
if not _b_loaded:
|
|
1130
|
+
_b_gc_cap, _b_gc_volt, _b_gc_cyc, _b_gc_chgm, _b_gc_dchm = \
|
|
1131
|
+
read_ec_csv_file(fpath, prefer_specific=True)
|
|
1132
|
+
_b_mass = mass_mg
|
|
1133
|
+
if _b_dqdv_header is not None:
|
|
1134
|
+
_b_hdrs = [h.strip().replace('\t', '') for h in _b_dqdv_header]
|
|
1135
|
+
_b_has_spec = any('Spec. Cap.(mAh/g)' in h for h in _b_hdrs)
|
|
1136
|
+
_b_has_abs = any(h == 'Capacity(mAh)' for h in _b_hdrs)
|
|
1137
|
+
if _b_has_abs and not _b_has_spec:
|
|
1138
|
+
if _b_mass and _b_mass > 0:
|
|
1139
|
+
_b_gc_cap = _b_gc_cap * (1000.0 / float(_b_mass))
|
|
1140
|
+
else:
|
|
1141
|
+
print(f"dQ/dV batch: {fname!r} — pass --mass <mg> for specific dQ/dV.")
|
|
1142
|
+
voltage, dqdv, cycles, charge_mask, discharge_mask, y_label = \
|
|
1143
|
+
compute_dqdv_numerical(_b_gc_cap, _b_gc_volt, _b_gc_cyc, _b_gc_chgm, _b_gc_dchm)
|
|
1144
|
+
print(f"dQ/dV batch: computing numerically from GC data for {fname!r}.")
|
|
1083
1145
|
|
|
1084
1146
|
# Process cycles similar to GC mode
|
|
1085
1147
|
if cycles is not None and cycles.size > 0:
|
|
@@ -1129,48 +1191,91 @@ def batch_process_ec(directory: str, args):
|
|
|
1129
1191
|
legend = ax_b.legend(loc='best', fontsize='small', framealpha=0.8, title='Cycle')
|
|
1130
1192
|
legend.get_title().set_fontsize('small')
|
|
1131
1193
|
|
|
1132
|
-
# ---- CPC Mode ----
|
|
1133
|
-
elif mode
|
|
1194
|
+
# ---- CPC / EPC Mode ----
|
|
1195
|
+
elif mode in ('cpc', 'epc'):
|
|
1134
1196
|
if ext == '.mpt':
|
|
1135
|
-
mass_mg = getattr(args, 'mass', None)
|
|
1136
1197
|
if mass_mg is None:
|
|
1137
|
-
print(f" Skipped {fname}:
|
|
1198
|
+
print(f" Skipped {fname}: {mode.upper()} mode (.mpt) requires --mass parameter")
|
|
1138
1199
|
plt.close(fig_b)
|
|
1139
1200
|
continue
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1201
|
+
if mode == 'cpc':
|
|
1202
|
+
cyc_nums, cap_charge, cap_discharge, _eff_dummy = cast(
|
|
1203
|
+
Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray],
|
|
1204
|
+
read_mpt_file(fpath, mode='cpc', mass_mg=mass_mg),
|
|
1205
|
+
)
|
|
1206
|
+
y_label = r'Specific Capacity (mAh g$^{-1}$)'
|
|
1207
|
+
else:
|
|
1208
|
+
cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = cast(
|
|
1209
|
+
Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray],
|
|
1210
|
+
read_mpt_file(fpath, mode='gc', mass_mg=mass_mg),
|
|
1211
|
+
)
|
|
1212
|
+
cyc_int_raw = np.array(np.rint(cycle_numbers), dtype=int)
|
|
1213
|
+
if cyc_int_raw.size:
|
|
1214
|
+
cycles_present = sorted(int(c) for c in np.unique(cyc_int_raw))
|
|
1215
|
+
en_charge = []
|
|
1216
|
+
en_discharge = []
|
|
1217
|
+
for cyc in cycles_present:
|
|
1218
|
+
mask_c = (cyc_int_raw == cyc) & charge_mask
|
|
1219
|
+
mask_d = (cyc_int_raw == cyc) & discharge_mask
|
|
1220
|
+
if np.count_nonzero(mask_c) >= 2:
|
|
1221
|
+
en_c = float(np.trapz(voltage[mask_c], cap_x[mask_c]))
|
|
1222
|
+
else:
|
|
1223
|
+
en_c = 0.0
|
|
1224
|
+
if np.count_nonzero(mask_d) >= 2:
|
|
1225
|
+
en_d = float(np.trapz(voltage[mask_d], cap_x[mask_d]))
|
|
1226
|
+
else:
|
|
1227
|
+
en_d = 0.0
|
|
1228
|
+
en_charge.append(en_c)
|
|
1229
|
+
en_discharge.append(en_d)
|
|
1230
|
+
cyc_nums = np.array(cycles_present)
|
|
1231
|
+
cap_charge = np.array(en_charge)
|
|
1232
|
+
cap_discharge = np.array(en_discharge)
|
|
1233
|
+
else:
|
|
1234
|
+
cyc_nums = np.array([1.0])
|
|
1235
|
+
cap_charge = np.array([0.0])
|
|
1236
|
+
cap_discharge = np.array([0.0])
|
|
1237
|
+
y_label = r'Specific Energy (mWh g$^{-1}$)'
|
|
1143
1238
|
elif ext == '.csv':
|
|
1144
1239
|
# For CSV CPC, read as GC-like data
|
|
1145
1240
|
cap_x, voltage, cycle_numbers, charge_mask, discharge_mask = \
|
|
1146
1241
|
read_ec_csv_file(fpath, prefer_specific=True)
|
|
1147
|
-
# Plot capacity vs cycle number
|
|
1148
1242
|
if cycle_numbers is not None:
|
|
1149
1243
|
cyc_int_raw = np.array(np.rint(cycle_numbers), dtype=int)
|
|
1150
1244
|
if cyc_int_raw.size:
|
|
1151
1245
|
cycles_present = sorted(int(c) for c in np.unique(cyc_int_raw))
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
cap_discharge = []
|
|
1246
|
+
cap_charge_list = []
|
|
1247
|
+
cap_discharge_list = []
|
|
1155
1248
|
for cyc in cycles_present:
|
|
1156
1249
|
mask_c = (cyc_int_raw == cyc) & charge_mask
|
|
1157
1250
|
mask_d = (cyc_int_raw == cyc) & discharge_mask
|
|
1158
|
-
|
|
1159
|
-
|
|
1251
|
+
if mode == 'cpc':
|
|
1252
|
+
val_c = np.max(cap_x[mask_c]) if np.any(mask_c) else 0.0
|
|
1253
|
+
val_d = np.max(cap_x[mask_d]) if np.any(mask_d) else 0.0
|
|
1254
|
+
else:
|
|
1255
|
+
if np.count_nonzero(mask_c) >= 2:
|
|
1256
|
+
val_c = float(np.trapz(voltage[mask_c], cap_x[mask_c]))
|
|
1257
|
+
else:
|
|
1258
|
+
val_c = 0.0
|
|
1259
|
+
if np.count_nonzero(mask_d) >= 2:
|
|
1260
|
+
val_d = float(np.trapz(voltage[mask_d], cap_x[mask_d]))
|
|
1261
|
+
else:
|
|
1262
|
+
val_d = 0.0
|
|
1263
|
+
cap_charge_list.append(val_c)
|
|
1264
|
+
cap_discharge_list.append(val_d)
|
|
1160
1265
|
cyc_nums = np.array(cycles_present)
|
|
1161
|
-
cap_charge = np.array(
|
|
1162
|
-
cap_discharge = np.array(
|
|
1266
|
+
cap_charge = np.array(cap_charge_list)
|
|
1267
|
+
cap_discharge = np.array(cap_discharge_list)
|
|
1163
1268
|
else:
|
|
1164
|
-
cyc_nums = np.array([1])
|
|
1165
|
-
cap_charge = np.array([0])
|
|
1166
|
-
cap_discharge = np.array([0])
|
|
1269
|
+
cyc_nums = np.array([1.0])
|
|
1270
|
+
cap_charge = np.array([0.0])
|
|
1271
|
+
cap_discharge = np.array([0.0])
|
|
1167
1272
|
else:
|
|
1168
|
-
cyc_nums = np.array([1])
|
|
1169
|
-
cap_charge = np.array([0])
|
|
1170
|
-
cap_discharge = np.array([0])
|
|
1171
|
-
|
|
1273
|
+
cyc_nums = np.array([1.0])
|
|
1274
|
+
cap_charge = np.array([0.0])
|
|
1275
|
+
cap_discharge = np.array([0.0])
|
|
1276
|
+
y_label = r'Specific Capacity (mAh g$^{-1}$)' if mode == 'cpc' else r'Specific Energy (mWh g$^{-1}$)'
|
|
1172
1277
|
else:
|
|
1173
|
-
raise ValueError(f"Unsupported file type for
|
|
1278
|
+
raise ValueError(f"Unsupported file type for {mode.upper()}: {ext}")
|
|
1174
1279
|
|
|
1175
1280
|
# Plot CPC data
|
|
1176
1281
|
ax_b.plot(cyc_nums, cap_charge, 'o-', color='#1f77b4',
|
|
@@ -1178,7 +1283,7 @@ def batch_process_ec(directory: str, args):
|
|
|
1178
1283
|
ax_b.plot(cyc_nums, cap_discharge, 's-', color='#ff7f0e',
|
|
1179
1284
|
linewidth=1.5, markersize=4, label='Discharge', alpha=0.8)
|
|
1180
1285
|
ax_b.set_xlabel('Cycle Number')
|
|
1181
|
-
ax_b.set_ylabel(
|
|
1286
|
+
ax_b.set_ylabel(y_label)
|
|
1182
1287
|
ax_b.legend()
|
|
1183
1288
|
ax_b.set_title(f"{fname}")
|
|
1184
1289
|
|