diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 22ef5de29..84fbcad19 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -11,6 +11,7 @@ on: paths: - 'docs/**' - 'mkdocs.yml' + - 'flixopt/**' # notebooks import flixopt; catch library changes that break docs workflow_dispatch: inputs: deploy: diff --git a/docs/notebooks/data/generate_realistic_profiles.py b/docs/notebooks/data/generate_realistic_profiles.py index c7577e6fd..c25600f1a 100644 --- a/docs/notebooks/data/generate_realistic_profiles.py +++ b/docs/notebooks/data/generate_realistic_profiles.py @@ -166,8 +166,12 @@ def generate( Electricity demand profile in kW """ slp_type = self.CONSUMER_TYPES[consumer_type] - e_slp = bdew.ElecSlp(self.year, holidays=self.holidays) - profile = e_slp.get_scaled_power_profiles({slp_type: annual_demand_kwh}) + # demandlib calls warnings.simplefilter("error") internally, which would otherwise + # leak into the global state and turn every later warning (e.g. third-party + # DeprecationWarnings) into a hard error. Contain that side effect here. + with warnings.catch_warnings(): + e_slp = bdew.ElecSlp(self.year, holidays=self.holidays) + profile = e_slp.get_scaled_power_profiles({slp_type: annual_demand_kwh}) # Resample to hourly and align with requested timesteps profile_hourly = profile[slp_type].resample('h').mean() return profile_hourly.reindex(timesteps, method='ffill').values diff --git a/flixopt/transform_accessor.py b/flixopt/transform_accessor.py index f7a3698cc..78742c273 100644 --- a/flixopt/transform_accessor.py +++ b/flixopt/transform_accessor.py @@ -900,8 +900,12 @@ def _build_cluster_config_with_weights( cluster: ClusterConfig | None, auto_weights: dict[str, float], available_columns: set[str] | None = None, - ) -> ClusterConfig: - """Merge auto-calculated weights into ClusterConfig. + ) -> tuple[ClusterConfig, dict[str, float]]: + """Resolve clustering weights and build a ClusterConfig without weights. + + Weights are returned separately so they can be passed as a top-level + argument to ``tsam.aggregate``. Passing weights via ``ClusterConfig`` is + deprecated in tsam (>= 3.4.0) and raises a ``DeprecationWarning``. Args: cluster: Optional user-provided ClusterConfig. @@ -912,7 +916,7 @@ def _build_cluster_config_with_weights( (e.g., constant arrays removed before clustering). Returns: - ClusterConfig with weights set (either user-provided or auto-calculated). + Tuple of (ClusterConfig without weights, resolved weights dict). """ from tsam import ClusterConfig @@ -926,19 +930,21 @@ def _build_cluster_config_with_weights( if available_columns is not None: weights = {name: w for name, w in weights.items() if name in available_columns} - # No ClusterConfig provided - use defaults with weights + # No ClusterConfig provided - use defaults (weights passed to aggregate()) if cluster is None: - return ClusterConfig(weights=weights) - - # ClusterConfig provided - use its settings with (possibly filtered) weights - return ClusterConfig( - method=cluster.method, - representation=cluster.representation, - weights=weights, - normalize_column_means=cluster.normalize_column_means, - use_duration_curves=cluster.use_duration_curves, - include_period_sums=cluster.include_period_sums, - solver=cluster.solver, + return ClusterConfig(), weights + + # ClusterConfig provided - preserve its settings; weights passed to aggregate() + return ( + ClusterConfig( + method=cluster.method, + representation=cluster.representation, + normalize_column_means=cluster.normalize_column_means, + use_duration_curves=cluster.use_duration_curves, + include_period_sums=cluster.include_period_sums, + solver=cluster.solver, + ), + weights, ) def sel( @@ -1786,7 +1792,7 @@ def to_clean_key(period_label, scenario_label) -> tuple: # Build ClusterConfig with auto-calculated weights, filtered to available columns clustering_weights = self._calculate_clustering_weights(ds_slice) - cluster_config = self._build_cluster_config_with_weights( + cluster_config, resolved_weights = self._build_cluster_config_with_weights( cluster, clustering_weights, available_columns=set(df_for_clustering.columns) ) @@ -1797,6 +1803,7 @@ def to_clean_key(period_label, scenario_label) -> tuple: period_duration=hours_per_cluster, temporal_resolution=dt, cluster=cluster_config, + weights=resolved_weights, extremes=extremes, segments=segments, preserve_column_means=preserve_column_means,