makepst 0.3.0__tar.gz → 0.4.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 (78) hide show
  1. {makepst-0.3.0 → makepst-0.4.0}/CHANGELOG.md +34 -0
  2. {makepst-0.3.0 → makepst-0.4.0}/CITATION.cff +2 -2
  3. {makepst-0.3.0 → makepst-0.4.0}/PKG-INFO +173 -49
  4. {makepst-0.3.0 → makepst-0.4.0}/README.md +172 -48
  5. {makepst-0.3.0 → makepst-0.4.0}/makepst/__init__.py +6 -4
  6. {makepst-0.3.0 → makepst-0.4.0}/makepst/checks.py +8 -5
  7. {makepst-0.3.0 → makepst-0.4.0}/makepst/cli.py +294 -19
  8. {makepst-0.3.0 → makepst-0.4.0}/makepst/diff.py +49 -2
  9. {makepst-0.3.0 → makepst-0.4.0}/makepst/excel.py +2 -0
  10. {makepst-0.3.0 → makepst-0.4.0}/makepst/hpstart.py +4 -2
  11. makepst-0.4.0/makepst/pestpp.py +241 -0
  12. makepst-0.4.0/makepst/pst.py +910 -0
  13. {makepst-0.3.0 → makepst-0.4.0}/makepst/reader.py +56 -20
  14. makepst-0.4.0/makepst/reweight.py +344 -0
  15. {makepst-0.3.0 → makepst-0.4.0}/makepst/rules.py +18 -0
  16. {makepst-0.3.0 → makepst-0.4.0}/makepst/sections.py +14 -0
  17. {makepst-0.3.0 → makepst-0.4.0}/makepst/writer.py +137 -20
  18. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/PKG-INFO +173 -49
  19. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/SOURCES.txt +8 -0
  20. {makepst-0.3.0 → makepst-0.4.0}/pyproject.toml +1 -1
  21. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.par +1 -1
  22. {makepst-0.3.0 → makepst-0.4.0}/tests/make_fixture.py +2 -1
  23. {makepst-0.3.0 → makepst-0.4.0}/tests/test_checks.py +10 -2
  24. {makepst-0.3.0 → makepst-0.4.0}/tests/test_diff.py +22 -3
  25. {makepst-0.3.0 → makepst-0.4.0}/tests/test_examples.py +31 -0
  26. {makepst-0.3.0 → makepst-0.4.0}/tests/test_fixture.py +50 -2
  27. {makepst-0.3.0 → makepst-0.4.0}/tests/test_makepst.py +34 -10
  28. makepst-0.4.0/tests/test_optional_positions.py +93 -0
  29. makepst-0.4.0/tests/test_optional_positions_variations.py +19 -0
  30. makepst-0.4.0/tests/test_pestpp.py +141 -0
  31. makepst-0.4.0/tests/test_rescale.py +193 -0
  32. makepst-0.4.0/tests/test_reweight.py +240 -0
  33. {makepst-0.3.0 → makepst-0.4.0}/tests/test_rules.py +18 -0
  34. makepst-0.4.0/tests/test_set_par.py +166 -0
  35. makepst-0.3.0/makepst/pst.py +0 -449
  36. {makepst-0.3.0 → makepst-0.4.0}/LICENSE +0 -0
  37. {makepst-0.3.0 → makepst-0.4.0}/MANIFEST.in +0 -0
  38. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/control.csv +0 -0
  39. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/flow.ins +0 -0
  40. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/flow.out +0 -0
  41. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/heads.ins +0 -0
  42. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/heads.out +0 -0
  43. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/io.csv +0 -0
  44. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/model.in +0 -0
  45. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/model.par +0 -0
  46. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/model.py +0 -0
  47. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/model.res +0 -0
  48. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/model.tpl +0 -0
  49. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/obs.csv +0 -0
  50. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/par.csv +0 -0
  51. {makepst-0.3.0 → makepst-0.4.0}/examples/minimal/pargp.csv +0 -0
  52. {makepst-0.3.0 → makepst-0.4.0}/makepst/__main__.py +0 -0
  53. {makepst-0.3.0 → makepst-0.4.0}/makepst/model_files.py +0 -0
  54. {makepst-0.3.0 → makepst-0.4.0}/makepst/phi.py +0 -0
  55. {makepst-0.3.0 → makepst-0.4.0}/makepst/provenance.py +0 -0
  56. {makepst-0.3.0 → makepst-0.4.0}/makepst/pyemu_bridge.py +0 -0
  57. {makepst-0.3.0 → makepst-0.4.0}/makepst/starter.py +0 -0
  58. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/dependency_links.txt +0 -0
  59. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/entry_points.txt +0 -0
  60. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/requires.txt +0 -0
  61. {makepst-0.3.0 → makepst-0.4.0}/makepst.egg-info/top_level.txt +0 -0
  62. {makepst-0.3.0 → makepst-0.4.0}/setup.cfg +0 -0
  63. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.3.obs.csv +0 -0
  64. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.3.par.csv +0 -0
  65. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.phi.actual.csv +0 -0
  66. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.pst +0 -0
  67. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.res +0 -0
  68. {makepst-0.3.0 → makepst-0.4.0}/tests/data/demo.xlsx +0 -0
  69. {makepst-0.3.0 → makepst-0.4.0}/tests/test_convenience.py +0 -0
  70. {makepst-0.3.0 → makepst-0.4.0}/tests/test_hpstart.py +0 -0
  71. {makepst-0.3.0 → makepst-0.4.0}/tests/test_log_bundle.py +0 -0
  72. {makepst-0.3.0 → makepst-0.4.0}/tests/test_model_files.py +0 -0
  73. {makepst-0.3.0 → makepst-0.4.0}/tests/test_phi.py +0 -0
  74. {makepst-0.3.0 → makepst-0.4.0}/tests/test_provenance.py +0 -0
  75. {makepst-0.3.0 → makepst-0.4.0}/tests/test_pyemu.py +0 -0
  76. {makepst-0.3.0 → makepst-0.4.0}/tests/test_starter.py +0 -0
  77. {makepst-0.3.0 → makepst-0.4.0}/tests/test_v2.py +0 -0
  78. {makepst-0.3.0 → makepst-0.4.0}/tests/test_version.py +0 -0
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 (2026-09-27)
4
+
5
+ - `rescale` / `Pst.rescale_par()`: move parameter values into `SCALE` so each selected parameter
6
+ starts at 1 (bounds divided by the value), or back with `--undo`; the model sees the same values.
7
+ Selection by `--par` globs and `--pargp` (fixed parameters included unless `--exclude_fixed`;
8
+ tied children follow their parent). Prior-information equations are rewritten exactly (every
9
+ residual is unchanged), and settings in parameter units that cannot be converted (absolute
10
+ derivative increments, `absolute(n)` limits, `++parcov` / `++base_jacobian` / `++ies_par_en`)
11
+ are reported.
12
+ - `fill_parval` (`build --fill_parval`, `parrep`), `tempchek --par` and `hpstart --par` convert a
13
+ `.par` value written under a different `SCALE` / `OFFSET` than the control file's through the
14
+ model value, instead of copying `PARVAL1` across. The test fixture's `demo.par` now records the
15
+ parameters' own offsets, as PEST writes them.
16
+ - Writing a control file no longer fails on an adjustable `PARVAL1` outside its bounds (a `parrep`
17
+ from an IES realization, say): it is a warning, as documented, and the file is written.
18
+ `makepst validate` still reports it as an error. `diff` normalizes a workbook side, as `build`
19
+ would, so a workbook compares equal to the control file built from it.
20
+
21
+ - `reweight`: balance observation-group contributions equally or to relative target shares,
22
+ multiply selected group weights directly, match weights to the current misfit with
23
+ `--discrepancy group|obs` (group phi = weighted-observation count, or `w = min(w, 1/|r|)`), consume `.res`/IES observation results, enforce
24
+ optional weight bounds with `--clip`, and write an auditable before/after report.
25
+
26
+ - `++` options are checked against PEST++ 5.2.29's option list (`makepst/pestpp.py`): a known option
27
+ with a value PEST++ would not accept, or the same option twice (also through an alias), is an error;
28
+ an unknown option is a warning with the closest known name (`did you mean "ies_num_reals"?`) and is
29
+ kept as written. Findings name the workbook cell, csv line or control-file line the option came from
30
+ (`PP!A17`). `validate` reports them; `build`, `parrep` and `set` print them but still write.
31
+
32
+ - Unknown control-file sections are retained verbatim, including their original header,
33
+ body lines and position, instead of being dropped during a read/write round trip.
34
+ - `Pst.validate()` now returns a read-only report; `apply_fixes(report)` and `normalize()` make
35
+ the previous tied-parameter, unused-group and prior-information cleanups explicit.
36
+
3
37
  ## 0.3.0 (2026-09-21)
4
38
 
5
39
  - `log`: ledger of every manifest under a folder — when, which command, which output from
@@ -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.4.0
10
+ date-released: 2026-09-27
11
11
  repository-code: "https://github.com/ougx/makePst"
12
12
  license: MIT
13
13
  keywords:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makepst
3
- Version: 0.3.0
3
+ Version: 0.4.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.4.0
490
+ 2026-09-21 08:10 update tr13-xlwings.xlsx <- tr13.xlsx, tr13.par [1068 rows in 8 sheets] makepst 0.4.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,19 @@ 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
566
  | control value that is computed (`npar` …) or unknown | ignored (warning) |
455
567
 
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:
568
+ `makepst validate` uses the same table checks and adds the **pestchek-style report**: it
569
+ changes nothing, looks beyond the tables at the files the control file points to, and exits 1
570
+ on errors so a batch file can stop:
459
571
 
460
572
  ```
461
573
  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
574
+ makepst validate tr13.xlsx # a workbook with a BUILD sheet is built first, in memory
463
575
  ```
464
576
 
465
577
  | check | severity |
@@ -476,13 +588,23 @@ makepst validate tr13.xlsm # a workbook with a BUILD sheet is built f
476
588
  | template: parameter space narrower than 3 characters or containing a tab | error |
477
589
  | 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
590
  | 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 |
591
+ | `++` 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 |
592
+ | `++` 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)`) |
593
+ | deprecated `++` option that PEST++ ignores (`mat_inv`, `upgrade_augment`, `svd_pack(propack)`, …) | note |
594
+ | unknown section in the `.pst` that makePst preserves verbatim | note |
480
595
  | template or instruction file missing; bad `ptf` / `pif` line | error |
481
596
  | parameter cited in no template; template citing an unknown parameter | error |
482
597
  | observation read by no instruction file, by two files, or twice in one; unknown observation in an instruction file | error |
483
598
  | model command / model-input folder not found next to the control file | warning |
484
599
  | `--outputs`: each instruction file run against its model output file when present | note (success) / warning (failure) |
485
600
 
601
+ The `++` checks use the option list in `makepst/pestpp.py`, read off PEST++ 5.2.29's option parser
602
+ (250 options and 34 aliases, with their types). They point at where each option came from —
603
+ `PP!A17` for a workbook cell, `pp.csv:17`, `line 42` of a control file — and `build` prints them
604
+ too, without refusing to write: plain PEST ignores `++` lines, and a newer PEST++ may know an
605
+ option this list does not. PEST++ itself stops on an option it does not accept, so fix or remove
606
+ what is flagged before a run.
607
+
486
608
  The `--outputs` interpreter follows the PEST manual (primary and secondary markers, `l`, `w`,
487
609
  `t`, `!name!`, `[name]c1:c2`, `(name)c1:c2`, `dum`, `&` continuation) and catches the classic
488
610
  mistakes — a `w` too few so a label is read instead of a number, a marker that never appears
@@ -501,14 +623,14 @@ from?" after the fact:
501
623
 
502
624
  ```json
