Follows 0.10.0's PEST++ rebuild by applying the same question to the other external authority PESTO drives -- APSIM -- and finding the same shape of defect: a working engine PESTO could not see, and an adapter whose tests only ever asked PESTO about itself.
APSIM_EXE_PATH. It consulted
only apsimx's exe.path option, so a machine with a working APSIM and the
environment variable exported still reported no engine, and
apsim_callback() stamped the closure's apsim_version as NA -- leaving
every run it fed unattributable to the simulator that produced it. APSIM has
no discoverable install location on macOS: apsimx::apsim_version() derives
the version from /Applications folder names, so it sees neither a
~/Applications install nor a source build, and never reads exe.path at
all. An explicit apsimx_options(exe.path=) still takes precedence. The
adapter now has tests that run against a real engine rather than stubs.This release rebuilds the PEST++ invocation layer. PESTO's shell-out to PEST++
had never run: control variables were passed as /h :name=value command-line
switches, which PEST++ has never accepted, so every call exited before the
binary started. The defects below have been present since the initial release
(9 April 2026) and shipped in every release since; they surfaced now because
this is the first time the layer was checked against the PEST++ sources and
then against the real binary, rather than against PESTO's own tests.
Users of pesto_ies_callback() -- PESTO's native R ensemble smoother, and the
path the benchmark suite exercises -- are unaffected: it does not shell out.
pesto_ies() and pesto_glm() now default noptmax to NULL, meaning
"leave the control file's own iteration cap alone". The previous defaults
(4 and 20) were documented as overrides but, because the whole
command-line path was broken, had never once been applied to a real run.
Honouring them now would have silently retuned every caller's control file,
so the safer reading of a value the user never chose is to respect the file.
Pass noptmax explicitly to override. A call that injects nothing writes no
file at all and runs the caller's own control file, rather than a
<base>_pesto.pst copy of it.PESTO now finds a PEST++ install that is not on the PATH. It consulted only
the PATH and a copy bundled at inst/bin -- which PESTO has never shipped,
so that branch could not fire and the documented "uses the bundled binary"
fallback did not exist. PEST++ has no installer and is not on the PATH by
default, so a machine could have it installed and configured and still be told
it was missing. Resolution is now exe, then the per-tool environment
variable (e.g. PESTPP_IES_EXE_PATH), then PESTPP_BIN_DIR, then the PATH.
pesto_version() reports a version rather than a failed run's log, and no
longer writes to the caller's working directory. PEST++ has no --version
flag: it read --version as a control-file name, printed its banner, failed
on the missing --version.pst, and left --version.log, --version.rec and
--version.rst behind. $pestpp_version held that whole transcript,
std::exception and all, and looked plausible only because the banner is
printed before the error. The banner is now parsed for its version: line,
from a temporary directory.
The PEST++ invocation layer has been rebuilt. PESTO passed control variables
as /h :name=value command-line switches, which PEST++ has never accepted:
its parser takes a control file, an optional /r or /j, and an optional
run-manager switch, where /h selects the PANTHER run manager and expects a
host:port. Every pesto_ies() call built at least one such switch, so the
binary exited with a command-line error before starting. Control variables
now go where PEST++ reads them: noptmax into * control data, and every
other option as a ++key(value) line. No test caught this because the
binary was never a test dependency; the suite checked PESTO against PESTO.
Verified against the PEST++ sources rather than against PESTO's own belief
about them.
create_pest_scenario() now writes instruction files into the control file.
instruction_files was read only to count them, so every control file PESTO
produced declared NINSFLE and then supplied none, and PEST++ refused all of
them: model input/output error: number of instruction files = 0. PESTO had
never written a runnable control file; nothing local could see it, because
the control file is only ever read by the binary.
$exit_code is an exit code again. With verbose = FALSE -- which every
example uses -- system2() returns the captured OUTPUT rather than the
status, so $exit_code held PEST++'s entire log.
pesto_glm() now honours noptmax and extra_args, and
pesto_sensitivity() now honours extra_args. All three were documented,
exported, and read by nothing.
pesto_sensitivity(method = "sobol") now runs Sobol. method selected the
label on the returned object but never reached the binary, so pestpp-sen ran
its Morris default and the result was reported as Sobol regardless.
PEST++ now runs in the directory holding the control file. The file is passed by basename, so it -- and every relative template, instruction, and model-command path inside it -- was previously resolved against R's working directory, which is only correct by coincidence.
read_pst() now reads NOPTMAX, and write_pst() emits it instead of a
hard-coded 30. A read/write round-trip previously reset the caller's
iteration cap without saying so.
write_ensemble() rejects a format it cannot write. format = "binary"
wrote a CSV under the requested name.
plot_identifiability() honours pst, documented as the source of
parameter names for a .jco that carries none but never consulted. Reading
such a file also dropped columns: blank labels all collided on one name, so
a three-column Jacobian came back with one.
Runs now write <name>_pesto.pst beside the control file and name their
outputs after it. This is the exact input PEST++ was given, recorded for
reproducibility.
apsim_callback() now reads the engine version for the "apsim_version"
provenance attribute from Models --version on the configured binary,
rather than from apsimx::apsim_version(). On macOS the latter infers the
version from /Applications folder names and cannot see a source build or an
install under ~/Applications, so the attribute previously fell back to
NA; it now reflects the simulator actually in use.tests/
(test-export-surface.R). The pkgdown site now renders maths with KaTeX
(resolving a \boldsymbol extension-load failure), all vignettes use
sentence-case headings and verified references (the PEST citation is corrected
to the 2015 Calibration and Uncertainty Analysis book), and
plot_identifiability() gains a top_n cap with a ranked-lollipop layout so
high-dimensional problems stay legible. A tools/check_publication_quality.R
guard blocks internal-flag words from shipped prose.ensemble_solution_gpu() renamed to ensemble_solution_adaptive(). The
former name implied GPU computation; the function performs CPU adaptive-SVD
backend selection (randomised SVD versus a dense LAPACK / Accelerate
decomposition), and its documentation no longer claims CUDA / cuSOLVER support.
ensemble_solution_gpu() is retained as a deprecated alias that warns and
forwards, and will be removed in a future release.pestpp_available() -- a non-erroring probe for a
PEST++ family executable (e.g. pestpp-ies, pestpp-glm). It is the
documented way for examples, vignettes, and conditional tests to skip
gracefully when no external binary is installed; every PESTO algorithm
runs natively in R without one.pestpp-ies 5.2.16) on a well-posed
linear problem and a non-linear ODE. The figures are frozen real
outputs of a fixed-seed reproducibility harness, shipped in
inst/extdata/pestpp_cache/: accuracy parity on the linear problem,
the ensemble methods' advantage over linearised GLM on the non-linear
one, a two-to-three-orders-of-magnitude wall-clock advantage, and the
calibration caveat (raw ensemble intervals under-cover; apply
inflation). A new Lineage and scope section grounds PESTO in PEST
(Doherty 2015) and PEST++ (White et al. 2020) and states the algorithm
boundary that makes the comparison fair.pestpp-ies version string in the vignette
to 5.2.16 (was mislabelled 5.2.25).apsim_callback() and the apsimx
package) as the primary worked partnership, alongside hydrological
models, other crop models, and ODE systems. The APSIM vignette is
retitled Calibrating APSIM with PESTO and was previously missing from
the README documentation list (now fixed). No code or API change.pesto_ensemble_manifest. Shipped as the Calibrating APSIM Wheat
with PESTO vignette (frozen real outputs, so it builds without APSIM) plus
a path-free reproducible driver at
system.file("case_studies/apsim_wheat_calibration.R", package = "PESTO").
A second part calibrates physiological parameters (RUE, phenology) to the
real observed biomass in apsimx::obsWheat: PESTO's posterior brackets
the independent apsimx::optim_apsimx() optimum for both parameters and
predicts held-out dates, adding the uncertainty the point optimiser lacks
(driver apsim_wheat_realdata.R).train_gp_surrogate_tuned() and its companion predict_gp_surrogate_tuned()
fit a GP surrogate at maximum-likelihood length scales rather than the
median heuristic train_gp_surrogate() uses by default. The fit is
anisotropic by default -- one length scale per input dimension, estimated
by maximising the GP's own log marginal likelihood through per-axis coordinate
pre-scaling (no change to the C++ kernel), with the response centred first. On
a strongly anisotropic response this is a large accuracy gain over a single
length scale: on the Branin function it cuts held-out error about 23-fold
versus the median-heuristic default (to roughly half a percent of the function
range) and about 4.6-fold versus a single MLE length scale, bringing the
surrogate within a small factor of a dedicated anisotropic GP (DiceKriging).
This resolves a known finding that the default surrogate was several-fold
worse than an MLE GP on an anisotropic function. The GP remains zero-mean, so
it stays a small factor above a fully-optimised trend-bearing GP -- a
documented limitation, not a length-scale defect.The release that readies PESTO as the authoritative ensemble-manifest emitter
for coordinated multi-tool runs and broadens the forward-model surface beyond
the apsimx simulator to native ordinary-differential-equation models.
ODE / compartmental forward-model templates. New exported builders turn
a system of ordinary differential equations into a typed
pesto_forward_model() that plugs straight into pesto_ies_callback(), a
pesto_multifidelity_model() stack, and the manifest emitter -- the ODE
analogue of the apsim_callback() adapter. ode_forward_model() is the
generic builder (supply a function(t, y, theta) right-hand side, the
initial state, and the time grid); crop_growth_forward_model() is the
logistic dry-matter-accumulation crop template (Goudriaan & Monteith 1990);
and seir_forward_model() is the closed-population SEIR epidemic template
(Anderson & May 1991). Integration is a self-contained fixed-step RK4 by
default (no new hard dependency); solver = "desolve" delegates to the
optional deSolve package for stiff systems. Each template is exercised by a
simulate-forward-then-invert test that recovers the generating parameters.
Manifest schema 1.1.0: grounded semantic descriptor (obs_schema).
pesto_ensemble_manifest gains an optional obs_schema slot stating the
physical quantity and unit of each output and parameter column (plus optional
per-column grounding provenance: verified_on, oracle_kind,
evidence_path). Build one with the new exported pesto_obs_schema() and pass
it through as_manifest(fit, obs_schema = ...). The descriptor turns column
meaning from out-of-band roxygen convention into a machine-checkable field, so
a downstream consumer can verify two manifests are commensurable by name rather
than positionally. The class validator rejects a descriptor naming a column
that does not exist in the data. obs_schema is provenance metadata and is not
folded into data_hash (correspondence is grounded by an independent
consumer, not by self-hash). Additive and backward-compatible: a 1.0.0
manifest reads back unchanged with obs_schema = NULL.
apsim_callback() stamps the in-use APSIM version. The returned
forward-model closure now carries an "apsim_version" attribute (from
apsimx::apsim_version(), NA_character_ when undeterminable), so a calibrated
run can be grounded to the exact simulator that produced it via
as_manifest(fit, apsim_version = attr(fm, "apsim_version")).
The In-Process IES via R Callback vignette gains an over-determination
guard section: it shows how conditioning on a likelihood tighter than the
data deserve (passing the standard error of a replicate mean,
$\sigma/\sqrt{m}$, instead of the field-realistic replicate spread $\sigma$)
collapses the ensemble and produces a confidently-wrong posterior, and how
ensemble_spread_ess() and credible-interval coverage diagnose it.
pesto_obs_schema() gains a runnable example.
Adds two opt-in countermeasures to the finite-ensemble pathologies that
make an iterative ensemble smoother over-confident: covariance inflation
(against under-dispersion / ensemble collapse) and covariance
localisation (against spurious finite-sample parameter-observation
correlations). Both default to off; a NULL specification leaves
pesto_ies_callback() and pesto_ies_filter() byte-identical to the
previous release.
pesto_inflation() -- inflation specification with four methods:
"rtps" (relaxation to prior spread, Whitaker & Hamill 2012; the
per-parameter, spectrally-aware workhorse), "adaptive" (global
inflation targeting a spread-retention floor), "multiplicative"
(fixed factor), and "none".pesto_localisation() -- localisation specification: "correlation"
(automatic, coordinate-free, Luo & Bhakta 2020 -- the recommended
default for parameter problems with no spatial metric) or "distance"
(classical Gaspari-Cohn taper of a parameter-to-observation distance
matrix).ensemble_spread_ess() -- the collapse diagnostic: the spectral
participation ratio of the parameter anomaly covariance, i.e. the
effective number of variance-carrying directions. Recorded on every
iteration regardless of method.correlation_localisation(), gaspari_cohn(),
ensemble_solution_localised() -- the C++ kernels backing the above.
ensemble_solution_localised() is the explicit-gain GLM update that
hosts the Schur-product localisation the SVD kernel cannot; with no
taper it reproduces ensemble_solution() (approximate form) to
truncation tolerance.pesto_ies_callback() and pesto_ies_filter() gain inflation and
localisation arguments (both NULL by default) and now record the
spread-ESS and (when active) inflation / localisation diagnostics in
their per-step metadata, which flow into the ensemble manifest.NULL localisation with use_approx = FALSE warns and drops the
null-space correction.Note on terminology: the spectral spread-ESS is scale-invariant, so it is
used as the collapse diagnostic, while the "adaptive" inflation
targets a variance-magnitude retention floor; "rtps" is the method
that reshapes the spectrum. See the Countering Ensemble Collapse:
Inflation and Localisation vignette.
Promotes the two-adapter forward-model contract from an implicit
convention to a typed, enforceable object, and makes the multi-fidelity
(cheap, expensive) bridge first-class (APSIM-bridge invariants 1 and
3). No breaking changes to existing calls: pesto_ies_callback() still
accepts a bare function(theta) -> obs.
pesto_forward_model() — an S7 class wrapping a forward callable with
its output dimensionality, expected parameter names, failure policy
(on_failure, max_fail_frac), evaluation strategy (serial /
"multicore" / custom map_fn), and a fidelity tag. This is the
single contract both the native-callback and .pst-file adapter modes
honour.pesto_evaluate() — generic that runs a forward model (or a
multi-fidelity model at a chosen level) and returns a
shape-guaranteed nreal x nobs matrix with "n_failures" /
"fail_idx" attributes.as_forward_model() — coerces a bare function (or passes through an
existing object) into the contract; used internally so bare functions
keep working unchanged.pesto_multifidelity_model() — an ordered stack of fidelity levels
(cheapest first) plus relative costs; the first-class form of the
bridge's fidelity vector.mf_control_variate() — the affine (Kennedy-O'Hagan AR(1))
control-variate primitive that debiases a cheap level against a sparse
expensive sample; the plug-in point for surrogate cascades.pesto_ies_filter() — a filtering counterpart to the batch smoother
pesto_ies_callback(). It assimilates time-ordered observation
windows one after another against a static parameter ensemble, the
posterior of each window becoming the prior of the next, so a tightening
parameter posterior is available after every window (the in-season
assimilation case). It reuses the forward-model contract (parallel- and
multi-fidelity-ready via a per-window fidelity_schedule) and the C++
ensemble_solution() kernel; window_noptmax > 1 gives an iterated
filter per window. The result records a per-window history including the
per-parameter ensemble standard deviation (the tightening trace).pesto_ies_filter_result) flow into the manifest
contract: as_manifest() tags them method = "ies_filter" (added to the
pesto_ensemble_manifest validator) and carries their fidelity
provenance, so a filtered ensemble is a first-class scenario for any
downstream consumer.pesto_ies_callback() gains fidelity_schedule (consulted only for a
pesto_multifidelity_model): the fidelity level evaluated at each
iteration, supporting cheap-early / expensive-late ramping. The final
ensemble refresh always uses the highest fidelity.pesto_ies_callback() run records its realised schedule
in the result ($fidelity = list(type, schedule, final_level, n_levels, costs)), as_manifest() inherits it into the pesto_ensemble_manifest
fidelity slot unless overridden, and write_manifest() /
read_manifest() round-trip the structured record faithfully (it is
outside the integrity hash, so it does not affect verify_manifest()).
Single-fidelity runs record NULL, so their manifests are unchanged.
The manifest fidelity slot is now documented as a structured
provenance list (legacy named-numeric tags are still accepted on read).pesto_forward_model
with parallel = "multicore" dispatches realisations across forked
workers via parallel::mclapply() with L'Ecuyer streams (reproducible
under RNGkind("L'Ecuyer-CMRG")); serial bulk evaluation is unchanged
and remains the default.apsim_callback() now writes each realisation to a unique per-run
file, making the closure safe to drive in parallel (wrap it in a
pesto_forward_model(parallel = "multicore"))..eval_forward_safe)
was retired in favour of the shared engine behind pesto_evaluate();
the on-error abort message changed from `forward_model` failed to
forward model failed.parallel (a base R package) added to Imports.A code-aesthetics and review-readability patch on top of 0.4.0. No
runtime behaviour changes; no exported-API changes; no shipped-data
changes. The aim is to lift every source surface to the bar set out in
r_style.md ahead of any AAGI-AUS push.
R/internal_validation.R introduces the shared primitive validators
(.assert_positive_scalar(), .assert_nonneg_scalar(),
.assert_character_scalar(), .assert_logical_scalar(),
.assert_path_exists(), .assert_matrix(),
.assert_numeric_vector(), .assert_function(),
.assert_data_frame(), .assert_choice(), .assert_same_ncol(),
.assert_same_nrow(), .assert_required_cols()). All
@noRd @keywords internal; every helper signals failure via
stop(call. = FALSE, ...) with a backticked argument name.apsim_callback.R, pesto_reference_ies.R,
pesto_run.R (pesto_ies_callback), pst_io.R, scenario.R,
surrogate.R, manifest.R, ensemble_io.R, plot.R, and
check_surrogate_regime.R now open with .check_* / .assert_*
calls instead of inline if (!is.x) stop(...) walls.pesto_ies_callback, pesto_ies,
pesto_glm, pesto_sweep, pesto_sensitivity, read_pst,
write_pst, apsim_callback, pesto_reference_ies,
.find_pestpp_exe) now carry Sparks-style dash-banner section
comments that paragraph the work (validate inputs / resolve paths /
iterate / parse outputs / assemble result).@importFrom ggplot2 annotations consolidated into
R/pesto-package.R; the per-function @importFrom annotation on
plot_phi() has been removed in favour of inline ggplot2::
qualification at the call sites.vignettes/apsim-callback.Rmd and
vignettes/ensemble-manifest.Rmd converted to . Capital joins per
manuscript_style.md invariant 5.Dependencies, Contributing, and Acknowledgements
sections per the AAGI repository-guidelines README contract.
Citation block bumped to R package version 0.4.1.DESCRIPTION URL and BugReports, CITATION.cff,
codemeta.json, inst/CITATION, _pkgdown.yml, and the README
citation + issues links now point to
https://github.com/AAGI-AUS/PESTO (canon checklist item 5).
README install instructions and the personal r-universe URL are
retained at max578/PESTO and https://max578.r-universe.dev as
interim distribution infrastructure until the AAGI-AUS push lands.This release contains no R, C++, or shipped-data changes. It is a
governance, metadata, and project-hygiene release that lands the AAGI
canon recipes on the max578/PESTO channel.
AAGI-AUS/PESTO to
max578/PESTO. DESCRIPTION, CITATION.cff, codemeta.json,
inst/CITATION, _pkgdown.yml, README.md, CONTRIBUTING.md,
API_STABILITY.md, and the pkgdown GitHub Actions workflow header
now point to https://github.com/max578/PESTO and
https://max578.github.io/PESTO. The aagi git remote is retained
as a frozen read-only mirror; no push to AAGI-AUS without explicit
per-instance maintainer approval.AAGI-AUS publication canon (it is
published through the max578 channel); project-local configuration
is excluded from R-package builds via .Rbuildignore.man/PESTO-package.Rd regenerated to inherit the new URLs from
DESCRIPTION via devtools::document().CITATION.cff (version: 0.1.0), codemeta.json (version: "0.1.0"),
inst/CITATION (R package version 0.3.3), and the README citation
block were not in lock-step with DESCRIPTION. All four are now on
0.4.0 with date-released: "2026-05-28" and
dateModified: "2026-05-28".codemeta.json copyrightHolder corrected to Person "Max Moldovan"
with ORCID 0000-0001-9680-8474 and Adelaide University affiliation,
matching Authors@R and LICENSE.md.CODE_OF_CONDUCT.md (Contributor Covenant v2.1, pointer form).SECURITY.md (vulnerability reporting policy; maintainer email,
five-working-day acknowledgement, scope statement).air.toml (Air formatter configuration: 80-char line width,
two-space indent, auto line endings)..lintr (lintr defaults aligned with r_style.md direction:
80-char line, snake_case / dotted.case / symbols object names,
two-space indent; src, tools, inst/extdata, vignettes
excluded)..Rbuildignore already excluded all four paths; no tarball impact.src/Makevars PKG_LIBS now follows $(BLAS_LIBS) with
$(FLIBS) per Writing R Extensions §1.2.1.5. Resolves the
pre-existing structural WARNING
("apparently using $(BLAS_LIBS) without following $(FLIBS) in
'src/Makevars'") that had survived several check passes as a
"documented local-env artifact". Makevars.win was already
correct; no change needed there.R_ext/Boolean.h:62 -Wfixed-enum-extension pragma) is in R's
own header on this toolchain version and is not present on CRAN's
build farm; it persists harmlessly.write_manifest(format = "csv") is renamed to
write_manifest(format = "csv_unverified") to flag the weaker
integrity contract at every call-site. The mode itself is unchanged
(CSV-only sidecars, hash recorded but not disk-verifiable).integrity: verifiable | not_verifiable
derived from format. Verifiable: rds, both. Not verifiable:
csv_unverified. Lets non-R downstream tools (e.g. Python pipelines)
branch on the integrity contract without parsing the PESTO-specific
format vocabulary.format = "csv" spelling is still accepted at the API
boundary with a deprecation warning; the persisted form always uses
csv_unverified. read_manifest() normalises old YAMLs on the
read side, so 0.3.1 manifests round-trip cleanly under 0.3.2.{rds, both, csv_unverified}. The old "csv" token is rejected at
slot-set time (only the renamed argument accepts it, with a warning).integrity: YAML field for verifiable modes.ensemble-manifest.Rmd reframed: explicit "Inspection
CSVs (verifiable, via format = 'both')" vs. "Unverified CSV export
(via format = 'csv_unverified')" sections; the latter is presented
as "for export, not for storage you intend to re-load and trust".write_manifest() gains a format = c("rds", "both", "csv")
argument. "rds" (default) preserves the current bit-exact binary
behaviour. "both" writes RDS sidecars plus parallel CSV inspection
files (*_inspection.csv); the SHA-256 hash stays bound to the RDS.
"csv" writes CSV-only sidecars for inspection / interchange
workflows where bit-exact integrity is not required.format on pesto_ensemble_manifest records the
on-disk serialisation mode (default "rds"; preserved through
read/write round-trips). Validator enforces the three-value vocabulary.read_manifest() dispatches on file extension in the YAML's
artefacts: block — reads RDS via readRDS(), CSV via
utils::read.csv().verify_manifest() gains a message field on its return list and
returns ok = NA (with explanation) for format = "csv" manifests
whose IEEE 754 doubles have round-tripped through a write formatter.
Existing format = "rds" callers see no behaviour change; the new
field is NULL in that case.format: key and an optional
inspection_csv: block when format = "both". Backwards-compatible:
YAMLs written by PESTO 0.3.0 (no format: key) read back with
format = "rds" per the default. No schema-version bump required.test-manifest.R cover all three formats plus the
unknown-format rejection path.pesto_ensemble_manifest — versioned, hashed,
provenance-tracked container for ensemble-run output. Slots cover
params, outputs, weights, obs_target, seed, data_hash
(SHA-256), fidelity, apsim_version, pesto_version, timestamp,
plus method context (method, noptmax, lambda_schedule,
failure_rate). This is the documented, versioned format that any
downstream tool can read.as_manifest() — S7 generic with a method for
pesto_ies_callback_result. Non-destructive: wraps without mutating
the source result.write_manifest() / read_manifest() — YAML+RDS serialisation.
The YAML carries metadata + relative paths to three sidecar RDS files
(*_params.rds, *_outputs.rds, *_assim.rds); RDS is used in
preference to CSV so IEEE 754 doubles round-trip bit-exactly (the
SHA-256 integrity check would otherwise trip on CSV formatter
precision loss).verify_manifest() — recomputes the SHA-256 over
(params, outputs, weights, obs_target, seed) and compares to the
stored value, returning a diagnostic list. Detects post-write
tampering with the sidecar CSVs.ensemble-manifest — end-to-end demo of construct →
write → read → verify, plus tamper-detection.pesto_ies_callback() — drives an Iterative Ensemble Smoother
entirely in R using a user-supplied forward-model callable, bypassing
the .pst-file write/read cycle of pesto_ies(). Each iteration calls
the existing C++ kernel ensemble_solution() (Chen & Oliver, 2013).
Tolerates per-realisation failures via on_failure = c("na", "stop")
and reports a failure_rate in the result object. Phase-1 behaviour
uses a single lambda per iteration (or user-supplied schedule); a
full pestpp-ies-style lambda line-search is a planned Phase-2
enhancement.apsim_callback() — adapter that wraps the apsimx package (now
in Suggests) into a forward-model closure suitable for
pesto_ies_callback(). Per-realisation template copy, parameter edit
via apsimx::edit_apsimx() / edit_apsim(), run, and extraction.
Failures (edit / run / extractor) surface as NA rows for the IES
driver to handle.apsim-callback — synthetic linear-Gaussian recovery
demo plus disabled apsimx example.inst/benchmarks/d4_callback_vs_pst.R.pesto_ies_callback() records obs_target, obs_sd, and weights
on its result list so the manifest emitter has full IES context to
capture..pst path) is
deferred to the §D1 scenario library landing. The current benchmark
script measures the callback path on a synthetic surrogate forward
model only.apsimx is in Suggests not Imports; apsim_callback() checks
for it at call time with requireNamespace().S7 (>= 0.2.0), yaml (>= 2.3.0),
digest (>= 0.6.0).ensemble_solution() sign-convention bug. The C++ kernel
requires obs_resid = sim - obs; the docstring previously stated
the inverse. Two genuine in-package call sites were silently
inverting upgrades: src/surrogate_ies.cpp:347 and
vignettes/surrogate-ies.Rmd:148, 181. Both fixed; surrogate-IES
now applies upgrades in the correct direction. Regression test
tests/testthat/test-ensemble-solution-sign.R asserts strict
monotone phi descent under the correct convention AND geometric
divergence under the inverted one.pesto_reference_ies() — pure-R, textbook implementation of the
Chen & Oliver (2013) eq. 12 IES update. Independent of the C++
kernel; used as the canonical comparison target by the comparison
vignette so it ships and runs without the upstream pestpp-ies
binary. Cross-validated against the C++ kernel at machine precision
(max element-wise delta = 5.8e-15).check_surrogate_regime() — soft guardrail that warns when the
surrogate-IES regime is unfavourable (n_train < threshold * n_params).
Stand-alone helper, not auto-invoked by pesto_surrogate_ies()
(v0.3 wiring candidate).plot_identifiability() gains a jacobian = NULL matrix-input
path. Backward-compatible: jco_file = NULL retained; the two are
mutually exclusive.vignettes/pestpp-comparison-and-simulation.Rmd now compares PESTO
native IES against pesto_reference_ies() by default — no upstream
binary required. The pure-R reference cache ships at
inst/extdata/pestpp_cache/scenario_a_reference.rds (SHA-256-pinned
to the prior ensemble). When the developer-side cache
tools/pestpp_benchmark/scenario_a_pestpp_ies.rds is present and
PESTO_PESTPP_BIN resolves, the vignette extends the agreement
plot with the live binary's posterior.Sys.getenv("PESTO_PESTPP_BIN") + Sys.which("pestpp-ies")
fallback.tools/pestpp_benchmark/run_benchmark.R regenerates both
caches deterministically. Documented in CONTRIBUTING.md.@examples block (30 of 30
documented exports/methods). The four external-binary runners
(pesto_ies, pesto_glm, pesto_sweep, pesto_sensitivity) and
pesto_surrogate_ies use guarded \donttest{} (no \dontrun{}).surrogate-ies.Rmd) and a "surrogate savings in this regime" note
(pestpp-comparison-and-simulation.Rmd) covering the
curse-of-dimensionality finding from investigation I3.?ensemble_solution now states the sim - obs
convention with a full GLM-derivation rationale.cran-comments.md with per-NOTE justification.CITATION.cff (CFF 1.2.0 + ORCID + preferred citation).codemeta.json (CodeMeta 2.0).CONTRIBUTING.md documenting the developer benchmark workflow.inst/WORDLIST with ~120 domain terms; Language: en-AU
added to DESCRIPTION.src/Makevars: PKG_LIBS gains $(FLIBS) (CRAN portability
requirement for $(BLAS_LIBS)).LICENSE renamed to LICENSE.md and .Rbuildignore-d (CRAN
convention for GPL-licensed packages).check_surrogate_regime() helper exported.aut, cre, and cph
(administrative consolidation; no licence change).LICENSE file rewritten with corrected canonical wording and copyright
attribution.src/ updated to reflect sole authorship.ensemble_solution() — High-performance C++ implementation of the IES
ensemble update equation (Chen & Oliver, 2013) via RcppEigen.ensemble_solution_mda() — Multiple Data Assimilation (Evensen, 2018)
update kernel.compute_phi() — Fast weighted sum-of-squares objective function.adaptive_svd() — Automatic SVD backend selection (LAPACK, Eigen BDCSVD,
or randomised SVD) based on matrix size and target rank.rsvd() — Randomised SVD (Halko-Martinsson-Tropp, 2011) for asymptotically
faster rank-k approximations.accelerate_svd() — Direct LAPACK SVD leveraging platform-optimised BLAS
(Apple Accelerate/AMX on macOS, MKL or OpenBLAS on Linux).ensemble_solution_gpu() — ensemble solution with adaptive SVD backend
selection and performance diagnostics (renamed to
ensemble_solution_adaptive() in the development version).train_gp_surrogate() — Gaussian Process surrogate model training with
automatic hyperparameter selection (median heuristic).predict_gp_surrogate() — GP prediction with uncertainty quantification.surrogate_ensemble_update() — Surrogate-accelerated IES update with
adaptive model/surrogate switching and control-variate bias correction.adaptive_ensemble_size() — Convergence-aware dynamic ensemble sizing
based on ESS and coefficient of variation diagnostics.read_pst() / write_pst() — PEST control file I/O.read_ensemble() / write_ensemble() — Ensemble file I/O (CSV + binary).pesto_ies(), pesto_glm(), pesto_sweep(), pesto_sensitivity() —
High-level wrappers for PEST++ executables.create_pest_scenario() — Programmatic scenario builder.plot_phi() — Objective function convergence plotting.plot_ensemble() — Prior/posterior parameter distribution comparison.plot_identifiability() — SVD-based parameter identifiability analysis.plot_surrogate_diagnostics() — Surrogate IES performance visualisation.