feat: Add temporal clustering functionality#1508
Merged
FabianHofmann merged 12 commits intomasterfrom Jan 26, 2026
Merged
Conversation
Implement temporal clustering methods for reducing network time resolution: - resample(): Aggregate snapshots at regular intervals (e.g., "3h", "24h") - downsample(): Select every Nth snapshot as representative - segment(): Use TSAM k-means clustering for variable-duration segments - from_snapshot_map(): Apply pre-computed aggregation mapping Key features: - TemporalClustering dataclass returns both network and snapshot mapping - TemporalClusteringAccessor accessible via n.cluster.temporal.* - Add n.nyears property for total modeled years - Configurable aggregation rules (mean, min, max for storage bounds) - Multi-period support for resample() - Leap day handling (transfer weights to March 1) - tsam optional dependency for segment() Co-Authored-By: Claude <[email protected]>
- Test downsample with multiperiod (NotImplementedError) - Test from_snapshot_map with DataFrame input - Test e_min_pu and e_max_pu aggregation rules - Test get_from_snapshot_map_result accessor - Test segment function with tsam (basic, accessor, errors) - Test segment with invalid num_segments - Test segment with multiperiod - Test segment with no dynamic data
Without overwrite=True, existing time series columns from the copied network are not updated with the resampled/aggregated data, since _import_series_from_df by default skips columns that already exist.
fneum
requested changes
Jan 5, 2026
Member
fneum
left a comment
There was a problem hiding this comment.
Cool! That's very useful! Just details as comments.
Member
|
Took the liberty to update |
Co-authored-by: Fabian Neumann <[email protected]>
- vectorize _build_resample_map and segment snapshot_map for performance - handle downsample edge case when snapshots not divisible by stride - add warning when clustering solved networks - add DatetimeIndex validation in resample - add num_segments upper bound validation in segment - fix docstrings: agglomerative clustering, not inplace, MIP solver - add explanatory comments for contiguous range and year-wise resampling - rename annual_max to normalization_factors - add edge case tests for non-divisible stride and large frequency - update API docs and user guide with temporal clustering - add code example to release notes
- Replace deprecated pd.Index.append() with pd.MultiIndex.from_tuples() - Vectorize snapshot_map assignment using numpy indexing - Add tests for snapshot_map, non-divisible stride, and large stride
Stochastic networks (with scenarios) are not yet supported by temporal clustering. Add explicit check to all four public functions to raise NotImplementedError with clear message.
Contributor
Author
|
@fneum thanks for the review, I (hope I) addressed all of your points. the resample is now much saver (datetime index required, multiperiod support, stoch networks raising error, zero/undivisble arguments tested). have a look again if you want |
fneum
reviewed
Jan 23, 2026
Member
fneum
left a comment
There was a problem hiding this comment.
I am happy with that version! Nice use of np.searchsorted!
fneum
approved these changes
Jan 23, 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.
Changes proposed in this Pull Request
This PR implements temporal clustering functionality for PyPSA, enabling users to reduce the temporal resolution of networks while preserving total modeled hours through snapshot weighting adjustments. This is an upstream implementation based on patterns from PyPSA-EUR.
New Functions in
pypsa.clustering.temporalresample(n, offset)- Aggregate snapshots at regular intervals (e.g., "3h", "6h", "24h")downsample(n, stride)- Select every Nth snapshot as representative with scaled weightssegment(n, num_segments)- Use TSAM k-means clustering for variable-duration segmentsfrom_snapshot_map(n, snapshot_map)- Apply pre-computed temporal aggregation mappingAccessor Pattern
All methods are accessible via
n.cluster.temporal.*:Additional Features
n.nyearsproperty - Returns total modeled years based on snapshot weightingsTemporalClusteringdataclass - Returns both clustered network and snapshot mappinge_max_puand max fore_min_puresample()handles networks with investment periodstsamoptional dependency - Forsegment()functionWeight Preservation Invariant
All methods preserve the invariant:
sum(weights) == total_modeled_hoursChecklist
docs.docs/release-notes.mdof the upcoming release is included.