Skip to content

New Feature: Adding input file verification to OpenFAST, FAST.Farm and drivers.#3379

Draft
mayankchetan wants to merge 30 commits into
OpenFAST:devfrom
mayankchetan:f/check-input
Draft

New Feature: Adding input file verification to OpenFAST, FAST.Farm and drivers.#3379
mayankchetan wants to merge 30 commits into
OpenFAST:devfrom
mayankchetan:f/check-input

Conversation

@mayankchetan

@mayankchetan mayankchetan commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Feature or improvement description

New -CheckInput command-line option that verifies an input deck without running a simulation:

openfast -CheckInput MyTurbine.fst

The check runs the full initialization path of every module enabled by the input file — reading and
validating all module input files, resolving file references, building meshes and airfoil tables,
loading controller/interface libraries, and running the glue code's cross-module consistency checks
and mesh-mapping setup — then exits before any time marching. Three outputs:

  • a self-contained console INPUT CHECK SUMMARY (per-module status, every error and warning with
    its source);
  • a machine-readable <RootName>.verify.yaml report, written incrementally (a file without the
    trailing overall_status: block indicates the check itself crashed — a deliberate liveness
    contract for scripted callers);
  • process exit code 0 (deck valid; warnings allowed) or 1 (one or more fatal input errors).

Unlike a normal run, which stops at the first fatal error, -CheckInput uses
attempt-everything error accumulation in the openfast glue: when one module's init fails, the
error is recorded and every remaining module is still attempted (with guarded/stubbed interface data
where a failed upstream module would otherwise cause invalid dereferences, disclosed in the report as
status unavailable). A deck with several independent problems is fully diagnosed in one
invocation — the motivating use case is scripted and agent-driven workflows that generate decks
programmatically, where today each error costs a full simulation launch. It also replaces the common
TMax = DT "one-timestep" validation hack.

The flag is available on every user-facing executable:

Executables Check semantics
openfast Full attempt-everything accumulation across all modules + glue cross-module checks
FAST.Farm Per-component accumulation; the per-turbine init loop runs all turbines with T<n>: attribution; downstream farm steps after a failure reported as skipped
turbsim, 12 module drivers (aerodyn, aeroacoustics, hydrodyn, seastate, moordyn, inflowwind, aerodisk, sed, soildyn, orca, beamdyn, unsteadyaero) Init-only check with report (fail-fast semantics; report suffix .driver.verify.yaml)
Excluded (documented in the user docs) servodyn_driver (hardcoded test harness; deck is a code literal), feam_driver (no CLI), openfast_registry, C++ glue (dryRun already exists in the C++ API)

Prior art: the C++ API's dryRun flag; LS-DYNA mcheck=1; OpenFOAM checkMesh.

Related issue, if one exists

No existing issue or PR requests an input-check mode for the Fortran CLI.

Impacted areas of the software

  • NWTC Library: new module NWTC_CheckInput.f90 (error collector, console summary writer,
    incremental YAML report writer, exit-code helper, driver-side helpers) + unit tests
    (fortran-lang test-drive, 13 tests).
  • VersionInfo / CheckArgs: new CHECKINPUT flag case + unit test.
  • openfast glue (FAST_Prog.f90, FAST_Subs.f90, FAST_Registry.txt/generated
    FAST_Types.f90): dispatch branch, new FAST_CheckInput_T driver routine, a CheckInputMode
    parameter, collect-and-continue behavior in FAST_InitializeAll's contained Failed() (gated on
    an optional collector argument), dereference guards/stub meshes for failed-module continuation,
    per-module report labels.
  • FAST.Farm glue (FAST_Farm.f90, FAST_Farm_Subs.f90): dispatch + Farm_CheckInput,
    collector threading through Farm_Initialize. Also fixes three latent IsInitialized-set-on-
    failed-init bugs (AWAE, per-turbine WD and FWrap instances) that would have had FARM_End tear
    down failed instances.
  • TurbSim (TurbSim.f90) and the 12 driver programs listed above (main files + their argument
    parsers; InflowWind_Driver_Registry.txt regenerated for one new flag field).
  • Regression test infrastructure: new baseline-free runner reg_tests/executeCheckInputTest.py
    (fixtures generated at test time by copying + corrupting pristine r-test cases — the r-test
    submodule is not modified) and 19 CTest cases under the checkinput label.
  • Documentation: docs/source/working.rst (usage, report contract, availability table,
    exclusions, DLL caveat, non-goals).

