Skip to content

Case Conversion (cobre-bridge)

cobre-bridge is a standalone Python package that converts NEWAVE (long-term) and DECOMP (short-term) hydrothermal dispatch cases into Cobre case directories and compares the source model’s published results with Cobre’s simulation output.

The package is maintained in a separate repository: github.com/cobre-rs/cobre-bridge.


Terminal window
uv tool install cobre-bridge # isolated, on-PATH CLI (recommended)
pipx install cobre-bridge # alternative
pip install cobre-bridge # into the current environment

cobre-bridge requires Python 3.12 or newer. The cobre CLI (the solver) is installed separately (see installing cobre) and is needed only to run cobre validate and cobre run on the converted case.

cobre-python is a required runtime dependency, not an optional extra — it provides the Python bindings that convert --validate, dashboard, and convert decomp’s boundary-FCF import need, so installing cobre-bridge pulls it in automatically. (compare does not use the bindings: it reads Cobre’s simulation-output parquet files directly.)

A cobre-bridge release X.Y.Z targets cobre X.Y.Z: the converted case follows that release’s input contract. The bridge records its minimum cobre version (0.18.0) in the conversion manifest and uses it to decide whether the installed cobre-python can run convert --validate. The Dependencies section lists the pin that tracks that minimum. If the installed cobre-python is older than the minimum cobre version, convert --validate skips its validation step (printing a note) instead of failing.


A NEWAVE case goes from preflight check to results comparison in six steps.

Terminal window
cobre-bridge check newave <newave-dir> # preflight check; exit 2 = will not convert
cobre-bridge convert newave <newave-dir> <case-dir> --dry-run # convert in memory, write nothing
cobre-bridge convert newave <newave-dir> <case-dir> --validate # convert, then validate with cobre-python
cobre validate <case-dir> # validate the converted case
cobre run <case-dir> # solve; results go to <case-dir>/output/
cobre-bridge compare newave <newave-dir> <case-dir>/output # published NEWAVE results vs Cobre simulation

For a DECOMP deck, replace newave with decomp in the cobre-bridge commands; Converting a DECOMP Deck covers the boundary-FCF import that convert decomp runs by default.


The convert subcommand reads a source case directory and writes a complete Cobre case directory:

Terminal window
cobre-bridge convert newave /path/to/source/case /path/to/output/case

convert newave flags:

FlagDescription
--validateAfter conversion, validate the output with the cobre package.
--forceOverwrite the destination directory if it already contains files.
--diagnostics-json PATHAlso write the conversion diagnostics (counts + findings) as JSON.
--jsonEmit a single machine-readable JSON verdict to stdout and suppress the human-readable (Rich) output.
--dry-runRun the full conversion in memory and report what would be written, without creating or modifying the destination.
-v, --verbose INTEGERIncrease console log verbosity (-v INFO, -vv DEBUG). Default: 0.
--log-file PATHWrite the full DEBUG log to PATH (the console verbosity is unaffected). Missing parent directories are created; a path that cannot be opened is reported as a --log-file option error and the command exits 2.
--no-colorDisable coloured output (also honoured via the NO_COLOR env var).
--quietSuppress the summary and info notes; warnings/errors still show.

convert decomp (below) accepts this same flag set plus --no-fcf.

CodeMeaning
0Conversion completed.
1Conversion error.
2--validate reported a load failure.

convert decomp uses the same 0/1/2 exit-code set. On every cobre-bridge command, a usage error (an unknown option, a missing argument, or an invalid option value such as an unwritable --log-file path) also exits 2, with a usage message.

With --json, convert (and every other cobre-bridge command) emits one JSON document to stdout instead of the Rich console output, with exactly five top-level keys in this fixed order: schema_version, command, status, summary, diagnostics. summary holds the command-specific payload (entity counts, for convert); diagnostics is the same findings list the Rich panels render.

For the complete flag reference for every command, see the cobre-bridge CLI reference.

Terminal window
cobre-bridge convert decomp /path/to/deck /path/to/output/case

By default, when the deck declares its cortes/cortesh cut files (its FC records), convert decomp imports the deck’s boundary FCF as a terminal-stage cobre policy checkpoint via an in-process, one-iteration cobre pass. This import is slow and requires cobre-python. It is always skipped under --dry-run, and is skipped — with an informational note, not an error — when the deck declares no cut files. Pass --no-fcf to skip the boundary-FCF import for a quicker conversion with no terminal FCF.

The conversion pipeline transforms the source case’s input files into a complete Cobre case directory, organised by output sub-directory:

  • system/ — plant and network configuration: hydros, thermals, buses, lines, non-controllable sources, hydro production models, and hydro geometry.
  • scenarios/ — stochastic inputs: inflow, load, and non-controllable seasonal statistics and factors.
  • constraints/ — per-stage bounds, penalty overrides, and generic constraints.
  • Top level — study configuration, stage definitions, penalties, and initial conditions.

