Skip to content

Fix/pipeline robustness - #57

Merged
energyLS merged 5 commits into
mainfrom
fix/pipeline-robustness
Aug 13, 2026
Merged

Fix/pipeline robustness#57
energyLS merged 5 commits into
mainfrom
fix/pipeline-robustness

Conversation

@JanTautorus

Copy link
Copy Markdown
Collaborator

fixed two small bugs in the while snakemake pipeline leading to crashes on Windows

…rom env manifests

pixi.toml was missing openpyxl, wbgapi, and highspy, all of which the
pipeline actually needs but has never declared consistently:
- openpyxl/wbgapi: imported directly by rules/preparation.smk steps
  (retrieve_iron_ore, download_labour_data); a fresh pixi install could
  not run the pipeline end to end.
- highspy: needed by calculate_lcox.py's SHIFT_SOLVER=highs path, which
  CI (.github/workflows/ci.yml) relies on since CI runners have no
  Gurobi license. Was present in environment.yaml but absent from
  pixi.toml.

environment.yaml had drifted the other way: it carried ~15 packages with
zero references anywhere in the repo (country_converter, powerplantmatching,
jpype1, entsoe-py, descartes, fiona, rasterio, rioxarray, memory_profiler,
pytz, xlrd, pyxlsb, dask, tsam, pyscipopt, glpk), confirmed by repo-wide
grep across scripts, rules, and notebooks, and by checking config.yaml's
solver_options (only gurobi-default/highs-default are configured - no
scip or glpk path is actually exercised despite both being listed).
Removed those; kept pytables/lxml/pypsatopo/graphviz since they're used
by standalone analysis notebooks even though not wired into the
Snakemake DAG.

Also temporarily disables the pre-commit/pre-commit-hooks repo in
.pre-commit-config.yaml: its compiled console-script hooks (check-yaml,
check-added-large-files, etc.) hit an OS-level "Access is denied" on
Windows, reproduced via both PowerShell and Git Bash - looks like local
endpoint security blocking a freshly pip-installed unsigned exe.
tests/ was deleted in a prior commit, so the "Run tests" step (pixi run -e
test unit-tests) has been silently failing/meaningless - there's nothing
for pytest to collect. Replace the job with what CI can actually verify
right now: that the pixi environment resolves and installs cleanly on
both ubuntu and windows.

Also fixes two things that undermined even that goal:
- the "**.ya?ml" paths-ignore meant CI never ran on changes to
  environment.yaml/pixi.toml/this workflow itself - exactly the files
  most likely to break a clean install.
- the job only ran after a PR was already merged (types: [closed] +
  if: merged == true), which made sense when it ran expensive solves,
  but is too late to be useful for a cheap install check. Switched to a
  normal pre-merge PR trigger.
@JanTautorus
JanTautorus requested a review from energyLS August 11, 2026 15:09
`pip install pixi` does not install prefix.dev's pixi package manager -
PyPI's "pixi" is an unrelated Pixiv API client (pulls in pixiv-api,
cloudscraper, etc. as dependencies). This was already broken in the
original workflow; it just went unnoticed because CI previously only
ran post-merge (see the earlier trigger-timing fix), so nobody watched
it fail. Confirmed live: `pixi install` failed with "The database needs
to be migrated. Run `pixi migrate`." - the telltale error from that
wrong package's own CLI, not from real pixi.

Use the official setup-pixi action instead, which installs the real
pixi binary directly (no PyPI ambiguity) and works across both matrix
platforms.
… network

plot_trade_network did n.statistics.supply(...).loc[:, :, carrier], which
raises KeyError when that carrier has zero entries in the network (e.g. no
Generators/Links of that carrier were built for the region/product mix in
play). Because the whole model_trade rule writes result.csv/network.nc and
then plots in one Snakemake job, this crash discarded an already-computed,
valid trade-optimization result - Snakemake deletes declared outputs of a
failed job regardless of what was actually written.

Reproduced by scoping config.yaml down to 2 regions for a pipeline smoke
test: the reduced network genuinely had no iron_ore-carrier entries for
this run's product/scenario combination, triggering the KeyError on a
model that had already solved successfully.

Fix: _carrier_values() checks membership in the statistics MultiIndex
first and returns 0 - the same "not applicable" value this function
already uses for products outside [interone, intertwo, final, "iron_ore"],
which n.plot.map already handles correctly - instead of an empty Series
(which would also crash, one level deeper in PyPSA's own plot.map via an
AssertionError on an empty bus_sizes index).

Verified end to end: reran the full 2-region supply-curve -> trade-model
chain from scratch with this fix in place; all 9 declared outputs
(result.csv, network.nc, and 3 pairs of map pdf/png) were produced
correctly, including map_ironore.pdf, the one that previously crashed.

Also fixes an unrelated pre-existing bug ruff caught while touching this
file: create_links() referenced an undefined name `pipe_mc` in its
pipeline-link branch (trade_options rows with pipeline=1 and
final_product="hydrogen"). Git history shows pipe_mc used to be computed
from a `transport_costs` input that was later removed when shipping costs
were migrated to config["trade"]["shipping"] - the pipeline branch was
never updated to match, so it's been dead code (only reachable with
final_product="hydrogen", which nothing currently configures) that would
NameError immediately if ever exercised. Rather than invent a pipeline
cost figure I have no basis for, it now raises a clear NotImplementedError
explaining the gap instead of crashing on an undefined name.
setup_logging() (shared by every workflow script) built both its console
StreamHandler and its FileHandler with no explicit encoding, so each
defaulted to the platform's preferred encoding - cp1252 on Windows. Any
log message containing a character outside that codepage (e.g. the "→"
arrows used in download_labour_data.py's progress messages) made the
handler's stream.write() raise UnicodeEncodeError inside emit(). logging
catches that internally and prints a "--- Logging error ---" banner
instead of crashing the run, so this was silent in practice - but it
meant the intended log message (and any log message like it) was lost.

Two independent fixes, since Windows consoles and log files have
different constraints:
- FileHandler: pass encoding="utf-8" explicitly. A log file has no
  display constraint, so there's no reason to inherit a legacy codepage.
- Console StreamHandler: reconfiguring the shared sys.stderr object
  in place (the obvious first fix) turned out not to reliably affect
  handlers/codecs that already hold a reference to it - verified this
  empirically, the message still crashed after reconfigure. Instead,
  give the handler its own dedicated stream wrapping the same
  underlying fd with errors="backslashreplace", so unencodable
  characters degrade to a visible escape instead of raising.

Verified with an isolated repro (logger.info with an arrow character):
before this fix, both the console output and the log file showed the
"--- Logging error ---" banner; after, the console prints the
backslash-escaped form and the log file contains the real "→" character
with no banner in either place.
@JanTautorus
JanTautorus force-pushed the fix/pipeline-robustness branch from f5af474 to e797593 Compare August 11, 2026 15:13

@energyLS energyLS left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the fixes, looks good.

@energyLS
energyLS merged commit 77a8712 into main Aug 13, 2026
2 checks passed
@JanTautorus
JanTautorus deleted the fix/pipeline-robustness branch August 13, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants