makepst 0.3.0__tar.gz → 0.5.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.
Files changed (86) hide show
  1. makepst-0.5.0/CHANGELOG.md +143 -0
  2. {makepst-0.3.0 → makepst-0.5.0}/CITATION.cff +2 -2
  3. {makepst-0.3.0 → makepst-0.5.0}/MANIFEST.in +1 -0
  4. {makepst-0.3.0 → makepst-0.5.0}/PKG-INFO +196 -51
  5. {makepst-0.3.0 → makepst-0.5.0}/README.md +195 -50
  6. {makepst-0.3.0 → makepst-0.5.0}/makepst/__init__.py +6 -4
  7. {makepst-0.3.0 → makepst-0.5.0}/makepst/checks.py +8 -5
  8. {makepst-0.3.0 → makepst-0.5.0}/makepst/cli.py +334 -32
  9. {makepst-0.3.0 → makepst-0.5.0}/makepst/diff.py +49 -2
  10. {makepst-0.3.0 → makepst-0.5.0}/makepst/excel.py +5 -0
  11. {makepst-0.3.0 → makepst-0.5.0}/makepst/hpstart.py +4 -2
  12. makepst-0.5.0/makepst/pestpp.py +244 -0
  13. {makepst-0.3.0 → makepst-0.5.0}/makepst/provenance.py +9 -0
  14. makepst-0.5.0/makepst/pst.py +944 -0
  15. {makepst-0.3.0 → makepst-0.5.0}/makepst/reader.py +65 -21
  16. makepst-0.5.0/makepst/reweight.py +344 -0
  17. {makepst-0.3.0 → makepst-0.5.0}/makepst/rules.py +102 -4
  18. {makepst-0.3.0 → makepst-0.5.0}/makepst/sections.py +77 -19
  19. {makepst-0.3.0 → makepst-0.5.0}/makepst/starter.py +9 -0
  20. makepst-0.5.0/makepst/writer.py +291 -0
  21. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/PKG-INFO +196 -51
  22. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/SOURCES.txt +14 -1
  23. {makepst-0.3.0 → makepst-0.5.0}/pyproject.toml +1 -1
  24. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.par +1 -1
  25. {makepst-0.3.0 → makepst-0.5.0}/tests/make_fixture.py +2 -1
  26. {makepst-0.3.0 → makepst-0.5.0}/tests/test_checks.py +10 -2
  27. makepst-0.5.0/tests/test_convenience.py +113 -0
  28. {makepst-0.3.0 → makepst-0.5.0}/tests/test_diff.py +22 -3
  29. {makepst-0.3.0 → makepst-0.5.0}/tests/test_examples.py +31 -0
  30. {makepst-0.3.0 → makepst-0.5.0}/tests/test_fixture.py +50 -2
  31. {makepst-0.3.0 → makepst-0.5.0}/tests/test_makepst.py +46 -10
  32. makepst-0.5.0/tests/test_optional_positions.py +93 -0
  33. makepst-0.5.0/tests/test_optional_positions_variations.py +19 -0
  34. makepst-0.5.0/tests/test_pest_hp.py +172 -0
  35. makepst-0.5.0/tests/test_pestpp.py +141 -0
  36. makepst-0.5.0/tests/test_rescale.py +193 -0
  37. makepst-0.5.0/tests/test_reweight.py +240 -0
  38. makepst-0.5.0/tests/test_rule_sources.py +152 -0
  39. {makepst-0.3.0 → makepst-0.5.0}/tests/test_rules.py +18 -0
  40. makepst-0.5.0/tests/test_set_par.py +166 -0
  41. makepst-0.5.0/tools/rule_sources.py +353 -0
  42. makepst-0.5.0/tools/snapshots/pestchek.json +719 -0
  43. makepst-0.5.0/tools/snapshots/pestpp.json +322 -0
  44. makepst-0.3.0/CHANGELOG.md +0 -73
  45. makepst-0.3.0/makepst/pst.py +0 -449
  46. makepst-0.3.0/makepst/writer.py +0 -164
  47. makepst-0.3.0/tests/test_convenience.py +0 -60
  48. {makepst-0.3.0 → makepst-0.5.0}/LICENSE +0 -0
  49. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/control.csv +0 -0
  50. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/flow.ins +0 -0
  51. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/flow.out +0 -0
  52. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/heads.ins +0 -0
  53. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/heads.out +0 -0
  54. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/io.csv +0 -0
  55. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/model.in +0 -0
  56. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/model.par +0 -0
  57. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/model.py +0 -0
  58. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/model.res +0 -0
  59. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/model.tpl +0 -0
  60. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/obs.csv +0 -0
  61. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/par.csv +0 -0
  62. {makepst-0.3.0 → makepst-0.5.0}/examples/minimal/pargp.csv +0 -0
  63. {makepst-0.3.0 → makepst-0.5.0}/makepst/__main__.py +0 -0
  64. {makepst-0.3.0 → makepst-0.5.0}/makepst/model_files.py +0 -0
  65. {makepst-0.3.0 → makepst-0.5.0}/makepst/phi.py +0 -0
  66. {makepst-0.3.0 → makepst-0.5.0}/makepst/pyemu_bridge.py +0 -0
  67. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/dependency_links.txt +0 -0
  68. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/entry_points.txt +0 -0
  69. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/requires.txt +0 -0
  70. {makepst-0.3.0 → makepst-0.5.0}/makepst.egg-info/top_level.txt +0 -0
  71. {makepst-0.3.0 → makepst-0.5.0}/setup.cfg +0 -0
  72. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.3.obs.csv +0 -0
  73. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.3.par.csv +0 -0
  74. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.phi.actual.csv +0 -0
  75. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.pst +0 -0
  76. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.res +0 -0
  77. {makepst-0.3.0 → makepst-0.5.0}/tests/data/demo.xlsx +0 -0
  78. {makepst-0.3.0 → makepst-0.5.0}/tests/test_hpstart.py +0 -0
  79. {makepst-0.3.0 → makepst-0.5.0}/tests/test_log_bundle.py +0 -0
  80. {makepst-0.3.0 → makepst-0.5.0}/tests/test_model_files.py +0 -0
  81. {makepst-0.3.0 → makepst-0.5.0}/tests/test_phi.py +0 -0
  82. {makepst-0.3.0 → makepst-0.5.0}/tests/test_provenance.py +0 -0
  83. {makepst-0.3.0 → makepst-0.5.0}/tests/test_pyemu.py +0 -0
  84. {makepst-0.3.0 → makepst-0.5.0}/tests/test_starter.py +0 -0
  85. {makepst-0.3.0 → makepst-0.5.0}/tests/test_v2.py +0 -0
  86. {makepst-0.3.0 → makepst-0.5.0}/tests/test_version.py +0 -0
