Skip to content

Tutorial: Validating BV4 ​

A walk-through of running adsmd on the tests/BV4 plant project: what each diagnostic means, which ones point at real input problems, and how the other subcommands help when you inspect a project.

Setup ​

Build the binary once:

bash
cd tools/adsmd
cargo build --release
PATH="$PWD/target/release:$PATH"   # convenience for the rest of this page

All commands on this page are run from tools/adsmd/.

First run ​

bash
adsmd validate ../../tests/BV4

The report lists every diagnostic with its rule id, file:line:col, a source snippet and, where one exists, a help line. It ends with a summary line. On a fresh checkout of the repository the summary is:

text
adsmd: 1 error(s), 1 warning(s), 166 info(s)

and the exit code is 2, because at least one error is present.

RuleSeverityCountWhat it reports on BV4
R0error1SystemReliability.txt cannot be parsed
R24warning1SPDS_Tave-Tref is in the OAT safety parameters but not in SPDS_input.txt
R13info149ZProcedure_*.txt files that are not in the coded-step index
R45info9initiating events scheduled after Truncation_time
R32info6.bak and .pre files under Input/
R30info1Input/Crew/StaticPIF.txt is not read by the engine
R33info1the project has no ZiniADS.txt

The totals depend on ZiniADS.txt

