A workflow is the ordered recipe that drives a whole forecast. Where the
lifecycle describes what happens in sequence — set T0,
import, pre-process, run the model, post-process, check thresholds, export — the
workflow file is that sequence written down. Each step is an <activity> that
runs one module instance; FEWS executes
the activities in the order they appear. The workflow is the script; the
modules are its lines.
A workflow file is a list of activities. Its root is <workflow>, and its
children are the steps — most commonly <activity> elements, run in the order
written.
workflow (version="1.1")
├─ properties? optional $key$ values shared with nested files
└─ (one or more, run in order):
├─ activity ← the common case: one step of the recipe
│ ├─ runIndependent? continue even if this step fails
│ ├─ ONE OF:
│ │ ├─ moduleInstanceId run a module instance
│ │ ├─ workflowId run another (nested) workflow
│ │ └─ predefinedActivity a built-in housekeeping task
│ ├─ ensemble? loop the step over ensemble members
│ ├─ fallbackActivity? run instead if this activity fails
│ └─ description?
├─ parallel run several activities at the same time
└─ sequence group activities so they can share properties
moduleInstanceId names the module instance
to run — here the import whose config file is ImportObservations …. The
version attribute is required on the root (the schema doesn’t fix its value;
1.1 is the usual choice, matching the other config files).
Each <activity> chooses one of three things to do:
Element
The activity…
moduleInstanceId
Runs a single module instance — an import, transformation, general adapter, export, etc. This is the everyday case.
workflowId
Runs another whole workflow in place — the building block for nesting and reuse (see below).
predefinedActivity
Runs a built-in housekeeping task that needs no config file (e.g. threshold event crossing). Rare in a first configuration.
Two more per-activity fields matter early:
runIndependent — true lets the workflow continue with the next activity
even if this one fails. The schema’s default is the opposite: stop the whole
workflow the moment an activity fails. Set it to true on activities whose
failure shouldn’t sink the run (a flaky optional import), and leave it false
where a later step genuinely can’t proceed without this one.
fallbackActivity — an alternative activity to run instead when this one
fails. Optional, and itself a full activity definition.
Most workflows are a short pipeline. This one imports observations, converts
stage to discharge, then exports the result — three module instances in order,
reusing ImportObservations from the earlier guides:
Order is the whole point: StageToDischarge reads what ImportObservations
wrote, and ExportForecast sends on what the transform produced. Put the
transform before the import and it finds no data. The import is
runIndependent (a gap in one gauge shouldn’t stop the run), but the transform
and export are not — if the transform fails there’s nothing worth exporting.
ImportAll and RunModels are themselves workflow files. Nesting keeps each
piece small and lets several parents share a common sub-workflow. The
one-instance-per-workflow rule below still applies across the nesting.
To run a step once per member of an ensemble (say
a 50-member weather forecast), add an <ensemble> block to the activity:
runInLooptrue runs the module once per member; false hands the whole
ensemble to the module in one go. You can narrow the loop to specific members
with ensembleMemberId, ensembleMemberIndex, or an
ensembleMemberIdRegularExpression — see the schema for the full choice.
Make non-critical steps survivable so one hiccup doesn’t abort the run:
Writing the file in WorkflowFiles/ makes it valid — but not yet runnable.
For FEWS to offer a workflow in the Manual Forecast dialog, or to let the system
schedule it, the workflow must be registered in the WorkflowDescriptors file
(Config/RegionConfigFiles/). That file, governed by
workflowDescriptors.xsd,
is the list of workflows the system knows about:
The link between “the file exists” and “the workflow can run”:
id (attribute, required) is the workflow’s identity in the UI and in task
definitions.
workflowFileName points at the workflow file to start (base name). If
omitted it defaults to the id, so a descriptor whose id matches the
file name needs no workflowFileName at all.
visible (default true) controls whether it appears in the dialog;
forecast (default true) marks it as a forecast run.
schedulingAllowed governs whether the system may schedule it (with
defaultSchedulingPeriod, minSchedulingInterval, and friends tuning how).
A module instance may appear only once per workflow. Straight from the
schema: “Each module instance may occur only once in a workflow (including
nested workflows).” Reuse across parents means factoring the shared step into
a sub-workflow, not naming the same moduleInstanceId twice.
Order is execution order. Activities run top to bottom. A transform placed
before the import it depends on simply finds no data — no error, just empty
output. Read the file as a timeline.
runIndependent defaults to stop. Omit it and a failing activity aborts
the whole workflow. That’s often what you want mid-pipeline; add
runIndependenttrue only where a failure is genuinely survivable.
The file exists but no button appears → it isn’t registered. A workflow
that runs fine from a test but never shows up in the UI is almost always
missing (or misspelled in) the WorkflowDescriptors
entry. Check id and workflowFileName.
A change has no effect → a higher-versioned file wins. As with every config
file, check for a sibling like ForecastRiver 1.01 default.xml. See how FEWS
finds a file.
Register, then look for it in the UI. Add the workflowDescriptor, reload
config, and confirm the workflow appears in the Manual Forecast dialog under
its name. If it doesn’t, re-check id, workflowFileName, and visible.
Run it and read the log in order. Each activity logs its start and finish.
Confirm they fire in the sequence you wrote, and that no unexpected activity
was skipped (a skipped step usually means runIndependent swallowed a
failure — look for the warning).
Confirm the data moved through. Browse the series each step should have
written via a filter — the import’s input, the
transform’s output, and (for an export) the file it produced. A missing link
points at either activity order or a module instance that failed.