For the authoritative, field-level source-to-Cobre lineage — which deck file, record, and column feeds which output field, and what transformation is applied — see the bridge’s own generated data maps. They are produced by scripts/gen-lineage-docs.py, verified by tests against a real conversion, marked “Do not edit by hand,” and written in Portuguese: newave-data-map.md and decomp-data-map.md.

convert newave writes this file set:

output/
config.json
stages.json
penalties.json
initial_conditions.json
conversion_manifest.json
system/
hydros.json
thermals.json
buses.json
lines.json
non_controllable_sources.json
hydro_production_models.json
hydro_geometry.parquet
tailrace_curves.parquet (conditional)
hydro_energy_productivity.parquet (conditional)
scenarios/
load_factors.json
non_controllable_factors.json
inflow_history.parquet
inflow_seasonal_stats.parquet
load_seasonal_stats.parquet
non_controllable_stats.parquet
constraints/
generic_parameters.json
line_bounds.parquet
hydro_bounds.parquet (conditional)
thermal_bounds.parquet (conditional)
penalty_overrides_bus.parquet (conditional)
penalty_overrides_hydro.parquet (conditional)
generic_constraints.json (conditional: deck has generic constraints)
generic_constraint_bounds.parquet (conditional: deck has generic constraints)

Files marked (conditional) are written only when the source deck supplies the corresponding data; --dry-run writes nothing at all (the conversion report’s would_write_paths previews the set a real run would write).

A case written by convert decomp does not write scenarios/inflow_history.parquet, constraints/penalty_overrides_bus.parquet or constraints/penalty_overrides_hydro.parquet, and it also writes files that convert newave never writes:

  • system/energy_contracts.json and system/pumping_stations.json
  • scenarios/external_inflow_scenarios.parquet, scenarios/external_load_scenarios.parquet and scenarios/external_ncs_scenarios.parquet
  • constraints/contract_bounds.parquet, constraints/hydro_unit_group_bounds.parquet and constraints/pumping_bounds.parquet
  • post_study_stages.json at the top level
  • the imported boundary under boundary/ (see Converting a DECOMP Deck)

After running both the source tool and Cobre on the same case, the compare subcommand aligns the source tool’s published results against Cobre’s simulation-output parquets:

Terminal window
cobre-bridge compare newave /path/to/source/case /path/to/cobre/output

compare newave <NEWAVE_DIR> <COBRE_OUTPUT_DIR> compares NEWAVE’s published results (MEDIAS-*.CSV, pmo.dat) against Cobre simulation output. compare decomp <DECOMP_DIR> <COBRE_OUTPUT_DIR> compares a DECOMP run’s published operation (dec_oper_*.csv) the same way. Both source directories must hold the case/deck and its result files directly (not nested in a subdirectory).

FlagDescription
--tolerance FLOATRelative tolerance for the results comparison. Default 1e-2. Env COBRE_BRIDGE_RESULTS_TOLERANCE.
--format FORMATOutput format(s): console,html,csv,parquet,json,all (comma-separated and/or repeatable). Default console,parquet,json. Env COBRE_BRIDGE_FORMAT.
--out-dir PATHDirectory for file artifacts. Default <cobre_output_dir>/comparison_artifacts. Env COBRE_BRIDGE_OUT_DIR.
--jsonEmit a single machine-readable JSON verdict to stdout instead of the Rich tables.

compare also shares convert’s -v/--verbose, --log-file, --no-color, and --quiet flags.

--tolerance, --format and --out-dir each take their value from the first source that sets it: the flag, then the environment variable, then the config file, then the built-in default. compare reads one config file, the first of these that exists: cobre-bridge.toml in the current directory or any parent; $XDG_CONFIG_HOME/cobre-bridge/config.toml, only when XDG_CONFIG_HOME is set and non-empty; ~/.config/cobre-bridge/config.toml. It is not merged with the others. It can set [compare.results] tolerance, [compare] format and [compare] out_dir; a malformed file, or a key of the wrong type, is ignored with a warning on stderr.

compare is informational: it reports every divergence it finds, beyond tolerance or not, but a divergence never fails the run. On both the newave and decomp tracks the exit code reports only whether the comparison could run:

Exit codeMeaning
0The comparison ran (whether or not any divergence was found).
1compare newave only: the NEWAVE case directory is missing. (On compare decomp a missing deck directory exits 2.)
2An invalid --format token, or a source deck or Cobre output file that cannot be read (missing or malformed data).

A NEWAVE case whose dger.dat requests training without a final simulation (tipo_execucao 1 with tipo_simulacao_final 0) converts with simulation.enabled set to false. cobre run then writes no simulation output, so compare newave finds no Cobre simulation output to read and exits 2, naming the missing simulation/hydro_bus_generation/ directory.


For programmatic use, install cobre-bridge into your project environment (pip install cobre-bridge; an isolated uv tool or pipx install exposes only the CLI), then import the conversion pipeline directly:

from pathlib import Path
from cobre_bridge.newave.pipeline import convert_newave_case
report = convert_newave_case(
Path("/path/to/source/case"),
Path("/path/to/output/case"),
on_phase=lambda phase: print(f"phase: {phase}"),
dry_run=False,
)
print(report)

convert_newave_case(src, dst, *, on_phase=None, dry_run=False) -> ConversionReport takes src/dst as positional Paths, plus two keyword-only arguments: an optional on_phase progress callback invoked with the current conversion phase’s name, and dry_run (default False) — set it to True to run the full conversion in memory and populate the report without writing anything to dst.

The returned ConversionReport carries hydro_count, thermal_count, bus_count, line_count, stage_count, diagnostics (structured findings), warnings (flat warning strings), and would_write_paths (the output paths written, or that would have been written under dry_run=True).

DECOMP conversions use the equivalent cobre_bridge.decomp.pipeline.convert_decomp_case(src, dst, *, force=False, on_phase=None, dry_run=False, fcf_inputs_out=None) -> ConversionReport.


check validates a source case or deck without converting or writing any output — a preflight check before committing to a full convert run:

Terminal window
cobre-bridge check newave /path/to/source/case
cobre-bridge check decomp /path/to/deck

check decomp also reports what the conversion will leave behind (deck features convert decomp does not carry over), so a limitation is never a silent omission.

check shares convert’s --json, -v/--verbose, --log-file, --no-color, and --quiet flags.

CodeMeaning
0Ready to convert.
1Ready, with warnings.
2Will not convert.

dashboard builds an interactive HTML dashboard from Cobre simulation results:

Terminal window
cobre-bridge dashboard /path/to/cobre/case
FlagDescription
--output, -o PATHOutput HTML file path. Default <case_dir>/dashboard.html.
--openOpen the generated dashboard in the default web browser after writing.
--jsonEmit a single machine-readable JSON verdict to stdout.

dashboard also shares convert’s -v/--verbose, --log-file, --no-color, and --quiet flags. Exit codes: 0 written, 1 error.


NEWAVE identifies subsystems and plants by arbitrary 1-based codes; Cobre uses 0-based ids with no gaps. cobre-bridge sorts the source codes in ascending order and assigns the ids 0, 1, 2 and so on in that order, so an id does not depend on how the deck orders its entities, and an entity keeps the same id in every output file. compare newave rebuilds the same numbering from the source case, so results trace back to the source codes. convert decomp sorts the deck’s codes the same way and adds one converter-created transshipment bus after the declared subsystems.

In a NEWAVE case, a hydro becomes a Cobre entity when confhd.dat marks it in service: operating (EX) or operating with an expansion still to come (EE). An EE plant operates from the first stage at the machine configuration modif.dat declares for the study start, and gains the machines listed in exph.dat as they enter service, through per-stage bounds. Plants that do not exist yet are left out, except a future plant whose exph.dat schedule fills its dead volume. Fictitious accounting plants are removed; they are identified structurally, as a plant with zero productivity that shares its inflow gauge with a generating plant, and a cascade link through a removed plant is rewired to the next real plant downstream.

convert decomp converts the deck’s operated hydros, with the deck’s registry overrides applied, and its small plants as must-run non-controllable sources.

For the full list of conversion rules, see the cobre-bridge NEWAVE track guide.

When the source case configures risk-averse optimization (CVaR), cobre-bridge converts the alpha and lambda parameters to per-stage risk_measure entries in stages.json. Three modes are supported:

  • Disabled — all stages use "expectation".
  • Constant — all stages use the same CVaR parameters.
  • Temporal — per-stage alpha/lambda values, with fallback to constants when a stage override is zero.

Three families of source constraints are converted into Cobre’s generic-constraint authoring format: one definition per constraint in generic_constraints.json (a name, an expression, and an optional slack), merged with sequential integer IDs, and per-stage right-hand-side bounds in generic_constraint_bounds.parquet.

  • VminOP — minimum stored-energy constraints. Each is a weighted sum of the participating reservoirs’ end-of-stage storage, where the weight is that reservoir’s accumulated productivity, referenced as a named parameter (@rho_acum_h<id>) declared in constraints/generic_parameters.json and resolved by Cobre to its own ρ at solve time. This keeps the constraint’s coefficients aligned with the study’s productivities rather than freezing a converted value.
  • Electric — operational constraints on hydro generation and line flows.
  • AGRINT — group dispatch constraints for thermal and hydro plants.

cobre-bridge 0.18.0 installs its runtime dependencies with the package. Two pins matter:

  • cobre-python==0.18.0: the pinned version is the bridge’s MIN_COBRE_VERSION (0.18.0), and a packaging test keeps the pin and that constant in lockstep.
  • inewave>=1.16.1: the NEWAVE file reader.

The minimum cobre version does not make versions interchangeable: an imported boundary needs an exact version match (see the caution under Converting a DECOMP Deck).