|
TRUST 1.9.8
HPC thermohydraulic platform
|
This page describes how to author a TRUST (or Baltik) validation report. It focuses on test-case declaration through the trustutils.run API. Postprocessing and plotting (trustutils.plot, trustutils.visit, trustutils.widget) are covered on a separate page.
See also How to execute a validation report for running an existing report, and Validation form API guide for the full doxygen API reference of trustutils.
A validation report is a Jupyter notebook paired with a src/ directory, sitting under Validation/Rapports_automatiques/<Category>/<FormName>/. It plays three roles at once:
The intended author is a TRUST or Baltik developer adding or qualifying a feature. The intended consumer is a future reader who needs to understand what was tested, plus the validation harness, which re-executes the form regularly.
The sample form Validation/Rapports_automatiques/Verification/SampleFormJupyter is a living example of every feature mentioned on this page.
A form has a small, fixed shape:
Two invariants:
Files such as legacy src/prepare, src/pre_run, src/post_run shell scripts and src/liste_cas_exclu_nr may still appear in older forms. New forms should not use them; see Patterns to avoid.
Five cells are enough for a runnable form. Markdown cells are added around them to describe the study.
The form is then opened in JupyterLab and executed top to bottom. PDF/HTML export and full NR execution are driven by Run_fiche (see How to execute a validation report).
Use from trustutils import run. The longer from trustutils.jupyter import run still works in many existing forms but is no longer the recommended path.
Cases are declared before running them, all in one cell (or a few), through the run module. The first call to any addCase* function materialises build/ by copying src/ into it.
Gotcha. Do not import matplotlib.* or from trustutils import plot in any cell that runs before run.runCases(). The non-regression extraction process executes every cell up to that point in a headless context, and a matplotlib import there breaks extraction. Keep all plotting imports in postprocessing cells. See No matplotlib or trustutils.plot before run.runCases().
Use this for the vast majority of cases. The first argument is the directory relative to src/ (use "." for the root). Common keyword arguments:
addCase returns the underlying TRUSTCase object, which can be stored for later manipulation:
To generate a family of cases from a single template, write $keyword placeholders in the .data file and substitute them at declaration time:
This is the preferred way to express mesh-refinement studies, discretization sweeps, or scheme comparisons.
Pass targetData="new_name.data" if the generated file should have a different name than the template. All the usual kwargs (nbProcs, excluNR, pre_run, post_run) are accepted.
When parameter substitution is not enough — for instance to derive a case from a previously derived case, or to manipulate the file tree programmatically — use the lower-level methods on TRUSTCase:
Note that copy() does not carry nbProcs, excluNR, pre_run or post_run over — these must be re-specified on the copy.
A case declared with nbProcs > 1 must be partitioned before run. Partitioning calls trust -partition internally:
By default (overwritePartition=True) the partition declared in the dataset is overwritten, and the domain is cut along a single direction — the original historical behaviour. Pass overwritePartition=False to reuse the user-specified Partition block from the .data file.
Any preparation that historically lived in a src/prepare bash script (generating meshes, converting MED files, sourcing environments, deriving intermediate datasets, ...) should now be a regular Python cell in the notebook, executed before the addCase* calls.
Helpful entry points:
If the preparation logic grows past a few lines, move it into src/python_modules/ and import it from the notebook (see Reusable Python).
Each case can be assigned two Python callables that run immediately before and after the trust execution. They are the modern replacement for the legacy src/pre_run and src/post_run bash scripts, and they are what makes non-trivial validation patterns (restarts in particular) integrate cleanly with the non-regression machinery.
Some validation scenarios cannot be expressed as a single trust invocation. The canonical example is a restart: case B needs a .sauv file produced by case A. Doing this with shell scripts entangles the form with bash plumbing that the NR extraction process cannot easily reason about, and that typically resorts to manual file copies and hard-coded paths.
Python hooks solve this. A pre_run is just a function: it can call any TRUST machinery, produce input files, or even launch other TRUSTCases and wait for them. And because the runtime knows it is a Python callable, it can execute it in NR-extraction mode too, so that the restart case generated for non-regression also has its .sauv file ready.
The same pre_run= / post_run= keyword arguments are accepted by run.addCase, run.addCaseFromTemplate, TRUSTCase() and TRUSTCase.copy(). A case cannot have both a Python hook and a src/pre_run (or src/post_run) bash script of the same kind — the runtime will refuse to run the case and ask for the bash file to be deleted.
For each case run.runCases() orchestrates:
In the default parallel mode, the pre_run runs synchronously, the trust invocation is submitted as a subprocess, and the post_run is attached as a callback that fires once the case finishes. In sequential mode (run.runCases(preventConcurrent=True)) all three steps run strictly in order on the same Python thread before the next case starts.
A pre_run (or post_run) may itself construct a TRUSTCase and call .run() on it. The scheduler detects these sub-cases, treats them as dependencies of the parent case, and waits for them to finish before launching the parent.
This is the recommended pattern for restart-based validation:
The form Validation/Rapports_automatiques/Verification/TestJupyterValidation/Test_Jupyter_pre_run_with_restart is the canonical worked example.
When the form is reloaded by the NR-extraction machinery (run.isExtractingNR() is True), the runtime calls registered Python pre_runs — so that the artifacts they produce (typically restart files) exist when the NR test runs — but it does not call post_runs, and it does not start the main trust invocations from the notebook.
Two practical consequences for hook authors:
The historical src/pre_run and src/post_run shell scripts still work for back-compatibility, but new forms should always prefer the Python hooks:
A case may not mix the two: if a pre_run= callable is provided, the runtime expects no src/pre_run script (and vice-versa).
Anything more than a few lines — mesh generation helpers, intricate pre_run bodies, custom postprocessing — belongs in src/python_modules/foo.py and is imported by the notebook:
src/python_modules/ is automatically on sys.path when the form runs.
One constraint: do not import trustutils.run from these modules. The orchestration (calls to reset, addCase, runCases, ...) belongs in the notebook itself, so that the form remains the single place that describes the test plan. The modules should expose pure helpers operating on files, arrays or TRUSTCase instances passed in by the notebook.
Every case declared in a form automatically becomes an NR test. By default, NR truncates the run to its first three timesteps.
When in doubt about NR behaviour, check run.isExtractingNR() from a cell to branch on it explicitly. Keep the case-declaration cell side-effect-free beyond what NR tolerates: it will be re-imported in that mode.
Older forms exhibit a number of patterns that we no longer recommend. Authors should not introduce these in new forms, and should migrate them opportunistically when touching existing forms.
| Avoid | Use instead |
|---|---|
| from trustutils.jupyter import run | from trustutils import run |
| src/prepare bash script | a plain Python cell in the notebook (or src/python_modules/) |
| src/pre_run, src/post_run bash scripts | pre_run= / post_run= Python callables (Hooks) |
| src/liste_cas_exclu_nr | excluNR=True on the case |
| run.executeScript("...") / run.executeCommand | reusable Python in src/python_modules/ |
| Hand-rolled file-tree copies and sed of .data | run.addCaseFromTemplate(...) or TRUSTCase.copy().substitute*() |
While authoring, the typical loop is to open the notebook in JupyterLab from the form root, edit and re-execute cells interactively, and only invoke Run_fiche once for full PDF export or NR extraction (see How to execute a validation report).
Two switches worth knowing: