gmxplot 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
gmxplot/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """gmxplot - GROMACS XVG file visualization and statistics tool."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
gmxplot/comparison.py CHANGED
@@ -57,15 +57,26 @@ def _compare_single(datasets: list, labels: list, style: StyleOptions) -> plt.Fi
57
57
  multi = len(ds0.y_cols) > 1
58
58
  colors = resolve_palette(style.palette, len(datasets))
59
59
  fig, ax = plt.subplots(figsize=style.figsize or FIGURE_SIZE_SINGLE)
60
- for col in ds0.y_cols:
60
+ if not multi:
61
+ # One series per file: match by position, not by column name. GROMACS
62
+ # names the legend after the selection, so files that differ only in the
63
+ # selection (ZF1-DNA, ZF2-DNA, ...) carry different column names and
64
+ # only the first file would be drawn.
61
65
  for i, ds in enumerate(datasets):
62
- if col not in ds.y_cols:
66
+ if not ds.y_cols:
63
67
  continue
64
- color = colors[i]
65
- label = labels[i] if labels else str(i)
66
- if multi:
68
+ _overlay_line(ax, ds, ds.y_cols[0], colors[i],
69
+ labels[i] if labels else str(i), style,
70
+ _effective_roll(ds, None))
71
+ else:
72
+ for col in ds0.y_cols:
73
+ for i, ds in enumerate(datasets):
74
+ if col not in ds.y_cols:
75
+ continue
76
+ color = colors[i]
77
+ label = labels[i] if labels else str(i)
67
78
  label = f'{label} {col}'
68
- _overlay_line(ax, ds, col, color, label, style, _effective_roll(ds, None))
79
+ _overlay_line(ax, ds, col, color, label, style, _effective_roll(ds, None))
69
80
  ax.set_xlabel(_x_title(ds0, style))
70
81
  ax.set_ylabel(style.ylabel or ds0.meta.y_label or '')
71
82
  ax.set_title(style.title or ds0.meta.title)
gmxplot/plotting.py CHANGED
@@ -97,16 +97,47 @@ def _make_axes(n_panels: int, figsize: Optional[Tuple[float, float]] = None):
97
97
  return fig, axes_flat, n_cols
98
98
 
99
99
 
100
+ # GROMACS writes ONE y-axis label for a whole multi-column file and merges
101
+ # repeated units: energy.xvg has 6 columns but the label only lists five
102
+ # "(...)" groups. A purely positional assignment therefore shifts every unit
103
+ # after the duplicate (Total Energy gets K, Temperature gets bar, ...), so fall
104
+ # back to per-term units when the counts do not line up.
105
+ _TERM_UNITS = [
106
+ (r'\bdensity\b', 'kg/m^3'),
107
+ (r'\bpressure\b|\bpres\.|\bvir-', 'bar'),
108
+ (r'\btemperature\b|^t-', 'K'),
109
+ (r'\bbox-[xyz]\b', 'nm'),
110
+ (r'\bvolume\b', 'nm^3'),
111
+ (r'\bpotential\b|\bkinetic\b|\btotal energy\b|\benthalpy\b|\bgibbs\b',
112
+ 'kJ/mol'),
113
+ (r'\bcoul|\blj-|\bdisper|\bbond|\bangle\b|\bdihedral|\bimproper|\bvdw',
114
+ 'kJ/mol'),
115
+ ]
116
+
117
+
118
+ def _unit_for_column(col: str, units: List[str]) -> Optional[str]:
119
+ """Resolve a unit for one column when the file's unit list is incomplete."""
120
+ label = clean_gromacs_label(col).lower()
121
+ for pattern, unit in _TERM_UNITS:
122
+ if re.search(pattern, label):
123
+ return unit
124
+ # A single unit for the whole file applies to every column.
125
+ return units[0] if len(units) == 1 else None
126
+
127
+
100
128
  def _y_titles(ds: Dataset) -> dict:
101
129
  """Map each y column to a display label with units when available."""
102
- units = re.findall(r'\(([^()]+)\)', ds.meta.y_label)
130
+ units = [clean_gromacs_label(u)
131
+ for u in re.findall(r'\(([^()]+)\)', ds.meta.y_label)]
103
132
  out = {}
104
- for i, col in enumerate(ds.y_cols):
133
+ if units and len(units) == len(ds.y_cols):
134
+ for col, unit in zip(ds.y_cols, units):
135
+ out[col] = f'{clean_gromacs_label(col)} ({unit})'
136
+ return out
137
+ for col in ds.y_cols:
105
138
  base = clean_gromacs_label(col)
106
- if i < len(units):
107
- out[col] = f'{base} ({clean_gromacs_label(units[i])})'
108
- else:
109
- out[col] = base
139
+ unit = _unit_for_column(col, units)
140
+ out[col] = f'{base} ({unit})' if unit else base
110
141
  return out
111
142
 
112
143
 
gmxplot/stats.py CHANGED
@@ -8,6 +8,7 @@ count-weighted x statistics; PCA analyzes every column (there is no x column).
8
8
  import math
9
9
  import os
10
10
  import re
11
+ from dataclasses import replace
11
12
  from pathlib import Path
12
13
  from typing import Dict, List, Optional
13
14
 
@@ -243,11 +244,10 @@ def generate_stats_csv(
243
244
  df, meta = read_xvg(fp)
244
245
  if df.empty:
245
246
  continue
246
- o = opts or DatasetOptions()
247
- if not o.label:
248
- o.label = os.path.basename(fp)
249
- if not o.path:
250
- o.path = fp
247
+ # Per-file copy: mutating a shared options object would pin every later
248
+ # row to the first file's label/path.
249
+ label = os.path.basename(fp)
250
+ o = replace(opts, label=label, path=fp) if opts else DatasetOptions(label=label, path=fp)
251
251
  ds = prepare_dataset(df, meta, o)
252
252
  bs = block_size
253
253
  if block_time is not None and bs is None and ds.x_col is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gmxplot
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: GROMACS XVG visualization and statistics with family/kind semantics, physical time axes, windows, replica aggregation, and dashboards
5
5
  Project-URL: Homepage, https://github.com/MaybeBio/gmxplot
6
6
  Project-URL: Issues, https://github.com/MaybeBio/gmxplot/issues
@@ -137,6 +137,12 @@ average and drift statistics. Nothing is smoothed by accident.
137
137
 
138
138
  ## 📦 Installation
139
139
 
140
+ ```bash
141
+ pip install gmxplot
142
+ ```
143
+
144
+ or
145
+
140
146
  ```bash
141
147
  git clone https://github.com/MaybeBio/gmxplot.git
142
148
  cd gmxplot
@@ -0,0 +1,16 @@
1
+ gmxplot/__init__.py,sha256=TThvsrSaI7Y7g1-8vfoj4C14rtS9TCRlCmfNezKUbSo,91
2
+ gmxplot/cli.py,sha256=Z1rdBQ_5swjaXtWzHoodq9JXe0gp0L9pp8-UmTf9L7o,34304
3
+ gmxplot/comparison.py,sha256=wVFmaUYgph9VjtsD8pTT4c3x35ACHB-UiAoMOFbKFEY,5368
4
+ gmxplot/config.py,sha256=9IGso6QluhPKtP204HJjqUUnpq5LVfZcPJTBgeGo190,2696
5
+ gmxplot/dashboard.py,sha256=8tbqgA-o_pYK8DNMusQUeqSp-29XPxt6kgd8b1zFABI,10766
6
+ gmxplot/dataset.py,sha256=bf-823OkOVo1XGCqadj-J5RaRf9genHIibDqdVDXJMc,9399
7
+ gmxplot/escape_codes.py,sha256=HR9F-sy_Jq4NOb74I7bZgUx6svjy2xYHMYTPQRBxBk4,1831
8
+ gmxplot/parser.py,sha256=oijRpBjucRzlMm8OcI4GAkYA6jQAaQ4lzZQZrRwdz7k,10308
9
+ gmxplot/plotting.py,sha256=ujWfJJYAzSaU9xnSO-G48XZIsJci5ajX5E3SkzBMo88,14653
10
+ gmxplot/replicas.py,sha256=eAp3yFZlg1FiY7YVFY7MFYvoXSIPMznj5GjuonVqmkM,7769
11
+ gmxplot/stats.py,sha256=ZuQbdPj4dV2XQfWD9FJZXBSZPFuc478xG7Efjo70Fnk,11496
12
+ gmxplot-0.2.2.dist-info/METADATA,sha256=kgFqDo-8Dqu_vWG69A71O3yrBGYfXYMFj_pevYQG1tg,21701
13
+ gmxplot-0.2.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
14
+ gmxplot-0.2.2.dist-info/entry_points.txt,sha256=ENtnfYYCM2sAELFTNdOc2lhOPna_ndmXImWPONbAKRA,44
15
+ gmxplot-0.2.2.dist-info/licenses/LICENSE,sha256=8-mfygEBYs_zUebQalb4qE5TIk8ni9ERmb9g52GWED0,308
16
+ gmxplot-0.2.2.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- gmxplot/__init__.py,sha256=FGkyYOsuFfy3yx5O9WvX5Q5rSvLfpr7x9Pxp8G3koGg,91
2
- gmxplot/cli.py,sha256=Z1rdBQ_5swjaXtWzHoodq9JXe0gp0L9pp8-UmTf9L7o,34304
3
- gmxplot/comparison.py,sha256=gYpIhKin5R7JonYmvC_Bvsaz2pEiV1xYAy6O-ldZaAU,4776
4
- gmxplot/config.py,sha256=9IGso6QluhPKtP204HJjqUUnpq5LVfZcPJTBgeGo190,2696
5
- gmxplot/dashboard.py,sha256=8tbqgA-o_pYK8DNMusQUeqSp-29XPxt6kgd8b1zFABI,10766
6
- gmxplot/dataset.py,sha256=bf-823OkOVo1XGCqadj-J5RaRf9genHIibDqdVDXJMc,9399
7
- gmxplot/escape_codes.py,sha256=HR9F-sy_Jq4NOb74I7bZgUx6svjy2xYHMYTPQRBxBk4,1831
8
- gmxplot/parser.py,sha256=oijRpBjucRzlMm8OcI4GAkYA6jQAaQ4lzZQZrRwdz7k,10308
9
- gmxplot/plotting.py,sha256=nSVjE3M02XLWNhOdpcTaHwtCZ1gotXWqhALJW9KoMpY,13334
10
- gmxplot/replicas.py,sha256=eAp3yFZlg1FiY7YVFY7MFYvoXSIPMznj5GjuonVqmkM,7769
11
- gmxplot/stats.py,sha256=2c2V6E6v03PtQEGu6nt3_IdLQO-Q2kGrEnHd6snox6g,11354
12
- gmxplot-0.2.0.dist-info/METADATA,sha256=RKNLi8WBDf54lLiZTq5OFaoKS8ecT7PFJc_dHrKP-xc,21664
13
- gmxplot-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
14
- gmxplot-0.2.0.dist-info/entry_points.txt,sha256=ENtnfYYCM2sAELFTNdOc2lhOPna_ndmXImWPONbAKRA,44
15
- gmxplot-0.2.0.dist-info/licenses/LICENSE,sha256=8-mfygEBYs_zUebQalb4qE5TIk8ni9ERmb9g52GWED0,308
16
- gmxplot-0.2.0.dist-info/RECORD,,