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:
Export — write the FEWS series the model needs into files, in the model’s
input folder, using names the model understands.
Execute — launch the model binary and wait for it to finish.
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.
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:
Form
Use 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.
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.
Begin from the state a previous run saved. Add an exportStateActivity (writes
the stored state out to where the model expects it, via stateExportDir and a
stateSelection relative to T0) and, after the run, an importStateActivity
(reads the model’s resulting end-state back and stores it as a new warm state for
next time). This carries real recent history forward and is what operational
systems run.
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.
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.
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.
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.
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.
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.