No changes to any module's physics or Validate* routines. All error detection reuses the
existing validators; the feature only changes what happens after they report.

Backward compatibility: without the flag, behavior is unchanged. All new code paths are gated on
the CheckInputMode parameter (default false) or the presence of the optional collector argument
(never passed by existing callers, including the C bindings / Simulink / AeroMap paths). The few
ungated allocated()/%committed guards are unreachable-when-false on normal runs (failure aborts
before those lines). Verified empirically: baseline regression tests pass (below) and byte-identical
output spot-checks were done per driver during development.

Additional supporting information

  • The attempt-everything mode doubles as a failure-path exerciser for init code. A sweep of the full
    r-test corpus (173 cases) through -CheckInput surfaced two latent state-consistency flaws in
    existing code that fail-fast behavior had been masking; both are guarded in this PR and locked in
    as regression tests:
    • the glue read SrvD%Input(...)%ExternalBlPitchCom unconditionally after ServoDyn init (never
      allocated if SrvD_Init fails early);
    • SlD_Init's REDWIN setup sets a fatal ErrStat without an immediate return, so a subsequent
      successful allocation's error check trips on the stale fatal, leaving WriteOutputHdr allocated
      but WriteOutputUnt not; FAST_InitOutput trusted the header array alone. (The missing
      early-return in SoilDyn.f90 itself is a candidate for a small separate fix; this PR only
      hardens the glue, per its no-module-changes rule.)
  • The same sweep found two r-test driver inputs that fail to parse as-shipped
    (ad_BAR_OLAF/ad_driver_Advanced.dvr: missing numCases; ad_Kite_OLAF/ad_driver_8figure.dvr:
    missing analysisType). Not addressed here — can open r-test issues if useful.
  • Known limitations (documented): runtime-only checks (large-deflection warnings, solver
    convergence, NaN growth) are out of scope by design; TurbSim's pre-existing output-writability
    probe leaves a 0-byte .wnd placeholder during a check; MoorDyn's module-level .MD.out header
    is still written (module scope); modules whose init loads an external DLL (SoilDyn REDWIN,
    OrcaFlex) report a missing DLL as that component's failure — deliberate, since the DLL is part of
    the deck/environment contract.

Generative AI usage

Substantial portions of this feature were developed with Anthropic Claude (Claude Code), under human
direction for all design decisions. Specifically, a multi-agent workflow was used: Claude Fable 5
orchestrated the work (design/spec interview with the human author, implementation planning, and
adjudication of review findings); Claude Sonnet agents performed the code implementation, the
per-task independent code reviews, and the codebase reconnaissance; Claude Opus performed the two
whole-branch final reviews; Claude Haiku handled small fully-specified tasks (documentation,
single-file test fixes). Every task was gated by an independent review pass (spec compliance + code
quality) before proceeding, and all changes are backed by the test evidence below.

Co-authored-by: Anthropic Claude claude@anthropic.com

This PR heavily relies on OpenFAST Agentic Kit (OAK) workflows.

Test results, if applicable

