Add an Exporter#
Exporters convert simulation results into external file formats. Six
exporters ship today under hydromodpy/results/exporters/: CSV,
NetCDF, GeoTIFF, VTU, Shapefile, and the portable .hmp package.
Use a new exporter when an external tool needs a format that none of those covers.
Contract#
Each exporter is a function that reads from the catalog connection plus the per-simulation Zarr / Parquet payloads and writes the chosen format to disk:
from pathlib import Path
import duckdb
def export_<format>(
conn: duckdb.DuckDBPyConnection,
sim_id: str,
output_path: str | Path,
**kwargs,
) -> Path:
"""Export simulation results to <format>."""
# 1. query the catalog for the rows you need
# 2. open the Zarr / Parquet payloads if needed
# 3. write the file
# 4. log the success and return the path
...
Files to create#
A new format called myfmt:
hydromodpy/results/exporters/myfmt.py
Skeleton:
import logging
from pathlib import Path
import duckdb
logger = logging.getLogger(__name__)
def export_myfmt(
conn: duckdb.DuckDBPyConnection,
sim_id: str,
output_path: str | Path,
*,
variable: str | None = None,
) -> Path:
"""Export ``timeseries`` of ``sim_id`` to a .myfmt file."""
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
query = "SELECT * FROM timeseries WHERE sim_id = ?"
params = [sim_id]
if variable is not None:
query += " AND variable = ?"
params.append(variable)
df = conn.execute(query, params).fetchdf()
_write_myfmt(df, output_path)
logger.info("Exported myfmt: %s", output_path)
return output_path
Wire it through SimulationCatalog.export#
The SimulationCatalog.export method dispatches on fmt. Add the
new branch in hydromodpy/results/catalog/reads.py:
if fmt == "myfmt":
from hydromodpy.results.exporters.myfmt import export_myfmt
return export_myfmt(self._db, sid, path, variable=variable, **kwargs)
Wire it through the CLI#
The hmp export command (hydromodpy/cli/commands/export.py)
forwards --myfmt to the exporter. Add an argument and a dispatch
branch:
parser.add_argument("--myfmt", action="store_true",
help="Export to .myfmt (custom format).")
if args.myfmt:
export_myfmt(catalog._conn, sim_id, output_dir / f"{sim_id}.myfmt")
Choose what to read#
Most exporters fall in one of three families:
Tabular: read
timeseries/budgets/mass_balanceParquet views via DuckDB. CSV is the canonical example.Field: read Zarr arrays through
Run.field/Run.fields. NetCDF, GeoTIFF, VTU follow this pattern.Bundle: read both plus the catalog rows.
.hmpis the reference (seehmp_package.py).
Reuse the connection handed by SimulationCatalog rather than
opening a new one. Reuse Run.field rather than reading raw Zarr
keys.
Tests to add#
Unit under
tests/unit/results/exporters/with a tiny fixture catalog (conftest.pyalready shipscatalog_with_data). Assert the file is created and round-trips for the field’s dtype and shape.Integration under
tests/integration/results/for an end-to-endhmp exportinvocation.
Pitfalls flagged by the layer matrix#
resultsmay importcore,schema,config, andresults. The documentedresults -> spatialtolerance is reserved for spatial-index storage. Do not importdisplay,analysis,simulation, orsolverin an exporter.Spatial reprojection helpers live in
core/io/(crs.py,raster_io.py,vector_io.py); reach them fromcore, not fromspatial.Exporters must remain idempotent: re-running on the same path must overwrite cleanly.
See also#
results for the catalog and the
RunAPI.Add a Figure for matplotlib outputs.
Results and Exports for the user-facing export inventory.