tests/*/ZiniADS.txt is git-ignored, so a fresh checkout of tests/BV4 has none. If your working copy has a local tests/BV4/ZiniADS.txt, the result changes:

  • R33 does not fire, so the run reports 165 infos.
  • R1 and R2 check the two paths in the file. When input-deck names a file that does not exist, R1 adds a second error: 2 error(s), 1 warning(s), 165 info(s).

The copy of BV4 that CMake writes to build/tests/BV4 has a generated ZiniADS.txt with a valid deck path; adsmd validate on that directory reports 1 error(s), 1 warning(s), 165 info(s).

Piped to a file or another program, the report uses plain ASCII frames, as in the excerpts below. On a terminal the same frames are drawn with Unicode box characters and colour.

What each diagnostic means ​

R0: parse error in SystemReliability.txt ​

text
adsmd::R0

  x could not parse `X_SGTR_SG_A` as a number
   ,-[…/tests/BV4/Input/HardwareReliability/SystemReliability.txt:2:1]
 1 | System_Reliability  0
 2 | Main_Steam_Isolation    X_SGTR_SG_A 9999999.0       1.0 6.0 4.0
   : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 3 | Reactor_Trip        X_TD_AFW_Pump   50.0        50.0    50.0    50.0
   `----

The file declares System_Reliability 0 and then lists 14 rows. With a count of 0 the first subsection is empty, so the next two tokens (Main_Steam_Isolation, X_SGTR_SG_A) are read as the header and the count of the second subsection, and X_SGTR_SG_A is not a number. The engine reads the file the same way: its stream read fails at that token, it prints a WARNING: SystemReliability.txt: malformed or unexpected content line, and it loads no hardware failure models from the file.

In your own project, fix this by making the count on line 1 match the rows that follow and by keeping all three subsection headers (see the format reference). tests/BV4 itself is a regression fixture whose expected outputs encode the current behaviour, so leave its inputs as they are.

A parse error stops only the parser of that file. The file is missing from the manifest, rules that need it (R10, R11, R42 here) have nothing to check, and every other rule still runs.

R24: KB safety parameter missing from SPDS_input.txt ​

text
adsmd::R24

  ! OAT safety-parameter `SPDS_Tave-Tref` is not present in `SPDS_input.txt`
   ,-[…/tests/BV4/Input/Crew/ActionTaker/KB_OAT_Safety_Parameters.txt:3:1]
 2 | SPDS_PZR_Level      PZR_Level       0.10    0.35    0.55    0.95    0.5
 3 | SPDS_Tave-Tref      Tave-Tref       -5.0    -1.0    1.0 5.0 0.5
   : ^^^^^^^^^^^^^^
   `----
  help: either add the entry to SPDS_input.txt or remove it from the KB

The action taker's safety-parameter list names an SPDS entry that the central SPDS_input.txt does not define. The JSON and HTML reports also carry up to three "did you mean" candidates for this diagnostic.

R13 × 149: step files outside the coded-step index ​

Each hit names a ZProcedure_<EOP>_<Step>.txt file whose (EOP, Step) pair has no row in Procedures.txt. On BV4 these are 34 E_0 files, 81 E_3 files, 33 ECA_0.0 files and 1 FRG_H.1 file. The severity is info because such files can be intentional. The .adsmd.toml example below switches this rule off.

R45 × 9: initiating events after Truncation_time ​

ZCtrlPar.txt sets Truncation_time to 15.5 s, and nine rows of Initiating_Event.txt have time = 850. Those events do not fire in a run that uses the file's truncation time. The engine's --truncation-time option changes the truncation time at run time; adsmd reads the value in ZCtrlPar.txt.

R32 × 6: editor artifacts ​

Four .bak and two .pre files in Input/Crew/Procedures/. The engine does not read them. R32 lists each one so that it is either removed or kept on purpose.

R30: StaticPIF.txt is unread ​

No engine code opens Input/Crew/StaticPIF.txt. Edits to it have no effect on a simulation.

R33 and R1: the role of ZiniADS.txt ​

adsmd recognises a directory as a plant project when it contains ZiniADS.txt, or when it contains both Input/Scheduler/ and Input/System/. In the second case R33 notes that the file is absent, and the RELAP5 deck is not scanned (the deck path comes from ZiniADS.txt), so R29 has nothing to check.

When ZiniADS.txt exists, R1 checks its input-deck path. A file whose input-deck points into Input/ while the deck sits in the project root produces:

text
adsmd::R1

  x input-deck path `…/tests/BV4/Input/BV4.i` does not exist; a file with the same name was found at `…/tests/
  | BV4/BV4.i`
   ,-[…/tests/BV4/ZiniADS.txt:2:1]
 1 | project-dir=…/tests/BV4
 2 | input-deck=…/tests/BV4/Input/BV4.i
   : ^^^^^^^^^^
   `----
  help: update ZiniADS.txt: `input-deck=…/tests/BV4/BV4.i`

The help line gives the corrected entry. After the path is fixed the R1 error is gone and the run reports 1 error(s), 1 warning(s), 165 info(s).

JSON output ​

For scripts, CI gates and dashboards use --json:

bash
adsmd validate ../../tests/BV4 --json | jq '.summary'
json
{
  "errors": 1,
  "exit_code": 2,
  "infos": 166,
  "warnings": 1
}

These are the fresh-checkout numbers from the first run. The full list is under .diagnostics:

bash
adsmd validate ../../tests/BV4 --json | jq '.diagnostics[] | {rule_id, severity, message}'

A per-rule count, which is how the table in First run was produced:

bash
adsmd validate ../../tests/BV4 --json | jq -r '.diagnostics[] | "R\(.rule_id) \(.severity)"' | sort | uniq -c

SARIF output ​

--sarif emits a SARIF 2.1.0 log for GitHub Code Scanning, Azure DevOps and editors with a SARIF viewer:

bash
adsmd validate ../../tests/BV4 --sarif > adsmd.sarif

Each rule that fired appears under runs[0].tool.driver.rules with the id ADSMD<NNNN> (ADSMD0000-PARSE for R0) and a helpUri that points at the rule catalog. A complete GitHub Actions job is in Integrating adsmd.

HTML report ​

bash
adsmd validate ../../tests/BV4 --html=bv4.html

writes one self-contained page (inline CSS and JavaScript, no external assets) with severity and rule filters, per-file grouping and source snippets.

Filtering by severity or rule ​

--severity <level> keeps only diagnostics at that level or above (error > warning > info):

bash
adsmd validate ../../tests/BV4 --severity error
text
adsmd: 1 error(s), 0 warning(s), 0 info(s)

(On a fresh checkout. A local ZiniADS.txt with a wrong deck path adds the R1 error.)

--rule R1,R7-R12,R29 keeps only the listed rule ids; ranges are inclusive. R0 (parse errors) is always kept, because hiding the parser would hide whole files.

bash
adsmd validate ../../tests/BV4 --rule R1,R7,R10,R11,R29

Both filters are applied before the summary and the exit code are computed. A diagnostic that is filtered out does not count toward the exit code.

adsmd rules prints the catalog (id, short name, one-line description):

bash
adsmd rules | head -3
text
R0    PARSE                                     Parser bailed before producing a typed value
R1    input-deck-exists                         ZiniADS input-deck path resolves
R2    project-dir-layout                        ZiniADS project-dir + Input/ exist

Exit codes and --exit-on ​

By default only errors make validate fail: the exit code is 2 when an error is present and 0 otherwise, even when warnings fired. --exit-on warning also returns 1 when the worst finding is a warning, --exit-on info returns 1 for any finding, and --exit-on never always returns 0. On BV4, which has an R0 error:

bash
adsmd validate ../../tests/BV4 --exit-on never

exits 0, and every other --exit-on level exits 2. The complete table is in the CLI reference.

Per-project config: .adsmd.toml ​

Put a .adsmd.toml in the plant project directory or in any directory above it. adsmd walks up from the project and uses the first file it finds. The [rules] table switches rules off or changes their severity, so a project can keep a clean report without passing --rule on every call.

toml
[rules]
R13 = "off"      # silence "ZProcedure not in the coded-step index" notes
R30 = "off"      # silence the StaticPIF.txt note
R32 = "warning"  # raise .bak/.orig/.pre artifacts from info to warning
R14 = "error"    # raise header/filename mismatches from warning to error

With this file next to a fresh-checkout copy of BV4, adsmd validate reports 1 error(s), 7 warning(s), 10 info(s): the 149 R13 and 1 R30 infos are gone and the 6 R32 findings count as warnings.

Values are case-insensitive: off, info, warning (or warn), error. Keys are R<n>, r<n> or <n>. R0 cannot be configured: a .adsmd.toml that names it, or that holds an unknown value, makes validate stop with exit code 64.

Rules that are switched off do not appear in the output and do not affect the exit code. Rules with a changed severity count at their new severity, so R32 = "warning" together with --exit-on warning makes editor artifacts fail a build.

Inspecting the manifest ​

adsmd manifest <plant-dir> prints the typed manifest as JSON, which shows what the parsers extracted:

bash
adsmd manifest ../../tests/BV4 | jq '{
  iE: .initiating_event.declared_count,
  channels_volumes: .channels.control_volumes.declared_count,
  cp_params: (.control_panel.parameters | length),
  cp_alarms: (
    [.control_panel.alarms_parameter,
     .control_panel.alarms_component,
     .control_panel.alarms_difference] | flatten | length
  ),
  fts: (.fault_trees | length)
}'
json
{
  "iE": 21,
  "channels_volumes": 20,
  "cp_params": 141,
  "cp_alarms": 93,
  "fts": 2
}

On BV4 the command also prints adsmd: 1 parse-error diagnostic(s) emitted to stderr; manifest may be partial on stderr, because SystemReliability.txt does not parse and the manifest has no hw_reliability entry. Only this count line is printed; use adsmd validate to see the parse error itself.

adsmd schema prints the JSON Schema (draft-07) of the manifest, generated from the Rust types together with their documentation comments:

bash
adsmd schema > plant-project.schema.json

Summary of a project: adsmd stats ​

bash
adsmd stats ../../tests/BV4

prints one line per file family, for example:

text
  Initiating_Event.txt: 21 action(s)
  RELAP5_channels.txt: 20 HV / 24 HJ / 51 VT / 31 LT / 99 IC / 0 HS / 78 CV
  ControlPanel.txt: 141 param / 77 comp-state / 29 fine / 67 ctrl / 38 alarm-p / 37 alarm-c / 18 alarm-d / 0 support-system
  Procedures: 13 multipliers / 302 coded steps / 452 step files / branches: A=8 CV=33 M=4

No rules are run. The ZiniADS.txt and Deck lines depend on whether a ZiniADS.txt is present.

Comparing two projects: adsmd diff ​

bash
adsmd diff ../../tests/BV4 ../../tests/BV3
text
─ ControlPanel.support_system_displays (0 total in a, 2 total in b)
  + FTANode0
  + FTANode6

─ Procedures.coded_steps (302 total in a, 303 total in b)
  + E_0/Step_3

The output starts with two lines naming a and b (omitted above). The exit code is 1 when differences exist and 0 when the compared name sets are identical.

Cross-references: adsmd xref ​

adsmd xref <plant-dir> <symbol> lists every place a name is declared or referenced in the manifest, one hit per line as tab-separated <file>:<line>:<col>, [<context>] and the source line, so an editor can jump to the location:

bash
adsmd xref ../../tests/BV4 X_PZR_PORV
text
…/Input/Indicators/ControlPanel.txt:226:1   [ControlPanel.fine_adjust_controls]   X_PZR_PORV IC_805 1.0 0.33 …
…/Input/System/Initiating_Event.txt:8:1     [Initiating_Event.action_symbol]      Open_PORV  0.33 850.0 X_PZR_PORV …
…/Input/System/Initiating_Event.txt:13:1    [Initiating_Event.action_symbol]      Close_PORV 0.0 -185.0 X_PZR_PORV …

(The real output has absolute paths and the full source lines; both are shortened here.)

The symbol is matched literally against channel symbols (HV_204), ControlPanel names (Containment_Pressure, alarm names, X_* control symbols, support-system display names), SPDS names, initiating-event action symbols, SystemReliability.txt names, EOP and step ids (E_0), and Calculation_Aid.txt curve and input names. The context tag identifies the manifest section that matched. The exit code is 1 when nothing matches.

Next steps ​

ADS-IDAC Engine Documentation