jarvisplot 1.0.1__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.
- jarvisplot/Figure/adapters.py +773 -0
- jarvisplot/Figure/cards/std_axes_adapter_config.json +23 -0
- jarvisplot/Figure/data_pipelines.py +87 -0
- jarvisplot/Figure/figure.py +1573 -0
- jarvisplot/Figure/helper.py +217 -0
- jarvisplot/Figure/load_data.py +252 -0
- jarvisplot/__init__.py +0 -0
- jarvisplot/cards/a4paper/1x1/ternary.json +6 -0
- jarvisplot/cards/a4paper/2x1/rect.json +106 -0
- jarvisplot/cards/a4paper/2x1/rect5x1.json +344 -0
- jarvisplot/cards/a4paper/2x1/rect_cmap.json +181 -0
- jarvisplot/cards/a4paper/2x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/2x1/ternary_cmap.json +189 -0
- jarvisplot/cards/a4paper/4x1/rect.json +106 -0
- jarvisplot/cards/a4paper/4x1/rect_cmap.json +174 -0
- jarvisplot/cards/a4paper/4x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/4x1/ternary_cmap.json +189 -0
- jarvisplot/cards/args.json +50 -0
- jarvisplot/cards/colors/colormaps.json +140 -0
- jarvisplot/cards/default/output.json +11 -0
- jarvisplot/cards/gambit/1x1/ternary.json +6 -0
- jarvisplot/cards/gambit/2x1/rect_cmap.json +200 -0
- jarvisplot/cards/gambit/2x1/ternary.json +139 -0
- jarvisplot/cards/gambit/2x1/ternary_cmap.json +205 -0
- jarvisplot/cards/icons/JarvisHEP.png +0 -0
- jarvisplot/cards/icons/gambit.png +0 -0
- jarvisplot/cards/icons/gambit_small.png +0 -0
- jarvisplot/cards/style_preference.json +23 -0
- jarvisplot/cli.py +64 -0
- jarvisplot/client.py +6 -0
- jarvisplot/config.py +69 -0
- jarvisplot/core.py +237 -0
- jarvisplot/data_loader.py +441 -0
- jarvisplot/inner_func.py +162 -0
- jarvisplot/utils/__init__.py +0 -0
- jarvisplot/utils/cmaps.py +258 -0
- jarvisplot/utils/interpolator.py +377 -0
- jarvisplot-1.0.1.dist-info/METADATA +80 -0
- jarvisplot-1.0.1.dist-info/RECORD +42 -0
- jarvisplot-1.0.1.dist-info/WHEEL +5 -0
- jarvisplot-1.0.1.dist-info/entry_points.txt +2 -0
- jarvisplot-1.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# src/utils/interpolator.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Callable, Dict, Optional, Tuple, Literal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
BoundsMode = Literal["clamp", "extrapolate", "nan", "error"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class CSVSourceSpec:
|
|
14
|
+
"""Where to read x/y pairs from."""
|
|
15
|
+
path: str # relative to YAML dir or absolute
|
|
16
|
+
x: str # column name (or index key later)
|
|
17
|
+
y: str # column name
|
|
18
|
+
sort_by: Optional[str] = None
|
|
19
|
+
drop_duplicates: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class Interp1DSpec:
|
|
24
|
+
"""Interpolation method + options."""
|
|
25
|
+
kind: str = "linear"
|
|
26
|
+
bounds: BoundsMode = "clamp"
|
|
27
|
+
fill_value: Optional[float] = None
|
|
28
|
+
assume_sorted: bool = False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class InterpolatorSpec:
|
|
33
|
+
"""Single named interpolator spec parsed from YAML."""
|
|
34
|
+
name: str
|
|
35
|
+
source: CSVSourceSpec
|
|
36
|
+
method: str = "interp1d" # keep extensible: "interp1d", "pchip", ...
|
|
37
|
+
options: Interp1DSpec = Interp1DSpec()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class LazyCallable:
|
|
41
|
+
"""
|
|
42
|
+
A callable wrapper that builds the real function on first call.
|
|
43
|
+
|
|
44
|
+
IMPORTANT: This is what you inject into eval globals so Figure/layer code
|
|
45
|
+
doesn't need to know about lazy loading.
|
|
46
|
+
"""
|
|
47
|
+
def __init__(self, name: str, builder: Callable[[], Callable], logger=None):
|
|
48
|
+
self._name = name
|
|
49
|
+
self._builder = builder
|
|
50
|
+
self._logger = logger
|
|
51
|
+
self._fn: Optional[Callable] = None
|
|
52
|
+
|
|
53
|
+
def _ensure(self) -> Callable:
|
|
54
|
+
if self._fn is None:
|
|
55
|
+
if self._logger:
|
|
56
|
+
self._logger.debug(f"LazyLoad: building interpolator '{self._name}'")
|
|
57
|
+
self._fn = self._builder()
|
|
58
|
+
return self._fn
|
|
59
|
+
|
|
60
|
+
def __call__(self, *args, **kwargs):
|
|
61
|
+
fn = self._ensure()
|
|
62
|
+
return fn(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class InterpolatorManager:
|
|
66
|
+
"""
|
|
67
|
+
Parses YAML 'Functions' and provides lazy-callable interpolators.
|
|
68
|
+
|
|
69
|
+
- Parsing stage: stores specs only (no IO, no scipy work)
|
|
70
|
+
- Runtime stage: first call triggers CSV read + interpolator build
|
|
71
|
+
- Cache: built callables are cached for reuse in the same run
|
|
72
|
+
|
|
73
|
+
Suggested usage:
|
|
74
|
+
mgr = InterpolatorManager.from_yaml(cfg, yaml_dir, shared, logger)
|
|
75
|
+
funcs_dict = mgr.as_eval_funcs() # inject into expression globals
|
|
76
|
+
# expression can call f_cut(x) directly
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, yaml_dir: Path, shared=None, logger=None):
|
|
80
|
+
self._yaml_dir = Path(yaml_dir)
|
|
81
|
+
self._shared = shared
|
|
82
|
+
self._logger = logger
|
|
83
|
+
|
|
84
|
+
self._specs: Dict[str, InterpolatorSpec] = {}
|
|
85
|
+
self._lazy: Dict[str, LazyCallable] = {}
|
|
86
|
+
self._built: Dict[str, Callable] = {}
|
|
87
|
+
|
|
88
|
+
# -------------------------
|
|
89
|
+
# YAML parsing (no IO)
|
|
90
|
+
# -------------------------
|
|
91
|
+
@classmethod
|
|
92
|
+
def from_yaml(
|
|
93
|
+
cls,
|
|
94
|
+
config: Any,
|
|
95
|
+
yaml_dir: str | Path,
|
|
96
|
+
shared=None,
|
|
97
|
+
logger=None,
|
|
98
|
+
key: str = "Functions",
|
|
99
|
+
) -> "InterpolatorManager":
|
|
100
|
+
"""
|
|
101
|
+
Parse config[key] list into specs and register lazy wrappers.
|
|
102
|
+
|
|
103
|
+
Accepts either the full YAML config dict (with top-level key 'Functions'), or
|
|
104
|
+
directly a list (cfg['Functions']).
|
|
105
|
+
"""
|
|
106
|
+
mgr = cls(yaml_dir=Path(yaml_dir), shared=shared, logger=logger)
|
|
107
|
+
|
|
108
|
+
# Accept either the full YAML config dict (preferred) or directly a list (cfg['Functions']).
|
|
109
|
+
if isinstance(config, list):
|
|
110
|
+
items = config
|
|
111
|
+
elif isinstance(config, dict):
|
|
112
|
+
items = config.get(key, []) or []
|
|
113
|
+
else:
|
|
114
|
+
items = []
|
|
115
|
+
|
|
116
|
+
for item in items:
|
|
117
|
+
spec = mgr._parse_one(item)
|
|
118
|
+
mgr.register(spec)
|
|
119
|
+
return mgr
|
|
120
|
+
|
|
121
|
+
def _parse_one(self, item: Dict[str, Any]) -> InterpolatorSpec:
|
|
122
|
+
"""
|
|
123
|
+
Convert a single YAML dict -> InterpolatorSpec.
|
|
124
|
+
|
|
125
|
+
Expected YAML shape (example):
|
|
126
|
+
- name: f_cut
|
|
127
|
+
source: {type: csv, path: ./a.csv, x: x, y: y, sort_by: x, drop_duplicates: x}
|
|
128
|
+
method: interp1d
|
|
129
|
+
options: {kind: linear, bounds: clamp, fill_value: null, assume_sorted: false}
|
|
130
|
+
|
|
131
|
+
Top-level YAML key is 'Functions'.
|
|
132
|
+
"""
|
|
133
|
+
# NOTE: only parse + validate keys here; do NOT read files.
|
|
134
|
+
if not isinstance(item, dict):
|
|
135
|
+
raise TypeError(f"Function spec must be a dict, got {type(item)}")
|
|
136
|
+
|
|
137
|
+
name = item.get("name", None)
|
|
138
|
+
if not name:
|
|
139
|
+
raise ValueError("Function spec missing required field 'name'")
|
|
140
|
+
|
|
141
|
+
source = item.get("source", None)
|
|
142
|
+
if not isinstance(source, dict):
|
|
143
|
+
raise ValueError(f"Function '{name}': missing/invalid 'source'")
|
|
144
|
+
|
|
145
|
+
stype = source.get("type", None)
|
|
146
|
+
if stype != "csv":
|
|
147
|
+
raise ValueError(f"Function '{name}': only source.type='csv' is supported (got {stype!r})")
|
|
148
|
+
|
|
149
|
+
path = source.get("path", None)
|
|
150
|
+
xcol = source.get("x", None)
|
|
151
|
+
ycol = source.get("y", None)
|
|
152
|
+
if not path or xcol is None or ycol is None:
|
|
153
|
+
raise ValueError(f"Function '{name}': source requires 'path', 'x', 'y'")
|
|
154
|
+
|
|
155
|
+
src = CSVSourceSpec(
|
|
156
|
+
path=str(path),
|
|
157
|
+
x=str(xcol),
|
|
158
|
+
y=str(ycol),
|
|
159
|
+
sort_by=source.get("sort_by", None),
|
|
160
|
+
drop_duplicates=source.get("drop_duplicates", None),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
method = item.get("method", "interp1d")
|
|
164
|
+
if method != "interp1d":
|
|
165
|
+
raise ValueError(f"Function '{name}': only method='interp1d' is supported (got {method!r})")
|
|
166
|
+
|
|
167
|
+
opt0 = item.get("options", {}) or {}
|
|
168
|
+
if not isinstance(opt0, dict):
|
|
169
|
+
raise ValueError(f"Function '{name}': options must be a dict")
|
|
170
|
+
|
|
171
|
+
opt = Interp1DSpec(
|
|
172
|
+
kind=str(opt0.get("kind", "linear")),
|
|
173
|
+
bounds=str(opt0.get("bounds", "clamp")),
|
|
174
|
+
fill_value=opt0.get("fill_value", None),
|
|
175
|
+
assume_sorted=bool(opt0.get("assume_sorted", False)),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if opt.kind != "linear":
|
|
179
|
+
raise ValueError(f"Function '{name}': only options.kind='linear' is supported (got {opt.kind!r})")
|
|
180
|
+
|
|
181
|
+
return InterpolatorSpec(name=name, source=src, method=method, options=opt)
|
|
182
|
+
|
|
183
|
+
# -------------------------
|
|
184
|
+
# Registry
|
|
185
|
+
# -------------------------
|
|
186
|
+
def register(self, spec: InterpolatorSpec) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Register a spec and create a LazyCallable wrapper for it.
|
|
189
|
+
"""
|
|
190
|
+
name = spec.name
|
|
191
|
+
if name in self._specs:
|
|
192
|
+
raise ValueError(f"Interpolator '{name}' already registered.")
|
|
193
|
+
self._specs[name] = spec
|
|
194
|
+
|
|
195
|
+
# wrap a builder closure; builder will do IO + build actual callable
|
|
196
|
+
self._lazy[name] = LazyCallable(name, builder=lambda n=name: self._build(n), logger=self._logger)
|
|
197
|
+
|
|
198
|
+
def get(self, name: str) -> Callable:
|
|
199
|
+
"""
|
|
200
|
+
Return callable for this interpolator. (LazyCallable until built.)
|
|
201
|
+
"""
|
|
202
|
+
if name in self._built:
|
|
203
|
+
return self._built[name]
|
|
204
|
+
if name in self._lazy:
|
|
205
|
+
return self._lazy[name]
|
|
206
|
+
raise KeyError(f"Unknown interpolator '{name}'")
|
|
207
|
+
|
|
208
|
+
def as_eval_funcs(self) -> Dict[str, Callable]:
|
|
209
|
+
"""
|
|
210
|
+
Dict suitable for injection into expression evaluation globals.
|
|
211
|
+
Keys are interpolator names; values are LazyCallable instances.
|
|
212
|
+
"""
|
|
213
|
+
return dict(self._lazy)
|
|
214
|
+
|
|
215
|
+
def summary(self) -> Dict[str, Any]:
|
|
216
|
+
"""
|
|
217
|
+
Small diagnostic summary: registered / built status.
|
|
218
|
+
"""
|
|
219
|
+
return {
|
|
220
|
+
"registered": sorted(self._specs.keys()),
|
|
221
|
+
"built": sorted(self._built.keys()),
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# -------------------------
|
|
225
|
+
# Lazy build (IO + compute)
|
|
226
|
+
# -------------------------
|
|
227
|
+
def _build(self, name: str) -> Callable:
|
|
228
|
+
"""
|
|
229
|
+
Build the real interpolator callable for `name`, cache it, and return it.
|
|
230
|
+
|
|
231
|
+
Responsibilities:
|
|
232
|
+
- read (x, y) from CSV (may use shared cache)
|
|
233
|
+
- construct interpolator according to method/options
|
|
234
|
+
- return a vectorized callable f(x) -> y
|
|
235
|
+
"""
|
|
236
|
+
if name in self._built:
|
|
237
|
+
return self._built[name]
|
|
238
|
+
if name not in self._specs:
|
|
239
|
+
raise KeyError(f"Unknown interpolator '{name}'")
|
|
240
|
+
|
|
241
|
+
spec = self._specs[name]
|
|
242
|
+
|
|
243
|
+
# Load data
|
|
244
|
+
x, y = self._load_csv_xy(spec.source)
|
|
245
|
+
|
|
246
|
+
# Build callable
|
|
247
|
+
if spec.method != "interp1d":
|
|
248
|
+
raise ValueError(f"Function '{name}': unsupported method {spec.method!r}")
|
|
249
|
+
|
|
250
|
+
fn = self._make_interp1d(x, y, spec.options)
|
|
251
|
+
self._built[name] = fn
|
|
252
|
+
return fn
|
|
253
|
+
|
|
254
|
+
# -------------------------
|
|
255
|
+
# Helpers (placeholders)
|
|
256
|
+
# -------------------------
|
|
257
|
+
def _resolve_path(self, p: str) -> Path:
|
|
258
|
+
"""
|
|
259
|
+
Resolve YAML-relative path or absolute path.
|
|
260
|
+
"""
|
|
261
|
+
p = str(p)
|
|
262
|
+
if p.startswith("&JP/"):
|
|
263
|
+
# allow internal JP paths if needed; resolve relative to project root (yaml_dir's parent)
|
|
264
|
+
# but keep simple: treat as relative to yaml_dir for now.
|
|
265
|
+
p = p[4:]
|
|
266
|
+
path = Path(p).expanduser()
|
|
267
|
+
if not path.is_absolute():
|
|
268
|
+
path = (self._yaml_dir / path).resolve()
|
|
269
|
+
return path
|
|
270
|
+
|
|
271
|
+
def _load_csv_xy(self, spec: CSVSourceSpec) -> Tuple[Any, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Read/clean CSV and return arrays (x, y).
|
|
274
|
+
"""
|
|
275
|
+
import pandas as _pd
|
|
276
|
+
import numpy as _np
|
|
277
|
+
|
|
278
|
+
csv_path = self._resolve_path(spec.path)
|
|
279
|
+
df = _pd.read_csv(csv_path)
|
|
280
|
+
|
|
281
|
+
# Optional sort/dedup on a specified column name
|
|
282
|
+
if spec.sort_by is not None and spec.sort_by in df.columns:
|
|
283
|
+
df = df.sort_values(by=spec.sort_by)
|
|
284
|
+
|
|
285
|
+
if spec.drop_duplicates is not None and spec.drop_duplicates in df.columns:
|
|
286
|
+
df = df.drop_duplicates(subset=spec.drop_duplicates, keep="first")
|
|
287
|
+
|
|
288
|
+
# Allow spec.x/spec.y to be either column names OR python expressions.
|
|
289
|
+
import math as _math
|
|
290
|
+
from ..inner_func import update_funcs as _update_funcs
|
|
291
|
+
|
|
292
|
+
local_vars = df.to_dict("series")
|
|
293
|
+
allowed_globals = _update_funcs({"np": _np, "math": _math})
|
|
294
|
+
|
|
295
|
+
def _eval_field(field: str):
|
|
296
|
+
if field in df.columns:
|
|
297
|
+
return _np.asarray(df[field].values, dtype=float)
|
|
298
|
+
# treat as expression
|
|
299
|
+
try:
|
|
300
|
+
arr = eval(field, allowed_globals, local_vars)
|
|
301
|
+
return _np.asarray(arr, dtype=float)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"CSV {csv_path}: cannot evaluate field '{field}'. "
|
|
305
|
+
f"Not a column and eval failed: {e}"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
x = _eval_field(spec.x)
|
|
309
|
+
y = _eval_field(spec.y)
|
|
310
|
+
|
|
311
|
+
if x.shape != y.shape:
|
|
312
|
+
raise ValueError(f"CSV {csv_path}: evaluated x/y have different shapes: {x.shape} vs {y.shape}")
|
|
313
|
+
|
|
314
|
+
# Ensure x is sorted for interpolation unless explicitly assumed sorted.
|
|
315
|
+
# Even if user sorted by another column, we still need monotonic x.
|
|
316
|
+
order = _np.argsort(x)
|
|
317
|
+
x = x[order]
|
|
318
|
+
y = y[order]
|
|
319
|
+
|
|
320
|
+
return x, y
|
|
321
|
+
|
|
322
|
+
def _make_interp1d(self, x, y, opt: Interp1DSpec) -> Callable:
|
|
323
|
+
"""
|
|
324
|
+
Create interp1d-like callable based on opt.
|
|
325
|
+
"""
|
|
326
|
+
import numpy as _np
|
|
327
|
+
|
|
328
|
+
x = _np.asarray(x, dtype=float)
|
|
329
|
+
y = _np.asarray(y, dtype=float)
|
|
330
|
+
|
|
331
|
+
if x.ndim != 1 or y.ndim != 1 or x.shape[0] < 2 or x.shape != y.shape:
|
|
332
|
+
raise ValueError("interp1d: x and y must be 1D arrays of the same length >= 2")
|
|
333
|
+
|
|
334
|
+
xmin = float(_np.min(x))
|
|
335
|
+
xmax = float(_np.max(x))
|
|
336
|
+
|
|
337
|
+
def _linear_extrap(xq: _np.ndarray) -> _np.ndarray:
|
|
338
|
+
# linear extrapolation using the first/last segments
|
|
339
|
+
yq = _np.interp(xq, x, y)
|
|
340
|
+
left = xq < xmin
|
|
341
|
+
right = xq > xmax
|
|
342
|
+
if left.any():
|
|
343
|
+
x0, x1 = x[0], x[1]
|
|
344
|
+
y0, y1 = y[0], y[1]
|
|
345
|
+
slope = (y1 - y0) / (x1 - x0)
|
|
346
|
+
yq[left] = y0 + slope * (xq[left] - x0)
|
|
347
|
+
if right.any():
|
|
348
|
+
x0, x1 = x[-2], x[-1]
|
|
349
|
+
y0, y1 = y[-2], y[-1]
|
|
350
|
+
slope = (y1 - y0) / (x1 - x0)
|
|
351
|
+
yq[right] = y1 + slope * (xq[right] - x1)
|
|
352
|
+
return yq
|
|
353
|
+
|
|
354
|
+
def f(xq):
|
|
355
|
+
xq = _np.asarray(xq, dtype=float)
|
|
356
|
+
if opt.bounds == "clamp":
|
|
357
|
+
xqc = _np.clip(xq, xmin, xmax)
|
|
358
|
+
return _np.interp(xqc, x, y)
|
|
359
|
+
elif opt.bounds == "extrapolate":
|
|
360
|
+
return _linear_extrap(xq)
|
|
361
|
+
elif opt.bounds == "nan":
|
|
362
|
+
yq = _np.interp(_np.clip(xq, xmin, xmax), x, y)
|
|
363
|
+
out = (xq < xmin) | (xq > xmax)
|
|
364
|
+
if out.any():
|
|
365
|
+
yq = yq.astype(float, copy=False)
|
|
366
|
+
yq[out] = _np.nan
|
|
367
|
+
return yq
|
|
368
|
+
elif opt.bounds == "error":
|
|
369
|
+
if _np.any((xq < xmin) | (xq > xmax)):
|
|
370
|
+
raise ValueError("interp1d: query x is out of bounds")
|
|
371
|
+
return _np.interp(xq, x, y)
|
|
372
|
+
else:
|
|
373
|
+
# fallback
|
|
374
|
+
xqc = _np.clip(xq, xmin, xmax)
|
|
375
|
+
return _np.interp(xqc, x, y)
|
|
376
|
+
|
|
377
|
+
return f
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jarvisplot
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: JarvisPLOT: YAML-driven plotting engine
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: loguru
|
|
8
|
+
Requires-Dist: pyyaml
|
|
9
|
+
Requires-Dist: jsonschema
|
|
10
|
+
Requires-Dist: numpy
|
|
11
|
+
Requires-Dist: pandas
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
|
|
14
|
+
# JarvisPLOT
|
|
15
|
+
|
|
16
|
+
JarvisPLOT is a lightweight, Python/Matplotlib-based plotting framework developed for **Jarvis-HEP**,
|
|
17
|
+
but it can also be used as a **standalone scientific plotting tool**.
|
|
18
|
+
|
|
19
|
+
It provides a simple command-line interface (CLI) to generate publication-quality figures from YAML configuration files, with most layout and style decisions handled by predefined profiles and style cards.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Command-Line Usage
|
|
24
|
+
|
|
25
|
+
Display help information:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
jplot -h
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run JarvisPLOT with one or more YAML configuration files:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
jplot path/to/config.yaml
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Example: SUSYRun2 Ternary Plots
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
jplot ./bin/SUSYRun2_EWMSSM.yaml
|
|
41
|
+
jplot ./bin/SUSYRun2_GEWMSSM.yaml
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **Note:** The data file paths inside the YAML files must be updated to match your local setup.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
|
|
50
|
+
- Figures are saved automatically to the output paths defined in the YAML configuration.
|
|
51
|
+
- Common output formats include PNG and PDF (backend-dependent).
|
|
52
|
+
- JarvisPLOT works in headless environments (SSH, batch jobs) without any GUI backend.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Requirements
|
|
57
|
+
|
|
58
|
+
### Python
|
|
59
|
+
- **Python ≥ 3.9** (tested on 3.9–3.12)
|
|
60
|
+
|
|
61
|
+
### Required Packages
|
|
62
|
+
- `numpy`
|
|
63
|
+
- `pandas`
|
|
64
|
+
- `matplotlib`
|
|
65
|
+
- `pyyaml`
|
|
66
|
+
- `jsonschema`
|
|
67
|
+
- `scipy` — numerical utilities
|
|
68
|
+
- `h5py` — required for loading HDF5 data files
|
|
69
|
+
|
|
70
|
+
### Github Page
|
|
71
|
+
[https://github.com/Pengxuan-Zhu-Phys/Jarvis-PLOT](https://github.com/Pengxuan-Zhu-Phys/Jarvis-PLOT)
|
|
72
|
+
|
|
73
|
+
### Documentation
|
|
74
|
+
[https://pengxuan-zhu-phys.github.io/Jarvis-Docs/](https://pengxuan-zhu-phys.github.io/Jarvis-Docs/)
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT License
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
jarvisplot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
jarvisplot/cli.py,sha256=QwVmR48yHPsGbGSWqNj8Pbo53nYaLSPrhMa3LPvsiNI,2543
|
|
3
|
+
jarvisplot/client.py,sha256=vjcxT4Y9v8F0nfqId4ROSE0xbbZW7cv-rIxOwh2LzFk,115
|
|
4
|
+
jarvisplot/config.py,sha256=edHNoumh4bDjzj7hP1dGM6chCz_El4qNPA_wL4M3LCQ,2282
|
|
5
|
+
jarvisplot/core.py,sha256=7ekrBL9UejjUYYTub8zYh2aOvSiEyp7V4T8vjhuCHhM,8890
|
|
6
|
+
jarvisplot/data_loader.py,sha256=QIN3NZPSl63xGSH_3C2upmj1-aTfkZrLY-oK6g8Lrz4,16740
|
|
7
|
+
jarvisplot/inner_func.py,sha256=hQeoJzVeTVjIb8_qRbHPf0_5lj-y0QEdOZEy-Yowm88,4158
|
|
8
|
+
jarvisplot/Figure/adapters.py,sha256=END12kkzmhnG_bJddfQ3CQy0P3DlrY5I6WRl2DfiKlU,28502
|
|
9
|
+
jarvisplot/Figure/data_pipelines.py,sha256=4mOHyYEHkqUCsEvDL2X7l2hE9ya2HyE0rmVqJLJ6b9E,3348
|
|
10
|
+
jarvisplot/Figure/figure.py,sha256=yPfKXiN-ahXuiR4x-xVAhzyRBoXdC41yofDAWF0S6a4,66593
|
|
11
|
+
jarvisplot/Figure/helper.py,sha256=5CJtJrKhIn7h-uBe7QWL9s2BoMIU9-JLtLmSSs86F80,7424
|
|
12
|
+
jarvisplot/Figure/load_data.py,sha256=U7dgJuHJJsLAonuFc0fMLbmWCJO5qnJsi4p6e3WrnSU,8997
|
|
13
|
+
jarvisplot/Figure/cards/std_axes_adapter_config.json,sha256=lokkIg-QIBZQ-lJr28cRbFdlvGP9-cCd64UIF9mnpxg,478
|
|
14
|
+
jarvisplot/cards/args.json,sha256=Rh4Qmd-Otsg4-IymPoMA5NqXLaPpajvNNezrdtliX_E,1439
|
|
15
|
+
jarvisplot/cards/style_preference.json,sha256=WbkrIa4RwEaHuMmyP_QO3nkjaU8M6COAMjexOCr-AX8,901
|
|
16
|
+
jarvisplot/cards/a4paper/1x1/ternary.json,sha256=kPz74R976P2HEl4e5yETFhukJIGHpekuH8LtkurXp94,69
|
|
17
|
+
jarvisplot/cards/a4paper/2x1/rect.json,sha256=zZ4Yo7i0_b4qNZp30AFVnLsWMPk9jPBIYdaJQDoT1Pc,2767
|
|
18
|
+
jarvisplot/cards/a4paper/2x1/rect5x1.json,sha256=uB4TcwxOIE2xnCeHLxCk_mS0M_ZMEP8pBuK_O6NX0a0,10318
|
|
19
|
+
jarvisplot/cards/a4paper/2x1/rect_cmap.json,sha256=ZiWxYMbqJvwulfDLR4-njwzcZf_I3DfK7hhfePT-MIw,5125
|
|
20
|
+
jarvisplot/cards/a4paper/2x1/ternary.json,sha256=hJl9B7o0Bg8Wl0InRo5PNyDc3FpPfdPmk7DmvQQNu6w,4051
|
|
21
|
+
jarvisplot/cards/a4paper/2x1/ternary_cmap.json,sha256=Av6MuK9z3S2Vpfvmb0QyxS1b7y0bjsIUvc6elB1J3As,5335
|
|
22
|
+
jarvisplot/cards/a4paper/4x1/rect.json,sha256=zZ4Yo7i0_b4qNZp30AFVnLsWMPk9jPBIYdaJQDoT1Pc,2767
|
|
23
|
+
jarvisplot/cards/a4paper/4x1/rect_cmap.json,sha256=TuoT_D-D2HcsQGiab-3NFlP-XJUtP9qv0kPcyUiT7wI,4842
|
|
24
|
+
jarvisplot/cards/a4paper/4x1/ternary.json,sha256=hJl9B7o0Bg8Wl0InRo5PNyDc3FpPfdPmk7DmvQQNu6w,4051
|
|
25
|
+
jarvisplot/cards/a4paper/4x1/ternary_cmap.json,sha256=Av6MuK9z3S2Vpfvmb0QyxS1b7y0bjsIUvc6elB1J3As,5335
|
|
26
|
+
jarvisplot/cards/colors/colormaps.json,sha256=mTiR9OmYjpYaby0F4jwddTbYNzfL86QkHECannTbYzQ,3839
|
|
27
|
+
jarvisplot/cards/default/output.json,sha256=x562t9OPPG4p8fubUPJz2CEyw2GxQYVIEUMMQB_Klpg,135
|
|
28
|
+
jarvisplot/cards/gambit/1x1/ternary.json,sha256=kPz74R976P2HEl4e5yETFhukJIGHpekuH8LtkurXp94,69
|
|
29
|
+
jarvisplot/cards/gambit/2x1/rect_cmap.json,sha256=SrxBn3Z8vLWi4tnZvv8eXFYwzQKzZZ7V1Ea3DRjDsfI,5595
|
|
30
|
+
jarvisplot/cards/gambit/2x1/ternary.json,sha256=hJl9B7o0Bg8Wl0InRo5PNyDc3FpPfdPmk7DmvQQNu6w,4051
|
|
31
|
+
jarvisplot/cards/gambit/2x1/ternary_cmap.json,sha256=sBTeGq7DVUutRNalbzQqIlUQZXmzxC0Xobc3_Btdmhg,5871
|
|
32
|
+
jarvisplot/cards/icons/JarvisHEP.png,sha256=iDXEeQhA6T7REjIoae6XEj619_z7IlWqhnY9JaLPAww,399549
|
|
33
|
+
jarvisplot/cards/icons/gambit.png,sha256=z6BRjaVbnAMhtWzCEf53VCfEFPxoMbEYWsOT9ZNyfOE,246969
|
|
34
|
+
jarvisplot/cards/icons/gambit_small.png,sha256=maQSLO4PPSn4_J9rqIqp0qvY4Rtpx9GiiPl7ONrieKk,83453
|
|
35
|
+
jarvisplot/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
jarvisplot/utils/cmaps.py,sha256=2IKOUzsOZb8KReEggbNCNAdzS6IhWrcMMSUw03CunpM,9449
|
|
37
|
+
jarvisplot/utils/interpolator.py,sha256=CG6Wi6BX2cAI-TdL0d51J9wXnjxawc4R_uPlNoXHfss,13126
|
|
38
|
+
jarvisplot-1.0.1.dist-info/METADATA,sha256=wk9MzJuePhkdxrnd_bNLzAPNjYgsmF5uUOjGh0rjH34,1864
|
|
39
|
+
jarvisplot-1.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
40
|
+
jarvisplot-1.0.1.dist-info/entry_points.txt,sha256=1dvnP7V3PTjdtL3G8OIDEKopJlDFJccC0EzFKmvp4jM,49
|
|
41
|
+
jarvisplot-1.0.1.dist-info/top_level.txt,sha256=Rvy30upSt2sE_reLghmQejiBTIo5t6HXLnMwIsiKWS8,11
|
|
42
|
+
jarvisplot-1.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jarvisplot
|