Dataset Processing Flow¶
This page documents the request-resolution and operation-execution flow.
rook.pflow is the processing-flow layer: it classifies request sources,
resolves one request decision, and executes that decision.
The current model is split into three questions:
what kind of request source did we receive?
should the request return existing files or run an operation?
how should the chosen operation open and process its inputs?
At A Glance¶
flowchart LR
Request["Request"] --> Source["Source"]
Source --> Decision["Decision"]
Decision --> Execution["Execution"]
Execution --> Response["Response"]
Request -.-> RequestHint["WPS call<br/>Workflow step"]
Source -.-> SourceHint["Collection<br/>Direct dataset<br/>Workflow files"]
Decision -.-> DecisionHint["Return files<br/>Run operation<br/>Reject request"]
Execution -.-> ExecutionHint["subset<br/>average<br/>regrid<br/>concat"]
class RequestHint,SourceHint,DecisionHint,ExecutionHint note
classDef note fill:#fff7d6,stroke:#c58a00,stroke-dasharray: 4 3,color:#3b2a00
Processing Phases¶
The high-level flow is deliberately small. It shows the stable phases that WPS requests and workflow steps pass through before a response is built.
flowchart TD
Start(["WPS request or workflow step"])
Start --> Normalize["Receive and normalize inputs"]
Normalize --> Source["Identify request source"]
Source --> Context["Resolve request context"]
Context --> Decide["Choose response path"]
Decide --> Original["Return original files"]
Decide --> Prepare["Prepare operation inputs"]
Decide --> Invalid["Reject invalid request"]
Prepare --> FixPolicy["Apply dataset fix policy"]
FixPolicy --> RunOps["Run operation"]
RunOps --> AdaptOperation["Adapt operation result"]
Original --> AdaptOriginal["Adapt original-file result"]
Invalid --> AdaptError["Adapt error"]
AdaptOperation --> End(["WPS/workflow response"])
AdaptOriginal --> End
AdaptError --> End
Source -.-> SourceNotes["Catalog collection<br/>Direct dataset<br/>Workflow files"]
Context -.-> ContextNotes["Project config<br/>Catalog lookup<br/>Dataset source identity"]
Decide -.-> DecisionNotes["Original files<br/>Operation processing<br/>Invalid request"]
FixPolicy -.-> FixNotes["Catalog-backed sources may receive fixes<br/>Direct sources open as-is<br/>Workflow outputs stay direct unless identified"]
RunOps -.-> OperationNotes["subset<br/>average<br/>regrid<br/>concat<br/>weighted average"]
class SourceNotes,ContextNotes,DecisionNotes,FixNotes,OperationNotes note
classDef note fill:#fff7d6,stroke:#c58a00,stroke-dasharray: 4 3,color:#3b2a00
Detailed Decision Rules¶
The detailed flow names the current request sources, decision values, and operation execution paths. It is more specific than the phase diagram, but it should still keep branch rules visible rather than hiding them in broad orchestration terms.
flowchart TD
Start(["Request arrives"])
Start --> WPS["WPS process adapter"]
Start --> Workflow["Workflow step"]
subgraph WorkflowLayer["Workflow operation vocabulary"]
Workflow --> Registry["Look up step run value<br/>in WORKFLOW_OPERATIONS"]
Registry --> PreviousFiles{"Previous step<br/>produced files?"}
PreviousFiles -- yes --> WorkflowFiles["WorkflowFiles"]
PreviousFiles -- no --> WorkflowCollection["Collection input"]
end
subgraph Resolution["Request source and decision"]
WPS --> Collection["Collection input"]
WorkflowCollection --> Collection
Collection --> Project["Resolve project"]
Project --> UsesCatalog{"Project uses<br/>Rook catalog?"}
UsesCatalog -- no --> DirectDataset["DirectDataset"]
UsesCatalog -- yes --> CatalogCollection["CatalogCollection"]
CatalogCollection --> Search["Search catalog<br/>with collection and time"]
Search --> Found{"All requested collections<br/>found?"}
Found -- no --> Invalid["InvalidRequest<br/>or InvalidCollection"]
Found -- yes --> ReturnPolicy{"Can return<br/>existing files?"}
ReturnPolicy -- "original_files<br/>or project policy" --> ReturnOriginal["ReturnOriginalFiles"]
ReturnPolicy -- no --> Processing{"Operation must<br/>write new data?"}
Processing -- yes --> RunResolved["RunOperation<br/>with DatasetSource values"]
Processing -- no --> Aligned{"Subset aligns with<br/>whole source files?"}
Aligned -- yes --> ReturnOriginal
Aligned -- no --> RunResolved
DirectDataset --> RunOriginal["RunOperation<br/>with original collection"]
end
subgraph Execution["Execution and response"]
WorkflowFiles --> WorkflowPrepare["Prepare workflow file inputs<br/>as FileMapper"]
WorkflowPrepare --> OperationRunner["Operation runner"]
RunOriginal --> PrepareOriginal["Clean operation inputs"]
RunResolved --> PrepareResolved["Clean operation inputs<br/>and replace collection"]
PrepareOriginal --> OperationRunner
PrepareResolved --> OperationRunner
OperationRunner --> Consolidate["Consolidate inputs<br/>to DatasetSource"]
Consolidate --> Open["Detect format and transport<br/>NetCDF, Zarr, Kerchunk, file, HTTP, S3"]
Open --> Fixes["Apply dataset fixes<br/>only when dataset id is known"]
Fixes --> Operation["Run subset, average,<br/>regrid, concat, or weighted average"]
ReturnOriginal --> OriginalResponse["Original-file response"]
Operation --> OutputResponse["Operation output files"]
end
Decision Ownership¶
WPS process adapters parse WPS inputs, choose the operation runner, and pass the
request to execute_resolved_request. They do not decide catalog behavior
themselves.
rook.workflow parses workflow documents, resolves dependencies between
steps, and looks up each step’s run value in WORKFLOW_OPERATIONS. Outputs
from previous steps enter the operation adapter as WorkflowFiles and are
prepared as a FileMapper before running the next operation.
rook.pflow.resolver.resolve_request_decision handles catalog-backed
requests. It classifies inputs as CatalogCollection or DirectDataset,
validates catalog search results, and chooses a ReturnOriginalFiles or
RunOperation decision.
rook.pflow.execution.execute_decision adapts the decision into output URIs.
It collects original file URLs when processing is skipped, otherwise it
prepares operation inputs and calls the operation runner.
rook.operations.consolidate converts operation collections into
DatasetSource values. It keeps direct Zarr, Kerchunk, and S3 inputs out of
catalog lookup, resolves catalog-backed NetCDF datasets to files, and preserves
dataset IDs where they are needed for dataset fixes.
rook.io.datasets owns format and transport detection, storage options, and
dataset opening. Catalog-specific fixes are applied only when a DatasetSource
has a dataset ID.
Dataset Fix Policy¶
Dataset fixes are an internal opening policy, not a WPS input switch. Deprecated
apply_fixes inputs are still accepted for compatibility, but they no longer
decide whether fixes run.
The rule is intentionally small:
catalog-resolved sources carry a dataset ID and may receive project-specific fixes while they are opened;
direct local, URL, S3, Zarr, and Kerchunk sources without a dataset ID are opened as-is;
workflow outputs are treated like direct files unless a future workflow model gives them an explicit source identity.
Decadal concat fixes remain an operation-specific rule for now. They are applied inside concat because they prepare multiple forecast files for concatenation, not because the generic dataset opener can infer the whole operation context.
Current Decision Values¶
The resolver returns one explicit decision value that describes what the caller must do next:
reject the request with a known error;
ReturnOriginalFiles;RunOperationwith the original collection;RunOperationwith catalog-resolvedDatasetSourcevalues.
The resolver should keep these responsibilities separate:
input classification: catalog collection versus direct dataset;
project and catalog resolution;
original-file eligibility;
subset-to-file alignment;
construction of operation sources;
WPS response and exception adaptation.
The execution side should be boring on purpose. Given a decision, it should either collect original-file URLs or prepare operation inputs and call the supplied runner. It should not repeat catalog decisions.
The type vocabulary is intentionally small:
RequestDecision = (
InvalidRequest
| ReturnOriginalFiles
| RunOperation
)
The important boundary is that request resolution decides what should happen, while operation execution decides how to run the selected operation.