Skip to content

Commit 60dfdd6

Browse files
authored
Merge branch 'PyPSA:master' into master
2 parents 254f2ea + 62a821f commit 60dfdd6

21 files changed

Lines changed: 14192 additions & 9800 deletions

.github/dependabot.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ updates:
1212
github-actions:
1313
patterns:
1414
- '*'
15+
- package-ecosystem: docker
16+
directories:
17+
- /**/*
18+
schedule:
19+
interval: weekly

.github/workflows/push-images.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ on:
66
- master
77
workflow_dispatch:
88

9+
# retagging must wait for a previous build to finish pushing
10+
concurrency:
11+
group: push-images
12+
913
env:
1014
IMAGE_NAME: pypsa/eur-dev-env
1115

@@ -43,7 +47,7 @@ jobs:
4347
fi
4448
4549
- name: 'Add SHA tag to existing image' # when rebuild is not needed
46-
if: env.hash_last_changed != github.sha
50+
if: env.hash_last_changed != github.sha && github.event_name != 'workflow_dispatch'
4751
run: |-
4852
docker pull ghcr.io/${{ env.IMAGE_NAME }}:${{ env.hash_last_changed }}
4953
docker tag ghcr.io/${{ env.IMAGE_NAME }}:${{ env.hash_last_changed }} ghcr.io/${{ env.IMAGE_NAME }}:${{ github.sha }}

.github/workflows/security-scan.yaml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,34 @@ on:
33
push:
44
branches: [master]
55
paths: [pixi.lock]
6+
# No path filter, a required check must report on every PR
67
pull_request:
78
branches: [master]
8-
paths: [pixi.lock]
99
schedule:
1010
- cron: "0 6 * * *"
1111
workflow_dispatch:
1212

1313
jobs:
14+
changes:
15+
name: Detect pixi.lock changes
16+
if: github.event_name == 'pull_request'
17+
runs-on: ubuntu-latest
18+
outputs:
19+
pixi: ${{ steps.diff.outputs.pixi }}
20+
steps:
21+
- id: diff
22+
env:
23+
GH_TOKEN: ${{ github.token }}
24+
run: |
25+
files=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename')
26+
grep -qxF pixi.lock <<< "$files" && pixi=true || pixi=false
27+
echo "pixi=$pixi" >> "$GITHUB_OUTPUT"
28+
1429
scan:
1530
name: SBOM and vulnerability scan
31+
needs: changes
32+
# Skip only when pixi.lock is confirmed unchanged
33+
if: ${{ !cancelled() && !(needs.changes.result == 'success' && needs.changes.outputs.pixi == 'false') }}
1634
runs-on: ubuntu-latest
1735
permissions:
1836
security-events: write
@@ -56,14 +74,13 @@ jobs:
5674
mv normalized.sarif "${{ steps.scan.outputs.sarif }}"
5775
5876
- name: Upload SARIF
59-
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
77+
if: github.ref == 'refs/heads/master'
6078
uses: github/codeql-action/upload-sarif@v4
6179
with:
6280
sarif_file: ${{ steps.scan.outputs.sarif }}
6381
category: pixi-vuln-scan
6482

65-
# Fork PRs cant upload SARIF (no access to token), so code scannings native diff
66-
# does not apply. This builds our own diff from a master-uploaded SARIF artifact instead.
83+
# Baseline for the PR CVE diff below
6784
- name: Upload SARIF as artifact
6885
if: github.ref == 'refs/heads/master'
6986
uses: actions/upload-artifact@v7
@@ -72,10 +89,9 @@ jobs:
7289
path: ${{ steps.scan.outputs.sarif }}
7390
retention-days: 30
7491

75-
# Fork PRs cant upload SARIF to code scanning (no token write access), so
76-
# diff against the master baseline artifact here instead
77-
- name: Download master baseline (fork PRs only)
78-
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
92+
# All PRs gate on new CVEs vs the master baseline
93+
- name: Download master baseline
94+
if: github.event_name == 'pull_request'
7995
uses: dawidd6/action-download-artifact@v21
8096
with:
8197
workflow: security-scan.yaml
@@ -84,8 +100,8 @@ jobs:
84100
if_no_artifact_found: fail
85101
path: master-baseline
86102

87-
- name: Fail on new CVEs (fork PRs only)
88-
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
103+
- name: Fail on new CVEs
104+
if: github.event_name == 'pull_request'
89105
env:
90106
SARIF_PATH: ${{ steps.scan.outputs.sarif }}
91107
run: |

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ repos:
1616
# Run ruff to lint and format
1717
- repo: https://github.com/astral-sh/ruff-pre-commit
1818
# Ruff version.
19-
rev: v0.15.9
19+
rev: v0.15.20
2020
hooks:
2121
# Run the linter.
2222
- id: ruff
@@ -51,7 +51,7 @@ repos:
5151

5252
# Format Snakemake rule / workflow files
5353
- repo: https://github.com/snakemake/snakefmt
54-
rev: v1.0.0
54+
rev: v2.0.3
5555
hooks:
5656
- id: snakefmt
5757

Snakefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ rule create_scenarios:
248248
"config/create_scenarios.py"
249249

250250

251+
# fmt: off[next]
251252
rule purge:
252253
run:
253254
import builtins
@@ -304,7 +305,7 @@ rule rulegraph:
304305
r"""
305306
# Generate DOT file using nested snakemake with the dumped final config
306307
echo "[Rule rulegraph] Using final config file: {input.config_file}"
307-
snakemake --rulegraph --configfile {input.config_file} --quiet | sed -n "/digraph/,\$p" > {output.dot}
308+
snakemake --rulegraph --configfile {input.config_file} --quiet | sed -n "/digraph/,\$p" >{output.dot}
308309
309310
# Generate visualizations from the DOT file
310311
if [ -s {output.dot} ]; then
@@ -343,7 +344,7 @@ rule filegraph:
343344
r"""
344345
# Generate DOT file using nested snakemake with the dumped final config
345346
echo "[Rule filegraph] Using final config file: {input.config_file}"
346-
snakemake --filegraph all --configfile {input.config_file} --quiet | sed -n "/digraph/,\$p" > {output.dot}
347+
snakemake --filegraph all --configfile {input.config_file} --quiet | sed -n "/digraph/,\$p" >{output.dot}
347348
348349
# Generate visualizations from the DOT file
349350
if [ -s {output.dot} ]; then