All on macOS (arm64), gfortran 15.2, RelWithDebInfo, double precision:

  • Unit tests: nwtc_library_utest 13/13 (new NWTC_CheckInput suite); versioninfo_utest 15/15
    (including the new flag case; no regressions).

  • New feature tests: ctest -R checkinput — 19/19. Includes the multi-error acceptance test (two
    independent module errors seeded, both must be reported in one run), failed-module cascade and
    segfault-regression cases, FAST.Farm per-turbine attribution, and per-executable rollout cases.
    These tests are baseline-free (exit code + report assertions only) and generate corrupted fixtures
    at test time.

  • Full r-test corpus sweep through -CheckInput (ad hoc, not CI): 173/173 cases complete without
    crashing — 168 pass, 3 fail cleanly with correct reports (1 macOS AMReX build limitation, the 2
    malformed r-test .dvr inputs noted above), 0 crashes.

  • Baseline physics regression (unchanged-behavior check): AWT_YFix_WSt, AOC_WSt,
    5MW_Land_DLL_WTurb — all pass channel-by-channel against stored baselines on the feature
    branch (reference DISCON controllers built via regression_test_controllers).

  • r-test branch merging required — not required: no files in the r-test submodule are added
    or modified; all new test fixtures are generated at test time from pristine copies.

  • please squash PR before merging

mayankchetan and others added 30 commits April 1, 2025 09:39
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…ror handling

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…failure paths

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…lable sticky

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…ulation)

Adds reg_tests/executeCheckInputTest.py (stdlib-only assertion runner) and
of_checkinput registrations in CTestList.cmake: 3 positive cases (AOC_WSt,
5MW_Land_BD_Init, AWT_YFix_WSt) plus one negative case that seeds an
ElastoDyn blade-file error and an InflowWind WindType error into a copy of
AOC_WSt and asserts both are reported in one run.

Fixtures are generated at test time by copying pristine r-test cases (and
corrupting the copy for the negative case) -- nothing is written back into
the r-test submodule. The runner additionally scans copied input files for
"../<name>/" sibling-directory references (e.g. AOC_WSt -> ../AOC,
5MW_Land_BD_Init -> ../5MW_Baseline) and copies those siblings alongside the
case directory, since r-test cases are not self-contained.

The two 5MW DLL-controller cases from the original plan were substituted
with controller-free equivalents (5MW_Land_BD_Init, AWT_YFix_WSt) because
the reference DISCON controllers are built by a separate CMake target not
compiled in this tree; see CTestList.cmake comments for details.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
… structure

Replace simple whitespace-stripping approach with structure-preserving comparison
that joins hard-wrapped console lines per summary entry before matching, preventing
false positives from messages straddling unrelated lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…th exit code

- SED's case-default block in FAST_InitializeAll now mirrors the ElastoDyn
  failure-path guard: on a failed SED_Init, stub HubPtMotion/NacelleMotion/
  PlatformPtMesh (committed, neutral) and mark the enabled downstream
  consumers (InflowWind/AeroDyn/AeroDisk/ServoDyn) 'unavailable', matching
  what ED already did. Downstream reads of SED%y%BladeRootMotion(k) (AeroDyn,
  ServoDyn) and SrvD's SED BlPitchInit copy are additionally guarded against
  unallocated/uncommitted/undersized data, closing the segfault gap where a
  corrupted SED deck could crash -CheckInput instead of being reported.
- NWTC_CheckInput's CkIn_CloseReport now derives overall_status from
  CkIn_ExitCode instead of NumErrors, so a fatal collected with an empty
  message (which does not increment NumErrors but does mark its component
  CkIn_St_Failed) can no longer make the report disagree with the process
  exit code. Covered by new unit test test_empty_message_fatal.
- Adds CTest checkinput_SED_error: corrupts SED's NumBl to 0 so SED_Init
  fails before committing its output meshes, and asserts SED is attributed,
  the process exits 1 with status failed, and it does not crash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