@@ -0,0 +1,143 @@
1
+ # Changelog
2
+
3
+ ## 0.5.0 (2026-09-29)
4
+
5
+ - `tools/rule_sources.py`: are `validate`'s rules still current? `pestpp` re-reads PEST++'s option
6
+ parser from GitHub (names, aliases, types, choices, deprecations); `pestchek --source DIR` reads a
7
+ PEST download's `pestchek.F` / `cheksub.F` (control-data keywords and every message). Each is
8
+ compared with the reviewed snapshot in `tools/snapshots/` and with makePst's own tables; exit 1 on
9
+ a change, 2 when the source cannot be read; `--write` records a reviewed source. Tests hold
10
+ `makepst/pestpp.py` and the control-data schema to the snapshots, and a weekly GitHub workflow
11
+ (`.github/workflows/rule-sources.yml`) opens an issue when PEST++'s parser changes. The PEST++
12
+ registry is unchanged; its provenance now names commit 02e1455 (5.2.29) - the commit named before,
13
+ ad8428d, has identical option parsing but is labelled 5.2.28 in the source.
14
+
15
+ - `build --dry_run`: the `validate` report on the control file a build would write, from a workbook's
16
+ BUILD sheet or from tables named on the command line; nothing is written, no manifest; exit 1 on
17
+ errors. A workbook with no BUILD sheet now says how to name its tables and check them this way.
18
+ - `validate` on a workbook judges it as `build` would write it, after the same normalization
19
+ (regularisation equations from `PRIOR`/`WEIGHT`, unused groups removed, ties to fixed parameters
20
+ fixed), listing those as notes. It used to judge the tables before that and report errors building
21
+ removes: a regularisation file with no "regul" group, prior information not generated.
22
+
23
+ - PEST_HP control variables are treated as `++` options are: a control-data token makePst does not
24
+ know (`newhpvar=7`, a new flag) is kept with the line it came from and written back there, instead
25
+ of being dropped, and `validate` warns about it with the closest known name. `dump` writes it to
26
+ the CONTROL sheet with its line (`control data line 4`), so `build` puts it back.
27
+ - Every PEST_HP variable PEST 17.2's pestchek reads is now known: `run_slow_fac`, `run_abandon_fac`,
28
+ `jcowarnthresh`, `jcozerothresh`, `zerosenval`, `hardstophours`, `softstophours`, `reg2measrat`,
29
+ `orr_not_first`, and `obsreref_N`; `name = value` with spaces around `=` is read, as PEST_HP does.
30
+ - `validate` checks that PEST can read each control value: an integer or a number where one is
31
+ expected (Fortran `1d-3` included), a known word for `precis`, `dpoint` and the other flags,
32
+ `absparmax(n)=value` with n 1-10; and the PEST_HP ranges and combinations (`RUN_ABANDON_FAC` 0 or
33
+ at least 1.2, not both stop-hours variables, `JCOZEROTHRESH` above `JCOWARNTHRESH`, `REG2MEASRAT`
34
+ below 1, `orr_not_first` and `ZEROSENVAL` only with what they need).
35
+ - The rule sources are pinned and reported: `PESTCHEK_SOURCE_VERSION` (17.2) beside
36
+ `PESTPP_REGISTRY_VERSION` (5.2.29); `validate` prints both and every manifest records them under
37
+ `checked_against`.
38
+
39
+ ## 0.4.0 (2026-09-27)
40
+
41
+ - `rescale` / `Pst.rescale_par()`: move parameter values into `SCALE` so each selected parameter
42
+ starts at 1 (bounds divided by the value), or back with `--undo`; the model sees the same values.
43
+ Selection by `--par` globs and `--pargp` (fixed parameters included unless `--exclude_fixed`;
44
+ tied children follow their parent). Prior-information equations are rewritten exactly (every
45
+ residual is unchanged), and settings in parameter units that cannot be converted (absolute
46
+ derivative increments, `absolute(n)` limits, `++parcov` / `++base_jacobian` / `++ies_par_en`)
47
+ are reported.
48
+ - `fill_parval` (`build --fill_parval`, `parrep`), `tempchek --par` and `hpstart --par` convert a
49
+ `.par` value written under a different `SCALE` / `OFFSET` than the control file's through the
50
+ model value, instead of copying `PARVAL1` across. The test fixture's `demo.par` now records the
51
+ parameters' own offsets, as PEST writes them.
52
+ - Writing a control file no longer fails on an adjustable `PARVAL1` outside its bounds (a `parrep`
53
+ from an IES realization, say): it is a warning, as documented, and the file is written.
54
+ `makepst validate` still reports it as an error. `diff` normalizes a workbook side, as `build`
55
+ would, so a workbook compares equal to the control file built from it.
56
+
57
+ - `reweight`: balance observation-group contributions equally or to relative target shares,
58
+ multiply selected group weights directly, match weights to the current misfit with
59
+ `--discrepancy group|obs` (group phi = weighted-observation count, or `w = min(w, 1/|r|)`), consume `.res`/IES observation results, enforce
60
+ optional weight bounds with `--clip`, and write an auditable before/after report.
61
+
62
+ - `++` options are checked against PEST++ 5.2.29's option list (`makepst/pestpp.py`): a known option
63
+ with a value PEST++ would not accept, or the same option twice (also through an alias), is an error;
64
+ an unknown option is a warning with the closest known name (`did you mean "ies_num_reals"?`) and is
65
+ kept as written. Findings name the workbook cell, csv line or control-file line the option came from
66
+ (`PP!A17`). `validate` reports them; `build`, `parrep` and `set` print them but still write.
67
+
68
+ - Unknown control-file sections are retained verbatim, including their original header,
69
+ body lines and position, instead of being dropped during a read/write round trip.
70
+ - `Pst.validate()` now returns a read-only report; `apply_fixes(report)` and `normalize()` make
71
+ the previous tied-parameter, unused-group and prior-information cleanups explicit.
72
+
73
+ ## 0.3.0 (2026-09-21)
74
+
75
+ - `log`: ledger of every manifest under a folder — when, which command, which output from
76
+ which sources, dimensions, version; `--file` filters by file name, path or sha256 prefix
77
+ ("which runs used that version of the workbook?"), `--command`, `--last`, `--check`
78
+ verifies every recorded hash
79
+ - `bundle`: zip a run for reproduction or review — control file, manifest, templates,
80
+ instruction files, model inputs, command-line files; `--sources`, `--outputs`, `--extra`,
81
+ `--list`, `--strict`; the zip gets its own manifest
82
+ - `provenance`: compare the hashes recorded in a manifest with the files on disk
83
+ (`unchanged` / `changed` / `missing`; exit 1 unless all unchanged)
84
+ - `update --dry_run`: preview matched names, target columns and skipped cells without writing
85
+ - `build` warns when a referenced sheet has formulas without cached values (a workbook saved
86
+ by openpyxl and not yet recalculated in Excel)
87
+ - `hpstart`: a PEST_HP `.hp` file with observation values from residuals or an IES realization
88
+
89
+ ## 0.2.0 (2026-09-19)
90
+
91
+ - `update`: the xlwings backend reads only headers, name and target columns, writes contiguous
92
+ cells in one call per run, suspends events and calculation while writing and recalculates
93
+ once before saving (1,068 parameters over eight sheets of a 27-sheet workbook: ~9 s, was
94
+ minutes); `--par` with `--pst` no longer rewrites the observation sheets
95
+ - `tempchek`: check a template file or write model input files from parameter values, with
96
+ PEST's number writer (maximum precision in the space, `PRECIS` / `DPOINT`, one word per
97
+ parameter, right-justified; checked against `tempchek.exe`); for a template + `.par` file
98
+ or for a whole case
99
+ - `inschek`: check an instruction file or read a model output file with it into an `.obf`
100
+ file, for one file or a whole case
101
+ - `validate`: values that cannot be written into their template space are errors; a line
102
+ advance not at the start of an instruction line and `t` / fixed instructions moving left
103
+ are errors (INSCHEK's rules); three more pestchek warnings (empty observation group,
104
+ `ICOV`/`ICOR`/`IEIG` with more than 300 adjustable parameters, PEST_HP-only variables —
105
+ the last as a note); an all-zero-weight observation group is now a note, not a warning
106
+ - `update`: cells inside array formulas or dynamic-array spill ranges are left alone (Excel
107
+ refuses them; xlwings no longer shows a dialog); names present on only one side are reported
108
+ with counts and examples
109
+ - `validate`: pestchek's rules added (parameter data, groups, observations, prior information,
110
+ ~60 control-variable ranges and consistency rules, template and instruction syntax), derived
111
+ from PEST 17's `pestchek.F` / `cheksub.F`; file paths resolved from the directory PEST runs
112
+ in, not only the control file's folder
113
+
114
+ ## 0.1.0 (2026-09-19)
115
+
116
+ First packaged release; replaces the single-file `makePst.py` script.
117
+
118
+ - `init`: starter workbook with headers, defaults, descriptions, drop-down lists, comments and
119
+ a `BUILD` sheet
120
+ - `build`: Excel/CSV tables -> control file; sheet globs (`book,PAR_*`); a workbook with a
121
+ `BUILD` sheet as the only argument; counts computed; PEST_HP keyed tokens and
122
+ `++` options; regularisation equations from `PRIOR`/`WEIGHT` columns; validation of tied
123
+ targets, groups, prior references and bounds
124
+ - `dump`: control file -> workbook with a `BUILD` sheet that rebuilds it
125
+ - `update`: `.par` / `.res` / `.rei` / PESTPP-IES ensembles into an existing workbook, matched
126
+ by name across sheets; formula cells protected; `--sheet` / `--group` filters; `--real`;
127
+ `PHI` sheet (objective function by observation group) and `PHI_IES` (realization phis)
128
+ whenever residuals are available
129
+ - `validate`: pestchek-style report on a control file or workbook: table checks, missing
130
+ files, parameter / observation names against template and instruction files, optional run
131
+ of instruction files against model outputs; exit 1 on errors
132
+ - PEST++ version-2 control files (`pcf version=2`, keyword control data, external csv
133
+ tables) are read and written (`--v2`); observation covariance files are kept
134
+ - `diff`: semantic comparison of two control files / workbooks (tables, effective control
135
+ values, options, io, comments), text report or workbook; exit 1 on differences
136
+ - pyEMU bridge: `to_pyemu()` / `from_pyemu()` via a temporary control file (`pyemu` extra)
137
+ - provenance: every producing command writes `<output>.manifest.json` (version, command
138
+ line, source hashes and sheets, output hash and counts)
139
+ - `parrep`: `.par` or IES realization into a control file (or a workbook with a `BUILD` sheet),
140
+ with `--set NAME=VALUE`
141
+ - Fixes over the old script: `jacupdate` was never written; values were truncated to 6
142
+ significant digits; `phistopthresh` could not be set; SVD / regularisation values in the
143
+ CONTROL sheet were ignored; prior information in estimation mode produced an invalid file
@@ -6,8 +6,8 @@ authors:
6
6
  - family-names: Ou
7
7
  given-names: Michael
8
8
  email: ougengxin@gmail.com
9
- version: 0.3.0
10
- date-released: 2026-09-21
9
+ version: 0.5.0
10
+ date-released: 2026-09-29
11
11
  repository-code: "https://github.com/ougx/makePst"
12
12
  license: MIT
13
13
  keywords:
@@ -1,5 +1,6 @@
1
1
  include LICENSE README.md CHANGELOG.md CITATION.cff
2
2
  recursive-include tests *.py *.xlsx *.pst *.par *.res *.csv
3
+ recursive-include tools *.py *.json
3
4
  recursive-include examples/minimal *.csv *.py *.tpl *.ins *.in *.out *.par *.res
4
5
  prune examples/mnnrd
5
6
  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.0
3
+ Version: 0.5.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,10 @@ 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
49
- .res/.rei or IES obs + existing .hp -> new .hp makepst hpstart
48
+ .par or IES realization (+ tweaks) -> new .pst makepst parrep
49
+ .pst -> values moved to SCALE makepst rescale
50
+ .res/.rei or IES obs + group targets -> reweighted .pst makepst reweight
51
+ .res/.rei or IES obs + existing .hp -> new .hp makepst hpstart
50
52
  two .pst / workbooks -> what changed makepst diff
51
53
  manifests in a folder -> run ledger makepst log
52
54
  .pst + everything it references -> zip for review makepst bundle
@@ -158,7 +160,7 @@ without `--force`.
158
160
 
159
161
  ```
160
162
  makepst validate model.pst --outputs
161
- makepst validate book.xlsm --strict
163
+ makepst validate book.xlsx --strict
162
164
  ```
163
165
 
164
166
  See [Validation](#validation). Template and instruction paths are resolved the way PEST
@@ -167,27 +169,43 @@ the current directory and the control file's parent, uses the one where the file
167
169
  says which; `--base_dir` overrides that. `--strict` also fails on warnings,
168
170
  `--quiet` hides the informational lines.
169
171
 
172
+ A workbook is accepted in place of a control file when it carries a `BUILD` sheet (one that
173
+ `dump` or `init` wrote), which is built first and then checked. For tables that have no
174
+ `BUILD` sheet — a hand-made workbook, or a set of CSV files — name them on a
175
+ [`build`](#build--tables--pst) command line and add `--dry_run`, which runs this same report
176
+ and writes nothing.
177
+
170
178
  ### build — tables → .pst
171
179
 
172
180
  ```
173
181
  makepst build out.pst [estimation|regularisation|prediction|pareto]
174
- --set_ctl_xls book.xlsm,CONTROL (or --set_ctl_csv file.csv)
175
- --add_pargp_xls book.xlsm,PARGP
176
- --add_par_xls book.xlsm,PAR_* (repeatable; sheet names may be globs)
177
- --add_obs_xls book.xlsm,OBS_*
178
- --add_io_xls book.xlsm,IO --add_pp_xls book.xlsm,PPcntl
182
+ --set_ctl_xls book.xlsx,CONTROL (or --set_ctl_csv file.csv)
183
+ --add_pargp_xls book.xlsx,PARGP
184
+ --add_par_xls book.xlsx,PAR_* (repeatable; sheet names may be globs)
185
+ --add_obs_xls book.xlsx,OBS_*
186
+ --add_io_xls book.xlsx,IO --add_pp_xls book.xlsx,PPcntl
179
187
  [--add_tied_xls ...] [--add_prior_xls ...] [--add_comment "text"] [--add_comment_xls ...]
180
188
  [--fill_parval run.par [--real NAME]] [--ss] [--no_dump_tpl] [--v2]
181
189
  ```
182
190
 
183
191
  Each `--add_*` takes `book,SHEET` (`_xls`) or a csv path (`_csv`) and may be repeated;
184
192
  tables split across sheets are concatenated. The sheet name may be a glob:
185
- `--add_par_xls tr13.xlsm,PAR_*` adds every `PAR_…` sheet in workbook order. Headers are
186
- case-insensitive. `makepst build book.xlsm [--out x.pst]` runs the command stored in the
193
+ `--add_par_xls tr13.xlsx,PAR_*` adds every `PAR_…` sheet in workbook order. Headers are
194
+ case-insensitive. `makepst build book.xlsx [--out x.pst]` runs the command stored in the
187
195
  workbook's `BUILD` sheet instead. The mode argument
188
196
  overrides `pestmode` in the CONTROL sheet; adding prior information switches to regularisation.
189
197
  `--ss` drops `ss*`/`sy*` parameters and groups (steady-state runs of a transient setup).
190
198
 
199
+ `--dry_run` builds the control file in memory, runs the full [validation](#validation) report
200
+ on it — including the template, instruction and model files it references — and writes
201
+ nothing (no `.pst`, no `dump.tpl`, no manifest), exiting 1 when there are errors. This is how
202
+ to check a workbook or a set of CSV tables that has no `BUILD` sheet; for one that has a
203
+ `BUILD` sheet, `makepst validate book.xlsx` does the same in one argument.
204
+
205
+ ```bash
206
+ makepst build case.pst --add_par_xls book.xlsx,PAR_* --add_obs_xls book.xlsx,OBS_* ... --dry_run
207
+ ```
208
+
191
209
  | table | columns |
192
210
  |---------|---------|
193
211
  | CONTROL | `NAME`, `VALUE` in the first four columns (`LINE`, `NAME`, `DEFAULT`, `VALUE`); blank values use the built-in default. Any variable of the control data, SVD, LSQR, AUI, SVD-assist or regularisation sections: `noptmax`, `jacupdate`, `lamforgive`, `win_mrun_hours`, `absparmax(1)=0.1 absparmax(2)=20`, `svdmode`, `phimlim`, … Counts are ignored (computed). |
@@ -200,6 +218,15 @@ overrides `pestmode` in the CONTROL sheet; adding prior information switches to
200
218
  | IO | `TYPE` (`cmd` / `tpl` / `ins`), `IN`, `OUT` |
201
219
  | PP | first two columns: PEST++ option name, value → `++name(value)` |
202
220
 
221
+ Classic PEST numeric control settings are positional. If a later setting is supplied,
222
+ all earlier numeric settings on that line must have values after built-in defaults
223
+ are applied. For example, `upvecbend` requires `iboundstick`, and `regsingthresh`
224
+ requires both `noptregadj` and `regweightrat`. Export raises an error naming any
225
+ missing predecessors instead of shifting settings into the wrong slots. Supply
226
+ those values explicitly; trailing optional fields may remain blank. Text flags
227
+ and named tokens such as `absparmax(1)=0.1` do not require numeric fillers. This
228
+ positional requirement does not apply to the version-2 keyword format (`--v2`).
229
+
203
230
  **Regularisation from the parameter table.** In regularisation mode each adjustable
204
231
  parameter with a `PRIOR` and a positive `WEIGHT` gets an equation: a numeric `PRIOR` gives
205
232
  `1.0 * log(p) = log10(v)` (or `1.0 * p = v` for untransformed parameters); a `PRIOR` naming
@@ -221,13 +248,13 @@ to recover a workbook that was lost.
221
248
  ### update — results → existing workbook
222
249
 
223
250
  ```
224
- makepst update book.xlsm --par run.par # PARVAL1
225
- makepst update book.xlsm --res run.res # adds MODELLED / RESIDUAL
226
- makepst update book.xlsm --pst run.pst --par_cols PARVAL1,PARTRANS,PARLBND,PARUBND
227
- makepst update book.xlsm --par run.3.par.csv --real best # PESTPP-IES ensemble
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
230
- [--sheet "PAR_*"] [--group hk,sy] [--overwrite_formulas] [--out copy.xlsm] [--backend openpyxl|xlwings]
251
+ makepst update book.xlsx --par run.par # PARVAL1
252
+ makepst update book.xlsx --res run.res # adds MODELLED / RESIDUAL
253
+ makepst update book.xlsx --pst run.pst --par_cols PARVAL1,PARTRANS,PARLBND,PARUBND
254
+ makepst update book.xlsx --par run.3.par.csv --real best # PESTPP-IES ensemble
255
+ makepst update book.xlsx --obs_csv run.3.obs.csv --pst run.pst # IES simulated values
256
+ makepst update book.xlsx --par run.par --dry_run # preview; write nothing
257
+ [--sheet "PAR_*"] [--group hk,sy] [--overwrite_formulas] [--out copy.xlsx] [--backend openpyxl|xlwings]
231
258
  ```
232
259
 
233
260
  > **`update` saves in place unless `--out` is given.** Use `--out` the first time, and keep
@@ -244,6 +271,8 @@ refuse), only counted. `--sheet` (glob, case-insensitive) restricts the workshee
244
271
  add `--pst` to supply them). `--pst` on its own writes the control file's parameter and
245
272
  observation columns; next to `--par`, `--res` or `--obs_csv` it only supplies groups and
246
273
  measured values, and the observation sheets are not touched by a parameter update.
274
+ Likewise, `--res` or `--obs_csv` with `--pst` leaves parameter cells unchanged unless
275
+ `--par` is also supplied.
247
276
 
248
277
  The xlwings backend reads only each sheet's header, name column and target columns, writes
249
278
  contiguous runs of cells in one call each and recalculates once before saving: the
@@ -272,18 +301,67 @@ unmatched names without saving a workbook or manifest. It does not test whether
272
301
  refuse individual writes. During `build`, missing cached formula results in referenced
273
302
  tables produce a warning; existing cached values may still be stale and cannot be verified.
274
303
 
304
+ ### reweight — balance observation-group weights
305
+
306
+ ```
307
+ makepst reweight run.pst balanced.pst --res run.res --equal
308
+ makepst reweight run.pst balanced.pst --res run.res --targets group_shares.csv
309
+ makepst reweight run.pst scaled.pst --factor heads=0.5 --factor flow=2
310
+ makepst reweight run.pst balanced.pst --obs_csv run.3.obs.csv --real base --equal
311
+ makepst reweight run.pst adjusted.pst --res run.res --discrepancy group
312
+ makepst reweight run.pst adjusted.pst --obs_csv run.0.obs.csv --discrepancy obs
313
+ ```
314
+
315
+ `reweight` writes a new control file; it never overwrites the input. `--equal` gives each
316
+ eligible measurement group the same initial objective-function contribution; a group whose
317
+ current phi is zero cannot be scaled to a share, so it is skipped with an INFO line and its
318
+ weights are left unchanged. `--targets`
319
+ reads a CSV or `BOOK,SHEET` table with `OBGNME,TARGET_SHARE` columns. Shares are relative, so
320
+ `4,3,2` means 44.44%, 33.33% and 22.22%; the selected groups' total phi is preserved. Direct
321
+ `--factor GROUP=NUMBER` specifications multiply the existing positive weights in named groups.
322
+
323
+ `--discrepancy` makes weights consistent with the current misfit, so that each weighted
324
+ observation contributes about 1 to phi. `--discrepancy group` scales each measurement group by
325
+ one factor so its phi equals its number of weighted observations (as PEST's PWTADJ2); relative
326
+ weights inside a group are kept and zero-phi groups are skipped. It raises the weights of groups
327
+ that already fit better than that, so pair it with `--clip` when some groups fit very closely.
328
+ `--discrepancy obs` sets `w = min(w, 1/|residual|)` for each observation (pyEMU's
329
+ `adjust_weights_discrepancy` with its original ceiling): observations the model cannot fit are
330
+ lowered to a contribution of 1, weights are never raised, and zero residuals keep their weight.
331
+ It is commonly applied to the prior mean or base realization before a PESTPP-IES run so that
332
+ observation noise is not smaller than the misfit the prior can reach. Total phi is not preserved
333
+ in either variant.
334
+
335
+ For equal, target and discrepancy modes, residuals are joined by observation name while weights and group
336
+ membership come from the input control file. Groups backed by covariance matrices, `predict`,
337
+ and `regul*` groups are not measurement groups and are rejected or skipped as appropriate.
338
+
339
+ Use `--clip MIN MAX` to constrain final positive weights, for example `--clip 0 10`. Original
340
+ zero weights remain zero. Clipping is included in the balancing calculation; if a requested
341
+ target cannot be reached under the bounds, the command fails instead of reporting a misleading
342
+ result. `--report FILE` writes the before/after group table, including phi, percentages,
343
+ factors, clipping counts and the number of weights lowered (`N_REDUCED`). `--dry_run` prints that table without writing a control file,
344
+ report or manifest.
345
+
346
+ The workbook remains the source of truth. If the control file is rebuilt from a workbook later,
347
+ formula-generated workbook weights will replace weights changed by `reweight` unless the change
348
+ is deliberately transferred back to the workbook.
349
+
275
350
  ### parrep — .par values → new .pst
276
351
 
277
352
  ```
278
353
  makepst parrep run.pst run.par next.pst [--set noptmax=0] [--set NAME=VALUE ...] [--v1|--v2]
279
354
  makepst parrep run.pst run.3.par.csv next.pst --real best
280
- makepst parrep book.xlsm run.par next.pst # base is the workbook
355
+ makepst parrep book.xlsx run.par next.pst # base is the workbook
281
356
  ```
282
357
 
283
358
  Like PEST's PARREP: `PARVAL1` is replaced from a `.par` file or an IES realization and a new
284
359
  control file is written; `--set` changes control values on the way. All supported content of
285
360
  the input control file is preserved semantically (see below); adjustable parameters that end
286
- up outside their bounds are reported.
361
+ up outside their bounds are reported. A `.par` file records the `SCALE` and `OFFSET` each value
362
+ was written with; where those differ from the control file's (a run from before a
363
+ [`rescale`](#rescale--parameter-values--scale)), the value is converted so the model sees the
364
+ same number (`PARVAL1 × SCALE + OFFSET`). An IES ensemble carries no scales and is copied as is.
287
365
 
288
366
  With a **workbook** as the base, the control file is first built from the command in the
289
367
  workbook's `BUILD` sheet (`dump` writes one; for a hand-made workbook paste the build command
@@ -292,6 +370,37 @@ to that workbook; other relative names resolve against the workbook's folder. No
292
370
  route rebuilds from the workbook's *current* state, so any edits made since the original
293
371
  control file was written are picked up too.
294
372
 
373
+ ### rescale — parameter values → SCALE
374
+
375
+ ```
376
+ makepst rescale run.pst run_unit.pst # every parameter starts at 1
377
+ makepst rescale run.pst run_unit.pst --par "hk*,sy*" --exclude_fixed
378
+ makepst rescale run.pst run_unit.pst --pargp kh --pargp kv
379
+ makepst rescale run_unit.pst run_back.pst --undo # SCALE folded back into PARVAL1
380
+ ```
381
+
382
+ PEST applies `SCALE` and `OFFSET` only when it writes a model input file, so a parameter at
383
+ value *v* can start at 1 without the model seeing any difference: `PARVAL1 = 1`,
384
+ `SCALE = SCALE × v`, bounds divided by *v* (swapped when *v* < 0), `OFFSET` unchanged. `--undo`
385
+ does the reverse (`PARVAL1 × SCALE`, bounds likewise, `SCALE = 1`).
386
+
387
+ - **Selection:** all parameters by default, fixed ones included; `--par` (names or globs) and
388
+ `--pargp` narrow it, and both must match when both are given; `--exclude_fixed` leaves fixed
389
+ parameters alone. Tied children follow a selected parent. Parameters at zero are skipped and
390
+ listed.
391
+ - **Prior information** is written in PEST's own units, so every equation naming a rescaled
392
+ parameter is rewritten exactly: a linear term takes *v* into its coefficient, a log term
393
+ moves `log10(v)` to the right-hand side. Each equation's residual is the same number as
394
+ before, so its weight is unchanged.
395
+ - **Warned, not converted:** settings in parameter units that one factor per parameter can't
396
+ convert — `INCTYP absolute` and `DERINCLB` of groups holding rescaled parameters, `absolute(n)`
397
+ change limits — and files named in the control file that were written for the old values
398
+ (`++parcov`, `++base_jacobian`, `++ies_par_en`, SVD-assist `basepestfile` / `basejacfile`).
399
+ A Jacobian given to PEST another way (a restart, a command-line switch) can't be seen from
400
+ the control file; don't reuse one across a rescale.
401
+
402
+ `--set`, `--eol`, `--v1|--v2` and `--no_manifest` work as for `parrep`.
403
+
295
404
  ### hpstart — modeled observations → new PEST_HP .hp file
296
405
 
297
406
  ```
@@ -350,8 +459,8 @@ read and observations the control file lists but no file produced are reported.
350
459
  ### diff — what changed
351
460
 
352
461
  ```
353
- makepst diff tr12.pst tr13.pst [--xlsx changes.xlsx] [--rtol 1e-9] [--max_rows 50]
354
- makepst diff tr13.xlsm tr13.pst # is the control file still what the workbook says?
462
+ makepst diff tr12.pst tr13.pst [--xlsx changes.xlsx] [--file changes.txt] [--rtol 1e-9] [--max_rows 50]
463
+ makepst diff tr13.xlsx tr13.pst # is the control file still what the workbook says?
355
464
  ```
356
465
 
357
466
  Compares the tables, not the text: parameters and observations added, removed or changed
@@ -361,11 +470,12 @@ instruction pairs, command lines and header comments. Numbers are compared with
361
470
  tolerance, so `49.7377` vs `49.73775` shows up at the default `1e-9` and disappears at
362
471
  `--rtol 1e-5`. Prints a table per section and a one-line summary; `--xlsx` writes the same
363
472
  tables to a workbook for review. Exits 1 when there are differences, like `diff`.
473
+ `--file` writes the complete text report, including rows beyond the console's `--max_rows` limit.
364
474
 
365
475
  ### log — the run ledger
366
476
 
367
477
  ```
368
- makepst log [FOLDER ...] [--file tr13.xlsm] [--command build] [--last 10] [--check] [--no_recursive]
478
+ makepst log [FOLDER ...] [--file tr13.xlsx] [--command build] [--last 10] [--check] [--no_recursive]
369
479
  ```
370
480
 
371
481
  Every manifest under the folders (default: the current one, recursively), oldest first, one
@@ -376,8 +486,8 @@ the workbook?" even after the file was renamed or overwritten. `--check` adds a
376
486
  `provenance`: `unchanged` / `changed` / `missing`.
377
487
 
378
488
  ```
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
489
+ 2026-09-19 21:14 build pest/tr13.pst <- tr13.xlsx [1068 par 41747 obs 159 prior] makepst 0.5.0
490
+ 2026-09-21 08:10 update tr13-xlwings.xlsx <- tr13.xlsx, tr13.par [1068 rows in 8 sheets] makepst 0.5.0
381
491
  ```
382
492
 
383
493
  ### bundle — a run, zipped for review
@@ -405,11 +515,13 @@ Preserved: every parameter, group, observation, prior-information equation, tied
405
515
  template/instruction pair, command line, `++` option, header comment, control-data value
406
516
  (including PEST_HP keyed tokens), SVD / LSQR / AUI / SVD-assist / regularisation values, and
407
517
  the verbatim text of `* sensitivity reuse`, `* derivatives command line`, `* predictive
408
- analysis` and `* pareto`.
518
+ analysis` and `* pareto`. Any section unknown to this version of makePst is also retained
519
+ verbatim at its original position, so newer or site-specific PEST content survives a round trip.
409
520
 
410
521
  Normalised: parameter, observation and group names are lower-cased (PEST is
411
522
  case-insensitive); numbers are written with 11 significant digits; whitespace and column
412
- alignment are makePst's own; comments *inside* sections are dropped; observation groups are
523
+ alignment in understood sections is makePst's own; comments *inside understood sections* are
524
+ dropped; observation groups are
413
525
  listed in first-use order (or the order of the source file when reading a `.pst`).
414
526
 
415
527
  Also preserved: covariance-file references in `* observation groups` (an `OBSGP` sheet on
@@ -419,27 +531,27 @@ written back as version 2 unless `--v1` is given; `--v2` writes any control file
419
531
  format, with `case.par_data.csv` / `obs_data` / `pargp_data` / `prior_data` beside it. Both
420
532
  versions of the same content are identical after reading (`makepst diff` says so).
421
533
 
422
- Not preserved: any unrecognised section (dropped with a warning naming it).
423
-
424
534
  ## Compatibility
425
535
 
426
536
  | | |
427
537
  |---|---|
428
538
  | PEST dialects | PEST, PEST_HP (`win_mrun_hours=`, `uptestmin=`, `uptestlim=`, `absparmax(n)=`), PEST++ (`++name(value)` options) |
429
- | Control-file sections | control data, singular value decomposition, lsqr, automatic user intervention, svd assist, parameter groups, parameter data (incl. tied pairs), observation groups, observation data, model command line, model input/output, prior information (with `&` continuation lines), regularisation. Kept verbatim: sensitivity reuse, derivatives command line, predictive analysis, pareto. |
539
+ | Control-file sections | control data, singular value decomposition, lsqr, automatic user intervention, svd assist, parameter groups, parameter data (incl. tied pairs), observation groups, observation data, model command line, model input/output, prior information (with `&` continuation lines), regularisation. Kept verbatim: sensitivity reuse, derivatives command line, predictive analysis, pareto, and all unknown sections. |
430
540
  | File formats | classic (version 1) and PEST++ version 2 (`pcf version=2`, `* control data keyword` with control variables and `++` options, `* … external` csv tables with `sep=` / `missing_values=`, `partied` column); observation covariance files in `* observation groups` |
431
- | Not supported | unknown sections — dropped with a warning |
432
541
  | Result files | `.par`, `.res` / `.rei`, PESTPP-IES `case.N.par.csv` / `case.N.obs.csv` / `case.phi.actual.csv` |
433
- | Table inputs | `.xlsx` / `.xlsm` sheets (openpyxl), `.csv` |
542
+ | Table inputs | `.xlsx` / `.xlsx` sheets (openpyxl), `.csv` |
434
543
  | Workbook update | openpyxl backend on any platform (macros kept, charts/images dropped, no recalculation); xlwings backend on Windows/macOS with Excel (everything kept, recalculated) |
435
544
  | Platforms | CI runs the suite on Ubuntu 24.04 and Windows Server 2022 for Python 3.9, 3.11 and 3.13. macOS is expected to work but is not exercised in CI. |
436
545
  | Python | ≥ 3.9; pandas ≥ 2.0 (tested with 2.3 and 3.0), numpy, openpyxl ≥ 3.1; optional xlwings, pyemu, pytest |
437
546
 
438
547
  ## Validation
439
548
 
440
- Two layers. `Pst.validate()` runs before every write (`build`, `dump`, `parrep`, `write_pst`)
441
- and enforces **internal consistency** of the tables, changing what it can and refusing what it
442
- cannot:
549
+ `Pst.validate()` is a read-only audit of the in-memory tables. It returns a report with
550
+ `errors`, `warnings`, and `fixes_available`; inspecting it never changes the `Pst`. Apply
551
+ reviewed fixes explicitly with `pst.apply_fixes(report)`, or use `pst.normalize()` to apply
552
+ all currently available normalizations. Writers refuse validation errors and serialize the
553
+ current tables without modifying them. The CLI's file-building commands call `normalize()`
554
+ explicitly before writing.
443
555
 
444
556
  | check | outcome |
445
557
  |---|---|
@@ -447,19 +559,20 @@ cannot:
447
559
  | duplicate parameter, observation or prior-information names | error |
448
560
  | parameter group used but not defined | error |
449
561
  | tied parameter whose target is missing, or tied to a tied parameter | error |
450
- | tied to a fixed parameter | parameter becomes fixed (message) |
451
- | parameter group defined but unused | dropped from the file |
452
- | prior equation referencing a fixed, tied or missing parameter | dropped (message) |
562
+ | tied to a fixed parameter | error and fix available: convert it to fixed |
563
+ | parameter group defined but unused | fix available: remove the group |
564
+ | prior equation referencing a fixed, tied or missing parameter | error and fix available: rebuild prior information, dropping those equations |
453
565
  | adjustable `PARVAL1` outside `[PARLBND, PARUBND]` | warning |
454
- | control value that is computed (`npar` …) or unknown | ignored (warning) |
566
+ | control value that is computed (`npar` …) | ignored (warning) |
567
+ | control variable makePst does not know (a newer PEST_HP keyword): kept on the line it came from; a CONTROL-sheet row gives that line in `LINE` (`control data line 4`) or is ignored | warning |
455
568
 
456
- `makepst validate` is the **pestchek-style report**: it changes nothing, looks beyond the
457
- tables at the files the control file points to, and exits 1 on errors so a batch file can
458
- stop:
569
+ `makepst validate` uses the same table checks and adds the **pestchek-style report**: it
570
+ changes nothing, looks beyond the tables at the files the control file points to, and exits 1
571
+ on errors so a batch file can stop:
459
572
 
460
573
  ```
461
574
  makepst validate model.pst [--outputs] [--strict] [--quiet] [--base_dir DIR]
462
- makepst validate tr13.xlsm # a workbook with a BUILD sheet is built first, in memory
575
+ makepst validate tr13.xlsx # a workbook with a BUILD sheet is built first, in memory
463
576
  ```
464
577
 
465
578
  | check | severity |
@@ -470,19 +583,47 @@ makepst validate tr13.xlsm # a workbook with a BUILD sheet is built f
470
583
  | malformed prior equation; non-numeric right-hand side; `log()` used on a non-log parameter or vice versa | error |
471
584
  | `DERCOM` beyond the number of model command lines | error |
472
585
  | 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 |
586
+ | **pestchek's rules** (from PEST 17.2'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 |
587
+ | control value PEST cannot read: not an integer or a number where one is expected (Fortran `1d-3` is fine), a word outside its field's vocabulary (`precis`, `dpoint`, `obsreref`, …), `absparmax(n)=` malformed or n outside 1-10; the PEST_HP limits and combinations (`RUN_SLOW_FAC`, `RUN_ABANDON_FAC`, `HARDSTOPHOURS` / `SOFTSTOPHOURS` not both, `JCOZEROTHRESH` above `JCOWARNTHRESH`, `ZEROSENVAL`, `REG2MEASRAT` below 1, `orr_not_first` only with `obsreref`) | error |
588
+ | control-data token not in PEST 17.2's pestchek — kept as written, with the closest known name (`"win_mrun_hour=2" … did you mean "win_mrun_hours"?`) | warning |
474
589
  | 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
590
  | PEST_HP-only control variables present (`WIN_MRUN_HOURS`, `UPTESTMIN`, `RRFSAVE`, …; plain PEST needs `/hpstart`); observation group whose weights are all zero | note |
476
591
  | template: parameter space narrower than 3 characters or containing a tab | error |
477
592
  | 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
593
  | 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 |
479
- | section in the `.pst` that makePst drops on read | warning |
594
+ | `++` option with a value PEST++ does not accept (integer given `2.5`, boolean given `t` or `yes`, number list with a non-number, `svd_pack` / `glm_normal_form` / `ies_subset_how` outside their choices, `ies_num_reals` < 1, …); the same option twice, directly or through an alias (`ies_par_en` and `ies_parameter_ensemble`) | error |
595
+ | `++` option not in PEST++'s option list — kept, with the closest known name (`unknown PEST++ option "ies_num_real"; did you mean "ies_num_reals"?`); a control variable such as `noptmax` given as a `++` option | warning (note with `++forgive_unknown_args(true)`) |
596
+ | deprecated `++` option that PEST++ ignores (`mat_inv`, `upgrade_augment`, `svd_pack(propack)`, …) | note |
597
+ | unknown section in the `.pst` that makePst preserves verbatim | note |
480
598
  | template or instruction file missing; bad `ptf` / `pif` line | error |
481
599
  | parameter cited in no template; template citing an unknown parameter | error |
482
600
  | observation read by no instruction file, by two files, or twice in one; unknown observation in an instruction file | error |
483
601
  | model command / model-input folder not found next to the control file | warning |
484
602
  | `--outputs`: each instruction file run against its model output file when present | note (success) / warning (failure) |
485
603
 
604
+ The `++` checks use the option list in `makepst/pestpp.py`, read off PEST++ 5.2.29's option parser
605
+ (250 options and 34 aliases, with their types). They point at where each option came from —
606
+ `PP!A17` for a workbook cell, `pp.csv:17`, `line 42` of a control file — and `build` prints them
607
+ too, without refusing to write: plain PEST ignores `++` lines, and a newer PEST++ may know an
608
+ option this list does not. PEST++ itself stops on an option it does not accept, so fix or remove
609
+ what is flagged before a run.
610
+
611
+ `validate` prints the two releases its rules were read from, and every manifest records them
612
+ (`checked_against`). Whether they are still current is checked by `tools/rule_sources.py`, which
613
+ re-reads the upstream source and compares it with the snapshot the rules were written against
614
+ (`tools/snapshots/`), exiting 1 on any change:
615
+
616
+ ```
617
+ python tools/rule_sources.py pestpp # usgs/pestpp develop, from GitHub
618
+ python tools/rule_sources.py pestchek --source pest_source # the source folder of a PEST download
619
+ ```
620
+
621
+ It reports added and removed `++` options, aliases, value types and choices; for PEST, the
622
+ control-data keywords pestchek reads and its messages (each message is a rule); and anything
623
+ makePst's own tables no longer agree with. It edits nothing: port what changed, then run it
624
+ again with `--write` to record the new source as reviewed, and the tests hold the code to that
625
+ snapshot. The PEST++ check runs weekly on GitHub and opens an issue when the parser changes.
626
+
486
627
  The `--outputs` interpreter follows the PEST manual (primary and secondary markers, `l`, `w`,
487
628
  `t`, `!name!`, `[name]c1:c2`, `(name)c1:c2`, `dum`, `&` continuation) and catches the classic
488
629
  mistakes — a `w` too few so a label is read instead of a number, a marker that never appears
@@ -501,14 +642,15 @@ from?" after the fact:
501
642
 
502
643
  ```json
503
644
  {
504
- "makepst": "0.3.0",
645
+ "makepst": "0.5.0",
505
646
  "command": "build",
506
- "argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsm,CONTROL", "..."],
647
+ "argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsx,CONTROL", "..."],
507
648
  "created": "2026-09-19T13:51:39-04:00",
508
649
  "user": "hydro", "host": "OU13700", "cwd": "D:\\...\\0023-makePst",
509
650
  "python": "3.12.12", "pandas": "3.0.5", "platform": "Windows-11-10.0.26200-SP0",
651
+ "checked_against": {"pestchek": "17.2", "pestpp": "5.2.29"},
510
652
  "sources": [
511
- {"path": "D:\\...\\tr13.xlsm", "sha256": "9d641a2f...", "size": 3089317,
653
+ {"path": "D:\\...\\tr13.xlsx", "sha256": "9d641a2f...", "size": 3089317,
512
654
  "modified": "2026-07-12T03:32:10-04:00", "role": "control",
513
655
  "sheets": ["CONTROL", "PARGP", "PAR_HK", "PAR_VK", "...", "PPglm"]}
514
656
  ],
@@ -568,10 +710,11 @@ from makepst import Pst, read_pst, write_pst, to_workbook, update_workbook, load
568
710
  pst = read_pst('run.pst') # Pst: .par/.obs/.pargp/.prior DataFrames, .control dict, ...
569
711
  pst.fill_parval('run.3.par.csv', real='best')
570
712
  pst.set_control({'noptmax': 0})
713
+ pst.rescale_par(patterns=['hk*'], include_fixed=False) # values -> SCALE; returns a report dict
571
714
  write_pst(pst, 'run_final.pst', dump_tpl=False)
572
715
 
573
716
  to_workbook(pst, 'run.xlsx', split=True)
574
- update_workbook('book.xlsm', par='run.par', res='run.res', out='book_results.xlsm',
717
+ update_workbook('book.xlsx', par='run.par', res='run.res', out='book_results.xlsx',
575
718
  sheets=['PAR_*'], groups=['hk'])
576
719
  ```
577
720
 
@@ -603,9 +746,10 @@ makepst/
603
746
  writer.py Pst -> .pst text (+ dump.tpl)
604
747
  reader.py .pst text -> Pst
605
748
  excel.py sheets -> Pst; Pst -> workbook; results -> existing workbook
606
- cli.py init / validate / build / dump / update / parrep / diff / tempchek / inschek / log / bundle
749
+ cli.py init / validate / build / dump / update / parrep / set / rescale / reweight / diff / tempchek / inschek / log / bundle
607
750
  diff.py semantic comparison of two Pst objects
608
751
  phi.py objective function by group from residuals; IES realization phis
752
+ reweight.py observation-group balancing, scaling and bounded weight adjustment
609
753
  pyemu_bridge.py to_pyemu / from_pyemu
610
754
  checks.py the validate report: table checks, template / instruction cross-checks, instruction interpreter
611
755
  rules.py pestchek's rules (parameters, groups, observations, prior information, control variables)
@@ -614,6 +758,7 @@ makepst/
614
758
  starter.py the `init` workbook: control-variable descriptions, example rows, drop-downs
615
759
  examples/minimal/ the tutorial inputs
616
760
  tests/ suite + fixture generator
761
+ tools/ rule_sources.py (are the rules current with PEST / PEST++?) and its snapshots
617
762
  ```
618
763
 
619
764
  ## Releasing
@@ -623,7 +768,7 @@ stored anywhere. To release: bump the version in `pyproject.toml` and the fallba
623
768
  `makepst/__init__.py` (a test keeps them equal), add a `CHANGELOG.md` entry, commit, then
624
769
 
625
770
  ```
626
- git tag v0.3.0 && git push origin main v0.3.0
771
+ git tag v0.5.0 && git push origin main v0.5.0
627
772
  ```
628
773
 
629
774
  The `publish` workflow checks that the tag matches the package version, runs the tests, builds