config/schema.default.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4379,7 +4379,7 @@
43794379
},
43804380
"bev_energy": {
43814381
"default": 0.05,
4382-
"description": "The average size of battery electric vehicles (BEV) in MWh.",
4382+
"description": "The average available net battery capacity of battery electric vehicles (BEV) in MWh.",
43834383
"type": "number"
43844384
},
43854385
"bev_charge_efficiency": {
@@ -10864,7 +10864,7 @@
1086410864
},
1086510865
"bev_energy": {
1086610866
"default": 0.05,
10867-
"description": "The average size of battery electric vehicles (BEV) in MWh.",
10867+
"description": "The average available net battery capacity of battery electric vehicles (BEV) in MWh.",
1086810868
"type": "number"
1086910869
},
1087010870
"bev_charge_efficiency": {

doc/release_notes.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<!-- Upcoming Release -->
88
<!-- ================= -->
99

10+
* refactor: `prepare_sector_network.py` functions now take `spatial` as an explicit parameter instead of a module-level global.
1011
* fix: update stale contribution docs (linting and formatting ruff)
1112
* feat: data version CSV / YAML file can be specified separately or extended by the user in the `data.version_files` config entry ([#2016](https://github.com/PyPSA/pypsa-eur/issues/2016)).
1213
* Fix: Resolve plotting crashes from missing `tech_colors` entries by adding `heat dsm` color and implementing upfront validation for missing keys in `plot_summary.py` ([#2108](https://github.com/PyPSA/pypsa-eur/issues/2108)).
@@ -55,9 +56,17 @@
5556
* fix: Ensure `inflow_t` is always defined in `attach_hydro`, resolving a pylint use-before-assignment issue ([#2224](https://github.com/PyPSA/pypsa-eur/pull/2224)).
5657
* Add missing regex anchor with `re.fullmatch` to `create_zenodo_deposition_cli` utils script ([#2225](https://github.com/PyPSA/pypsa-eur/pull/2225)).
5758

59+
* doc: Add note about deep-discharge protection for BEV available battery capacity ([#2227](https://github.com/PyPSA/pypsa-eur/pull/2227)).
5860
* doc: Add FAQ section to docs (`faq.md`) with troubleshooting for `SSL: CERTIFICATE_VERIFY_FAILED` during data retrieval behind proxy servers ([#2228](https://github.com/PyPSA/pypsa-eur/pull/2228)).
5961
* Fix Ultranet (TYNDP 254) to end in Philippsburg (#2236)
6062

63+
* chore: Bump the python version to 3.12 ([#2235](https://github.com/PyPSA/pypsa-eur/pull/2235)). This does not increase the real requirement, because 3.12 is already the effective minimum.
64+
65+
* Fix: `build_electricity_demand_base` fetched the non-existent `load.distribution_key.pop` key instead of correct `population`, silently ignoring the configured value ([#2241](https://github.com/PyPSA/pypsa-eur/pull/2241)).
66+
* Fix: `fill_missing_years` applied the backward fill across country boundaries, so countries without any Eurostat value silently inherited the values of the next country ([#2242](https://github.com/PyPSA/pypsa-eur/pull/2242)).
67+
68+
* chore: Drop the `grpcio < 1.78` pin and add a temporary `python < 3.14` pin ([#2244](https://github.com/PyPSA/pypsa-eur/pull/2244)). The upper bound on python will be lifted once [#2245](https://github.com/PyPSA/pypsa-eur/issues/2245) is resolved.
69+
6170
## PyPSA-Eur v2026.02.0 (18th February 2026)
6271

6372
**Features**

doc/supply_demand.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,9 @@ For the electrified land transport, country-specific factors are computed by com
536536

537537
For BEVs the user can define the [storage energy capacity](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L173), [charging power capacity](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L176), and [charging efficiency](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L174).
538538

539-
For BEV, smart charging is an option. A [certain share](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L172) of the BEV fleet can shift their charging time. The BEV state of charge is forced to be higher than a [set percentage](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L163), e.g. 75%, every day at a [specified hour](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L164), e.g., 7 am, to ensure that the batteries are sufficiently charged for peak usage in the morning and they not behave as seasonal storage. They also have the option to participate in vehicle-to-grid (V2G) services to facilitate system operation if that [is enabled](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L179).
539+
For BEV, smart charging is an option. A [certain share](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L172) of the BEV fleet can shift their charging time. The BEV state of charge is forced to be higher than a [set percentage](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L163), e.g. 75%, every day at a [specified hour](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L164), e.g., 7 am, to ensure that the batteries are sufficiently charged for peak usage in the morning and they not behave as seasonal storage.
540+
No lower limit on the state of charge is imposed, but BEV batteries are considered protected from e.g. deep discharge by the on-board battery management system which only makes the usable capacity available to the vehicle, equal to the configured storage energy capacity.
541+
They also have the option to participate in vehicle-to-grid (V2G) services to facilitate system operation if that [is enabled](https://github.com/PyPSA/pypsa-eur-sec/blob/3daff49c9999ba7ca7534df4e587e1d516044fc3/config.default.yaml#L179).
540542

541543
The battery cost of BEV is not included in the model since it is assumed that BEV owners buy them to primarily satisfy their mobility needs.
542544

docker/dev-env/Dockerfile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
#
33
# SPDX-License-Identifier: CC0-1.0
44

5-
FROM ghcr.io/prefix-dev/pixi:0.63.2
5+
FROM ghcr.io/prefix-dev/pixi:0.75.0
66

77
LABEL org.opencontainers.image.source=https://github.com/PyPSA/pypsa-eur
88

9-
RUN pixi self-update
10-
9+
# ca-certificates: pixi >=0.64 uses system CA certs, base image ships none
1110
RUN apt-get update && \
12-
apt-get install -y --no-install-recommends bash git && \
11+
apt-get install -y --no-install-recommends ca-certificates bash git && \
1312
rm -rf /var/lib/apt/lists/*
1413

1514
# Install pixi env at /opt/pixi-env (allow for bind mounts)

0 commit comments

Comments
 (0)