- FAST_CheckInput_T: correct the comment describing what reaches the outer
  ErrStat>=AbortErrLev check (module Init failures take the collect path,
  not FAST_Init's); drop the unused FIAStat, discarding CkIn_ComponentStatus's
  return value inline instead.
- Add one-line invariant comments at the unconditional ED-mesh guard sites
  (IfW, AeroDyn, SubDyn), and note where the else-branch is now unreachable
  in practice because the corresponding case-default block stubs the mesh --
  kept as defense-in-depth for other uncommitted-mesh causes.
- Set CurrentComponent = 'ExternalInflow' at the top of the ExtInfw init
  block so a failure there is attributed to it instead of whatever module's
  label was last set.
- CkIn_OpenReport resets collector%UnYaml to -1 when OpenFOutFile fails, so
  a failed report doesn't leave a stale unit number behind.
- of_checkinput now applies the same TO_NATIVE_PATH/backslash-escaping path
  hygiene the regression() function already uses for its path arguments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the P1-P4 driver pattern into TurbSim.f90: -CheckInput now
dispatches into a real read -> validate -> grid -> profiles -> summary
check instead of exiting via the blanket NormStop before reading
anything. Failures are intercepted at the existing CheckError
chokepoint and attributed to one of five stages (Input, Validation,
Grid, Profiles, Summary); a passing run reports all five and exits 0
with <Root>.verify.yaml written. TS_ValidateInput's non-fatal warnings
are captured into the Validation component instead of being printed
and discarded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Farm_Initialize gains an optional CheckInputCollectorType collector (its
presence is the mode, mirroring FAST_Subs's -CheckInput wiring); the patched
Failed() collects-and-continues per component instead of aborting. Since
FAST.Farm's components are strongly sequential (AWAE feeds WD, WD feeds
turbines) and there's no stub infrastructure for farm-level data, a failed
component marks all downstream components 'skipped' rather than attempting
them against garbage state -- a deliberate deviation from the attempt-
everything default used elsewhere. Farm_InitFAST's existing per-turbine loop
already runs every wrapped turbine before returning, so a single turbine's
init failure still surfaces every other turbine's status, with 'T<n>:'-
prefixed messages identifying which turbine failed.

New Farm_CheckInput driver dispatches from FAST_Farm.f90's -CheckInput flag,
runs Farm_Initialize with the collector, tears down via FARM_End (verified
safe on partially-initialized state across several seeded failure modes),
and exits with a report-derived code via CkIn_DriverFinish.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…itialized on init success

Farm_CheckInput previously called FARM_End (teardown) before finalizing the
-CheckInput report, so a crash in FARM_End on partially-initialized state
would lose an otherwise-completed check's report (yaml stuck on the crashed
placeholder, no console summary). Reordered to mirror FAST_CheckInput_T
(FAST_Subs.f90): write the summary, close the report, capture the exit code,
THEN tear down, THEN exit.

Also audited every IsInitialized = .true. assignment in Farm_Initialize and
its init helpers. Under -CheckInput's collect-and-continue Failed(),
if(Failed()) return no longer returns on failure, so farm%AWAE%IsInitialized
was being set even when AWAE_Init failed -- gated it on success captured
before Failed() can mask it. Also found (and fixed) a related pre-existing
bug in the WakeDynamics and FAST-wrapper per-turbine init loops, where
IsInitialized was set unconditionally before checking that turbine's own
init status; harmless previously since a failure aborted the process before
FARM_End ran, but newly reachable now that Farm_CheckInput always calls
FARM_End regardless of outcome.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the shared P1-P4 driver pattern into HydroDyn_DriverCode.f90 and
SeaState_DriverCode.f90, replacing the blanket NormStop() false-pass with
a real read -> init -> validate sequence attributed to Driver/SeaState/
HydroDyn stages and a <Root>.driver.verify.yaml report.

HydroDyn: two interval-mismatch checks called HD_DvrEnd() directly,
bypassing the CheckError() chokepoint the brief's recon described -- routed
both through CheckError() instead (behaviorally identical for the normal
path) so every fatal gets the CkIn_DriverFail interception. Guarded the
InitOutputFile call (which unconditionally opens <OutRootName>.out) on
CheckInputMode so a passing check run leaves no compute artifact behind.

SeaState: no shared CheckError -- interception added at the top of
SeaSt_DvrCleanup, gated on ErrStat >= AbortErrLev since that routine is
also the unconditional success-teardown path. Guarded WaveElevGrid_Output
(a genuine compute artifact) on CheckInputMode as well.

Verified via 4 smokes (HD/SeaState x positive/seeded-negative) against
reg_tests/r-test cases, and confirmed the normal (non-check) path is
bit-identical to pre-patch output via a stash-and-rebuild diff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the shared P1-P4 driver pattern into AeroDyn_Driver.f90/
AeroDyn_Driver_Subs.f90 and AeroAcoustics_Driver.f90/
AeroAcoustics_Driver_Subs.f90.

