PyPSA / PyPSA/pypsa-usa

Scale SLURM memory requests to network size; measure them with a benchmark sweep

Open
#811 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
148
Forks
55
Avg merge
2d 11h
Merged PRs (30d)
18

Description

Problem

SLURM memory requests are not commensurate with the size of the network being built or solved. mem_mb is either a flat constant that ignores every wildcard, or a heuristic keyed off input file bytes with a coefficient nobody has re-fit since v0. Under-requesting is a hard OOM kill; over-requesting costs queue priority and allocation. Right now we do both, in different rules, for no principled reason.

Companion to #808, which covers stale/over-provisioned walltime and the missing benchmark: directives. This one is specifically about memory vs. network size.

Current state on origin/develop

Of ~59 rules:

  • 37 request a flat constant independent of interconnect, clusters, simpl, snapshot resolution, planning horizons, or sector coupling. A usa build at 500 nodes with 8760 hourly snapshots and a texas build at 10 nodes with 24 snapshots ask sbatch for exactly the same memory.
  • 17 scale as input.size // N * attempt * K — the byte size of the input files. For compressed netCDF this is a weak proxy for peak RSS, and the divisors/multipliers (//70000 * 2, //150000 * 1.5, //100000 * 150) are unexplained magic numbers.

Illustrative inconsistencies:

Rule Requests Comment
build_powerplants 30 GB flat measured 2.0 GB peak on CA (#808)
build_fuel_prices 30 GB flat measured 1.9 GB peak on CA (#808)
build_clustered_population_layouts 50 GB flat largest request in the repo
add_sectors 4 GB flat one of the heaviest scripts, one of the smallest requests
build_renewable_profiles ATLITE_NPROCESSES * 5 GB scales with processes, not with cutout extent or region count
solve_network input.size // 100000 * attempt * 150 ≈1.5 MB of RAM per input byte; dominates the whole allocation

Two structural gaps:

  1. No config layer for memory. walltime: lives in config.slurm.yaml and is per-rule overridable (config_provider("walltime", "<rule>")). mem_mb has no equivalent — tuning it for a site means editing workflow/rules/*.smk.
  2. memory(w) in workflow/rules/common.smk:93 is dead code. It is the PyPSA-Eur size-aware estimator (15000 + 195 * clusters, scaled by the Nh/Nseg opts and the number of planning horizons) and it is never called from anywhere in the workflow. Either wire up something like it or delete it.

Proposal

1. Measure first — run a sizing sweep. Nothing here should be guessed. Build and solve a matrix that spans the dimensions we believe drive memory, with benchmark: on every rule (#808 item 1) so each job emits max_rss / max_vms / s:

Axis Points
interconnect texas, western, eastern, usa
clusters small / medium / full (e.g. 10, 60, 500)
temporal 1h, 3h, 24h (and a seg case)
horizons single vs. multi planning horizon
sector E vs. E-G

A fractional design (not the full cross product) is enough: hold everything else fixed while sweeping one axis at a time, plus a couple of corner cases (usa × 500 × 1h × E-G) to check the fit doesn't blow up at the top end.

2. Fit per-rule memory to a structural regressor, not to file bytes. Candidate regressors:

  • build/cluster rules: n_buses, n_generators, number of bus regions, cutout cell count
  • solve_network: LP size ≈ (n_gen + n_lines + n_storage) × n_snapshots × n_horizons; better still, log the actual variable/constraint/nonzero counts the solver reports and fit against those
  • sector rules: the above × number of sector carriers

Target form mem_mb = a + b * regressor, fit from the sweep, with asymmetric headroom — memory overrun kills the job, so ~1.5× on fitted peak RSS, and keep the existing attempt multiplier as retry escalation.

3. Add a mem: block to config.slurm.yaml mirroring walltime:, so rules read config_provider("mem", "<rule>", default=<fitted expression>) and sites can override per rule without touching workflow code.

4. Ship the aggregation script proposed in #808 item 3 so it reports recommended mem_mb alongside walltime from accumulated benchmarks/ TSVs, and re-fitting is a one-command job as the model grows.

Open questions

  • Do we want one fitted expression per rule, or a small number of rule classes (retrieve / build / cluster / solve) with shared coefficients? The latter is far less to maintain and probably accurate enough for everything except solve_network.
  • Sweep cost: a usa × 500 × 1h solve is expensive to run purely for measurement. Do we fit the solve rule from runs we're doing anyway (opportunistic benchmark collection) rather than a dedicated sweep?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read workflow/rules/common.smk:93 and config.slurm.yaml first, then trace the current mem_mb expressions in workflow/rules/*.smk. Use the proposed benchmark sweep and accumulated benchmarks/ TSVs to compare max_rss, max_vms, and runtime against network-size regressors; done means fitted sizing, configurable mem overrides, and aggregation output.

Written by the indexing model from the issue text.

Assessment

Domain
devops, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.