503
625
  {
504
- "makepst": "0.3.0",
626
+ "makepst": "0.4.0",
505
627
  "command": "build",
506
- "argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsm,CONTROL", "..."],
628
+ "argv": ["build", "tr13.pst", "regul", "--set_ctl_xls", "tr13.xlsx,CONTROL", "..."],
507
629
  "created": "2026-09-19T13:51:39-04:00",
508
630
  "user": "hydro", "host": "OU13700", "cwd": "D:\\...\\0023-makePst",
509
631
  "python": "3.12.12", "pandas": "3.0.5", "platform": "Windows-11-10.0.26200-SP0",
510
632
  "sources": [
511
- {"path": "D:\\...\\tr13.xlsm", "sha256": "9d641a2f...", "size": 3089317,
633
+ {"path": "D:\\...\\tr13.xlsx", "sha256": "9d641a2f...", "size": 3089317,
512
634
  "modified": "2026-07-12T03:32:10-04:00", "role": "control",
513
635
  "sheets": ["CONTROL", "PARGP", "PAR_HK", "PAR_VK", "...", "PPglm"]}
514
636
  ],
@@ -568,10 +690,11 @@ from makepst import Pst, read_pst, write_pst, to_workbook, update_workbook, load
568
690
  pst = read_pst('run.pst') # Pst: .par/.obs/.pargp/.prior DataFrames, .control dict, ...
569
691
  pst.fill_parval('run.3.par.csv', real='best')
570
692
  pst.set_control({'noptmax': 0})
693
+ pst.rescale_par(patterns=['hk*'], include_fixed=False) # values -> SCALE; returns a report dict
571
694
  write_pst(pst, 'run_final.pst', dump_tpl=False)
572
695
 
573
696
  to_workbook(pst, 'run.xlsx', split=True)
574
- update_workbook('book.xlsm', par='run.par', res='run.res', out='book_results.xlsm',
697
+ update_workbook('book.xlsx', par='run.par', res='run.res', out='book_results.xlsx',
575
698
  sheets=['PAR_*'], groups=['hk'])
576
699
  ```
577
700
 
@@ -603,9 +726,10 @@ makepst/
603
726
  writer.py Pst -> .pst text (+ dump.tpl)
604
727
  reader.py .pst text -> Pst
605
728
  excel.py sheets -> Pst; Pst -> workbook; results -> existing workbook
606
- cli.py init / validate / build / dump / update / parrep / diff / tempchek / inschek / log / bundle
729
+ cli.py init / validate / build / dump / update / parrep / set / rescale / reweight / diff / tempchek / inschek / log / bundle
607
730
  diff.py semantic comparison of two Pst objects
608
731
  phi.py objective function by group from residuals; IES realization phis
732
+ reweight.py observation-group balancing, scaling and bounded weight adjustment
609
733
  pyemu_bridge.py to_pyemu / from_pyemu
610
734
  checks.py the validate report: table checks, template / instruction cross-checks, instruction interpreter
611
735
  rules.py pestchek's rules (parameters, groups, observations, prior information, control variables)
@@ -623,7 +747,7 @@ stored anywhere. To release: bump the version in `pyproject.toml` and the fallba
623
747
  `makepst/__init__.py` (a test keeps them equal), add a `CHANGELOG.md` entry, commit, then
624
748
 
625
749
  ```
626
- git tag v0.3.0 && git push origin main v0.3.0
750
+ git tag v0.4.0 && git push origin main v0.4.0
627
751
  ```
628
752
 
629
753
  The `publish` workflow checks that the tag matches the package version, runs the tests, builds