AeroDyn: Dvr_SimData/AllData are Registry-generated, so rather than
regenerating the registry, the -CheckInput flag (detected inside
Dvr_Init, where CheckArgs already lives) is threaded to the main
program via a new optional out-argument on Dvr_Init, into a
program-level CheckInputMode in AeroDyn_Driver.f90. Dvr_InitCase gets
the same optional argument (in) to gate its own output-file/VTK-reference
side effects. Under -CheckInput the main program still runs
Dvr_Init + Dvr_InitCase for every case in a combined-case deck (validating
all of them) but never Dvr_TimeStep, attributing each case to
'Case1'..'CaseN' (or 'Case' for a single-case deck) and reporting all of
them once the case loop completes without a fatal.

AeroAcoustics: straight P1-P4 across Driver/AirfoilInfo/AeroAcoustics
stages; Init_AAmodule's internal output-file open is gated the same way
as AeroDyn's.

Verified via 4 AeroDyn smokes (single-case and combined-case x
positive/seeded-negative) against reg_tests/r-test, plus a stash/rebuild
bit-identical diff confirming the normal (non-check) path is unaffected.
No AeroAcoustics r-test case exists; covered instead by a missing-file
run and a hand-built minimal deck seeding a missing airfoil path, both
exercising the full chokepoint/report pipeline -- no positive
all-components-pass smoke was built (see task report for the gap).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the shared P1-P4 driver pattern into MoorDyn_Driver.f90, attributing
checks to four stages: Driver (ReadDriverInputFile), SeaState (optional
SeaSt_Init, gated not_used when no SeaStateInputFile), MoorDyn (MD_Init +
pre-loop compute setup), and Motions (the prescribed-motion input-file
processing block, gated not_used when InputsMod != 1).

Handles the re-fetch quirk: the raw get_command_argument(1, ...) re-fetch
bypasses CheckArgs' parse, so under CheckInputMode it is overridden with the
CheckArgs-parsed MD_InitInp%FileName (one gated assignment) so both
"<file> -checkinput" and "-checkinput <file>" resolve the driver file
correctly; the non-check path is untouched.

AbortIfFailed() already folds ErrStat2/ErrMsg2 into ErrStat/ErrMsg (taking
whichever is more severe) before aborting, so the CkIn_DriverFail
interception is placed right after that merge and reports ErrStat/ErrMsg
directly.

Verified via 3 smokes (SeaState-enabled positive, Motions-enabled positive,
seeded-negative with a garbage line-type name) against reg_tests/r-test
cases, both argument orders under check mode, and confirmed the normal
(non-check) path is bit-identical to pre-patch output via a stash-and-rebuild
diff (only the human-readable timestamp line differs).

Note: MD_Init unconditionally opens <RootName>.MD.out inside MoorDyn.f90 (not
the driver) and writes one t=0 row before the driver reaches P4 -- left
ungated since fixing it is out of this task's driver-only scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the shared P1-P4 driver pattern into the five custom-parser (non-CheckArgs)
drivers: InflowWind, AeroDisk, Simplified-ElastoDyn (SED), SoilDyn, and
OrcaFlexInterface -- each with its own ParseArg CHECKINPUT branch, a CheckInput
logical on the driver Flags type, a default, and a help-text line.

InflowWind: IfWDriver_Flags is Registry-generated, so the field was added to
InflowWind_Driver_Registry.txt and InflowWind_Driver_Types.f90 was regenerated
manually via the openfast_registry binary (git diff: 4 lines, all
CheckInput-related). P4 fires before the WindGrid/Points/FFT compute-setup
block; the HAWC/Bladed/VTK/Uniform format-conversion writes are also gated
under CheckInputMode since they are file-writing side effects independent of
validation.

