makepst 0.1.0__tar.gz → 0.3.0__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.
- makepst-0.3.0/CHANGELOG.md +73 -0
- {makepst-0.1.0 → makepst-0.3.0}/CITATION.cff +2 -2
- {makepst-0.1.0 → makepst-0.3.0}/MANIFEST.in +2 -1
- {makepst-0.1.0 → makepst-0.3.0}/PKG-INFO +144 -15
- {makepst-0.1.0 → makepst-0.3.0}/README.md +143 -14
- {makepst-0.1.0 → makepst-0.3.0}/makepst/__init__.py +5 -2
- {makepst-0.1.0 → makepst-0.3.0}/makepst/checks.py +114 -2
- makepst-0.3.0/makepst/cli.py +683 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/excel.py +229 -41
- makepst-0.3.0/makepst/hpstart.py +64 -0
- makepst-0.3.0/makepst/model_files.py +181 -0
- makepst-0.3.0/makepst/provenance.py +251 -0
- makepst-0.3.0/makepst/rules.py +403 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/PKG-INFO +144 -15
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/SOURCES.txt +8 -0
- {makepst-0.1.0 → makepst-0.3.0}/pyproject.toml +1 -1
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_checks.py +20 -0
- makepst-0.3.0/tests/test_convenience.py +60 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_fixture.py +35 -0
- makepst-0.3.0/tests/test_hpstart.py +68 -0
- makepst-0.3.0/tests/test_log_bundle.py +98 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_makepst.py +2 -1
- makepst-0.3.0/tests/test_model_files.py +207 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_provenance.py +4 -6
- makepst-0.3.0/tests/test_rules.py +194 -0
- makepst-0.1.0/CHANGELOG.md +0 -32
- makepst-0.1.0/makepst/cli.py +0 -351
- makepst-0.1.0/makepst/provenance.py +0 -89
- {makepst-0.1.0 → makepst-0.3.0}/LICENSE +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/control.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/flow.ins +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/flow.out +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/heads.ins +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/heads.out +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/io.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/model.in +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/model.par +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/model.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/model.res +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/model.tpl +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/obs.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/par.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/examples/minimal/pargp.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/__main__.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/diff.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/phi.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/pst.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/pyemu_bridge.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/reader.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/sections.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/starter.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst/writer.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/dependency_links.txt +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/entry_points.txt +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/requires.txt +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/makepst.egg-info/top_level.txt +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/setup.cfg +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.3.obs.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.3.par.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.par +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.phi.actual.csv +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.pst +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.res +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/data/demo.xlsx +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/make_fixture.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_diff.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_examples.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_phi.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_pyemu.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_starter.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_v2.py +0 -0
- {makepst-0.1.0 → makepst-0.3.0}/tests/test_version.py +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (2026-09-21)
|
|
4
|
+
|
|
5
|
+
- `log`: ledger of every manifest under a folder — when, which command, which output from
|
|
6
|
+
which sources, dimensions, version; `--file` filters by file name, path or sha256 prefix
|
|
7
|
+
("which runs used that version of the workbook?"), `--command`, `--last`, `--check`
|
|
8
|
+
verifies every recorded hash
|
|
9
|
+
- `bundle`: zip a run for reproduction or review — control file, manifest, templates,
|
|
10
|
+
instruction files, model inputs, command-line files; `--sources`, `--outputs`, `--extra`,
|
|
11
|
+
`--list`, `--strict`; the zip gets its own manifest
|
|
12
|
+
- `provenance`: compare the hashes recorded in a manifest with the files on disk
|
|
13
|
+
(`unchanged` / `changed` / `missing`; exit 1 unless all unchanged)
|
|
14
|
+
- `update --dry_run`: preview matched names, target columns and skipped cells without writing
|
|
15
|
+
- `build` warns when a referenced sheet has formulas without cached values (a workbook saved
|
|
16
|
+
by openpyxl and not yet recalculated in Excel)
|
|
17
|
+
- `hpstart`: a PEST_HP `.hp` file with observation values from residuals or an IES realization
|
|
18
|
+
|
|
19
|
+
## 0.2.0 (2026-09-19)
|
|
20
|
+
|
|
21
|
+
- `update`: the xlwings backend reads only headers, name and target columns, writes contiguous
|
|
22
|
+
cells in one call per run, suspends events and calculation while writing and recalculates
|
|
23
|
+
once before saving (1,068 parameters over eight sheets of a 27-sheet workbook: ~9 s, was
|
|
24
|
+
minutes); `--par` with `--pst` no longer rewrites the observation sheets
|
|
25
|
+
- `tempchek`: check a template file or write model input files from parameter values, with
|
|
26
|
+
PEST's number writer (maximum precision in the space, `PRECIS` / `DPOINT`, one word per
|
|
27
|
+
parameter, right-justified; checked against `tempchek.exe`); for a template + `.par` file
|
|
28
|
+
or for a whole case
|
|
29
|
+
- `inschek`: check an instruction file or read a model output file with it into an `.obf`
|
|
30
|
+
file, for one file or a whole case
|
|
31
|
+
- `validate`: values that cannot be written into their template space are errors; a line
|
|
32
|
+
advance not at the start of an instruction line and `t` / fixed instructions moving left
|
|
33
|
+
are errors (INSCHEK's rules); three more pestchek warnings (empty observation group,
|
|
34
|
+
`ICOV`/`ICOR`/`IEIG` with more than 300 adjustable parameters, PEST_HP-only variables —
|
|
35
|
+
the last as a note); an all-zero-weight observation group is now a note, not a warning
|
|
36
|
+
- `update`: cells inside array formulas or dynamic-array spill ranges are left alone (Excel
|
|
37
|
+
refuses them; xlwings no longer shows a dialog); names present on only one side are reported
|
|
38
|
+
with counts and examples
|
|
39
|
+
- `validate`: pestchek's rules added (parameter data, groups, observations, prior information,
|
|
40
|
+
~60 control-variable ranges and consistency rules, template and instruction syntax), derived
|
|
41
|
+
from PEST 17's `pestchek.F` / `cheksub.F`; file paths resolved from the directory PEST runs
|
|
42
|
+
in, not only the control file's folder
|
|
43
|
+
|
|
44
|
+
## 0.1.0 (2026-09-19)
|
|
45
|
+
|
|
46
|
+
First packaged release; replaces the single-file `makePst.py` script.
|
|
47
|
+
|
|
48
|
+
- `init`: starter workbook with headers, defaults, descriptions, drop-down lists, comments and
|
|
49
|
+
a `BUILD` sheet
|
|
50
|
+
- `build`: Excel/CSV tables -> control file; sheet globs (`book,PAR_*`); a workbook with a
|
|
51
|
+
`BUILD` sheet as the only argument; counts computed; PEST_HP keyed tokens and
|
|
52
|
+
`++` options; regularisation equations from `PRIOR`/`WEIGHT` columns; validation of tied
|
|
53
|
+
targets, groups, prior references and bounds
|
|
54
|
+
- `dump`: control file -> workbook with a `BUILD` sheet that rebuilds it
|
|
55
|
+
- `update`: `.par` / `.res` / `.rei` / PESTPP-IES ensembles into an existing workbook, matched
|
|
56
|
+
by name across sheets; formula cells protected; `--sheet` / `--group` filters; `--real`;
|
|
57
|
+
`PHI` sheet (objective function by observation group) and `PHI_IES` (realization phis)
|
|
58
|
+
whenever residuals are available
|
|
59
|
+
- `validate`: pestchek-style report on a control file or workbook: table checks, missing
|
|
60
|
+
files, parameter / observation names against template and instruction files, optional run
|
|
61
|
+
of instruction files against model outputs; exit 1 on errors
|
|
62
|
+
- PEST++ version-2 control files (`pcf version=2`, keyword control data, external csv
|
|
63
|
+
tables) are read and written (`--v2`); observation covariance files are kept
|
|
64
|
+
- `diff`: semantic comparison of two control files / workbooks (tables, effective control
|
|
65
|
+
values, options, io, comments), text report or workbook; exit 1 on differences
|
|
66
|
+
- pyEMU bridge: `to_pyemu()` / `from_pyemu()` via a temporary control file (`pyemu` extra)
|
|
67
|
+
- provenance: every producing command writes `<output>.manifest.json` (version, command
|
|
68
|
+
line, source hashes and sheets, output hash and counts)
|
|
69
|
+
- `parrep`: `.par` or IES realization into a control file (or a workbook with a `BUILD` sheet),
|
|
70
|
+
with `--set NAME=VALUE`
|
|
71
|
+
- Fixes over the old script: `jacupdate` was never written; values were truncated to 6
|
|
72
|
+
significant digits; `phistopthresh` could not be set; SVD / regularisation values in the
|
|
73
|
+
CONTROL sheet were ignored; prior information in estimation mode produced an invalid file
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
include LICENSE README.md CHANGELOG.md CITATION.cff
|
|
2
2
|
recursive-include tests *.py *.xlsx *.pst *.par *.res *.csv
|
|
3
|
-
recursive-include examples *.csv *.py *.tpl *.ins *.in *.out *.par *.res
|
|
3
|
+
recursive-include examples/minimal *.csv *.py *.tpl *.ins *.in *.out *.par *.res
|
|
4
|
+
prune examples/mnnrd
|
|
4
5
|
global-exclude __pycache__ *.py[cod] *.manifest.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: makepst
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Build PEST / PEST++ / PEST_HP control files from spreadsheet tables, read them back, and push results into the workbook
|
|
5
5
|
Author-email: Michael Ou <ougengxin@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -45,8 +45,11 @@ reads control files back into editable workbooks, and returns calibration result
|
|
|
45
45
|
Excel / CSV tables -> .pst makepst build
|
|
46
46
|
.pst -> editable workbook makepst dump
|
|
47
47
|
.par / .res / .rei / IES ensemble -> existing workbook makepst update
|
|
48
|
-
.par or IES realization (+ tweaks) -> new .pst makepst parrep
|
|
48
|
+
.par or IES realization (+ tweaks) -> new .pst makepst parrep
|
|
49
|
+
.res/.rei or IES obs + existing .hp -> new .hp makepst hpstart
|
|
49
50
|
two .pst / workbooks -> what changed makepst diff
|
|
51
|
+
manifests in a folder -> run ledger makepst log
|
|
52
|
+
.pst + everything it references -> zip for review makepst bundle
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
See [What round-tripping preserves](#what-round-tripping-preserves) for the exact guarantees
|
|
@@ -120,6 +123,10 @@ makepst build model.pst regul --set_ctl_csv control.csv --add_pargp_csv pargp.cs
|
|
|
120
123
|
# 2. check it against the template / instruction / output files, like pestchek
|
|
121
124
|
makepst validate model.pst --outputs
|
|
122
125
|
|
|
126
|
+
# 2b. what PEST will do with them: write the model input file, read the model output files
|
|
127
|
+
makepst tempchek model.pst --dry_run
|
|
128
|
+
makepst inschek model.pst
|
|
129
|
+
|
|
123
130
|
# 3. control file -> workbook (CONTROL / PARGP / PAR / OBS / PRIOR / IO sheets + a BUILD sheet)
|
|
124
131
|
makepst dump model.pst model.xlsx
|
|
125
132
|
|
|
@@ -154,8 +161,10 @@ makepst validate model.pst --outputs
|
|
|
154
161
|
makepst validate book.xlsm --strict
|
|
155
162
|
```
|
|
156
163
|
|
|
157
|
-
See [Validation](#validation). Template and instruction paths are resolved
|
|
158
|
-
|
|
164
|
+
See [Validation](#validation). Template and instruction paths are resolved the way PEST
|
|
165
|
+
resolves them, from the directory PEST is run in: makePst tries the control file's folder,
|
|
166
|
+
the current directory and the control file's parent, uses the one where the files are, and
|
|
167
|
+
says which; `--base_dir` overrides that. `--strict` also fails on warnings,
|
|
159
168
|
`--quiet` hides the informational lines.
|
|
160
169
|
|
|
161
170
|
### build — tables → .pst
|
|
@@ -217,6 +226,7 @@ makepst update book.xlsm --res run.res # adds MODELLED
|
|
|
217
226
|
makepst update book.xlsm --pst run.pst --par_cols PARVAL1,PARTRANS,PARLBND,PARUBND
|
|
218
227
|
makepst update book.xlsm --par run.3.par.csv --real best # PESTPP-IES ensemble
|
|
219
228
|
makepst update book.xlsm --obs_csv run.3.obs.csv --pst run.pst # IES simulated values
|
|
229
|
+
makepst update book.xlsm --par run.par --dry_run # preview; write nothing
|
|
220
230
|
[--sheet "PAR_*"] [--group hk,sy] [--overwrite_formulas] [--out copy.xlsm] [--backend openpyxl|xlwings]
|
|
221
231
|
```
|
|
222
232
|
|
|
@@ -225,10 +235,19 @@ makepst update book.xlsm --obs_csv run.3.obs.csv --pst run.pst # IES simulated
|
|
|
225
235
|
|
|
226
236
|
Every worksheet whose header row contains `PARNME` (or `OBSNME`) is matched row by row on the
|
|
227
237
|
name, so tables spread over several sheets are all found; only the requested columns are
|
|
228
|
-
written and only for names present in the
|
|
229
|
-
|
|
238
|
+
written and only for names present in both: names in the results with no row in the workbook,
|
|
239
|
+
and workbook rows absent from the results, are left alone and reported as a warning with
|
|
240
|
+
counts and examples. Cells that hold formulas are left alone unless `--overwrite_formulas`;
|
|
241
|
+
cells inside an array formula or a dynamic-array spill range are never written (Excel would
|
|
242
|
+
refuse), only counted. `--sheet` (glob, case-insensitive) restricts the worksheets;
|
|
230
243
|
`--group` restricts the parameter / observation groups (a `.par` file carries no groups, so
|
|
231
|
-
add `--pst` to supply them).
|
|
244
|
+
add `--pst` to supply them). `--pst` on its own writes the control file's parameter and
|
|
245
|
+
observation columns; next to `--par`, `--res` or `--obs_csv` it only supplies groups and
|
|
246
|
+
measured values, and the observation sheets are not touched by a parameter update.
|
|
247
|
+
|
|
248
|
+
The xlwings backend reads only each sheet's header, name column and target columns, writes
|
|
249
|
+
contiguous runs of cells in one call each and recalculates once before saving: the
|
|
250
|
+
demonstration workbook (27 sheets, 1,068 parameters over eight sheets) updates in about 9 s.
|
|
232
251
|
|
|
233
252
|
PESTPP-IES ensembles (`case.N.par.csv`, `case.N.obs.csv`) are recognised by their
|
|
234
253
|
`real_name` header. `--real` picks the realization: a name (`base`, `17`) or `best` — the
|
|
@@ -245,8 +264,13 @@ Both sheets are rewritten on each run (`--no_phi` to skip).
|
|
|
245
264
|
|
|
246
265
|
Two backends: **xlwings** (used when installed; drives Excel invisibly, so everything in the
|
|
247
266
|
workbook is preserved and formulas recalculate; Windows/macOS with Excel) or **openpyxl**
|
|
248
|
-
(no Excel needed; keeps VBA macros; drops charts and images;
|
|
249
|
-
|
|
267
|
+
(no Excel needed; keeps VBA macros; drops charts and images; does not recalculate formulas).
|
|
268
|
+
Before building from a workbook updated with openpyxl, recalculate and save it in Excel;
|
|
269
|
+
otherwise the build may read missing or stale cached formula values. The backend used is printed.
|
|
270
|
+
`--dry_run` uses openpyxl to preview matched names, target columns, skipped formulas and
|
|
271
|
+
unmatched names without saving a workbook or manifest. It does not test whether Excel will
|
|
272
|
+
refuse individual writes. During `build`, missing cached formula results in referenced
|
|
273
|
+
tables produce a warning; existing cached values may still be stale and cannot be verified.
|
|
250
274
|
|
|
251
275
|
### parrep — .par values → new .pst
|
|
252
276
|
|
|
@@ -268,6 +292,61 @@ to that workbook; other relative names resolve against the workbook's folder. No
|
|
|
268
292
|
route rebuilds from the workbook's *current* state, so any edits made since the original
|
|
269
293
|
control file was written are picked up too.
|
|
270
294
|
|
|
295
|
+
### hpstart — modeled observations → new PEST_HP .hp file
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
makepst hpstart case.pst start.hp --template case.hp --res case.res
|
|
299
|
+
makepst hpstart case.pst start.hp --template case.hp --obs_csv case.3.obs.csv --real best
|
|
300
|
+
makepst hpstart case.pst start.hp --template case.hp --res case.rei --par case.par
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
By default, parameter values are copied unchanged from the template `.hp`, **not** from
|
|
304
|
+
`PARVAL1` in the `.pst`. Use `--par` to replace them with values from a PEST `.par` file
|
|
305
|
+
or a selected IES parameter realization. The `.pst` supplies parameter and observation
|
|
306
|
+
names and order; the template supplies PEST_HP instruction metadata that a residual or
|
|
307
|
+
IES observation file cannot provide. `hpstart` replaces the template's reference
|
|
308
|
+
modeled-observation array in `.pst` observation order. Every required observation must
|
|
309
|
+
be present with a finite value; extra result rows are ignored. The template must come
|
|
310
|
+
from the same control-file layout and
|
|
311
|
+
observation/parameter order. The binary format stores counts but not names, so matching
|
|
312
|
+
counts alone cannot establish that the template has the right order. The original `.hp`
|
|
313
|
+
is never modified, and the new file receives a provenance manifest by default.
|
|
314
|
+
|
|
315
|
+
### tempchek — templates → model input files
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
makepst tempchek model.tpl # check the template, list its parameters
|
|
319
|
+
makepst tempchek model.tpl model.in [model.par] # write model.in (values from model.par by default)
|
|
320
|
+
makepst tempchek model.pst [--par run.par] [--real best] [--dry_run] # every template of the case
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Like PEST's TEMPCHEK: a template is checked, then its model input file is written with
|
|
324
|
+
parameter values from a `.par` file (or, given a control file / workbook, from `PARVAL1`
|
|
325
|
+
with `SCALE` and `OFFSET` applied, or from `--par` — a `.par` file or an IES realization).
|
|
326
|
+
Numbers are written the way PEST writes them (its `WRTSIG`): into the parameter space with
|
|
327
|
+
as much precision as the space allows, fixed or exponential notation, right-justified, one
|
|
328
|
+
word per parameter sized for its narrowest space, `PRECIS` / `DPOINT` honoured
|
|
329
|
+
(`double`, `nopoint`). Checked against `tempchek.exe` on a grid of values and widths from 3
|
|
330
|
+
to 23 characters: the same fields are refused, and the written value is never further from
|
|
331
|
+
the true value than PEST's. A value that cannot be written (`1e-10` in a five-character
|
|
332
|
+
space, say) is an error and nothing is written; parameters in the `.par` file that the
|
|
333
|
+
template does not cite are warnings. `--dry_run` checks without writing.
|
|
334
|
+
|
|
335
|
+
### inschek — model output files → observation values
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
makepst inschek heads.ins # check the instruction file, list its observations
|
|
339
|
+
makepst inschek heads.ins heads.out [--out heads.obf]
|
|
340
|
+
makepst inschek model.pst [--out model.obf] # every instruction file of the case, one .obf
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Like PEST's INSCHEK: the instruction file is checked (the syntax rules in the validation
|
|
344
|
+
table below, including that a line advance can only start an instruction line and that fixed
|
|
345
|
+
/ tab instructions must move right), then read against the model output file with the
|
|
346
|
+
interpreter that `validate --outputs` uses, and the values go to an observation value file
|
|
347
|
+
(`name value` per line, INSCHEK's `.obf`). With a control file, all instruction files are
|
|
348
|
+
read and observations the control file lists but no file produced are reported.
|
|
349
|
+
|
|
271
350
|
### diff — what changed
|
|
272
351
|
|
|
273
352
|
```
|
|
@@ -283,6 +362,39 @@ tolerance, so `49.7377` vs `49.73775` shows up at the default `1e-9` and disappe
|
|
|
283
362
|
`--rtol 1e-5`. Prints a table per section and a one-line summary; `--xlsx` writes the same
|
|
284
363
|
tables to a workbook for review. Exits 1 when there are differences, like `diff`.
|
|
285
364
|
|
|
365
|
+
### log — the run ledger
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
makepst log [FOLDER ...] [--file tr13.xlsm] [--command build] [--last 10] [--check] [--no_recursive]
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Every manifest under the folders (default: the current one, recursively), oldest first, one
|
|
372
|
+
line each: when, which command, which output, from which sources, the dimensions, the
|
|
373
|
+
makePst version. `--file` keeps the entries whose output or sources include a file — by name,
|
|
374
|
+
path or **sha256 prefix**, so `--file b43fc509` answers "which runs used *that* version of
|
|
375
|
+
the workbook?" even after the file was renamed or overwritten. `--check` adds a column from
|
|
376
|
+
`provenance`: `unchanged` / `changed` / `missing`.
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
2026-09-19 21:14 build pest/tr13.pst <- tr13.xlsm [1068 par 41747 obs 159 prior] makepst 0.3.0
|
|
380
|
+
2026-09-21 08:10 update tr13-xlwings.xlsm <- tr13.xlsm, tr13.par [1068 rows in 8 sheets] makepst 0.3.0
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### bundle — a run, zipped for review
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
makepst bundle model.pst [run.zip] [--sources] [--outputs] [--extra "bin/**"] [--list] [--strict]
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Zips the control file with its manifest, every template and instruction file, the model
|
|
390
|
+
input files the templates write, and any file named on the model command line, with paths
|
|
391
|
+
relative to the run directory so the archive unzips runnable. `--sources` adds the manifest's
|
|
392
|
+
sources (the workbook, the `.par` ...), `--outputs` the model output files, `--extra` any
|
|
393
|
+
glob relative to the run directory (the model binaries, say). Files the control file
|
|
394
|
+
references but that don't exist are listed as `MISSING` (exit 1 with `--strict`); `--list`
|
|
395
|
+
shows what would be bundled without writing. The zip gets its own manifest, so `log` shows
|
|
396
|
+
who bundled what and when.
|
|
397
|
+
|
|
286
398
|
## What round-tripping preserves
|
|
287
399
|
|
|
288
400
|
`dump` → `build` and `read_pst` → `write_pst` are **semantically lossless for supported
|
|
@@ -358,6 +470,12 @@ makepst validate tr13.xlsm # a workbook with a BUILD sheet is built f
|
|
|
358
470
|
| malformed prior equation; non-numeric right-hand side; `log()` used on a non-log parameter or vice versa | error |
|
|
359
471
|
| `DERCOM` beyond the number of model command lines | error |
|
|
360
472
|
| name longer than PEST's limit (12 parameter / 20 observation / 12 group; PEST++ allows 200) | warning |
|
|
473
|
+
| **pestchek's rules** (from PEST 17's `pestchek.F` / `cheksub.F`): `PARCHGLIM` vocabulary and `absolute(n)` needing `absparmax(n)=`; log parameters factor-limited with positive values; factor-limited bounds of one sign and non-zero; `SCALE ≠ 0`; tied to itself, tied or parent with initial value 0; group `none` reserved; all parameters fixed/tied; `INCTYP` / `FORCEN` / `DERMTHD` vocabularies and their compatibility; split-column ranges; duplicate groups; `dum` as an observation name; prior weights, labels (≤ 20, not an observation name), duplicated parameters, all-zero factors, group `predict`; regularisation needs a `regul*` group, prediction exactly one `predict` observation; ~60 control-variable range and consistency rules (`RLAMFAC`, `PHIRATSUF`, `FACPARMAX`, `NPHISTP`, `RELPARSTP < RELPARMAX`, `EIGTHRESH`, `PHIMLIM < PHIMACCEPT < 1.2·PHIMLIM`, `WFMIN ≤ WFINIT ≤ WFMAX`, `UPTESTMIN`/`UPTESTLIM`, SVD vs AUI vs LSQR exclusivity, …) | error |
|
|
474
|
+
| pestchek's warnings: derivative increment larger than a third of the range; initial value 0 with a relative increment; group with only fixed/tied parameters; log parameters with a non-relative increment; observation group listed but empty (dropped on write); `MAXSING` above the adjustable-parameter count; more than 300 adjustable parameters with `ICOV`/`ICOR`/`IEIG` on; `NOPTMAX 0` | warning |
|
|
475
|
+
| PEST_HP-only control variables present (`WIN_MRUN_HOURS`, `UPTESTMIN`, `RRFSAVE`, …; plain PEST needs `/hpstart`); observation group whose weights are all zero | note |
|
|
476
|
+
| template: parameter space narrower than 3 characters or containing a tab | error |
|
|
477
|
+
| template: a value that cannot be written into its space as PEST writes numbers (`PRECIS` / `DPOINT` honoured), e.g. `1e-10` in five characters | error |
|
|
478
|
+
| instruction file syntax: `l` / `t` integers, marker delimiters, `!` balance, `[name]n1:n2` order, `dum` in a fixed field, continuation `&` placement, `l` only at the start of a line, `t` and fixed columns moving left to right, unknown instructions — checked statically, without model output | error |
|
|
361
479
|
| section in the `.pst` that makePst drops on read | warning |
|
|
362
480
|
| template or instruction file missing; bad `ptf` / `pif` line | error |
|
|
363
481
|
| parameter cited in no template; template citing an unknown parameter | error |
|
|
@@ -383,7 +501,7 @@ from?" after the fact:
|
|
|
383
501
|
|
|
384
502
|
```json
|
|
385
503
|
{
|
|
386
|
-
"makepst": "0.
|
|
504
|
+
"makepst": "0.3.0",
|
|
387
505
|
"command": "build",
|
|
388
506
|
"argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsm,CONTROL", "..."],
|
|
389
507
|
"created": "2026-09-19T13:51:39-04:00",
|
|
@@ -395,7 +513,7 @@ from?" after the fact:
|
|
|
395
513
|
"sheets": ["CONTROL", "PARGP", "PAR_HK", "PAR_VK", "...", "PPglm"]}
|
|
396
514
|
],
|
|
397
515
|
"output": {"path": "D:\\...\\tr13.pst", "sha256": "d93f317d...", "size": 2163331,
|
|
398
|
-
"npar": 1068, "nobs": 41747, "npargp": 12, "nprior":
|
|
516
|
+
"npar": 1068, "nobs": 41747, "npargp": 12, "nprior": 159, "nobsgp": 12,
|
|
399
517
|
"ntplfle": 18, "ninsfle": 7, "pestmode": "regularisation"}
|
|
400
518
|
}
|
|
401
519
|
```
|
|
@@ -407,6 +525,15 @@ realization and `--set` values (and, from a workbook, the resolved build argumen
|
|
|
407
525
|
left alone, and the backend. A hash mismatch between a manifest and the workbook on disk is
|
|
408
526
|
the signal that the control file no longer corresponds to the spreadsheet.
|
|
409
527
|
|
|
528
|
+
Run `makepst provenance model.pst` (or pass `model.pst.manifest.json`) to compare every
|
|
529
|
+
recorded input and output hash with the files at their recorded paths. It reports
|
|
530
|
+
`unchanged`, `changed`, `missing`, or `not recorded` and exits with status 1 unless all
|
|
531
|
+
records are unchanged. This checks file identity, not the validity of PEST tables; use
|
|
532
|
+
`makepst validate` for the latter. Moving files requires updating their recorded paths.
|
|
533
|
+
`makepst log` lists the manifests of a whole project folder as a ledger (with `--check`,
|
|
534
|
+
each one verified), and `makepst bundle` zips a run with everything its control file
|
|
535
|
+
references — see the two commands above.
|
|
536
|
+
|
|
410
537
|
## Relationship to pyEMU
|
|
411
538
|
|
|
412
539
|
[pyEMU](https://github.com/pypest/pyemu) is the broader toolkit: it constructs PEST++
|
|
@@ -454,7 +581,7 @@ update_workbook('book.xlsm', par='run.par', res='run.res', out='book_results.xls
|
|
|
454
581
|
python -m pytest tests -q
|
|
455
582
|
```
|
|
456
583
|
|
|
457
|
-
|
|
584
|
+
106 tests, no external data needed: a synthetic workbook in the real project layout
|
|
458
585
|
(`tests/data/demo.xlsx`, generated by `tests/make_fixture.py`) with a golden control file,
|
|
459
586
|
`.par`, `.res` and IES ensemble files. They cover golden-file builds from Excel and CSV, the
|
|
460
587
|
`read → write → read` identity, `dump → build` byte equality, workbook updates (multi-sheet
|
|
@@ -476,12 +603,14 @@ makepst/
|
|
|
476
603
|
writer.py Pst -> .pst text (+ dump.tpl)
|
|
477
604
|
reader.py .pst text -> Pst
|
|
478
605
|
excel.py sheets -> Pst; Pst -> workbook; results -> existing workbook
|
|
479
|
-
cli.py init / validate / build / dump / update / parrep / diff
|
|
606
|
+
cli.py init / validate / build / dump / update / parrep / diff / tempchek / inschek / log / bundle
|
|
480
607
|
diff.py semantic comparison of two Pst objects
|
|
481
608
|
phi.py objective function by group from residuals; IES realization phis
|
|
482
609
|
pyemu_bridge.py to_pyemu / from_pyemu
|
|
483
610
|
checks.py the validate report: table checks, template / instruction cross-checks, instruction interpreter
|
|
484
|
-
|
|
611
|
+
rules.py pestchek's rules (parameters, groups, observations, prior information, control variables)
|
|
612
|
+
model_files.py PEST's number writer, template filling, .obf files (tempchek / inschek)
|
|
613
|
+
provenance.py the <output>.manifest.json sidecar; provenance / log / bundle
|
|
485
614
|
starter.py the `init` workbook: control-variable descriptions, example rows, drop-downs
|
|
486
615
|
examples/minimal/ the tutorial inputs
|
|
487
616
|
tests/ suite + fixture generator
|
|
@@ -494,7 +623,7 @@ stored anywhere. To release: bump the version in `pyproject.toml` and the fallba
|
|
|
494
623
|
`makepst/__init__.py` (a test keeps them equal), add a `CHANGELOG.md` entry, commit, then
|
|
495
624
|
|
|
496
625
|
```
|
|
497
|
-
git tag v0.
|
|
626
|
+
git tag v0.3.0 && git push origin main v0.3.0
|
|
498
627
|
```
|
|
499
628
|
|
|
500
629
|
The `publish` workflow checks that the tag matches the package version, runs the tests, builds
|
|
@@ -14,8 +14,11 @@ reads control files back into editable workbooks, and returns calibration result
|
|
|
14
14
|
Excel / CSV tables -> .pst makepst build
|
|
15
15
|
.pst -> editable workbook makepst dump
|
|
16
16
|
.par / .res / .rei / IES ensemble -> existing workbook makepst update
|
|
17
|
-
.par or IES realization (+ tweaks) -> new .pst makepst parrep
|
|
17
|
+
.par or IES realization (+ tweaks) -> new .pst makepst parrep
|
|
18
|
+
.res/.rei or IES obs + existing .hp -> new .hp makepst hpstart
|
|
18
19
|
two .pst / workbooks -> what changed makepst diff
|
|
20
|
+
manifests in a folder -> run ledger makepst log
|
|
21
|
+
.pst + everything it references -> zip for review makepst bundle
|
|
19
22
|
```
|
|
20
23
|
|
|
21
24
|
See [What round-tripping preserves](#what-round-tripping-preserves) for the exact guarantees
|
|
@@ -89,6 +92,10 @@ makepst build model.pst regul --set_ctl_csv control.csv --add_pargp_csv pargp.cs
|
|
|
89
92
|
# 2. check it against the template / instruction / output files, like pestchek
|
|
90
93
|
makepst validate model.pst --outputs
|
|
91
94
|
|
|
95
|
+
# 2b. what PEST will do with them: write the model input file, read the model output files
|
|
96
|
+
makepst tempchek model.pst --dry_run
|
|
97
|
+
makepst inschek model.pst
|
|
98
|
+
|
|
92
99
|
# 3. control file -> workbook (CONTROL / PARGP / PAR / OBS / PRIOR / IO sheets + a BUILD sheet)
|
|
93
100
|
makepst dump model.pst model.xlsx
|
|
94
101
|
|
|
@@ -123,8 +130,10 @@ makepst validate model.pst --outputs
|
|
|
123
130
|
makepst validate book.xlsm --strict
|
|
124
131
|
```
|
|
125
132
|
|
|
126
|
-
See [Validation](#validation). Template and instruction paths are resolved
|
|
127
|
-
|
|
133
|
+
See [Validation](#validation). Template and instruction paths are resolved the way PEST
|
|
134
|
+
resolves them, from the directory PEST is run in: makePst tries the control file's folder,
|
|
135
|
+
the current directory and the control file's parent, uses the one where the files are, and
|
|
136
|
+
says which; `--base_dir` overrides that. `--strict` also fails on warnings,
|
|
128
137
|
`--quiet` hides the informational lines.
|
|
129
138
|
|
|
130
139
|
### build — tables → .pst
|
|
@@ -186,6 +195,7 @@ makepst update book.xlsm --res run.res # adds MODELLED
|
|
|
186
195
|
makepst update book.xlsm --pst run.pst --par_cols PARVAL1,PARTRANS,PARLBND,PARUBND
|
|
187
196
|
makepst update book.xlsm --par run.3.par.csv --real best # PESTPP-IES ensemble
|
|
188
197
|
makepst update book.xlsm --obs_csv run.3.obs.csv --pst run.pst # IES simulated values
|
|
198
|
+
makepst update book.xlsm --par run.par --dry_run # preview; write nothing
|
|
189
199
|
[--sheet "PAR_*"] [--group hk,sy] [--overwrite_formulas] [--out copy.xlsm] [--backend openpyxl|xlwings]
|
|
190
200
|
```
|
|
191
201
|
|
|
@@ -194,10 +204,19 @@ makepst update book.xlsm --obs_csv run.3.obs.csv --pst run.pst # IES simulated
|
|
|
194
204
|
|
|
195
205
|
Every worksheet whose header row contains `PARNME` (or `OBSNME`) is matched row by row on the
|
|
196
206
|
name, so tables spread over several sheets are all found; only the requested columns are
|
|
197
|
-
written and only for names present in the
|
|
198
|
-
|
|
207
|
+
written and only for names present in both: names in the results with no row in the workbook,
|
|
208
|
+
and workbook rows absent from the results, are left alone and reported as a warning with
|
|
209
|
+
counts and examples. Cells that hold formulas are left alone unless `--overwrite_formulas`;
|
|
210
|
+
cells inside an array formula or a dynamic-array spill range are never written (Excel would
|
|
211
|
+
refuse), only counted. `--sheet` (glob, case-insensitive) restricts the worksheets;
|
|
199
212
|
`--group` restricts the parameter / observation groups (a `.par` file carries no groups, so
|
|
200
|
-
add `--pst` to supply them).
|
|
213
|
+
add `--pst` to supply them). `--pst` on its own writes the control file's parameter and
|
|
214
|
+
observation columns; next to `--par`, `--res` or `--obs_csv` it only supplies groups and
|
|
215
|
+
measured values, and the observation sheets are not touched by a parameter update.
|
|
216
|
+
|
|
217
|
+
The xlwings backend reads only each sheet's header, name column and target columns, writes
|
|
218
|
+
contiguous runs of cells in one call each and recalculates once before saving: the
|
|
219
|
+
demonstration workbook (27 sheets, 1,068 parameters over eight sheets) updates in about 9 s.
|
|
201
220
|
|
|
202
221
|
PESTPP-IES ensembles (`case.N.par.csv`, `case.N.obs.csv`) are recognised by their
|
|
203
222
|
`real_name` header. `--real` picks the realization: a name (`base`, `17`) or `best` — the
|
|
@@ -214,8 +233,13 @@ Both sheets are rewritten on each run (`--no_phi` to skip).
|
|
|
214
233
|
|
|
215
234
|
Two backends: **xlwings** (used when installed; drives Excel invisibly, so everything in the
|
|
216
235
|
workbook is preserved and formulas recalculate; Windows/macOS with Excel) or **openpyxl**
|
|
217
|
-
(no Excel needed; keeps VBA macros; drops charts and images;
|
|
218
|
-
|
|
236
|
+
(no Excel needed; keeps VBA macros; drops charts and images; does not recalculate formulas).
|
|
237
|
+
Before building from a workbook updated with openpyxl, recalculate and save it in Excel;
|
|
238
|
+
otherwise the build may read missing or stale cached formula values. The backend used is printed.
|
|
239
|
+
`--dry_run` uses openpyxl to preview matched names, target columns, skipped formulas and
|
|
240
|
+
unmatched names without saving a workbook or manifest. It does not test whether Excel will
|
|
241
|
+
refuse individual writes. During `build`, missing cached formula results in referenced
|
|
242
|
+
tables produce a warning; existing cached values may still be stale and cannot be verified.
|
|
219
243
|
|
|
220
244
|
### parrep — .par values → new .pst
|
|
221
245
|
|
|
@@ -237,6 +261,61 @@ to that workbook; other relative names resolve against the workbook's folder. No
|
|
|
237
261
|
route rebuilds from the workbook's *current* state, so any edits made since the original
|
|
238
262
|
control file was written are picked up too.
|
|
239
263
|
|
|
264
|
+
### hpstart — modeled observations → new PEST_HP .hp file
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
makepst hpstart case.pst start.hp --template case.hp --res case.res
|
|
268
|
+
makepst hpstart case.pst start.hp --template case.hp --obs_csv case.3.obs.csv --real best
|
|
269
|
+
makepst hpstart case.pst start.hp --template case.hp --res case.rei --par case.par
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
By default, parameter values are copied unchanged from the template `.hp`, **not** from
|
|
273
|
+
`PARVAL1` in the `.pst`. Use `--par` to replace them with values from a PEST `.par` file
|
|
274
|
+
or a selected IES parameter realization. The `.pst` supplies parameter and observation
|
|
275
|
+
names and order; the template supplies PEST_HP instruction metadata that a residual or
|
|
276
|
+
IES observation file cannot provide. `hpstart` replaces the template's reference
|
|
277
|
+
modeled-observation array in `.pst` observation order. Every required observation must
|
|
278
|
+
be present with a finite value; extra result rows are ignored. The template must come
|
|
279
|
+
from the same control-file layout and
|
|
280
|
+
observation/parameter order. The binary format stores counts but not names, so matching
|
|
281
|
+
counts alone cannot establish that the template has the right order. The original `.hp`
|
|
282
|
+
is never modified, and the new file receives a provenance manifest by default.
|
|
283
|
+
|
|
284
|
+
### tempchek — templates → model input files
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
makepst tempchek model.tpl # check the template, list its parameters
|
|
288
|
+
makepst tempchek model.tpl model.in [model.par] # write model.in (values from model.par by default)
|
|
289
|
+
makepst tempchek model.pst [--par run.par] [--real best] [--dry_run] # every template of the case
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Like PEST's TEMPCHEK: a template is checked, then its model input file is written with
|
|
293
|
+
parameter values from a `.par` file (or, given a control file / workbook, from `PARVAL1`
|
|
294
|
+
with `SCALE` and `OFFSET` applied, or from `--par` — a `.par` file or an IES realization).
|
|
295
|
+
Numbers are written the way PEST writes them (its `WRTSIG`): into the parameter space with
|
|
296
|
+
as much precision as the space allows, fixed or exponential notation, right-justified, one
|
|
297
|
+
word per parameter sized for its narrowest space, `PRECIS` / `DPOINT` honoured
|
|
298
|
+
(`double`, `nopoint`). Checked against `tempchek.exe` on a grid of values and widths from 3
|
|
299
|
+
to 23 characters: the same fields are refused, and the written value is never further from
|
|
300
|
+
the true value than PEST's. A value that cannot be written (`1e-10` in a five-character
|
|
301
|
+
space, say) is an error and nothing is written; parameters in the `.par` file that the
|
|
302
|
+
template does not cite are warnings. `--dry_run` checks without writing.
|
|
303
|
+
|
|
304
|
+
### inschek — model output files → observation values
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
makepst inschek heads.ins # check the instruction file, list its observations
|
|
308
|
+
makepst inschek heads.ins heads.out [--out heads.obf]
|
|
309
|
+
makepst inschek model.pst [--out model.obf] # every instruction file of the case, one .obf
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Like PEST's INSCHEK: the instruction file is checked (the syntax rules in the validation
|
|
313
|
+
table below, including that a line advance can only start an instruction line and that fixed
|
|
314
|
+
/ tab instructions must move right), then read against the model output file with the
|
|
315
|
+
interpreter that `validate --outputs` uses, and the values go to an observation value file
|
|
316
|
+
(`name value` per line, INSCHEK's `.obf`). With a control file, all instruction files are
|
|
317
|
+
read and observations the control file lists but no file produced are reported.
|
|
318
|
+
|
|
240
319
|
### diff — what changed
|
|
241
320
|
|
|
242
321
|
```
|
|
@@ -252,6 +331,39 @@ tolerance, so `49.7377` vs `49.73775` shows up at the default `1e-9` and disappe
|
|
|
252
331
|
`--rtol 1e-5`. Prints a table per section and a one-line summary; `--xlsx` writes the same
|
|
253
332
|
tables to a workbook for review. Exits 1 when there are differences, like `diff`.
|
|
254
333
|
|
|
334
|
+
### log — the run ledger
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
makepst log [FOLDER ...] [--file tr13.xlsm] [--command build] [--last 10] [--check] [--no_recursive]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Every manifest under the folders (default: the current one, recursively), oldest first, one
|
|
341
|
+
line each: when, which command, which output, from which sources, the dimensions, the
|
|
342
|
+
makePst version. `--file` keeps the entries whose output or sources include a file — by name,
|
|
343
|
+
path or **sha256 prefix**, so `--file b43fc509` answers "which runs used *that* version of
|
|
344
|
+
the workbook?" even after the file was renamed or overwritten. `--check` adds a column from
|
|
345
|
+
`provenance`: `unchanged` / `changed` / `missing`.
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
2026-09-19 21:14 build pest/tr13.pst <- tr13.xlsm [1068 par 41747 obs 159 prior] makepst 0.3.0
|
|
349
|
+
2026-09-21 08:10 update tr13-xlwings.xlsm <- tr13.xlsm, tr13.par [1068 rows in 8 sheets] makepst 0.3.0
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### bundle — a run, zipped for review
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
makepst bundle model.pst [run.zip] [--sources] [--outputs] [--extra "bin/**"] [--list] [--strict]
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Zips the control file with its manifest, every template and instruction file, the model
|
|
359
|
+
input files the templates write, and any file named on the model command line, with paths
|
|
360
|
+
relative to the run directory so the archive unzips runnable. `--sources` adds the manifest's
|
|
361
|
+
sources (the workbook, the `.par` ...), `--outputs` the model output files, `--extra` any
|
|
362
|
+
glob relative to the run directory (the model binaries, say). Files the control file
|
|
363
|
+
references but that don't exist are listed as `MISSING` (exit 1 with `--strict`); `--list`
|
|
364
|
+
shows what would be bundled without writing. The zip gets its own manifest, so `log` shows
|
|
365
|
+
who bundled what and when.
|
|
366
|
+
|
|
255
367
|
## What round-tripping preserves
|
|
256
368
|
|
|
257
369
|
`dump` → `build` and `read_pst` → `write_pst` are **semantically lossless for supported
|
|
@@ -327,6 +439,12 @@ makepst validate tr13.xlsm # a workbook with a BUILD sheet is built f
|
|
|
327
439
|
| malformed prior equation; non-numeric right-hand side; `log()` used on a non-log parameter or vice versa | error |
|
|
328
440
|
| `DERCOM` beyond the number of model command lines | error |
|
|
329
441
|
| name longer than PEST's limit (12 parameter / 20 observation / 12 group; PEST++ allows 200) | warning |
|
|
442
|
+
| **pestchek's rules** (from PEST 17's `pestchek.F` / `cheksub.F`): `PARCHGLIM` vocabulary and `absolute(n)` needing `absparmax(n)=`; log parameters factor-limited with positive values; factor-limited bounds of one sign and non-zero; `SCALE ≠ 0`; tied to itself, tied or parent with initial value 0; group `none` reserved; all parameters fixed/tied; `INCTYP` / `FORCEN` / `DERMTHD` vocabularies and their compatibility; split-column ranges; duplicate groups; `dum` as an observation name; prior weights, labels (≤ 20, not an observation name), duplicated parameters, all-zero factors, group `predict`; regularisation needs a `regul*` group, prediction exactly one `predict` observation; ~60 control-variable range and consistency rules (`RLAMFAC`, `PHIRATSUF`, `FACPARMAX`, `NPHISTP`, `RELPARSTP < RELPARMAX`, `EIGTHRESH`, `PHIMLIM < PHIMACCEPT < 1.2·PHIMLIM`, `WFMIN ≤ WFINIT ≤ WFMAX`, `UPTESTMIN`/`UPTESTLIM`, SVD vs AUI vs LSQR exclusivity, …) | error |
|
|
443
|
+
| pestchek's warnings: derivative increment larger than a third of the range; initial value 0 with a relative increment; group with only fixed/tied parameters; log parameters with a non-relative increment; observation group listed but empty (dropped on write); `MAXSING` above the adjustable-parameter count; more than 300 adjustable parameters with `ICOV`/`ICOR`/`IEIG` on; `NOPTMAX 0` | warning |
|
|
444
|
+
| PEST_HP-only control variables present (`WIN_MRUN_HOURS`, `UPTESTMIN`, `RRFSAVE`, …; plain PEST needs `/hpstart`); observation group whose weights are all zero | note |
|
|
445
|
+
| template: parameter space narrower than 3 characters or containing a tab | error |
|
|
446
|
+
| template: a value that cannot be written into its space as PEST writes numbers (`PRECIS` / `DPOINT` honoured), e.g. `1e-10` in five characters | error |
|
|
447
|
+
| instruction file syntax: `l` / `t` integers, marker delimiters, `!` balance, `[name]n1:n2` order, `dum` in a fixed field, continuation `&` placement, `l` only at the start of a line, `t` and fixed columns moving left to right, unknown instructions — checked statically, without model output | error |
|
|
330
448
|
| section in the `.pst` that makePst drops on read | warning |
|
|
331
449
|
| template or instruction file missing; bad `ptf` / `pif` line | error |
|
|
332
450
|
| parameter cited in no template; template citing an unknown parameter | error |
|
|
@@ -352,7 +470,7 @@ from?" after the fact:
|
|
|
352
470
|
|
|
353
471
|
```json
|
|
354
472
|
{
|
|
355
|
-
"makepst": "0.
|
|
473
|
+
"makepst": "0.3.0",
|
|
356
474
|
"command": "build",
|
|
357
475
|
"argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsm,CONTROL", "..."],
|
|
358
476
|
"created": "2026-09-19T13:51:39-04:00",
|
|
@@ -364,7 +482,7 @@ from?" after the fact:
|
|
|
364
482
|
"sheets": ["CONTROL", "PARGP", "PAR_HK", "PAR_VK", "...", "PPglm"]}
|
|
365
483
|
],
|
|
366
484
|
"output": {"path": "D:\\...\\tr13.pst", "sha256": "d93f317d...", "size": 2163331,
|
|
367
|
-
"npar": 1068, "nobs": 41747, "npargp": 12, "nprior":
|
|
485
|
+
"npar": 1068, "nobs": 41747, "npargp": 12, "nprior": 159, "nobsgp": 12,
|
|
368
486
|
"ntplfle": 18, "ninsfle": 7, "pestmode": "regularisation"}
|
|
369
487
|
}
|
|
370
488
|
```
|
|
@@ -376,6 +494,15 @@ realization and `--set` values (and, from a workbook, the resolved build argumen
|
|
|
376
494
|
left alone, and the backend. A hash mismatch between a manifest and the workbook on disk is
|
|
377
495
|
the signal that the control file no longer corresponds to the spreadsheet.
|
|
378
496
|
|
|
497
|
+
Run `makepst provenance model.pst` (or pass `model.pst.manifest.json`) to compare every
|
|
498
|
+
recorded input and output hash with the files at their recorded paths. It reports
|
|
499
|
+
`unchanged`, `changed`, `missing`, or `not recorded` and exits with status 1 unless all
|
|
500
|
+
records are unchanged. This checks file identity, not the validity of PEST tables; use
|
|
501
|
+
`makepst validate` for the latter. Moving files requires updating their recorded paths.
|
|
502
|
+
`makepst log` lists the manifests of a whole project folder as a ledger (with `--check`,
|
|
503
|
+
each one verified), and `makepst bundle` zips a run with everything its control file
|
|
504
|
+
references — see the two commands above.
|
|
505
|
+
|
|
379
506
|
## Relationship to pyEMU
|
|
380
507
|
|
|
381
508
|
[pyEMU](https://github.com/pypest/pyemu) is the broader toolkit: it constructs PEST++
|
|
@@ -423,7 +550,7 @@ update_workbook('book.xlsm', par='run.par', res='run.res', out='book_results.xls
|
|
|
423
550
|
python -m pytest tests -q
|
|
424
551
|
```
|
|
425
552
|
|
|
426
|
-
|
|
553
|
+
106 tests, no external data needed: a synthetic workbook in the real project layout
|
|
427
554
|
(`tests/data/demo.xlsx`, generated by `tests/make_fixture.py`) with a golden control file,
|
|
428
555
|
`.par`, `.res` and IES ensemble files. They cover golden-file builds from Excel and CSV, the
|
|
429
556
|
`read → write → read` identity, `dump → build` byte equality, workbook updates (multi-sheet
|
|
@@ -445,12 +572,14 @@ makepst/
|
|
|
445
572
|
writer.py Pst -> .pst text (+ dump.tpl)
|
|
446
573
|
reader.py .pst text -> Pst
|
|
447
574
|
excel.py sheets -> Pst; Pst -> workbook; results -> existing workbook
|
|
448
|
-
cli.py init / validate / build / dump / update / parrep / diff
|
|
575
|
+
cli.py init / validate / build / dump / update / parrep / diff / tempchek / inschek / log / bundle
|
|
449
576
|
diff.py semantic comparison of two Pst objects
|
|
450
577
|
phi.py objective function by group from residuals; IES realization phis
|
|
451
578
|
pyemu_bridge.py to_pyemu / from_pyemu
|
|
452
579
|
checks.py the validate report: table checks, template / instruction cross-checks, instruction interpreter
|
|
453
|
-
|
|
580
|
+
rules.py pestchek's rules (parameters, groups, observations, prior information, control variables)
|
|
581
|
+
model_files.py PEST's number writer, template filling, .obf files (tempchek / inschek)
|
|
582
|
+
provenance.py the <output>.manifest.json sidecar; provenance / log / bundle
|
|
454
583
|
starter.py the `init` workbook: control-variable descriptions, example rows, drop-downs
|
|
455
584
|
examples/minimal/ the tutorial inputs
|
|
456
585
|
tests/ suite + fixture generator
|
|
@@ -463,7 +592,7 @@ stored anywhere. To release: bump the version in `pyproject.toml` and the fallba
|
|
|
463
592
|
`makepst/__init__.py` (a test keeps them equal), add a `CHANGELOG.md` entry, commit, then
|
|
464
593
|
|
|
465
594
|
```
|
|
466
|
-
git tag v0.
|
|
595
|
+
git tag v0.3.0 && git push origin main v0.3.0
|
|
467
596
|
```
|
|
468
597
|
|
|
469
598
|
The `publish` workflow checks that the tag matches the package version, runs the tests, builds
|