Split openTEPES_OutputResults into new modules#128
Merged
Conversation
The output-writing code lived in one 2827-line file. This splits it into eight files grouped by topic (investment, generation, storage, sector coupling, network, economic, summary, and a raw parameter dump), with the shared plot helpers in a Common module. The old file becomes a thin facade that re-exports every public name, so existing imports keep working. Function bodies are unchanged. Run order still comes from OUTPUT_REGISTRY in openTEPES.py and is untouched. Verified on case 9n with PYTHONHASHSEED=0: all 87 result tables are bit-identical to the previous code; total cost 164.382043867 MEUR.
The three network maps each built a flow series and each picked the same snapshot period and scenario with identical code. Those two helpers move to a new openTEPES_OutputResultsMapCommon module and are called from one place. The per-sector map-frame builder stays in each module because its body genuinely differs (the electric map scales by 1e3 and adds a voltage-to-width and an overload colour step the others do not have). Output is unchanged. Verified on case 9n with PYTHONHASHSEED=0: all 87 result tables stay bit-identical to the previous code; total cost 164.382043867 MEUR.
Removes openTEPES_OutputResults.py. The package __init__ now imports each openTEPES_OutputResults<Concern> module directly, and openTEPES.py imports the result functions from those modules — matching how the Input and ProblemSolving splits are already wired, so there is no leftover re-export shim. Output is unchanged. Verified on case 9n with PYTHONHASHSEED=0: all 87 result tables stay bit-identical; total cost 164.382043867 MEUR.
- Wrap each module's cross-module import in the same try/except ImportError
guard the Input modules use, so they also resolve when a script is run
directly (e.g. VS Code) with no parent package.
- Bump the header date to June 03, 2026 to match the rest of the package.
- Remove no-op "".join([f"..."]) wrappers (34 of them); each is just the
f-string and only added a list allocation per element in hot loops.
- Stop mutating mTEPES.pMaxStorage inside ESSOperationResults; compute the
inventory-utilization denominator inline instead, so the scaled value no
longer leaks to later reads of the parameter.
No behaviour change. Verified on case 9n with PYTHONHASHSEED=0: all 87 result
tables stay bit-identical; total cost 164.382043867 MEUR.
Two operating-reserve guards in the marginal and economic results looped over storage units (es) but checked pIndOperReserve*[nr] — nr only existed as a leftover from an earlier for-loop. Use es, matching the correct sibling guards. On case 9n the result is unchanged; on cases where the leftover unit's reserve flags differ from the storage units' this corrects which operating-reserve-revenue tables are written.
Per Andres' review on PR #128: openTEPES_OutputResultsSectorCoupling.py bundled two unrelated carriers. Move each writer into its own module -- openTEPES_OutputResultsHydrogen.py (NetworkH2OperationResults) and openTEPES_OutputResultsHeat.py (NetworkHeatOperationResults). Pure move: both function bodies are unchanged. Import sites in openTEPES.py and __init__.py and the CHANGELOG are updated; OUTPUT_REGISTRY dispatch is unchanged. Verified bit-identical output (PYTHONHASHSEED=0) on 9n, 9n_heat, and a new 9n_H2 case, before vs after the split.
Parametrise the single-stage solve test with the new 9n_H2 case so CI exercises the hydrogen result writer on the minimal 9-node system, the counterpart of 9n_heat. Expected cost 259.547153 under the 7-day HiGHS fixture; confirmed deterministic across repeated solves.
Add a CHANGELOG entry for the 9n_H2 case and its CI coverage. Redraw the Layer 6 (Results) block of the architecture diagram to show the real implemented concern modules (including the new Hydrogen and Heat modules) in green, and re-render the PNG.
arght
approved these changes
Jun 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reason
The output code was one large file. Grouping it by topic makes it easier to read and maintain, and lines it up with the rest of the layered restructure.
Changes
Splits the ~2800-line
openTEPES_OutputResults.pyinto new modules at the package root — the output-side counterpart of the earlieropenTEPES_Input*andopenTEPES_ProblemSolving*splits.New modules:
...Investment,...Generation,...Storage,...SectorCoupling(hydrogen + heat networks),...Network(electricity network + map),...Economic(marginal / cost / economic),...Summary(summary / flexibility / reliability), and...RawDump(the raw parameter/variable/constraint DuckDB dump). Shared pieces live in...Common(output-directory helper + the three Altair plot builders) and...MapCommon(the flow-series and snapshot helpers the three network maps had each copied).The old
openTEPES_OutputResults.pyis removed:__init__.pyandopenTEPES.pyimport the result functions from the concern modules directly, the same way the Input and ProblemSolving splits are wired. Dispatch order is still controlled byOUTPUT_REGISTRYinopenTEPES.pyand is unchanged. Cross-module imports use the sametry/except ImportErrorrelative-import guard as the other split modules, so "Run Python File" works.Additional changes not planned
"".join([f"..."])wrappers (each is just the f-string), and stoppedESSOperationResultswriting its inventory-utilisation scaling back intomTEPES.pMaxStorage(computed locally instead).esbut checkedpIndOperReserve*[nr]—nrwas a leftover from an earlier loop. They now usees, matching the two sibling guards that were already correct (a third readGen ... or ... Genwhere siblings readGen ... or ... Con; the second is nowCon). Unchanged on 9n; corrected for cases where the leftover unit's reserve flags differ from the storage units'.