Skip to content

Run a model (General Adapter)

Most real models — HBV, Sacramento, a hydraulic engine, a rainfall-runoff binary — are external programs that know nothing about FEWS. The General Adapter is the module that lets FEWS drive one anyway. It sits at hop ④ (process), the same processing slot as a transformation, but instead of computing a new series itself it hands data out to a model, runs it, and reads the results back in. It’s the config file that turns “run the model” in a workflow into an actual subprocess call.

A model can’t read the FEWS data store, and FEWS can’t read the model’s memory. The General Adapter bridges that gap with a fixed three-beat cycle:

  1. Export — write the FEWS series the model needs into files, in the model’s input folder, using names the model understands.
  2. Execute — launch the model binary and wait for it to finish.
  3. Import — read the files the model wrote back into FEWS as brand-new internal series.

Everything else in the schema is refinement around those three beats. FEWS calls each beat a group of activities, and they run top-to-bottom in exactly that order.

The root element is generalAdapterRun: a general block of paths and settings, then an activities block holding the ordered activity groups.

generalAdapterRun
├─ general ← paths + shared settings for the whole run
│ ├─ rootDir, workDir (where the model lives / scratch space)
│ ├─ exportDir, importDir (the swap folders FEWS ⇄ model)
│ ├─ dumpFileDir, dumpDir (what to save if the run crashes)
│ ├─ diagnosticFile (the model's log, read back by FEWS)
│ └─ exportIdMap / importIdMap (optional name translation at the boundary)
├─ burnInProfile(s) ← optional, for cold-state starts
└─ activities
├─ startUpActivities (purge/makeDir/unzip — clean the slate)
├─ exportActivities (write FEWS series → model input files)
├─ waitActivities (optional: wait for a file, for parallel runs)
├─ executeActivities (run the model binary or a Java adapter)
├─ importActivities (read model output files → new FEWS series)
└─ shutDownActivities (zip/purge — tidy up)

Every group is optional (minOccurs="0"), but a useful run always has at least exportActivities, executeActivities, and importActivities — the three beats. The mental split mirrors the import module: the exportActivities and importActivities speak in FEWS timeSeriesSets, while the model only ever sees files in exportDir / importDir.

A rainfall-runoff model that takes observed discharge as input and produces a simulated discharge series. Input is Q.obs at every gauge in AllDischargeGauges; output is Q.sim at the same locations.

Config/ModuleConfigFiles/RainfallRunoffModel 1.00 default.xml
<?xml version="1.0" encoding="UTF-8"?>
<generalAdapterRun xmlns="http://www.wldelft.nl/fews">
<general>
<rootDir>%REGION_HOME%/Modules/rrmodel</rootDir>
<workDir>%ROOT_DIR%/work</workDir>
<exportDir>%ROOT_DIR%/input</exportDir>
<importDir>%ROOT_DIR%/output</importDir>
<dumpFileDir>%ROOT_DIR%/dump</dumpFileDir>
<dumpDir>%ROOT_DIR%</dumpDir>
<diagnosticFile>%ROOT_DIR%/output/diagnostics.xml</diagnosticFile>
<missVal>-999</missVal>
</general>
<activities>
<!-- 1. EXPORT: write the FEWS input series to a model input file -->
<exportActivities>
<exportTimeSeriesActivity>
<exportFile>discharge_in.xml</exportFile>
<timeSeriesSets>
<timeSeriesSet>
<moduleInstanceId>ImportObservations</moduleInstanceId>
<valueType>scalar</valueType>
<parameterId>Q.obs</parameterId>
<locationSetId>AllDischargeGauges</locationSetId>
<timeSeriesType>external historical</timeSeriesType>
<timeStep unit="hour" multiplier="1"/>
<readWriteMode>read only</readWriteMode>
</timeSeriesSet>
</timeSeriesSets>
</exportTimeSeriesActivity>
</exportActivities>
<!-- 2. EXECUTE: run the model binary -->
<executeActivities>
<executeActivity>
<command>
<executable>%ROOT_DIR%/bin/rrmodel.exe</executable>
</command>
<arguments>
<argument>%ROOT_DIR%</argument>
</arguments>
<timeOut>600000</timeOut>
</executeActivity>
</executeActivities>
<!-- 3. IMPORT: read the model's output file back as a new FEWS series -->
<importActivities>
<importTimeSeriesActivity>
<importFile>discharge_out.xml</importFile>
<timeSeriesSets>
<timeSeriesSet>
<moduleInstanceId>RainfallRunoffModel</moduleInstanceId>
<valueType>scalar</valueType>
<parameterId>Q.sim</parameterId>
<locationSetId>AllDischargeGauges</locationSetId>
<timeSeriesType>simulated forecasting</timeSeriesType>
<timeStep unit="hour" multiplier="1"/>
<readWriteMode>add originals</readWriteMode>
</timeSeriesSet>
</timeSeriesSets>
</importTimeSeriesActivity>
</importActivities>
</activities>
</generalAdapterRun>

An exportTimeSeriesActivity names an exportFile (always written into exportDir) and one or more timeSeriesSets describing which internal series to pull from the data store. This is the read side, so the sets use the header of data that already exists — here ImportObservations / Q.obs, the series the import guide produced. FEWS looks that series up, extracts the run period, and serialises it to discharge_in.xml in the PI-XML format the model’s adapter expects.

If the model uses different names than FEWS, set exportIdMap in <general>; FEWS translates internal → external on the way out, exactly like an export. Different units? exportUnitConversionsId.

executeActivity is the subprocess. Its command is a <choice> — pick one:

FormUse when
<executable>You’re launching a native binary (.exe, a shell script, a Fortran/C model).
<className> (+ optional <binDir>)You’re calling a Java model adapter — the pre/post-adapter pattern that converts PI-XML ⇆ the model’s native format.

arguments (a list of <argument> elements) are passed on the command line; timeOut (milliseconds, required) is the kill-switch if the model hangs. FEWS waits for the process to exit before moving to the import beat.

Import: model output files → new FEWS series

Section titled “Import: model output files → new FEWS series”

An importTimeSeriesActivity mirrors the export: an importFile (always read from importDir) and timeSeriesSets describing the new internal series to create from it. This is the write side. Here the set uses a fresh header — moduleInstanceId is RainfallRunoffModel (the model, not the import) and timeSeriesType is simulated forecasting, because this is model output running into the future. That header is how everything downstream — a display, a threshold check, an export — will find the model result.

A model like a rainfall-runoff engine doesn’t start from nothing — it needs an initial state (soil moisture, storages, reservoir levels) to begin the run. The General Adapter handles state with two dedicated activities, and which one you use is the cold-start vs warm-start choice made concrete:

Begin from a fixed, pre-defined state. FEWS ships the cold state to the model’s input folder as part of the export beat. The optional burnInProfile blocks at the top of generalAdapterRun exist specifically to smooth over cold starts by defining a burn-in period. Simple and reproducible, but the state may not reflect recent conditions — the usual choice while developing and testing.

The state activities live in the same exportActivities / importActivities groups as the time-series ones and run in the same order — state out with the inputs, state back in with the outputs.

  • Pre-/post-adapter sandwich. Three executeActivity blocks (convert in, run, convert out) is the norm for any model that doesn’t natively speak PI-XML. See the tip above.
  • Placeholder input files. Set exportPlaceholderFile to true on an export activity to write the file’s headers only, with no data — a way to tell the model which series it should expect to fill in.
  • Boundary translation. Reach for exportIdMap / importIdMap and the …UnitConversionsId fields when the model’s vocabulary differs from FEWS’s — same two-way ID mapping idea as import/export.
  • Loud model logs. Point diagnosticFile at the log the model (or its adapter) writes; FEWS reads it back and surfaces the model’s own warnings and errors in the FEWS log, so a model failure isn’t a black box.
  • Warm-state loop. Pair exportStateActivity + importStateActivity so each run both consumes and produces a state — the backbone of an operational forecast chain.

general (paths and shared settings)

FieldMeaning
rootDirRoot of the model instance; anchors %ROOT_DIR% in the paths below.
workDirScratch/working directory (often %TEMP_DIR%).
exportDirFolder FEWS writes model inputs into.
importDirFolder FEWS reads model outputs from.
dumpFileDir, dumpDirWhere to save a binary dump if the run fails, for debugging.
diagnosticFileThe model/adapter log FEWS reads back (always in importDir).
exportIdMap / importIdMapOptional internal ⇄ external name translation at the model boundary.
exportUnitConversionsId / importUnitConversionsIdOptional unit conversion at the boundary.
missValMissing-value marker written to/read from model files (default -999).
piVersionPI-XML format version FEWS writes (default 1.2).

activities (the ordered groups)

GroupWhat it does
startUpActivitiespurgeActivity, makeDir, unzipActivity — prepare a clean folder.
exportActivitiesWrite FEWS data out — exportTimeSeriesActivity, exportStateActivity, and many more.
waitActivitieswaitForFileCreation — wait for a file before executing (parallel runs).
executeActivitiesexecuteActivity — run the binary/adapter; attribute executeImportActivitiesOnError.
importActivitiesRead model results in — importTimeSeriesActivity, importStateActivity, importNetcdfActivity, …
shutDownActivitieszipActivity, purgeActivity — tidy up afterward.

executeActivity (the model call)

FieldMeaning
commandexecutable | classNameThe binary to run, or a Java adapter class.
argumentsargumentCommand-line arguments (repeatable).
timeOutMilliseconds before FEWS kills a hung run (required).
ignoreExitCodeDon’t fail on a non-zero exit code (useful when the log is more descriptive).
environmentVariablesEnv vars passed to the process.
  • Model runs but imports nothing → the output file or its header didn’t match. The importTimeSeriesActivity’s timeSeriesSet must describe exactly what the model wrote — right parameterId, locationSetId, timeStep, and timeSeriesType. If any field is off, FEWS reads the file but stores an empty series. Check the file in importDir by hand, then line the set up against it.
  • Export writes an empty input file → the source series doesn’t exist yet. The export set reads from the data store; if the import that fills Q.obs hasn’t run earlier in the workflow, or its header doesn’t match your export set, there’s nothing to export and the model gets empty inputs. Order matters — import before model in the workflow.
  • Names don’t line up at the boundary → wrong or missing id map. The model writes files under its names; FEWS reads under yours. Without an importIdMap, an external location the model calls STN01 won’t resolve to RiverGauge_01. Silent, exactly like ID mapping drops on import.
  • The model log is your first stop. A non-zero exit code or errors in diagnosticFile mean the model itself failed — read that log before touching the FEWS config. When the model reports errors via its log rather than its exit code, ignoreExitCode lets the post-adapter still run.
  • The run hangs → timeOut too short (or the model is waiting on input). FEWS kills the process at timeOut milliseconds. A model that blocks on missing input, or one that legitimately takes longer than the limit, both surface here.
  • readWriteMode won’t rescue a mismatch. As on import, readWriteMode is honoured only by the Time Series Display; it doesn’t change how the General Adapter reads or writes series. Fix the header, not the mode.
  • A change has no effect → a higher-versioned file wins. Check for a sibling like RainfallRunoffModel 1.01 default.xml. See how FEWS finds a file.
  1. Run the module inside a workflow (model after the import that supplies its inputs) and read the FEWS log. The General Adapter reports each activity — export, execute, import — and echoes the model’s own diagnosticFile messages.
  2. Peek at the swap folders. Confirm a non-empty input file landed in exportDir before the run, and a non-empty output file in importDir after. Empty files pinpoint which beat failed.
  3. Browse the result. Add a filter whose timeSeriesSet matches the import header (RainfallRunoffModel / Q.sim / simulated forecasting) and confirm the simulated series appears, running past T0 into the future.
  4. Sanity-check the state. For a warm-start chain, confirm a new state was written (the importStateActivity) so the next run has something to start from.