AeroDisk and SED: the direct -adsk/-sed file mode never populates
CaseTime/CaseData (only the driver-input-file parse path does), so the
time-step-default logic crashes on an unallocated array in that mode -- a
pre-existing bug in both, out of scope to fix. Under -CheckInput this is
intercepted before it's reached, failing the 'Driver' component with a clear
message instead.

SoilDyn: RootName is normally derived only post-init; derived it early
(lexically, from the already-final SlDIptFileName) purely to name the
-CheckInput report. The SlD_Init call site is an inline duplicate of CheckErr
(doesn't call it) -- gave it its own interception, since the REDWIN DLL loads
inside SlD_Init and a missing/broken DLL must surface as a failed component
rather than crash.

OrcaFlexInterface: no shared CheckErr/CheckCallErr chokepoint -- gated each
pre-P4 inline abort site individually, with the critical one at Orca_Init
(where the OrcaFlex DLL dlopens).

Verified via 2 smokes per driver (positive + negative) against reg_tests/r-test
cases where they exist (InflowWind, AeroDisk, SED); SoilDyn and OrcaFlex have no
r-test coverage, so minimal decks were hand-built from each module's file-parser
source. OrcaFlex has no DLL on this machine, so only the negative (DLL-missing)
path is exercisable; documented rather than faked. Re-verified normal
(non-check) runs are unaffected for all four drivers with real decks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
CkIn_CloseReport previously emitted a severe "Report file is not open" for
any collector whose report was never opened, including driver failures that
happen before RootName is known (e.g. AeroDisk/SED direct-file mode under
-CheckInput). That left exit 1 with no verify.yaml at all -- the
machine-readable contract broke exactly when input was most broken.

Now, when the report was never opened, CkIn_CloseReport opens a last-resort
checkinput.verify.yaml in the CWD, backfills every already-collected
component, and falls through to the normal trailer. The already-opened-then-
closed case still gets the severe (that is a caller bug).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Wires the shared P1-P4 driver pattern into Driver_Beam.f90 and
UnsteadyAero_Driver.f90 -- the last two "bare-args" drivers with no prior flag
dispatch at all (raw GET_COMMAND_ARGUMENT / a bare argument-count check), so
both needed a small hand-rolled command-line scan rather than reusing
CheckArgs.

BeamDyn: attributes checks to 'Driver' (BD_ReadDvrFile) and 'BeamDyn' (BD_Init
+ mesh setup, including quasi-static init, which is part of init per the
plan's explicit decision). P4 cuts before the t=0 BD_CalcOutput -- that's
compute, not part of the check, unlike MoorDyn where a t=0 write slipped in.
Gates the output-file open (Dvr_InitializeOutputFile) and both VTK-reference
write blocks under CheckInputMode.

UnsteadyAero: attributes checks to 'Driver' (ReadDriverInputFile +
Dvr_SetParameters), 'LinDyn' (LD_Init, SimMod 3 only; not_used otherwise), and
'UnsteadyAero' (UA_Init including airfoil polars via driverInputsToUAInitData).
Replaces the "count>1 -> help+exit" dispatch, which today makes
"file -checkinput" a false pass (miscounted as too many args, prints help,
exits 0) -- closes that gap while preserving the same help+exit contract for
any other unrecognized flag or a genuine second non-flag argument.
Dvr_InitializeOutputs (the P4 anchor) was checked and does no file I/O, so
nothing needed gating there.

Verified via smokes against reg_tests/r-test cases: BeamDyn positive (both arg
orders) + seeded negative (missing blade file); UnsteadyAero positive for both
SimMod=1 and SimMod=3 (both arg orders) + seeded negative (missing airfoil
file), plus confirmed the old "file -checkinput" false pass is gone while
unrelated unknown-flag/too-many-arg behavior is unchanged. Both drivers'
normal (non-check) runs are bit-identical to pre-patch output via
stash-and-rebuild comparison (BeamDyn .out diff, UnsteadyAero .outb MD5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…odule drivers

Generalizes executeCheckInputTest.py (--input to bypass the *.fst glob for
driver decks named .dvr/.inp/.fstf; --report-glob for <root>.driver.verify.yaml;
--exe-args pass-through) and adds a driver_checkinput() CMake function alongside
of_checkinput() for cases living outside reg_tests/r-test/glue-codes/openfast.

Also fixes a latent bug in the console/YAML parity check surfaced by BeamDyn's
negative case: a message containing a quoted path was unescaped only within its
truncated [:60] slice, which can leave a lone backslash with no matching quote
when the slice boundary falls mid-escape. Now unescapes the full message before
truncating.

New registrations (12, alongside Plan 1's original 5):
- checkinput_turbsim / checkinput_turbsim_bad
- checkinput_fastfarm / checkinput_fastfarm_turbine_error (guarded by
  BUILD_FASTFARM, matching the existing ff_regression() registrations)
- checkinput_addriver / checkinput_addriver_bad
- checkinput_moordyn / checkinput_moordyn_bad
- checkinput_beamdyn_driver / checkinput_beamdyn_driver_bad
- checkinput_inflowwind / checkinput_inflowwind_bad

Each corruption regex was verified by running executeCheckInputTest.py by hand
against the built executable before being wired into CTest.

Not covered by CTest here (smoke-tested only, in their own Plan-2 task
reports): aeroacoustics_driver, seastate_driver, hydrodyn_driver, aerodisk_driver,
sed_driver, soildyn_driver, orca_driver, unsteadyaero_driver. No r-test case
exists for aeroacoustics_driver; the others were left for a future pass to keep
this task's diff bounded to the brief's explicit minimum set -- logged here
rather than silently capped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Document the availability of -CheckInput flag across all user-facing executables
(openfast, FAST.Farm, turbsim, and 11 single-module drivers), report naming
conventions, excluded executables, and DLL-loading caveats.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…oorDyn check before t0 compute

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
…us sweep

A 173-case corpus sweep of `-CheckInput` against reg_tests/r-test/glue-codes/openfast
found two reproducible SIGSEGVs in FAST_InitializeAll's attempt-everything
collect-and-continue path:

1. 5MW_Land_AeroMap: ServoDyn's input file is genuinely absent from this deck dir, so
   SrvD_Init fails and Failed() collects+continues instead of returning. ~80 lines
   later, the "Initialize external inputs for first step" block unconditionally reads
   SrvD%Input(INPUT_CURR,1)%ExternalBlPitchCom -- an allocatable array SrvD_Init never
   reaches allocating -- and segfaults on SIZE() of an unallocated array. Fixed by
   gating that whole block on the array actually being allocated.

2. 5MW_OC3Mnpl_Sld_REDWIN: a four-module failure cascade (ElastoDyn/SeaState/SoilDyn/
   HydroDyn) ending in a segfault during output setup. SoilDyn.f90's SlD_Init has a
   REDWIN-DLL-load call with no accompanying "if (Failed()) return", so when REDWIN
   fails to load, the stale Fatal ErrStat trips the *next*, otherwise-successful
   AllocAry(WriteOutputHdr,...)'s Failed() check, returning before
   AllocAry(WriteOutputUnt,...) runs -- leaving WriteOutputHdr allocated but
   WriteOutputUnt not. FAST_InitOutput derived numOuts(Module_SlD) from WriteOutputHdr
   alone and then indexed WriteOutputUnt(i) too. Fixed by requiring both arrays
   allocated before trusting SoilDyn has any outputs.

Added two CTest negatives (checkinput_aeromap_srvd_missing,
checkinput_redwin_cascade) via of_checkinput() using the decks as-shipped (no
corruption needed -- they fail on their own). Full report:
.superpowers/sdd/sweep-crash-fix-report.md (gitignored, not part of this commit).

Verified: ctest -R checkinput 19/19, ctest -R nwtc_library_utest 1/1 (13/13
sub-tests), happy-path re-check (AOC_WSt, 5MW_Land_DLL_WTurb with the built DISCON
controller, hd_5MW_OC3Spar_DLL_WTurb_WavesIrr hydrodyn driver case) all still PASSED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RaXZQch1V1FSfKxuKSRM1R
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants