From e7bfe80803591c9ca82535ac7ec59a2e1b981568 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 3 Aug 2026 11:14:44 +0200 Subject: [PATCH 001/241] Added entsoe-token to gitignore to prevent accidental uploads --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7210c19..7fde88f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ gurobi.log **/results/* **/temp/* **/tmp/* + +#ignore ENTSOE token +resources/user/token_entsoe.txt \ No newline at end of file From 66366edafe54837eb13eb7e84ce2d1b57daf5267 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 3 Aug 2026 11:53:20 +0200 Subject: [PATCH 002/241] Setting up the configuration changes to accept ordered rules for cleaning the data with config yaml validation. --- config/config.yaml | 14 +++++++ tests/integration/test_config.yaml | 9 ++++ workflow/internal/config.schema.yaml | 61 ++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/config/config.yaml b/config/config.yaml index 76f5b5b..c3e1f08 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -3,3 +3,17 @@ temporal_scope: start: "2017-01-01" end: "2018-01-01" use_load: entsoe_api # entsoe_opsd or entsoe_api + +# Gap filling rules for creating synthetic demand data. Rule order is respected +# i.e. whichever is listed first, is applied first. Enabled by default therefore +# "enable: false" may be used to deactivate these rules. Each rules requires at least +# "method" and "max_gap", additional parameters are optional under schema validation +# but may be required by each rule respectively +gap_filling: + enabled: true + rules: + - method: linear_interpolation + max_gap: 3h + - method: copy_period + max_gap: 168h + shift: 168h \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 26904bf..a98e110 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -3,3 +3,12 @@ integration_test: start: "2017-01-01" end: "2017-01-03" use_load: entsoe_api # entsoe_opsd or entsoe_api + +gap_filling: + enabled: true + rules: + - method: linear_interpolation + max_gap: 3h + - method: copy_period + max_gap: 168h + shift: 168h \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 37541ef..d0b98dd 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -8,8 +8,48 @@ $defs: pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$' description: "Date in ISO 8601 format (YYYY-MM-DD)." + duration: + type: string + pattern: '^[1-9]\d*(h|d|w)$' + description: > + Positive duration expressed as an integer followed by h, d, or w, + for example 3h, 7d, or 1w. + + linear_interpolation_rule: + type: object + additionalProperties: false + properties: + method: + const: linear_interpolation + max_gap: + $ref: "#/$defs/duration" + required: + - method + - max_gap + + copy_period_rule: + type: object + additionalProperties: false + properties: + method: + const: copy_period + max_gap: + $ref: "#/$defs/duration" + shift: + $ref: "#/$defs/duration" + required: + - method + - max_gap + - shift + + gap_filling_rule: + oneOf: + - $ref: "#/$defs/linear_interpolation_rule" + - $ref: "#/$defs/copy_period_rule" + type: object additionalProperties: false + properties: temporal_scope: type: object @@ -24,9 +64,30 @@ properties: required: - start - end + use_load: type: string description: "The source of load data to use in the simulation. Options are 'entsoe_api' or 'entsoe_opsd'." enum: - "entsoe_api" - "entsoe_opsd" + + gap_filling: + type: object + additionalProperties: false + description: > + Ordered rules used to fill missing national electricity-demand values. + properties: + enabled: + type: boolean + default: true + rules: + type: array + description: > + Gap-filling rules applied in the order listed. + minItems: 1 + items: + $ref: "#/$defs/gap_filling_rule" + required: + - enabled + - rules From 09d04b69ed034cb84934a03576d53c1a867b0611 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 3 Aug 2026 13:11:17 +0200 Subject: [PATCH 003/241] Enabled the gap_filling pipeline capability, modified the config schema validation to accept rules, and made the inptus required in clean.smk --- workflow/internal/config.schema.yaml | 21 +- workflow/rules/clean.smk | 2 + workflow/scripts/clean_load_entsoe_opsd.py | 7 + workflow/scripts/gap_filling/__init__.py | 0 .../gap_filling/linear_interpolation.py | 44 ++++ workflow/scripts/gap_filling/pipeline.py | 245 ++++++++++++++++++ 6 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 workflow/scripts/gap_filling/__init__.py create mode 100644 workflow/scripts/gap_filling/linear_interpolation.py create mode 100644 workflow/scripts/gap_filling/pipeline.py diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index d0b98dd..0c6b4ab 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -75,19 +75,28 @@ properties: gap_filling: type: object additionalProperties: false - description: > - Ordered rules used to fill missing national electricity-demand values. properties: enabled: type: boolean - default: true + rules: type: array - description: > - Gap-filling rules applied in the order listed. - minItems: 1 + description: Gap-filling rules applied in the order listed. items: $ref: "#/$defs/gap_filling_rule" + required: - enabled - rules + + allOf: + - if: + properties: + enabled: + const: true + required: + - enabled + then: + properties: + rules: + minItems: 1 diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 512536d..1e7fec6 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -3,6 +3,8 @@ rule clean_load_entsoe_opsd: load="/automatic/load_entsoe_opsd.csv", output: load="/automatic/load_entsoe_opsd.parquet", + params: + gap_filling=config["gap_filling"], log: "/clean_load_entsoe_opsd.log", conda: diff --git a/workflow/scripts/clean_load_entsoe_opsd.py b/workflow/scripts/clean_load_entsoe_opsd.py index 6a5cb3e..e171e20 100644 --- a/workflow/scripts/clean_load_entsoe_opsd.py +++ b/workflow/scripts/clean_load_entsoe_opsd.py @@ -7,6 +7,7 @@ import pandas as pd import pycountry from _schemas import LoadENTSOE +from gap_filling.pipeline import fill_gaps if TYPE_CHECKING: snakemake: Any @@ -46,6 +47,12 @@ def main(path_raw_load, output_load): ) load_pivot.index = pd.to_datetime(load_pivot.index).tz_localize(None) + #Fill data gaps + load_pivot, value_source = fill_gaps( + load_pivot, + config=snakemake.params.gap_filling, + ) + # save data and plots load_pivot.to_parquet(output_load) diff --git a/workflow/scripts/gap_filling/__init__.py b/workflow/scripts/gap_filling/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/gap_filling/linear_interpolation.py b/workflow/scripts/gap_filling/linear_interpolation.py new file mode 100644 index 0000000..c3a99c8 --- /dev/null +++ b/workflow/scripts/gap_filling/linear_interpolation.py @@ -0,0 +1,44 @@ +"""A gap filling method: linear interpolation.""" + +from __future__ import annotations + +import pandas as pd + +METHOD_NAME = "linear_interpolation" + + +def apply_linear_interpolation( + load: pd.DataFrame, + *, + max_gap: str | pd.Timedelta, + original_gap_duration: pd.DataFrame, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Fill bounded missing runs using linear interpolation. + + Only complete original gaps whose duration is less than or equal to + ``max_gap`` are eligible. + + Returns: + ------- + filled: + Load dataframe after applying this rule. + newly_filled: + Boolean dataframe identifying values filled by this rule. + """ + max_gap = pd.Timedelta(max_gap) + + eligible = ( + load.isna() + & original_gap_duration.gt(pd.Timedelta(0)) + & original_gap_duration.le(max_gap) + ) + + interpolated = load.interpolate( + method="time", + limit_area="inside", + ) + + filled = load.mask(eligible, interpolated) + newly_filled = load.isna() & filled.notna() + + return filled, newly_filled diff --git a/workflow/scripts/gap_filling/pipeline.py b/workflow/scripts/gap_filling/pipeline.py new file mode 100644 index 0000000..bc05b47 --- /dev/null +++ b/workflow/scripts/gap_filling/pipeline.py @@ -0,0 +1,245 @@ +"""The pipeline coordinates the gap-filling rules and ensures each receives the necessary parameters.""" +from __future__ import annotations + +import logging +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + +from gap_filling.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION +from gap_filling.linear_interpolation import apply_linear_interpolation + +logger = logging.getLogger(__name__) + +def fill_gaps( + load: pd.DataFrame, + *, + config: Mapping[str, Any], +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply configured gap-filling rules and record value provenance. + + Parameters + ---------- + load: + Hourly demand data indexed by timestamp, with one column per country. + config: + Gap-filling configuration containing ``enabled`` and ``rules``. + + Returns: + ------- + filled: + Load after applying the configured rules. If gap filling is disabled, + this is an unchanged copy of ``load``. + value_source: + Per-cell provenance such as ``observed``, + ``linear_interpolation``, or ``missing``. + """ + _validate_load(load) + _validate_config(config) + + filled = load.copy() + value_source = _initialise_value_source(load) + + if not config["enabled"]: + logger.info("Gap filling is disabled.") + return filled, value_source + + rules = config["rules"] + original_gap_duration = calculate_missing_run_durations(load) + + for rule in rules: + method = _get_method(rule) + + if method == LINEAR_INTERPOLATION: + filled, newly_filled = apply_linear_interpolation( + filled, + max_gap=rule["max_gap"], + original_gap_duration=original_gap_duration, + ) + else: + raise ValueError( + f"Unsupported gap-filling method: {method!r}" + ) + + value_source = value_source.mask(newly_filled, method) + _log_rule_results(method, newly_filled) + + unresolved = int(filled.isna().to_numpy().sum()) + logger.info( + "Gap filling completed with %s unresolved values.", + unresolved, + ) + + return filled, value_source + +def calculate_missing_run_durations( + load: pd.DataFrame, +) -> pd.DataFrame: + """Return the original duration of each missing run. + + Observed values receive a duration of zero. + """ + timestep = _infer_regular_timestep(load.index) + + durations = pd.DataFrame( + pd.Timedelta(0), + index=load.index, + columns=load.columns, + ) + + for column in load.columns: + missing = load[column].isna() + group_ids = missing.ne(missing.shift()).cumsum() + + run_lengths = ( + missing.groupby(group_ids) + .transform("sum") + .where(missing, 0) + ) + + durations[column] = run_lengths * timestep + + return durations + + +def _get_method(rule: Mapping[str, Any]) -> str: + try: + method = rule["method"] + except KeyError as error: + raise ValueError( + "Each gap-filling rule must define a 'method'." + ) from error + + if not isinstance(method, str): + raise TypeError( + "Gap-filling rule 'method' must be a string." + ) + + return method + + +def _validate_load(load: pd.DataFrame) -> None: + if not isinstance(load, pd.DataFrame): + raise TypeError("Load must be a pandas DataFrame.") + + if load.empty: + raise ValueError("Load dataframe is empty.") + + timestep = _infer_regular_timestep(load.index) + + if timestep != pd.Timedelta(hours=1): + raise ValueError( + "Gap filling currently expects hourly load data. " + f"Found timestep {timestep}." + ) + + if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in load.dtypes): + raise TypeError("All load columns must be numeric.") + + +def _infer_regular_timestep( + index: pd.Index, +) -> pd.Timedelta: + if not isinstance(index, pd.DatetimeIndex): + raise TypeError( + "Load data must use a pandas DatetimeIndex." + ) + + if not index.is_monotonic_increasing: + raise ValueError( + "Load timestamps must be sorted in increasing order." + ) + + if index.has_duplicates: + raise ValueError( + "Load timestamps must not contain duplicates." + ) + + differences = index.to_series().diff().dropna() + + if differences.empty: + raise ValueError( + "At least two timestamps are required for gap filling." + ) + + timestep = differences.iloc[0] + + if not differences.eq(timestep).all(): + raise ValueError( + "Load data must have a complete, regular time index " + "before gap filling." + ) + + return timestep + + +def _log_rule_results( + method: str, + newly_filled: pd.DataFrame, +) -> None: + total = int(newly_filled.to_numpy().sum()) + + logger.info( + "Gap-filling method '%s' filled %s values.", + method, + total, + ) + + for country, count in newly_filled.sum().items(): + count = int(count) + + if count: + logger.info( + "%s: %s values filled using '%s'.", + country, + count, + method, + ) + +def _initialise_value_source(load: pd.DataFrame) -> pd.DataFrame: + value_source = pd.DataFrame( + "observed", + index=load.index, + columns=load.columns, + dtype="string", + ) + + return value_source.mask(load.isna(), "missing") + + +def _validate_config(config: Mapping[str, Any]) -> None: + if not isinstance(config, Mapping): + raise TypeError( + "Gap-filling configuration must be a mapping." + ) + + if "enabled" not in config: + raise ValueError( + "Gap-filling configuration must define 'enabled'." + ) + + if not isinstance(config["enabled"], bool): + raise TypeError( + "Gap-filling configuration 'enabled' must be a boolean." + ) + + if "rules" not in config: + raise ValueError( + "Gap-filling configuration must define 'rules'." + ) + + if not isinstance(config["rules"], Sequence) or isinstance( + config["rules"], + (str, bytes), + ): + raise TypeError( + "Gap-filling configuration 'rules' must be an ordered sequence." + ) + + if config["enabled"] and not config["rules"]: + raise ValueError( + "At least one gap-filling rule is required when gap filling " + "is enabled." + ) + From 277dc68377dd6d5331cfa78f1ab82a9531df919d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 3 Aug 2026 14:08:46 +0200 Subject: [PATCH 004/241] Fixed a misconfig --- tests/integration/test_config.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index a98e110..130f751 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -4,11 +4,8 @@ integration_test: end: "2017-01-03" use_load: entsoe_api # entsoe_opsd or entsoe_api -gap_filling: - enabled: true - rules: - - method: linear_interpolation - max_gap: 3h - - method: copy_period - max_gap: 168h - shift: 168h \ No newline at end of file + gap_filling: + enabled: true + rules: + - method: linear_interpolation + max_gap: 3h \ No newline at end of file From 990f5cb67b6c83337eb725e0dd2f1ca0208f3b0d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 10:03:10 +0200 Subject: [PATCH 005/241] Impemented the copy_period logic --- config/config.yaml | 2 +- tests/integration/test_config.yaml | 5 +- workflow/internal/config.schema.yaml | 14 ++- workflow/scripts/clean_load_entsoe_opsd.py | 8 ++ workflow/scripts/gap_filling/copy_period.py | 104 ++++++++++++++++++++ workflow/scripts/gap_filling/pipeline.py | 15 +++ 6 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 workflow/scripts/gap_filling/copy_period.py diff --git a/config/config.yaml b/config/config.yaml index c3e1f08..6e94b80 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -16,4 +16,4 @@ gap_filling: max_gap: 3h - method: copy_period max_gap: 168h - shift: 168h \ No newline at end of file + source_offset: -168h \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 130f751..b10a5d3 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -8,4 +8,7 @@ integration_test: enabled: true rules: - method: linear_interpolation - max_gap: 3h \ No newline at end of file + max_gap: 3h + - method: copy_period + max_gap: 168h + source_offset: -168h \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 0c6b4ab..e39c6d3 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -14,6 +14,11 @@ $defs: description: > Positive duration expressed as an integer followed by h, d, or w, for example 3h, 7d, or 1w. + signed_duration: + type: string + pattern: '^-?[1-9]\d*(h|d|w)$' + description: > + Non-zero signed duration such as -168h, 168h, -7d, or 1w. linear_interpolation_rule: type: object @@ -35,12 +40,15 @@ $defs: const: copy_period max_gap: $ref: "#/$defs/duration" - shift: - $ref: "#/$defs/duration" + source_offset: + $ref: "#/$defs/signed_duration" + require_complete_source: + type: boolean + default: true required: - method - max_gap - - shift + - source_offset gap_filling_rule: oneOf: diff --git a/workflow/scripts/clean_load_entsoe_opsd.py b/workflow/scripts/clean_load_entsoe_opsd.py index e171e20..de4a13e 100644 --- a/workflow/scripts/clean_load_entsoe_opsd.py +++ b/workflow/scripts/clean_load_entsoe_opsd.py @@ -1,5 +1,6 @@ """Clean load data from ENTSO-E.""" +import logging import sys from typing import TYPE_CHECKING, Any from warnings import warn @@ -9,6 +10,13 @@ from _schemas import LoadENTSOE from gap_filling.pipeline import fill_gaps +logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", +) + +logger = logging.getLogger(__name__) + if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/gap_filling/copy_period.py b/workflow/scripts/gap_filling/copy_period.py new file mode 100644 index 0000000..db9baac --- /dev/null +++ b/workflow/scripts/gap_filling/copy_period.py @@ -0,0 +1,104 @@ +"""A gap filling method: copy period.""" + +from __future__ import annotations + +import pandas as pd + +METHOD_NAME = "copy_period" + + +def apply_copy_period( + load: pd.DataFrame, + *, + max_gap: str | pd.Timedelta, + source_offset: str | pd.Timedelta, + original_gap_duration: pd.DataFrame, + require_complete_source: bool = True, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Fill eligible missing runs from another period in the same time series. + + For a target timestamp ``t``, the source value is taken from + ``t + source_offset``. A negative offset therefore copies from an + earlier period. + + Only original missing runs no longer than ``max_gap`` are eligible. + """ + max_gap = pd.Timedelta(max_gap) + source_offset = pd.Timedelta(source_offset) + + if max_gap <= pd.Timedelta(0): + raise ValueError("'max_gap' must be greater than zero.") + + if source_offset == pd.Timedelta(0): + raise ValueError("'source_offset' must not be zero.") + + if not isinstance(require_complete_source, bool): + raise TypeError("'require_complete_source' must be a boolean.") + + eligible = ( + load.isna() + & original_gap_duration.gt(pd.Timedelta(0)) + & original_gap_duration.le(max_gap) + ) + + source = _values_at_offset( + load, + source_offset=source_offset, + ) + + if require_complete_source: + eligible = _require_complete_source_for_each_gap( + eligible=eligible, + source=source, + ) + else: + eligible &= source.notna() + + filled = load.mask(eligible, source) + newly_filled = load.isna() & filled.notna() + + return filled, newly_filled + + +def _values_at_offset( + load: pd.DataFrame, + *, + source_offset: pd.Timedelta, +) -> pd.DataFrame: + """Align values at ``timestamp + source_offset`` to each target timestamp.""" + source_timestamps = load.index + source_offset + + source = load.reindex(source_timestamps) + source.index = load.index + + return source + + +def _require_complete_source_for_each_gap( + *, + eligible: pd.DataFrame, + source: pd.DataFrame, +) -> pd.DataFrame: + """Keep a gap eligible only when every source value for that gap exists.""" + result = pd.DataFrame( + False, + index=eligible.index, + columns=eligible.columns, + ) + + for column in eligible.columns: + eligible_column = eligible[column] + gap_ids = eligible_column.ne( + eligible_column.shift(fill_value=False) + ).cumsum() + + for _, gap_mask in eligible_column.groupby(gap_ids): + gap_index = gap_mask.index[gap_mask] + + if gap_index.empty: + continue + + if source.loc[gap_index, column].notna().all(): + result.loc[gap_index, column] = True + + return result diff --git a/workflow/scripts/gap_filling/pipeline.py b/workflow/scripts/gap_filling/pipeline.py index bc05b47..f2f1d9d 100644 --- a/workflow/scripts/gap_filling/pipeline.py +++ b/workflow/scripts/gap_filling/pipeline.py @@ -7,6 +7,8 @@ import pandas as pd +from gap_filling.copy_period import METHOD_NAME as COPY_PERIOD +from gap_filling.copy_period import apply_copy_period from gap_filling.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION from gap_filling.linear_interpolation import apply_linear_interpolation @@ -57,6 +59,19 @@ def fill_gaps( max_gap=rule["max_gap"], original_gap_duration=original_gap_duration, ) + + elif method == COPY_PERIOD: + filled, newly_filled = apply_copy_period( + filled, + max_gap=rule["max_gap"], + source_offset=rule["source_offset"], + require_complete_source=rule.get( + "require_complete_source", + True, + ), + original_gap_duration=original_gap_duration, + ) + else: raise ValueError( f"Unsupported gap-filling method: {method!r}" From 3db22e8cda73ec14a5de6ffc8f40cba6953b2a43 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 10:11:29 +0200 Subject: [PATCH 006/241] enabled value source as a snakemake output --- workflow/rules/clean.smk | 1 + workflow/scripts/clean_load_entsoe_opsd.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 1e7fec6..d34a00a 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -3,6 +3,7 @@ rule clean_load_entsoe_opsd: load="/automatic/load_entsoe_opsd.csv", output: load="/automatic/load_entsoe_opsd.parquet", + value_source="/automatic/load_entsoe_opsd_value_source.parquet", params: gap_filling=config["gap_filling"], log: diff --git a/workflow/scripts/clean_load_entsoe_opsd.py b/workflow/scripts/clean_load_entsoe_opsd.py index de4a13e..371ae88 100644 --- a/workflow/scripts/clean_load_entsoe_opsd.py +++ b/workflow/scripts/clean_load_entsoe_opsd.py @@ -34,7 +34,7 @@ def get_map_alpha2_to_alpha3(countries_alpha_2): return map_alpha2_to_alpha3 -def main(path_raw_load, output_load): +def main(path_raw_load, output): """Clean ENTSO-E load data (units of MW), downloaded from open power system data (OPSD).""" load = pd.read_csv(path_raw_load) load = LoadENTSOE.validate(load) @@ -62,9 +62,10 @@ def main(path_raw_load, output_load): ) # save data and plots - load_pivot.to_parquet(output_load) + load_pivot.to_parquet(output.output_load) + value_source.to_parquet(output.value_source) if __name__ == "__main__": sys.stderr = open(snakemake.log[0], "w", buffering=1) - main(path_raw_load=snakemake.input.load, output_load=snakemake.output.load) + main(path_raw_load=snakemake.input.load, output=snakemake.output) From 50a398df41553cae9b9b0639b0f7b17774066357 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 12:59:27 +0200 Subject: [PATCH 007/241] Restructured the code base to permit use of and combine both entsoe and opsd data sources to resolve missing data issues. The code is restructured into loading, preparation, and cleaning stages now. --- config/config.yaml | 5 +- tests/integration/test_config.yaml | 5 +- workflow/internal/config.schema.yaml | 19 ++- workflow/rules/automatic.smk | 2 +- workflow/rules/clean.smk | 61 +++++++- workflow/rules/prepare_demand.smk | 2 +- workflow/scripts/clean_demand.py | 120 ++++++++++++++++ workflow/scripts/clean_load_entsoe_opsd.py | 71 ---------- .../{gap_filling => cleaning}/__init__.py | 0 workflow/scripts/cleaning/combine_sources.py | 92 ++++++++++++ .../{gap_filling => cleaning}/copy_period.py | 0 .../linear_interpolation.py | 0 .../{gap_filling => cleaning}/pipeline.py | 32 ++++- workflow/scripts/download_load_entsoe_api.py | 34 ++++- workflow/scripts/prepare_load_opsd.py | 133 ++++++++++++++++++ 15 files changed, 485 insertions(+), 91 deletions(-) create mode 100644 workflow/scripts/clean_demand.py delete mode 100644 workflow/scripts/clean_load_entsoe_opsd.py rename workflow/scripts/{gap_filling => cleaning}/__init__.py (100%) create mode 100644 workflow/scripts/cleaning/combine_sources.py rename workflow/scripts/{gap_filling => cleaning}/copy_period.py (100%) rename workflow/scripts/{gap_filling => cleaning}/linear_interpolation.py (100%) rename workflow/scripts/{gap_filling => cleaning}/pipeline.py (89%) create mode 100644 workflow/scripts/prepare_load_opsd.py diff --git a/config/config.yaml b/config/config.yaml index 6e94b80..de445c3 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,7 +2,10 @@ temporal_scope: start: "2017-01-01" end: "2018-01-01" -use_load: entsoe_api # entsoe_opsd or entsoe_api +# provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api + load_sources: + -entsoe_api + -opsd_api # Gap filling rules for creating synthetic demand data. Rule order is respected # i.e. whichever is listed first, is applied first. Enabled by default therefore diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index b10a5d3..4574541 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -2,7 +2,10 @@ integration_test: temporal_scope: start: "2017-01-01" end: "2017-01-03" - use_load: entsoe_api # entsoe_opsd or entsoe_api + # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api + load_sources: + - entsoe_api + - opsd_api gap_filling: enabled: true diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index e39c6d3..cec7e9b 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -73,12 +73,19 @@ properties: - start - end - use_load: - type: string - description: "The source of load data to use in the simulation. Options are 'entsoe_api' or 'entsoe_opsd'." - enum: - - "entsoe_api" - - "entsoe_opsd" + load_sources: + type: array + description: > + Ordered electricity-demand sources. Sources are applied from highest + to lowest priority, with later sources filling only values missing + from earlier sources. + minItems: 1 + uniqueItems: true + items: + type: string + enum: + - entsoe_api + - opsd_api gap_filling: type: object diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 65bfe58..88a57aa 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -12,7 +12,7 @@ rule download_load_entsoe_api: conda: "../envs/module.yaml" params: - country_codes_entsoe=internal["load_entsoe_api"]["countries"], + country_codes=internal["load_entsoe_api"]["countries"], message: "Download electricity load from ENTSOE." script: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index d34a00a..8884f0c 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,17 +1,68 @@ -rule clean_load_entsoe_opsd: +rule prepare_load_opsd: input: load="/automatic/load_entsoe_opsd.csv", output: - load="/automatic/load_entsoe_opsd.parquet", - value_source="/automatic/load_entsoe_opsd_value_source.parquet", + load="/automatic/load_opsd_api.parquet", params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe_api"]["countries"], + log: + "/prepare_load_opsd.log", + conda: + "../envs/module.yaml" + message: + "Prepare electricity-demand data from OPSD." + script: + "../scripts/prepare_load_opsd.py" + + +LOAD_SOURCE_PATHS = { + "entsoe_api": ( + "/automatic/" + "load_entsoe_api.parquet" + ), + "opsd_api": ( + "/automatic/" + "load_opsd_api.parquet" + ), +} + + +def configured_load_inputs(wildcards): + return [ + LOAD_SOURCE_PATHS[source_name] + for source_name in config["load_sources"] + ] + + +rule clean_demand: + input: + configured_load_inputs + output: + demand=( + "/automatic/" + "load_cleaned.parquet" + ), + data_source=( + "/automatic/" + "load_data_source.parquet" + ), + value_source=( + "/automatic/" + "load_value_source.parquet" + ), + params: + source_names=config["load_sources"], gap_filling=config["gap_filling"], log: - "/clean_load_entsoe_opsd.log", + "/clean_demand.log", conda: "../envs/module.yaml" + message: + "Combine and clean electricity-demand sources." script: - "../scripts/clean_load_entsoe_opsd.py" + "../scripts/clean_demand.py" rule clean_population: diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index fc42704..8e7cff5 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -1,6 +1,6 @@ rule demand_electricity_raster: input: - demand=f"/automatic/load_{config['use_load']}.parquet", + demand="/automatic/load_cleaned.parquet", shapes="", population="/automatic/{shape}/population_clean.tif", output: diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py new file mode 100644 index 0000000..39c9208 --- /dev/null +++ b/workflow/scripts/clean_demand.py @@ -0,0 +1,120 @@ +"""Combine and clean prepared electricity-demand sources.""" + +import logging +import sys +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pandas as pd +from cleaning.pipeline import clean_demand + +if TYPE_CHECKING: + snakemake: Any + +logger = logging.getLogger(__name__) + + +def main( + *, + input_paths: Sequence[str | Path], + source_names: Sequence[str], + gap_filling_config: Mapping[str, Any], + output: Any, +) -> None: + """Read prepared sources, clean demand, and write provenance.""" + if len(input_paths) != len(source_names): + raise ValueError( + "The number of input files must equal the number " + "of configured load sources." + ) + + sources = { + source_name: _read_prepared_source(path) + for source_name, path in zip( + source_names, + input_paths, + strict=True, + ) + } + + cleaned, data_source, value_source = clean_demand( + sources, + source_priority=source_names, + gap_filling_config=gap_filling_config, + ) + + cleaned.to_parquet(output.demand) + data_source.to_parquet(output.data_source) + value_source.to_parquet(output.value_source) + + _log_source_counts(data_source) + + +def _read_prepared_source( + path: str | Path, +) -> pd.DataFrame: + """Read and validate one prepared demand source.""" + demand = pd.read_parquet(path) + + if not isinstance(demand.index, pd.DatetimeIndex): + demand.index = pd.to_datetime( + demand.index, + utc=True, + ) + + elif demand.index.tz is None: + demand.index = demand.index.tz_localize("UTC") + + else: + demand.index = demand.index.tz_convert("UTC") + + if demand.index.has_duplicates: + raise ValueError( + f"Demand source contains duplicate timestamps: {path}" + ) + + if demand.columns.has_duplicates: + raise ValueError( + f"Demand source contains duplicate columns: {path}" + ) + + return demand.sort_index() + + +def _log_source_counts( + data_source: pd.DataFrame, +) -> None: + """Log the number of cells supplied by each observed source.""" + counts = data_source.stack().value_counts() + + for source_name, count in counts.items(): + logger.info( + "%s supplied %s observed values.", + source_name, + int(count), + ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + main( + input_paths=list(snakemake.input), + source_names=list( + snakemake.params.source_names + ), + gap_filling_config=( + snakemake.params.gap_filling + ), + output=snakemake.output, + ) diff --git a/workflow/scripts/clean_load_entsoe_opsd.py b/workflow/scripts/clean_load_entsoe_opsd.py deleted file mode 100644 index 371ae88..0000000 --- a/workflow/scripts/clean_load_entsoe_opsd.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Clean load data from ENTSO-E.""" - -import logging -import sys -from typing import TYPE_CHECKING, Any -from warnings import warn - -import pandas as pd -import pycountry -from _schemas import LoadENTSOE -from gap_filling.pipeline import fill_gaps - -logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", -) - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - snakemake: Any - - -def get_map_alpha2_to_alpha3(countries_alpha_2): - """Get mapping from alpha-2 to alpha-3 country codes.""" - map_alpha2_to_alpha3 = {} - for alpha2 in countries_alpha_2: - country = pycountry.countries.get(alpha_2=alpha2) - if country is not None: - map_alpha2_to_alpha3[alpha2] = country.alpha_3 - else: - warn(f"Country with alpha-2 code '{alpha2}' not found in pycountry.") - - return map_alpha2_to_alpha3 - - -def main(path_raw_load, output): - """Clean ENTSO-E load data (units of MW), downloaded from open power system data (OPSD).""" - load = pd.read_csv(path_raw_load) - load = LoadENTSOE.validate(load) - - # filter the raw data - load = load.loc[load["variable"] == "load"] - load = load.loc[load["attribute"] == "actual_entsoe_power_statistics"] - - # map country codes from alpha-2 to alpha-3 - map_alpha2_to_alpha3 = get_map_alpha2_to_alpha3(load["region"].unique()) - - load = load.loc[load["region"].isin(map_alpha2_to_alpha3.keys())] - load.loc[:, "region"] = load.loc[:, "region"].map(map_alpha2_to_alpha3) - - # pivot the data to have one column per region - load_pivot = pd.pivot( - load, index=["utc_timestamp"], columns=["region"], values="data" - ) - load_pivot.index = pd.to_datetime(load_pivot.index).tz_localize(None) - - #Fill data gaps - load_pivot, value_source = fill_gaps( - load_pivot, - config=snakemake.params.gap_filling, - ) - - # save data and plots - load_pivot.to_parquet(output.output_load) - value_source.to_parquet(output.value_source) - - -if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - main(path_raw_load=snakemake.input.load, output=snakemake.output) diff --git a/workflow/scripts/gap_filling/__init__.py b/workflow/scripts/cleaning/__init__.py similarity index 100% rename from workflow/scripts/gap_filling/__init__.py rename to workflow/scripts/cleaning/__init__.py diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py new file mode 100644 index 0000000..5aca76e --- /dev/null +++ b/workflow/scripts/cleaning/combine_sources.py @@ -0,0 +1,92 @@ +"""Combine prepared demand sources in priority order.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence + +import pandas as pd + + +def combine_sources( + sources: Mapping[str, pd.DataFrame], + *, + priority: Sequence[str], +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Combine sources and record which source supplied each value.""" + if not priority: + raise ValueError( + "At least one demand source must be configured." + ) + + missing_sources = [ + source + for source in priority + if source not in sources + ] + + if missing_sources: + raise ValueError( + "Configured demand sources were not supplied: " + f"{missing_sources}" + ) + + selected = { + source: sources[source] + for source in priority + } + + _validate_source_alignment(selected) + + first_source = priority[0] + combined = selected[first_source].copy() + + data_source = pd.DataFrame( + pd.NA, + index=combined.index, + columns=combined.columns, + dtype="string", + ) + + data_source = data_source.mask( + combined.notna(), + first_source, + ) + + for source_name in priority[1:]: + candidate = selected[source_name] + + newly_supplied = ( + combined.isna() + & candidate.notna() + ) + + combined = combined.combine_first(candidate) + + data_source = data_source.mask( + newly_supplied, + source_name, + ) + + return combined, data_source + + +def _validate_source_alignment( + sources: Mapping[str, pd.DataFrame], +) -> None: + """Require all prepared sources to use the same target grid.""" + source_items = list(sources.items()) + + reference_name, reference = source_items[0] + + for source_name, source in source_items[1:]: + if not source.index.equals(reference.index): + raise ValueError( + f"Demand source {source_name!r} does not use the " + f"same time index as {reference_name!r}." + ) + + if not source.columns.equals(reference.columns): + raise ValueError( + f"Demand source {source_name!r} does not use the " + f"same country columns as {reference_name!r}." + ) diff --git a/workflow/scripts/gap_filling/copy_period.py b/workflow/scripts/cleaning/copy_period.py similarity index 100% rename from workflow/scripts/gap_filling/copy_period.py rename to workflow/scripts/cleaning/copy_period.py diff --git a/workflow/scripts/gap_filling/linear_interpolation.py b/workflow/scripts/cleaning/linear_interpolation.py similarity index 100% rename from workflow/scripts/gap_filling/linear_interpolation.py rename to workflow/scripts/cleaning/linear_interpolation.py diff --git a/workflow/scripts/gap_filling/pipeline.py b/workflow/scripts/cleaning/pipeline.py similarity index 89% rename from workflow/scripts/gap_filling/pipeline.py rename to workflow/scripts/cleaning/pipeline.py index f2f1d9d..21c99cf 100644 --- a/workflow/scripts/gap_filling/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -7,13 +7,37 @@ import pandas as pd -from gap_filling.copy_period import METHOD_NAME as COPY_PERIOD -from gap_filling.copy_period import apply_copy_period -from gap_filling.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION -from gap_filling.linear_interpolation import apply_linear_interpolation +from cleaning.combine_sources import combine_sources +from cleaning.copy_period import METHOD_NAME as COPY_PERIOD +from cleaning.copy_period import apply_copy_period +from cleaning.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION +from cleaning.linear_interpolation import apply_linear_interpolation logger = logging.getLogger(__name__) +def clean_demand( + sources: Mapping[str, pd.DataFrame], + *, + source_priority: Sequence[str], + gap_filling_config: Mapping[str, Any], +) -> tuple[ + pd.DataFrame, + pd.DataFrame, + pd.DataFrame, +]: + """Combine observed sources and fill remaining gaps.""" + combined, data_source = combine_sources( + sources, + priority=source_priority, + ) + + cleaned, value_source = fill_gaps( + combined, + config=gap_filling_config, + ) + + return cleaned, data_source, value_source + def fill_gaps( load: pd.DataFrame, *, diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index eaa637a..558691c 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -59,6 +59,38 @@ def main(start, end, country_codes, token, output_load): df = df.resample("1h").mean() + # reindexing to add some security to the entsoe download + target_index = pd.date_range( + start=start, + end=end, + freq="h", + inclusive="left", + ) + + df = df.reindex( + index=target_index, + columns=country_codes, + ) + + non_numeric_columns = df.select_dtypes( + exclude="number" + ).columns + + invalid = { + column: df[column].dropna().head().tolist() + for column in non_numeric_columns + if not df[column].dropna().empty + } + + if invalid: + raise TypeError( + "ENTSO-E load contains non-numeric values: " + f"{invalid}" + ) + + #Pre-cleaning. Replaces empty object columns with NaN columns to allow for data-source combining + df = df.astype(float) + df.to_parquet(output_load) @@ -67,7 +99,7 @@ def main(start, end, country_codes, token, output_load): main( start=snakemake.config["temporal_scope"]["start"], end=snakemake.config["temporal_scope"]["end"], - country_codes=snakemake.params.country_codes_entsoe, + country_codes=snakemake.params.country_codes, token=snakemake.input.token_entsoe, output_load=snakemake.output.load, ) diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py new file mode 100644 index 0000000..7b8e5b0 --- /dev/null +++ b/workflow/scripts/prepare_load_opsd.py @@ -0,0 +1,133 @@ +"""Prepare electricity-demand data downloaded from OPSD.""" + +import sys +from typing import TYPE_CHECKING, Any +from warnings import warn + +import pandas as pd +import pycountry +from _schemas import LoadENTSOE + +if TYPE_CHECKING: + snakemake: Any + +def as_utc_timestamp(value) -> pd.Timestamp: + """Convert a timestamp-like value to UTC.""" + timestamp = pd.Timestamp(value) + + if timestamp.tzinfo is None: + return timestamp.tz_localize("UTC") + + return timestamp.tz_convert("UTC") + + +def get_map_alpha2_to_alpha3( + countries_alpha_2, +) -> dict[str, str]: + """Map ISO alpha-2 country codes to alpha-3 codes.""" + mapping = {} + + for alpha2 in countries_alpha_2: + country = pycountry.countries.get(alpha_2=alpha2) + + if country is not None: + mapping[alpha2] = country.alpha_3 + else: + warn( + f"Country with alpha-2 code '{alpha2}' " + "not found in pycountry." + ) + + return mapping + + +def main( + path_raw_load, + output_load, + start, + end, + country_codes, +): + """Prepare OPSD demand for the configured scope.""" + load = pd.read_csv(path_raw_load) + load = LoadENTSOE.validate(load) + + load = load.loc[load["variable"] == "load"] + load = load.loc[ + load["attribute"] + == "actual_entsoe_power_statistics" + ].copy() + + start = as_utc_timestamp(start) + end = as_utc_timestamp(end) + + load["utc_timestamp"] = pd.to_datetime( + load["utc_timestamp"], + utc=True, + ) + + # Filter the large long-format table before pivoting. + load = load.loc[ + (load["utc_timestamp"] >= start) + & (load["utc_timestamp"] < end) + ].copy() + + country_mapping = get_map_alpha2_to_alpha3( + load["region"].unique() + ) + + load = load.loc[ + load["region"].isin(country_mapping) + ].copy() + + load.loc[:, "region"] = load["region"].map( + country_mapping + ) + + # Keep only countries required by this module run. + load = load.loc[ + load["region"].isin(country_codes) + ].copy() + + load.loc[:, "data"] = pd.to_numeric( + load["data"], + errors="raise", + ) + + prepared = pd.pivot( + load, + index="utc_timestamp", + columns="region", + values="data", + ) + + target_index = pd.date_range( + start=start, + end=end, + freq="h", + inclusive="left", + ) + + prepared = prepared.reindex( + index=target_index, + columns=country_codes, + ) + + prepared = prepared.astype(float) + + prepared.to_parquet(output_load) + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + main( + path_raw_load=snakemake.input.load, + output_load=snakemake.output.load, + start=snakemake.params.start, + end=snakemake.params.end, + country_codes=list(snakemake.params.country_codes), + ) From efa29e7d0e1895b3de19c3843c312a4ae94d4ada Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 15:18:25 +0200 Subject: [PATCH 008/241] Implemented the provenance tracking capability and updated schema validation. --- config/config.yaml | 23 +- tests/conftest.py | 13 +- tests/integration/test_config.yaml | 6 +- tests/unit/test_cleaning_pipeline.py | 304 +++++++++++++++++++ workflow/internal/config.schema.yaml | 130 +++----- workflow/rules/clean.smk | 8 +- workflow/scripts/clean_demand.py | 54 +++- workflow/scripts/cleaning/combine_sources.py | 29 +- workflow/scripts/cleaning/pipeline.py | 186 ++++++++++-- workflow/scripts/cleaning/provenance.py | 106 +++++++ 10 files changed, 720 insertions(+), 139 deletions(-) create mode 100644 tests/unit/test_cleaning_pipeline.py create mode 100644 workflow/scripts/cleaning/provenance.py diff --git a/config/config.yaml b/config/config.yaml index de445c3..8e7362c 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,21 +2,22 @@ temporal_scope: start: "2017-01-01" end: "2018-01-01" -# provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api - load_sources: - -entsoe_api - -opsd_api -# Gap filling rules for creating synthetic demand data. Rule order is respected -# i.e. whichever is listed first, is applied first. Enabled by default therefore -# "enable: false" may be used to deactivate these rules. Each rules requires at least -# "method" and "max_gap", additional parameters are optional under schema validation -# but may be required by each rule respectively +# Provide a list of sources in priority order. +# Options: entsoe_api, opsd_api +load_sources: + - entsoe_api + - opsd_api + +# Gap-filling rules are applied in the listed order. gap_filling: enabled: true rules: - - method: linear_interpolation + - name: interpolate_short_gaps + method: linear_interpolation max_gap: 3h - - method: copy_period + + - name: copy_previous_week + method: copy_period max_gap: 168h source_offset: -168h \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index fe22e42..43f5843 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,22 @@ """Shared test fixtures.""" import os +import sys from pathlib import Path from urllib.request import urlretrieve import pytest + +MODULE_PATH = Path(__file__).resolve().parent.parent +WORKFLOW_SCRIPTS = MODULE_PATH / "workflow" / "scripts" + +sys.path.insert( + 0, + str(WORKFLOW_SCRIPTS), +) + + TEST_FILES = { "EUROPE_S_C1_ADM1": "https://zenodo.org/records/20765043/files/EUROPE_S_C1_ADM1.parquet?download=1", "EUROPE_L_C34_ADM1": "https://zenodo.org/records/20765043/files/EUROPE_L_C34_ADM1.parquet?download=1", @@ -19,7 +30,7 @@ def module_path(): """Parent directory of the project.""" # If your module needs files in resources/user/, place automated downloads here. - return Path(__file__).parent.parent + return MODULE_PATH @pytest.fixture(scope="session") diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 4574541..4d8fcaf 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -10,8 +10,10 @@ integration_test: gap_filling: enabled: true rules: - - method: linear_interpolation + - name: interpolate_short_gaps + method: linear_interpolation max_gap: 3h - - method: copy_period + - name: copy_previous_week + method: copy_period max_gap: 168h source_offset: -168h \ No newline at end of file diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py new file mode 100644 index 0000000..5aeae55 --- /dev/null +++ b/tests/unit/test_cleaning_pipeline.py @@ -0,0 +1,304 @@ +"""Tests for the demand-cleaning pipeline.""" + +from __future__ import annotations + +import numpy as np +import pandas as pd + +from cleaning.pipeline import clean_demand + + +def test_clean_demand_records_methods_and_ranks() -> None: + """Track observed sources, filling rules, and unresolved gaps.""" + index = pd.date_range( + start="2017-01-01", + periods=400, + freq="h", + tz="UTC", + ) + + primary = pd.DataFrame( + { + "AAA": np.arange( + len(index), + dtype=float, + ), + }, + index=index, + ) + + fallback = pd.DataFrame( + np.nan, + index=index, + columns=["AAA"], + dtype=float, + ) + + # Four missing values at the start cannot be interpolated because + # the run exceeds max_gap and cannot be copied from a previous week. + unresolved_timestamps = index[0:4] + primary.loc[ + unresolved_timestamps, + "AAA", + ] = np.nan + + # One primary-source gap is supplied directly by the fallback source. + fallback_timestamp = index[200] + primary.loc[ + fallback_timestamp, + "AAA", + ] = np.nan + fallback.loc[ + fallback_timestamp, + "AAA", + ] = 10_000.0 + + # A two-hour gap is filled by the first rule. + interpolation_timestamps = index[220:222] + primary.loc[ + interpolation_timestamps, + "AAA", + ] = np.nan + + # A four-hour gap exceeds the interpolation limit but can be copied + # from the corresponding values seven days earlier. + copy_timestamps = index[250:254] + primary.loc[ + copy_timestamps, + "AAA", + ] = np.nan + + sources = { + "primary": primary, + "fallback": fallback, + } + + gap_filling_config = { + "enabled": True, + "rules": [ + { + "name": "interpolate_short_gaps", + "method": "linear_interpolation", + "max_gap": "3h", + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + "require_complete_source": True, + }, + ], + } + + ( + cleaned, + data_source, + cleaning_method, + cleaning_method_rank, + ) = clean_demand( + sources, + source_priority=[ + "primary", + "fallback", + ], + gap_filling_config=gap_filling_config, + ) + + # All outputs use the same grid. + for frame in [ + data_source, + cleaning_method, + cleaning_method_rank, + ]: + assert frame.index.equals(cleaned.index) + assert frame.columns.equals(cleaned.columns) + + # Primary observations have rank 0. + primary_mask = ( + primary.notna() + & fallback.isna() + ) + + assert ( + data_source.to_numpy()[ + primary_mask.to_numpy() + ] + == "primary" + ).all() + + assert ( + cleaning_method.to_numpy()[ + primary_mask.to_numpy() + ] + == "observed_primary" + ).all() + + assert ( + cleaning_method_rank.to_numpy()[ + primary_mask.to_numpy() + ] + == 0 + ).all() + + # The fallback observation has rank 1. + assert ( + cleaned.loc[ + fallback_timestamp, + "AAA", + ] + == 10_000.0 + ) + + assert ( + data_source.loc[ + fallback_timestamp, + "AAA", + ] + == "fallback" + ) + + assert ( + cleaning_method.loc[ + fallback_timestamp, + "AAA", + ] + == "observed_fallback" + ) + + assert ( + cleaning_method_rank.loc[ + fallback_timestamp, + "AAA", + ] + == 1 + ) + + # The two-hour gap is filled by interpolation at rank 2. + assert cleaned.loc[ + interpolation_timestamps, + "AAA", + ].notna().all() + + assert cleaning_method.loc[ + interpolation_timestamps, + "AAA", + ].eq( + "interpolate_short_gaps" + ).all() + + assert cleaning_method_rank.loc[ + interpolation_timestamps, + "AAA", + ].eq(2).all() + + left_value = cleaned.loc[ + index[219], + "AAA", + ] + right_value = cleaned.loc[ + index[222], + "AAA", + ] + + expected_interpolation = np.linspace( + left_value, + right_value, + 4, + )[1:3] + + np.testing.assert_allclose( + cleaned.loc[ + interpolation_timestamps, + "AAA", + ].to_numpy(), + expected_interpolation, + ) + + # The four-hour gap is copied from seven days earlier at rank 3. + source_timestamps = ( + copy_timestamps + - pd.Timedelta(hours=168) + ) + + np.testing.assert_allclose( + cleaned.loc[ + copy_timestamps, + "AAA", + ].to_numpy(), + cleaned.loc[ + source_timestamps, + "AAA", + ].to_numpy(), + ) + + assert cleaning_method.loc[ + copy_timestamps, + "AAA", + ].eq( + "copy_previous_week" + ).all() + + assert cleaning_method_rank.loc[ + copy_timestamps, + "AAA", + ].eq(3).all() + + # The initial gap remains unresolved at the final rank. + assert cleaned.loc[ + unresolved_timestamps, + "AAA", + ].isna().all() + + assert cleaning_method.loc[ + unresolved_timestamps, + "AAA", + ].eq("missing").all() + + assert cleaning_method_rank.loc[ + unresolved_timestamps, + "AAA", + ].eq(4).all() + + # Derived and unresolved values have no observed data source. + derived_or_missing = ( + interpolation_timestamps + .append(copy_timestamps) + .append(unresolved_timestamps) + ) + + assert data_source.loc[ + derived_or_missing, + "AAA", + ].isna().all() + + expected_method_counts = { + "observed_primary": 389, + "observed_fallback": 1, + "interpolate_short_gaps": 2, + "copy_previous_week": 4, + "missing": 4, + } + + assert ( + cleaning_method["AAA"] + .value_counts() + .to_dict() + == expected_method_counts + ) + + expected_rank_counts = { + 0: 389, + 1: 1, + 2: 2, + 3: 4, + 4: 4, + } + + assert ( + cleaning_method_rank["AAA"] + .value_counts() + .sort_index() + .to_dict() + == expected_rank_counts + ) \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index cec7e9b..438873a 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -14,104 +14,62 @@ $defs: description: > Positive duration expressed as an integer followed by h, d, or w, for example 3h, 7d, or 1w. + signed_duration: type: string pattern: '^-?[1-9]\d*(h|d|w)$' description: > Non-zero signed duration such as -168h, 168h, -7d, or 1w. - linear_interpolation_rule: - type: object - additionalProperties: false - properties: - method: - const: linear_interpolation - max_gap: - $ref: "#/$defs/duration" - required: - - method - - max_gap - - copy_period_rule: + gap_filling_rule_base: type: object - additionalProperties: false properties: + name: + type: string + pattern: '^[a-z0-9_]+$' + description: > + Unique identifier for this configured gap-filling rule. Used for + provenance reporting and cleaning-method ranking. method: - const: copy_period - max_gap: - $ref: "#/$defs/duration" - source_offset: - $ref: "#/$defs/signed_duration" - require_complete_source: - type: boolean - default: true + type: string + description: Gap-filling method applied by this rule. required: + - name - method - - max_gap - - source_offset + + linear_interpolation_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + - type: object + properties: + method: + const: linear_interpolation + max_gap: + $ref: "#/$defs/duration" + required: + - max_gap + unevaluatedProperties: false + + copy_period_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + - type: object + properties: + method: + const: copy_period + max_gap: + $ref: "#/$defs/duration" + source_offset: + $ref: "#/$defs/signed_duration" + require_complete_source: + type: boolean + default: true + required: + - max_gap + - source_offset + unevaluatedProperties: false gap_filling_rule: oneOf: - $ref: "#/$defs/linear_interpolation_rule" - - $ref: "#/$defs/copy_period_rule" - -type: object -additionalProperties: false - -properties: - temporal_scope: - type: object - description: "The temporal scope of the simulation." - properties: - start: - $ref: "#/$defs/iso_date" - description: "Start date the simulation in ISO 8601 format (YYYY-MM-DD)." - end: - $ref: "#/$defs/iso_date" - description: "End date the simulation in ISO 8601 format (YYYY-MM-DD)." - required: - - start - - end - - load_sources: - type: array - description: > - Ordered electricity-demand sources. Sources are applied from highest - to lowest priority, with later sources filling only values missing - from earlier sources. - minItems: 1 - uniqueItems: true - items: - type: string - enum: - - entsoe_api - - opsd_api - - gap_filling: - type: object - additionalProperties: false - properties: - enabled: - type: boolean - - rules: - type: array - description: Gap-filling rules applied in the order listed. - items: - $ref: "#/$defs/gap_filling_rule" - - required: - - enabled - - rules - - allOf: - - if: - properties: - enabled: - const: true - required: - - enabled - then: - properties: - rules: - minItems: 1 + - $ref: "#/$defs/copy_period_rule" \ No newline at end of file diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 8884f0c..18a5b6a 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -48,9 +48,13 @@ rule clean_demand: "/automatic/" "load_data_source.parquet" ), - value_source=( + cleaning_method=( "/automatic/" - "load_value_source.parquet" + "load_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/" + "load_cleaning_method_rank.parquet" ), params: source_names=config["load_sources"], diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 39c9208..eec341e 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -38,7 +38,12 @@ def main( ) } - cleaned, data_source, value_source = clean_demand( + ( + cleaned, + data_source, + cleaning_method, + cleaning_method_rank, + ) = clean_demand( sources, source_priority=source_names, gap_filling_config=gap_filling_config, @@ -46,10 +51,18 @@ def main( cleaned.to_parquet(output.demand) data_source.to_parquet(output.data_source) - value_source.to_parquet(output.value_source) + cleaning_method.to_parquet( + output.cleaning_method + ) + cleaning_method_rank.to_parquet( + output.cleaning_method_rank + ) _log_source_counts(data_source) - + _log_cleaning_method_counts( + cleaning_method, + cleaning_method_rank, + ) def _read_prepared_source( path: str | Path, @@ -96,6 +109,41 @@ def _log_source_counts( ) +def _log_cleaning_method_counts( + cleaning_method: pd.DataFrame, + cleaning_method_rank: pd.DataFrame, +) -> None: + """Log the number of cells assigned to each cleaning method.""" + method_counts = cleaning_method.stack().value_counts() + + for method_name, count in method_counts.items(): + method_mask = ( + cleaning_method.eq(method_name) + .fillna(False) + .to_numpy(dtype=bool) + ) + + ranks = cleaning_method_rank.to_numpy()[ + method_mask + ] + + unique_ranks = pd.unique(ranks) + + if len(unique_ranks) != 1: + raise ValueError( + "Cleaning method " + f"{method_name!r} has multiple ranks: " + f"{unique_ranks.tolist()}" + ) + + logger.info( + "Cleaning method '%s' at rank %s supplied %s values.", + method_name, + int(unique_ranks[0]), + int(count), + ) + + if __name__ == "__main__": sys.stderr = open( snakemake.log[0], diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py index 5aca76e..c1cf6b3 100644 --- a/workflow/scripts/cleaning/combine_sources.py +++ b/workflow/scripts/cleaning/combine_sources.py @@ -11,8 +11,8 @@ def combine_sources( sources: Mapping[str, pd.DataFrame], *, priority: Sequence[str], -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Combine sources and record which source supplied each value.""" +) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: + """Combine sources and record source and cleaning-method provenance.""" if not priority: raise ValueError( "At least one demand source must be configured." @@ -47,11 +47,25 @@ def combine_sources( dtype="string", ) + cleaning_method = pd.DataFrame( + pd.NA, + index=combined.index, + columns=combined.columns, + dtype="string", + ) + + first_source_values = combined.notna() + data_source = data_source.mask( - combined.notna(), + first_source_values, first_source, ) + cleaning_method = cleaning_method.mask( + first_source_values, + f"observed_{first_source}", + ) + for source_name in priority[1:]: candidate = selected[source_name] @@ -67,7 +81,12 @@ def combine_sources( source_name, ) - return combined, data_source + cleaning_method = cleaning_method.mask( + newly_supplied, + f"observed_{source_name}", + ) + + return combined, data_source, cleaning_method def _validate_source_alignment( @@ -89,4 +108,4 @@ def _validate_source_alignment( raise ValueError( f"Demand source {source_name!r} does not use the " f"same country columns as {reference_name!r}." - ) + ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 21c99cf..8e986a3 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -12,9 +12,11 @@ from cleaning.copy_period import apply_copy_period from cleaning.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION from cleaning.linear_interpolation import apply_linear_interpolation +from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank logger = logging.getLogger(__name__) + def clean_demand( sources: Mapping[str, pd.DataFrame], *, @@ -24,58 +26,98 @@ def clean_demand( pd.DataFrame, pd.DataFrame, pd.DataFrame, + pd.DataFrame, ]: """Combine observed sources and fill remaining gaps.""" - combined, data_source = combine_sources( + ( + combined, + data_source, + cleaning_method, + ) = combine_sources( sources, priority=source_priority, ) - cleaned, value_source = fill_gaps( + cleaned, cleaning_method = fill_gaps( combined, + cleaning_method=cleaning_method, config=gap_filling_config, ) - return cleaned, data_source, value_source + rules = gap_filling_config["rules"] + + cleaning_method_ranks = build_cleaning_method_ranks( + source_priority=source_priority, + rules=rules, + ) + + cleaning_method_rank = derive_cleaning_method_rank( + cleaning_method=cleaning_method, + ranks=cleaning_method_ranks, + ) + + return ( + cleaned, + data_source, + cleaning_method, + cleaning_method_rank, + ) + def fill_gaps( load: pd.DataFrame, *, + cleaning_method: pd.DataFrame, config: Mapping[str, Any], ) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply configured gap-filling rules and record value provenance. + """Apply configured gap-filling rules and record method provenance. Parameters ---------- load: Hourly demand data indexed by timestamp, with one column per country. + cleaning_method: + Per-cell cleaning-method provenance for the observed input values. + Missing input values should contain ``pd.NA``. config: Gap-filling configuration containing ``enabled`` and ``rules``. - Returns: + Returns ------- filled: Load after applying the configured rules. If gap filling is disabled, this is an unchanged copy of ``load``. - value_source: - Per-cell provenance such as ``observed``, - ``linear_interpolation``, or ``missing``. + cleaning_method: + Per-cell provenance containing the observed-source identifier, + configured gap-filling rule name, or ``missing``. """ _validate_load(load) + _validate_cleaning_method( + load=load, + cleaning_method=cleaning_method, + ) _validate_config(config) filled = load.copy() - value_source = _initialise_value_source(load) + cleaning_method = cleaning_method.copy() if not config["enabled"]: logger.info("Gap filling is disabled.") - return filled, value_source + + cleaning_method = cleaning_method.fillna( + "missing" + ) + + return filled, cleaning_method rules = config["rules"] - original_gap_duration = calculate_missing_run_durations(load) + original_gap_duration = calculate_missing_run_durations( + load + ) for rule in rules: method = _get_method(rule) + rule_name = _get_rule_name(rule) if method == LINEAR_INTERPOLATION: filled, newly_filled = apply_linear_interpolation( @@ -101,16 +143,32 @@ def fill_gaps( f"Unsupported gap-filling method: {method!r}" ) - value_source = value_source.mask(newly_filled, method) - _log_rule_results(method, newly_filled) + cleaning_method = cleaning_method.mask( + newly_filled, + rule_name, + ) + + _log_rule_results( + rule_name=rule_name, + method=method, + newly_filled=newly_filled, + ) + + cleaning_method = cleaning_method.fillna( + "missing" + ) + + unresolved = int( + filled.isna().to_numpy().sum() + ) - unresolved = int(filled.isna().to_numpy().sum()) logger.info( "Gap filling completed with %s unresolved values.", unresolved, ) - return filled, value_source + return filled, cleaning_method + def calculate_missing_run_durations( load: pd.DataFrame, @@ -158,6 +216,29 @@ def _get_method(rule: Mapping[str, Any]) -> str: return method +def _get_rule_name( + rule: Mapping[str, Any], +) -> str: + try: + name = rule["name"] + except KeyError as error: + raise ValueError( + "Each gap-filling rule must define a 'name'." + ) from error + + if not isinstance(name, str): + raise TypeError( + "Gap-filling rule 'name' must be a string." + ) + + if not name: + raise ValueError( + "Gap-filling rule 'name' must not be empty." + ) + + return name + + def _validate_load(load: pd.DataFrame) -> None: if not isinstance(load, pd.DataFrame): raise TypeError("Load must be a pandas DataFrame.") @@ -214,13 +295,18 @@ def _infer_regular_timestep( def _log_rule_results( + *, + rule_name: str, method: str, newly_filled: pd.DataFrame, ) -> None: - total = int(newly_filled.to_numpy().sum()) + total = int( + newly_filled.to_numpy().sum() + ) logger.info( - "Gap-filling method '%s' filled %s values.", + "Gap-filling rule '%s' using method '%s' filled %s values.", + rule_name, method, total, ) @@ -230,22 +316,12 @@ def _log_rule_results( if count: logger.info( - "%s: %s values filled using '%s'.", + "%s: %s values filled using rule '%s'.", country, count, - method, + rule_name, ) -def _initialise_value_source(load: pd.DataFrame) -> pd.DataFrame: - value_source = pd.DataFrame( - "observed", - index=load.index, - columns=load.columns, - dtype="string", - ) - - return value_source.mask(load.isna(), "missing") - def _validate_config(config: Mapping[str, Any]) -> None: if not isinstance(config, Mapping): @@ -282,3 +358,55 @@ def _validate_config(config: Mapping[str, Any]) -> None: "is enabled." ) + +def _validate_cleaning_method( + *, + load: pd.DataFrame, + cleaning_method: pd.DataFrame, +) -> None: + if not isinstance(cleaning_method, pd.DataFrame): + raise TypeError( + "Cleaning method must be a pandas DataFrame." + ) + + if not cleaning_method.index.equals(load.index): + raise ValueError( + "Cleaning-method provenance must use the same " + "index as the load data." + ) + + if not cleaning_method.columns.equals(load.columns): + raise ValueError( + "Cleaning-method provenance must use the same " + "columns as the load data." + ) + + missing_observed_provenance = ( + load.notna() + & cleaning_method.isna() + ) + + if missing_observed_provenance.any().any(): + count = int( + missing_observed_provenance.to_numpy().sum() + ) + + raise ValueError( + "Cleaning-method provenance is missing for " + f"{count} observed load values." + ) + + provenance_for_missing_values = ( + load.isna() + & cleaning_method.notna() + ) + + if provenance_for_missing_values.any().any(): + count = int( + provenance_for_missing_values.to_numpy().sum() + ) + + raise ValueError( + "Cleaning-method provenance is already assigned " + f"to {count} missing load values." + ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py new file mode 100644 index 0000000..9874cf9 --- /dev/null +++ b/workflow/scripts/cleaning/provenance.py @@ -0,0 +1,106 @@ +"""Cleaning-method provenance and ranking helpers.""" + +from collections import Counter +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + + +def validate_rule_names( + *, + rules: Sequence[Mapping[str, Any]], + source_priority: Sequence[str], +) -> None: + """Validate rule-name uniqueness and reserved-name collisions.""" + names = [str(rule["name"]) for rule in rules] + + counts = Counter(names) + duplicates = sorted( + name + for name, count in counts.items() + if count > 1 + ) + + if duplicates: + raise ValueError( + "Gap-filling rule names must be unique. " + f"Duplicate names: {duplicates}" + ) + + reserved_names = { + "missing", + *( + f"observed_{source_name}" + for source_name in source_priority + ), + } + + collisions = sorted( + set(names) & reserved_names + ) + + if collisions: + raise ValueError( + "Gap-filling rule names conflict with reserved " + f"cleaning-method names: {collisions}" + ) + + +def build_cleaning_method_ranks( + *, + source_priority: Sequence[str], + rules: Sequence[Mapping[str, Any]], +) -> dict[str, int]: + """Build cleaning-method ranks from configured order.""" + validate_rule_names( + rules=rules, + source_priority=source_priority, + ) + + ranks: dict[str, int] = {} + + for rank, source_name in enumerate(source_priority): + ranks[f"observed_{source_name}"] = rank + + first_gap_filling_rank = len(source_priority) + + for rule_position, rule in enumerate(rules): + ranks[str(rule["name"])] = ( + first_gap_filling_rank + + rule_position + ) + + ranks["missing"] = ( + len(source_priority) + + len(rules) + ) + + return ranks + + +def derive_cleaning_method_rank( + *, + cleaning_method: pd.DataFrame, + ranks: Mapping[str, int], +) -> pd.DataFrame: + """Translate cleaning-method names to integer ranks.""" + present_methods = set( + cleaning_method.stack().astype(str).unique() + ) + + unknown_methods = sorted( + present_methods - set(ranks) + ) + + if unknown_methods: + raise ValueError( + "No cleaning-method rank is defined for: " + f"{unknown_methods}" + ) + + cleaning_method_rank = cleaning_method.replace( + ranks + ) + + return cleaning_method_rank.astype("int16") \ No newline at end of file From 67a7629e590730e1cab0efc957eadb041654052d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 16:56:55 +0200 Subject: [PATCH 009/241] Updates to deliver gap filling plotting logic and to force snakemake to redownload when data params change --- workflow/rules/automatic.smk | 2 + workflow/rules/clean.smk | 29 + .../cleaning/plot_cleaning_timeline.py | 737 ++++++++++++++++++ 3 files changed, 768 insertions(+) create mode 100644 workflow/scripts/cleaning/plot_cleaning_timeline.py diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 88a57aa..9b37521 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -13,6 +13,8 @@ rule download_load_entsoe_api: "../envs/module.yaml" params: country_codes=internal["load_entsoe_api"]["countries"], + temporal_start=config["temporal_scope"]["start"], + temporal_end=config["temporal_scope"]["end"], message: "Download electricity load from ENTSOE." script: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 18a5b6a..2481e5e 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -69,6 +69,34 @@ rule clean_demand: "../scripts/clean_demand.py" +rule plot_cleaning_timeline: + input: + demand=( + "/automatic/" + "load_cleaned.parquet" + ), + cleaning_method=( + "/automatic/" + "load_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/" + "load_cleaning_method_rank.parquet" + ), + output: + plot=( + "/{shape}/" + "load_cleaning_timeline.pdf" + ), + log: + "/{shape}/plot_cleaning_timeline.log", + conda: + "../envs/module.yaml" + message: + "Plot electricity-demand cleaning provenance." + script: + "../scripts/cleaning/plot_cleaning_timeline.py" + rule clean_population: input: vector="", @@ -79,3 +107,4 @@ rule clean_population: "/{shape}/clean_population.log", wrapper: "v7.2.0/geo/rasterio/clip-geotiff" + diff --git a/workflow/scripts/cleaning/plot_cleaning_timeline.py b/workflow/scripts/cleaning/plot_cleaning_timeline.py new file mode 100644 index 0000000..928d270 --- /dev/null +++ b/workflow/scripts/cleaning/plot_cleaning_timeline.py @@ -0,0 +1,737 @@ +"""Plot electricity demand and cleaning-method provenance through time.""" + +import logging +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import matplotlib.dates as mdates +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from matplotlib.colors import ListedColormap, to_rgba +from matplotlib.lines import Line2D +from matplotlib.patches import Patch + +if TYPE_CHECKING: + snakemake: Any + +logger = logging.getLogger(__name__) + + +def main( + *, + demand_path: str | Path, + cleaning_method_path: str | Path, + cleaning_method_rank_path: str | Path, + output_path: str | Path, +) -> None: + """Create the electricity-demand cleaning diagnostic.""" + demand = pd.read_parquet(demand_path) + cleaning_method = pd.read_parquet(cleaning_method_path) + cleaning_method_rank = pd.read_parquet( + cleaning_method_rank_path + ) + + _validate_alignment( + demand=demand, + cleaning_method=cleaning_method, + cleaning_method_rank=cleaning_method_rank, + ) + + metadata = _build_rank_metadata( + cleaning_method=cleaning_method, + cleaning_method_rank=cleaning_method_rank, + ) + + rank_colours = _build_rank_colours(metadata) + + background, background_cmap = ( + _encode_rank_background( + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + rank_colours=rank_colours, + ) + ) + + logger.info( + "Loaded %s timestamps for %s countries.", + len(demand), + len(demand.columns), + ) + + logger.info( + "Cleaning-method ranks:\n%s", + metadata.to_string(index=False), + ) + + figure, axis = _plot_cleaning_background( + demand=demand, + background=background, + background_cmap=background_cmap, + ) + + mean_load_gw = _add_normalised_demand_traces( + axis=axis, + demand=demand, + ) + + _add_mean_load_labels( + axis=axis, + mean_load_gw=mean_load_gw, + countries=demand.columns, + ) + + _add_dynamic_legend( + figure=figure, + metadata=metadata, + rank_colours=rank_colours, + ) + + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + figure.savefig( + output_path, + bbox_inches="tight", + ) + + plt.close(figure) + + logger.info( + "Saved cleaning timeline to %s.", + output_path, + ) + + +def _validate_alignment( + *, + demand: pd.DataFrame, + cleaning_method: pd.DataFrame, + cleaning_method_rank: pd.DataFrame, +) -> None: + """Require all plot inputs to use the same time-country grid.""" + for name, frame in { + "cleaning_method": cleaning_method, + "cleaning_method_rank": cleaning_method_rank, + }.items(): + if not frame.index.equals(demand.index): + raise ValueError( + f"{name} does not use the same time index " + "as demand." + ) + + if not frame.columns.equals(demand.columns): + raise ValueError( + f"{name} does not use the same country columns " + "as demand." + ) + + if not isinstance(demand.index, pd.DatetimeIndex): + raise TypeError( + "Demand must use a pandas DatetimeIndex." + ) + + if demand.index.has_duplicates: + raise ValueError( + "Demand timestamps must not contain duplicates." + ) + + if not demand.index.is_monotonic_increasing: + raise ValueError( + "Demand timestamps must be sorted." + ) + + if demand.columns.has_duplicates: + raise ValueError( + "Demand countries must not contain duplicates." + ) + + +def _build_rank_metadata( + *, + cleaning_method: pd.DataFrame, + cleaning_method_rank: pd.DataFrame, +) -> pd.DataFrame: + """Return one ordered record for each method and rank.""" + methods = cleaning_method.stack( + future_stack=True + ).rename("cleaning_method") + + ranks = cleaning_method_rank.stack( + future_stack=True + ).rename("cleaning_method_rank") + + metadata = ( + pd.concat( + [methods, ranks], + axis=1, + ) + .dropna() + .drop_duplicates() + ) + + if metadata.empty: + raise ValueError( + "No cleaning-method provenance was found." + ) + + metadata["cleaning_method_rank"] = metadata[ + "cleaning_method_rank" + ].astype(int) + + rank_count_per_method = ( + metadata.groupby("cleaning_method")[ + "cleaning_method_rank" + ] + .nunique() + ) + + methods_with_multiple_ranks = ( + rank_count_per_method.loc[ + rank_count_per_method > 1 + ] + .index + .tolist() + ) + + if methods_with_multiple_ranks: + raise ValueError( + "Cleaning methods must map to exactly one rank. " + "Methods with multiple ranks: " + f"{methods_with_multiple_ranks}" + ) + + method_count_per_rank = ( + metadata.groupby("cleaning_method_rank")[ + "cleaning_method" + ] + .nunique() + ) + + ranks_with_multiple_methods = ( + method_count_per_rank.loc[ + method_count_per_rank > 1 + ] + .index + .tolist() + ) + + if ranks_with_multiple_methods: + raise ValueError( + "Cleaning-method ranks must map to exactly one " + "method. Ranks with multiple methods: " + f"{ranks_with_multiple_methods}" + ) + + metadata["category"] = metadata[ + "cleaning_method" + ].map(_classify_method) + + return ( + metadata.sort_values("cleaning_method_rank") + .reset_index(drop=True) + ) + + +def _classify_method(method: str) -> str: + """Classify one method for semantic colour assignment.""" + if method == "missing": + return "missing" + + if method.startswith("observed_"): + return "observed" + + return "imputed" + + +def _build_rank_colours( + metadata: pd.DataFrame, +) -> dict[int, tuple[float, float, float, float]]: + """Assign colours according to method category and rank.""" + colours: dict[ + int, + tuple[float, float, float, float], + ] = {} + + observed = metadata.loc[ + metadata["category"] == "observed" + ].sort_values("cleaning_method_rank") + + imputed = metadata.loc[ + metadata["category"] == "imputed" + ].sort_values("cleaning_method_rank") + + missing = metadata.loc[ + metadata["category"] == "missing" + ].sort_values("cleaning_method_rank") + + if not observed.empty: + principal_rank = int( + observed.iloc[0]["cleaning_method_rank"] + ) + + # The preferred observed source is visually neutral. + colours[principal_rank] = to_rgba("white") + + fallback_observed = observed.iloc[1:] + + if not fallback_observed.empty: + green_positions = np.linspace( + 0.3, + 0.6, + len(fallback_observed), + ) + + for (_, row), position in zip( + fallback_observed.iterrows(), + green_positions, + strict=True, + ): + rank = int( + row["cleaning_method_rank"] + ) + + colours[rank] = plt.colormaps[ + "Greens" + ](position) + + if not imputed.empty: + imputation_positions = np.linspace( + 0.35, + 0.8, + len(imputed), + ) + + for (_, row), position in zip( + imputed.iterrows(), + imputation_positions, + strict=True, + ): + rank = int( + row["cleaning_method_rank"] + ) + + colours[rank] = plt.colormaps[ + "YlOrBr" + ](position) + + for _, row in missing.iterrows(): + rank = int( + row["cleaning_method_rank"] + ) + + colours[rank] = plt.colormaps[ + "Reds" + ](0.8) + + expected_ranks = set( + metadata["cleaning_method_rank"] + ) + + unassigned_ranks = ( + expected_ranks - set(colours) + ) + + if unassigned_ranks: + raise ValueError( + "No colour was assigned to cleaning-method " + "ranks: " + f"{sorted(unassigned_ranks)}" + ) + + return colours + + +def _encode_rank_background( + *, + cleaning_method_rank: pd.DataFrame, + metadata: pd.DataFrame, + rank_colours: dict[ + int, + tuple[float, float, float, float], + ], +) -> tuple[np.ndarray, ListedColormap]: + """Encode ranks as contiguous plotting codes.""" + rank_order = ( + metadata["cleaning_method_rank"] + .astype(int) + .tolist() + ) + + rank_to_code = { + rank: code + for code, rank in enumerate(rank_order) + } + + encoded = cleaning_method_rank.apply( + lambda column: column.map(rank_to_code) + ) + + if encoded.isna().any().any(): + present_ranks = set( + cleaning_method_rank.stack( + future_stack=True + ) + .dropna() + .astype(int) + .unique() + ) + + unknown_ranks = sorted( + present_ranks - set(rank_to_code) + ) + + raise ValueError( + "Cleaning-method rank matrix contains ranks " + "without metadata: " + f"{unknown_ranks}" + ) + + colour_list = [ + rank_colours[rank] + for rank in rank_order + ] + + # Input frames are time × country, whereas imshow expects + # country × time for this figure orientation. + background = encoded.to_numpy( + dtype=int + ).T + + return ( + background, + ListedColormap(colour_list), + ) + + +def _plot_cleaning_background( + *, + demand: pd.DataFrame, + background: np.ndarray, + background_cmap: ListedColormap, +) -> tuple[plt.Figure, plt.Axes]: + """Plot cleaning-method ranks over time by country.""" + country_count = len(demand.columns) + + if len(demand.index) < 2: + raise ValueError( + "At least two timestamps are required to plot " + "the cleaning timeline." + ) + + time_step = ( + demand.index.to_series() + .diff() + .dropna() + .median() + ) + + if ( + pd.isna(time_step) + or time_step <= pd.Timedelta(0) + ): + raise ValueError( + "Could not determine a valid temporal resolution." + ) + + start = demand.index[0] + end = demand.index[-1] + time_step + + figure_height = max( + 6.0, + country_count * 0.3, + ) + + figure, axis = plt.subplots( + figsize=(16, figure_height), + constrained_layout=True, + ) + + axis.imshow( + background, + aspect="auto", + interpolation="nearest", + cmap=background_cmap, + extent=( + mdates.date2num(start), + mdates.date2num(end), + country_count - 0.5, + -0.5, + ), + rasterized=True, + zorder=0, + ) + + row_centres = np.arange(country_count) + + axis.set_yticks(row_centres) + axis.set_yticklabels(demand.columns) + + axis.set_xlim(start, end) + axis.set_ylim( + country_count - 0.5, + -0.5, + ) + + # Light boundaries make individual country strips clear + # without obscuring the provenance colours. + axis.set_yticks( + np.arange(-0.5, country_count, 1), + minor=True, + ) + + axis.grid( + axis="y", + which="minor", + linewidth=0.4, + alpha=0.35, + ) + + axis.tick_params( + axis="y", + which="minor", + left=False, + ) + + axis.set_xlabel("Time") + axis.set_ylabel("Country") + + date_locator = mdates.AutoDateLocator( + minticks=4, + maxticks=12, + ) + + axis.xaxis.set_major_locator(date_locator) + axis.xaxis.set_major_formatter( + mdates.ConciseDateFormatter( + date_locator + ) + ) + + axis.set_title( + "Electricity demand and cleaning provenance" + ) + + return figure, axis + + +def _add_normalised_demand_traces( + *, + axis: plt.Axes, + demand: pd.DataFrame, + half_height: float = 0.35, + quantile: float = 0.99, +) -> dict[str, float]: + """Overlay mean-normalised hourly demand traces.""" + mean_load_gw: dict[str, float] = {} + + for row_index, country in enumerate( + demand.columns + ): + series = demand[country].astype(float) + + mean_load = series.mean(skipna=True) + mean_load_gw[country] = mean_load / 1000 + + if pd.isna(mean_load) or mean_load == 0: + continue + + relative = (series / mean_load) - 1 + + scale = relative.abs().quantile( + quantile + ) + + if pd.isna(scale) or scale == 0: + plotted_y = pd.Series( + row_index, + index=series.index, + dtype=float, + ) + else: + scaled = relative.clip( + lower=-scale, + upper=scale, + ) / scale + + # The y-axis is inverted, so subtracting makes + # above-average demand appear visually upward. + plotted_y = ( + row_index + - scaled * half_height + ) + + axis.plot( + series.index, + plotted_y, + color="black", + linewidth=0.6, + alpha=0.9, + zorder=3, + ) + + return mean_load_gw + + +def _add_mean_load_labels( + *, + axis: plt.Axes, + mean_load_gw: dict[str, float], + countries: pd.Index, +) -> None: + """Annotate country rows with mean load in GW.""" + for row_index, country in enumerate(countries): + mean_value = mean_load_gw[country] + + label = ( + "—" + if pd.isna(mean_value) + else f"{mean_value:.1f}" + ) + + axis.text( + 1.01, + row_index, + label, + transform=axis.get_yaxis_transform(), + ha="left", + va="center", + fontsize=8, + clip_on=False, + ) + + axis.text( + 1.01, + 1.01, + "Mean\n(GW)", + transform=axis.transAxes, + ha="left", + va="bottom", + fontsize=8, + ) + + +def _add_dynamic_legend( + *, + figure: plt.Figure, + metadata: pd.DataFrame, + rank_colours: dict[ + int, + tuple[float, float, float, float], + ], +) -> None: + """Add a rank-ordered method legend.""" + handles: list[Patch | Line2D] = [ + Line2D( + [0], + [0], + color="black", + linewidth=1, + label="Mean-normalised hourly demand", + ) + ] + + for row in metadata.itertuples( + index=False + ): + rank = int( + row.cleaning_method_rank + ) + method = str( + row.cleaning_method + ) + + colour = rank_colours[rank] + + # A border keeps the white principal-source patch + # visible in the legend. + edge_colour = ( + "0.65" + if row.category == "observed" + and rank == metadata[ + "cleaning_method_rank" + ].min() + else "none" + ) + + handles.append( + Patch( + facecolor=colour, + edgecolor=edge_colour, + linewidth=0.8, + label=( + f"Rank {rank}: " + f"{_format_method_label(method)}" + ), + ) + ) + + figure.legend( + handles=handles, + loc="outside lower center", + ncols=min( + 4, + len(handles), + ), + frameon=True, + fontsize=8, + ) + + +def _format_method_label( + method: str, +) -> str: + """Convert a method identifier into a legend label.""" + if method == "missing": + return "Missing" + + if method.startswith("observed_"): + source = method.removeprefix( + "observed_" + ) + + source_labels = { + "entsoe_api": "Observed: ENTSO-E API", + "opsd_api": "Observed: OPSD API", + } + + return source_labels.get( + source, + ( + "Observed: " + + source.replace("_", " ").upper() + ), + ) + + return method.replace( + "_", + " ", + ).capitalize() + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + main( + demand_path=snakemake.input.demand, + cleaning_method_path=( + snakemake.input.cleaning_method + ), + cleaning_method_rank_path=( + snakemake.input.cleaning_method_rank + ), + output_path=snakemake.output.plot, + ) \ No newline at end of file From 6bdfb24a64c5cd8d3f83d1b869e28c6097aef67f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 4 Aug 2026 22:53:34 +0200 Subject: [PATCH 010/241] update config schema as accidentally removed rules in previous patch --- workflow/internal/config.schema.yaml | 97 ++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 438873a..a6c951d 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -1,12 +1,18 @@ $schema: "https://json-schema.org/draft/2020-12/schema" -description: "Schema for user-provided configuration files." + +description: > + Schema for user-provided configuration files for the electricity-demand + module. + +type: object $defs: iso_date: type: string format: date pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$' - description: "Date in ISO 8601 format (YYYY-MM-DD)." + description: > + Date in ISO 8601 format (YYYY-MM-DD). duration: type: string @@ -30,9 +36,12 @@ $defs: description: > Unique identifier for this configured gap-filling rule. Used for provenance reporting and cleaning-method ranking. + method: type: string - description: Gap-filling method applied by this rule. + description: > + Gap-filling method applied by this rule. + required: - name - method @@ -40,36 +49,116 @@ $defs: linear_interpolation_rule: allOf: - $ref: "#/$defs/gap_filling_rule_base" + - type: object properties: method: const: linear_interpolation + max_gap: $ref: "#/$defs/duration" + required: - max_gap + unevaluatedProperties: false copy_period_rule: allOf: - $ref: "#/$defs/gap_filling_rule_base" + - type: object properties: method: const: copy_period + max_gap: $ref: "#/$defs/duration" + source_offset: $ref: "#/$defs/signed_duration" + require_complete_source: type: boolean default: true + description: > + Require all source-period values needed for a gap to be + available before filling that gap. + required: - max_gap - source_offset + unevaluatedProperties: false gap_filling_rule: oneOf: - $ref: "#/$defs/linear_interpolation_rule" - - $ref: "#/$defs/copy_period_rule" \ No newline at end of file + - $ref: "#/$defs/copy_period_rule" + + +properties: + temporal_scope: + type: object + description: > + End-exclusive temporal scope for the electricity-demand time series. + + properties: + start: + $ref: "#/$defs/iso_date" + + end: + $ref: "#/$defs/iso_date" + + required: + - start + - end + + additionalProperties: false + + load_sources: + type: array + description: > + Ordered electricity-demand sources. Sources listed earlier have + higher priority when more than one source supplies the same value. + + minItems: 1 + uniqueItems: true + + items: + type: string + enum: + - entsoe_api + - neso + - opsd_api + + gap_filling: + type: object + description: > + Ordered gap-filling configuration. Rules are applied in the order + provided, and that order contributes to cleaning-method ranking. + + properties: + enabled: + type: boolean + default: true + + rules: + type: array + default: [] + items: + $ref: "#/$defs/gap_filling_rule" + + required: + - enabled + - rules + + additionalProperties: false + + +required: + - temporal_scope + - load_sources + - gap_filling + +additionalProperties: false \ No newline at end of file From 508a51b282c16e3eb89cc4307dc75aad8f3ae8f0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 5 Aug 2026 10:49:57 +0200 Subject: [PATCH 011/241] Added NESO integration to pull in GB data, updated the plotting function with improved colour-blind friendliness, and updated pixi env accordingly to include cmap --- config/config.yaml | 1 + pixi.lock | 26 +- pixi.toml | 1 + tests/integration/test_config.yaml | 5 +- workflow/envs/module.linux-64.pin.txt | 1 + workflow/envs/module.osx-arm64.pin.txt | 1 + workflow/envs/module.win-64.pin.txt | 1 + workflow/envs/module.yaml | 1 + workflow/rules/automatic.smk | 56 ++- workflow/rules/clean.smk | 28 +- workflow/scripts/cleaning/neso.py | 91 ++++ .../cleaning/plot_cleaning_timeline.py | 414 +++++++++--------- workflow/scripts/download_load_neso.py | 267 +++++++++++ workflow/scripts/prepare_load_neso.py | 284 ++++++++++++ 14 files changed, 952 insertions(+), 225 deletions(-) create mode 100644 workflow/scripts/cleaning/neso.py create mode 100644 workflow/scripts/download_load_neso.py create mode 100644 workflow/scripts/prepare_load_neso.py diff --git a/config/config.yaml b/config/config.yaml index 8e7362c..780e31f 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -7,6 +7,7 @@ temporal_scope: # Options: entsoe_api, opsd_api load_sources: - entsoe_api + - neso - opsd_api # Gap-filling rules are applied in the listed order. diff --git a/pixi.lock b/pixi.lock index 72b12c5..6fba675 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1,20 +1,8 @@ version: 7 platforms: - name: linux-64 - virtual-packages: - - __unix=0=0 - - __linux=4.18 - - __glibc=2.28 - - __archspec=0=x86_64 - name: osx-arm64 - virtual-packages: - - __unix=0=0 - - __osx=13.0 - - __archspec=0=m1 - name: win-64 - virtual-packages: - - __win=10.0 - - __archspec=0=x86_64 environments: default: channels: @@ -1065,6 +1053,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda @@ -1138,6 +1127,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda @@ -1399,6 +1389,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda @@ -6959,6 +6950,17 @@ packages: run_exports: {} size: 27353 timestamp: 1765303462831 +- conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda + sha256: 4336667af7d00b19b2d0d8ca5a227dcf20fd40ca8c44f1dc360790e8a8060320 + md5: 992b4902eb5ff13646048cd7629a82bf + depends: + - numpy + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 678920 + timestamp: 1775317331599 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 md5: 962b9857ee8e7018c22f2776ffa0b2d7 diff --git a/pixi.toml b/pixi.toml index 7aac49e..e512f3d 100644 --- a/pixi.toml +++ b/pixi.toml @@ -32,6 +32,7 @@ entsoe-py = ">=0.8.0" dask = ">=2026.6.0" numpy = ">=1.26.4" gregor = ">=0.1.0" +cmap = ">=0.7.2" [environments] module = { features = ["module"], no-default-feature = true } diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 4d8fcaf..03579ff 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,10 +1,11 @@ integration_test: temporal_scope: - start: "2017-01-01" - end: "2017-01-03" + start: "2021-01-01" + end: "2021-12-31" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api + - neso - opsd_api gap_filling: diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 5484a75..193d65e 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -274,3 +274,4 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda#2e7dedf73dfbfcee662e2a0f6175e4bb +https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index 535e460..4545170 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -261,3 +261,4 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.9.1-hbf5303f_0.conda#93440b8d934e90496e1939ecd72cbf0c +https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index d294585..12a1611 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -254,3 +254,4 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda#45d3504d24c6a853f22bb93d721d9d20 +https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index 86aaa08..ee07bab 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -13,3 +13,4 @@ dependencies: - dask >=2026.6.0 - numpy >=1.26.4 - gregor >=0.1.0 +- cmap >=0.7.2 diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 9b37521..f126da2 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,5 +1,45 @@ -"""Rules to used to download automatic resource files.""" +"""Rules used to download automatic resource files.""" +from datetime import datetime, timedelta + +def _years_in_temporal_scope( + temporal_start: str, + temporal_end: str, +) -> tuple[int, ...]: + """Return calendar years intersecting an end-exclusive scope.""" + start = datetime.fromisoformat(temporal_start) + end = datetime.fromisoformat(temporal_end) + + if end <= start: + raise ValueError( + "temporal_scope.end must be later than " + "temporal_scope.start." + ) + + # The configured end is exclusive. Subtract a very small + # amount so 2025-01-01 does not require the 2025 file. + final_included_time = end - timedelta( + microseconds=1 + ) + + return tuple( + range( + start.year, + final_included_time.year + 1, + ) + ) + + +NESO_YEARS = _years_in_temporal_scope( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], +) + +NESO_RAW_FILES = expand( + "/automatic/neso/" + "historic_demand_{year}.csv", + year=NESO_YEARS, +) rule download_load_entsoe_api: input: @@ -38,6 +78,20 @@ rule download_load_entsoe_opsd: curl -sSLo {output.load:q} {params.url_load:q} """ +rule download_load_neso: + output: + annual_files=NESO_RAW_FILES, + params: + years=NESO_YEARS, + log: + "/download_load_neso.log", + localrule: True + conda: + "../envs/module.yaml" + message: + "Download historic electricity demand from NESO." + script: + "../scripts/download_load_neso.py" rule download_population: output: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 2481e5e..bcbfe1e 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -17,11 +17,34 @@ rule prepare_load_opsd: "../scripts/prepare_load_opsd.py" +rule prepare_load_neso: + input: + annual_files=rules.download_load_neso.output.annual_files, + output: + load="/automatic/load_neso.parquet", + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe_api"]["countries"], + log: + "/prepare_load_neso.log", + conda: + "../envs/module.yaml" + message: + "Prepare electricity-demand data from NESO." + script: + "../scripts/prepare_load_neso.py" + + LOAD_SOURCE_PATHS = { "entsoe_api": ( "/automatic/" "load_entsoe_api.parquet" ), + "neso": ( + "/automatic/" + "load_neso.parquet" + ), "opsd_api": ( "/automatic/" "load_opsd_api.parquet" @@ -29,7 +52,7 @@ LOAD_SOURCE_PATHS = { } -def configured_load_inputs(wildcards): +def configured_load_inputs(_wildcards): return [ LOAD_SOURCE_PATHS[source_name] for source_name in config["load_sources"] @@ -88,6 +111,9 @@ rule plot_cleaning_timeline: "/{shape}/" "load_cleaning_timeline.pdf" ), + params: + source_names=config["load_sources"], + gap_filling=config["gap_filling"], log: "/{shape}/plot_cleaning_timeline.log", conda: diff --git a/workflow/scripts/cleaning/neso.py b/workflow/scripts/cleaning/neso.py new file mode 100644 index 0000000..da9dd88 --- /dev/null +++ b/workflow/scripts/cleaning/neso.py @@ -0,0 +1,91 @@ +"""Prepare NESO settlement-period data.""" + +import pandas as pd + + +def add_utc_timestamps( + data: pd.DataFrame, +) -> pd.DataFrame: + """Convert NESO settlement dates and periods to UTC timestamps.""" + required_columns = { + "SETTLEMENT_DATE", + "SETTLEMENT_PERIOD", + "ND", + } + + missing_columns = required_columns - set(data.columns) + + if missing_columns: + raise ValueError( + "NESO data are missing required columns: " + f"{sorted(missing_columns)}" + ) + + prepared = data.copy() + + prepared["SETTLEMENT_DATE"] = pd.to_datetime( + prepared["SETTLEMENT_DATE"], + errors="raise", + ).dt.normalize() + + prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( + prepared["SETTLEMENT_PERIOD"], + errors="raise", + ).astype(int) + + timestamp_parts: list[pd.Series] = [] + + for settlement_date, day in prepared.groupby( + "SETTLEMENT_DATE", + sort=True, + ): + day = day.sort_values("SETTLEMENT_PERIOD").copy() + + expected_periods = list(range(1, len(day) + 1)) + observed_periods = day["SETTLEMENT_PERIOD"].tolist() + + if observed_periods != expected_periods: + raise ValueError( + "NESO settlement periods are not consecutive for " + f"{settlement_date.date()}. Expected 1-{len(day)}." + ) + + local_start = pd.Timestamp( + settlement_date, + tz="Europe/London", + ) + local_end = local_start + pd.DateOffset(days=1) + + expected_index = pd.date_range( + start=local_start, + end=local_end, + freq="30min", + inclusive="left", + ) + + if len(day) != len(expected_index): + raise ValueError( + "NESO settlement-period count does not match the " + "Europe/London clock for " + f"{settlement_date.date()}: " + f"{len(day)} records versus " + f"{len(expected_index)} expected." + ) + + timestamp_parts.append( + pd.Series( + expected_index, + index=day.index, + name="timestamp", + ) + ) + + prepared["timestamp"] = pd.concat( + timestamp_parts + ).sort_index() + + prepared["timestamp"] = ( + prepared["timestamp"].dt.tz_convert("UTC") + ) + + return prepared.sort_values("timestamp") \ No newline at end of file diff --git a/workflow/scripts/cleaning/plot_cleaning_timeline.py b/workflow/scripts/cleaning/plot_cleaning_timeline.py index 928d270..808d781 100644 --- a/workflow/scripts/cleaning/plot_cleaning_timeline.py +++ b/workflow/scripts/cleaning/plot_cleaning_timeline.py @@ -9,8 +9,8 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd +from cmap import Colormap from matplotlib.colors import ListedColormap, to_rgba -from matplotlib.lines import Line2D from matplotlib.patches import Patch if TYPE_CHECKING: @@ -25,6 +25,8 @@ def main( cleaning_method_path: str | Path, cleaning_method_rank_path: str | Path, output_path: str | Path, + source_names: list[str], + gap_filling_config: dict[str, Any], ) -> None: """Create the electricity-demand cleaning diagnostic.""" demand = pd.read_parquet(demand_path) @@ -39,9 +41,15 @@ def main( cleaning_method_rank=cleaning_method_rank, ) - metadata = _build_rank_metadata( + metadata = _build_cleaning_method_metadata( + source_names=source_names, + gap_filling_config=gap_filling_config, + ) + + _validate_provenance_metadata( cleaning_method=cleaning_method, cleaning_method_rank=cleaning_method_rank, + metadata=metadata, ) rank_colours = _build_rank_colours(metadata) @@ -82,10 +90,16 @@ def main( countries=demand.columns, ) - _add_dynamic_legend( - figure=figure, - metadata=metadata, - rank_colours=rank_colours, + legend_handles = _build_legend_handles( + metadata, + rank_colours, + ) + + figure.legend( + handles=legend_handles, + loc="center left", + bbox_to_anchor=(1.01, 0.5), + frameon=False, ) output_path = Path(output_path) @@ -107,6 +121,35 @@ def main( ) +def _build_legend_handles( + metadata: pd.DataFrame, + rank_colours: dict[ + int, + tuple[float, float, float, float], + ], +) -> list[Patch]: + """Create handles for every configured rank.""" + handles: list[Patch] = [] + + ordered = metadata.sort_values( + "cleaning_method_rank" + ) + + for row in ordered.itertuples(index=False): + rank = int(row.cleaning_method_rank) + + handles.append( + Patch( + facecolor=rank_colours[rank], + edgecolor="0.65", + linewidth=0.8, + label=str(row.label), + ) + ) + + return handles + + def _validate_alignment( *, demand: pd.DataFrame, @@ -151,12 +194,13 @@ def _validate_alignment( ) -def _build_rank_metadata( +def _validate_provenance_metadata( *, cleaning_method: pd.DataFrame, cleaning_method_rank: pd.DataFrame, -) -> pd.DataFrame: - """Return one ordered record for each method and rank.""" + metadata: pd.DataFrame, +) -> None: + """Validate observed provenance against configured metadata.""" methods = cleaning_method.stack( future_stack=True ).rename("cleaning_method") @@ -165,7 +209,7 @@ def _build_rank_metadata( future_stack=True ).rename("cleaning_method_rank") - metadata = ( + present = ( pd.concat( [methods, ranks], axis=1, @@ -174,173 +218,129 @@ def _build_rank_metadata( .drop_duplicates() ) - if metadata.empty: - raise ValueError( - "No cleaning-method provenance was found." - ) - - metadata["cleaning_method_rank"] = metadata[ + present["cleaning_method_rank"] = present[ "cleaning_method_rank" ].astype(int) - rank_count_per_method = ( - metadata.groupby("cleaning_method")[ - "cleaning_method_rank" + configured = metadata[ + [ + "cleaning_method", + "cleaning_method_rank", ] - .nunique() - ) - - methods_with_multiple_ranks = ( - rank_count_per_method.loc[ - rank_count_per_method > 1 - ] - .index - .tolist() - ) - - if methods_with_multiple_ranks: - raise ValueError( - "Cleaning methods must map to exactly one rank. " - "Methods with multiple ranks: " - f"{methods_with_multiple_ranks}" - ) - - method_count_per_rank = ( - metadata.groupby("cleaning_method_rank")[ - "cleaning_method" - ] - .nunique() - ) + ] - ranks_with_multiple_methods = ( - method_count_per_rank.loc[ - method_count_per_rank > 1 - ] - .index - .tolist() - ) + checked = present.merge( + configured, + on=[ + "cleaning_method", + "cleaning_method_rank", + ], + how="left", + indicator=True, + ) + + unknown = checked.loc[ + checked["_merge"] == "left_only", + [ + "cleaning_method", + "cleaning_method_rank", + ], + ] - if ranks_with_multiple_methods: + if not unknown.empty: raise ValueError( - "Cleaning-method ranks must map to exactly one " - "method. Ranks with multiple methods: " - f"{ranks_with_multiple_methods}" + "Cleaning provenance contains method/rank pairs " + "that are not defined by the configuration:\n" + f"{unknown.to_string(index=False)}" ) - metadata["category"] = metadata[ - "cleaning_method" - ].map(_classify_method) - - return ( - metadata.sort_values("cleaning_method_rank") - .reset_index(drop=True) - ) - - -def _classify_method(method: str) -> str: - """Classify one method for semantic colour assignment.""" - if method == "missing": - return "missing" - - if method.startswith("observed_"): - return "observed" - - return "imputed" - def _build_rank_colours( metadata: pd.DataFrame, ) -> dict[int, tuple[float, float, float, float]]: - """Assign colours according to method category and rank.""" + """Assign colours by provenance category.""" colours: dict[ int, tuple[float, float, float, float], ] = {} - observed = metadata.loc[ - metadata["category"] == "observed" - ].sort_values("cleaning_method_rank") - - imputed = metadata.loc[ - metadata["category"] == "imputed" - ].sort_values("cleaning_method_rank") + ordered = metadata.sort_values( + "cleaning_method_rank" + ) - missing = metadata.loc[ - metadata["category"] == "missing" - ].sort_values("cleaning_method_rank") + observed = ordered.loc[ + ordered["category"] == "observed" + ] - if not observed.empty: - principal_rank = int( - observed.iloc[0]["cleaning_method_rank"] - ) + imputed = ordered.loc[ + ordered["category"] == "imputed" + ] - # The preferred observed source is visually neutral. - colours[principal_rank] = to_rgba("white") + missing = ordered.loc[ + ordered["category"] == "missing" + ] - fallback_observed = observed.iloc[1:] + if observed.empty: + raise ValueError( + "At least one observed source must be configured." + ) - if not fallback_observed.empty: - green_positions = np.linspace( - 0.3, - 0.6, - len(fallback_observed), - ) + # Primary source is white. Subsequent observed sources + # become gradually darker, but remain very light so that + # the black demand trace stays clearly visible. + observed_shades = np.linspace( + 1.0, + 0.60, + len(observed), + ) - for (_, row), position in zip( - fallback_observed.iterrows(), - green_positions, - strict=True, - ): - rank = int( - row["cleaning_method_rank"] - ) + for (_, row), shade in zip( + observed.iterrows(), + observed_shades, + strict=True, + ): + rank = int(row["cleaning_method_rank"]) - colours[rank] = plt.colormaps[ - "Greens" - ](position) + colours[rank] = ( + float(shade), + float(shade), + float(shade), + 1.0, + ) if not imputed.empty: - imputation_positions = np.linspace( - 0.35, - 0.8, + colourtheme = Colormap( + "bids:plasma" + ).to_mpl() + + positions = np.linspace( + 0.0, + 0.9, len(imputed), ) for (_, row), position in zip( imputed.iterrows(), - imputation_positions, + positions, strict=True, ): - rank = int( - row["cleaning_method_rank"] - ) - - colours[rank] = plt.colormaps[ - "YlOrBr" - ](position) + rank = int(row["cleaning_method_rank"]) + colours[rank] = colourtheme(position) for _, row in missing.iterrows(): - rank = int( - row["cleaning_method_rank"] - ) - - colours[rank] = plt.colormaps[ - "Reds" - ](0.8) + rank = int(row["cleaning_method_rank"]) + colours[rank] = to_rgba("#ff0000") expected_ranks = set( - metadata["cleaning_method_rank"] + metadata["cleaning_method_rank"].astype(int) ) - unassigned_ranks = ( - expected_ranks - set(colours) - ) + missing_colours = expected_ranks - set(colours) - if unassigned_ranks: + if missing_colours: raise ValueError( - "No colour was assigned to cleaning-method " - "ranks: " - f"{sorted(unassigned_ranks)}" + "No colour was assigned to ranks: " + f"{sorted(missing_colours)}" ) return colours @@ -615,73 +615,6 @@ def _add_mean_load_labels( ) -def _add_dynamic_legend( - *, - figure: plt.Figure, - metadata: pd.DataFrame, - rank_colours: dict[ - int, - tuple[float, float, float, float], - ], -) -> None: - """Add a rank-ordered method legend.""" - handles: list[Patch | Line2D] = [ - Line2D( - [0], - [0], - color="black", - linewidth=1, - label="Mean-normalised hourly demand", - ) - ] - - for row in metadata.itertuples( - index=False - ): - rank = int( - row.cleaning_method_rank - ) - method = str( - row.cleaning_method - ) - - colour = rank_colours[rank] - - # A border keeps the white principal-source patch - # visible in the legend. - edge_colour = ( - "0.65" - if row.category == "observed" - and rank == metadata[ - "cleaning_method_rank" - ].min() - else "none" - ) - - handles.append( - Patch( - facecolor=colour, - edgecolor=edge_colour, - linewidth=0.8, - label=( - f"Rank {rank}: " - f"{_format_method_label(method)}" - ), - ) - ) - - figure.legend( - handles=handles, - loc="outside lower center", - ncols=min( - 4, - len(handles), - ), - frameon=True, - fontsize=8, - ) - - def _format_method_label( method: str, ) -> str: @@ -712,6 +645,71 @@ def _format_method_label( " ", ).capitalize() +def _build_cleaning_method_metadata( + *, + source_names: list[str], + gap_filling_config: dict[str, Any], +) -> pd.DataFrame: + """Build complete method metadata in configured rank order.""" + rows: list[dict[str, Any]] = [] + rank = 0 + + for source_name in source_names: + rows.append( + { + "cleaning_method": ( + f"observed_{source_name}" + ), + "cleaning_method_rank": rank, + "label": ( + f"Rank {rank}: Observed " + f"({_format_source_name(source_name)})" + ), + "category": "observed", + } + ) + rank += 1 + + for rule in gap_filling_config["rules"]: + rule_name = rule["name"] + + rows.append( + { + "cleaning_method": rule_name, + "cleaning_method_rank": rank, + "label": ( + f"Rank {rank}: " + f"{_format_rule_name(rule_name)}" + ), + "category": "imputed", + } + ) + rank += 1 + + rows.append( + { + "cleaning_method": "missing", + "cleaning_method_rank": rank, + "label": f"Rank {rank}: Missing", + "category": "missing", + } + ) + + return pd.DataFrame(rows) + + +def _format_source_name(source_name: str) -> str: + mapping = { + "entsoe_api": "ENTSO-E", + "neso": "NESO", + "opsd_api": "OPSD", + } + return mapping.get(source_name, source_name) + + +def _format_rule_name(name: str) -> str: + return name.replace("_", " ").capitalize() + if __name__ == "__main__": sys.stderr = open( @@ -727,11 +725,9 @@ def _format_method_label( main( demand_path=snakemake.input.demand, - cleaning_method_path=( - snakemake.input.cleaning_method - ), - cleaning_method_rank_path=( - snakemake.input.cleaning_method_rank - ), + cleaning_method_path=snakemake.input.cleaning_method, + cleaning_method_rank_path=snakemake.input.cleaning_method_rank, output_path=snakemake.output.plot, - ) \ No newline at end of file + source_names=snakemake.params.source_names, + gap_filling_config=snakemake.params.gap_filling, + ) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py new file mode 100644 index 0000000..7f027a7 --- /dev/null +++ b/workflow/scripts/download_load_neso.py @@ -0,0 +1,267 @@ +"""Download annual historic electricity-demand data from NESO.""" + +import json +import logging +import shutil +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Any +from urllib.parse import urlencode +from urllib.request import Request, urlopen + +if TYPE_CHECKING: + snakemake: Any + + +logger = logging.getLogger(__name__) + +NESO_API_BASE = "https://api.neso.energy/api/3/action" +USER_AGENT = ( + "modelblocks-module-demand-electricity/" + "NESO historic demand downloader" +) + + +def _request_json( + endpoint: str, + parameters: dict[str, str | int], +) -> dict[str, Any]: + """Request one JSON response from the NESO CKAN API.""" + query = urlencode(parameters) + url = f"{NESO_API_BASE}/{endpoint}?{query}" + + request = Request( + url, + headers={"User-Agent": USER_AGENT}, + ) + + with urlopen(request, timeout=120) as response: + payload = json.load(response) + + if not payload.get("success", False): + raise RuntimeError( + f"NESO API request failed for {endpoint}: {payload}" + ) + + result = payload.get("result") + + if not isinstance(result, dict): + raise RuntimeError( + f"NESO API returned an unexpected result for {endpoint}." + ) + + return result + + +def _get_historic_demand_dataset() -> dict[str, Any]: + """Return the NESO Historic Demand Data dataset.""" + return _request_json( + "package_show", + { + "id": "historic-demand-data", + }, + ) + + +def _select_csv_resource( + dataset: dict[str, Any], + *, + year: int, +) -> dict[str, Any]: + """Select the annual NESO historic-demand CSV resource.""" + resources = dataset.get("resources", []) + + if not isinstance(resources, list): + raise RuntimeError( + "NESO Historic Demand Data has no valid resource list." + ) + + expected_filename = f"demanddata_{year}.csv" + expected_title = f"historic demand data {year}" + + matching_resources: list[dict[str, Any]] = [] + + for resource in resources: + name = str( + resource.get("name", "") + ).strip().casefold() + + url = str( + resource.get("url", "") + ).strip() + + format_name = str( + resource.get("format", "") + ).strip().casefold() + + url_lower = url.casefold() + + is_csv = ( + format_name == "csv" + or url_lower.endswith(".csv") + ) + + matches_year = ( + expected_title in name + or expected_filename in url_lower + ) + + if is_csv and matches_year and url: + matching_resources.append(resource) + + if len(matching_resources) != 1: + available_resources = [ + { + "name": resource.get("name"), + "format": resource.get("format"), + "url": resource.get("url"), + } + for resource in resources + ] + + raise RuntimeError( + "Could not identify exactly one NESO historic-demand " + f"CSV resource for {year}. " + f"Matches: {len(matching_resources)}. " + f"Resources: {available_resources}" + ) + + return matching_resources[0] + +def _download_file( + *, + url: str, + output_path: Path, +) -> None: + """Download one file atomically.""" + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + temporary_path = output_path.with_suffix( + output_path.suffix + ".part" + ) + + temporary_path.unlink( + missing_ok=True + ) + + request = Request( + url, + headers={"User-Agent": USER_AGENT}, + ) + + try: + with ( + urlopen(request, timeout=300) as response, + temporary_path.open("wb") as output_file, + ): + shutil.copyfileobj( + response, + output_file, + ) + + if temporary_path.stat().st_size == 0: + raise RuntimeError( + f"NESO download produced an empty file: {url}" + ) + + temporary_path.replace(output_path) + + except Exception: + temporary_path.unlink( + missing_ok=True + ) + raise + + +def download_annual_files( + *, + years: list[int], + output_paths: list[str | Path], +) -> None: + """Discover and download the NESO CSV for each requested year.""" + if len(years) != len(output_paths): + raise ValueError( + "The number of NESO years must match the number " + "of output paths." + ) + + if len(set(years)) != len(years): + raise ValueError( + f"NESO years must be unique: {years}" + ) + + dataset = _get_historic_demand_dataset() + + for year, raw_output_path in zip( + years, + output_paths, + strict=True, + ): + output_path = Path(raw_output_path) + + if ( + output_path.exists() + and output_path.stat().st_size > 0 + ): + logger.info( + "Retaining existing NESO file for %s: %s", + year, + output_path, + ) + continue + + logger.info( + "Selecting NESO historic-demand resource for %s.", + year, + ) + + resource = _select_csv_resource( + dataset, + year=year, + ) + + url = str(resource["url"]) + + logger.info( + "Downloading NESO historic demand for %s from %s.", + year, + url, + ) + + _download_file( + url=url, + output_path=output_path, + ) + + logger.info( + "Saved NESO historic demand for %s to %s.", + year, + output_path, + ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + download_annual_files( + years=[ + int(year) + for year in snakemake.params.years + ], + output_paths=[ + Path(path) + for path in snakemake.output + ], + ) \ No newline at end of file diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py new file mode 100644 index 0000000..8ef63a6 --- /dev/null +++ b/workflow/scripts/prepare_load_neso.py @@ -0,0 +1,284 @@ +"""Prepare NESO historic demand on the configured hourly target grid.""" + +import logging +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Any, Iterable + +import pandas as pd +from cleaning.neso import add_utc_timestamps + +if TYPE_CHECKING: + snakemake: Any + + +logger = logging.getLogger(__name__) + +REQUIRED_COLUMNS = [ + "SETTLEMENT_DATE", + "SETTLEMENT_PERIOD", + "ND", +] + + +def _read_neso_files( + paths: Iterable[str | Path], +) -> pd.DataFrame: + """Read and combine annual NESO historic-demand files.""" + frames: list[pd.DataFrame] = [] + + for raw_path in paths: + path = Path(raw_path) + + if not path.exists(): + raise FileNotFoundError( + f"NESO input file does not exist: {path}" + ) + + logger.info( + "Reading NESO historic demand from %s.", + path, + ) + + try: + frame = pd.read_csv( + path, + usecols=REQUIRED_COLUMNS, + ) + except ValueError as error: + available_columns = pd.read_csv( + path, + nrows=0, + ).columns.tolist() + + raise ValueError( + f"NESO file {path} does not contain the required " + f"columns {REQUIRED_COLUMNS}. " + f"Available columns: {available_columns}" + ) from error + + frames.append(frame) + + if not frames: + raise ValueError( + "At least one NESO input file is required." + ) + + return pd.concat( + frames, + ignore_index=True, + ) + + +def _prepare_half_hourly_demand( + raw: pd.DataFrame, +) -> pd.Series: + """Convert raw NESO records to a UTC half-hourly demand series.""" + prepared = add_utc_timestamps(raw) + + prepared["ND"] = pd.to_numeric( + prepared["ND"], + errors="coerce", + ) + + invalid_demand_count = int( + prepared["ND"].isna().sum() + ) + + if invalid_demand_count: + logger.warning( + "NESO contains %s missing or non-numeric ND values.", + invalid_demand_count, + ) + + half_hourly = ( + prepared + .set_index("timestamp")["ND"] + .sort_index() + .rename("GBR") + ) + + duplicate_mask = half_hourly.index.duplicated( + keep=False + ) + + if duplicate_mask.any(): + duplicate_timestamps = ( + half_hourly.index[duplicate_mask] + .unique() + .astype(str) + .tolist() + ) + + raise ValueError( + "NESO data contain duplicate UTC timestamps: " + f"{duplicate_timestamps[:10]}" + ) + + if not half_hourly.index.is_monotonic_increasing: + raise ValueError( + "Prepared NESO timestamps are not sorted." + ) + + return half_hourly + + +def _aggregate_hourly( + half_hourly: pd.Series, +) -> pd.Series: + """Aggregate half-hourly MW observations to hourly mean MW.""" + hourly_counts = half_hourly.resample("1h").count() + + incomplete_hours = hourly_counts.loc[ + hourly_counts.between( + 1, + 1, + inclusive="both", + ) + ] + + if not incomplete_hours.empty: + logger.warning( + "NESO contains %s hours with only one valid " + "half-hourly ND observation.", + len(incomplete_hours), + ) + + hourly = ( + half_hourly + .resample("1h") + .mean() + .rename("GBR") + ) + + return hourly + + +def _build_target_index( + *, + temporal_start: str, + temporal_end: str, +) -> pd.DatetimeIndex: + """Create the configured end-exclusive hourly UTC index.""" + start = pd.Timestamp(temporal_start) + end = pd.Timestamp(temporal_end) + + if start.tzinfo is None: + start = start.tz_localize("UTC") + else: + start = start.tz_convert("UTC") + + if end.tzinfo is None: + end = end.tz_localize("UTC") + else: + end = end.tz_convert("UTC") + + if end <= start: + raise ValueError( + "The temporal-scope end must be later than its start." + ) + + return pd.date_range( + start=start, + end=end, + freq="1h", + inclusive="left", + name="time", + ) + + +def prepare_load_neso( + *, + input_paths: Iterable[str | Path], + output_path: str | Path, + temporal_start: str, + temporal_end: str, + countries: Iterable[str], +) -> None: + """Prepare NESO demand on the common time-country target grid.""" + target_countries = list(countries) + + if len(target_countries) != len( + set(target_countries) + ): + raise ValueError( + "Target country codes must be unique." + ) + + target_index = _build_target_index( + temporal_start=temporal_start, + temporal_end=temporal_end, + ) + + result = pd.DataFrame( + index=target_index, + columns=target_countries, + dtype=float, + ) + + if "GBR" not in target_countries: + logger.info( + "GBR is not part of the configured country scope. " + "Writing an empty NESO target-grid dataframe." + ) + else: + raw = _read_neso_files(input_paths) + half_hourly = _prepare_half_hourly_demand(raw) + hourly = _aggregate_hourly(half_hourly) + + result["GBR"] = hourly.reindex( + target_index + ) + + supplied = int( + result["GBR"].notna().sum() + ) + + missing = int( + result["GBR"].isna().sum() + ) + + logger.info( + "Prepared NESO GBR demand: %s supplied hourly " + "values and %s missing values.", + supplied, + missing, + ) + + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + result.to_parquet(output_path) + + logger.info( + "Saved prepared NESO demand to %s with shape %s.", + output_path, + result.shape, + ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + prepare_load_neso( + input_paths=[ + Path(path) + for path in snakemake.input.annual_files + ], + output_path=snakemake.output.load, + temporal_start=snakemake.params.start, + temporal_end=snakemake.params.end, + countries=snakemake.params.country_codes, + ) From 94cd15b8c4ca2035558a47fa0ac307a81e5a9484 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 5 Aug 2026 13:43:42 +0200 Subject: [PATCH 012/241] Implemented average periods gap filling mode --- config/config.yaml | 17 ++- tests/integration/test_config.yaml | 16 ++- workflow/internal/config.schema.yaml | 30 +++++ workflow/rules/automatic.smk | 19 +++ workflow/rules/clean.smk | 21 +++ workflow/scripts/cleaning/average_periods.py | 126 ++++++++++++++++++ workflow/scripts/cleaning/external_profile.py | 36 +++++ workflow/scripts/cleaning/pipeline.py | 10 ++ 8 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 workflow/scripts/cleaning/average_periods.py create mode 100644 workflow/scripts/cleaning/external_profile.py diff --git a/config/config.yaml b/config/config.yaml index 780e31f..b079fe5 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -14,11 +14,24 @@ load_sources: gap_filling: enabled: true rules: + # interpolates across tiny gaps where <=3 hours are missing - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h - + # takes the average of the same hour, one week in the future and one week in the past + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + #copies from the previous period, maximum of up to 1 week - name: copy_previous_week method: copy_period max_gap: 168h - source_offset: -168h \ No newline at end of file + source_offset: -168h + #copies from the following period, maximum of up to 1 week + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 03579ff..65d3533 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -11,10 +11,24 @@ integration_test: gap_filling: enabled: true rules: + # interpolates across tiny gaps where <=3 hours are missing - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h + # takes the average of the same hour, one week in the future and one week in the past + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + #copies from the previous period, maximum of up to 1 week - name: copy_previous_week method: copy_period max_gap: 168h - source_offset: -168h \ No newline at end of file + source_offset: -168h + #copies from the following period, maximum of up to 1 week + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index a6c951d..d5d95eb 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -27,6 +27,16 @@ $defs: description: > Non-zero signed duration such as -168h, 168h, -7d, or 1w. + signed_durations: + type: array + minItems: 2 + uniqueItems: true + items: + $ref: "#/$defs/signed_duration" + description: > + Two or more unique, non-zero signed durations identifying the + source periods to average. + gap_filling_rule_base: type: object properties: @@ -60,7 +70,26 @@ $defs: required: - max_gap + unevaluatedProperties: false + average_periods_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + + - type: object + properties: + method: + const: average_periods + + max_gap: + $ref: "#/$defs/duration" + + source_offsets: + $ref: "#/$defs/signed_durations" + + required: + - max_gap + - source_offsets unevaluatedProperties: false copy_period_rule: @@ -94,6 +123,7 @@ $defs: gap_filling_rule: oneOf: - $ref: "#/$defs/linear_interpolation_rule" + - $ref: "#/$defs/average_periods_rule" - $ref: "#/$defs/copy_period_rule" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index f126da2..5c5181d 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -93,6 +93,25 @@ rule download_load_neso: script: "../scripts/download_load_neso.py" + +rule download_synthetic_electricity_demand: + output: + csv=( + "/automatic/" + "synthetic_electricity_demand/" + "demand_hourly.csv" + ), + params: + url=..., + md5="a9b59e5a32ad422bcd9e12fea5dc291a", + log: + "/download_synthetic_electricity_demand.log", + conda: + "../envs/module.yaml" + script: + "../scripts/download_synthetic_electricity_demand.py" + + rule download_population: output: population="/automatic/population.zip", diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index bcbfe1e..7830fd2 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -34,6 +34,27 @@ rule prepare_load_neso: "Prepare electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" + +rule prepare_synthetic_electricity_demand: + input: + csv=rules.download_synthetic_electricity_demand.output.csv, + output: + load=( + "/automatic/" + "load_synthetic_electricity_demand.parquet" + ), + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe_api"]["countries"], + log: + "/prepare_load_synthetic.log", + conda: + "../envs/module.yaml" + message: + "Prepare electricity-demand data from PyPSA synthetic profile." + script: + "../scripts/prepare_load_synthetic.py" LOAD_SOURCE_PATHS = { diff --git a/workflow/scripts/cleaning/average_periods.py b/workflow/scripts/cleaning/average_periods.py new file mode 100644 index 0000000..bc32355 --- /dev/null +++ b/workflow/scripts/cleaning/average_periods.py @@ -0,0 +1,126 @@ +"""A gap-filling method: average corresponding values from other periods.""" + +from __future__ import annotations + +from collections.abc import Sequence + +import pandas as pd + + +METHOD_NAME = "average_periods" + + +def apply_average_periods( + load: pd.DataFrame, + *, + max_gap: str | pd.Timedelta, + source_offsets: Sequence[str | pd.Timedelta], + original_gap_duration: pd.DataFrame, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Fill missing runs using the mean of complete source periods. + + For a target timestamp ``t``, source values are taken from + ``t + source_offset`` for each configured offset. + + For example, offsets ``-7D`` and ``7D`` average the same hour + from the previous and following weeks. + + A gap is filled only when: + + - it was missing in the original data; + - its original duration does not exceed ``max_gap``; + - every configured source period is complete for the whole gap. + """ + max_gap = pd.Timedelta(max_gap) + + offsets = tuple( + pd.Timedelta(offset) + for offset in source_offsets + ) + + if max_gap <= pd.Timedelta(0): + raise ValueError( + "'max_gap' must be greater than zero." + ) + + if len(offsets) < 2: + raise ValueError( + "'source_offsets' must contain at least two offsets." + ) + + if len(set(offsets)) != len(offsets): + raise ValueError( + "'source_offsets' must not contain duplicates." + ) + + if pd.Timedelta(0) in offsets: + raise ValueError( + "'source_offsets' must not contain zero." + ) + + eligible = ( + load.isna() + & original_gap_duration.gt(pd.Timedelta(0)) + & original_gap_duration.le(max_gap) + ) + + sources = [ + _values_at_offset( + load, + source_offset=offset, + ) + for offset in offsets + ] + + candidate = _mean_complete_sources( + sources=sources, + ) + + eligible &= candidate.notna() + + filled = load.mask( + eligible, + candidate, + ) + + newly_filled = ( + load.isna() + & filled.notna() + ) + + return filled, newly_filled + +def _values_at_offset( + load: pd.DataFrame, + *, + source_offset: pd.Timedelta, +) -> pd.DataFrame: + """Align values at timestamp + offset to target timestamps.""" + source_timestamps = ( + load.index + source_offset + ) + + source = load.reindex( + source_timestamps + ) + + source.index = load.index + + return source + + +def _mean_complete_sources( + *, + sources: Sequence[pd.DataFrame], +) -> pd.DataFrame: + """Calculate the mean only where every source is available.""" + source_sum = sources[0].copy() + complete = sources[0].notna() + + for source in sources[1:]: + source_sum = source_sum + source + complete &= source.notna() + + candidate = source_sum / len(sources) + + return candidate.where(complete) \ No newline at end of file diff --git a/workflow/scripts/cleaning/external_profile.py b/workflow/scripts/cleaning/external_profile.py new file mode 100644 index 0000000..0776b81 --- /dev/null +++ b/workflow/scripts/cleaning/external_profile.py @@ -0,0 +1,36 @@ +"""A gap-filling method using an aligned external profile.""" + +import pandas as pd + +METHOD_NAME = "external_profile" + + +def apply_external_profile( + load: pd.DataFrame, + *, + profile: pd.DataFrame, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Fill remaining missing cells from an aligned external profile.""" + if not profile.index.equals(load.index): + raise ValueError( + "External profile must use the same index as load." + ) + + if not profile.columns.equals(load.columns): + raise ValueError( + "External profile must use the same columns as load." + ) + + eligible = load.isna() & profile.notna() + + filled = load.mask( + eligible, + profile, + ) + + newly_filled = ( + load.isna() + & filled.notna() + ) + + return filled, newly_filled \ No newline at end of file diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 8e986a3..0ac516e 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -7,6 +7,8 @@ import pandas as pd +from cleaning.average_periods import METHOD_NAME as AVERAGE_PERIODS +from cleaning.average_periods import apply_average_periods from cleaning.combine_sources import combine_sources from cleaning.copy_period import METHOD_NAME as COPY_PERIOD from cleaning.copy_period import apply_copy_period @@ -126,6 +128,14 @@ def fill_gaps( original_gap_duration=original_gap_duration, ) + elif method == AVERAGE_PERIODS: + filled, newly_filled = apply_average_periods( + filled, + max_gap=rule["max_gap"], + source_offsets=rule["source_offsets"], + original_gap_duration=original_gap_duration, + ) + elif method == COPY_PERIOD: filled, newly_filled = apply_copy_period( filled, From 99512a60e10421dfa0ea5b01bf7887ca0416d864 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 5 Aug 2026 13:54:24 +0200 Subject: [PATCH 013/241] Changed config larameter from 'enabled' (boolean) to 'mode' (str) to allow for different levels of data cleaning (off,basic,advanced). Advanced not yet implemented. --- tests/integration/test_config.yaml | 2 +- workflow/internal/config.schema.yaml | 17 ++++++++--- workflow/scripts/cleaning/pipeline.py | 43 +++++++++++++++++++++------ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 65d3533..2de254c 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -9,7 +9,7 @@ integration_test: - opsd_api gap_filling: - enabled: true + mode: basic rules: # interpolates across tiny gaps where <=3 hours are missing - name: interpolate_short_gaps diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index d5d95eb..438da9b 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -169,9 +169,18 @@ properties: provided, and that order contributes to cleaning-method ranking. properties: - enabled: - type: boolean - default: true + mode: + type: string + enum: + - off + - basic + - advanced + default: basic + description: > + Gap-filling mode. 'off' disables gap filling, 'basic' applies + configured rules using data available within the requested temporal + scope, and 'advanced' additionally uses auxiliary-data planning and + advanced filling strategies. rules: type: array @@ -180,7 +189,7 @@ properties: $ref: "#/$defs/gap_filling_rule" required: - - enabled + - mode - rules additionalProperties: false diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 0ac516e..72c27ab 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -103,8 +103,12 @@ def fill_gaps( filled = load.copy() cleaning_method = cleaning_method.copy() - if not config["enabled"]: - logger.info("Gap filling is disabled.") + mode = config["mode"] + + if mode == "off": + logger.info( + "Gap filling is disabled because mode is 'off'." + ) cleaning_method = cleaning_method.fillna( "missing" @@ -112,6 +116,12 @@ def fill_gaps( return filled, cleaning_method + if mode == "advanced": + raise NotImplementedError( + "Gap-filling mode 'advanced' is not yet implemented. " + "Use mode 'basic' to apply the configured rules." + ) + rules = config["rules"] original_gap_duration = calculate_missing_run_durations( load @@ -339,14 +349,29 @@ def _validate_config(config: Mapping[str, Any]) -> None: "Gap-filling configuration must be a mapping." ) - if "enabled" not in config: + if "mode" not in config: raise ValueError( - "Gap-filling configuration must define 'enabled'." + "Gap-filling configuration must define 'mode'." ) - if not isinstance(config["enabled"], bool): + mode = config["mode"] + + if not isinstance(mode, str): raise TypeError( - "Gap-filling configuration 'enabled' must be a boolean." + "Gap-filling configuration 'mode' must be a string." + ) + + supported_modes = { + "off", + "basic", + "advanced", + } + + if mode not in supported_modes: + raise ValueError( + "Unsupported gap-filling mode " + f"{mode!r}. Expected one of " + f"{sorted(supported_modes)}." ) if "rules" not in config: @@ -362,10 +387,10 @@ def _validate_config(config: Mapping[str, Any]) -> None: "Gap-filling configuration 'rules' must be an ordered sequence." ) - if config["enabled"] and not config["rules"]: + if mode in {"basic", "advanced"} and not config["rules"]: raise ValueError( - "At least one gap-filling rule is required when gap filling " - "is enabled." + "At least one gap-filling rule is required when " + f"gap-filling mode is {mode!r}." ) From 85868893533e1849745bc5b8e4a3ff2a2d42c118 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 6 Aug 2026 14:22:10 +0200 Subject: [PATCH 014/241] Included functionality to generate the gap report for advanced gap-filling. --- config/config.yaml | 3 +- tests/integration/test_config.yaml | 47 ++--- tests/unit/test_cleaning_pipeline.py | 263 +++++++++++++++++++++++++- workflow/rules/clean.smk | 4 + workflow/scripts/clean_demand.py | 37 ++++ workflow/scripts/cleaning/pipeline.py | 107 +++++++++-- 6 files changed, 422 insertions(+), 39 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index b079fe5..09ff5d7 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -12,7 +12,8 @@ load_sources: # Gap-filling rules are applied in the listed order. gap_filling: - enabled: true + #sets the data cleaning mode. Options: off,basic,Advanced + mode: advanced rules: # interpolates across tiny gaps where <=3 hours are missing - name: interpolate_short_gaps diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 2de254c..11a7e6a 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -9,26 +9,27 @@ integration_test: - opsd_api gap_filling: - mode: basic - rules: - # interpolates across tiny gaps where <=3 hours are missing - - name: interpolate_short_gaps - method: linear_interpolation - max_gap: 3h - # takes the average of the same hour, one week in the future and one week in the past - - name: average_adjacent_weeks - method: average_periods - max_gap: 326h - source_offsets: - - -7d - - 7d - #copies from the previous period, maximum of up to 1 week - - name: copy_previous_week - method: copy_period - max_gap: 168h - source_offset: -168h - #copies from the following period, maximum of up to 1 week - - name: copy_following_week - method: copy_period - max_gap: 168h - source_offset: 168h \ No newline at end of file + #sets the data cleaning mode. Options: off,basic,Advanced + mode: advanced + rules: + # interpolates across tiny gaps where <=3 hours are missing + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + # takes the average of the same hour, one week in the future and one week in the past + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + #copies from the previous period, maximum of up to 1 week + - name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h + #copies from the following period, maximum of up to 1 week + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h \ No newline at end of file diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index 5aeae55..826a4bb 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -4,8 +4,7 @@ import numpy as np import pandas as pd - -from cleaning.pipeline import clean_demand +from cleaning.pipeline import build_gap_report, clean_demand def test_clean_demand_records_methods_and_ranks() -> None: @@ -74,7 +73,7 @@ def test_clean_demand_records_methods_and_ranks() -> None: } gap_filling_config = { - "enabled": True, + "mode": "basic", "rules": [ { "name": "interpolate_short_gaps", @@ -96,6 +95,7 @@ def test_clean_demand_records_methods_and_ranks() -> None: data_source, cleaning_method, cleaning_method_rank, + gap_report, ) = clean_demand( sources, source_priority=[ @@ -105,6 +105,8 @@ def test_clean_demand_records_methods_and_ranks() -> None: gap_filling_config=gap_filling_config, ) + assert gap_report.empty + # All outputs use the same grid. for frame in [ data_source, @@ -301,4 +303,257 @@ def test_clean_demand_records_methods_and_ranks() -> None: .sort_index() .to_dict() == expected_rank_counts - ) \ No newline at end of file + ) + + +def test_advanced_mode_reports_unresolved_gaps() -> None: + """Report contiguous gaps remaining after basic gap filling.""" + index = pd.date_range( + start="2017-01-01", + periods=400, + freq="h", + tz="UTC", + ) + + primary = pd.DataFrame( + { + "AAA": np.arange( + len(index), + dtype=float, + ), + }, + index=index, + ) + + fallback = pd.DataFrame( + np.nan, + index=index, + columns=["AAA"], + dtype=float, + ) + + unresolved_timestamps = index[0:4] + primary.loc[ + unresolved_timestamps, + "AAA", + ] = np.nan + + sources = { + "primary": primary, + "fallback": fallback, + } + + gap_filling_config = { + "mode": "advanced", + "rules": [ + { + "name": "interpolate_short_gaps", + "method": "linear_interpolation", + "max_gap": "3h", + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + "require_complete_source": True, + }, + ], + } + + ( + cleaned, + _data_source, + cleaning_method, + _cleaning_method_rank, + gap_report, + ) = clean_demand( + sources, + source_priority=[ + "primary", + "fallback", + ], + gap_filling_config=gap_filling_config, + ) + + assert cleaned.loc[ + unresolved_timestamps, + "AAA", + ].isna().all() + + assert cleaning_method.loc[ + unresolved_timestamps, + "AAA", + ].eq("missing").all() + + assert len(gap_report) == 1 + + row = gap_report.iloc[0] + + assert row["country"] == "AAA" + assert row["gap_start"] == index[0] + assert row["gap_end"] == index[3] + assert row["gap_hours"] == 4 + assert bool(row["touches_start_boundary"]) + assert not bool(row["touches_end_boundary"]) + + +def test_build_gap_report_identifies_contiguous_gaps_and_boundaries() -> None: + """Test the gap report captures gaps and boundaries as expected.""" + index = pd.date_range( + "2021-01-01 00:00", + periods=8, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": pd.array( + [ + pd.NA, + 10.0, + 11.0, + pd.NA, + pd.NA, + 14.0, + 15.0, + 16.0, + ], + dtype="Float64", + ), + "TUR": pd.array( + [pd.NA] * 8, + dtype="Float64", + ), + "MNE": pd.array( + [ + 20.0, + 21.0, + 22.0, + 23.0, + 24.0, + 25.0, + pd.NA, + pd.NA, + ], + dtype="Float64", + ), + }, + index=index, + ) + + result = build_gap_report( + load, + enabled=True, + ) + + expected = pd.DataFrame( + { + "country": [ + "ALB", + "ALB", + "MNE", + "TUR", + ], + "gap_start": [ + index[0], + index[3], + index[6], + index[0], + ], + "gap_end": [ + index[0], + index[4], + index[7], + index[7], + ], + "gap_hours": [ + 1, + 2, + 2, + 8, + ], + "touches_start_boundary": [ + True, + False, + False, + True, + ], + "touches_end_boundary": [ + False, + False, + True, + True, + ], + } + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) + + +def test_build_gap_report_returns_empty_report_when_disabled() -> None: + """Tests that gap report is empty when disabled.""" + result = build_gap_report( + pd.DataFrame(), + enabled=False, + ) + + expected = pd.DataFrame( + columns=[ + "country", + "gap_start", + "gap_end", + "gap_hours", + "touches_start_boundary", + "touches_end_boundary", + ] + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) + + +def test_build_gap_report_returns_empty_report_when_no_gaps_remain() -> None: + """Tests that gap report is empty when there are no gaps.""" + index = pd.date_range( + "2021-01-01 00:00", + periods=3, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [10.0, 11.0, 12.0], + "TUR": [20.0, 21.0, 22.0], + }, + index=index, + ) + + result = build_gap_report( + load, + enabled=True, + ) + + expected = pd.DataFrame( + columns=[ + "country", + "gap_start", + "gap_end", + "gap_hours", + "touches_start_boundary", + "touches_end_boundary", + ] + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) + diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 7830fd2..714a76f 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -100,6 +100,10 @@ rule clean_demand: "/automatic/" "load_cleaning_method_rank.parquet" ), + gap_report=( + "/automatic/" + "load_gap_report.parquet" + ), params: source_names=config["load_sources"], gap_filling=config["gap_filling"], diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index eec341e..1d6f3b9 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -43,6 +43,7 @@ def main( data_source, cleaning_method, cleaning_method_rank, + gap_report ) = clean_demand( sources, source_priority=source_names, @@ -57,12 +58,18 @@ def main( cleaning_method_rank.to_parquet( output.cleaning_method_rank ) + gap_report.to_parquet( + output.gap_report, + index=False, + ) _log_source_counts(data_source) _log_cleaning_method_counts( cleaning_method, cleaning_method_rank, ) + _log_gap_report(gap_report) + def _read_prepared_source( path: str | Path, @@ -144,6 +151,36 @@ def _log_cleaning_method_counts( ) +def _log_gap_report( + gap_report: pd.DataFrame, +) -> None: + """Log unresolved-gap counts by country.""" + if gap_report.empty: + logger.info( + "No advanced unresolved-gap report was generated." + ) + return + + logger.info( + "Gap report contains %s contiguous unresolved gaps.", + len(gap_report), + ) + + country_summary = gap_report.groupby( + "country" + ).agg( + gap_count=("country", "size"), + missing_hours=("gap_hours", "sum"), + ) + + for country, row in country_summary.iterrows(): + logger.info( + "%s: %s unresolved gaps covering %s hours.", + country, + int(row["gap_count"]), + int(row["missing_hours"]), + ) + if __name__ == "__main__": sys.stderr = open( snakemake.log[0], diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 72c27ab..a3812e7 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -25,10 +25,11 @@ def clean_demand( source_priority: Sequence[str], gap_filling_config: Mapping[str, Any], ) -> tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, + pd.DataFrame, #data + pd.DataFrame, #sources + pd.DataFrame, #method + pd.DataFrame, #rank + pd.DataFrame, #gap_report ]: """Combine observed sources and fill remaining gaps.""" ( @@ -58,11 +59,25 @@ def clean_demand( ranks=cleaning_method_ranks, ) + gap_report = build_gap_report( + cleaned, + enabled=gap_filling_config["mode"] == "advanced", + ) + + if gap_filling_config["mode"] == "advanced": + logger.info( + "Advanced gap diagnosis found %s unresolved gaps " + "covering %s values.", + len(gap_report), + int(gap_report["gap_hours"].sum()), + ) + return ( cleaned, data_source, cleaning_method, cleaning_method_rank, + gap_report ) @@ -82,7 +97,7 @@ def fill_gaps( Per-cell cleaning-method provenance for the observed input values. Missing input values should contain ``pd.NA``. config: - Gap-filling configuration containing ``enabled`` and ``rules``. + Gap-filling configuration containing ``mode`` and ``rules``. Returns ------- @@ -116,12 +131,6 @@ def fill_gaps( return filled, cleaning_method - if mode == "advanced": - raise NotImplementedError( - "Gap-filling mode 'advanced' is not yet implemented. " - "Use mode 'basic' to apply the configured rules." - ) - rules = config["rules"] original_gap_duration = calculate_missing_run_durations( load @@ -220,6 +229,82 @@ def calculate_missing_run_durations( return durations +def build_gap_report( + load: pd.DataFrame, + *, + enabled: bool, +) -> pd.DataFrame: + """Describe contiguous unresolved gaps in cleaned load data. + + An empty report with the expected columns is returned when reporting + is disabled or when no unresolved gaps remain. + """ + columns = [ + "country", + "gap_start", + "gap_end", + "gap_hours", + "touches_start_boundary", + "touches_end_boundary", + ] + + if not enabled: + return pd.DataFrame(columns=columns) + + _validate_load(load) + + records: list[dict[str, Any]] = [] + + first_timestamp = load.index[0] + last_timestamp = load.index[-1] + + for country in load.columns: + missing = load[country].isna() + + if not missing.any(): + continue + + group_ids = missing.ne( + missing.shift(fill_value=False) + ).cumsum() + + for _, group in missing.groupby(group_ids): + if not bool(group.iloc[0]): + continue + + timestamps = group.index + + records.append( + { + "country": country, + "gap_start": timestamps[0], + "gap_end": timestamps[-1], + "gap_hours": len(timestamps), + "touches_start_boundary": ( + timestamps[0] == first_timestamp + ), + "touches_end_boundary": ( + timestamps[-1] == last_timestamp + ), + } + ) + + report = pd.DataFrame.from_records( + records, + columns=columns, + ) + + if report.empty: + return report + + return report.sort_values( + [ + "country", + "gap_start", + ] + ).reset_index(drop=True) + + def _get_method(rule: Mapping[str, Any]) -> str: try: method = rule["method"] From 0d1dfafa1a15aa77a4cf191ea25db40e5a4d0163 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 6 Aug 2026 14:40:25 +0200 Subject: [PATCH 015/241] Restructured directory to separate basic cleaning and advanced cleaning functions --- tests/unit/test_cleaning_pipeline.py | 3 +- .../scripts/cleaning/advanced/__init__.py | 0 .../scripts/cleaning/advanced/gap_report.py | 85 ++++ workflow/scripts/cleaning/basic/__init__.py | 0 .../cleaning/{ => basic}/average_periods.py | 0 .../cleaning/{ => basic}/copy_period.py | 0 .../{ => basic}/linear_interpolation.py | 0 workflow/scripts/cleaning/basic/pipeline.py | 415 ++++++++++++++++ workflow/scripts/cleaning/pipeline.py | 461 +----------------- workflow/scripts/cleaning/validation.py | 63 +++ 10 files changed, 568 insertions(+), 459 deletions(-) create mode 100644 workflow/scripts/cleaning/advanced/__init__.py create mode 100644 workflow/scripts/cleaning/advanced/gap_report.py create mode 100644 workflow/scripts/cleaning/basic/__init__.py rename workflow/scripts/cleaning/{ => basic}/average_periods.py (100%) rename workflow/scripts/cleaning/{ => basic}/copy_period.py (100%) rename workflow/scripts/cleaning/{ => basic}/linear_interpolation.py (100%) create mode 100644 workflow/scripts/cleaning/basic/pipeline.py create mode 100644 workflow/scripts/cleaning/validation.py diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index 826a4bb..a0eb086 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -4,7 +4,8 @@ import numpy as np import pandas as pd -from cleaning.pipeline import build_gap_report, clean_demand +from cleaning.advanced.gap_report import build_gap_report +from cleaning.pipeline import clean_demand def test_clean_demand_records_methods_and_ranks() -> None: diff --git a/workflow/scripts/cleaning/advanced/__init__.py b/workflow/scripts/cleaning/advanced/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/advanced/gap_report.py b/workflow/scripts/cleaning/advanced/gap_report.py new file mode 100644 index 0000000..315b49d --- /dev/null +++ b/workflow/scripts/cleaning/advanced/gap_report.py @@ -0,0 +1,85 @@ +"""Build reports for unresolved gaps after basic cleaning.""" + +from __future__ import annotations + +from typing import Any + +import pandas as pd + +from cleaning.validation import validate_load + + +def build_gap_report( + load: pd.DataFrame, + *, + enabled: bool, +) -> pd.DataFrame: + """Describe contiguous unresolved gaps in cleaned load data. + + An empty report with the expected columns is returned when reporting + is disabled or when no unresolved gaps remain. + """ + columns = [ + "country", + "gap_start", + "gap_end", + "gap_hours", + "touches_start_boundary", + "touches_end_boundary", + ] + + if not enabled: + return pd.DataFrame(columns=columns) + + validate_load(load) + + records: list[dict[str, Any]] = [] + + first_timestamp = load.index[0] + last_timestamp = load.index[-1] + + for country in load.columns: + missing = load[country].isna() + + if not missing.any(): + continue + + group_ids = missing.ne( + missing.shift(fill_value=False) + ).cumsum() + + for _, group in missing.groupby(group_ids): + if not bool(group.iloc[0]): + continue + + timestamps = group.index + + records.append( + { + "country": country, + "gap_start": timestamps[0], + "gap_end": timestamps[-1], + "gap_hours": len(timestamps), + "touches_start_boundary": ( + timestamps[0] == first_timestamp + ), + "touches_end_boundary": ( + timestamps[-1] == last_timestamp + ), + } + ) + + report = pd.DataFrame.from_records( + records, + columns=columns, + ) + + if report.empty: + return report + + return report.sort_values( + [ + "country", + "gap_start", + ] + ).reset_index(drop=True) \ No newline at end of file diff --git a/workflow/scripts/cleaning/basic/__init__.py b/workflow/scripts/cleaning/basic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/average_periods.py b/workflow/scripts/cleaning/basic/average_periods.py similarity index 100% rename from workflow/scripts/cleaning/average_periods.py rename to workflow/scripts/cleaning/basic/average_periods.py diff --git a/workflow/scripts/cleaning/copy_period.py b/workflow/scripts/cleaning/basic/copy_period.py similarity index 100% rename from workflow/scripts/cleaning/copy_period.py rename to workflow/scripts/cleaning/basic/copy_period.py diff --git a/workflow/scripts/cleaning/linear_interpolation.py b/workflow/scripts/cleaning/basic/linear_interpolation.py similarity index 100% rename from workflow/scripts/cleaning/linear_interpolation.py rename to workflow/scripts/cleaning/basic/linear_interpolation.py diff --git a/workflow/scripts/cleaning/basic/pipeline.py b/workflow/scripts/cleaning/basic/pipeline.py new file mode 100644 index 0000000..757daad --- /dev/null +++ b/workflow/scripts/cleaning/basic/pipeline.py @@ -0,0 +1,415 @@ +"""Coordinate deterministic basic gap-filling rules.""" + +from __future__ import annotations + +import logging +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + +from cleaning.basic.average_periods import METHOD_NAME as AVERAGE_PERIODS +from cleaning.basic.average_periods import apply_average_periods +from cleaning.basic.copy_period import METHOD_NAME as COPY_PERIOD +from cleaning.basic.copy_period import apply_copy_period +from cleaning.basic.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION +from cleaning.basic.linear_interpolation import apply_linear_interpolation +from cleaning.validation import infer_regular_timestep, validate_load + +logger = logging.getLogger(__name__) + + +def fill_basic_gaps( + load: pd.DataFrame, + *, + cleaning_method: pd.DataFrame, + config: Mapping[str, Any], +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply configured gap-filling rules and record method provenance. + + Parameters + ---------- + load: + Hourly demand data indexed by timestamp, with one column per country. + cleaning_method: + Per-cell cleaning-method provenance for the observed input values. + Missing input values should contain ``pd.NA``. + config: + Gap-filling configuration containing ``mode`` and ``rules``. + + Returns + ------- + filled: + Load after applying the configured rules. If gap filling is disabled, + this is an unchanged copy of ``load``. + cleaning_method: + Per-cell provenance containing the observed-source identifier, + configured gap-filling rule name, or ``missing``. + """ + validate_load(load) + _validate_cleaning_method( + load=load, + cleaning_method=cleaning_method, + ) + _validate_config(config) + + filled = load.copy() + cleaning_method = cleaning_method.copy() + + mode = config["mode"] + + if mode == "off": + logger.info( + "Gap filling is disabled because mode is 'off'." + ) + + cleaning_method = cleaning_method.fillna( + "missing" + ) + + return filled, cleaning_method + + rules = config["rules"] + original_gap_duration = calculate_missing_run_durations( + load + ) + + for rule in rules: + method = _get_method(rule) + rule_name = _get_rule_name(rule) + + if method == LINEAR_INTERPOLATION: + filled, newly_filled = apply_linear_interpolation( + filled, + max_gap=rule["max_gap"], + original_gap_duration=original_gap_duration, + ) + + elif method == AVERAGE_PERIODS: + filled, newly_filled = apply_average_periods( + filled, + max_gap=rule["max_gap"], + source_offsets=rule["source_offsets"], + original_gap_duration=original_gap_duration, + ) + + elif method == COPY_PERIOD: + filled, newly_filled = apply_copy_period( + filled, + max_gap=rule["max_gap"], + source_offset=rule["source_offset"], + require_complete_source=rule.get( + "require_complete_source", + True, + ), + original_gap_duration=original_gap_duration, + ) + + else: + raise ValueError( + f"Unsupported gap-filling method: {method!r}" + ) + + cleaning_method = cleaning_method.mask( + newly_filled, + rule_name, + ) + + _log_rule_results( + rule_name=rule_name, + method=method, + newly_filled=newly_filled, + ) + + cleaning_method = cleaning_method.fillna( + "missing" + ) + + unresolved = int( + filled.isna().to_numpy().sum() + ) + + logger.info( + "Gap filling completed with %s unresolved values.", + unresolved, + ) + + return filled, cleaning_method + + +def calculate_missing_run_durations( + load: pd.DataFrame, +) -> pd.DataFrame: + """Return the original duration of each missing run. + + Observed values receive a duration of zero. + """ + timestep = infer_regular_timestep(load.index) + + durations = pd.DataFrame( + pd.Timedelta(0), + index=load.index, + columns=load.columns, + ) + + for column in load.columns: + missing = load[column].isna() + group_ids = missing.ne(missing.shift()).cumsum() + + run_lengths = ( + missing.groupby(group_ids) + .transform("sum") + .where(missing, 0) + ) + + durations[column] = run_lengths * timestep + + return durations + + +def build_gap_report( + load: pd.DataFrame, + *, + enabled: bool, +) -> pd.DataFrame: + """Describe contiguous unresolved gaps in cleaned load data. + + An empty report with the expected columns is returned when reporting + is disabled or when no unresolved gaps remain. + """ + columns = [ + "country", + "gap_start", + "gap_end", + "gap_hours", + "touches_start_boundary", + "touches_end_boundary", + ] + + if not enabled: + return pd.DataFrame(columns=columns) + + validate_load(load) + + records: list[dict[str, Any]] = [] + + first_timestamp = load.index[0] + last_timestamp = load.index[-1] + + for country in load.columns: + missing = load[country].isna() + + if not missing.any(): + continue + + group_ids = missing.ne( + missing.shift(fill_value=False) + ).cumsum() + + for _, group in missing.groupby(group_ids): + if not bool(group.iloc[0]): + continue + + timestamps = group.index + + records.append( + { + "country": country, + "gap_start": timestamps[0], + "gap_end": timestamps[-1], + "gap_hours": len(timestamps), + "touches_start_boundary": ( + timestamps[0] == first_timestamp + ), + "touches_end_boundary": ( + timestamps[-1] == last_timestamp + ), + } + ) + + report = pd.DataFrame.from_records( + records, + columns=columns, + ) + + if report.empty: + return report + + return report.sort_values( + [ + "country", + "gap_start", + ] + ).reset_index(drop=True) + + +def _get_method(rule: Mapping[str, Any]) -> str: + try: + method = rule["method"] + except KeyError as error: + raise ValueError( + "Each gap-filling rule must define a 'method'." + ) from error + + if not isinstance(method, str): + raise TypeError( + "Gap-filling rule 'method' must be a string." + ) + + return method + + +def _get_rule_name( + rule: Mapping[str, Any], +) -> str: + try: + name = rule["name"] + except KeyError as error: + raise ValueError( + "Each gap-filling rule must define a 'name'." + ) from error + + if not isinstance(name, str): + raise TypeError( + "Gap-filling rule 'name' must be a string." + ) + + if not name: + raise ValueError( + "Gap-filling rule 'name' must not be empty." + ) + + return name + + +def _log_rule_results( + *, + rule_name: str, + method: str, + newly_filled: pd.DataFrame, +) -> None: + total = int( + newly_filled.to_numpy().sum() + ) + + logger.info( + "Gap-filling rule '%s' using method '%s' filled %s values.", + rule_name, + method, + total, + ) + + for country, count in newly_filled.sum().items(): + count = int(count) + + if count: + logger.info( + "%s: %s values filled using rule '%s'.", + country, + count, + rule_name, + ) + + +def _validate_config(config: Mapping[str, Any]) -> None: + if not isinstance(config, Mapping): + raise TypeError( + "Gap-filling configuration must be a mapping." + ) + + if "mode" not in config: + raise ValueError( + "Gap-filling configuration must define 'mode'." + ) + + mode = config["mode"] + + if not isinstance(mode, str): + raise TypeError( + "Gap-filling configuration 'mode' must be a string." + ) + + supported_modes = { + "off", + "basic", + "advanced", + } + + if mode not in supported_modes: + raise ValueError( + "Unsupported gap-filling mode " + f"{mode!r}. Expected one of " + f"{sorted(supported_modes)}." + ) + + if "rules" not in config: + raise ValueError( + "Gap-filling configuration must define 'rules'." + ) + + if not isinstance(config["rules"], Sequence) or isinstance( + config["rules"], + (str, bytes), + ): + raise TypeError( + "Gap-filling configuration 'rules' must be an ordered sequence." + ) + + if mode in {"basic", "advanced"} and not config["rules"]: + raise ValueError( + "At least one gap-filling rule is required when " + f"gap-filling mode is {mode!r}." + ) + + +def _validate_cleaning_method( + *, + load: pd.DataFrame, + cleaning_method: pd.DataFrame, +) -> None: + if not isinstance(cleaning_method, pd.DataFrame): + raise TypeError( + "Cleaning method must be a pandas DataFrame." + ) + + if not cleaning_method.index.equals(load.index): + raise ValueError( + "Cleaning-method provenance must use the same " + "index as the load data." + ) + + if not cleaning_method.columns.equals(load.columns): + raise ValueError( + "Cleaning-method provenance must use the same " + "columns as the load data." + ) + + missing_observed_provenance = ( + load.notna() + & cleaning_method.isna() + ) + + if missing_observed_provenance.any().any(): + count = int( + missing_observed_provenance.to_numpy().sum() + ) + + raise ValueError( + "Cleaning-method provenance is missing for " + f"{count} observed load values." + ) + + provenance_for_missing_values = ( + load.isna() + & cleaning_method.notna() + ) + + if provenance_for_missing_values.any().any(): + count = int( + provenance_for_missing_values.to_numpy().sum() + ) + + raise ValueError( + "Cleaning-method provenance is already assigned " + f"to {count} missing load values." + ) diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index a3812e7..d86fc4e 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -7,13 +7,9 @@ import pandas as pd -from cleaning.average_periods import METHOD_NAME as AVERAGE_PERIODS -from cleaning.average_periods import apply_average_periods +from cleaning.advanced.gap_report import build_gap_report +from cleaning.basic.pipeline import fill_basic_gaps from cleaning.combine_sources import combine_sources -from cleaning.copy_period import METHOD_NAME as COPY_PERIOD -from cleaning.copy_period import apply_copy_period -from cleaning.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION -from cleaning.linear_interpolation import apply_linear_interpolation from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank logger = logging.getLogger(__name__) @@ -41,7 +37,7 @@ def clean_demand( priority=source_priority, ) - cleaned, cleaning_method = fill_gaps( + cleaned, cleaning_method = fill_basic_gaps( combined, cleaning_method=cleaning_method, config=gap_filling_config, @@ -79,454 +75,3 @@ def clean_demand( cleaning_method_rank, gap_report ) - - -def fill_gaps( - load: pd.DataFrame, - *, - cleaning_method: pd.DataFrame, - config: Mapping[str, Any], -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply configured gap-filling rules and record method provenance. - - Parameters - ---------- - load: - Hourly demand data indexed by timestamp, with one column per country. - cleaning_method: - Per-cell cleaning-method provenance for the observed input values. - Missing input values should contain ``pd.NA``. - config: - Gap-filling configuration containing ``mode`` and ``rules``. - - Returns - ------- - filled: - Load after applying the configured rules. If gap filling is disabled, - this is an unchanged copy of ``load``. - cleaning_method: - Per-cell provenance containing the observed-source identifier, - configured gap-filling rule name, or ``missing``. - """ - _validate_load(load) - _validate_cleaning_method( - load=load, - cleaning_method=cleaning_method, - ) - _validate_config(config) - - filled = load.copy() - cleaning_method = cleaning_method.copy() - - mode = config["mode"] - - if mode == "off": - logger.info( - "Gap filling is disabled because mode is 'off'." - ) - - cleaning_method = cleaning_method.fillna( - "missing" - ) - - return filled, cleaning_method - - rules = config["rules"] - original_gap_duration = calculate_missing_run_durations( - load - ) - - for rule in rules: - method = _get_method(rule) - rule_name = _get_rule_name(rule) - - if method == LINEAR_INTERPOLATION: - filled, newly_filled = apply_linear_interpolation( - filled, - max_gap=rule["max_gap"], - original_gap_duration=original_gap_duration, - ) - - elif method == AVERAGE_PERIODS: - filled, newly_filled = apply_average_periods( - filled, - max_gap=rule["max_gap"], - source_offsets=rule["source_offsets"], - original_gap_duration=original_gap_duration, - ) - - elif method == COPY_PERIOD: - filled, newly_filled = apply_copy_period( - filled, - max_gap=rule["max_gap"], - source_offset=rule["source_offset"], - require_complete_source=rule.get( - "require_complete_source", - True, - ), - original_gap_duration=original_gap_duration, - ) - - else: - raise ValueError( - f"Unsupported gap-filling method: {method!r}" - ) - - cleaning_method = cleaning_method.mask( - newly_filled, - rule_name, - ) - - _log_rule_results( - rule_name=rule_name, - method=method, - newly_filled=newly_filled, - ) - - cleaning_method = cleaning_method.fillna( - "missing" - ) - - unresolved = int( - filled.isna().to_numpy().sum() - ) - - logger.info( - "Gap filling completed with %s unresolved values.", - unresolved, - ) - - return filled, cleaning_method - - -def calculate_missing_run_durations( - load: pd.DataFrame, -) -> pd.DataFrame: - """Return the original duration of each missing run. - - Observed values receive a duration of zero. - """ - timestep = _infer_regular_timestep(load.index) - - durations = pd.DataFrame( - pd.Timedelta(0), - index=load.index, - columns=load.columns, - ) - - for column in load.columns: - missing = load[column].isna() - group_ids = missing.ne(missing.shift()).cumsum() - - run_lengths = ( - missing.groupby(group_ids) - .transform("sum") - .where(missing, 0) - ) - - durations[column] = run_lengths * timestep - - return durations - - -def build_gap_report( - load: pd.DataFrame, - *, - enabled: bool, -) -> pd.DataFrame: - """Describe contiguous unresolved gaps in cleaned load data. - - An empty report with the expected columns is returned when reporting - is disabled or when no unresolved gaps remain. - """ - columns = [ - "country", - "gap_start", - "gap_end", - "gap_hours", - "touches_start_boundary", - "touches_end_boundary", - ] - - if not enabled: - return pd.DataFrame(columns=columns) - - _validate_load(load) - - records: list[dict[str, Any]] = [] - - first_timestamp = load.index[0] - last_timestamp = load.index[-1] - - for country in load.columns: - missing = load[country].isna() - - if not missing.any(): - continue - - group_ids = missing.ne( - missing.shift(fill_value=False) - ).cumsum() - - for _, group in missing.groupby(group_ids): - if not bool(group.iloc[0]): - continue - - timestamps = group.index - - records.append( - { - "country": country, - "gap_start": timestamps[0], - "gap_end": timestamps[-1], - "gap_hours": len(timestamps), - "touches_start_boundary": ( - timestamps[0] == first_timestamp - ), - "touches_end_boundary": ( - timestamps[-1] == last_timestamp - ), - } - ) - - report = pd.DataFrame.from_records( - records, - columns=columns, - ) - - if report.empty: - return report - - return report.sort_values( - [ - "country", - "gap_start", - ] - ).reset_index(drop=True) - - -def _get_method(rule: Mapping[str, Any]) -> str: - try: - method = rule["method"] - except KeyError as error: - raise ValueError( - "Each gap-filling rule must define a 'method'." - ) from error - - if not isinstance(method, str): - raise TypeError( - "Gap-filling rule 'method' must be a string." - ) - - return method - - -def _get_rule_name( - rule: Mapping[str, Any], -) -> str: - try: - name = rule["name"] - except KeyError as error: - raise ValueError( - "Each gap-filling rule must define a 'name'." - ) from error - - if not isinstance(name, str): - raise TypeError( - "Gap-filling rule 'name' must be a string." - ) - - if not name: - raise ValueError( - "Gap-filling rule 'name' must not be empty." - ) - - return name - - -def _validate_load(load: pd.DataFrame) -> None: - if not isinstance(load, pd.DataFrame): - raise TypeError("Load must be a pandas DataFrame.") - - if load.empty: - raise ValueError("Load dataframe is empty.") - - timestep = _infer_regular_timestep(load.index) - - if timestep != pd.Timedelta(hours=1): - raise ValueError( - "Gap filling currently expects hourly load data. " - f"Found timestep {timestep}." - ) - - if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in load.dtypes): - raise TypeError("All load columns must be numeric.") - - -def _infer_regular_timestep( - index: pd.Index, -) -> pd.Timedelta: - if not isinstance(index, pd.DatetimeIndex): - raise TypeError( - "Load data must use a pandas DatetimeIndex." - ) - - if not index.is_monotonic_increasing: - raise ValueError( - "Load timestamps must be sorted in increasing order." - ) - - if index.has_duplicates: - raise ValueError( - "Load timestamps must not contain duplicates." - ) - - differences = index.to_series().diff().dropna() - - if differences.empty: - raise ValueError( - "At least two timestamps are required for gap filling." - ) - - timestep = differences.iloc[0] - - if not differences.eq(timestep).all(): - raise ValueError( - "Load data must have a complete, regular time index " - "before gap filling." - ) - - return timestep - - -def _log_rule_results( - *, - rule_name: str, - method: str, - newly_filled: pd.DataFrame, -) -> None: - total = int( - newly_filled.to_numpy().sum() - ) - - logger.info( - "Gap-filling rule '%s' using method '%s' filled %s values.", - rule_name, - method, - total, - ) - - for country, count in newly_filled.sum().items(): - count = int(count) - - if count: - logger.info( - "%s: %s values filled using rule '%s'.", - country, - count, - rule_name, - ) - - -def _validate_config(config: Mapping[str, Any]) -> None: - if not isinstance(config, Mapping): - raise TypeError( - "Gap-filling configuration must be a mapping." - ) - - if "mode" not in config: - raise ValueError( - "Gap-filling configuration must define 'mode'." - ) - - mode = config["mode"] - - if not isinstance(mode, str): - raise TypeError( - "Gap-filling configuration 'mode' must be a string." - ) - - supported_modes = { - "off", - "basic", - "advanced", - } - - if mode not in supported_modes: - raise ValueError( - "Unsupported gap-filling mode " - f"{mode!r}. Expected one of " - f"{sorted(supported_modes)}." - ) - - if "rules" not in config: - raise ValueError( - "Gap-filling configuration must define 'rules'." - ) - - if not isinstance(config["rules"], Sequence) or isinstance( - config["rules"], - (str, bytes), - ): - raise TypeError( - "Gap-filling configuration 'rules' must be an ordered sequence." - ) - - if mode in {"basic", "advanced"} and not config["rules"]: - raise ValueError( - "At least one gap-filling rule is required when " - f"gap-filling mode is {mode!r}." - ) - - -def _validate_cleaning_method( - *, - load: pd.DataFrame, - cleaning_method: pd.DataFrame, -) -> None: - if not isinstance(cleaning_method, pd.DataFrame): - raise TypeError( - "Cleaning method must be a pandas DataFrame." - ) - - if not cleaning_method.index.equals(load.index): - raise ValueError( - "Cleaning-method provenance must use the same " - "index as the load data." - ) - - if not cleaning_method.columns.equals(load.columns): - raise ValueError( - "Cleaning-method provenance must use the same " - "columns as the load data." - ) - - missing_observed_provenance = ( - load.notna() - & cleaning_method.isna() - ) - - if missing_observed_provenance.any().any(): - count = int( - missing_observed_provenance.to_numpy().sum() - ) - - raise ValueError( - "Cleaning-method provenance is missing for " - f"{count} observed load values." - ) - - provenance_for_missing_values = ( - load.isna() - & cleaning_method.notna() - ) - - if provenance_for_missing_values.any().any(): - count = int( - provenance_for_missing_values.to_numpy().sum() - ) - - raise ValueError( - "Cleaning-method provenance is already assigned " - f"to {count} missing load values." - ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/validation.py b/workflow/scripts/cleaning/validation.py new file mode 100644 index 0000000..76ec32f --- /dev/null +++ b/workflow/scripts/cleaning/validation.py @@ -0,0 +1,63 @@ +"""Shared validation for cleaned electricity-demand data.""" + +import pandas as pd + + +def validate_load(load: pd.DataFrame) -> None: + """Validate the structure of hourly electricity-demand data.""" + if not isinstance(load, pd.DataFrame): + raise TypeError("Load must be a pandas DataFrame.") + + if load.empty: + raise ValueError("Load dataframe is empty.") + + timestep = infer_regular_timestep(load.index) + + if timestep != pd.Timedelta(hours=1): + raise ValueError( + "Gap filling currently expects hourly load data. " + f"Found timestep {timestep}." + ) + + if not all( + pd.api.types.is_numeric_dtype(dtype) + for dtype in load.dtypes + ): + raise TypeError("All load columns must be numeric.") + + +def infer_regular_timestep( + index: pd.Index, +) -> pd.Timedelta: + """Infer and validate the regular timestep of a datetime index.""" + if not isinstance(index, pd.DatetimeIndex): + raise TypeError( + "Load data must use a pandas DatetimeIndex." + ) + + if not index.is_monotonic_increasing: + raise ValueError( + "Load timestamps must be sorted in increasing order." + ) + + if index.has_duplicates: + raise ValueError( + "Load timestamps must not contain duplicates." + ) + + differences = index.to_series().diff().dropna() + + if differences.empty: + raise ValueError( + "At least two timestamps are required for gap filling." + ) + + timestep = differences.iloc[0] + + if not differences.eq(timestep).all(): + raise ValueError( + "Load data must have a complete, regular time index " + "before gap filling." + ) + + return timestep \ No newline at end of file From 280e0446c055ed33d6506793e7ea022e3e397e61 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 6 Aug 2026 16:22:49 +0200 Subject: [PATCH 016/241] Added the advanced planning framework and schema validation changes. --- config/config.yaml | 56 ++- tests/integration/test_config.yaml | 56 ++- tests/unit/test_advanced_apply.py | 110 +++++ .../test_advanced_construct_from_sources.py | 44 ++ tests/unit/test_advanced_plan.py | 316 +++++++++++++ tests/unit/test_cleaning_pipeline.py | 66 +-- tests/unit/test_config_schema.py | 48 ++ workflow/internal/config.schema.yaml | 214 ++++++++- workflow/scripts/cleaning/advanced/apply.py | 55 +++ .../advanced/construct_from_sources.py | 21 + .../{ => advanced}/external_profile.py | 0 workflow/scripts/cleaning/advanced/plan.py | 428 ++++++++++++++++++ workflow/scripts/cleaning/pipeline.py | 11 +- 13 files changed, 1337 insertions(+), 88 deletions(-) create mode 100644 tests/unit/test_advanced_apply.py create mode 100644 tests/unit/test_advanced_construct_from_sources.py create mode 100644 tests/unit/test_advanced_plan.py create mode 100644 tests/unit/test_config_schema.py create mode 100644 workflow/scripts/cleaning/advanced/apply.py create mode 100644 workflow/scripts/cleaning/advanced/construct_from_sources.py rename workflow/scripts/cleaning/{ => advanced}/external_profile.py (100%) create mode 100644 workflow/scripts/cleaning/advanced/plan.py diff --git a/config/config.yaml b/config/config.yaml index 09ff5d7..56fb019 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -12,27 +12,37 @@ load_sources: # Gap-filling rules are applied in the listed order. gap_filling: - #sets the data cleaning mode. Options: off,basic,Advanced + # Sets the data-cleaning mode. Options: off, basic, advanced. mode: advanced - rules: - # interpolates across tiny gaps where <=3 hours are missing - - name: interpolate_short_gaps - method: linear_interpolation - max_gap: 3h - # takes the average of the same hour, one week in the future and one week in the past - - name: average_adjacent_weeks - method: average_periods - max_gap: 326h - source_offsets: - - -7d - - 7d - #copies from the previous period, maximum of up to 1 week - - name: copy_previous_week - method: copy_period - max_gap: 168h - source_offset: -168h - #copies from the following period, maximum of up to 1 week - - name: copy_following_week - method: copy_period - max_gap: 168h - source_offset: 168h \ No newline at end of file + + basic: + # Basic gap-filling rules are applied in the listed order. + rules: + # Interpolates across tiny gaps where no more than 3 hours are missing. + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + + # Takes the average of the same hour one week earlier and later. + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + + # Copies from the previous week for gaps of up to one week. + - name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h + + # Copies from the following week for gaps of up to one week. + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h + + advanced: + # Explicit country-period instructions for unresolved cases. + overrides: {} \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 11a7e6a..f6c0230 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -9,27 +9,37 @@ integration_test: - opsd_api gap_filling: - #sets the data cleaning mode. Options: off,basic,Advanced + # Sets the data-cleaning mode. Options: off, basic, advanced. mode: advanced - rules: - # interpolates across tiny gaps where <=3 hours are missing - - name: interpolate_short_gaps - method: linear_interpolation - max_gap: 3h - # takes the average of the same hour, one week in the future and one week in the past - - name: average_adjacent_weeks - method: average_periods - max_gap: 326h - source_offsets: - - -7d - - 7d - #copies from the previous period, maximum of up to 1 week - - name: copy_previous_week - method: copy_period - max_gap: 168h - source_offset: -168h - #copies from the following period, maximum of up to 1 week - - name: copy_following_week - method: copy_period - max_gap: 168h - source_offset: 168h \ No newline at end of file + + basic: + # Basic gap-filling rules are applied in the listed order. + rules: + # Interpolates across tiny gaps where no more than 3 hours are missing. + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + + # Takes the average of the same hour one week earlier and later. + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + + # Copies from the previous week for gaps of up to one week. + - name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h + + # Copies from the following week for gaps of up to one week. + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h + + advanced: + # Explicit country-period instructions for unresolved cases. + overrides: {} \ No newline at end of file diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py new file mode 100644 index 0000000..1537131 --- /dev/null +++ b/tests/unit/test_advanced_apply.py @@ -0,0 +1,110 @@ +"""Tests for applying advanced auxiliary-fill rules.""" + +import pandas as pd +import pytest + +from cleaning.advanced.apply import ( + apply_auxiliary_fill_rule, +) + + +def _load() -> pd.DataFrame: + index = pd.date_range( + "2021-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + return pd.DataFrame( + { + "ALB": [ + 1.0, + pd.NA, + 3.0, + ], + }, + index=index, + dtype="Float64", + ) + + +def test_external_profile_execution_is_not_implemented() -> None: + rule = { + "method": "external_profile", + } + + with pytest.raises( + NotImplementedError, + match="external_profile", + ): + apply_auxiliary_fill_rule( + _load(), + rule_name="external_albania", + rule=rule, + ) + + +def test_construct_from_sources_dispatches_to_placeholder() -> None: + rule = { + "method": "construct_from_sources", + } + + with pytest.raises( + NotImplementedError, + match="construct_from_sources", + ): + apply_auxiliary_fill_rule( + _load(), + rule_name="construct_albania", + rule=rule, + ) + + +def test_manual_review_cannot_be_applied_automatically() -> None: + rule = { + "method": "manual_review", + } + + with pytest.raises( + ValueError, + match="requires manual review", + ): + apply_auxiliary_fill_rule( + _load(), + rule_name="review_albania", + rule=rule, + ) + + +def test_leave_missing_returns_unchanged_copy() -> None: + load = _load() + + result = apply_auxiliary_fill_rule( + load, + rule_name="leave_albania_missing", + rule={ + "method": "leave_missing", + }, + ) + + pd.testing.assert_frame_equal( + result, + load, + ) + + assert result is not load + + +def test_rejects_unsupported_method() -> None: + with pytest.raises( + ValueError, + match="Unsupported advanced-fill method", + ): + apply_auxiliary_fill_rule( + _load(), + rule_name="invalid_rule", + rule={ + "method": "unknown", + }, + ) diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py new file mode 100644 index 0000000..b2c2795 --- /dev/null +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -0,0 +1,44 @@ +"""Tests for source-based advanced profile construction.""" + +import pandas as pd +import pytest +from cleaning.advanced.construct_from_sources import construct_from_sources + + +def test_construct_from_sources_is_not_yet_implemented() -> None: + index = pd.date_range( + "2021-01-01", + periods=2, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + {"ALB": [1.0, 2.0]}, + index=index, + ) + + rule = { + "country": "ALB", + "start": "2021-01-01", + "end": "2021-01-01 01:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "ALB", + "start": "2022-01-01", + "end": "2022-01-01 01:00", + } + ], + } + + with pytest.raises( + NotImplementedError, + match="construct_from_sources", + ): + construct_from_sources( + load, + rule_name="replace_albania", + rule=rule, + ) \ No newline at end of file diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py new file mode 100644 index 0000000..ae9450f --- /dev/null +++ b/tests/unit/test_advanced_plan.py @@ -0,0 +1,316 @@ +"""Tests for advanced auxiliary-fill planning.""" +import pandas as pd +import pytest +from cleaning.advanced.plan import ( + build_auxiliary_fill_plan, + validate_auxiliary_fill_rule, +) + + +def test_validate_construct_from_sources_rule() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MKD", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + }, + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "weight": 2, + }, + ], + } + + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_validate_external_profile_rule() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "external_profile", + } + + validate_auxiliary_fill_rule( + "external_albania_2023", + rule, + ) + +def test_construct_from_sources_requires_sources() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + } + + with pytest.raises( + ValueError, + match="must define 'sources'", + ): + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_source_weight_must_be_positive() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "weight": 0, + } + ], + } + + with pytest.raises( + ValueError, + match="must be greater than zero", + ): + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_validate_construct_from_sources_with_scaling() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MKD", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + } + ], + "scaling": { + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2022-01-01", + "end": "2022-12-31 23:00", + }, + { + "country": "ALB", + "start": "2024-01-01", + "end": "2024-12-31 23:00", + "weight": 2, + }, + ], + }, + } + + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_match_energy_scaling_requires_target_sources() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + } + ], + "scaling": { + "method": "match_energy", + }, + } + + with pytest.raises( + ValueError, + match="must define 'target_sources'", + ): + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_rejects_unsupported_scaling_method() -> None: + rule = { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + } + ], + "scaling": { + "method": "unknown", + "target_sources": [ + { + "country": "ALB", + "start": "2022-01-01", + "end": "2022-12-31 23:00", + } + ], + }, + } + + with pytest.raises( + ValueError, + match="Unsupported scaling method", + ): + validate_auxiliary_fill_rule( + "replace_albania_2023", + rule, + ) + +def test_build_auxiliary_fill_plan_normalizes_rules() -> None: + rules = { + "external_albania": { + "country": "ALB", + "start": "2024-01-01", + "end": "2024-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "external_profile", + }, + "construct_albania": { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + }, + { + "country": "MKD", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + "weight": 2, + }, + ], + "scaling": { + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2022-01-01", + "end": "2022-12-31 23:00", + } + ], + }, + }, + } + + result = build_auxiliary_fill_plan(rules) + + expected = pd.DataFrame( + { + "rule_name": [ + "construct_albania", + "external_albania", + ], + "country": [ + "ALB", + "ALB", + ], + "target_start": [ + pd.Timestamp( + "2023-01-01", + tz="UTC", + ), + pd.Timestamp( + "2024-01-01", + tz="UTC", + ), + ], + "target_end": [ + pd.Timestamp( + "2023-12-31 23:00", + tz="UTC", + ), + pd.Timestamp( + "2024-12-31 23:00", + tz="UTC", + ), + ], + "scope": [ + "overwrite_entire_period", + "overwrite_entire_period", + ], + "method": [ + "construct_from_sources", + "external_profile", + ], + "status": [ + "ready", + "not_implemented", + ], + "source_count": [ + 2, + 0, + ], + "scaling_method": [ + "match_energy", + None, + ], + } + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) + +def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: + result = build_auxiliary_fill_plan({}) + + expected = pd.DataFrame( + columns=[ + "rule_name", + "country", + "target_start", + "target_end", + "scope", + "method", + "status", + "source_count", + "scaling_method", + ] + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index a0eb086..fa6314b 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -75,20 +75,25 @@ def test_clean_demand_records_methods_and_ranks() -> None: gap_filling_config = { "mode": "basic", - "rules": [ - { - "name": "interpolate_short_gaps", - "method": "linear_interpolation", - "max_gap": "3h", - }, - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - "require_complete_source": True, - }, - ], + "basic": { + "rules": [ + { + "name": "interpolate_short_gaps", + "method": "linear_interpolation", + "max_gap": "3h", + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + "require_complete_source": True, + }, + ], + }, + "advanced": { + "overrides": {}, + }, } ( @@ -346,20 +351,25 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: gap_filling_config = { "mode": "advanced", - "rules": [ - { - "name": "interpolate_short_gaps", - "method": "linear_interpolation", - "max_gap": "3h", - }, - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - "require_complete_source": True, - }, - ], + "basic": { + "rules": [ + { + "name": "interpolate_short_gaps", + "method": "linear_interpolation", + "max_gap": "3h", + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + "require_complete_source": True, + }, + ], + }, + "advanced": { + "overrides": {}, + }, } ( diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py new file mode 100644 index 0000000..9f8bfc5 --- /dev/null +++ b/tests/unit/test_config_schema.py @@ -0,0 +1,48 @@ +"""Tests for the module configuration schema.""" + +from pathlib import Path + +import yaml +from jsonschema import Draft202012Validator +from jsonschema.exceptions import ValidationError + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +CONFIG_PATH = REPOSITORY_ROOT / "config" / "config.yaml" +SCHEMA_PATH = ( + REPOSITORY_ROOT + / "workflow" + / "internal" + / "config.schema.yaml" +) + + +def test_default_config_matches_schema() -> None: + """Validate the default user configuration against its schema.""" + with CONFIG_PATH.open(encoding="utf-8") as config_file: + config = yaml.safe_load(config_file) + + with SCHEMA_PATH.open(encoding="utf-8") as schema_file: + schema = yaml.safe_load(schema_file) + + validator = Draft202012Validator(schema) + errors = sorted( + validator.iter_errors(config), + key=lambda error: list(error.absolute_path), + ) + + assert not errors, "\n".join( + _format_validation_error(error) + for error in errors + ) + + +def _format_validation_error( + error: ValidationError, +) -> str: + """Format one schema-validation error with its config location.""" + path = ".".join( + str(part) + for part in error.absolute_path + ) + + return f"{path or ''}: {error.message}" \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 438da9b..0972541 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -37,6 +37,169 @@ $defs: Two or more unique, non-zero signed durations identifying the source periods to average. + hourly_timestamp: + type: string + pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])([ T]([01]\d|2[0-3]):00)?$' + description: > + Hourly timestamp interpreted as UTC. May be written as YYYY-MM-DD + for midnight or as YYYY-MM-DD HH:00. + + auxiliary_source: + type: object + properties: + country: + type: string + pattern: '^[A-Z]{3}$' + description: > + ISO alpha-3 country code supplying the source profile. + + start: + $ref: "#/$defs/hourly_timestamp" + + end: + $ref: "#/$defs/hourly_timestamp" + + weight: + type: number + exclusiveMinimum: 0 + default: 1 + description: > + Relative weight assigned to this source when source profiles + are combined using a weighted mean. + + required: + - country + - start + - end + + additionalProperties: false + + auxiliary_sources: + type: array + minItems: 1 + items: + $ref: "#/$defs/auxiliary_source" + description: > + One or more country-period profiles combined using a weighted mean. + + auxiliary_scaling: + type: object + properties: + method: + const: match_energy + + target_sources: + $ref: "#/$defs/auxiliary_sources" + description: > + Reference periods whose weighted-mean energy determines the + energy total of the constructed target profile. + + required: + - method + - target_sources + + additionalProperties: false + + auxiliary_fill_rule_base: + type: object + properties: + country: + type: string + pattern: '^[A-Z]{3}$' + description: > + ISO alpha-3 code of the country whose demand values are targeted. + + start: + $ref: "#/$defs/hourly_timestamp" + + end: + $ref: "#/$defs/hourly_timestamp" + + scope: + type: string + enum: + - fill_gaps_within_period + - overwrite_entire_period + description: > + Whether the instruction fills only missing values within the + target period or replaces all values in that period. + + method: + type: string + description: > + Method used to obtain or handle the target demand values. + + required: + - country + - start + - end + - scope + - method + + + construct_from_sources_rule: + allOf: + - $ref: "#/$defs/auxiliary_fill_rule_base" + + - type: object + properties: + method: + const: construct_from_sources + + sources: + $ref: "#/$defs/auxiliary_sources" + + scaling: + $ref: "#/$defs/auxiliary_scaling" + + required: + - sources + + unevaluatedProperties: false + + + external_profile_rule: + allOf: + - $ref: "#/$defs/auxiliary_fill_rule_base" + + - type: object + properties: + method: + const: external_profile + + unevaluatedProperties: false + + + manual_review_rule: + allOf: + - $ref: "#/$defs/auxiliary_fill_rule_base" + + - type: object + properties: + method: + const: manual_review + + unevaluatedProperties: false + + + leave_missing_rule: + allOf: + - $ref: "#/$defs/auxiliary_fill_rule_base" + + - type: object + properties: + method: + const: leave_missing + + unevaluatedProperties: false + + auxiliary_fill_rule: + oneOf: + - $ref: "#/$defs/construct_from_sources_rule" + - $ref: "#/$defs/external_profile_rule" + - $ref: "#/$defs/manual_review_rule" + - $ref: "#/$defs/leave_missing_rule" + gap_filling_rule_base: type: object properties: @@ -165,8 +328,9 @@ properties: gap_filling: type: object description: > - Ordered gap-filling configuration. Rules are applied in the order - provided, and that order contributes to cleaning-method ranking. + Gap-filling configuration. Basic rules are applied in order using + data within the requested temporal scope. Advanced mode additionally + supports auxiliary-data planning and filling. properties: mode: @@ -178,19 +342,47 @@ properties: default: basic description: > Gap-filling mode. 'off' disables gap filling, 'basic' applies - configured rules using data available within the requested temporal - scope, and 'advanced' additionally uses auxiliary-data planning and - advanced filling strategies. + configured deterministic rules, and 'advanced' additionally + produces and applies auxiliary-fill instructions. + + basic: + type: object + properties: + rules: + type: array + default: [] + items: + $ref: "#/$defs/gap_filling_rule" + description: > + Ordered deterministic gap-filling rules. + + required: + - rules + + additionalProperties: false + + advanced: + type: object + properties: + overrides: + type: object + default: {} + description: > + Named country-period instructions that override general + advanced gap-handling decisions. + + additionalProperties: + $ref: "#/$defs/auxiliary_fill_rule" + + required: + - overrides - rules: - type: array - default: [] - items: - $ref: "#/$defs/gap_filling_rule" + additionalProperties: false required: - mode - - rules + - basic + - advanced additionalProperties: false diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py new file mode 100644 index 0000000..25b8eaf --- /dev/null +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -0,0 +1,55 @@ +"""Apply validated advanced auxiliary-fill rules.""" + +from collections.abc import Mapping +from typing import Any + +import pandas as pd + +from cleaning.advanced.construct_from_sources import ( + METHOD_NAME as CONSTRUCT_FROM_SOURCES, +) +from cleaning.advanced.construct_from_sources import ( + construct_from_sources, +) +from cleaning.advanced.external_profile import ( + METHOD_NAME as EXTERNAL_PROFILE, +) + +MANUAL_REVIEW = "manual_review" +LEAVE_MISSING = "leave_missing" + + +def apply_auxiliary_fill_rule( + load: pd.DataFrame, + *, + rule_name: str, + rule: Mapping[str, Any], +) -> pd.DataFrame: + """Apply one validated advanced-fill rule.""" + method = rule["method"] + + if method == CONSTRUCT_FROM_SOURCES: + return construct_from_sources( + load, + rule_name=rule_name, + rule=rule, + ) + + if method == EXTERNAL_PROFILE: + raise NotImplementedError( + "Advanced-fill method 'external_profile' is recognized " + "but has not yet been implemented." + ) + + if method == MANUAL_REVIEW: + raise ValueError( + f"Advanced-fill rule {rule_name!r} requires manual " + "review and cannot be applied automatically." + ) + + if method == LEAVE_MISSING: + return load.copy() + + raise ValueError( + f"Unsupported advanced-fill method {method!r}." + ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/construct_from_sources.py new file mode 100644 index 0000000..81d81cf --- /dev/null +++ b/workflow/scripts/cleaning/advanced/construct_from_sources.py @@ -0,0 +1,21 @@ +"""Construct an auxiliary demand profile from configured source periods.""" + +from collections.abc import Mapping +from typing import Any + +import pandas as pd + +METHOD_NAME = "construct_from_sources" + + +def construct_from_sources( + load: pd.DataFrame, + *, + rule_name: str, + rule: Mapping[str, Any], +) -> pd.DataFrame: + """Construct a target profile from configured country-period sources.""" + raise NotImplementedError( + "Advanced-fill method 'construct_from_sources' " + "has not yet been implemented." + ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/external_profile.py b/workflow/scripts/cleaning/advanced/external_profile.py similarity index 100% rename from workflow/scripts/cleaning/external_profile.py rename to workflow/scripts/cleaning/advanced/external_profile.py diff --git a/workflow/scripts/cleaning/advanced/plan.py b/workflow/scripts/cleaning/advanced/plan.py new file mode 100644 index 0000000..c633cd1 --- /dev/null +++ b/workflow/scripts/cleaning/advanced/plan.py @@ -0,0 +1,428 @@ +"""Validate and build advanced auxiliary-fill plans.""" + +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + +from cleaning.advanced.construct_from_sources import ( + METHOD_NAME as CONSTRUCT_FROM_SOURCES, +) +from cleaning.advanced.external_profile import METHOD_NAME as EXTERNAL_PROFILE + +FILL_GAPS_WITHIN_PERIOD = "fill_gaps_within_period" +OVERWRITE_ENTIRE_PERIOD = "overwrite_entire_period" + +MANUAL_REVIEW = "manual_review" +LEAVE_MISSING = "leave_missing" + + +def validate_auxiliary_fill_rule( + rule_name: str, + rule: Mapping[str, Any], +) -> None: + """Validate one configured advanced-fill rule.""" + if not isinstance(rule_name, str): + raise TypeError( + "Advanced-fill rule name must be a string." + ) + + if not rule_name: + raise ValueError( + "Advanced-fill rule name must not be empty." + ) + + if not isinstance(rule, Mapping): + raise TypeError( + f"Advanced-fill rule {rule_name!r} must be a mapping." + ) + + country = _get_required_string( + rule, + key="country", + rule_name=rule_name, + ) + start = _get_required_timestamp( + rule, + key="start", + rule_name=rule_name, + ) + end = _get_required_timestamp( + rule, + key="end", + rule_name=rule_name, + ) + + if end < start: + raise ValueError( + f"Advanced-fill rule {rule_name!r} has an end " + "timestamp before its start timestamp." + ) + + scope = _get_required_string( + rule, + key="scope", + rule_name=rule_name, + ) + method = _get_required_string( + rule, + key="method", + rule_name=rule_name, + ) + + supported_scopes = { + FILL_GAPS_WITHIN_PERIOD, + OVERWRITE_ENTIRE_PERIOD, + } + + if scope not in supported_scopes: + raise ValueError( + f"Unsupported scope {scope!r} in advanced-fill rule " + f"{rule_name!r}. Expected one of " + f"{sorted(supported_scopes)}." + ) + + supported_methods = { + CONSTRUCT_FROM_SOURCES, + EXTERNAL_PROFILE, + MANUAL_REVIEW, + LEAVE_MISSING, + } + + if method not in supported_methods: + raise ValueError( + f"Unsupported method {method!r} in advanced-fill rule " + f"{rule_name!r}. Expected one of " + f"{sorted(supported_methods)}." + ) + + if method == CONSTRUCT_FROM_SOURCES: + _validate_sources( + rule, + rule_name=rule_name, + ) + + if "scaling" in rule: + _validate_scaling( + rule["scaling"], + rule_name=rule_name, + ) + + elif method in { + MANUAL_REVIEW, + LEAVE_MISSING, + }: + if "sources" in rule: + raise ValueError( + f"Advanced-fill rule {rule_name!r} uses method " + f"{method!r} and must not define 'sources'." + ) + + # External-profile-specific configuration will be added when + # acquisition of external profiles is implemented. + +def _validate_sources( + rule: Mapping[str, Any], + *, + rule_name: str, +) -> None: + """Validate source references for source-based construction.""" + if "sources" not in rule: + raise ValueError( + f"Advanced-fill rule {rule_name!r} using method " + f"{CONSTRUCT_FROM_SOURCES!r} must define 'sources'." + ) + + sources = rule["sources"] + + if not isinstance(sources, Sequence) or isinstance( + sources, + (str, bytes), + ): + raise TypeError( + f"'sources' in advanced-fill rule {rule_name!r} " + "must be an ordered sequence." + ) + + if not sources: + raise ValueError( + f"'sources' in advanced-fill rule {rule_name!r} " + "must contain at least one source." + ) + + for position, source in enumerate(sources): + _validate_source( + source, + rule_name=rule_name, + position=position, + ) + + +def _validate_source( + source: object, + *, + rule_name: str, + position: int, + context: str = "source", +) -> None: + """Validate one country-period source reference.""" + if not isinstance(source, Mapping): + raise TypeError( + f"{context.capitalize()} {position} in advanced-fill rule " + f"{rule_name!r} must be a mapping." + ) + + _get_required_string( + source, + key="country", + rule_name=rule_name, + context=f"{context} {position}", + ) + + start = _get_required_timestamp( + source, + key="start", + rule_name=rule_name, + context=f"{context} {position}", + ) + end = _get_required_timestamp( + source, + key="end", + rule_name=rule_name, + context=f"{context} {position}", + ) + + if end < start: + raise ValueError( + f"{context.capitalize()} {position} in advanced-fill rule " + f"{rule_name!r} has an end timestamp before its " + "start timestamp." + ) + + weight = source.get("weight", 1.0) + + if not isinstance(weight, int | float): + raise TypeError( + f"Weight for {context} {position} in advanced-fill rule " + f"{rule_name!r} must be numeric." + ) + + if weight <= 0: + raise ValueError( + f"Weight for {context} {position} in advanced-fill rule " + f"{rule_name!r} must be greater than zero." + ) + + +def _get_required_string( + config: Mapping[str, Any], + *, + key: str, + rule_name: str, + context: str = "rule", +) -> str: + """Return one required non-empty string field.""" + if key not in config: + raise ValueError( + f"Advanced-fill {context} in rule {rule_name!r} " + f"must define {key!r}." + ) + + value = config[key] + + if not isinstance(value, str): + raise TypeError( + f"Advanced-fill {context} field {key!r} in rule " + f"{rule_name!r} must be a string." + ) + + if not value: + raise ValueError( + f"Advanced-fill {context} field {key!r} in rule " + f"{rule_name!r} must not be empty." + ) + + return value + + +def _get_required_timestamp( + config: Mapping[str, Any], + *, + key: str, + rule_name: str, + context: str = "rule", +) -> pd.Timestamp: + """Return one required timestamp as a UTC pandas timestamp.""" + if key not in config: + raise ValueError( + f"Advanced-fill {context} in rule {rule_name!r} " + f"must define {key!r}." + ) + + try: + timestamp = pd.Timestamp(config[key]) + except (TypeError, ValueError) as error: + raise ValueError( + f"Advanced-fill {context} field {key!r} in rule " + f"{rule_name!r} is not a valid timestamp." + ) from error + + if timestamp.tzinfo is None: + timestamp = timestamp.tz_localize("UTC") + else: + timestamp = timestamp.tz_convert("UTC") + + return timestamp + + +def _validate_scaling( + scaling: object, + *, + rule_name: str, +) -> None: + """Validate optional scaling configuration.""" + if not isinstance(scaling, Mapping): + raise TypeError( + f"'scaling' in advanced-fill rule {rule_name!r} " + "must be a mapping." + ) + + method = _get_required_string( + scaling, + key="method", + rule_name=rule_name, + context="scaling", + ) + + supported_methods = { + "match_energy", + } + + if method not in supported_methods: + raise ValueError( + f"Unsupported scaling method {method!r} in " + f"advanced-fill rule {rule_name!r}. Expected one of " + f"{sorted(supported_methods)}." + ) + + if "target_sources" not in scaling: + raise ValueError( + f"Scaling configuration in advanced-fill rule " + f"{rule_name!r} must define 'target_sources'." + ) + + target_sources = scaling["target_sources"] + + if not isinstance(target_sources, Sequence) or isinstance( + target_sources, + (str, bytes), + ): + raise TypeError( + f"'target_sources' in advanced-fill rule " + f"{rule_name!r} must be an ordered sequence." + ) + + if not target_sources: + raise ValueError( + f"'target_sources' in advanced-fill rule " + f"{rule_name!r} must contain at least one source." + ) + + for position, source in enumerate(target_sources): + _validate_source( + source, + rule_name=rule_name, + position=position, + context="scaling target source", + ) + +def build_auxiliary_fill_plan( + rules: Mapping[str, Mapping[str, Any]], +) -> pd.DataFrame: + """Validate and normalize configured advanced-fill rules.""" + if not isinstance(rules, Mapping): + raise TypeError( + "Advanced-fill rules must be provided as a mapping." + ) + + records: list[dict[str, Any]] = [] + + for rule_name, rule in rules.items(): + validate_auxiliary_fill_rule( + rule_name, + rule, + ) + + method = rule["method"] + + if method == CONSTRUCT_FROM_SOURCES: + status = "ready" + elif method == EXTERNAL_PROFILE: + status = "not_implemented" + elif method == MANUAL_REVIEW: + status = "manual_review" + elif method == LEAVE_MISSING: + status = "leave_missing" + else: + raise AssertionError( + f"Unhandled advanced-fill method: {method!r}" + ) + + scaling = rule.get("scaling") + + records.append( + { + "rule_name": rule_name, + "country": rule["country"], + "target_start": _get_required_timestamp( + rule, + key="start", + rule_name=rule_name, + ), + "target_end": _get_required_timestamp( + rule, + key="end", + rule_name=rule_name, + ), + "scope": rule["scope"], + "method": method, + "status": status, + "source_count": len( + rule.get("sources", []) + ), + "scaling_method": ( + scaling["method"] + if scaling is not None + else None + ), + } + ) + + columns = [ + "rule_name", + "country", + "target_start", + "target_end", + "scope", + "method", + "status", + "source_count", + "scaling_method", + ] + + plan = pd.DataFrame.from_records( + records, + columns=columns, + ) + + if plan.empty: + return plan + + return plan.sort_values( + [ + "country", + "target_start", + "rule_name", + ] + ).reset_index(drop=True) + diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index d86fc4e..eb8b6d2 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -37,17 +37,22 @@ def clean_demand( priority=source_priority, ) + basic_config = { + "mode": gap_filling_config["mode"], + "rules": gap_filling_config["basic"]["rules"], + } + cleaned, cleaning_method = fill_basic_gaps( combined, cleaning_method=cleaning_method, - config=gap_filling_config, + config=basic_config, ) - rules = gap_filling_config["rules"] + basic_rules = gap_filling_config["basic"]["rules"] cleaning_method_ranks = build_cleaning_method_ranks( source_priority=source_priority, - rules=rules, + rules=basic_rules, ) cleaning_method_rank = derive_cleaning_method_rank( From 8578036c90edb68186ebdc6d13191b366daf9c03 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 6 Aug 2026 16:50:26 +0200 Subject: [PATCH 017/241] Generates the auxiliary plan (part 1) --- tests/unit/test_cleaning_pipeline.py | 75 +++++++++++++++++++++ workflow/rules/clean.smk | 4 ++ workflow/scripts/clean_demand.py | 16 +++-- workflow/scripts/cleaning/basic/pipeline.py | 2 +- workflow/scripts/cleaning/pipeline.py | 19 +++++- 5 files changed, 109 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index fa6314b..f0a96e1 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -102,6 +102,7 @@ def test_clean_demand_records_methods_and_ranks() -> None: cleaning_method, cleaning_method_rank, gap_report, + auxiliary_fill_plan ) = clean_demand( sources, source_priority=[ @@ -378,6 +379,7 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: cleaning_method, _cleaning_method_rank, gap_report, + auxiliary_fill_plan ) = clean_demand( sources, source_priority=[ @@ -387,6 +389,19 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: gap_filling_config=gap_filling_config, ) + assert auxiliary_fill_plan.empty + assert list(auxiliary_fill_plan.columns) == [ + "rule_name", + "country", + "target_start", + "target_end", + "scope", + "method", + "status", + "source_count", + "scaling_method", + ] + assert cleaned.loc[ unresolved_timestamps, "AAA", @@ -568,3 +583,63 @@ def test_build_gap_report_returns_empty_report_when_no_gaps_remain() -> None: expected, ) +def test_advanced_mode_builds_auxiliary_fill_plan() -> None: + """Tests construction of the auxiliary fill plan.""" + index = pd.date_range( + "2021-01-01", + periods=3, + freq="h", + tz="UTC", + ) + source = pd.DataFrame( + {"ALB": [1.0, None, 3.0]}, + index=index, + ) + + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "overrides": { + "fill_albania_from_greece": { + "country": "ALB", + "start": "2021-01-01 01:00", + "end": "2021-01-01 01:00", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2021-01-01 01:00", + "end": "2021-01-01 01:00", + } + ], + } + }, + }, + } + + ( + _cleaned, + _data_source, + _cleaning_method, + _cleaning_method_rank, + _gap_report, + auxiliary_fill_plan, + ) = clean_demand( + {"entsoe_api": source}, + source_priority=["entsoe_api"], + gap_filling_config=config, + ) + + assert len(auxiliary_fill_plan) == 1 + + row = auxiliary_fill_plan.iloc[0] + + assert row["rule_name"] == "fill_albania_from_greece" + assert row["country"] == "ALB" + assert row["method"] == "construct_from_sources" + assert row["status"] == "ready" + assert row["source_count"] == 1 \ No newline at end of file diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 714a76f..8c94687 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -104,6 +104,10 @@ rule clean_demand: "/automatic/" "load_gap_report.parquet" ), + auxiliary_fill_plan=( + "/automatic/" + "load_auxiliary_fill_plan.parquet" + ), params: source_names=config["load_sources"], gap_filling=config["gap_filling"], diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 1d6f3b9..03b5cbd 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -43,7 +43,8 @@ def main( data_source, cleaning_method, cleaning_method_rank, - gap_report + gap_report, + auxiliary_fill_plan ) = clean_demand( sources, source_priority=source_names, @@ -51,23 +52,28 @@ def main( ) cleaned.to_parquet(output.demand) + data_source.to_parquet(output.data_source) cleaning_method.to_parquet( output.cleaning_method ) + cleaning_method_rank.to_parquet( output.cleaning_method_rank ) + gap_report.to_parquet( output.gap_report, index=False, ) - _log_source_counts(data_source) - _log_cleaning_method_counts( - cleaning_method, - cleaning_method_rank, + auxiliary_fill_plan.to_parquet( + output.auxiliary_fill_plan, + index=False, ) + + _log_source_counts(data_source) + _log_cleaning_method_counts(cleaning_method,cleaning_method_rank) _log_gap_report(gap_report) diff --git a/workflow/scripts/cleaning/basic/pipeline.py b/workflow/scripts/cleaning/basic/pipeline.py index 757daad..b82500d 100644 --- a/workflow/scripts/cleaning/basic/pipeline.py +++ b/workflow/scripts/cleaning/basic/pipeline.py @@ -355,7 +355,7 @@ def _validate_config(config: Mapping[str, Any]) -> None: "Gap-filling configuration 'rules' must be an ordered sequence." ) - if mode in {"basic", "advanced"} and not config["rules"]: + if mode == "basic" and not config["rules"]: raise ValueError( "At least one gap-filling rule is required when " f"gap-filling mode is {mode!r}." diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index eb8b6d2..199968f 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -8,6 +8,7 @@ import pandas as pd from cleaning.advanced.gap_report import build_gap_report +from cleaning.advanced.plan import build_auxiliary_fill_plan from cleaning.basic.pipeline import fill_basic_gaps from cleaning.combine_sources import combine_sources from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank @@ -26,6 +27,7 @@ def clean_demand( pd.DataFrame, #method pd.DataFrame, #rank pd.DataFrame, #gap_report + pd.DataFrame, #auxiliary_fill_plan ]: """Combine observed sources and fill remaining gaps.""" ( @@ -66,6 +68,11 @@ def clean_demand( ) if gap_filling_config["mode"] == "advanced": + advanced_overrides = gap_filling_config["advanced"]["overrides"] + auxiliary_fill_plan = build_auxiliary_fill_plan( + advanced_overrides + ) + logger.info( "Advanced gap diagnosis found %s unresolved gaps " "covering %s values.", @@ -73,10 +80,20 @@ def clean_demand( int(gap_report["gap_hours"].sum()), ) + logger.info( + "Advanced auxiliary-fill plan contains %s configured " + "instructions.", + len(auxiliary_fill_plan), + ) + else: + auxiliary_fill_plan = build_auxiliary_fill_plan({}) + return ( cleaned, data_source, cleaning_method, cleaning_method_rank, - gap_report + gap_report, + auxiliary_fill_plan, ) + From 4fc75cb199e0695adf3cda450166aaeae65f2903 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 11:12:37 +0200 Subject: [PATCH 018/241] Decouple basic gap filling from workflow mode --- workflow/scripts/cleaning/basic/pipeline.py | 84 ++++++--------------- workflow/scripts/cleaning/pipeline.py | 10 +-- 2 files changed, 29 insertions(+), 65 deletions(-) diff --git a/workflow/scripts/cleaning/basic/pipeline.py b/workflow/scripts/cleaning/basic/pipeline.py index b82500d..95302a6 100644 --- a/workflow/scripts/cleaning/basic/pipeline.py +++ b/workflow/scripts/cleaning/basic/pipeline.py @@ -23,7 +23,8 @@ def fill_basic_gaps( load: pd.DataFrame, *, cleaning_method: pd.DataFrame, - config: Mapping[str, Any], + rules: Sequence[Mapping[str, Any]], + enabled: bool = True, ) -> tuple[pd.DataFrame, pd.DataFrame]: """Apply configured gap-filling rules and record method provenance. @@ -34,8 +35,10 @@ def fill_basic_gaps( cleaning_method: Per-cell cleaning-method provenance for the observed input values. Missing input values should contain ``pd.NA``. - config: - Gap-filling configuration containing ``mode`` and ``rules``. + rules: + Ordered basic gap-filling rules. + enabled: + Whether basic gap filling should be applied. Returns ------- @@ -46,30 +49,24 @@ def fill_basic_gaps( Per-cell provenance containing the observed-source identifier, configured gap-filling rule name, or ``missing``. """ + _validate_settings( + enabled=enabled, + rules=rules + ) validate_load(load) _validate_cleaning_method( load=load, cleaning_method=cleaning_method, ) - _validate_config(config) filled = load.copy() cleaning_method = cleaning_method.copy() - mode = config["mode"] - - if mode == "off": - logger.info( - "Gap filling is disabled because mode is 'off'." - ) - - cleaning_method = cleaning_method.fillna( - "missing" - ) - - return filled, cleaning_method + if not enabled: + logger.info("Basic gap filling is disabled.") + cleaning_method = cleaning_method.fillna("missing") + return filled, cleaning_method - rules = config["rules"] original_gap_duration = calculate_missing_run_durations( load ) @@ -311,54 +308,23 @@ def _log_rule_results( ) -def _validate_config(config: Mapping[str, Any]) -> None: - if not isinstance(config, Mapping): - raise TypeError( - "Gap-filling configuration must be a mapping." - ) - - if "mode" not in config: - raise ValueError( - "Gap-filling configuration must define 'mode'." - ) - - mode = config["mode"] - - if not isinstance(mode, str): +def _validate_settings( + *, + enabled: bool, + rules: Sequence[Mapping[str, Any]], +) -> None: + """Validate basic gap-filling settings.""" + if not isinstance(enabled, bool): raise TypeError( - "Gap-filling configuration 'mode' must be a string." - ) - - supported_modes = { - "off", - "basic", - "advanced", - } - - if mode not in supported_modes: - raise ValueError( - "Unsupported gap-filling mode " - f"{mode!r}. Expected one of " - f"{sorted(supported_modes)}." + "Basic gap-filling 'enabled' must be a boolean." ) - if "rules" not in config: - raise ValueError( - "Gap-filling configuration must define 'rules'." - ) - - if not isinstance(config["rules"], Sequence) or isinstance( - config["rules"], + if not isinstance(rules, Sequence) or isinstance( + rules, (str, bytes), ): raise TypeError( - "Gap-filling configuration 'rules' must be an ordered sequence." - ) - - if mode == "basic" and not config["rules"]: - raise ValueError( - "At least one gap-filling rule is required when " - f"gap-filling mode is {mode!r}." + "Basic gap-filling 'rules' must be an ordered sequence." ) diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 199968f..4b8d40e 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -39,15 +39,13 @@ def clean_demand( priority=source_priority, ) - basic_config = { - "mode": gap_filling_config["mode"], - "rules": gap_filling_config["basic"]["rules"], - } + basic_rules = gap_filling_config["basic"]["rules"] cleaned, cleaning_method = fill_basic_gaps( - combined, + load=combined, cleaning_method=cleaning_method, - config=basic_config, + rules=basic_rules, + enabled=gap_filling_config["mode"] != "off", ) basic_rules = gap_filling_config["basic"]["rules"] From 8ef4804a2ff191ed973232e20b1c8d5de362e679 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 11:41:46 +0200 Subject: [PATCH 019/241] Generalize ENTSO-E download parameters --- workflow/scripts/download_load_entsoe_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 558691c..4307eb3 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -97,8 +97,8 @@ def main(start, end, country_codes, token, output_load): if __name__ == "__main__": sys.stderr = open(snakemake.log[0], "w", buffering=1) main( - start=snakemake.config["temporal_scope"]["start"], - end=snakemake.config["temporal_scope"]["end"], + start=snakemake.params.temporal_start, + end=snakemake.params.temporal_end, country_codes=snakemake.params.country_codes, token=snakemake.input.token_entsoe, output_load=snakemake.output.load, From 2ae5f370bfa72ee31885d875c758ed33075527e2 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 12:16:23 +0200 Subject: [PATCH 020/241] Centralize hourly UTC time-index handling --- workflow/scripts/_time.py | 38 +++++++++++++++++++ workflow/scripts/download_load_entsoe_api.py | 9 ++--- workflow/scripts/prepare_load_neso.py | 40 ++------------------ workflow/scripts/prepare_load_opsd.py | 14 +------ 4 files changed, 48 insertions(+), 53 deletions(-) create mode 100644 workflow/scripts/_time.py diff --git a/workflow/scripts/_time.py b/workflow/scripts/_time.py new file mode 100644 index 0000000..d1595f6 --- /dev/null +++ b/workflow/scripts/_time.py @@ -0,0 +1,38 @@ +"""Shared time-index utilities.""" + +from __future__ import annotations + +import pandas as pd + + +def as_utc_timestamp(value: object) -> pd.Timestamp: + """Convert a timestamp-like value to UTC.""" + timestamp = pd.Timestamp(value) + + if timestamp.tzinfo is None: + return timestamp.tz_localize("UTC") + + return timestamp.tz_convert("UTC") + + +def build_hourly_index( + *, + start: object, + end: object, +) -> pd.DatetimeIndex: + """Create an end-exclusive hourly UTC index.""" + start_timestamp = as_utc_timestamp(start) + end_timestamp = as_utc_timestamp(end) + + if end_timestamp <= start_timestamp: + raise ValueError( + "The temporal end must be later than its start." + ) + + return pd.date_range( + start=start_timestamp, + end=end_timestamp, + freq="h", + inclusive="left", + name="time", + ) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 4307eb3..cebc30c 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -7,6 +7,7 @@ import pandas as pd import pycountry import yaml +from _time import as_utc_timestamp, build_hourly_index from entsoe import EntsoePandasClient from entsoe.exceptions import NoMatchingDataError @@ -29,8 +30,8 @@ def load_yaml(path): def main(start, end, country_codes, token, output_load): """Download load in MW via the ENTSO-E API.""" - start = pd.Timestamp(start, tz="UTC") - end = pd.Timestamp(end, tz="UTC") + start = as_utc_timestamp(start) + end = as_utc_timestamp(end) token = load_txt(token).strip() client = EntsoePandasClient(api_key=token) @@ -60,11 +61,9 @@ def main(start, end, country_codes, token, output_load): df = df.resample("1h").mean() # reindexing to add some security to the entsoe download - target_index = pd.date_range( + target_index = build_hourly_index( start=start, end=end, - freq="h", - inclusive="left", ) df = df.reindex( diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 8ef63a6..b935509 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Iterable import pandas as pd +from _time import build_hourly_index from cleaning.neso import add_utc_timestamps if TYPE_CHECKING: @@ -154,39 +155,6 @@ def _aggregate_hourly( return hourly -def _build_target_index( - *, - temporal_start: str, - temporal_end: str, -) -> pd.DatetimeIndex: - """Create the configured end-exclusive hourly UTC index.""" - start = pd.Timestamp(temporal_start) - end = pd.Timestamp(temporal_end) - - if start.tzinfo is None: - start = start.tz_localize("UTC") - else: - start = start.tz_convert("UTC") - - if end.tzinfo is None: - end = end.tz_localize("UTC") - else: - end = end.tz_convert("UTC") - - if end <= start: - raise ValueError( - "The temporal-scope end must be later than its start." - ) - - return pd.date_range( - start=start, - end=end, - freq="1h", - inclusive="left", - name="time", - ) - - def prepare_load_neso( *, input_paths: Iterable[str | Path], @@ -205,9 +173,9 @@ def prepare_load_neso( "Target country codes must be unique." ) - target_index = _build_target_index( - temporal_start=temporal_start, - temporal_end=temporal_end, + target_index = build_hourly_index( + start=temporal_start, + end=temporal_end, ) result = pd.DataFrame( diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 7b8e5b0..b87e9f5 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -7,19 +7,11 @@ import pandas as pd import pycountry from _schemas import LoadENTSOE +from _time import as_utc_timestamp, build_hourly_index if TYPE_CHECKING: snakemake: Any -def as_utc_timestamp(value) -> pd.Timestamp: - """Convert a timestamp-like value to UTC.""" - timestamp = pd.Timestamp(value) - - if timestamp.tzinfo is None: - return timestamp.tz_localize("UTC") - - return timestamp.tz_convert("UTC") - def get_map_alpha2_to_alpha3( countries_alpha_2, @@ -101,11 +93,9 @@ def main( values="data", ) - target_index = pd.date_range( + target_index = build_hourly_index( start=start, end=end, - freq="h", - inclusive="left", ) prepared = prepared.reindex( From bdaad4c6cb485111d497ef279e6528776af78cab Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 12:29:35 +0200 Subject: [PATCH 021/241] Requirements gathering functions implemented for auxiliary data pull --- tests/unit/test_advanced_requirements.py | 221 ++++++++++++++++++ .../scripts/cleaning/advanced/requirements.py | 126 ++++++++++ 2 files changed, 347 insertions(+) create mode 100644 tests/unit/test_advanced_requirements.py create mode 100644 workflow/scripts/cleaning/advanced/requirements.py diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py new file mode 100644 index 0000000..f75a6a6 --- /dev/null +++ b/tests/unit/test_advanced_requirements.py @@ -0,0 +1,221 @@ +"""Tests for compiling auxiliary-data requirements.""" + +import pandas as pd +from cleaning.advanced.requirements import ( + REQUIREMENT_COLUMNS, + compile_auxiliary_requirements, +) + + +def test_compile_auxiliary_requirements_collects_sources() -> None: + overrides = { + "reconstruct_albania": { + "country": "ALB", + "start": "2021-01-01", + "end": "2021-01-03", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-01-01", + "end": "2019-01-03", + }, + { + "country": "MNE", + "start": "2020-01-01", + "end": "2020-01-03", + "weight": 2, + }, + ], + }, + } + + result = compile_auxiliary_requirements(overrides) + + expected = pd.DataFrame( + { + "country": ["GRC", "MNE"], + "start": pd.to_datetime( + [ + "2019-01-01", + "2020-01-01", + ], + utc=True, + ), + "end": pd.to_datetime( + [ + "2019-01-03", + "2020-01-03", + ], + utc=True, + ), + } + ) + + pd.testing.assert_frame_equal( + result, + expected, + ) + + +def test_compile_auxiliary_requirements_includes_scaling_sources() -> None: + overrides = { + "reconstruct_albania": { + "country": "ALB", + "start": "2021-01-01", + "end": "2021-01-03", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-01-01", + "end": "2019-01-03", + }, + ], + "scaling": { + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-01-03", + }, + ], + }, + }, + } + + result = compile_auxiliary_requirements(overrides) + + assert list(result["country"]) == [ + "ALB", + "GRC", + ] + + assert result.loc[ + result["country"] == "ALB", + "start", + ].iloc[0] == pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + + +def test_compile_auxiliary_requirements_deduplicates_sources() -> None: + source = { + "country": "GRC", + "start": "2019-01-01", + "end": "2019-01-03", + } + + overrides = { + "first_rule": { + "method": "construct_from_sources", + "sources": [source], + }, + "second_rule": { + "method": "construct_from_sources", + "sources": [source], + }, + } + + result = compile_auxiliary_requirements(overrides) + + assert len(result) == 1 + + +def test_compile_auxiliary_requirements_ignores_other_methods() -> None: + overrides = { + "manual_case": { + "method": "manual_review", + }, + "leave_case": { + "method": "leave_missing", + }, + } + + result = compile_auxiliary_requirements(overrides) + + assert result.empty + assert list(result.columns) == REQUIREMENT_COLUMNS + + +def test_compile_auxiliary_requirements_returns_empty_schema() -> None: + result = compile_auxiliary_requirements({}) + + assert result.empty + assert list(result.columns) == REQUIREMENT_COLUMNS + +def test_compile_auxiliary_requirements_merges_overlapping_periods() -> None: + overrides = { + "first_rule": { + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-01-01", + "end": "2019-06-01", + }, + ], + }, + "second_rule": { + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-05-01", + "end": "2019-12-01", + }, + ], + }, + } + + result = compile_auxiliary_requirements(overrides) + + assert len(result) == 1 + assert result.iloc[0]["start"] == pd.Timestamp( + "2019-01-01", + tz="UTC", + ) + assert result.iloc[0]["end"] == pd.Timestamp( + "2019-12-01", + tz="UTC", + ) + +def test_compile_auxiliary_requirements_merges_adjacent_periods() -> None: + overrides = { + "first_rule": { + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-01-01", + "end": "2019-02-01", + }, + ], + }, + "second_rule": { + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2019-02-01", + "end": "2019-03-01", + }, + ], + }, + } + + result = compile_auxiliary_requirements(overrides) + + assert len(result) == 1 + assert result.iloc[0]["start"] == pd.Timestamp( + "2019-01-01", + tz="UTC", + ) + assert result.iloc[0]["end"] == pd.Timestamp( + "2019-03-01", + tz="UTC", + ) diff --git a/workflow/scripts/cleaning/advanced/requirements.py b/workflow/scripts/cleaning/advanced/requirements.py new file mode 100644 index 0000000..ff57940 --- /dev/null +++ b/workflow/scripts/cleaning/advanced/requirements.py @@ -0,0 +1,126 @@ +"""Compile auxiliary-data requirements for advanced gap filling.""" + +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any + +import pandas as pd +from _time import as_utc_timestamp + +from cleaning.advanced.construct_from_sources import METHOD_NAME + +REQUIREMENT_COLUMNS = [ + "country", + "start", + "end", +] + + +def compile_auxiliary_requirements( + overrides: Mapping[str, Mapping[str, Any]], +) -> pd.DataFrame: + """Compile country-period data required by advanced overrides.""" + requirements: list[dict[str, Any]] = [] + + for rule in overrides.values(): + if rule["method"] != METHOD_NAME: + continue + + requirements.extend( + _collect_sources(rule["sources"]) + ) + + scaling = rule.get("scaling") + + if scaling is not None: + requirements.extend( + _collect_sources( + scaling["target_sources"] + ) + ) + + if not requirements: + return pd.DataFrame( + columns=REQUIREMENT_COLUMNS + ) + + requirements_frame = ( + pd.DataFrame(requirements) + .drop_duplicates() + .sort_values( + ["country", "start", "end"] + ) + .reset_index(drop=True) + ) + + return _merge_requirements( + requirements_frame + ) + + +def _collect_sources( + sources: list[Mapping[str, Any]], +) -> list[dict[str, Any]]: + """Extract acquisition-relevant fields from source definitions.""" + return [ + { + "country": source["country"], + "start": as_utc_timestamp(source["start"]), + "end": as_utc_timestamp(source["end"]), + } + for source in sources + ] + +def _merge_requirements( + requirements: pd.DataFrame, +) -> pd.DataFrame: + """Merge overlapping or adjacent country-period requirements.""" + if requirements.empty: + return requirements.copy() + + merged_rows: list[dict[str, Any]] = [] + + for country, country_requirements in requirements.groupby( + "country", + sort=True, + ): + ordered = country_requirements.sort_values( + ["start", "end"] + ) + + current_start = ordered.iloc[0]["start"] + current_end = ordered.iloc[0]["end"] + + for row in ordered.iloc[1:].itertuples(index=False): + if row.start <= current_end: + current_end = max( + current_end, + row.end, + ) + continue + + merged_rows.append( + { + "country": country, + "start": current_start, + "end": current_end, + } + ) + + current_start = row.start + current_end = row.end + + merged_rows.append( + { + "country": country, + "start": current_start, + "end": current_end, + } + ) + + return pd.DataFrame( + merged_rows, + columns=REQUIREMENT_COLUMNS, + ) + From e7bd5fb8e36ef360b3ec4aaf53b43509062dcfcc Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 12:38:39 +0200 Subject: [PATCH 022/241] Expand auxiliary requirements for basic cleaning --- tests/unit/test_advanced_requirements.py | 121 ++++++++++++++++++ .../scripts/cleaning/advanced/requirements.py | 88 ++++++++++++- 2 files changed, 208 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index f75a6a6..0bd1fdf 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -4,6 +4,8 @@ from cleaning.advanced.requirements import ( REQUIREMENT_COLUMNS, compile_auxiliary_requirements, + expand_auxiliary_requirements, + get_basic_cleaning_context, ) @@ -219,3 +221,122 @@ def test_compile_auxiliary_requirements_merges_adjacent_periods() -> None: "2019-03-01", tz="UTC", ) + +def test_expand_requirements_without_rules_is_unchanged() -> None: + requirements = pd.DataFrame( + { + "country": ["GRC"], + "start": [pd.Timestamp("2020-01-01", tz="UTC")], + "end": [pd.Timestamp("2020-02-01", tz="UTC")], + } + ) + + result = expand_auxiliary_requirements( + requirements, + rules=[], + ) + + pd.testing.assert_frame_equal( + result, + requirements, + ) + +def test_expand_requirements_can_be_disabled() -> None: + requirements = pd.DataFrame( + { + "country": ["GRC"], + "start": [pd.Timestamp("2020-01-01", tz="UTC")], + "end": [pd.Timestamp("2020-02-01", tz="UTC")], + } + ) + + rules = [ + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + } + ] + + result = expand_auxiliary_requirements( + requirements, + rules=rules, + enabled=False, + ) + + pd.testing.assert_frame_equal( + result, + requirements, + ) + +def test_expand_requirements_can_be_disabled() -> None: + requirements = pd.DataFrame( + { + "country": ["GRC"], + "start": [pd.Timestamp("2020-01-01", tz="UTC")], + "end": [pd.Timestamp("2020-02-01", tz="UTC")], + } + ) + + rules = [ + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + } + ] + + result = expand_auxiliary_requirements( + requirements, + rules=rules, + enabled=False, + ) + + pd.testing.assert_frame_equal( + result, + requirements, + ) + + +def test_basic_context_for_copy_period() -> None: + rules = [ + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + } + ] + + left, right = get_basic_cleaning_context(rules) + + assert left == pd.Timedelta("7D") + assert right == pd.Timedelta("7D") + + +def test_basic_context_compounds_across_ordered_rules() -> None: + rules = [ + { + "name": "average_adjacent_weeks", + "method": "average_periods", + "max_gap": "168h", + "source_offsets": [ + "-168h", + "168h", + ], + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + }, + ] + + left, right = get_basic_cleaning_context(rules) + + assert left == pd.Timedelta("14D") + assert right == pd.Timedelta("7D") + diff --git a/workflow/scripts/cleaning/advanced/requirements.py b/workflow/scripts/cleaning/advanced/requirements.py index ff57940..8278964 100644 --- a/workflow/scripts/cleaning/advanced/requirements.py +++ b/workflow/scripts/cleaning/advanced/requirements.py @@ -2,7 +2,7 @@ from __future__ import annotations -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from typing import Any import pandas as pd @@ -124,3 +124,89 @@ def _merge_requirements( columns=REQUIREMENT_COLUMNS, ) +def get_basic_cleaning_context( + rules: Sequence[Mapping[str, Any]], +) -> tuple[pd.Timedelta, pd.Timedelta]: + """Return required left and right context for ordered basic rules.""" + left = pd.Timedelta(0) + right = pd.Timedelta(0) + + for rule in rules: + method = rule["method"] + max_gap = pd.Timedelta(rule["max_gap"]) + + # Context is also needed to classify gaps correctly at boundaries. + rule_left = -max_gap + rule_right = max_gap + + if method == "linear_interpolation": + offsets = [ + -pd.Timedelta(hours=1), + pd.Timedelta(hours=1), + ] + + elif method == "copy_period": + offsets = [ + pd.Timedelta(rule["source_offset"]), + ] + + elif method == "average_periods": + offsets = [ + pd.Timedelta(offset) + for offset in rule["source_offsets"] + ] + + else: + raise ValueError( + f"Unsupported basic gap-filling method: {method!r}" + ) + + previous_left = left + previous_right = right + + for offset in offsets: + rule_left = min( + rule_left, + offset + previous_left, + ) + rule_right = max( + rule_right, + offset + previous_right, + ) + + left = min( + previous_left, + rule_left, + ) + right = max( + previous_right, + rule_right, + ) + + return -left, right + +def expand_auxiliary_requirements( + requirements: pd.DataFrame, + *, + rules: Sequence[Mapping[str, Any]], + enabled: bool = True, +) -> pd.DataFrame: + """Expand auxiliary periods with context needed for basic cleaning.""" + if requirements.empty or not enabled or not rules: + return requirements.copy() + + left_context, right_context = get_basic_cleaning_context( + rules + ) + + expanded = requirements.copy() + + expanded["start"] = ( + expanded["start"] - left_context + ) + expanded["end"] = ( + expanded["end"] + right_context + ) + + return _merge_requirements(expanded) + From 2ef2d58fd19501d14962c9a0f5baad47103c4f7d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 12:50:42 +0200 Subject: [PATCH 023/241] Requirements configuration completed --- config/config.yaml | 4 + tests/integration/test_config.yaml | 4 + tests/unit/test_advanced_requirements.py | 39 +++++++++ tests/unit/test_cleaning_pipeline.py | 15 ++++ tests/unit/test_config_schema.py | 80 ++++++++++++++++++- workflow/internal/config.schema.yaml | 32 ++++++++ .../scripts/cleaning/advanced/requirements.py | 17 ++++ 7 files changed, 190 insertions(+), 1 deletion(-) diff --git a/config/config.yaml b/config/config.yaml index 56fb019..fab6b20 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -44,5 +44,9 @@ gap_filling: source_offset: 168h advanced: + auxiliary_data: + basic_cleaning: + enabled: true + # Explicit country-period instructions for unresolved cases. overrides: {} \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index f6c0230..7a7b3dd 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -41,5 +41,9 @@ integration_test: source_offset: 168h advanced: + auxiliary_data: + basic_cleaning: + enabled: true + # Explicit country-period instructions for unresolved cases. overrides: {} \ No newline at end of file diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index 0bd1fdf..da25531 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -3,6 +3,7 @@ import pandas as pd from cleaning.advanced.requirements import ( REQUIREMENT_COLUMNS, + build_auxiliary_acquisition_requirements, compile_auxiliary_requirements, expand_auxiliary_requirements, get_basic_cleaning_context, @@ -340,3 +341,41 @@ def test_basic_context_compounds_across_ordered_rules() -> None: assert left == pd.Timedelta("14D") assert right == pd.Timedelta("7D") + +def test_build_auxiliary_acquisition_requirements_uses_basic_cleaning_config() -> None: + overrides = { + "reconstruct_albania": { + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2020-01-01", + "end": "2020-02-01", + }, + ], + }, + } + + basic_rules = [ + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + }, + ] + + result = build_auxiliary_acquisition_requirements( + overrides=overrides, + basic_rules=basic_rules, + basic_cleaning_enabled=True, + ) + + assert result.iloc[0]["start"] == pd.Timestamp( + "2019-12-25", + tz="UTC", + ) + assert result.iloc[0]["end"] == pd.Timestamp( + "2020-02-08", + tz="UTC", + ) diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index f0a96e1..b8a814e 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -92,6 +92,11 @@ def test_clean_demand_records_methods_and_ranks() -> None: ], }, "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + }, + }, "overrides": {}, }, } @@ -369,6 +374,11 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: ], }, "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + }, + }, "overrides": {}, }, } @@ -602,6 +612,11 @@ def test_advanced_mode_builds_auxiliary_fill_plan() -> None: "rules": [], }, "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + }, + }, "overrides": { "fill_albania_from_greece": { "country": "ALB", diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 9f8bfc5..9574411 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -45,4 +45,82 @@ def _format_validation_error( for part in error.absolute_path ) - return f"{path or ''}: {error.message}" \ No newline at end of file + return f"{path or ''}: {error.message}" + +def test_advanced_auxiliary_basic_cleaning_can_be_disabled() -> None: + """Allow auxiliary basic cleaning to be disabled.""" + with SCHEMA_PATH.open(encoding="utf-8") as schema_file: + schema = yaml.safe_load(schema_file) + + config = { + "temporal_scope": { + "start": "2021-01-01", + "end": "2022-01-01", + }, + "load_sources": [ + "entsoe_api", + ], + "gap_filling": { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + }, + }, + "overrides": {}, + }, + }, + } + + validator = Draft202012Validator(schema) + errors = list( + validator.iter_errors(config) + ) + + assert not errors, "\n".join( + _format_validation_error(error) + for error in errors + ) + +def test_advanced_mode_allows_no_rules_or_overrides() -> None: + """Allow advanced mode to run for diagnosis only.""" + with SCHEMA_PATH.open(encoding="utf-8") as schema_file: + schema = yaml.safe_load(schema_file) + + config = { + "temporal_scope": { + "start": "2021-01-01", + "end": "2022-01-01", + }, + "load_sources": [ + "entsoe_api", + ], + "gap_filling": { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + }, + }, + "overrides": {}, + }, + }, + } + + validator = Draft202012Validator(schema) + errors = list( + validator.iter_errors(config) + ) + + assert not errors, "\n".join( + _format_validation_error(error) + for error in errors + ) diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 0972541..6a0ff97 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -199,6 +199,33 @@ $defs: - $ref: "#/$defs/external_profile_rule" - $ref: "#/$defs/manual_review_rule" - $ref: "#/$defs/leave_missing_rule" + + auxiliary_basic_cleaning: + type: object + properties: + enabled: + type: boolean + default: true + description: > + Apply the configured basic gap-filling rules to auxiliary + demand data before it is used for advanced profile construction. + + required: + - enabled + + additionalProperties: false + + + auxiliary_data: + type: object + properties: + basic_cleaning: + $ref: "#/$defs/auxiliary_basic_cleaning" + + required: + - basic_cleaning + + additionalProperties: false gap_filling_rule_base: type: object @@ -364,6 +391,9 @@ properties: advanced: type: object properties: + auxiliary_data: + $ref: "#/$defs/auxiliary_data" + overrides: type: object default: {} @@ -375,10 +405,12 @@ properties: $ref: "#/$defs/auxiliary_fill_rule" required: + - auxiliary_data - overrides additionalProperties: false + required: - mode - basic diff --git a/workflow/scripts/cleaning/advanced/requirements.py b/workflow/scripts/cleaning/advanced/requirements.py index 8278964..e62b638 100644 --- a/workflow/scripts/cleaning/advanced/requirements.py +++ b/workflow/scripts/cleaning/advanced/requirements.py @@ -210,3 +210,20 @@ def expand_auxiliary_requirements( return _merge_requirements(expanded) + +def build_auxiliary_acquisition_requirements( + *, + overrides: Mapping[str, Mapping[str, Any]], + basic_rules: Sequence[Mapping[str, Any]], + basic_cleaning_enabled: bool, +) -> pd.DataFrame: + """Build expanded auxiliary-data requirements for acquisition.""" + exact_requirements = compile_auxiliary_requirements( + overrides + ) + + return expand_auxiliary_requirements( + exact_requirements, + rules=basic_rules, + enabled=basic_cleaning_enabled, + ) \ No newline at end of file From ab7adeff94ec5ab644cb3c2a151583b103c6e4fc Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 13:24:32 +0200 Subject: [PATCH 024/241] Commit in advance of snakemake checkpoints -friendly restructure --- tests/unit/test_advanced_source_requests.py | 264 ++++++++++++++++++ workflow/rules/automatic.smk | 103 +++++-- workflow/scripts/_time.py | 25 ++ .../cleaning/advanced/source_requests.py | 132 +++++++++ 4 files changed, 493 insertions(+), 31 deletions(-) create mode 100644 tests/unit/test_advanced_source_requests.py create mode 100644 workflow/scripts/cleaning/advanced/source_requests.py diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py new file mode 100644 index 0000000..ad0cf96 --- /dev/null +++ b/tests/unit/test_advanced_source_requests.py @@ -0,0 +1,264 @@ +"""Tests for auxiliary source-request planning.""" + +import pandas as pd +import pytest +from cleaning.advanced.source_requests import ( + SOURCE_REQUEST_COLUMNS, + build_auxiliary_source_requests, + build_auxiliary_source_batches, +) + + +def test_build_source_requests_uses_all_applicable_sources() -> None: + requirements = pd.DataFrame( + { + "country": ["GBR", "GRC"], + "start": pd.to_datetime( + [ + "2020-01-01", + "2021-01-01", + ], + utc=True, + ), + "end": pd.to_datetime( + [ + "2020-02-01", + "2021-02-01", + ], + utc=True, + ), + } + ) + + result = build_auxiliary_source_requests( + requirements, + source_names=[ + "entsoe_api", + "neso", + "opsd", + ], + ) + + assert list( + result[["source", "country"]] + .itertuples(index=False, name=None) + ) == [ + ("entsoe_api", "GBR"), + ("entsoe_api", "GRC"), + ("neso", "GBR"), + ("opsd", "GBR"), + ("opsd", "GRC"), + ] + + +def test_neso_is_only_requested_for_gbr() -> None: + requirements = pd.DataFrame( + { + "country": ["GRC"], + "start": [ + pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + ], + "end": [ + pd.Timestamp( + "2020-02-01", + tz="UTC", + ) + ], + } + ) + + result = build_auxiliary_source_requests( + requirements, + source_names=["neso"], + ) + + assert result.empty + assert list(result.columns) == SOURCE_REQUEST_COLUMNS + + +def test_empty_requirements_return_empty_source_request_schema() -> None: + requirements = pd.DataFrame( + columns=[ + "country", + "start", + "end", + ] + ) + + result = build_auxiliary_source_requests( + requirements, + source_names=[ + "entsoe_api", + "neso", + "opsd", + ], + ) + + assert result.empty + assert list(result.columns) == SOURCE_REQUEST_COLUMNS + + +def test_unknown_source_is_rejected() -> None: + requirements = pd.DataFrame( + columns=[ + "country", + "start", + "end", + ] + ) + + with pytest.raises( + ValueError, + match="Unsupported auxiliary load sources", + ): + build_auxiliary_source_requests( + requirements, + source_names=["mystery_source"], + ) + + +def test_duplicate_source_names_are_rejected() -> None: + requirements = pd.DataFrame( + columns=[ + "country", + "start", + "end", + ] + ) + + with pytest.raises( + ValueError, + match="must be unique", + ): + build_auxiliary_source_requests( + requirements, + source_names=[ + "entsoe_api", + "entsoe_api", + ], + ) + +def test_build_source_batches_groups_matching_periods() -> None: + requests = pd.DataFrame( + { + "source": [ + "entsoe_api", + "entsoe_api", + "entsoe_api", + ], + "country": [ + "ALB", + "GRC", + "MNE", + ], + "start": pd.to_datetime( + [ + "2020-01-01", + "2020-01-01", + "2021-01-01", + ], + utc=True, + ), + "end": pd.to_datetime( + [ + "2020-02-01", + "2020-02-01", + "2021-02-01", + ], + utc=True, + ), + } + ) + + result = build_auxiliary_source_batches( + requests + ) + + assert result == [ + { + "source": "entsoe_api", + "start": pd.Timestamp( + "2020-01-01", + tz="UTC", + ), + "end": pd.Timestamp( + "2020-02-01", + tz="UTC", + ), + "countries": [ + "ALB", + "GRC", + ], + }, + { + "source": "entsoe_api", + "start": pd.Timestamp( + "2021-01-01", + tz="UTC", + ), + "end": pd.Timestamp( + "2021-02-01", + tz="UTC", + ), + "countries": [ + "MNE", + ], + }, + ] + +def test_build_source_batches_keeps_sources_separate() -> None: + start = pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + end = pd.Timestamp( + "2020-02-01", + tz="UTC", + ) + + requests = pd.DataFrame( + { + "source": [ + "entsoe_api", + "opsd", + ], + "country": [ + "GBR", + "GBR", + ], + "start": [ + start, + start, + ], + "end": [ + end, + end, + ], + } + ) + + result = build_auxiliary_source_batches( + requests + ) + + assert len(result) == 2 + + assert [ + batch["source"] + for batch in result + ] == [ + "entsoe_api", + "opsd", + ] + +def test_build_source_batches_returns_empty_list() -> None: + requests = pd.DataFrame( + columns=SOURCE_REQUEST_COLUMNS + ) + + assert build_auxiliary_source_batches( + requests + ) == [] diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 5c5181d..8bdb1c7 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,38 +1,18 @@ """Rules used to download automatic resource files.""" -from datetime import datetime, timedelta - -def _years_in_temporal_scope( - temporal_start: str, - temporal_end: str, -) -> tuple[int, ...]: - """Return calendar years intersecting an end-exclusive scope.""" - start = datetime.fromisoformat(temporal_start) - end = datetime.fromisoformat(temporal_end) - - if end <= start: - raise ValueError( - "temporal_scope.end must be later than " - "temporal_scope.start." - ) - - # The configured end is exclusive. Subtract a very small - # amount so 2025-01-01 does not require the 2025 file. - final_included_time = end - timedelta( - microseconds=1 - ) - - return tuple( - range( - start.year, - final_included_time.year + 1, - ) - ) +from _time import years_in_period +from cleaning.advanced.requirements import ( + build_auxiliary_acquisition_requirements, +) +from cleaning.advanced.source_requests import ( + build_auxiliary_source_batches, + build_auxiliary_source_requests, +) -NESO_YEARS = _years_in_temporal_scope( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], +NESO_YEARS = years_in_period( + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], ) NESO_RAW_FILES = expand( @@ -41,6 +21,67 @@ NESO_RAW_FILES = expand( year=NESO_YEARS, ) +def _build_auxiliary_source_batches(): + """Build source-specific acquisition batches for advanced gap filling.""" + gap_filling = config["gap_filling"] + + if gap_filling["mode"] != "advanced": + return [] + + advanced = gap_filling["advanced"] + + requirements = build_auxiliary_acquisition_requirements( + overrides=advanced["overrides"], + basic_rules=gap_filling["basic"]["rules"], + basic_cleaning_enabled=( + advanced["auxiliary_data"] + ["basic_cleaning"] + ["enabled"] + ), + ) + + requests = build_auxiliary_source_requests( + requirements, + source_names=config["load_sources"], + ) + + return build_auxiliary_source_batches( + requests + ) + + +AUXILIARY_SOURCE_BATCHES = ( + _build_auxiliary_source_batches() +) + +def _index_auxiliary_batches( + source_name: str, +) -> dict[str, dict]: + """Index auxiliary batches belonging to one source.""" + source_batches = [ + batch + for batch in AUXILIARY_SOURCE_BATCHES + if batch["source"] == source_name + ] + + return { + str(index): batch + for index, batch in enumerate(source_batches) + } + + +AUXILIARY_ENTSOE_BATCHES = ( + _index_auxiliary_batches("entsoe_api") +) + +AUXILIARY_OPSD_BATCHES = ( + _index_auxiliary_batches("opsd") +) + +AUXILIARY_NESO_BATCHES = ( + _index_auxiliary_batches("neso") +) + rule download_load_entsoe_api: input: token_entsoe="", diff --git a/workflow/scripts/_time.py b/workflow/scripts/_time.py index d1595f6..d119f59 100644 --- a/workflow/scripts/_time.py +++ b/workflow/scripts/_time.py @@ -36,3 +36,28 @@ def build_hourly_index( inclusive="left", name="time", ) + +def years_in_period( + *, + start: object, + end: object, +) -> tuple[int, ...]: + """Return calendar years intersecting an end-exclusive period.""" + start_timestamp = as_utc_timestamp(start) + end_timestamp = as_utc_timestamp(end) + + if end_timestamp <= start_timestamp: + raise ValueError( + "The period end must be later than its start." + ) + + final_included_time = ( + end_timestamp - pd.Timedelta(microseconds=1) + ) + + return tuple( + range( + start_timestamp.year, + final_included_time.year + 1, + ) + ) \ No newline at end of file diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/source_requests.py new file mode 100644 index 0000000..f0f029d --- /dev/null +++ b/workflow/scripts/cleaning/advanced/source_requests.py @@ -0,0 +1,132 @@ +"""Plan source requests for auxiliary electricity-demand data.""" + +from __future__ import annotations + +from collections.abc import Sequence + +import pandas as pd + +SOURCE_REQUEST_COLUMNS = [ + "source", + "country", + "start", + "end", +] + +SUPPORTED_SOURCES = { + "entsoe_api", + "neso", + "opsd", +} + + +def build_auxiliary_source_requests( + requirements: pd.DataFrame, + *, + source_names: Sequence[str], +) -> pd.DataFrame: + """Map auxiliary requirements onto applicable configured sources.""" + _validate_source_names(source_names) + + if requirements.empty: + return pd.DataFrame( + columns=SOURCE_REQUEST_COLUMNS + ) + + requests: list[dict[str, object]] = [] + + for source_name in source_names: + for row in requirements.itertuples(index=False): + if not _source_supports_country( + source_name, + row.country, + ): + continue + + requests.append( + { + "source": source_name, + "country": row.country, + "start": row.start, + "end": row.end, + } + ) + + return pd.DataFrame( + requests, + columns=SOURCE_REQUEST_COLUMNS, + ) + + +def _source_supports_country( + source_name: str, + country: str, +) -> bool: + """Return whether a source is structurally applicable to a country.""" + if source_name == "neso": + return country == "GBR" + + if source_name in { + "entsoe_api", + "opsd", + }: + return True + + raise ValueError( + f"Unsupported auxiliary load source: {source_name!r}" + ) + + +def _validate_source_names( + source_names: Sequence[str], +) -> None: + """Validate configured sources used for auxiliary acquisition.""" + unknown = [ + source_name + for source_name in source_names + if source_name not in SUPPORTED_SOURCES + ] + + if unknown: + raise ValueError( + "Unsupported auxiliary load sources: " + f"{unknown}" + ) + + if len(source_names) != len(set(source_names)): + raise ValueError( + "Auxiliary load source names must be unique." + ) + +def build_auxiliary_source_batches( + requests: pd.DataFrame, +) -> list[dict[str, object]]: + """Group source requests sharing the same acquisition period.""" + if requests.empty: + return [] + + batches: list[dict[str, object]] = [] + + grouped = requests.groupby( + ["source", "start", "end"], + sort=False, + ) + + for ( + source, + start, + end, + ), group in grouped: + batches.append( + { + "source": source, + "start": start, + "end": end, + "countries": sorted( + group["country"].unique().tolist() + ), + } + ) + + return batches + From db6f6f694193fd7641499a0a88e024f6ad44e0a2 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 14:28:48 +0200 Subject: [PATCH 025/241] Added snakemake checkpoint --- tests/unit/test_advanced_source_requests.py | 85 +++++++- tests/unit/test_plan_auxiliary_data.py | 194 ++++++++++++++++++ workflow/Snakefile | 1 + workflow/rules/advanced.smk | 17 ++ workflow/rules/automatic.smk | 102 +++------ workflow/scripts/_time.py | 2 +- .../cleaning/advanced/source_requests.py | 33 ++- workflow/scripts/plan_auxiliary_data.py | 102 +++++++++ 8 files changed, 456 insertions(+), 80 deletions(-) create mode 100644 tests/unit/test_plan_auxiliary_data.py create mode 100644 workflow/rules/advanced.smk create mode 100644 workflow/scripts/plan_auxiliary_data.py diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index ad0cf96..b5c719b 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -4,8 +4,9 @@ import pytest from cleaning.advanced.source_requests import ( SOURCE_REQUEST_COLUMNS, - build_auxiliary_source_requests, + _build_batch_id, build_auxiliary_source_batches, + build_auxiliary_source_requests, ) @@ -40,8 +41,10 @@ def test_build_source_requests_uses_all_applicable_sources() -> None: ) assert list( - result[["source", "country"]] - .itertuples(index=False, name=None) + result[["source", "country"]].itertuples( + index=False, + name=None, + ) ) == [ ("entsoe_api", "GBR"), ("entsoe_api", "GRC"), @@ -141,6 +144,50 @@ def test_duplicate_source_names_are_rejected() -> None: ], ) + +def test_build_batch_id_is_independent_of_country_order() -> None: + first = _build_batch_id( + source="entsoe_api", + start=pd.Timestamp( + "2020-01-01", + tz="UTC", + ), + end=pd.Timestamp( + "2020-02-01", + tz="UTC", + ), + countries=[ + "ALB", + "GRC", + ], + ) + + second = _build_batch_id( + source="entsoe_api", + start=pd.Timestamp( + "2020-01-01", + tz="UTC", + ), + end=pd.Timestamp( + "2020-02-01", + tz="UTC", + ), + countries=[ + "GRC", + "ALB", + ], + ) + + assert first == second + + assert first == ( + "entsoe_api__" + "20200101T0000__" + "20200201T0000__" + "ALB-GRC" + ) + + def test_build_source_batches_groups_matching_periods() -> None: requests = pd.DataFrame( { @@ -179,6 +226,12 @@ def test_build_source_batches_groups_matching_periods() -> None: assert result == [ { + "batch_id": ( + "entsoe_api__" + "20200101T0000__" + "20200201T0000__" + "ALB-GRC" + ), "source": "entsoe_api", "start": pd.Timestamp( "2020-01-01", @@ -194,6 +247,12 @@ def test_build_source_batches_groups_matching_periods() -> None: ], }, { + "batch_id": ( + "entsoe_api__" + "20210101T0000__" + "20210201T0000__" + "MNE" + ), "source": "entsoe_api", "start": pd.Timestamp( "2021-01-01", @@ -209,6 +268,7 @@ def test_build_source_batches_groups_matching_periods() -> None: }, ] + def test_build_source_batches_keeps_sources_separate() -> None: start = pd.Timestamp( "2020-01-01", @@ -254,6 +314,25 @@ def test_build_source_batches_keeps_sources_separate() -> None: "opsd", ] + assert [ + batch["batch_id"] + for batch in result + ] == [ + ( + "entsoe_api__" + "20200101T0000__" + "20200201T0000__" + "GBR" + ), + ( + "opsd__" + "20200101T0000__" + "20200201T0000__" + "GBR" + ), + ] + + def test_build_source_batches_returns_empty_list() -> None: requests = pd.DataFrame( columns=SOURCE_REQUEST_COLUMNS diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py new file mode 100644 index 0000000..3c6f98b --- /dev/null +++ b/tests/unit/test_plan_auxiliary_data.py @@ -0,0 +1,194 @@ +"""Tests for auxiliary acquisition planning.""" + +import pandas as pd +from plan_auxiliary_data import build_auxiliary_acquisition_plan + + +def test_plan_is_empty_outside_advanced_mode() -> None: + config = { + "mode": "basic", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + } + }, + "overrides": {}, + }, + } + + fill_plan = pd.DataFrame( + { + "rule_name": ["example"], + } + ) + + result = build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=config, + source_names=[ + "entsoe_api", + "neso", + "opsd", + ], + ) + + assert result == { + "batches": [], + } + + +def test_advanced_plan_with_no_overrides_is_empty() -> None: + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": True, + } + }, + "overrides": {}, + }, + } + + fill_plan = pd.DataFrame() + + result = build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=config, + source_names=[ + "entsoe_api", + "neso", + "opsd", + ], + ) + + assert result == { + "batches": [], + } + +def test_advanced_plan_builds_serializable_source_batches() -> None: + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + } + }, + "overrides": { + "fill_albania": { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2020-01-01", + "end": "2020-02-01", + "weight": 1, + } + ], + } + }, + }, + } + + fill_plan = pd.DataFrame( + { + "rule_name": ["fill_albania"], + "method": ["construct_from_sources"], + "status": ["ready"], + } + ) + + result = build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=config, + source_names=[ + "entsoe_api", + "opsd", + ], + ) + + assert result == { + "batches": [ + { + "batch_id": ( + "entsoe_api__" + "20200101T0000__" + "20200201T0000__" + "GRC" + ), + "source": "entsoe_api", + "start": "2020-01-01T00:00:00+00:00", + "end": "2020-02-01T00:00:00+00:00", + "countries": ["GRC"], + }, + { + "batch_id": ( + "opsd__" + "20200101T0000__" + "20200201T0000__" + "GRC" + ), + "source": "opsd", + "start": "2020-01-01T00:00:00+00:00", + "end": "2020-02-01T00:00:00+00:00", + "countries": ["GRC"], + }, + ] + } + +def test_empty_fill_plan_produces_no_acquisition_batches() -> None: + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + } + }, + "overrides": { + "fill_albania": { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2020-01-01", + "end": "2020-02-01", + "weight": 1, + } + ], + } + }, + }, + } + + result = build_auxiliary_acquisition_plan( + fill_plan=pd.DataFrame(), + gap_filling_config=config, + source_names=["entsoe_api"], + ) + + assert result == { + "batches": [], + } diff --git a/workflow/Snakefile b/workflow/Snakefile index e36502a..57a471f 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -37,6 +37,7 @@ workflow.source_path("scripts/_schemas.py") include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/prepare_demand.smk" +include: "rules/advanced.smk" rule all: diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk new file mode 100644 index 0000000..f57c1c9 --- /dev/null +++ b/workflow/rules/advanced.smk @@ -0,0 +1,17 @@ +checkpoint plan_auxiliary_data: + input: + fill_plan=rules.clean_demand.output.auxiliary_fill_plan, + output: + plan=( + "/automatic/" + "auxiliary/acquisition_plan.json" + ), + params: + gap_filling=config["gap_filling"], + source_names=config["load_sources"], + conda: + "../envs/module.yaml" + message: + "Plan auxiliary electricity-demand acquisition." + script: + "../scripts/plan_auxiliary_data.py" \ No newline at end of file diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 8bdb1c7..7f48bf0 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,18 +1,34 @@ """Rules used to download automatic resource files.""" -from _time import years_in_period -from cleaning.advanced.requirements import ( - build_auxiliary_acquisition_requirements, -) -from cleaning.advanced.source_requests import ( - build_auxiliary_source_batches, - build_auxiliary_source_requests, -) - - -NESO_YEARS = years_in_period( - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], +from datetime import datetime, timedelta + +def _years_in_period( + start: str, + end: str, + ) -> tuple[int, ...]: + """Return calendar years intersecting an end-exclusive period.""" + start = datetime.fromisoformat(start) + end = datetime.fromisoformat(end) + + if end <= start: + raise ValueError( + "Period end must be later than period start." + ) + + final_included_time = end - timedelta( + microseconds=1 + ) + + return tuple( + range( + start.year, + final_included_time.year + 1, + ) + ) + +NESO_YEARS = _years_in_period( + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], ) NESO_RAW_FILES = expand( @@ -21,66 +37,6 @@ NESO_RAW_FILES = expand( year=NESO_YEARS, ) -def _build_auxiliary_source_batches(): - """Build source-specific acquisition batches for advanced gap filling.""" - gap_filling = config["gap_filling"] - - if gap_filling["mode"] != "advanced": - return [] - - advanced = gap_filling["advanced"] - - requirements = build_auxiliary_acquisition_requirements( - overrides=advanced["overrides"], - basic_rules=gap_filling["basic"]["rules"], - basic_cleaning_enabled=( - advanced["auxiliary_data"] - ["basic_cleaning"] - ["enabled"] - ), - ) - - requests = build_auxiliary_source_requests( - requirements, - source_names=config["load_sources"], - ) - - return build_auxiliary_source_batches( - requests - ) - - -AUXILIARY_SOURCE_BATCHES = ( - _build_auxiliary_source_batches() -) - -def _index_auxiliary_batches( - source_name: str, -) -> dict[str, dict]: - """Index auxiliary batches belonging to one source.""" - source_batches = [ - batch - for batch in AUXILIARY_SOURCE_BATCHES - if batch["source"] == source_name - ] - - return { - str(index): batch - for index, batch in enumerate(source_batches) - } - - -AUXILIARY_ENTSOE_BATCHES = ( - _index_auxiliary_batches("entsoe_api") -) - -AUXILIARY_OPSD_BATCHES = ( - _index_auxiliary_batches("opsd") -) - -AUXILIARY_NESO_BATCHES = ( - _index_auxiliary_batches("neso") -) rule download_load_entsoe_api: input: diff --git a/workflow/scripts/_time.py b/workflow/scripts/_time.py index d119f59..1212f5f 100644 --- a/workflow/scripts/_time.py +++ b/workflow/scripts/_time.py @@ -60,4 +60,4 @@ def years_in_period( start_timestamp.year, final_included_time.year + 1, ) - ) \ No newline at end of file + ) diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/source_requests.py index f0f029d..72785bf 100644 --- a/workflow/scripts/cleaning/advanced/source_requests.py +++ b/workflow/scripts/cleaning/advanced/source_requests.py @@ -117,16 +117,43 @@ def build_auxiliary_source_batches( start, end, ), group in grouped: + + countries = sorted( + group["country"].unique().tolist() + ) + batches.append( { + "batch_id": _build_batch_id( + source=source, + start=start, + end=end, + countries=countries, + ), "source": source, "start": start, "end": end, - "countries": sorted( - group["country"].unique().tolist() - ), + "countries": countries, } ) return batches +def _build_batch_id( + *, + source: str, + start: pd.Timestamp, + end: pd.Timestamp, + countries: list[str], +) -> str: + """Build a deterministic identifier for an auxiliary source batch.""" + countries_part = "-".join( + sorted(countries) + ) + + return ( + f"{source}__" + f"{start.strftime('%Y%m%dT%H%M')}__" + f"{end.strftime('%Y%m%dT%H%M')}__" + f"{countries_part}" + ) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py new file mode 100644 index 0000000..2988141 --- /dev/null +++ b/workflow/scripts/plan_auxiliary_data.py @@ -0,0 +1,102 @@ +"""Plan auxiliary electricity-demand acquisition.""" + +import json +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pandas as pd +from cleaning.advanced.requirements import build_auxiliary_acquisition_requirements +from cleaning.advanced.source_requests import ( + build_auxiliary_source_batches, + build_auxiliary_source_requests, +) + +if TYPE_CHECKING: + snakemake: Any + + +def build_auxiliary_acquisition_plan( + *, + fill_plan: pd.DataFrame, + gap_filling_config: Mapping[str, Any], + source_names: Sequence[str], +) -> dict[str, list[dict[str, object]]]: + """Build a JSON-serializable auxiliary acquisition plan.""" + if gap_filling_config["mode"] != "advanced": + return {"batches": []} + + if fill_plan.empty: + return {"batches": []} + + advanced = gap_filling_config["advanced"] + + requirements = build_auxiliary_acquisition_requirements( + overrides=advanced["overrides"], + basic_rules=gap_filling_config["basic"]["rules"], + basic_cleaning_enabled=( + advanced["auxiliary_data"] + ["basic_cleaning"] + ["enabled"] + ), + ) + + requests = build_auxiliary_source_requests( + requirements, + source_names=source_names, + ) + + batches = build_auxiliary_source_batches( + requests + ) + + return { + "batches": [ + { + **batch, + "start": batch["start"].isoformat(), + "end": batch["end"].isoformat(), + } + for batch in batches + ] + } + + +def write_auxiliary_acquisition_plan( + *, + plan: dict[str, list[dict[str, object]]], + output_path: str | Path, +) -> None: + """Write the auxiliary acquisition plan as JSON.""" + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with output_path.open( + "w", + encoding="utf-8", + ) as file: + json.dump( + plan, + file, + indent=2, + ) + + +if __name__ == "__main__": + fill_plan = pd.read_parquet( + snakemake.input.fill_plan + ) + + plan = build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=snakemake.params.gap_filling, + source_names=snakemake.params.source_names, + ) + + write_auxiliary_acquisition_plan( + plan=plan, + output_path=snakemake.output.plan, + ) From 0ff9cdb71f164f8dc4c792b4d10b364ff57a1f04 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 14:36:55 +0200 Subject: [PATCH 026/241] Bug fixes with aux checkpoint --- tests/integration/test_config.yaml | 13 ++++++++++++- tests/unit/test_advanced_source_requests.py | 14 +++++++------- tests/unit/test_plan_auxiliary_data.py | 10 +++++----- .../scripts/cleaning/advanced/source_requests.py | 4 ++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 7a7b3dd..37a34a6 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -46,4 +46,15 @@ integration_test: enabled: true # Explicit country-period instructions for unresolved cases. - overrides: {} \ No newline at end of file + overrides: + test_construct_from_greece: + country: ALB + start: "2020-01-01" + end: "2020-02-01" + scope: fill_gaps_within_period + method: construct_from_sources + sources: + - country: GRC + start: "2020-01-01" + end: "2020-02-01" + weight: 1 \ No newline at end of file diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index b5c719b..bda9e82 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -36,7 +36,7 @@ def test_build_source_requests_uses_all_applicable_sources() -> None: source_names=[ "entsoe_api", "neso", - "opsd", + "opsd_api", ], ) @@ -49,8 +49,8 @@ def test_build_source_requests_uses_all_applicable_sources() -> None: ("entsoe_api", "GBR"), ("entsoe_api", "GRC"), ("neso", "GBR"), - ("opsd", "GBR"), - ("opsd", "GRC"), + ("opsd_api", "GBR"), + ("opsd_api", "GRC"), ] @@ -96,7 +96,7 @@ def test_empty_requirements_return_empty_source_request_schema() -> None: source_names=[ "entsoe_api", "neso", - "opsd", + "opsd_api", ], ) @@ -283,7 +283,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: { "source": [ "entsoe_api", - "opsd", + "opsd_api", ], "country": [ "GBR", @@ -311,7 +311,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: for batch in result ] == [ "entsoe_api", - "opsd", + "opsd_api", ] assert [ @@ -325,7 +325,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: "GBR" ), ( - "opsd__" + "opsd_api__" "20200101T0000__" "20200201T0000__" "GBR" diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 3c6f98b..01a6268 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -32,7 +32,7 @@ def test_plan_is_empty_outside_advanced_mode() -> None: source_names=[ "entsoe_api", "neso", - "opsd", + "opsd_api", ], ) @@ -65,7 +65,7 @@ def test_advanced_plan_with_no_overrides_is_empty() -> None: source_names=[ "entsoe_api", "neso", - "opsd", + "opsd_api", ], ) @@ -118,7 +118,7 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: gap_filling_config=config, source_names=[ "entsoe_api", - "opsd", + "opsd_api", ], ) @@ -138,12 +138,12 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: }, { "batch_id": ( - "opsd__" + "opsd_api__" "20200101T0000__" "20200201T0000__" "GRC" ), - "source": "opsd", + "source": "opsd_api", "start": "2020-01-01T00:00:00+00:00", "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/source_requests.py index 72785bf..49298a8 100644 --- a/workflow/scripts/cleaning/advanced/source_requests.py +++ b/workflow/scripts/cleaning/advanced/source_requests.py @@ -16,7 +16,7 @@ SUPPORTED_SOURCES = { "entsoe_api", "neso", - "opsd", + "opsd_api", } @@ -68,7 +68,7 @@ def _source_supports_country( if source_name in { "entsoe_api", - "opsd", + "opsd_api", }: return True From 4d3ce51874545ff70aaba8413c68b5c889b20a83 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 14:48:02 +0200 Subject: [PATCH 027/241] Updated cleaned boundary --- workflow/rules/advanced.smk | 17 ++++++++++++++++- workflow/rules/clean.smk | 4 ++-- workflow/scripts/finalise_clean_demand.py | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 workflow/scripts/finalise_clean_demand.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index f57c1c9..0523875 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -14,4 +14,19 @@ checkpoint plan_auxiliary_data: message: "Plan auxiliary electricity-demand acquisition." script: - "../scripts/plan_auxiliary_data.py" \ No newline at end of file + "../scripts/plan_auxiliary_data.py" + +rule finalise_clean_demand: + input: + demand=rules.clean_demand.output.demand, + output: + demand=( + "/automatic/" + "load_cleaned.parquet" + ), + conda: + "../envs/module.yaml" + message: + "Finalise cleaned electricity demand." + script: + "../scripts/finalise_clean_demand.py" \ No newline at end of file diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 8c94687..9231815 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -86,7 +86,7 @@ rule clean_demand: output: demand=( "/automatic/" - "load_cleaned.parquet" + "load_basic_cleaned.parquet" ), data_source=( "/automatic/" @@ -125,7 +125,7 @@ rule plot_cleaning_timeline: input: demand=( "/automatic/" - "load_cleaned.parquet" + "load_basic_cleaned.parquet" ), cleaning_method=( "/automatic/" diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py new file mode 100644 index 0000000..a83976b --- /dev/null +++ b/workflow/scripts/finalise_clean_demand.py @@ -0,0 +1,15 @@ +import shutil +from pathlib import Path + +source = Path(snakemake.input.demand) +target = Path(snakemake.output.demand) + +target.parent.mkdir( + parents=True, + exist_ok=True, +) + +shutil.copyfile( + source, + target, +) From 19767a2cb3562a2dba475bab8c3b59003dd1287d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 14:55:16 +0200 Subject: [PATCH 028/241] Batch ID in advanced mode updated to hash countries as there was potential for file names too long for Windows --- tests/unit/test_advanced_source_requests.py | 120 ++++++++++++------ tests/unit/test_plan_auxiliary_data.py | 32 +++-- .../cleaning/advanced/source_requests.py | 9 +- 3 files changed, 111 insertions(+), 50 deletions(-) diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index bda9e82..fc50134 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -180,14 +180,42 @@ def test_build_batch_id_is_independent_of_country_order() -> None: assert first == second - assert first == ( + assert first.startswith( "entsoe_api__" "20200101T0000__" "20200201T0000__" - "ALB-GRC" ) +def test_build_batch_id_changes_for_different_country_sets() -> None: + common = { + "source": "entsoe_api", + "start": pd.Timestamp( + "2020-01-01", + tz="UTC", + ), + "end": pd.Timestamp( + "2020-02-01", + tz="UTC", + ), + } + + first = _build_batch_id( + **common, + countries=["ALB"], + ) + + second = _build_batch_id( + **common, + countries=[ + "ALB", + "GRC", + ], + ) + + assert first != second + + def test_build_source_batches_groups_matching_periods() -> None: requests = pd.DataFrame( { @@ -226,11 +254,20 @@ def test_build_source_batches_groups_matching_periods() -> None: assert result == [ { - "batch_id": ( - "entsoe_api__" - "20200101T0000__" - "20200201T0000__" - "ALB-GRC" + "batch_id": _build_batch_id( + source="entsoe_api", + start=pd.Timestamp( + "2020-01-01", + tz="UTC", + ), + end=pd.Timestamp( + "2020-02-01", + tz="UTC", + ), + countries=[ + "ALB", + "GRC", + ], ), "source": "entsoe_api", "start": pd.Timestamp( @@ -247,11 +284,19 @@ def test_build_source_batches_groups_matching_periods() -> None: ], }, { - "batch_id": ( - "entsoe_api__" - "20210101T0000__" - "20210201T0000__" - "MNE" + "batch_id": _build_batch_id( + source="entsoe_api", + start=pd.Timestamp( + "2021-01-01", + tz="UTC", + ), + end=pd.Timestamp( + "2021-02-01", + tz="UTC", + ), + countries=[ + "MNE", + ], ), "source": "entsoe_api", "start": pd.Timestamp( @@ -304,32 +349,31 @@ def test_build_source_batches_keeps_sources_separate() -> None: requests ) - assert len(result) == 2 - - assert [ - batch["source"] - for batch in result - ] == [ - "entsoe_api", - "opsd_api", - ] - - assert [ - batch["batch_id"] - for batch in result - ] == [ - ( - "entsoe_api__" - "20200101T0000__" - "20200201T0000__" - "GBR" - ), - ( - "opsd_api__" - "20200101T0000__" - "20200201T0000__" - "GBR" - ), + assert result == [ + { + "batch_id": _build_batch_id( + source="entsoe_api", + start=start, + end=end, + countries=["GBR"], + ), + "source": "entsoe_api", + "start": start, + "end": end, + "countries": ["GBR"], + }, + { + "batch_id": _build_batch_id( + source="opsd_api", + start=start, + end=end, + countries=["GBR"], + ), + "source": "opsd_api", + "start": start, + "end": end, + "countries": ["GBR"], + }, ] diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 01a6268..57d41e3 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -1,6 +1,7 @@ """Tests for auxiliary acquisition planning.""" import pandas as pd +from cleaning.advanced.source_requests import _build_batch_id from plan_auxiliary_data import build_auxiliary_acquisition_plan @@ -73,6 +74,7 @@ def test_advanced_plan_with_no_overrides_is_empty() -> None: "batches": [], } + def test_advanced_plan_builds_serializable_source_batches() -> None: config = { "mode": "advanced", @@ -122,14 +124,23 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: ], ) + start = pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + end = pd.Timestamp( + "2020-02-01", + tz="UTC", + ) + assert result == { "batches": [ { - "batch_id": ( - "entsoe_api__" - "20200101T0000__" - "20200201T0000__" - "GRC" + "batch_id": _build_batch_id( + source="entsoe_api", + start=start, + end=end, + countries=["GRC"], ), "source": "entsoe_api", "start": "2020-01-01T00:00:00+00:00", @@ -137,11 +148,11 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: "countries": ["GRC"], }, { - "batch_id": ( - "opsd_api__" - "20200101T0000__" - "20200201T0000__" - "GRC" + "batch_id": _build_batch_id( + source="opsd_api", + start=start, + end=end, + countries=["GRC"], ), "source": "opsd_api", "start": "2020-01-01T00:00:00+00:00", @@ -151,6 +162,7 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: ] } + def test_empty_fill_plan_produces_no_acquisition_batches() -> None: config = { "mode": "advanced", diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/source_requests.py index 49298a8..12f975c 100644 --- a/workflow/scripts/cleaning/advanced/source_requests.py +++ b/workflow/scripts/cleaning/advanced/source_requests.py @@ -2,6 +2,7 @@ from __future__ import annotations +import hashlib from collections.abc import Sequence import pandas as pd @@ -147,13 +148,17 @@ def _build_batch_id( countries: list[str], ) -> str: """Build a deterministic identifier for an auxiliary source batch.""" - countries_part = "-".join( + countries_key = ",".join( sorted(countries) ) + countries_hash = hashlib.sha1( + countries_key.encode("utf-8") + ).hexdigest()[:8] + return ( f"{source}__" f"{start.strftime('%Y%m%dT%H%M')}__" f"{end.strftime('%Y%m%dT%H%M')}__" - f"{countries_part}" + f"{countries_hash}" ) From f4d496b34eef5374f3159b0a43cabe8e9e1389c8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 15:12:13 +0200 Subject: [PATCH 029/241] Add dynamic auxiliary ENTSO-E and OPSD preparation --- workflow/rules/advanced.smk | 221 +++++++++++++++++++++++++++++++++++- 1 file changed, 220 insertions(+), 1 deletion(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 0523875..2dd847f 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,3 +1,136 @@ +import json + +def auxiliary_acquisition_plan(_wildcards): + """Return the acquisition plan after the checkpoint completes.""" + return ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + +def get_auxiliary_batch( + plan_path, + *, + batch_id: str, + source: str, +) -> dict: + """Return one source batch from the acquisition plan.""" + with open( + plan_path, + encoding="utf-8", + ) as file: + plan = json.load(file) + + matches = [ + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == batch_id + and batch["source"] == source + ) + ] + + if len(matches) != 1: + raise ValueError( + "Expected exactly one auxiliary batch for " + f"{source=} and {batch_id=}, found {len(matches)}." + ) + + return matches[0] + + +def get_auxiliary_entsoe_batch( + wildcards, + input, +) -> dict: + """Return the ENTSO-E batch for this job.""" + return get_auxiliary_batch( + input.plan, + batch_id=wildcards.batch_id, + source="entsoe_api", + ) + + +def auxiliary_entsoe_outputs(_wildcards): + """Return all ENTSO-E outputs required by the acquisition plan.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + with open( + plan_path, + encoding="utf-8", + ) as file: + plan = json.load(file) + + batch_ids = [ + batch["batch_id"] + for batch in plan["batches"] + if batch["source"] == "entsoe_api" + ] + + return [ + ( + "/automatic/" + "auxiliary/entsoe_api/" + f"{batch_id}.parquet" + ) + for batch_id in batch_ids + ] + + +def get_auxiliary_opsd_batch( + wildcards, + input, +) -> dict: + """Return the OPSD batch for this job.""" + return get_auxiliary_batch( + input.plan, + batch_id=wildcards.batch_id, + source="opsd_api", + ) + + +def auxiliary_opsd_outputs(_wildcards): + """Return all OPSD outputs required by the acquisition plan.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + with open( + plan_path, + encoding="utf-8", + ) as file: + plan = json.load(file) + + batch_ids = [ + batch["batch_id"] + for batch in plan["batches"] + if batch["source"] == "opsd_api" + ] + + return [ + ( + "/automatic/" + "auxiliary/opsd_api/" + f"{batch_id}.parquet" + ) + for batch_id in batch_ids + ] + + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -16,6 +149,7 @@ checkpoint plan_auxiliary_data: script: "../scripts/plan_auxiliary_data.py" + rule finalise_clean_demand: input: demand=rules.clean_demand.output.demand, @@ -29,4 +163,89 @@ rule finalise_clean_demand: message: "Finalise cleaned electricity demand." script: - "../scripts/finalise_clean_demand.py" \ No newline at end of file + "../scripts/finalise_clean_demand.py" + + +rule download_auxiliary_load_entsoe_api: + input: + token_entsoe="", + plan=auxiliary_acquisition_plan, + output: + load=( + "/automatic/" + "auxiliary/entsoe_api/" + "{batch_id}.parquet" + ), + params: + temporal_start=lambda wildcards, input: ( + get_auxiliary_entsoe_batch( + wildcards, + input, + )["start"] + ), + temporal_end=lambda wildcards, input: ( + get_auxiliary_entsoe_batch( + wildcards, + input, + )["end"] + ), + country_codes=lambda wildcards, input: ( + get_auxiliary_entsoe_batch( + wildcards, + input, + )["countries"] + ), + log: + ( + "/auxiliary/" + "entsoe_api/{batch_id}.log" + ), + localrule: True + conda: + "../envs/module.yaml" + message: + "Download auxiliary electricity load from ENTSO-E." + script: + "../scripts/download_load_entsoe_api.py" + + +rule prepare_auxiliary_load_opsd: + input: + load=rules.download_load_entsoe_opsd.output.load, + plan=auxiliary_acquisition_plan, + output: + load=( + "/automatic/" + "auxiliary/opsd_api/" + "{batch_id}.parquet" + ), + params: + start=lambda wildcards, input: ( + get_auxiliary_opsd_batch( + wildcards, + input, + )["start"] + ), + end=lambda wildcards, input: ( + get_auxiliary_opsd_batch( + wildcards, + input, + )["end"] + ), + country_codes=lambda wildcards, input: ( + get_auxiliary_opsd_batch( + wildcards, + input, + )["countries"] + ), + log: + ( + "/auxiliary/" + "opsd_api/{batch_id}.log" + ), + conda: + "../envs/module.yaml" + message: + "Prepare auxiliary electricity-demand data from OPSD." + script: + "../scripts/prepare_load_opsd.py" \ No newline at end of file From 604364cbe448831796eefc2a97552e327486ce5f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 15:18:39 +0200 Subject: [PATCH 030/241] Make NESO downloads reusable by year --- workflow/Snakefile | 1 + workflow/rules/automatic.smk | 13 ++++++++----- workflow/rules/clean.smk | 2 +- workflow/scripts/download_load_neso.py | 6 ++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index 57a471f..4387115 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -34,6 +34,7 @@ workflow.source_path("scripts/_schemas.py") # Add all your includes here. +#important tha automatic.smk is included first, because clean.smk uses parameters defined within automatic.smk include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/prepare_demand.smk" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 7f48bf0..47eab6a 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -75,18 +75,21 @@ rule download_load_entsoe_opsd: curl -sSLo {output.load:q} {params.url_load:q} """ -rule download_load_neso: +rule download_load_neso_year: output: - annual_files=NESO_RAW_FILES, + annual_file=( + "/automatic/neso/" + "historic_demand_{year}.csv" + ), params: - years=NESO_YEARS, + year=lambda wildcards: int(wildcards.year), log: - "/download_load_neso.log", + "/download_load_neso_{year}.log", localrule: True conda: "../envs/module.yaml" message: - "Download historic electricity demand from NESO." + "Download NESO historic electricity demand for {wildcards.year}." script: "../scripts/download_load_neso.py" diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 9231815..d1557d6 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -19,7 +19,7 @@ rule prepare_load_opsd: rule prepare_load_neso: input: - annual_files=rules.download_load_neso.output.annual_files, + annual_files=NESO_RAW_FILES, output: load="/automatic/load_neso.parquet", params: diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 7f027a7..089feee 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -257,11 +257,9 @@ def download_annual_files( download_annual_files( years=[ - int(year) - for year in snakemake.params.years + int(snakemake.params.year) ], output_paths=[ - Path(path) - for path in snakemake.output + Path(snakemake.output.annual_file) ], ) \ No newline at end of file From dfaf983147f942cf648e0d2392f743f5909764ff Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 15:25:23 +0200 Subject: [PATCH 031/241] Prepare neso auxiliary data --- tests/integration/test_config.yaml | 2 +- workflow/rules/advanced.smk | 116 ++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 37a34a6..0d1fa17 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -54,7 +54,7 @@ integration_test: scope: fill_gaps_within_period method: construct_from_sources sources: - - country: GRC + - country: GBR start: "2020-01-01" end: "2020-02-01" weight: 1 \ No newline at end of file diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 2dd847f..dc8a0a7 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -130,6 +130,79 @@ def auxiliary_opsd_outputs(_wildcards): ] +def get_auxiliary_neso_batch( + wildcards, + input, +) -> dict: + """Return the NESO batch for this job.""" + return get_auxiliary_batch( + input.plan, + batch_id=wildcards.batch_id, + source="neso", + ) + + +def auxiliary_neso_raw_files(wildcards): + """Return annual NESO files required by one auxiliary batch.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + batch = get_auxiliary_batch( + plan_path, + batch_id=wildcards.batch_id, + source="neso", + ) + + years = _years_in_period( + batch["start"], + batch["end"], + ) + + return [ + ( + "/automatic/neso/" + f"historic_demand_{year}.csv" + ) + for year in years + ] + + +def auxiliary_neso_outputs(_wildcards): + """Return all NESO outputs required by the acquisition plan.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + with open( + plan_path, + encoding="utf-8", + ) as file: + plan = json.load(file) + + batch_ids = [ + batch["batch_id"] + for batch in plan["batches"] + if batch["source"] == "neso" + ] + + return [ + ( + "/automatic/" + "auxiliary/neso/" + f"{batch_id}.parquet" + ) + for batch_id in batch_ids + ] + checkpoint plan_auxiliary_data: input: @@ -248,4 +321,45 @@ rule prepare_auxiliary_load_opsd: message: "Prepare auxiliary electricity-demand data from OPSD." script: - "../scripts/prepare_load_opsd.py" \ No newline at end of file + "../scripts/prepare_load_opsd.py" + +rule prepare_auxiliary_load_neso: + input: + plan=auxiliary_acquisition_plan, + annual_files=auxiliary_neso_raw_files, + output: + load=( + "/automatic/" + "auxiliary/neso/" + "{batch_id}.parquet" + ), + params: + start=lambda wildcards, input: ( + get_auxiliary_neso_batch( + wildcards, + input, + )["start"] + ), + end=lambda wildcards, input: ( + get_auxiliary_neso_batch( + wildcards, + input, + )["end"] + ), + country_codes=lambda wildcards, input: ( + get_auxiliary_neso_batch( + wildcards, + input, + )["countries"] + ), + log: + ( + "/auxiliary/" + "neso/{batch_id}.log" + ), + conda: + "../envs/module.yaml" + message: + "Prepare auxiliary electricity-demand data from NESO." + script: + "../scripts/prepare_load_neso.py" \ No newline at end of file From 370f11ffe2e1b3efcc865be6bf789163b2308f9a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 7 Aug 2026 15:44:17 +0200 Subject: [PATCH 032/241] Combines the separate auxiliary sources into one dataframe (not implemented the cleaning yet) --- tests/unit/test_advanced_combine_auxiliary.py | 85 ++++++++++++++ tests/unit/test_advanced_source_requests.py | 100 ++++++++++------ tests/unit/test_plan_auxiliary_data.py | 12 +- workflow/rules/advanced.smk | 111 +++++++++++++++++- .../cleaning/advanced/combine_auxiliary.py | 38 ++++++ .../cleaning/advanced/source_requests.py | 30 ++++- workflow/scripts/combine_auxiliary_sources.py | 32 +++++ 7 files changed, 364 insertions(+), 44 deletions(-) create mode 100644 tests/unit/test_advanced_combine_auxiliary.py create mode 100644 workflow/scripts/cleaning/advanced/combine_auxiliary.py create mode 100644 workflow/scripts/combine_auxiliary_sources.py diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py new file mode 100644 index 0000000..20eb930 --- /dev/null +++ b/tests/unit/test_advanced_combine_auxiliary.py @@ -0,0 +1,85 @@ +import pandas as pd +from cleaning.advanced.combine_auxiliary import combine_auxiliary_sources + + +def test_combine_auxiliary_sources_aligns_country_columns() -> None: + index = pd.date_range( + "2020-01-01", + periods=2, + freq="h", + tz="UTC", + ) + + entsoe = pd.DataFrame( + { + "GBR": [10.0, 11.0], + "GRC": [20.0, 21.0], + }, + index=index, + ) + + neso = pd.DataFrame( + { + "GBR": [12.0, 13.0], + }, + index=index, + ) + + combined, data_source, cleaning_method = ( + combine_auxiliary_sources( + { + "entsoe_api": entsoe, + "neso": neso, + }, + priority=[ + "neso", + "entsoe_api", + ], + ) + ) + + assert list(combined.columns) == [ + "GBR", + "GRC", + ] + + assert combined["GBR"].tolist() == [ + 12.0, + 13.0, + ] + + assert combined["GRC"].tolist() == [ + 20.0, + 21.0, + ] + + assert data_source["GBR"].tolist() == [ + "neso", + "neso", + ] + + assert data_source["GRC"].tolist() == [ + "entsoe_api", + "entsoe_api", + ] + + assert cleaning_method.shape == combined.shape + + +def test_combine_auxiliary_sources_handles_empty_input() -> None: + combined, data_source, cleaning_method = ( + combine_auxiliary_sources( + {}, + priority=[ + "entsoe_api", + "neso", + "opsd_api", + ], + ) + ) + + assert combined.empty + assert data_source.empty + assert cleaning_method.empty + + diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index fc50134..f2b5322 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -5,6 +5,7 @@ from cleaning.advanced.source_requests import ( SOURCE_REQUEST_COLUMNS, _build_batch_id, + _build_group_id, build_auxiliary_source_batches, build_auxiliary_source_requests, ) @@ -216,6 +217,25 @@ def test_build_batch_id_changes_for_different_country_sets() -> None: assert first != second +def test_build_group_id_depends_only_on_period() -> None: + start = pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + end = pd.Timestamp( + "2020-02-01", + tz="UTC", + ) + + assert _build_group_id( + start=start, + end=end, + ) == ( + "20200101T0000__" + "20200201T0000" + ) + + def test_build_source_batches_groups_matching_periods() -> None: requests = pd.DataFrame( { @@ -252,64 +272,61 @@ def test_build_source_batches_groups_matching_periods() -> None: requests ) + first_start = pd.Timestamp( + "2020-01-01", + tz="UTC", + ) + first_end = pd.Timestamp( + "2020-02-01", + tz="UTC", + ) + second_start = pd.Timestamp( + "2021-01-01", + tz="UTC", + ) + second_end = pd.Timestamp( + "2021-02-01", + tz="UTC", + ) + assert result == [ { + "group_id": _build_group_id( + start=first_start, + end=first_end, + ), "batch_id": _build_batch_id( source="entsoe_api", - start=pd.Timestamp( - "2020-01-01", - tz="UTC", - ), - end=pd.Timestamp( - "2020-02-01", - tz="UTC", - ), + start=first_start, + end=first_end, countries=[ "ALB", "GRC", ], ), "source": "entsoe_api", - "start": pd.Timestamp( - "2020-01-01", - tz="UTC", - ), - "end": pd.Timestamp( - "2020-02-01", - tz="UTC", - ), + "start": first_start, + "end": first_end, "countries": [ "ALB", "GRC", ], }, { + "group_id": _build_group_id( + start=second_start, + end=second_end, + ), "batch_id": _build_batch_id( source="entsoe_api", - start=pd.Timestamp( - "2021-01-01", - tz="UTC", - ), - end=pd.Timestamp( - "2021-02-01", - tz="UTC", - ), - countries=[ - "MNE", - ], + start=second_start, + end=second_end, + countries=["MNE"], ), "source": "entsoe_api", - "start": pd.Timestamp( - "2021-01-01", - tz="UTC", - ), - "end": pd.Timestamp( - "2021-02-01", - tz="UTC", - ), - "countries": [ - "MNE", - ], + "start": second_start, + "end": second_end, + "countries": ["MNE"], }, ] @@ -349,8 +366,14 @@ def test_build_source_batches_keeps_sources_separate() -> None: requests ) + group_id = _build_group_id( + start=start, + end=end, + ) + assert result == [ { + "group_id": group_id, "batch_id": _build_batch_id( source="entsoe_api", start=start, @@ -363,6 +386,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: "countries": ["GBR"], }, { + "group_id": group_id, "batch_id": _build_batch_id( source="opsd_api", start=start, diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 57d41e3..3a05672 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -1,7 +1,10 @@ """Tests for auxiliary acquisition planning.""" import pandas as pd -from cleaning.advanced.source_requests import _build_batch_id +from cleaning.advanced.source_requests import ( + _build_batch_id, + _build_group_id, +) from plan_auxiliary_data import build_auxiliary_acquisition_plan @@ -133,9 +136,15 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: tz="UTC", ) + group_id = _build_group_id( + start=start, + end=end, + ) + assert result == { "batches": [ { + "group_id": group_id, "batch_id": _build_batch_id( source="entsoe_api", start=start, @@ -148,6 +157,7 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: "countries": ["GRC"], }, { + "group_id": group_id, "batch_id": _build_batch_id( source="opsd_api", start=start, diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index dc8a0a7..6fb3825 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -204,6 +204,85 @@ def auxiliary_neso_outputs(_wildcards): ] +def get_auxiliary_group_batches( + plan_path, + *, + group_id: str, +) -> list[dict]: + """Return all acquisition batches belonging to one auxiliary group.""" + with open(plan_path, encoding="utf-8") as file: + plan = json.load(file) + + batches = [ + batch + for batch in plan["batches"] + if batch["group_id"] == group_id + ] + + if not batches: + raise ValueError( + f"No auxiliary batches found for group {group_id!r}." + ) + + return batches + + +def auxiliary_group_source_files(wildcards): + """Return prepared source files for one auxiliary group.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + batches = get_auxiliary_group_batches( + plan_path, + group_id=wildcards.group_id, + ) + + return [ + ( + "/automatic/" + f"auxiliary/{batch['source']}/" + f"{batch['batch_id']}.parquet" + ) + for batch in batches + ] + + +def auxiliary_combined_outputs(_wildcards): + """Return all combined auxiliary group outputs.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + with open(plan_path, encoding="utf-8") as file: + plan = json.load(file) + + group_ids = sorted( + { + batch["group_id"] + for batch in plan["batches"] + } + ) + + return [ + ( + "/automatic/" + "auxiliary/combined/" + f"{group_id}.parquet" + ) + for group_id in group_ids + ] + + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -362,4 +441,34 @@ rule prepare_auxiliary_load_neso: message: "Prepare auxiliary electricity-demand data from NESO." script: - "../scripts/prepare_load_neso.py" \ No newline at end of file + "../scripts/prepare_load_neso.py" + + +rule combine_auxiliary_sources: + input: + plan=auxiliary_acquisition_plan, + sources=auxiliary_group_source_files, + output: + demand=( + "/automatic/" + "auxiliary/combined/" + "{group_id}.parquet" + ), + data_source=( + "/automatic/" + "auxiliary/combined/" + "{group_id}_data_source.parquet" + ), + cleaning_method=( + "/automatic/" + "auxiliary/combined/" + "{group_id}_cleaning_method.parquet" + ), + params: + source_priority=config["load_sources"], + conda: + "../envs/module.yaml" + message: + "Combine auxiliary electricity-demand sources." + script: + "../scripts/combine_auxiliary_sources.py" diff --git a/workflow/scripts/cleaning/advanced/combine_auxiliary.py b/workflow/scripts/cleaning/advanced/combine_auxiliary.py new file mode 100644 index 0000000..66d9141 --- /dev/null +++ b/workflow/scripts/cleaning/advanced/combine_auxiliary.py @@ -0,0 +1,38 @@ +from collections.abc import Mapping, Sequence + +import pandas as pd + +from cleaning.combine_sources import combine_sources + + +def combine_auxiliary_sources( + loads: Mapping[str, pd.DataFrame], + *, + priority: Sequence[str], +) -> tuple[ + pd.DataFrame, + pd.DataFrame, + pd.DataFrame, +]: + """Combine prepared auxiliary sources using configured source priority.""" + if not loads: + empty = pd.DataFrame() + return empty, empty.copy(), empty.copy() + + columns = sorted( + { + column + for load in loads.values() + for column in load.columns + } + ) + + aligned = { + source: load.reindex(columns=columns) + for source, load in loads.items() + } + + return combine_sources( + aligned, + priority=priority, + ) diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/source_requests.py index 12f975c..a2777b3 100644 --- a/workflow/scripts/cleaning/advanced/source_requests.py +++ b/workflow/scripts/cleaning/advanced/source_requests.py @@ -102,14 +102,18 @@ def _validate_source_names( def build_auxiliary_source_batches( requests: pd.DataFrame, ) -> list[dict[str, object]]: - """Group source requests sharing the same acquisition period.""" + """Group compatible auxiliary source requests into batches.""" if requests.empty: return [] batches: list[dict[str, object]] = [] grouped = requests.groupby( - ["source", "start", "end"], + [ + "source", + "start", + "end", + ], sort=False, ) @@ -118,13 +122,18 @@ def build_auxiliary_source_batches( start, end, ), group in grouped: - countries = sorted( - group["country"].unique().tolist() + group["country"] + .unique() + .tolist() ) batches.append( { + "group_id": _build_group_id( + start=start, + end=end, + ), "batch_id": _build_batch_id( source=source, start=start, @@ -140,6 +149,7 @@ def build_auxiliary_source_batches( return batches + def _build_batch_id( *, source: str, @@ -162,3 +172,15 @@ def _build_batch_id( f"{end.strftime('%Y%m%dT%H%M')}__" f"{countries_hash}" ) + + +def _build_group_id( + *, + start: pd.Timestamp, + end: pd.Timestamp, +) -> str: + """Build a deterministic identifier for an auxiliary period group.""" + return ( + f"{start.strftime('%Y%m%dT%H%M')}__" + f"{end.strftime('%Y%m%dT%H%M')}" + ) diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py new file mode 100644 index 0000000..d0711b2 --- /dev/null +++ b/workflow/scripts/combine_auxiliary_sources.py @@ -0,0 +1,32 @@ +from pathlib import Path + +import pandas as pd +from cleaning.advanced.combine_auxiliary import combine_auxiliary_sources + + +def _source_name(path: str | Path) -> str: + """Return source name from auxiliary source path.""" + return Path(path).parent.name + + +loads = { + _source_name(path): pd.read_parquet(path) + for path in snakemake.input.sources +} + +combined, data_source, cleaning_method = ( + combine_auxiliary_sources( + loads, + priority=snakemake.params.source_priority, + ) +) + +combined.to_parquet( + snakemake.output.demand +) +data_source.to_parquet( + snakemake.output.data_source +) +cleaning_method.to_parquet( + snakemake.output.cleaning_method +) From d41be022b1ab4e9aab696b79f50074627ec6bf2a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 10:44:05 +0200 Subject: [PATCH 033/241] Apply basic cleaning to auxiliary fill data --- workflow/rules/advanced.smk | 41 ++++++++++++++++++++++++ workflow/scripts/clean_auxiliary_data.py | 27 ++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 workflow/scripts/clean_auxiliary_data.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 6fb3825..db750b7 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -472,3 +472,44 @@ rule combine_auxiliary_sources: "Combine auxiliary electricity-demand sources." script: "../scripts/combine_auxiliary_sources.py" + + +rule clean_auxiliary_data: + input: + demand=( + "/automatic/" + "auxiliary/combined/" + "{group_id}.parquet" + ), + cleaning_method=( + "/automatic/" + "auxiliary/combined/" + "{group_id}_cleaning_method.parquet" + ), + output: + demand=( + "/automatic/" + "auxiliary/cleaned/" + "{group_id}.parquet" + ), + cleaning_method=( + "/automatic/" + "auxiliary/cleaned/" + "{group_id}_cleaning_method.parquet" + ), + params: + basic_rules=config["gap_filling"]["basic"]["rules"], + enabled=( + config["gap_filling"] + ["advanced"] + ["auxiliary_data"] + ["basic_cleaning"] + ["enabled"] + ), + conda: + "../envs/module.yaml" + message: + "Apply basic cleaning to auxiliary electricity demand." + script: + "../scripts/clean_auxiliary_data.py" + diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py new file mode 100644 index 0000000..a94c6a2 --- /dev/null +++ b/workflow/scripts/clean_auxiliary_data.py @@ -0,0 +1,27 @@ +import pandas as pd + +from cleaning.basic.pipeline import fill_basic_gaps + + +load = pd.read_parquet( + snakemake.input.demand +) + +cleaning_method = pd.read_parquet( + snakemake.input.cleaning_method +) + +cleaned, cleaning_method = fill_basic_gaps( + load, + cleaning_method=cleaning_method, + rules=snakemake.params.basic_rules, + enabled=snakemake.params.enabled, +) + +cleaned.to_parquet( + snakemake.output.demand +) + +cleaning_method.to_parquet( + snakemake.output.cleaning_method +) From ca225421a3b9e12cd39076da0dbfedc9cf55660f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 10:49:40 +0200 Subject: [PATCH 034/241] Implement weighted auxiliary source construction --- .../test_advanced_construct_from_sources.py | 138 ++++++++++++++---- .../advanced/construct_from_sources.py | 71 +++++++-- 2 files changed, 168 insertions(+), 41 deletions(-) diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index b2c2795..eeb82cd 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -1,44 +1,124 @@ """Tests for source-based advanced profile construction.""" import pandas as pd -import pytest from cleaning.advanced.construct_from_sources import construct_from_sources -def test_construct_from_sources_is_not_yet_implemented() -> None: - index = pd.date_range( - "2021-01-01", - periods=2, +def test_construct_from_single_source() -> None: + auxiliary_index = pd.date_range( + "2019-01-01", + periods=3, freq="h", tz="UTC", ) - load = pd.DataFrame( - {"ALB": [1.0, 2.0]}, - index=index, + auxiliary = pd.DataFrame( + { + "GRC": [ + 10.0, + 20.0, + 30.0, + ] + }, + index=auxiliary_index, ) - rule = { - "country": "ALB", - "start": "2021-01-01", - "end": "2021-01-01 01:00", - "scope": "overwrite_entire_period", - "method": "construct_from_sources", - "sources": [ + target_index = pd.date_range( + "2020-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ { - "country": "ALB", - "start": "2022-01-01", - "end": "2022-01-01 01:00", + "country": "GRC", + "start": "2019-01-01T00:00:00+00:00", + "end": "2019-01-01T03:00:00+00:00", + "weight": 1, } ], - } - - with pytest.raises( - NotImplementedError, - match="construct_from_sources", - ): - construct_from_sources( - load, - rule_name="replace_albania", - rule=rule, - ) \ No newline at end of file + ) + + expected = pd.Series( + [ + 10.0, + 20.0, + 30.0, + ], + index=target_index, + dtype=float, + ) + + pd.testing.assert_series_equal( + result, + expected, + ) + +def test_construct_from_sources_uses_weighted_mean() -> None: + auxiliary_index = pd.date_range( + "2019-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + auxiliary = pd.DataFrame( + { + "GRC": [ + 10.0, + 20.0, + 30.0, + ], + "GBR": [ + 30.0, + 40.0, + 50.0, + ], + }, + index=auxiliary_index, + ) + + target_index = pd.date_range( + "2020-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ + { + "country": "GRC", + "start": "2019-01-01T00:00:00+00:00", + "end": "2019-01-01T03:00:00+00:00", + "weight": 1, + }, + { + "country": "GBR", + "start": "2019-01-01T00:00:00+00:00", + "end": "2019-01-01T03:00:00+00:00", + "weight": 3, + }, + ], + ) + + expected = pd.Series( + [ + 25.0, + 35.0, + 45.0, + ], + index=target_index, + dtype=float, + ) + + pd.testing.assert_series_equal( + result, + expected, + ) diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/construct_from_sources.py index 81d81cf..dc2ac66 100644 --- a/workflow/scripts/cleaning/advanced/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/construct_from_sources.py @@ -1,21 +1,68 @@ """Construct an auxiliary demand profile from configured source periods.""" -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from typing import Any import pandas as pd - -METHOD_NAME = "construct_from_sources" +from _time import as_utc_timestamp def construct_from_sources( - load: pd.DataFrame, + auxiliary: pd.DataFrame, *, - rule_name: str, - rule: Mapping[str, Any], -) -> pd.DataFrame: - """Construct a target profile from configured country-period sources.""" - raise NotImplementedError( - "Advanced-fill method 'construct_from_sources' " - "has not yet been implemented." - ) \ No newline at end of file + target_index: pd.DatetimeIndex, + sources: Sequence[Mapping[str, Any]], +) -> pd.Series: + """Construct a target demand profile from weighted auxiliary sources.""" + weighted_sources: list[pd.Series] = [] + weights: list[float] = [] + + for source in sources: + country = source["country"] + start = as_utc_timestamp( + source["start"] + ) + end = as_utc_timestamp( + source["end"] + ) + weight = float( + source.get("weight", 1) + ) + + source_values = auxiliary.loc[ + (auxiliary.index >= start) + & (auxiliary.index < end), + country, + ] + + if len(source_values) != len(target_index): + raise ValueError( + "Auxiliary source period must contain " + "the same number of values as the target " + f"period. Source {country!r} contains " + f"{len(source_values)} values; target " + f"contains {len(target_index)}." + ) + + remapped = pd.Series( + source_values.to_numpy(), + index=target_index, + dtype=float, + ) + + weighted_sources.append( + remapped * weight + ) + weights.append(weight) + + if not weighted_sources: + raise ValueError( + "At least one auxiliary source is required." + ) + + weighted_sum = sum( + weighted_sources[1:], + weighted_sources[0].copy(), + ) + + return weighted_sum / sum(weights) \ No newline at end of file From c6c601ee139a84307c2cfcfecbb9c77cad85e1bc Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 11:00:13 +0200 Subject: [PATCH 035/241] Construct auxiliary profile for filling gaps (advanced) --- workflow/rules/advanced.smk | 84 +++++++++++++++++++ .../scripts/construct_auxiliary_profile.py | 37 ++++++++ 2 files changed, 121 insertions(+) create mode 100644 workflow/scripts/construct_auxiliary_profile.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index db750b7..2f32da7 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,4 +1,5 @@ import json +import pandas as pd def auxiliary_acquisition_plan(_wildcards): """Return the acquisition plan after the checkpoint completes.""" @@ -282,6 +283,67 @@ def auxiliary_combined_outputs(_wildcards): ] +def auxiliary_rule_cleaned_files(wildcards): + plan_path = checkpoints.plan_auxiliary_data.get().output.plan + + with open(plan_path, encoding="utf-8") as file: + plan = json.load(file) + + override = ( + config["gap_filling"] + ["advanced"] + ["overrides"] + [wildcards.rule_name] + ) + + group_ids = set() + + for source in override["sources"]: + country = source["country"] + start = pd.Timestamp(source["start"]) + + if start.tzinfo is None: + start = start.tz_localize("UTC") + else: + start = start.tz_convert("UTC") + + end = pd.Timestamp(source["end"]) + + if end.tzinfo is None: + end = end.tz_localize("UTC") + else: + end = end.tz_convert("UTC") + + matching_groups = { + batch["group_id"] + for batch in plan["batches"] + if ( + country in batch["countries"] + and pd.Timestamp(batch["start"]) <= start + and pd.Timestamp(batch["end"]) >= end + ) + } + + if len(matching_groups) != 1: + raise ValueError( + "Expected exactly one cleaned auxiliary group " + f"covering {country!r} from {start} to {end}, " + f"found {sorted(matching_groups)}." + ) + + group_ids.update(matching_groups) + + return [ + ( + "/automatic/" + "auxiliary/cleaned/" + f"{group_id}.parquet" + ) + for group_id in sorted(group_ids) + ] + + + checkpoint plan_auxiliary_data: input: @@ -513,3 +575,25 @@ rule clean_auxiliary_data: script: "../scripts/clean_auxiliary_data.py" +rule construct_auxiliary_profile: + input: + sources=auxiliary_rule_cleaned_files, + output: + profile=( + "/automatic/" + "auxiliary/constructed/" + "{rule_name}.parquet" + ), + params: + override=lambda wildcards: ( + config["gap_filling"] + ["advanced"] + ["overrides"] + [wildcards.rule_name] + ), + conda: + "../envs/module.yaml" + message: + "Construct auxiliary demand profile for {wildcards.rule_name}." + script: + "../scripts/construct_auxiliary_profile.py" \ No newline at end of file diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py new file mode 100644 index 0000000..3899a49 --- /dev/null +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -0,0 +1,37 @@ +import pandas as pd +from _time import build_hourly_index +from cleaning.advanced.construct_from_sources import construct_from_sources + +loads = [ + pd.read_parquet(path) + for path in snakemake.input.sources +] + +if not loads: + raise ValueError( + "No cleaned auxiliary data were supplied." + ) + +auxiliary = loads[0].copy() + +for load in loads[1:]: + auxiliary = auxiliary.combine_first(load) + +override = snakemake.params.override + +target_index = build_hourly_index( + start=override["start"], + end=override["end"], +) + +profile = construct_from_sources( + auxiliary, + target_index=target_index, + sources=override["sources"], +) + +profile.to_frame( + name=override["country"] +).to_parquet( + snakemake.output.profile +) From 41ea1b2f88c4df38941f3864cbaf39d5841732fc Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 11:12:24 +0200 Subject: [PATCH 036/241] Apply constructed auxiliary profiles to target demand --- tests/unit/test_advanced_apply.py | 130 +++++++++++++++++- workflow/scripts/cleaning/advanced/apply.py | 100 ++++++++++++-- .../advanced/construct_from_sources.py | 2 + 3 files changed, 214 insertions(+), 18 deletions(-) diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 1537131..bd49d89 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -29,6 +29,27 @@ def _load() -> pd.DataFrame: ) +def _cleaning_method() -> pd.DataFrame: + index = pd.date_range( + "2021-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + return pd.DataFrame( + { + "ALB": [ + "observed_entsoe_api", + "missing", + "observed_entsoe_api", + ], + }, + index=index, + ) + + + def test_external_profile_execution_is_not_implemented() -> None: rule = { "method": "external_profile", @@ -40,27 +61,113 @@ def test_external_profile_execution_is_not_implemented() -> None: ): apply_auxiliary_fill_rule( _load(), + _cleaning_method(), rule_name="external_albania", rule=rule, ) -def test_construct_from_sources_dispatches_to_placeholder() -> None: +def test_construct_from_sources_requires_profile() -> None: rule = { "method": "construct_from_sources", } with pytest.raises( - NotImplementedError, - match="construct_from_sources", + ValueError, + match="requires a constructed auxiliary profile", ): apply_auxiliary_fill_rule( _load(), + _cleaning_method(), rule_name="construct_albania", rule=rule, ) +def test_construct_from_sources_fills_gaps() -> None: + load = _load() + cleaning_method = _cleaning_method() + + profile = pd.Series( + [ + 10.0, + 20.0, + 30.0, + ], + index=load.index, + ) + + rule = { + "method": "construct_from_sources", + "country": "ALB", + "start": "2021-01-01T00:00:00+00:00", + "end": "2021-01-01T03:00:00+00:00", + "scope": "fill_gaps_within_period", + } + + filled, methods = apply_auxiliary_fill_rule( + load, + cleaning_method, + rule_name="construct_albania", + rule=rule, + profile=profile, + ) + + assert filled["ALB"].tolist() == [ + 1.0, + 20.0, + 3.0, + ] + + assert methods["ALB"].tolist() == [ + "observed_entsoe_api", + "construct_albania", + "observed_entsoe_api", + ] + + +def test_construct_from_sources_overwrites_entire_period() -> None: + load = _load() + cleaning_method = _cleaning_method() + + profile = pd.Series( + [ + 10.0, + 20.0, + 30.0, + ], + index=load.index, + ) + + rule = { + "method": "construct_from_sources", + "country": "ALB", + "start": "2021-01-01T00:00:00+00:00", + "end": "2021-01-01T03:00:00+00:00", + "scope": "overwrite_entire_period", + } + + filled, methods = apply_auxiliary_fill_rule( + load, + cleaning_method, + rule_name="construct_albania", + rule=rule, + profile=profile, + ) + + assert filled["ALB"].tolist() == [ + 10.0, + 20.0, + 30.0, + ] + + assert methods["ALB"].tolist() == [ + "construct_albania", + "construct_albania", + "construct_albania", + ] + + def test_manual_review_cannot_be_applied_automatically() -> None: rule = { "method": "manual_review", @@ -72,16 +179,19 @@ def test_manual_review_cannot_be_applied_automatically() -> None: ): apply_auxiliary_fill_rule( _load(), + _cleaning_method(), rule_name="review_albania", rule=rule, ) -def test_leave_missing_returns_unchanged_copy() -> None: +def test_leave_missing_returns_unchanged_copies() -> None: load = _load() + cleaning_method = _cleaning_method() - result = apply_auxiliary_fill_rule( + result_load, result_method = apply_auxiliary_fill_rule( load, + cleaning_method, rule_name="leave_albania_missing", rule={ "method": "leave_missing", @@ -89,11 +199,16 @@ def test_leave_missing_returns_unchanged_copy() -> None: ) pd.testing.assert_frame_equal( - result, + result_load, load, ) + pd.testing.assert_frame_equal( + result_method, + cleaning_method, + ) - assert result is not load + assert result_load is not load + assert result_method is not cleaning_method def test_rejects_unsupported_method() -> None: @@ -103,6 +218,7 @@ def test_rejects_unsupported_method() -> None: ): apply_auxiliary_fill_rule( _load(), + _cleaning_method(), rule_name="invalid_rule", rule={ "method": "unknown", diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 25b8eaf..2212797 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -4,16 +4,12 @@ from typing import Any import pandas as pd +from _time import as_utc_timestamp from cleaning.advanced.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) -from cleaning.advanced.construct_from_sources import ( - construct_from_sources, -) -from cleaning.advanced.external_profile import ( - METHOD_NAME as EXTERNAL_PROFILE, -) +from cleaning.advanced.external_profile import METHOD_NAME as EXTERNAL_PROFILE MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" @@ -21,18 +17,31 @@ def apply_auxiliary_fill_rule( load: pd.DataFrame, + cleaning_method: pd.DataFrame, *, rule_name: str, rule: Mapping[str, Any], -) -> pd.DataFrame: + profile: pd.Series | None = None, +) -> tuple[pd.DataFrame, pd.DataFrame]: """Apply one validated advanced-fill rule.""" method = rule["method"] if method == CONSTRUCT_FROM_SOURCES: - return construct_from_sources( + if profile is None: + raise ValueError( + f"Advanced-fill rule {rule_name!r} requires " + "a constructed auxiliary profile." + ) + + return apply_constructed_profile( load, + cleaning_method, + profile, + country=rule["country"], + start=as_utc_timestamp(rule["start"]), + end=as_utc_timestamp(rule["end"]), + scope=rule["scope"], rule_name=rule_name, - rule=rule, ) if method == EXTERNAL_PROFILE: @@ -48,8 +57,77 @@ def apply_auxiliary_fill_rule( ) if method == LEAVE_MISSING: - return load.copy() + return load.copy(), cleaning_method.copy() raise ValueError( f"Unsupported advanced-fill method {method!r}." - ) \ No newline at end of file + ) + + + +def apply_constructed_profile( + load: pd.DataFrame, + cleaning_method: pd.DataFrame, + profile: pd.Series, + *, + country: str, + start: pd.Timestamp, + end: pd.Timestamp, + scope: str, + rule_name: str, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply a constructed auxiliary profile to the target demand.""" + filled = load.copy() + methods = cleaning_method.copy() + + target_index = filled.index[ + (filled.index >= start) + & (filled.index < end) + ] + + if not profile.index.equals(target_index): + raise ValueError( + "Constructed profile index must exactly match " + "the target period." + ) + + if country not in filled.columns: + raise ValueError( + f"Target country {country!r} is not present in load data." + ) + + if scope == "fill_gaps_within_period": + replace_mask = filled.loc[ + target_index, + country, + ].isna() + + elif scope == "overwrite_entire_period": + replace_mask = pd.Series( + True, + index=target_index, + ) + + else: + raise ValueError( + f"Unsupported advanced fill scope: {scope!r}" + ) + + replacement_index = target_index[ + replace_mask.to_numpy() + ] + + filled.loc[ + replacement_index, + country, + ] = profile.loc[ + replacement_index + ] + + methods.loc[ + replacement_index, + country, + ] = rule_name + + return filled, methods + diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/construct_from_sources.py index dc2ac66..0182a5a 100644 --- a/workflow/scripts/cleaning/advanced/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/construct_from_sources.py @@ -6,6 +6,8 @@ import pandas as pd from _time import as_utc_timestamp +METHOD_NAME = "construct_from_sources" + def construct_from_sources( auxiliary: pd.DataFrame, From 6f3419abcc2e140ca02c4ec3a518a1f2e200c942 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 11:59:58 +0200 Subject: [PATCH 037/241] Support sequential advanced overrides and leap-year alignment --- tests/integration/test_config.yaml | 16 ++- tests/unit/test_advanced_apply.py | 89 ++++++++++++++- .../test_advanced_construct_from_sources.py | 101 ++++++++++++++++++ workflow/rules/advanced.smk | 53 ++++++++- workflow/scripts/apply_advanced_overrides.py | 32 ++++++ workflow/scripts/cleaning/advanced/apply.py | 23 ++++ .../advanced/construct_from_sources.py | 85 ++++++++++++++- workflow/scripts/cleaning/advanced/plan.py | 8 ++ 8 files changed, 398 insertions(+), 9 deletions(-) create mode 100644 workflow/scripts/apply_advanced_overrides.py diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 0d1fa17..018a3b6 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -47,14 +47,26 @@ integration_test: # Explicit country-period instructions for unresolved cases. overrides: + test_construct_from_greece: country: ALB - start: "2020-01-01" - end: "2020-02-01" + start: "2021-01-01" + end: "2021-02-01" scope: fill_gaps_within_period method: construct_from_sources sources: - country: GBR start: "2020-01-01" end: "2020-02-01" + weight: 1 + test_construct_second: + country: ALB + start: "2021-02-01" + end: "2021-03-01" + scope: fill_gaps_within_period + method: construct_from_sources + sources: + - country: GBR + start: "2020-02-01" + end: "2020-03-01" weight: 1 \ No newline at end of file diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index bd49d89..8a7b58c 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -2,9 +2,9 @@ import pandas as pd import pytest - from cleaning.advanced.apply import ( apply_auxiliary_fill_rule, + apply_auxiliary_fill_rules, ) @@ -224,3 +224,90 @@ def test_rejects_unsupported_method() -> None: "method": "unknown", }, ) + + +def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: + load = _load() + cleaning_method = _cleaning_method() + + first_profile = pd.Series( + [ + 10.0, + 20.0, + 30.0, + ], + index=load.index, + ) + + second_profile = pd.Series( + [ + 100.0, + 200.0, + 300.0, + ], + index=load.index, + ) + + overrides = { + "fill_gaps": { + "method": "construct_from_sources", + "country": "ALB", + "start": "2021-01-01T00:00:00+00:00", + "end": "2021-01-01T03:00:00+00:00", + "scope": "fill_gaps_within_period", + }, + "overwrite": { + "method": "construct_from_sources", + "country": "ALB", + "start": "2021-01-01T00:00:00+00:00", + "end": "2021-01-01T03:00:00+00:00", + "scope": "overwrite_entire_period", + }, + } + + profiles = { + "fill_gaps": first_profile, + "overwrite": second_profile, + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + profiles=profiles, + ) + + assert filled["ALB"].tolist() == [ + 100.0, + 200.0, + 300.0, + ] + + assert methods["ALB"].tolist() == [ + "overwrite", + "overwrite", + "overwrite", + ] + +def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: + load = _load() + cleaning_method = _cleaning_method() + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides={}, + profiles={}, + ) + + pd.testing.assert_frame_equal( + filled, + load, + ) + pd.testing.assert_frame_equal( + methods, + cleaning_method, + ) + + assert filled is not load + assert methods is not cleaning_method diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index eeb82cd..7329aa5 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -122,3 +122,104 @@ def test_construct_from_sources_uses_weighted_mean() -> None: result, expected, ) + + +def test_construct_from_sources_drops_february_29() -> None: + source_index = pd.date_range( + "2020-02-01", + "2020-03-01", + freq="h", + inclusive="left", + tz="UTC", + ) + + values = pd.Series( + 1.0, + index=source_index, + ) + + values.loc[ + (values.index.month == 2) + & (values.index.day == 29) + ] = 999.0 + + auxiliary = values.to_frame("GBR") + + target_index = pd.date_range( + "2021-02-01", + "2021-03-01", + freq="h", + inclusive="left", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ + { + "country": "GBR", + "start": "2020-02-01", + "end": "2020-03-01", + } + ], + ) + + assert len(result) == 672 + assert not (result == 999.0).any() + + +def test_construct_from_sources_interpolates_february_29() -> None: + auxiliary_index = pd.date_range( + "2021-02-01", + "2021-03-02", + freq="h", + inclusive="left", + tz="UTC", + ) + + values = pd.Series( + 10.0, + index=auxiliary_index, + ) + + values.loc[ + (values.index.month == 2) + & (values.index.day == 28) + ] = 20.0 + + values.loc[ + (values.index.month == 3) + & (values.index.day == 1) + ] = 40.0 + + auxiliary = values.to_frame("GBR") + + target_index = pd.date_range( + "2020-02-01", + "2020-03-01", + freq="h", + inclusive="left", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ + { + "country": "GBR", + "start": "2021-02-01", + "end": "2021-03-01", + } + ], + ) + + target_feb_29 = ( + (result.index.month == 2) + & (result.index.day == 29) + ) + + assert len(result) == 696 + assert (result.loc[target_feb_29] == 30.0).all() + diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 2f32da7..6981bc1 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -343,6 +343,23 @@ def auxiliary_rule_cleaned_files(wildcards): ] +def advanced_constructed_profiles(_wildcards): + """Return constructed profiles required by advanced overrides.""" + overrides = ( + config["gap_filling"] + ["advanced"] + ["overrides"] + ) + + return [ + ( + "/automatic/" + "auxiliary/constructed/" + f"{rule_name}.parquet" + ) + for rule_name, override in overrides.items() + if override["method"] == "construct_from_sources" + ] checkpoint plan_auxiliary_data: @@ -596,4 +613,38 @@ rule construct_auxiliary_profile: message: "Construct auxiliary demand profile for {wildcards.rule_name}." script: - "../scripts/construct_auxiliary_profile.py" \ No newline at end of file + "../scripts/construct_auxiliary_profile.py" + + +rule apply_advanced_overrides: + input: + demand=( + "/automatic/" + "load_basic_cleaned.parquet" + ), + cleaning_method=( + "/automatic/" + "load_cleaning_method.parquet" + ), + profiles=advanced_constructed_profiles, + output: + demand=( + "/automatic/" + "load_advanced_cleaned.parquet" + ), + cleaning_method=( + "/automatic/" + "load_advanced_cleaning_method.parquet" + ), + params: + overrides=( + config["gap_filling"] + ["advanced"] + ["overrides"] + ), + conda: + "../envs/module.yaml" + message: + "Apply advanced electricity-demand overrides." + script: + "../scripts/apply_advanced_overrides.py" \ No newline at end of file diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py new file mode 100644 index 0000000..6bc25b5 --- /dev/null +++ b/workflow/scripts/apply_advanced_overrides.py @@ -0,0 +1,32 @@ +from pathlib import Path + +import pandas as pd +from cleaning.advanced.apply import apply_auxiliary_fill_rules + +load = pd.read_parquet( + snakemake.input.demand +) + +cleaning_method = pd.read_parquet( + snakemake.input.cleaning_method +) + +profiles = { + Path(path).stem: pd.read_parquet(path).iloc[:, 0] + for path in snakemake.input.profiles +} + +filled, cleaning_method = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=snakemake.params.overrides, + profiles=profiles, +) + +filled.to_parquet( + snakemake.output.demand +) + +cleaning_method.to_parquet( + snakemake.output.cleaning_method +) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 2212797..9c4be2d 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -131,3 +131,26 @@ def apply_constructed_profile( return filled, methods +def apply_auxiliary_fill_rules( + load: pd.DataFrame, + cleaning_method: pd.DataFrame, + *, + overrides: Mapping[str, Mapping[str, Any]], + profiles: Mapping[str, pd.Series], +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply validated advanced-fill rules in configuration order.""" + filled = load.copy() + methods = cleaning_method.copy() + + for rule_name, rule in overrides.items(): + profile = profiles.get(rule_name) + + filled, methods = apply_auxiliary_fill_rule( + filled, + methods, + rule_name=rule_name, + rule=rule, + profile=profile, + ) + + return filled, methods diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/construct_from_sources.py index 0182a5a..026bbd6 100644 --- a/workflow/scripts/cleaning/advanced/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/construct_from_sources.py @@ -8,6 +8,80 @@ METHOD_NAME = "construct_from_sources" +def _align_leap_day( + auxiliary: pd.Series, + *, + start: pd.Timestamp, + end: pd.Timestamp, + target_index: pd.DatetimeIndex, +) -> pd.Series: + """Align source values with target calendar around February 29.""" + source_values = auxiliary.loc[ + (auxiliary.index >= start) + & (auxiliary.index < end) + ] + + source_has_leap_day = ( + (source_values.index.month == 2) + & (source_values.index.day == 29) + ).any() + + target_has_leap_day = ( + (target_index.month == 2) + & (target_index.day == 29) + ).any() + + if source_has_leap_day and not target_has_leap_day: + leap_day = ( + (source_values.index.month == 2) + & (source_values.index.day == 29) + ) + + return source_values.loc[~leap_day] + + if target_has_leap_day and not source_has_leap_day: + feb_28 = auxiliary.loc[ + (auxiliary.index.year == start.year) + & (auxiliary.index.month == 2) + & (auxiliary.index.day == 28) + ] + + march_1 = auxiliary.loc[ + (auxiliary.index.year == start.year) + & (auxiliary.index.month == 3) + & (auxiliary.index.day == 1) + ] + + if len(feb_28) != 24 or len(march_1) != 24: + raise ValueError( + "Cannot construct February 29 because complete " + "February 28 and March 1 source data are required." + ) + + leap_values = ( + feb_28.to_numpy(dtype=float) + + march_1.to_numpy(dtype=float) + ) / 2 + + insertion_point = ( + source_values.index.month < 3 + ).sum() + + values = source_values.to_numpy(dtype=float) + + aligned = pd.Series( + data=[ + *values[:insertion_point], + *leap_values, + *values[insertion_point:], + ], + dtype=float, + ) + + return aligned + + return source_values + def construct_from_sources( auxiliary: pd.DataFrame, @@ -31,11 +105,12 @@ def construct_from_sources( source.get("weight", 1) ) - source_values = auxiliary.loc[ - (auxiliary.index >= start) - & (auxiliary.index < end), - country, - ] + source_values = _align_leap_day( + auxiliary[country], + start=start, + end=end, + target_index=target_index, + ) if len(source_values) != len(target_index): raise ValueError( diff --git a/workflow/scripts/cleaning/advanced/plan.py b/workflow/scripts/cleaning/advanced/plan.py index c633cd1..f4c6f1c 100644 --- a/workflow/scripts/cleaning/advanced/plan.py +++ b/workflow/scripts/cleaning/advanced/plan.py @@ -347,6 +347,14 @@ def build_auxiliary_fill_plan( records: list[dict[str, Any]] = [] + # TODO: Support reusable libraries of advanced overrides. + # Before planning auxiliary acquisition, filter configured overrides + # against the current model countries and temporal scope. Overrides + # that do not intersect the current target scope should be ignored, + # while applicable overrides should continue to be validated strictly. + # This allows users to have a general overrides file for which they + # need not retune the dates for each new run/horizon. + for rule_name, rule in rules.items(): validate_auxiliary_fill_rule( rule_name, From c1395141dc62f2357344ac2f6d4d79f94064ea0d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:03:46 +0200 Subject: [PATCH 038/241] Advanced workflow is fully wired into the load_cleaned.parquet now --- workflow/rules/advanced.smk | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 6981bc1..23eee06 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -362,6 +362,17 @@ def advanced_constructed_profiles(_wildcards): ] +def final_clean_demand_input(_wildcards): + """Return the cleaned demand appropriate for the configured mode.""" + if config["gap_filling"]["mode"] == "advanced": + return ( + "/automatic/" + "load_advanced_cleaned.parquet" + ) + + return rules.clean_demand.output.demand + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -383,7 +394,7 @@ checkpoint plan_auxiliary_data: rule finalise_clean_demand: input: - demand=rules.clean_demand.output.demand, + demand=final_clean_demand_input, output: demand=( "/automatic/" From c6516f392c47892c2320ec95ecc55f6e9bf02897 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:15:24 +0200 Subject: [PATCH 039/241] Advanced filling now shows in the diagnostic plot --- workflow/rules/advanced.smk | 25 ++++++++- workflow/rules/clean.smk | 4 +- .../cleaning/plot_cleaning_timeline.py | 17 ++++++- workflow/scripts/finalise_clean_demand.py | 51 +++++++++++++++---- 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 23eee06..d39bd80 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -373,6 +373,17 @@ def final_clean_demand_input(_wildcards): return rules.clean_demand.output.demand +def final_cleaning_method_input(_wildcards): + """Return cleaning provenance appropriate for the configured mode.""" + if config["gap_filling"]["mode"] == "advanced": + return ( + "/automatic/" + "load_advanced_cleaning_method.parquet" + ) + + return rules.clean_demand.output.cleaning_method + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -395,15 +406,27 @@ checkpoint plan_auxiliary_data: rule finalise_clean_demand: input: demand=final_clean_demand_input, + cleaning_method=final_cleaning_method_input, output: demand=( "/automatic/" "load_cleaned.parquet" ), + cleaning_method=( + "/automatic/" + "load_final_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/" + "load_final_cleaning_method_rank.parquet" + ), + params: + source_names=config["load_sources"], + gap_filling=config["gap_filling"], conda: "../envs/module.yaml" message: - "Finalise cleaned electricity demand." + "Finalise cleaned electricity demand and provenance." script: "../scripts/finalise_clean_demand.py" diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index d1557d6..972c132 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -129,11 +129,11 @@ rule plot_cleaning_timeline: ), cleaning_method=( "/automatic/" - "load_cleaning_method.parquet" + "load_final_cleaning_method.parquet" ), cleaning_method_rank=( "/automatic/" - "load_cleaning_method_rank.parquet" + "load_final_cleaning_method_rank.parquet" ), output: plot=( diff --git a/workflow/scripts/cleaning/plot_cleaning_timeline.py b/workflow/scripts/cleaning/plot_cleaning_timeline.py index 808d781..ac9953f 100644 --- a/workflow/scripts/cleaning/plot_cleaning_timeline.py +++ b/workflow/scripts/cleaning/plot_cleaning_timeline.py @@ -670,7 +670,22 @@ def _build_cleaning_method_metadata( ) rank += 1 - for rule in gap_filling_config["rules"]: + rules = list( + gap_filling_config["basic"]["rules"] + ) + + if gap_filling_config["mode"] == "advanced": + rules.extend( + { + "name": rule_name, + **override, + } + for rule_name, override in ( + gap_filling_config["advanced"]["overrides"].items() + ) + ) + + for rule in rules: rule_name = rule["name"] rows.append( diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index a83976b..66416c3 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -1,15 +1,48 @@ import shutil -from pathlib import Path -source = Path(snakemake.input.demand) -target = Path(snakemake.output.demand) +import pandas as pd +from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -target.parent.mkdir( - parents=True, - exist_ok=True, +shutil.copyfile( + snakemake.input.demand, + snakemake.output.demand, ) -shutil.copyfile( - source, - target, +cleaning_method = pd.read_parquet( + snakemake.input.cleaning_method ) + +gap_filling = snakemake.params.gap_filling + +rules = list( + gap_filling["basic"]["rules"] +) + +if gap_filling["mode"] == "advanced": + rules.extend( + { + "name": rule_name, + **override, + } + for rule_name, override in ( + gap_filling["advanced"]["overrides"].items() + ) + ) + +ranks = build_cleaning_method_ranks( + source_priority=snakemake.params.source_names, + rules=rules, +) + +cleaning_method_rank = derive_cleaning_method_rank( + cleaning_method=cleaning_method, + ranks=ranks, +) + +cleaning_method.to_parquet( + snakemake.output.cleaning_method +) + +cleaning_method_rank.to_parquet( + snakemake.output.cleaning_method_rank +) \ No newline at end of file From 4c62cab9a08d4915575ba780c1232b5db4325f76 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:41:23 +0200 Subject: [PATCH 040/241] Refactored the basic subdirectory --- workflow/scripts/cleaning/basic/{pipeline.py => apply.py} | 0 workflow/scripts/cleaning/basic/{ => rules}/average_periods.py | 0 workflow/scripts/cleaning/basic/{ => rules}/copy_period.py | 0 .../scripts/cleaning/basic/{ => rules}/linear_interpolation.py | 0 workflow/scripts/{_plots.py => common/plots.py} | 0 workflow/scripts/{_schemas.py => common/schemas.py} | 0 workflow/scripts/{_time.py => common/time.py} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename workflow/scripts/cleaning/basic/{pipeline.py => apply.py} (100%) rename workflow/scripts/cleaning/basic/{ => rules}/average_periods.py (100%) rename workflow/scripts/cleaning/basic/{ => rules}/copy_period.py (100%) rename workflow/scripts/cleaning/basic/{ => rules}/linear_interpolation.py (100%) rename workflow/scripts/{_plots.py => common/plots.py} (100%) rename workflow/scripts/{_schemas.py => common/schemas.py} (100%) rename workflow/scripts/{_time.py => common/time.py} (100%) diff --git a/workflow/scripts/cleaning/basic/pipeline.py b/workflow/scripts/cleaning/basic/apply.py similarity index 100% rename from workflow/scripts/cleaning/basic/pipeline.py rename to workflow/scripts/cleaning/basic/apply.py diff --git a/workflow/scripts/cleaning/basic/average_periods.py b/workflow/scripts/cleaning/basic/rules/average_periods.py similarity index 100% rename from workflow/scripts/cleaning/basic/average_periods.py rename to workflow/scripts/cleaning/basic/rules/average_periods.py diff --git a/workflow/scripts/cleaning/basic/copy_period.py b/workflow/scripts/cleaning/basic/rules/copy_period.py similarity index 100% rename from workflow/scripts/cleaning/basic/copy_period.py rename to workflow/scripts/cleaning/basic/rules/copy_period.py diff --git a/workflow/scripts/cleaning/basic/linear_interpolation.py b/workflow/scripts/cleaning/basic/rules/linear_interpolation.py similarity index 100% rename from workflow/scripts/cleaning/basic/linear_interpolation.py rename to workflow/scripts/cleaning/basic/rules/linear_interpolation.py diff --git a/workflow/scripts/_plots.py b/workflow/scripts/common/plots.py similarity index 100% rename from workflow/scripts/_plots.py rename to workflow/scripts/common/plots.py diff --git a/workflow/scripts/_schemas.py b/workflow/scripts/common/schemas.py similarity index 100% rename from workflow/scripts/_schemas.py rename to workflow/scripts/common/schemas.py diff --git a/workflow/scripts/_time.py b/workflow/scripts/common/time.py similarity index 100% rename from workflow/scripts/_time.py rename to workflow/scripts/common/time.py From 2310d6ddc881ac5f6f3f591a03fe182bf4720a1f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:41:34 +0200 Subject: [PATCH 041/241] refactored the basic subdirectory --- workflow/scripts/clean_auxiliary_data.py | 2 +- workflow/scripts/cleaning/advanced/apply.py | 2 +- .../advanced/construct_from_sources.py | 2 +- .../scripts/cleaning/advanced/requirements.py | 2 +- workflow/scripts/cleaning/basic/apply.py | 90 ++----------------- .../scripts/cleaning/basic/rules/__init__.py | 0 workflow/scripts/cleaning/pipeline.py | 2 +- workflow/scripts/common/__init__.py | 0 .../scripts/construct_auxiliary_profile.py | 2 +- .../scripts/demand_electricity_polygon.py | 4 +- workflow/scripts/demand_electricity_raster.py | 4 +- workflow/scripts/download_load_entsoe_api.py | 2 +- workflow/scripts/prepare_load_neso.py | 2 +- workflow/scripts/prepare_load_opsd.py | 4 +- 14 files changed, 22 insertions(+), 96 deletions(-) create mode 100644 workflow/scripts/cleaning/basic/rules/__init__.py create mode 100644 workflow/scripts/common/__init__.py diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py index a94c6a2..25d93fb 100644 --- a/workflow/scripts/clean_auxiliary_data.py +++ b/workflow/scripts/clean_auxiliary_data.py @@ -1,6 +1,6 @@ import pandas as pd -from cleaning.basic.pipeline import fill_basic_gaps +from cleaning.basic.apply import fill_basic_gaps load = pd.read_parquet( diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 9c4be2d..75bb49c 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -4,7 +4,7 @@ from typing import Any import pandas as pd -from _time import as_utc_timestamp +from common.time import as_utc_timestamp from cleaning.advanced.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/construct_from_sources.py index 026bbd6..db1bf7b 100644 --- a/workflow/scripts/cleaning/advanced/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/construct_from_sources.py @@ -4,7 +4,7 @@ from typing import Any import pandas as pd -from _time import as_utc_timestamp +from common.time import as_utc_timestamp METHOD_NAME = "construct_from_sources" diff --git a/workflow/scripts/cleaning/advanced/requirements.py b/workflow/scripts/cleaning/advanced/requirements.py index e62b638..babdc8e 100644 --- a/workflow/scripts/cleaning/advanced/requirements.py +++ b/workflow/scripts/cleaning/advanced/requirements.py @@ -6,7 +6,7 @@ from typing import Any import pandas as pd -from _time import as_utc_timestamp +from common.time import as_utc_timestamp from cleaning.advanced.construct_from_sources import METHOD_NAME diff --git a/workflow/scripts/cleaning/basic/apply.py b/workflow/scripts/cleaning/basic/apply.py index 95302a6..3aad56e 100644 --- a/workflow/scripts/cleaning/basic/apply.py +++ b/workflow/scripts/cleaning/basic/apply.py @@ -8,12 +8,14 @@ import pandas as pd -from cleaning.basic.average_periods import METHOD_NAME as AVERAGE_PERIODS -from cleaning.basic.average_periods import apply_average_periods -from cleaning.basic.copy_period import METHOD_NAME as COPY_PERIOD -from cleaning.basic.copy_period import apply_copy_period -from cleaning.basic.linear_interpolation import METHOD_NAME as LINEAR_INTERPOLATION -from cleaning.basic.linear_interpolation import apply_linear_interpolation +from cleaning.basic.rules.average_periods import METHOD_NAME as AVERAGE_PERIODS +from cleaning.basic.rules.average_periods import apply_average_periods +from cleaning.basic.rules.copy_period import METHOD_NAME as COPY_PERIOD +from cleaning.basic.rules.copy_period import apply_copy_period +from cleaning.basic.rules.linear_interpolation import ( + METHOD_NAME as LINEAR_INTERPOLATION, +) +from cleaning.basic.rules.linear_interpolation import apply_linear_interpolation from cleaning.validation import infer_regular_timestep, validate_load logger = logging.getLogger(__name__) @@ -164,82 +166,6 @@ def calculate_missing_run_durations( return durations -def build_gap_report( - load: pd.DataFrame, - *, - enabled: bool, -) -> pd.DataFrame: - """Describe contiguous unresolved gaps in cleaned load data. - - An empty report with the expected columns is returned when reporting - is disabled or when no unresolved gaps remain. - """ - columns = [ - "country", - "gap_start", - "gap_end", - "gap_hours", - "touches_start_boundary", - "touches_end_boundary", - ] - - if not enabled: - return pd.DataFrame(columns=columns) - - validate_load(load) - - records: list[dict[str, Any]] = [] - - first_timestamp = load.index[0] - last_timestamp = load.index[-1] - - for country in load.columns: - missing = load[country].isna() - - if not missing.any(): - continue - - group_ids = missing.ne( - missing.shift(fill_value=False) - ).cumsum() - - for _, group in missing.groupby(group_ids): - if not bool(group.iloc[0]): - continue - - timestamps = group.index - - records.append( - { - "country": country, - "gap_start": timestamps[0], - "gap_end": timestamps[-1], - "gap_hours": len(timestamps), - "touches_start_boundary": ( - timestamps[0] == first_timestamp - ), - "touches_end_boundary": ( - timestamps[-1] == last_timestamp - ), - } - ) - - report = pd.DataFrame.from_records( - records, - columns=columns, - ) - - if report.empty: - return report - - return report.sort_values( - [ - "country", - "gap_start", - ] - ).reset_index(drop=True) - - def _get_method(rule: Mapping[str, Any]) -> str: try: method = rule["method"] diff --git a/workflow/scripts/cleaning/basic/rules/__init__.py b/workflow/scripts/cleaning/basic/rules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 4b8d40e..9d47532 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -9,7 +9,7 @@ from cleaning.advanced.gap_report import build_gap_report from cleaning.advanced.plan import build_auxiliary_fill_plan -from cleaning.basic.pipeline import fill_basic_gaps +from cleaning.basic.apply import fill_basic_gaps from cleaning.combine_sources import combine_sources from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank diff --git a/workflow/scripts/common/__init__.py b/workflow/scripts/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 3899a49..b0e79f3 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,6 +1,6 @@ import pandas as pd -from _time import build_hourly_index from cleaning.advanced.construct_from_sources import construct_from_sources +from common.time import build_hourly_index loads = [ pd.read_parquet(path) diff --git a/workflow/scripts/demand_electricity_polygon.py b/workflow/scripts/demand_electricity_polygon.py index b02fb8a..8beccdd 100644 --- a/workflow/scripts/demand_electricity_polygon.py +++ b/workflow/scripts/demand_electricity_polygon.py @@ -9,8 +9,8 @@ import matplotlib.pyplot as plt import pandas as pd import rioxarray as rxr -from _plots import map_polygon -from _schemas import Shapes +from common.plots import map_polygon +from common.schemas import Shapes if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/demand_electricity_raster.py b/workflow/scripts/demand_electricity_raster.py index 31664f9..9654f07 100644 --- a/workflow/scripts/demand_electricity_raster.py +++ b/workflow/scripts/demand_electricity_raster.py @@ -10,8 +10,8 @@ import numpy as np import pandas as pd import rioxarray as rxr -from _plots import map_raster, plot_national_profiles -from _schemas import Shapes +from common.plots import map_raster, plot_national_profiles +from common.schemas import Shapes if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index cebc30c..0b561d6 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -7,7 +7,7 @@ import pandas as pd import pycountry import yaml -from _time import as_utc_timestamp, build_hourly_index +from common.time import as_utc_timestamp, build_hourly_index from entsoe import EntsoePandasClient from entsoe.exceptions import NoMatchingDataError diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index b935509..8312ae9 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -6,8 +6,8 @@ from typing import TYPE_CHECKING, Any, Iterable import pandas as pd -from _time import build_hourly_index from cleaning.neso import add_utc_timestamps +from common.time import build_hourly_index if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index b87e9f5..a11c204 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -6,8 +6,8 @@ import pandas as pd import pycountry -from _schemas import LoadENTSOE -from _time import as_utc_timestamp, build_hourly_index +from common.schemas import LoadENTSOE +from common.time import as_utc_timestamp, build_hourly_index if TYPE_CHECKING: snakemake: Any From 883af25c085c60e3e6b02e6ae9d1576c680d3a04 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:44:18 +0200 Subject: [PATCH 042/241] Refactored the advanced folders --- workflow/scripts/cleaning/advanced/apply.py | 6 ++++-- workflow/scripts/cleaning/advanced/methods/__init__.py | 0 .../advanced/{ => methods}/construct_from_sources.py | 0 .../cleaning/advanced/{ => methods}/external_profile.py | 0 workflow/scripts/cleaning/advanced/planning/__init__.py | 0 workflow/scripts/cleaning/advanced/{ => planning}/plan.py | 6 ++++-- .../cleaning/advanced/{ => planning}/requirements.py | 4 +++- .../cleaning/advanced/{ => planning}/source_requests.py | 0 workflow/scripts/cleaning/pipeline.py | 2 +- workflow/scripts/construct_auxiliary_profile.py | 5 ++++- workflow/scripts/plan_auxiliary_data.py | 7 +++++-- 11 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 workflow/scripts/cleaning/advanced/methods/__init__.py rename workflow/scripts/cleaning/advanced/{ => methods}/construct_from_sources.py (100%) rename workflow/scripts/cleaning/advanced/{ => methods}/external_profile.py (100%) create mode 100644 workflow/scripts/cleaning/advanced/planning/__init__.py rename workflow/scripts/cleaning/advanced/{ => planning}/plan.py (98%) rename workflow/scripts/cleaning/advanced/{ => planning}/requirements.py (98%) rename workflow/scripts/cleaning/advanced/{ => planning}/source_requests.py (100%) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 75bb49c..14f53fc 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -6,10 +6,12 @@ import pandas as pd from common.time import as_utc_timestamp -from cleaning.advanced.construct_from_sources import ( +from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) -from cleaning.advanced.external_profile import METHOD_NAME as EXTERNAL_PROFILE +from workflow.scripts.cleaning.advanced.methods.external_profile import ( + METHOD_NAME as EXTERNAL_PROFILE, +) MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" diff --git a/workflow/scripts/cleaning/advanced/methods/__init__.py b/workflow/scripts/cleaning/advanced/methods/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/advanced/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py similarity index 100% rename from workflow/scripts/cleaning/advanced/construct_from_sources.py rename to workflow/scripts/cleaning/advanced/methods/construct_from_sources.py diff --git a/workflow/scripts/cleaning/advanced/external_profile.py b/workflow/scripts/cleaning/advanced/methods/external_profile.py similarity index 100% rename from workflow/scripts/cleaning/advanced/external_profile.py rename to workflow/scripts/cleaning/advanced/methods/external_profile.py diff --git a/workflow/scripts/cleaning/advanced/planning/__init__.py b/workflow/scripts/cleaning/advanced/planning/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/advanced/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py similarity index 98% rename from workflow/scripts/cleaning/advanced/plan.py rename to workflow/scripts/cleaning/advanced/planning/plan.py index f4c6f1c..f69fa35 100644 --- a/workflow/scripts/cleaning/advanced/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -5,10 +5,12 @@ import pandas as pd -from cleaning.advanced.construct_from_sources import ( +from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) -from cleaning.advanced.external_profile import METHOD_NAME as EXTERNAL_PROFILE +from workflow.scripts.cleaning.advanced.methods.external_profile import ( + METHOD_NAME as EXTERNAL_PROFILE, +) FILL_GAPS_WITHIN_PERIOD = "fill_gaps_within_period" OVERWRITE_ENTIRE_PERIOD = "overwrite_entire_period" diff --git a/workflow/scripts/cleaning/advanced/requirements.py b/workflow/scripts/cleaning/advanced/planning/requirements.py similarity index 98% rename from workflow/scripts/cleaning/advanced/requirements.py rename to workflow/scripts/cleaning/advanced/planning/requirements.py index babdc8e..ee975fb 100644 --- a/workflow/scripts/cleaning/advanced/requirements.py +++ b/workflow/scripts/cleaning/advanced/planning/requirements.py @@ -8,7 +8,9 @@ import pandas as pd from common.time import as_utc_timestamp -from cleaning.advanced.construct_from_sources import METHOD_NAME +from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( + METHOD_NAME, +) REQUIREMENT_COLUMNS = [ "country", diff --git a/workflow/scripts/cleaning/advanced/source_requests.py b/workflow/scripts/cleaning/advanced/planning/source_requests.py similarity index 100% rename from workflow/scripts/cleaning/advanced/source_requests.py rename to workflow/scripts/cleaning/advanced/planning/source_requests.py diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 9d47532..6b9f59f 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -8,10 +8,10 @@ import pandas as pd from cleaning.advanced.gap_report import build_gap_report -from cleaning.advanced.plan import build_auxiliary_fill_plan from cleaning.basic.apply import fill_basic_gaps from cleaning.combine_sources import combine_sources from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank +from workflow.scripts.cleaning.advanced.planning.plan import build_auxiliary_fill_plan logger = logging.getLogger(__name__) diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index b0e79f3..4fa9129 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,7 +1,10 @@ import pandas as pd -from cleaning.advanced.construct_from_sources import construct_from_sources from common.time import build_hourly_index +from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( + construct_from_sources, +) + loads = [ pd.read_parquet(path) for path in snakemake.input.sources diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 2988141..2da01fe 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -6,12 +6,15 @@ from typing import TYPE_CHECKING, Any import pandas as pd -from cleaning.advanced.requirements import build_auxiliary_acquisition_requirements -from cleaning.advanced.source_requests import ( +from workflow.scripts.cleaning.advanced.planning.source_requests import ( build_auxiliary_source_batches, build_auxiliary_source_requests, ) +from workflow.scripts.cleaning.advanced.planning.requirements import ( + build_auxiliary_acquisition_requirements, +) + if TYPE_CHECKING: snakemake: Any From e936aed8ce6b684e2ce6f7be39f12d0c260a4f57 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 12:53:00 +0200 Subject: [PATCH 043/241] Reorganised folders and fixed bad imports --- tests/unit/test_advanced_construct_from_sources.py | 2 +- tests/unit/test_advanced_plan.py | 2 +- tests/unit/test_advanced_requirements.py | 2 +- tests/unit/test_advanced_source_requests.py | 2 +- tests/unit/test_plan_auxiliary_data.py | 2 +- workflow/scripts/cleaning/advanced/apply.py | 6 ++---- workflow/scripts/cleaning/advanced/planning/plan.py | 6 ++---- .../scripts/cleaning/advanced/planning/requirements.py | 4 +--- workflow/scripts/cleaning/pipeline.py | 2 +- workflow/scripts/cleaning/sources/__init__.py | 0 workflow/scripts/cleaning/{ => sources}/neso.py | 0 workflow/scripts/construct_auxiliary_profile.py | 5 +---- workflow/scripts/plan_auxiliary_data.py | 9 ++++----- workflow/scripts/prepare_load_neso.py | 2 +- 14 files changed, 17 insertions(+), 27 deletions(-) create mode 100644 workflow/scripts/cleaning/sources/__init__.py rename workflow/scripts/cleaning/{ => sources}/neso.py (100%) diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index 7329aa5..5664498 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -1,7 +1,7 @@ """Tests for source-based advanced profile construction.""" import pandas as pd -from cleaning.advanced.construct_from_sources import construct_from_sources +from cleaning.advanced.methods.construct_from_sources import construct_from_sources def test_construct_from_single_source() -> None: diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index ae9450f..606b68a 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -1,7 +1,7 @@ """Tests for advanced auxiliary-fill planning.""" import pandas as pd import pytest -from cleaning.advanced.plan import ( +from cleaning.advanced.planning.plan import ( build_auxiliary_fill_plan, validate_auxiliary_fill_rule, ) diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index da25531..b874cb1 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -1,7 +1,7 @@ """Tests for compiling auxiliary-data requirements.""" import pandas as pd -from cleaning.advanced.requirements import ( +from cleaning.advanced.planning.requirements import ( REQUIREMENT_COLUMNS, build_auxiliary_acquisition_requirements, compile_auxiliary_requirements, diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index f2b5322..3521d46 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -2,7 +2,7 @@ import pandas as pd import pytest -from cleaning.advanced.source_requests import ( +from cleaning.advanced.planning.source_requests import ( SOURCE_REQUEST_COLUMNS, _build_batch_id, _build_group_id, diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 3a05672..e2a387d 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -1,7 +1,7 @@ """Tests for auxiliary acquisition planning.""" import pandas as pd -from cleaning.advanced.source_requests import ( +from cleaning.advanced.planning.source_requests import ( _build_batch_id, _build_group_id, ) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 14f53fc..4810599 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -6,12 +6,10 @@ import pandas as pd from common.time import as_utc_timestamp -from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( +from cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) -from workflow.scripts.cleaning.advanced.methods.external_profile import ( - METHOD_NAME as EXTERNAL_PROFILE, -) +from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index f69fa35..e904a00 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -5,12 +5,10 @@ import pandas as pd -from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( +from cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) -from workflow.scripts.cleaning.advanced.methods.external_profile import ( - METHOD_NAME as EXTERNAL_PROFILE, -) +from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE FILL_GAPS_WITHIN_PERIOD = "fill_gaps_within_period" OVERWRITE_ENTIRE_PERIOD = "overwrite_entire_period" diff --git a/workflow/scripts/cleaning/advanced/planning/requirements.py b/workflow/scripts/cleaning/advanced/planning/requirements.py index ee975fb..e38a92f 100644 --- a/workflow/scripts/cleaning/advanced/planning/requirements.py +++ b/workflow/scripts/cleaning/advanced/planning/requirements.py @@ -8,9 +8,7 @@ import pandas as pd from common.time import as_utc_timestamp -from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( - METHOD_NAME, -) +from cleaning.advanced.methods.construct_from_sources import METHOD_NAME REQUIREMENT_COLUMNS = [ "country", diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 6b9f59f..5748b3e 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -8,10 +8,10 @@ import pandas as pd from cleaning.advanced.gap_report import build_gap_report +from cleaning.advanced.planning.plan import build_auxiliary_fill_plan from cleaning.basic.apply import fill_basic_gaps from cleaning.combine_sources import combine_sources from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -from workflow.scripts.cleaning.advanced.planning.plan import build_auxiliary_fill_plan logger = logging.getLogger(__name__) diff --git a/workflow/scripts/cleaning/sources/__init__.py b/workflow/scripts/cleaning/sources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflow/scripts/cleaning/neso.py b/workflow/scripts/cleaning/sources/neso.py similarity index 100% rename from workflow/scripts/cleaning/neso.py rename to workflow/scripts/cleaning/sources/neso.py diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 4fa9129..2ae1970 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,10 +1,7 @@ import pandas as pd +from cleaning.advanced.methods.construct_from_sources import construct_from_sources from common.time import build_hourly_index -from workflow.scripts.cleaning.advanced.methods.construct_from_sources import ( - construct_from_sources, -) - loads = [ pd.read_parquet(path) for path in snakemake.input.sources diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 2da01fe..573f35f 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -6,15 +6,14 @@ from typing import TYPE_CHECKING, Any import pandas as pd -from workflow.scripts.cleaning.advanced.planning.source_requests import ( +from cleaning.advanced.planning.requirements import ( + build_auxiliary_acquisition_requirements, +) +from cleaning.advanced.planning.source_requests import ( build_auxiliary_source_batches, build_auxiliary_source_requests, ) -from workflow.scripts.cleaning.advanced.planning.requirements import ( - build_auxiliary_acquisition_requirements, -) - if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 8312ae9..7df0047 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, Iterable import pandas as pd -from cleaning.neso import add_utc_timestamps +from cleaning.sources.neso import add_utc_timestamps from common.time import build_hourly_index if TYPE_CHECKING: From 716706caa76f6aca40f0523a750b94c8fd288490 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 13:37:37 +0200 Subject: [PATCH 044/241] Centralize final cleaning provenance --- tests/unit/test_provenance.py | 57 +++++++++++++++++++ workflow/rules/clean.smk | 2 +- workflow/scripts/cleaning/pipeline.py | 2 - .../cleaning/plot_cleaning_timeline.py | 17 ++---- workflow/scripts/cleaning/provenance.py | 26 ++++++++- workflow/scripts/finalise_clean_demand.py | 21 +++---- 6 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 tests/unit/test_provenance.py diff --git a/tests/unit/test_provenance.py b/tests/unit/test_provenance.py new file mode 100644 index 0000000..21475df --- /dev/null +++ b/tests/unit/test_provenance.py @@ -0,0 +1,57 @@ +"""Tests for provenance rules.""" + +from cleaning.provenance import build_final_cleaning_rules + + +def test_build_final_cleaning_rules_includes_advanced_overrides(): + config = { + "mode": "advanced", + "basic": { + "rules": [ + { + "name": "basic_rule", + "method": "linear_interpolation", + } + ] + }, + "advanced": { + "overrides": { + "advanced_rule": { + "method": "construct_from_sources", + } + } + }, + } + + rules = build_final_cleaning_rules(config) + + assert [rule["name"] for rule in rules] == [ + "basic_rule", + "advanced_rule", + ] + +def test_build_final_cleaning_rules_excludes_advanced_overrides_in_basic_mode(): + config = { + "mode": "basic", + "basic": { + "rules": [ + { + "name": "basic_rule", + "method": "linear_interpolation", + } + ] + }, + "advanced": { + "overrides": { + "advanced_rule": { + "method": "construct_from_sources", + } + } + }, + } + + rules = build_final_cleaning_rules(config) + + assert [rule["name"] for rule in rules] == [ + "basic_rule", + ] diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 972c132..a66881e 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -125,7 +125,7 @@ rule plot_cleaning_timeline: input: demand=( "/automatic/" - "load_basic_cleaned.parquet" + "load_cleaned.parquet" ), cleaning_method=( "/automatic/" diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 5748b3e..a7da305 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -48,8 +48,6 @@ def clean_demand( enabled=gap_filling_config["mode"] != "off", ) - basic_rules = gap_filling_config["basic"]["rules"] - cleaning_method_ranks = build_cleaning_method_ranks( source_priority=source_priority, rules=basic_rules, diff --git a/workflow/scripts/cleaning/plot_cleaning_timeline.py b/workflow/scripts/cleaning/plot_cleaning_timeline.py index ac9953f..5a3d657 100644 --- a/workflow/scripts/cleaning/plot_cleaning_timeline.py +++ b/workflow/scripts/cleaning/plot_cleaning_timeline.py @@ -13,6 +13,8 @@ from matplotlib.colors import ListedColormap, to_rgba from matplotlib.patches import Patch +from cleaning.provenance import build_final_cleaning_rules + if TYPE_CHECKING: snakemake: Any @@ -670,21 +672,10 @@ def _build_cleaning_method_metadata( ) rank += 1 - rules = list( - gap_filling_config["basic"]["rules"] + rules = build_final_cleaning_rules( + gap_filling_config ) - if gap_filling_config["mode"] == "advanced": - rules.extend( - { - "name": rule_name, - **override, - } - for rule_name, override in ( - gap_filling_config["advanced"]["overrides"].items() - ) - ) - for rule in rules: rule_name = rule["name"] diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py index 9874cf9..5619f65 100644 --- a/workflow/scripts/cleaning/provenance.py +++ b/workflow/scripts/cleaning/provenance.py @@ -103,4 +103,28 @@ def derive_cleaning_method_rank( ranks ) - return cleaning_method_rank.astype("int16") \ No newline at end of file + return cleaning_method_rank.astype("int16") + + +def build_final_cleaning_rules( + gap_filling_config: Mapping[str, Any], +) -> list[dict[str, Any]]: + """Return configured cleaning rules in final provenance order.""" + rules = [ + dict(rule) + for rule in gap_filling_config["basic"]["rules"] + ] + + if gap_filling_config["mode"] == "advanced": + rules.extend( + { + "name": rule_name, + **override, + } + for rule_name, override in ( + gap_filling_config["advanced"]["overrides"].items() + ) + ) + + return rules + diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 66416c3..45f7669 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -1,7 +1,11 @@ import shutil import pandas as pd -from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank +from cleaning.provenance import ( + build_cleaning_method_ranks, + build_final_cleaning_rules, + derive_cleaning_method_rank, +) shutil.copyfile( snakemake.input.demand, @@ -14,21 +18,10 @@ gap_filling = snakemake.params.gap_filling -rules = list( - gap_filling["basic"]["rules"] +rules = build_final_cleaning_rules( + gap_filling ) -if gap_filling["mode"] == "advanced": - rules.extend( - { - "name": rule_name, - **override, - } - for rule_name, override in ( - gap_filling["advanced"]["overrides"].items() - ) - ) - ranks = build_cleaning_method_ranks( source_priority=snakemake.params.source_names, rules=rules, From a8bd3aba0ba4005fae05b00bc76083497d65721a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 14:04:36 +0200 Subject: [PATCH 045/241] Refactored plotting logic to separate it from snakemake script calls --- .../cleaning/{plot_cleaning_timeline.py => plot_timeline.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename workflow/scripts/cleaning/{plot_cleaning_timeline.py => plot_timeline.py} (100%) diff --git a/workflow/scripts/cleaning/plot_cleaning_timeline.py b/workflow/scripts/cleaning/plot_timeline.py similarity index 100% rename from workflow/scripts/cleaning/plot_cleaning_timeline.py rename to workflow/scripts/cleaning/plot_timeline.py From 8b07b6a7c049f1626c25339007d0bc3ee6b7d0b4 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 14:04:46 +0200 Subject: [PATCH 046/241] As per previous commit --- workflow/Snakefile | 4 +- workflow/rules/advanced.smk | 124 ++++++--------------- workflow/rules/clean.smk | 2 +- workflow/scripts/cleaning/plot_timeline.py | 28 +---- workflow/scripts/plot_cleaning_timeline.py | 26 +++++ 5 files changed, 67 insertions(+), 117 deletions(-) create mode 100644 workflow/scripts/plot_cleaning_timeline.py diff --git a/workflow/Snakefile b/workflow/Snakefile index 4387115..0881eda 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -29,8 +29,8 @@ with open(workflow.source_path("internal/settings.yaml"), "r") as f: # Load python files in scripts that are used in other scripts, but not run itself. # This is necessary for integrating the snakemake module via reference to GitHub. -workflow.source_path("scripts/_plots.py") -workflow.source_path("scripts/_schemas.py") +workflow.source_path("scripts/common/plots.py") +workflow.source_path("scripts/common/schemas.py") # Add all your includes here. diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index d39bd80..458407c 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,6 +1,25 @@ import json import pandas as pd + +def _read_plan_file(plan_path): + with open(plan_path, encoding="utf-8") as file: + return json.load(file) + + +def _read_auxiliary_plan(_wildcards=None): + """Read the resolved auxiliary acquisition plan.""" + plan_path = ( + checkpoints + .plan_auxiliary_data + .get() + .output + .plan + ) + + return _read_plan_file(plan_path) + + def auxiliary_acquisition_plan(_wildcards): """Return the acquisition plan after the checkpoint completes.""" return ( @@ -13,18 +32,12 @@ def auxiliary_acquisition_plan(_wildcards): def get_auxiliary_batch( - plan_path, + plan: dict, *, batch_id: str, source: str, ) -> dict: """Return one source batch from the acquisition plan.""" - with open( - plan_path, - encoding="utf-8", - ) as file: - plan = json.load(file) - matches = [ batch for batch in plan["batches"] @@ -49,27 +62,15 @@ def get_auxiliary_entsoe_batch( ) -> dict: """Return the ENTSO-E batch for this job.""" return get_auxiliary_batch( - input.plan, + _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="entsoe_api", ) -def auxiliary_entsoe_outputs(_wildcards): +def auxiliary_entsoe_outputs(wildcards): """Return all ENTSO-E outputs required by the acquisition plan.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) - - with open( - plan_path, - encoding="utf-8", - ) as file: - plan = json.load(file) + plan = _read_auxiliary_plan(wildcards) batch_ids = [ batch["batch_id"] @@ -93,27 +94,15 @@ def get_auxiliary_opsd_batch( ) -> dict: """Return the OPSD batch for this job.""" return get_auxiliary_batch( - input.plan, + _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="opsd_api", ) -def auxiliary_opsd_outputs(_wildcards): +def auxiliary_opsd_outputs(wildcards): """Return all OPSD outputs required by the acquisition plan.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) - - with open( - plan_path, - encoding="utf-8", - ) as file: - plan = json.load(file) + plan = _read_auxiliary_plan(wildcards) batch_ids = [ batch["batch_id"] @@ -137,7 +126,7 @@ def get_auxiliary_neso_batch( ) -> dict: """Return the NESO batch for this job.""" return get_auxiliary_batch( - input.plan, + _read_auxiliary_plan(input.plan), batch_id=wildcards.batch_id, source="neso", ) @@ -145,16 +134,10 @@ def get_auxiliary_neso_batch( def auxiliary_neso_raw_files(wildcards): """Return annual NESO files required by one auxiliary batch.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) + plan = _read_auxiliary_plan(wildcards) batch = get_auxiliary_batch( - plan_path, + plan, batch_id=wildcards.batch_id, source="neso", ) @@ -173,21 +156,9 @@ def auxiliary_neso_raw_files(wildcards): ] -def auxiliary_neso_outputs(_wildcards): +def auxiliary_neso_outputs(wildcards): """Return all NESO outputs required by the acquisition plan.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) - - with open( - plan_path, - encoding="utf-8", - ) as file: - plan = json.load(file) + plan = _read_auxiliary_plan(wildcards) batch_ids = [ batch["batch_id"] @@ -206,14 +177,11 @@ def auxiliary_neso_outputs(_wildcards): def get_auxiliary_group_batches( - plan_path, + plan: dict, *, group_id: str, ) -> list[dict]: """Return all acquisition batches belonging to one auxiliary group.""" - with open(plan_path, encoding="utf-8") as file: - plan = json.load(file) - batches = [ batch for batch in plan["batches"] @@ -230,16 +198,10 @@ def get_auxiliary_group_batches( def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) + plan = _read_auxiliary_plan(wildcards) batches = get_auxiliary_group_batches( - plan_path, + plan, group_id=wildcards.group_id, ) @@ -253,18 +215,9 @@ def auxiliary_group_source_files(wildcards): ] -def auxiliary_combined_outputs(_wildcards): +def auxiliary_combined_outputs(wildcards): """Return all combined auxiliary group outputs.""" - plan_path = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) - - with open(plan_path, encoding="utf-8") as file: - plan = json.load(file) + plan = _read_auxiliary_plan(wildcards) group_ids = sorted( { @@ -284,10 +237,7 @@ def auxiliary_combined_outputs(_wildcards): def auxiliary_rule_cleaned_files(wildcards): - plan_path = checkpoints.plan_auxiliary_data.get().output.plan - - with open(plan_path, encoding="utf-8") as file: - plan = json.load(file) + plan = _read_auxiliary_plan(wildcards) override = ( config["gap_filling"] diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index a66881e..f9b221e 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -150,7 +150,7 @@ rule plot_cleaning_timeline: message: "Plot electricity-demand cleaning provenance." script: - "../scripts/cleaning/plot_cleaning_timeline.py" + "../scripts/plot_cleaning_timeline.py" rule clean_population: input: diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index 5a3d657..48a3b9c 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -1,9 +1,8 @@ """Plot electricity demand and cleaning-method provenance through time.""" import logging -import sys from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import Any import matplotlib.dates as mdates import matplotlib.pyplot as plt @@ -15,9 +14,6 @@ from cleaning.provenance import build_final_cleaning_rules -if TYPE_CHECKING: - snakemake: Any - logger = logging.getLogger(__name__) @@ -715,25 +711,3 @@ def _format_source_name(source_name: str) -> str: def _format_rule_name(name: str) -> str: return name.replace("_", " ").capitalize() - - -if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) - - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) - - main( - demand_path=snakemake.input.demand, - cleaning_method_path=snakemake.input.cleaning_method, - cleaning_method_rank_path=snakemake.input.cleaning_method_rank, - output_path=snakemake.output.plot, - source_names=snakemake.params.source_names, - gap_filling_config=snakemake.params.gap_filling, - ) diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py new file mode 100644 index 0000000..af0562a --- /dev/null +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -0,0 +1,26 @@ +import logging +import sys + +from cleaning.plot_timeline import main + +sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, +) + +logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", +) + +main( + demand_path=snakemake.input.demand, + cleaning_method_path=snakemake.input.cleaning_method, + cleaning_method_rank_path=( + snakemake.input.cleaning_method_rank + ), + output_path=snakemake.output.plot, + source_names=snakemake.params.source_names, + gap_filling_config=snakemake.params.gap_filling, +) From 8a57b9c4ee2850ec4f313a316ac6faf1b2993948 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 14:07:44 +0200 Subject: [PATCH 047/241] Changed plotting to viridis for colourblind friendliness and visual semantics --- workflow/scripts/cleaning/plot_timeline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index 48a3b9c..381801b 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -308,12 +308,12 @@ def _build_rank_colours( if not imputed.empty: colourtheme = Colormap( - "bids:plasma" + "bids:viridis" ).to_mpl() positions = np.linspace( - 0.0, - 0.9, + 0.05, + 0.95, len(imputed), ) From d3ae4f154984bea0891738d997ed829208526120 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 14:46:35 +0200 Subject: [PATCH 048/241] Add energy-matched auxiliary profile scaling --- tests/integration/test_config.yaml | 7 + .../test_advanced_construct_from_sources.py | 130 ++++++++++++++++++ workflow/rules/advanced.smk | 14 +- .../cleaning/advanced/combine_auxiliary.py | 18 ++- .../methods/construct_from_sources.py | 97 ++++++++++++- .../scripts/construct_auxiliary_profile.py | 1 + 6 files changed, 261 insertions(+), 6 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 018a3b6..c1c061a 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -59,6 +59,13 @@ integration_test: start: "2020-01-01" end: "2020-02-01" weight: 1 + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-01-01" + end: "2024-03-01" + weight: 1 test_construct_second: country: ALB start: "2021-02-01" diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index 5664498..e95bb55 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -223,3 +223,133 @@ def test_construct_from_sources_interpolates_february_29() -> None: assert len(result) == 696 assert (result.loc[target_feb_29] == 30.0).all() +def test_construct_from_sources_matches_reference_energy() -> None: + auxiliary_index = pd.date_range( + "2019-01-01", + periods=6, + freq="h", + tz="UTC", + ) + + auxiliary = pd.DataFrame( + { + "GBR": [ + 10.0, + 20.0, + 30.0, + 100.0, + 100.0, + 100.0, + ], + "ALB": [ + 0.0, + 0.0, + 0.0, + 20.0, + 40.0, + 60.0, + ], + }, + index=auxiliary_index, + ) + + target_index = pd.date_range( + "2020-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ + { + "country": "GBR", + "start": "2019-01-01T00:00:00+00:00", + "end": "2019-01-01T03:00:00+00:00", + } + ], + scaling={ + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2019-01-01T03:00:00+00:00", + "end": "2019-01-01T06:00:00+00:00", + } + ], + }, + ) + + expected = pd.Series( + [ + 20.0, + 40.0, + 60.0, + ], + index=target_index, + dtype=float, + ) + + pd.testing.assert_series_equal( + result, + expected, + ) + +def test_match_energy_uses_weighted_target_energy() -> None: + auxiliary_index = pd.date_range( + "2019-01-01", + periods=6, + freq="h", + tz="UTC", + ) + + auxiliary = pd.DataFrame( + { + "GBR": [10, 20, 30, 0, 0, 0], + "ALB": [0, 0, 0, 20, 20, 20], + "GRC": [0, 0, 0, 60, 60, 60], + }, + index=auxiliary_index, + dtype=float, + ) + + target_index = pd.date_range( + "2020-01-01", + periods=3, + freq="h", + tz="UTC", + ) + + result = construct_from_sources( + auxiliary, + target_index=target_index, + sources=[ + { + "country": "GBR", + "start": "2019-01-01T00:00:00+00:00", + "end": "2019-01-01T03:00:00+00:00", + } + ], + scaling={ + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2019-01-01T03:00:00+00:00", + "end": "2019-01-01T06:00:00+00:00", + "weight": 1, + }, + { + "country": "GRC", + "start": "2019-01-01T03:00:00+00:00", + "end": "2019-01-01T06:00:00+00:00", + "weight": 3, + }, + ], + }, + ) + + assert result.sum() == 150.0 + diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 458407c..e4a08a6 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -237,6 +237,7 @@ def auxiliary_combined_outputs(wildcards): def auxiliary_rule_cleaned_files(wildcards): + """Return cleaned auxiliary files required by one advanced override.""" plan = _read_auxiliary_plan(wildcards) override = ( @@ -246,19 +247,26 @@ def auxiliary_rule_cleaned_files(wildcards): [wildcards.rule_name] ) + required_sources = list(override["sources"]) + + scaling = override.get("scaling") + if scaling is not None: + required_sources.extend( + scaling.get("target_sources", []) + ) + group_ids = set() - for source in override["sources"]: + for source in required_sources: country = source["country"] start = pd.Timestamp(source["start"]) + end = pd.Timestamp(source["end"]) if start.tzinfo is None: start = start.tz_localize("UTC") else: start = start.tz_convert("UTC") - end = pd.Timestamp(source["end"]) - if end.tzinfo is None: end = end.tz_localize("UTC") else: diff --git a/workflow/scripts/cleaning/advanced/combine_auxiliary.py b/workflow/scripts/cleaning/advanced/combine_auxiliary.py index 66d9141..ef14c55 100644 --- a/workflow/scripts/cleaning/advanced/combine_auxiliary.py +++ b/workflow/scripts/cleaning/advanced/combine_auxiliary.py @@ -14,11 +14,25 @@ def combine_auxiliary_sources( pd.DataFrame, pd.DataFrame, ]: - """Combine prepared auxiliary sources using configured source priority.""" + """Combine available auxiliary sources using configured source priority.""" if not loads: empty = pd.DataFrame() return empty, empty.copy(), empty.copy() + unexpected_sources = set(loads) - set(priority) + + if unexpected_sources: + raise ValueError( + "Auxiliary sources were supplied but are not configured in " + f"source priority: {sorted(unexpected_sources)}." + ) + + available_priority = [ + source + for source in priority + if source in loads + ] + columns = sorted( { column @@ -34,5 +48,5 @@ def combine_auxiliary_sources( return combine_sources( aligned, - priority=priority, + priority=available_priority, ) diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py index db1bf7b..0d85ec1 100644 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py @@ -83,11 +83,97 @@ def _align_leap_day( return source_values +def _match_energy( + profile: pd.Series, + *, + auxiliary: pd.DataFrame, + target_sources: Sequence[Mapping[str, Any]], +) -> pd.Series: + """Scale a profile to the weighted-mean energy of reference periods.""" + weighted_energy = 0.0 + total_weight = 0.0 + + for source in target_sources: + country = source["country"] + start = as_utc_timestamp( + source["start"] + ) + end = as_utc_timestamp( + source["end"] + ) + weight = float( + source.get("weight", 1) + ) + + source_values = auxiliary.loc[ + (auxiliary.index >= start) + & (auxiliary.index < end), + country, + ] + + if source_values.empty: + raise ValueError( + "Scaling source period contains no values. " + f"Source {country!r}: {start} to {end}." + ) + + if source_values.isna().any(): + raise ValueError( + "Scaling source period contains missing values. " + f"Source {country!r}: {start} to {end}." + ) + + weighted_energy += ( + float(source_values.sum()) * weight + ) + total_weight += weight + + if total_weight == 0: + raise ValueError( + "Scaling source weights must sum to more than zero." + ) + + target_energy = weighted_energy / total_weight + profile_energy = float(profile.sum()) + + if profile_energy == 0: + raise ValueError( + "Cannot match energy for a constructed profile " + "with zero total energy." + ) + + return profile * ( + target_energy / profile_energy + ) + + +def _apply_scaling( + profile: pd.Series, + *, + auxiliary: pd.DataFrame, + scaling: Mapping[str, Any], +) -> pd.Series: + """Scale a constructed profile according to its configured method.""" + method = scaling["method"] + + if method != "match_energy": + raise ValueError( + f"Unsupported auxiliary scaling method: {method!r}." + ) + + return _match_energy( + profile, + auxiliary=auxiliary, + target_sources=scaling["target_sources"], + ) + + def construct_from_sources( auxiliary: pd.DataFrame, *, target_index: pd.DatetimeIndex, sources: Sequence[Mapping[str, Any]], + scaling: Mapping[str, Any] | None = None, ) -> pd.Series: """Construct a target demand profile from weighted auxiliary sources.""" weighted_sources: list[pd.Series] = [] @@ -142,4 +228,13 @@ def construct_from_sources( weighted_sources[0].copy(), ) - return weighted_sum / sum(weights) \ No newline at end of file + profile = weighted_sum / sum(weights) + + if scaling is not None: + profile = _apply_scaling( + profile, + auxiliary=auxiliary, + scaling=scaling, + ) + + return profile \ No newline at end of file diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 2ae1970..f02fa56 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -28,6 +28,7 @@ auxiliary, target_index=target_index, sources=override["sources"], + scaling=override.get("scaling"), ) profile.to_frame( From 583c7dca9910c34d3802c3c2bf7010197d8a143d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 16:29:29 +0200 Subject: [PATCH 049/241] Validated end to end --- tests/integration/test_config.yaml | 35 +++-- workflow/Snakefile | 9 ++ workflow/rules/advanced.smk | 148 ++++-------------- .../cleaning/advanced/planning/selection.py | 123 +++++++++++++++ 4 files changed, 183 insertions(+), 132 deletions(-) create mode 100644 workflow/scripts/cleaning/advanced/planning/selection.py diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index c1c061a..45b3acb 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,7 +1,7 @@ integration_test: temporal_scope: - start: "2021-01-01" - end: "2021-12-31" + start: "2022-01-01" + end: "2023-01-01" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api @@ -48,32 +48,39 @@ integration_test: # Explicit country-period instructions for unresolved cases. overrides: - test_construct_from_greece: + build_ALB_from_GBR_ALB_winter: country: ALB - start: "2021-01-01" - end: "2021-02-01" + start: "2022-01-01" + end: "2022-02-01" scope: fill_gaps_within_period method: construct_from_sources sources: - country: GBR - start: "2020-01-01" - end: "2020-02-01" + start: "2024-01-01" + end: "2024-02-01" weight: 1 scaling: method: match_energy target_sources: - country: ALB start: "2024-01-01" - end: "2024-03-01" + end: "2024-02-01" weight: 1 - test_construct_second: + build_ALB_from_GBR_summer: country: ALB - start: "2021-02-01" - end: "2021-03-01" + start: "2022-05-01" + end: "2022-06-01" scope: fill_gaps_within_period method: construct_from_sources sources: - country: GBR - start: "2020-02-01" - end: "2020-03-01" - weight: 1 \ No newline at end of file + start: "2022-05-01" + end: "2022-06-01" + weight: 1 + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-05-01" + end: "2024-06-01" + weight: 1 \ No newline at end of file diff --git a/workflow/Snakefile b/workflow/Snakefile index 0881eda..da9187c 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -1,3 +1,5 @@ +import sys + import yaml from snakemake.utils import min_version, validate @@ -27,6 +29,13 @@ validate(config, workflow.source_path("internal/config.schema.yaml")) with open(workflow.source_path("internal/settings.yaml"), "r") as f: internal = yaml.safe_load(f) + +# Make reusable workflow Python packages available to rule files. +scripts_path = workflow.source_path("scripts") + +if scripts_path not in sys.path: + sys.path.insert(0, scripts_path) + # Load python files in scripts that are used in other scripts, but not run itself. # This is necessary for integrating the snakemake module via reference to GitHub. workflow.source_path("scripts/common/plots.py") diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index e4a08a6..31da0f5 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,5 +1,12 @@ import json -import pandas as pd + +from cleaning.advanced.planning.selection import ( + get_auxiliary_batch, + get_auxiliary_group_batches, + get_auxiliary_group_ids, + get_required_auxiliary_group_ids, + get_source_batch_ids, +) def _read_plan_file(plan_path): @@ -31,31 +38,6 @@ def auxiliary_acquisition_plan(_wildcards): ) -def get_auxiliary_batch( - plan: dict, - *, - batch_id: str, - source: str, -) -> dict: - """Return one source batch from the acquisition plan.""" - matches = [ - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == batch_id - and batch["source"] == source - ) - ] - - if len(matches) != 1: - raise ValueError( - "Expected exactly one auxiliary batch for " - f"{source=} and {batch_id=}, found {len(matches)}." - ) - - return matches[0] - - def get_auxiliary_entsoe_batch( wildcards, input, @@ -72,11 +54,10 @@ def auxiliary_entsoe_outputs(wildcards): """Return all ENTSO-E outputs required by the acquisition plan.""" plan = _read_auxiliary_plan(wildcards) - batch_ids = [ - batch["batch_id"] - for batch in plan["batches"] - if batch["source"] == "entsoe_api" - ] + batch_ids = get_source_batch_ids( + plan, + source="entsoe_api", + ) return [ ( @@ -104,11 +85,10 @@ def auxiliary_opsd_outputs(wildcards): """Return all OPSD outputs required by the acquisition plan.""" plan = _read_auxiliary_plan(wildcards) - batch_ids = [ - batch["batch_id"] - for batch in plan["batches"] - if batch["source"] == "opsd_api" - ] + batch_ids = get_source_batch_ids( + plan, + source="opsd_api", + ) return [ ( @@ -126,7 +106,7 @@ def get_auxiliary_neso_batch( ) -> dict: """Return the NESO batch for this job.""" return get_auxiliary_batch( - _read_auxiliary_plan(input.plan), + _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="neso", ) @@ -160,11 +140,10 @@ def auxiliary_neso_outputs(wildcards): """Return all NESO outputs required by the acquisition plan.""" plan = _read_auxiliary_plan(wildcards) - batch_ids = [ - batch["batch_id"] - for batch in plan["batches"] - if batch["source"] == "neso" - ] + batch_ids = get_source_batch_ids( + plan, + source="neso", + ) return [ ( @@ -176,26 +155,6 @@ def auxiliary_neso_outputs(wildcards): ] -def get_auxiliary_group_batches( - plan: dict, - *, - group_id: str, -) -> list[dict]: - """Return all acquisition batches belonging to one auxiliary group.""" - batches = [ - batch - for batch in plan["batches"] - if batch["group_id"] == group_id - ] - - if not batches: - raise ValueError( - f"No auxiliary batches found for group {group_id!r}." - ) - - return batches - - def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" plan = _read_auxiliary_plan(wildcards) @@ -219,12 +178,7 @@ def auxiliary_combined_outputs(wildcards): """Return all combined auxiliary group outputs.""" plan = _read_auxiliary_plan(wildcards) - group_ids = sorted( - { - batch["group_id"] - for batch in plan["batches"] - } - ) + group_ids = get_auxiliary_group_ids(plan) return [ ( @@ -240,56 +194,14 @@ def auxiliary_rule_cleaned_files(wildcards): """Return cleaned auxiliary files required by one advanced override.""" plan = _read_auxiliary_plan(wildcards) - override = ( - config["gap_filling"] - ["advanced"] - ["overrides"] - [wildcards.rule_name] - ) - - required_sources = list(override["sources"]) - - scaling = override.get("scaling") - if scaling is not None: - required_sources.extend( - scaling.get("target_sources", []) - ) + override = config["gap_filling"]["advanced"]["overrides"][ + wildcards.rule_name + ] - group_ids = set() - - for source in required_sources: - country = source["country"] - start = pd.Timestamp(source["start"]) - end = pd.Timestamp(source["end"]) - - if start.tzinfo is None: - start = start.tz_localize("UTC") - else: - start = start.tz_convert("UTC") - - if end.tzinfo is None: - end = end.tz_localize("UTC") - else: - end = end.tz_convert("UTC") - - matching_groups = { - batch["group_id"] - for batch in plan["batches"] - if ( - country in batch["countries"] - and pd.Timestamp(batch["start"]) <= start - and pd.Timestamp(batch["end"]) >= end - ) - } - - if len(matching_groups) != 1: - raise ValueError( - "Expected exactly one cleaned auxiliary group " - f"covering {country!r} from {start} to {end}, " - f"found {sorted(matching_groups)}." - ) - - group_ids.update(matching_groups) + group_ids = get_required_auxiliary_group_ids( + plan, + override=override, + ) return [ ( @@ -297,7 +209,7 @@ def auxiliary_rule_cleaned_files(wildcards): "auxiliary/cleaned/" f"{group_id}.parquet" ) - for group_id in sorted(group_ids) + for group_id in group_ids ] diff --git a/workflow/scripts/cleaning/advanced/planning/selection.py b/workflow/scripts/cleaning/advanced/planning/selection.py new file mode 100644 index 0000000..6c6b71c --- /dev/null +++ b/workflow/scripts/cleaning/advanced/planning/selection.py @@ -0,0 +1,123 @@ +from collections.abc import Mapping, Sequence + +from common.time import as_utc_timestamp + + +def get_auxiliary_batch( + plan: Mapping, + *, + batch_id: str, + source: str, +) -> Mapping: + """Return one source batch from an auxiliary acquisition plan.""" + matches = [ + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == batch_id + and batch["source"] == source + ) + ] + + if len(matches) != 1: + raise ValueError( + "Expected exactly one auxiliary batch for " + f"{source=} and {batch_id=}, found {len(matches)}." + ) + + return matches[0] + + +def get_auxiliary_group_batches( + plan: Mapping, + *, + group_id: str, +) -> list[Mapping]: + """Return acquisition batches belonging to one auxiliary group.""" + batches = [ + batch + for batch in plan["batches"] + if batch["group_id"] == group_id + ] + + if not batches: + raise ValueError( + f"No auxiliary batches found for group {group_id!r}." + ) + + return batches + + +def get_source_batch_ids( + plan: Mapping, + *, + source: str, +) -> list[str]: + """Return batch identifiers for one auxiliary source.""" + return [ + batch["batch_id"] + for batch in plan["batches"] + if batch["source"] == source + ] + + +def get_auxiliary_group_ids( + plan: Mapping, +) -> list[str]: + """Return all auxiliary group identifiers.""" + return sorted( + { + batch["group_id"] + for batch in plan["batches"] + } + ) + + +def get_required_auxiliary_sources( + override: Mapping, +) -> list[Mapping]: + """Return all auxiliary sources consumed by an override.""" + sources = list(override["sources"]) + + scaling = override.get("scaling") + if scaling is not None: + sources.extend( + scaling.get("target_sources", []) + ) + + return sources + + +def get_required_auxiliary_group_ids( + plan: Mapping, + *, + override: Mapping, +) -> list[str]: + """Return auxiliary groups required to execute one override.""" + group_ids: set[str] = set() + + for source in get_required_auxiliary_sources(override): + country = source["country"] + start = as_utc_timestamp(source["start"]) + end = as_utc_timestamp(source["end"]) + + matching_groups = { + batch["group_id"] + for batch in plan["batches"] + if ( + country in batch["countries"] + and as_utc_timestamp(batch["start"]) <= start + and as_utc_timestamp(batch["end"]) >= end + ) + } + + if len(matching_groups) != 1: + raise ValueError( + "Expected exactly one auxiliary group covering " + f"{country!r} from {start} to {end}, " + f"found {sorted(matching_groups)}." + ) + + group_ids.update(matching_groups) + + return sorted(group_ids) From bc28a272a5deb72b298862dbf30ec33a500e98ee Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 17:04:21 +0200 Subject: [PATCH 050/241] Testing a larger run --- tests/integration/test_config.yaml | 2 +- workflow/scripts/prepare_load_opsd.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 45b3acb..963ee1c 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,7 +1,7 @@ integration_test: temporal_scope: start: "2022-01-01" - end: "2023-01-01" + end: "2025-01-01" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index a11c204..e473520 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -9,6 +9,8 @@ from common.schemas import LoadENTSOE from common.time import as_utc_timestamp, build_hourly_index +from time import perf_counter + if TYPE_CHECKING: snakemake: Any From f67275d70b3755280cdbc95e82072602d63cf3f5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 10 Aug 2026 17:38:29 +0200 Subject: [PATCH 051/241] Optimised the ENTSOE Downloader to make requests in parallel alongside other minor efficiency gains. --- workflow/scripts/download_load_entsoe_api.py | 185 ++++++++++++++++--- 1 file changed, 164 insertions(+), 21 deletions(-) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 0b561d6..b42c8cb 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -1,6 +1,8 @@ """Download electricity load data from ENTSO-E using the entsoe-py library.""" import sys +from concurrent.futures import ThreadPoolExecutor, as_completed +from time import perf_counter from typing import TYPE_CHECKING, Any from warnings import warn @@ -14,11 +16,17 @@ if TYPE_CHECKING: snakemake: Any +# Defines how many parallel threads may make calls to ENTSOE. +# Setting this to 3, unsure how ENTSO-E would feel about more +# parallel requests. +MAX_WORKERS = 3 + def load_txt(filepath): """Load text file.""" with open(filepath) as file: data = file.read() + return data @@ -28,39 +36,162 @@ def load_yaml(path): return yaml.safe_load(file) -def main(start, end, country_codes, token, output_load): +def download_country( + *, + country_alpha_3: str, + start: pd.Timestamp, + end: pd.Timestamp, + token: str, +) -> tuple[str, pd.Series, float]: + """Download ENTSO-E load for one country.""" + country = pycountry.countries.get( + alpha_3=country_alpha_3 + ) + + if country is None: + raise ValueError( + "Unknown ISO alpha-3 country code: " + f"{country_alpha_3!r}." + ) + + country_alpha_2 = country.alpha_2 + + client = EntsoePandasClient( + api_key=token, + timeout=60, + ) + + country_start = perf_counter() + + try: + df_country = client.query_load( + country_code=country_alpha_2, + start=start, + end=end, + ) + + df_country = df_country["Actual Load"] + df_country.name = country_alpha_3 + + except NoMatchingDataError: + warn( + f"No data found for " + f"{country_alpha_2}/{country_alpha_3} " + f"in the given period: {start} to {end}" + ) + + df_country = pd.Series( + name=country_alpha_3, + dtype=float, + ) + + elapsed = perf_counter() - country_start + + return ( + country_alpha_3, + df_country, + elapsed, + ) + + +def main( + start, + end, + country_codes, + token, + output_load, +): """Download load in MW via the ENTSO-E API.""" start = as_utc_timestamp(start) end = as_utc_timestamp(end) token = load_txt(token).strip() - client = EntsoePandasClient(api_key=token) - data = [] - for country_alpha_3 in country_codes: - country_alpha_2 = pycountry.countries.get(alpha_3=country_alpha_3).alpha_2 + country_codes = list(country_codes) + total_countries = len(country_codes) - try: - df_country = client.query_load( - country_code=country_alpha_2, start=start, end=end - ) - df_country = df_country["Actual Load"] - df_country.name = country_alpha_3 + download_start = perf_counter() + + print( + f"Downloading ENTSO-E load for {total_countries} countries " + f"from {start} to {end} using " + f"{MAX_WORKERS} parallel workers...", + flush=True, + ) - except NoMatchingDataError: - warn( - f"No data found for {country_alpha_2}/{country_alpha_3} in the given period: {start} to {end}" + data_by_country = {} + + with ThreadPoolExecutor( + max_workers=MAX_WORKERS + ) as executor: + futures = { + executor.submit( + download_country, + country_alpha_3=country_alpha_3, + start=start, + end=end, + token=token, + ): country_alpha_3 + for country_alpha_3 in country_codes + } + + for completed, future in enumerate( + as_completed(futures), + start=1, + ): + country_alpha_3 = futures[future] + + try: + ( + country_alpha_3, + df_country, + elapsed, + ) = future.result() + + except Exception as exc: + raise RuntimeError( + "Failed to download ENTSO-E load for " + f"{country_alpha_3!r}." + ) from exc + + data_by_country[ + country_alpha_3 + ] = df_country + + print( + f"[{completed}/{total_countries}] " + f"Finished {country_alpha_3} " + f"in {elapsed:.1f}s.", + flush=True, ) - df_country = pd.Series(name=country_alpha_3) - data.append(df_country) + print( + f"Finished ENTSO-E downloads in " + f"{perf_counter() - download_start:.1f}s.", + flush=True, + ) + + # Restore configured country order because futures complete + # in arbitrary order. + data = [ + data_by_country[country_alpha_3] + for country_alpha_3 in country_codes + ] + + processing_start = perf_counter() - df = pd.concat(data, axis=1) + df = pd.concat( + data, + axis=1, + ) - df.index = pd.to_datetime(df.index, utc=True) + df.index = pd.to_datetime( + df.index, + utc=True, + ) df = df.resample("1h").mean() - # reindexing to add some security to the entsoe download + # Reindexing adds security to the ENTSO-E download. target_index = build_hourly_index( start=start, end=end, @@ -87,14 +218,26 @@ def main(start, end, country_codes, token, output_load): f"{invalid}" ) - #Pre-cleaning. Replaces empty object columns with NaN columns to allow for data-source combining + # Pre-cleaning. Replace empty object columns with NaN + # columns to allow data-source combining. df = df.astype(float) df.to_parquet(output_load) + print( + f"ENTSO-E processing and write completed in " + f"{perf_counter() - processing_start:.1f}s.", + flush=True, + ) + if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + main( start=snakemake.params.temporal_start, end=snakemake.params.temporal_end, From c73e03cd3326828376d8abe13eeb38380ede198e Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 11 Aug 2026 09:53:12 +0200 Subject: [PATCH 052/241] Advanced overrides are now filtered active/inactive based on countries and scope. Users can keep a generic config file and not worry about whether each rule is specifically relevant. --- tests/unit/test_advanced_plan.py | 223 +++++++++++++++++- tests/unit/test_plan_auxiliary_data.py | 113 ++++++++- .../cleaning/advanced/planning/plan.py | 71 +++++- workflow/scripts/cleaning/pipeline.py | 22 +- workflow/scripts/plan_auxiliary_data.py | 23 +- 5 files changed, 432 insertions(+), 20 deletions(-) diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index 606b68a..a56b42f 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -1,4 +1,5 @@ """Tests for advanced auxiliary-fill planning.""" + import pandas as pd import pytest from cleaning.advanced.planning.plan import ( @@ -6,6 +7,18 @@ validate_auxiliary_fill_rule, ) +TARGET_COUNTRIES = ["ALB"] + +TARGET_START = pd.Timestamp( + "2022-01-01", + tz="UTC", +) + +TARGET_END = pd.Timestamp( + "2025-01-01", + tz="UTC", +) + def test_validate_construct_from_sources_rule() -> None: rule = { @@ -34,6 +47,7 @@ def test_validate_construct_from_sources_rule() -> None: rule, ) + def test_validate_external_profile_rule() -> None: rule = { "country": "ALB", @@ -48,6 +62,7 @@ def test_validate_external_profile_rule() -> None: rule, ) + def test_construct_from_sources_requires_sources() -> None: rule = { "country": "ALB", @@ -66,6 +81,7 @@ def test_construct_from_sources_requires_sources() -> None: rule, ) + def test_source_weight_must_be_positive() -> None: rule = { "country": "ALB", @@ -92,6 +108,7 @@ def test_source_weight_must_be_positive() -> None: rule, ) + def test_validate_construct_from_sources_with_scaling() -> None: rule = { "country": "ALB", @@ -129,6 +146,7 @@ def test_validate_construct_from_sources_with_scaling() -> None: rule, ) + def test_match_energy_scaling_requires_target_sources() -> None: rule = { "country": "ALB", @@ -157,6 +175,7 @@ def test_match_energy_scaling_requires_target_sources() -> None: rule, ) + def test_rejects_unsupported_scaling_method() -> None: rule = { "country": "ALB", @@ -192,6 +211,7 @@ def test_rejects_unsupported_scaling_method() -> None: rule, ) + def test_build_auxiliary_fill_plan_normalizes_rules() -> None: rules = { "external_albania": { @@ -233,7 +253,12 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: }, } - result = build_auxiliary_fill_plan(rules) + result = build_auxiliary_fill_plan( + rules, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) expected = pd.DataFrame( { @@ -293,8 +318,14 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: expected, ) + def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: - result = build_auxiliary_fill_plan({}) + result = build_auxiliary_fill_plan( + {}, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) expected = pd.DataFrame( columns=[ @@ -314,3 +345,191 @@ def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: result, expected, ) + + +def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: + rules = { + "albania": { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2023-01-01", + "end": "2023-02-01", + } + ], + }, + "montenegro": { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2023-01-01", + "end": "2023-02-01", + } + ], + }, + } + + result = build_auxiliary_fill_plan( + rules, + target_countries=["ALB"], + target_start=TARGET_START, + target_end=TARGET_END, + ) + + assert result["rule_name"].tolist() == [ + "albania" + ] + + +def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: + rules = { + "before": { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2020-01-01", + "end": "2020-02-01", + } + ], + }, + "after": { + "country": "ALB", + "start": "2026-01-01", + "end": "2026-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2026-01-01", + "end": "2026-02-01", + } + ], + }, + } + + result = build_auxiliary_fill_plan( + rules, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) + + assert result.empty + + +def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: + rules = { + "partial": { + "country": "ALB", + "start": "2021-12-01", + "end": "2022-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2021-12-01", + "end": "2022-02-01", + } + ], + }, + } + + result = build_auxiliary_fill_plan( + rules, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) + + assert result["rule_name"].tolist() == [ + "partial" + ] + + +def test_build_auxiliary_fill_plan_validates_inactive_rule() -> None: + rules = { + "invalid_montenegro": { + "country": "MNE", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "not_a_scope", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2020-01-01", + "end": "2020-02-01", + } + ], + }, + } + + with pytest.raises( + ValueError, + match="Unsupported scope", + ): + build_auxiliary_fill_plan( + rules, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) + + +def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: + rules = { + "ends_at_start": { + "country": "ALB", + "start": "2021-12-01", + "end": "2022-01-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2021-12-01", + "end": "2022-01-01", + } + ], + }, + "starts_at_end": { + "country": "ALB", + "start": "2025-01-01", + "end": "2025-02-01", + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2025-01-01", + "end": "2025-02-01", + } + ], + }, + } + + result = build_auxiliary_fill_plan( + rules, + target_countries=TARGET_COUNTRIES, + target_start=TARGET_START, + target_end=TARGET_END, + ) + + assert result.empty diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index e2a387d..1827ac5 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -1,10 +1,8 @@ """Tests for auxiliary acquisition planning.""" import pandas as pd -from cleaning.advanced.planning.source_requests import ( - _build_batch_id, - _build_group_id, -) +import pytest +from cleaning.advanced.planning.source_requests import _build_batch_id, _build_group_id from plan_auxiliary_data import build_auxiliary_acquisition_plan @@ -214,3 +212,110 @@ def test_empty_fill_plan_produces_no_acquisition_batches() -> None: assert result == { "batches": [], } + + +def test_acquisition_plan_uses_only_overrides_in_fill_plan() -> None: + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + } + }, + "overrides": { + "active": { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GRC", + "start": "2020-01-01", + "end": "2020-02-01", + "weight": 1, + } + ], + }, + "inactive": { + "country": "MNE", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "SRB", + "start": "2020-01-01", + "end": "2020-02-01", + "weight": 1, + } + ], + }, + }, + }, + } + + fill_plan = pd.DataFrame( + { + "rule_name": ["active"], + "method": ["construct_from_sources"], + "status": ["ready"], + } + ) + + result = build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=config, + source_names=["entsoe_api"], + ) + + assert all( + "GRC" in batch["countries"] + for batch in result["batches"] + ) + + assert all( + "SRB" not in batch["countries"] + for batch in result["batches"] + ) + + +def test_acquisition_plan_rejects_unknown_fill_plan_rule() -> None: + config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + } + }, + "overrides": {}, + }, + } + + fill_plan = pd.DataFrame( + { + "rule_name": ["missing_rule"], + } + ) + + with pytest.raises( + ValueError, + match="references unknown advanced overrides", + ): + build_auxiliary_acquisition_plan( + fill_plan=fill_plan, + gap_filling_config=config, + source_names=["entsoe_api"], + ) + + diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index e904a00..4d05836 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -336,8 +336,62 @@ def _validate_scaling( context="scaling target source", ) + +def override_intersects_target_scope( + rule: Mapping[str, Any], + *, + rule_name: str, + target_countries: Sequence[str], + target_start: pd.Timestamp, + target_end: pd.Timestamp, +) -> bool: + """Return whether an advanced override intersects the model scope.""" + rule_start = _get_required_timestamp( + rule, + key="start", + rule_name=rule_name, + ) + rule_end = _get_required_timestamp( + rule, + key="end", + rule_name=rule_name, + ) + + target_start = pd.Timestamp(target_start) + + if target_start.tzinfo is None: + target_start = target_start.tz_localize("UTC") + else: + target_start = target_start.tz_convert("UTC") + + target_end = pd.Timestamp(target_end) + + if target_end.tzinfo is None: + target_end = target_end.tz_localize("UTC") + else: + target_end = target_end.tz_convert("UTC") + + country_intersects = ( + rule["country"] in target_countries + ) + + period_intersects = ( + rule_start < target_end + and rule_end > target_start + ) + + return ( + country_intersects + and period_intersects + ) + + def build_auxiliary_fill_plan( rules: Mapping[str, Mapping[str, Any]], + *, + target_countries: Sequence[str], + target_start: pd.Timestamp, + target_end: pd.Timestamp, ) -> pd.DataFrame: """Validate and normalize configured advanced-fill rules.""" if not isinstance(rules, Mapping): @@ -347,20 +401,21 @@ def build_auxiliary_fill_plan( records: list[dict[str, Any]] = [] - # TODO: Support reusable libraries of advanced overrides. - # Before planning auxiliary acquisition, filter configured overrides - # against the current model countries and temporal scope. Overrides - # that do not intersect the current target scope should be ignored, - # while applicable overrides should continue to be validated strictly. - # This allows users to have a general overrides file for which they - # need not retune the dates for each new run/horizon. - for rule_name, rule in rules.items(): validate_auxiliary_fill_rule( rule_name, rule, ) + if not override_intersects_target_scope( + rule, + rule_name=rule_name, + target_countries=target_countries, + target_start=target_start, + target_end=target_end, + ): + continue + method = rule["method"] if method == CONSTRUCT_FROM_SOURCES: diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index a7da305..63ee452 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -63,10 +63,18 @@ def clean_demand( enabled=gap_filling_config["mode"] == "advanced", ) + target_countries = list(cleaned.columns) + target_start = cleaned.index.min() + target_end = cleaned.index.max() + pd.Timedelta(hours=1) + if gap_filling_config["mode"] == "advanced": advanced_overrides = gap_filling_config["advanced"]["overrides"] + auxiliary_fill_plan = build_auxiliary_fill_plan( - advanced_overrides + advanced_overrides, + target_countries=target_countries, + target_start=target_start, + target_end=target_end, ) logger.info( @@ -77,12 +85,17 @@ def clean_demand( ) logger.info( - "Advanced auxiliary-fill plan contains %s configured " + "Advanced auxiliary-fill plan contains %s active " "instructions.", len(auxiliary_fill_plan), ) else: - auxiliary_fill_plan = build_auxiliary_fill_plan({}) + auxiliary_fill_plan = build_auxiliary_fill_plan( + {}, + target_countries=target_countries, + target_start=target_start, + target_end=target_end, + ) return ( cleaned, @@ -91,5 +104,4 @@ def clean_demand( cleaning_method_rank, gap_report, auxiliary_fill_plan, - ) - + ) \ No newline at end of file diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 573f35f..9797315 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -33,8 +33,29 @@ def build_auxiliary_acquisition_plan( advanced = gap_filling_config["advanced"] + active_rule_names = set( + fill_plan["rule_name"] + ) + + unknown_rule_names = ( + active_rule_names + - set(advanced["overrides"]) + ) + + if unknown_rule_names: + raise ValueError( + "Auxiliary fill plan references unknown advanced " + f"overrides: {sorted(unknown_rule_names)}." + ) + + active_overrides = { + rule_name: override + for rule_name, override in advanced["overrides"].items() + if rule_name in active_rule_names + } + requirements = build_auxiliary_acquisition_requirements( - overrides=advanced["overrides"], + overrides=active_overrides, basic_rules=gap_filling_config["basic"]["rules"], basic_cleaning_enabled=( advanced["auxiliary_data"] From f6489bca265118e8469aeb57b6cfbfff95e51ada Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 11 Aug 2026 15:06:19 +0200 Subject: [PATCH 053/241] Implemented active vs. inactive rules such that users can maintain a general library of overrides without being concerned with specific case relevance. --- tests/integration/test_config.yaml | 15 ++- tests/unit/test_cleaning_pipeline.py | 101 ++++++++++++++- tests/unit/test_plan_auxiliary_data.py | 124 +++++-------------- workflow/rules/advanced.smk | 16 ++- workflow/scripts/apply_advanced_overrides.py | 30 ++++- workflow/scripts/plan_auxiliary_data.py | 26 ++-- 6 files changed, 207 insertions(+), 105 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 963ee1c..685e7b8 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -66,6 +66,7 @@ integration_test: start: "2024-01-01" end: "2024-02-01" weight: 1 + build_ALB_from_GBR_summer: country: ALB start: "2022-05-01" @@ -83,4 +84,16 @@ integration_test: - country: ALB start: "2024-05-01" end: "2024-06-01" - weight: 1 \ No newline at end of file + weight: 1 + + example_inactive_MNE_override: + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps_within_period + method: construct_from_sources + sources: + - country: SRB + start: "2022-03-01" + end: "2022-04-01" + weight: 1 \ No newline at end of file diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index b8a814e..ffcb339 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -657,4 +657,103 @@ def test_advanced_mode_builds_auxiliary_fill_plan() -> None: assert row["country"] == "ALB" assert row["method"] == "construct_from_sources" assert row["status"] == "ready" - assert row["source_count"] == 1 \ No newline at end of file + assert row["source_count"] == 1 + + +def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: + index = pd.date_range( + start="2022-01-01", + end="2022-01-03 23:00", + freq="h", + tz="UTC", + ) + + source = pd.DataFrame( + { + "ALB": range(len(index)), + }, + index=index, + dtype=float, + ) + + sources = { + "primary": source, + } + + gap_filling_config = { + "mode": "advanced", + "basic": { + "rules": [], + }, + "advanced": { + "auxiliary_data": { + "basic_cleaning": { + "enabled": False, + } + }, + "overrides": { + "active_albania": { + "country": "ALB", + "start": "2022-01-02", + "end": "2022-01-03", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2022-01-02", + "end": "2022-01-03", + "weight": 1, + } + ], + }, + "inactive_country": { + "country": "MNE", + "start": "2022-01-02", + "end": "2022-01-03", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2022-01-02", + "end": "2022-01-03", + "weight": 1, + } + ], + }, + "inactive_period": { + "country": "ALB", + "start": "2021-01-01", + "end": "2021-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2021-01-01", + "end": "2021-02-01", + "weight": 1, + } + ], + }, + }, + }, + } + + ( + _cleaned, + _data_source, + _cleaning_method, + _cleaning_method_rank, + _gap_report, + auxiliary_fill_plan, + ) = clean_demand( + sources, + source_priority=["primary"], + gap_filling_config=gap_filling_config, + ) + + assert auxiliary_fill_plan["rule_name"].tolist() == [ + "active_albania" + ] diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 1827ac5..7c1aeac 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -2,43 +2,33 @@ import pandas as pd import pytest -from cleaning.advanced.planning.source_requests import _build_batch_id, _build_group_id +from cleaning.advanced.planning.source_requests import ( + _build_batch_id, + _build_group_id, +) from plan_auxiliary_data import build_auxiliary_acquisition_plan def test_plan_is_empty_outside_advanced_mode() -> None: config = { "mode": "basic", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": {}, }, } - fill_plan = pd.DataFrame( - { - "rule_name": ["example"], - } - ) + fill_plan = pd.DataFrame({"rule_name": ["example"]}) result = build_auxiliary_acquisition_plan( fill_plan=fill_plan, gap_filling_config=config, - source_names=[ - "entsoe_api", - "neso", - "opsd_api", - ], + source_names=["entsoe_api", "neso", "opsd_api"], ) assert result == { + "active_rule_names": [], "batches": [], } @@ -46,32 +36,21 @@ def test_plan_is_empty_outside_advanced_mode() -> None: def test_advanced_plan_with_no_overrides_is_empty() -> None: config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": {}, }, } - fill_plan = pd.DataFrame() - result = build_auxiliary_acquisition_plan( - fill_plan=fill_plan, + fill_plan=pd.DataFrame(), gap_filling_config=config, - source_names=[ - "entsoe_api", - "neso", - "opsd_api", - ], + source_names=["entsoe_api", "neso", "opsd_api"], ) assert result == { + "active_rule_names": [], "batches": [], } @@ -79,15 +58,9 @@ def test_advanced_plan_with_no_overrides_is_empty() -> None: def test_advanced_plan_builds_serializable_source_batches() -> None: config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": { "fill_albania": { "country": "ALB", @@ -119,27 +92,15 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: result = build_auxiliary_acquisition_plan( fill_plan=fill_plan, gap_filling_config=config, - source_names=[ - "entsoe_api", - "opsd_api", - ], - ) - - start = pd.Timestamp( - "2020-01-01", - tz="UTC", - ) - end = pd.Timestamp( - "2020-02-01", - tz="UTC", + source_names=["entsoe_api", "opsd_api"], ) - group_id = _build_group_id( - start=start, - end=end, - ) + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + group_id = _build_group_id(start=start, end=end) assert result == { + "active_rule_names": ["fill_albania"], "batches": [ { "group_id": group_id, @@ -167,22 +128,16 @@ def test_advanced_plan_builds_serializable_source_batches() -> None: "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], }, - ] + ], } def test_empty_fill_plan_produces_no_acquisition_batches() -> None: config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": { "fill_albania": { "country": "ALB", @@ -210,6 +165,7 @@ def test_empty_fill_plan_produces_no_acquisition_batches() -> None: ) assert result == { + "active_rule_names": [], "batches": [], } @@ -217,15 +173,9 @@ def test_empty_fill_plan_produces_no_acquisition_batches() -> None: def test_acquisition_plan_uses_only_overrides_in_fill_plan() -> None: config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": { "active": { "country": "ALB", @@ -275,6 +225,8 @@ def test_acquisition_plan_uses_only_overrides_in_fill_plan() -> None: source_names=["entsoe_api"], ) + assert result["active_rule_names"] == ["active"] + assert all( "GRC" in batch["countries"] for batch in result["batches"] @@ -289,24 +241,14 @@ def test_acquisition_plan_uses_only_overrides_in_fill_plan() -> None: def test_acquisition_plan_rejects_unknown_fill_plan_rule() -> None: config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": {}, }, } - fill_plan = pd.DataFrame( - { - "rule_name": ["missing_rule"], - } - ) + fill_plan = pd.DataFrame({"rule_name": ["missing_rule"]}) with pytest.raises( ValueError, @@ -317,5 +259,3 @@ def test_acquisition_plan_rejects_unknown_fill_plan_rule() -> None: gap_filling_config=config, source_names=["entsoe_api"], ) - - diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 31da0f5..baf2cbf 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -213,8 +213,14 @@ def auxiliary_rule_cleaned_files(wildcards): ] -def advanced_constructed_profiles(_wildcards): - """Return constructed profiles required by advanced overrides.""" +def advanced_constructed_profiles(wildcards): + """Return constructed profiles required by active advanced overrides.""" + plan = _read_auxiliary_plan(wildcards) + + active_rule_names = set( + plan["active_rule_names"] + ) + overrides = ( config["gap_filling"] ["advanced"] @@ -228,7 +234,10 @@ def advanced_constructed_profiles(_wildcards): f"{rule_name}.parquet" ) for rule_name, override in overrides.items() - if override["method"] == "construct_from_sources" + if ( + rule_name in active_rule_names + and override["method"] == "construct_from_sources" + ) ] @@ -530,6 +539,7 @@ rule apply_advanced_overrides: "/automatic/" "load_cleaning_method.parquet" ), + plan=auxiliary_acquisition_plan, profiles=advanced_constructed_profiles, output: demand=( diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 6bc25b5..c7a9bd8 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -1,3 +1,4 @@ +import json from pathlib import Path import pandas as pd @@ -11,6 +12,33 @@ snakemake.input.cleaning_method ) +with open( + snakemake.input.plan, + encoding="utf-8", +) as file: + plan = json.load(file) + +active_rule_names = set( + plan["active_rule_names"] +) + +unknown_rule_names = ( + active_rule_names + - set(snakemake.params.overrides) +) + +if unknown_rule_names: + raise ValueError( + "Auxiliary acquisition plan references unknown advanced " + f"overrides: {sorted(unknown_rule_names)}." + ) + +active_overrides = { + rule_name: override + for rule_name, override in snakemake.params.overrides.items() + if rule_name in active_rule_names +} + profiles = { Path(path).stem: pd.read_parquet(path).iloc[:, 0] for path in snakemake.input.profiles @@ -19,7 +47,7 @@ filled, cleaning_method = apply_auxiliary_fill_rules( load, cleaning_method, - overrides=snakemake.params.overrides, + overrides=active_overrides, profiles=profiles, ) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 9797315..ea32cbe 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -23,13 +23,19 @@ def build_auxiliary_acquisition_plan( fill_plan: pd.DataFrame, gap_filling_config: Mapping[str, Any], source_names: Sequence[str], -) -> dict[str, list[dict[str, object]]]: +) -> dict[str, object]: """Build a JSON-serializable auxiliary acquisition plan.""" if gap_filling_config["mode"] != "advanced": - return {"batches": []} + return { + "active_rule_names": [], + "batches": [], + } if fill_plan.empty: - return {"batches": []} + return { + "active_rule_names": [], + "batches": [], + } advanced = gap_filling_config["advanced"] @@ -48,10 +54,15 @@ def build_auxiliary_acquisition_plan( f"overrides: {sorted(unknown_rule_names)}." ) - active_overrides = { - rule_name: override - for rule_name, override in advanced["overrides"].items() + ordered_active_rule_names = [ + rule_name + for rule_name in advanced["overrides"] if rule_name in active_rule_names + ] + + active_overrides = { + rule_name: advanced["overrides"][rule_name] + for rule_name in ordered_active_rule_names } requirements = build_auxiliary_acquisition_requirements( @@ -74,6 +85,7 @@ def build_auxiliary_acquisition_plan( ) return { + "active_rule_names": ordered_active_rule_names, "batches": [ { **batch, @@ -81,7 +93,7 @@ def build_auxiliary_acquisition_plan( "end": batch["end"].isoformat(), } for batch in batches - ] + ], } From 456f1144b23e7cee8d353ac8b0b87c6d665354e5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 11 Aug 2026 16:00:58 +0200 Subject: [PATCH 054/241] Added config test for advanced overrides --- tests/unit/test_advanced_apply.py | 146 ++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 8a7b58c..cc4d7d5 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -311,3 +311,149 @@ def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: assert filled is not load assert methods is not cleaning_method + + +def test_overwrite_entire_period_replaces_existing_values() -> None: + index = pd.date_range( + "2022-01-01", + periods=4, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [10.0, 20.0, 30.0, 40.0], + }, + index=index, + ) + + cleaning_method = pd.DataFrame( + { + "ALB": ["observed_entsoe_api"] * 4, + }, + index=index, + ) + + profile = pd.Series( + [100.0, 200.0], + index=index[1:3], + name="ALB", + ) + + overrides = { + "replace_albania": { + "country": "ALB", + "start": index[1], + "end": index[3], + "scope": "overwrite_entire_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2024-01-01", + "end": "2024-01-01 02:00", + "weight": 1, + } + ], + } + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + profiles={ + "replace_albania": profile, + }, + ) + + assert filled["ALB"].tolist() == [ + 10.0, + 100.0, + 200.0, + 40.0, + ] + + assert methods["ALB"].tolist() == [ + "observed_entsoe_api", + "replace_albania", + "replace_albania", + "observed_entsoe_api", + ] + +def test_fill_gaps_within_period_preserves_existing_values() -> None: + index = pd.date_range( + "2022-01-01", + periods=4, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [10.0, float("nan"), 30.0, 40.0], + }, + index=index, + ) + + cleaning_method = pd.DataFrame( + { + "ALB": [ + "observed_entsoe_api", + "missing", + "observed_entsoe_api", + "observed_entsoe_api", + ], + }, + index=index, + ) + + profile = pd.Series( + [100.0, 200.0], + index=index[1:3], + name="ALB", + ) + + overrides = { + "fill_albania": { + "country": "ALB", + "start": index[1], + "end": index[3], + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": "GBR", + "start": "2024-01-01", + "end": "2024-01-01 02:00", + "weight": 1, + } + ], + } + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + profiles={ + "fill_albania": profile, + }, + ) + + assert filled["ALB"].tolist() == [ + 10.0, + 100.0, + 30.0, + 40.0, + ] + + assert methods["ALB"].tolist() == [ + "observed_entsoe_api", + "fill_albania", + "observed_entsoe_api", + "observed_entsoe_api", + ] + + From 223c7817fb0d0203e45f8d8ee3d8d4a36a65dcad Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 10:20:25 +0200 Subject: [PATCH 055/241] refactor: compile advanced execution manifest --- tests/unit/test_plan_auxiliary_data.py | 493 +++++++++++------- .../cleaning/advanced/planning/execution.py | 200 +++++++ workflow/scripts/plan_auxiliary_data.py | 100 +--- 3 files changed, 518 insertions(+), 275 deletions(-) create mode 100644 workflow/scripts/cleaning/advanced/planning/execution.py diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 7c1aeac..35c7579 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -1,261 +1,390 @@ -"""Tests for auxiliary acquisition planning.""" +"""Tests for compiling the advanced execution plan.""" + +import json import pandas as pd import pytest -from cleaning.advanced.planning.source_requests import ( - _build_batch_id, - _build_group_id, -) -from plan_auxiliary_data import build_auxiliary_acquisition_plan +from cleaning.advanced.planning.execution import build_advanced_execution_plan +from cleaning.advanced.planning.source_requests import _build_batch_id, _build_group_id -def test_plan_is_empty_outside_advanced_mode() -> None: - config = { - "mode": "basic", - "basic": {"rules": []}, +def _config( + overrides: dict, + *, + mode: str = "advanced", + basic_cleaning_enabled: bool = False, + basic_rules: list[dict] | None = None, +) -> dict: + return { + "mode": mode, + "basic": {"rules": basic_rules or []}, "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": {}, + "auxiliary_data": { + "basic_cleaning": { + "enabled": basic_cleaning_enabled, + } + }, + "overrides": overrides, }, } - fill_plan = pd.DataFrame({"rule_name": ["example"]}) - result = build_auxiliary_acquisition_plan( - fill_plan=fill_plan, - gap_filling_config=config, - source_names=["entsoe_api", "neso", "opsd_api"], - ) - - assert result == { - "active_rule_names": [], - "batches": [], +def _construct_override( + *, + source_country: str = "GRC", + source_start: str = "2020-01-01", + source_end: str = "2020-02-01", +) -> dict: + return { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "construct_from_sources", + "sources": [ + { + "country": source_country, + "start": source_start, + "end": source_end, + "weight": 1, + } + ], } -def test_advanced_plan_with_no_overrides_is_empty() -> None: - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": {}, - }, - } +def _fill_plan(rule_names: list[str]) -> pd.DataFrame: + if not rule_names: + return pd.DataFrame() - result = build_auxiliary_acquisition_plan( - fill_plan=pd.DataFrame(), - gap_filling_config=config, - source_names=["entsoe_api", "neso", "opsd_api"], + return pd.DataFrame( + { + "rule_name": rule_names, + "status": ["ready"] * len(rule_names), + } ) - assert result == { + +def _empty_execution_plan() -> dict: + return { + "version": 1, "active_rule_names": [], + "rules": {}, "batches": [], + "batch_ids_by_source": {}, + "groups": {}, + "constructed_profile_rule_names": [], } -def test_advanced_plan_builds_serializable_source_batches() -> None: - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": { - "fill_albania": { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps_within_period", - "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2020-01-01", - "end": "2020-02-01", - "weight": 1, - } - ], - } - }, - }, - } +def test_plan_is_empty_outside_advanced_mode() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["example"]), + gap_filling_config=_config({}, mode="basic"), + source_names=["entsoe_api", "neso", "opsd_api"], + ) - fill_plan = pd.DataFrame( - { - "rule_name": ["fill_albania"], - "method": ["construct_from_sources"], - "status": ["ready"], - } + assert result == _empty_execution_plan() + + +def test_advanced_plan_with_empty_fill_plan_is_empty() -> None: + result = build_advanced_execution_plan( + fill_plan=pd.DataFrame(), + gap_filling_config=_config( + {"fill_albania": _construct_override()} + ), + source_names=["entsoe_api"], ) - result = build_auxiliary_acquisition_plan( - fill_plan=fill_plan, - gap_filling_config=config, + assert result == _empty_execution_plan() + + +def test_plan_builds_complete_execution_manifest() -> None: + override = _construct_override() + + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + {"fill_albania": override} + ), source_names=["entsoe_api", "opsd_api"], ) start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") group_id = _build_group_id(start=start, end=end) + entsoe_batch_id = _build_batch_id( + source="entsoe_api", + start=start, + end=end, + countries=["GRC"], + ) + opsd_batch_id = _build_batch_id( + source="opsd_api", + start=start, + end=end, + countries=["GRC"], + ) assert result == { + "version": 1, "active_rule_names": ["fill_albania"], + "rules": { + "fill_albania": { + "override": override, + "required_group_ids": [group_id], + } + }, "batches": [ { "group_id": group_id, - "batch_id": _build_batch_id( - source="entsoe_api", - start=start, - end=end, - countries=["GRC"], - ), + "batch_id": entsoe_batch_id, "source": "entsoe_api", "start": "2020-01-01T00:00:00+00:00", "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], + "years": [2020], }, { "group_id": group_id, - "batch_id": _build_batch_id( - source="opsd_api", - start=start, - end=end, - countries=["GRC"], - ), + "batch_id": opsd_batch_id, "source": "opsd_api", "start": "2020-01-01T00:00:00+00:00", "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], + "years": [2020], }, ], + "batch_ids_by_source": { + "entsoe_api": [entsoe_batch_id], + "opsd_api": [opsd_batch_id], + }, + "groups": { + group_id: [entsoe_batch_id, opsd_batch_id], + }, + "constructed_profile_rule_names": ["fill_albania"], } -def test_empty_fill_plan_produces_no_acquisition_batches() -> None: - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": { - "fill_albania": { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps_within_period", - "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2020-01-01", - "end": "2020-02-01", - "weight": 1, - } - ], - } - }, - }, - } +def test_plan_uses_only_overrides_in_fill_plan() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["active"]), + gap_filling_config=_config( + { + "active": _construct_override( + source_country="GRC" + ), + "inactive": _construct_override( + source_country="SRB" + ), + } + ), + source_names=["entsoe_api"], + ) - result = build_auxiliary_acquisition_plan( - fill_plan=pd.DataFrame(), - gap_filling_config=config, + assert result["active_rule_names"] == ["active"] + assert list(result["rules"]) == ["active"] + assert result["constructed_profile_rule_names"] == ["active"] + assert all( + "GRC" in batch["countries"] + for batch in result["batches"] + ) + assert all( + "SRB" not in batch["countries"] + for batch in result["batches"] + ) + + +def test_plan_preserves_configured_override_order() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["second", "first"]), + gap_filling_config=_config( + { + "first": _construct_override( + source_country="GRC" + ), + "second": _construct_override( + source_country="SRB" + ), + } + ), source_names=["entsoe_api"], ) - assert result == { - "active_rule_names": [], - "batches": [], - } + assert result["active_rule_names"] == ["first", "second"] + assert list(result["rules"]) == ["first", "second"] + assert result["constructed_profile_rule_names"] == [ + "first", + "second", + ] -def test_acquisition_plan_uses_only_overrides_in_fill_plan() -> None: - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": { - "active": { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps_within_period", - "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2020-01-01", - "end": "2020-02-01", - "weight": 1, - } - ], - }, - "inactive": { - "country": "MNE", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps_within_period", - "method": "construct_from_sources", - "sources": [ - { - "country": "SRB", - "start": "2020-01-01", - "end": "2020-02-01", - "weight": 1, - } - ], - }, - }, - }, +def test_plan_resolves_scaling_target_sources_to_groups() -> None: + override = _construct_override() + override["scaling"] = { + "method": "match_energy", + "target_sources": [ + { + "country": "ALB", + "start": "2020-03-01", + "end": "2020-04-01", + "weight": 1, + } + ], } - fill_plan = pd.DataFrame( + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + {"fill_albania": override} + ), + source_names=["entsoe_api"], + ) + + expected_group_ids = sorted( + [ + _build_group_id( + start=pd.Timestamp("2020-01-01", tz="UTC"), + end=pd.Timestamp("2020-02-01", tz="UTC"), + ), + _build_group_id( + start=pd.Timestamp("2020-03-01", tz="UTC"), + end=pd.Timestamp("2020-04-01", tz="UTC"), + ), + ] + ) + + assert ( + result["rules"]["fill_albania"]["required_group_ids"] + == expected_group_ids + ) + + +def test_plan_resolves_rule_to_expanded_basic_cleaning_group() -> None: + override = _construct_override() + basic_rules = [ { - "rule_name": ["active"], - "method": ["construct_from_sources"], - "status": ["ready"], + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + "require_complete_source": True, } - ) + ] - result = build_auxiliary_acquisition_plan( - fill_plan=fill_plan, - gap_filling_config=config, + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + {"fill_albania": override}, + basic_cleaning_enabled=True, + basic_rules=basic_rules, + ), source_names=["entsoe_api"], ) - assert result["active_rule_names"] == ["active"] + batch = result["batches"][0] - assert all( - "GRC" in batch["countries"] - for batch in result["batches"] - ) + assert batch["start"] < "2020-01-01T00:00:00+00:00" + assert batch["end"] > "2020-02-01T00:00:00+00:00" + assert result["rules"]["fill_albania"]["required_group_ids"] == [ + batch["group_id"] + ] - assert all( - "SRB" not in batch["countries"] - for batch in result["batches"] - ) +def test_non_construct_rule_requires_no_auxiliary_groups() -> None: + override = { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps_within_period", + "method": "leave_missing", + } -def test_acquisition_plan_rejects_unknown_fill_plan_rule() -> None: - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": {}, - }, + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["leave_albania"]), + gap_filling_config=_config( + {"leave_albania": override} + ), + source_names=["entsoe_api"], + ) + + assert result["rules"] == { + "leave_albania": { + "override": override, + "required_group_ids": [], + } } + assert result["batches"] == [] + assert result["groups"] == {} + assert result["constructed_profile_rule_names"] == [] - fill_plan = pd.DataFrame({"rule_name": ["missing_rule"]}) +def test_batch_years_use_half_open_period_semantics() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + { + "fill_albania": _construct_override( + source_country="GBR", + source_start="2020-12-31", + source_end="2021-01-01", + ) + } + ), + source_names=["neso"], + ) + + assert result["batches"][0]["years"] == [2020] + + +def test_plan_rejects_unknown_fill_plan_rule() -> None: with pytest.raises( ValueError, match="references unknown advanced overrides", ): - build_auxiliary_acquisition_plan( - fill_plan=fill_plan, - gap_filling_config=config, + build_advanced_execution_plan( + fill_plan=_fill_plan(["missing_rule"]), + gap_filling_config=_config({}), source_names=["entsoe_api"], ) + + +def test_plan_rejects_duplicate_fill_plan_rules() -> None: + with pytest.raises( + ValueError, + match="contains duplicate rule names", + ): + build_advanced_execution_plan( + fill_plan=_fill_plan( + ["fill_albania", "fill_albania"] + ), + gap_filling_config=_config( + {"fill_albania": _construct_override()} + ), + source_names=["entsoe_api"], + ) + + +def test_plan_rejects_unresolvable_auxiliary_group() -> None: + with pytest.raises( + ValueError, + match="Expected exactly one auxiliary group covering", + ): + build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + {"fill_albania": _construct_override()} + ), + source_names=[], + ) + + +def test_plan_is_json_serializable() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["fill_albania"]), + gap_filling_config=_config( + {"fill_albania": _construct_override()} + ), + source_names=["entsoe_api"], + ) + + json.dumps(result) diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py new file mode 100644 index 0000000..846b00b --- /dev/null +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -0,0 +1,200 @@ +"""Compile the execution manifest for advanced gap filling.""" + +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + +from cleaning.advanced.methods.construct_from_sources import ( + METHOD_NAME as CONSTRUCT_FROM_SOURCES, +) +from cleaning.advanced.planning.requirements import ( + build_auxiliary_acquisition_requirements, +) +from cleaning.advanced.planning.selection import get_required_auxiliary_group_ids +from cleaning.advanced.planning.source_requests import ( + build_auxiliary_source_batches, + build_auxiliary_source_requests, +) + +EXECUTION_PLAN_VERSION = 1 + + +def build_advanced_execution_plan( + *, + fill_plan: pd.DataFrame, + gap_filling_config: Mapping[str, Any], + source_names: Sequence[str], +) -> dict[str, object]: + """Compile all domain-aware information needed by the advanced DAG.""" + if gap_filling_config["mode"] != "advanced" or fill_plan.empty: + return _empty_execution_plan() + + overrides = gap_filling_config["advanced"]["overrides"] + ordered_active_rule_names = _get_ordered_active_rule_names( + fill_plan, + overrides=overrides, + ) + active_overrides = { + rule_name: overrides[rule_name] + for rule_name in ordered_active_rule_names + } + + requirements = build_auxiliary_acquisition_requirements( + overrides=active_overrides, + basic_rules=gap_filling_config["basic"]["rules"], + basic_cleaning_enabled=( + gap_filling_config["advanced"] + ["auxiliary_data"] + ["basic_cleaning"] + ["enabled"] + ), + ) + requests = build_auxiliary_source_requests( + requirements, + source_names=source_names, + ) + batches = [ + _serialize_batch(batch) + for batch in build_auxiliary_source_batches(requests) + ] + + rules: dict[str, dict[str, object]] = {} + constructed_profile_rule_names: list[str] = [] + batch_plan = {"batches": batches} + + for rule_name in ordered_active_rule_names: + override = active_overrides[rule_name] + required_group_ids: list[str] = [] + + if override["method"] == CONSTRUCT_FROM_SOURCES: + required_group_ids = get_required_auxiliary_group_ids( + batch_plan, + override=override, + ) + constructed_profile_rule_names.append(rule_name) + + rules[rule_name] = { + "override": override, + "required_group_ids": required_group_ids, + } + + return { + "version": EXECUTION_PLAN_VERSION, + "active_rule_names": ordered_active_rule_names, + "rules": rules, + "batches": batches, + "batch_ids_by_source": _index_batch_ids_by_source(batches), + "groups": _index_batch_ids_by_group(batches), + "constructed_profile_rule_names": constructed_profile_rule_names, + } + + +def _empty_execution_plan() -> dict[str, object]: + """Return an empty plan with the complete manifest schema.""" + return { + "version": EXECUTION_PLAN_VERSION, + "active_rule_names": [], + "rules": {}, + "batches": [], + "batch_ids_by_source": {}, + "groups": {}, + "constructed_profile_rule_names": [], + } + + +def _get_ordered_active_rule_names( + fill_plan: pd.DataFrame, + *, + overrides: Mapping[str, Mapping[str, Any]], +) -> list[str]: + """Return active rule names in configured execution order.""" + if "rule_name" not in fill_plan.columns: + raise ValueError( + "Auxiliary fill plan must define a 'rule_name' column." + ) + + duplicate_rule_names = sorted( + fill_plan.loc[ + fill_plan["rule_name"].duplicated(), + "rule_name", + ] + .drop_duplicates() + .tolist() + ) + if duplicate_rule_names: + raise ValueError( + "Auxiliary fill plan contains duplicate rule names: " + f"{duplicate_rule_names}." + ) + + active_rule_names = set(fill_plan["rule_name"]) + unknown_rule_names = active_rule_names - set(overrides) + if unknown_rule_names: + raise ValueError( + "Auxiliary fill plan references unknown advanced " + f"overrides: {sorted(unknown_rule_names)}." + ) + + return [ + rule_name + for rule_name in overrides + if rule_name in active_rule_names + ] + + +def _serialize_batch( + batch: Mapping[str, object], +) -> dict[str, object]: + """Convert one planned batch to JSON-compatible values.""" + start = pd.Timestamp(batch["start"]) + end = pd.Timestamp(batch["end"]) + + if end <= start: + raise ValueError( + "Auxiliary batch end must be later than its start." + ) + + final_included_time = end - pd.Timedelta(nanoseconds=1) + + return { + **batch, + "start": start.isoformat(), + "end": end.isoformat(), + "years": list( + range( + start.year, + final_included_time.year + 1, + ) + ), + } + + +def _index_batch_ids_by_source( + batches: Sequence[Mapping[str, object]], +) -> dict[str, list[str]]: + """Index planned batch identifiers by source.""" + result: dict[str, list[str]] = {} + + for batch in batches: + result.setdefault( + str(batch["source"]), + [], + ).append(str(batch["batch_id"])) + + return result + + +def _index_batch_ids_by_group( + batches: Sequence[Mapping[str, object]], +) -> dict[str, list[str]]: + """Index planned batch identifiers by period group.""" + result: dict[str, list[str]] = {} + + for batch in batches: + result.setdefault( + str(batch["group_id"]), + [], + ).append(str(batch["batch_id"])) + + return result diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index ea32cbe..fe9a56a 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -1,108 +1,22 @@ -"""Plan auxiliary electricity-demand acquisition.""" +"""Write the compiled advanced electricity-demand execution plan.""" import json -from collections.abc import Mapping, Sequence from pathlib import Path from typing import TYPE_CHECKING, Any import pandas as pd -from cleaning.advanced.planning.requirements import ( - build_auxiliary_acquisition_requirements, -) -from cleaning.advanced.planning.source_requests import ( - build_auxiliary_source_batches, - build_auxiliary_source_requests, -) +from cleaning.advanced.planning.execution import build_advanced_execution_plan if TYPE_CHECKING: snakemake: Any -def build_auxiliary_acquisition_plan( +def write_advanced_execution_plan( *, - fill_plan: pd.DataFrame, - gap_filling_config: Mapping[str, Any], - source_names: Sequence[str], -) -> dict[str, object]: - """Build a JSON-serializable auxiliary acquisition plan.""" - if gap_filling_config["mode"] != "advanced": - return { - "active_rule_names": [], - "batches": [], - } - - if fill_plan.empty: - return { - "active_rule_names": [], - "batches": [], - } - - advanced = gap_filling_config["advanced"] - - active_rule_names = set( - fill_plan["rule_name"] - ) - - unknown_rule_names = ( - active_rule_names - - set(advanced["overrides"]) - ) - - if unknown_rule_names: - raise ValueError( - "Auxiliary fill plan references unknown advanced " - f"overrides: {sorted(unknown_rule_names)}." - ) - - ordered_active_rule_names = [ - rule_name - for rule_name in advanced["overrides"] - if rule_name in active_rule_names - ] - - active_overrides = { - rule_name: advanced["overrides"][rule_name] - for rule_name in ordered_active_rule_names - } - - requirements = build_auxiliary_acquisition_requirements( - overrides=active_overrides, - basic_rules=gap_filling_config["basic"]["rules"], - basic_cleaning_enabled=( - advanced["auxiliary_data"] - ["basic_cleaning"] - ["enabled"] - ), - ) - - requests = build_auxiliary_source_requests( - requirements, - source_names=source_names, - ) - - batches = build_auxiliary_source_batches( - requests - ) - - return { - "active_rule_names": ordered_active_rule_names, - "batches": [ - { - **batch, - "start": batch["start"].isoformat(), - "end": batch["end"].isoformat(), - } - for batch in batches - ], - } - - -def write_auxiliary_acquisition_plan( - *, - plan: dict[str, list[dict[str, object]]], + plan: dict[str, object], output_path: str | Path, ) -> None: - """Write the auxiliary acquisition plan as JSON.""" + """Write the advanced execution plan as JSON.""" output_path = Path(output_path) output_path.parent.mkdir( parents=True, @@ -125,13 +39,13 @@ def write_auxiliary_acquisition_plan( snakemake.input.fill_plan ) - plan = build_auxiliary_acquisition_plan( + plan = build_advanced_execution_plan( fill_plan=fill_plan, gap_filling_config=snakemake.params.gap_filling, source_names=snakemake.params.source_names, ) - write_auxiliary_acquisition_plan( + write_advanced_execution_plan( plan=plan, output_path=snakemake.output.plan, ) From bb22bb9262db33c2696a9a61f37c4015cff182bf Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 10:33:47 +0200 Subject: [PATCH 056/241] Stripped python dependencies (local) from advanced.smk --- workflow/rules/advanced.smk | 174 ++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index baf2cbf..3e139db 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,13 +1,5 @@ import json -from cleaning.advanced.planning.selection import ( - get_auxiliary_batch, - get_auxiliary_group_batches, - get_auxiliary_group_ids, - get_required_auxiliary_group_ids, - get_source_batch_ids, -) - def _read_plan_file(plan_path): with open(plan_path, encoding="utf-8") as file: @@ -15,8 +7,8 @@ def _read_plan_file(plan_path): def _read_auxiliary_plan(_wildcards=None): - """Read the resolved auxiliary acquisition plan.""" - plan_path = ( + """Read the resolved advanced execution plan.""" + plan_file = ( checkpoints .plan_auxiliary_data .get() @@ -24,11 +16,12 @@ def _read_auxiliary_plan(_wildcards=None): .plan ) - return _read_plan_file(plan_path) + with plan_file.open() as file: + return json.load(file) def auxiliary_acquisition_plan(_wildcards): - """Return the acquisition plan after the checkpoint completes.""" + """Return the execution plan after the checkpoint completes.""" return ( checkpoints .plan_auxiliary_data @@ -38,26 +31,54 @@ def auxiliary_acquisition_plan(_wildcards): ) +def _get_batch( + plan, + *, + batch_id, + source=None, +): + """Return one batch from the compiled execution plan.""" + matches = [ + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == batch_id + and ( + source is None + or batch["source"] == source + ) + ) + ] + + if len(matches) != 1: + source_text = ( + f" for source {source!r}" + if source is not None + else "" + ) + raise ValueError( + "Expected exactly one auxiliary batch " + f"{batch_id!r}{source_text}, found {len(matches)}." + ) + + return matches[0] + + def get_auxiliary_entsoe_batch( wildcards, input, ) -> dict: """Return the ENTSO-E batch for this job.""" - return get_auxiliary_batch( + return _get_batch( _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="entsoe_api", ) -def auxiliary_entsoe_outputs(wildcards): - """Return all ENTSO-E outputs required by the acquisition plan.""" - plan = _read_auxiliary_plan(wildcards) - - batch_ids = get_source_batch_ids( - plan, - source="entsoe_api", - ) +def auxiliary_entsoe_outputs(_wildcards): + """Return all ENTSO-E outputs required by the execution plan.""" + plan = _read_auxiliary_plan() return [ ( @@ -65,7 +86,9 @@ def auxiliary_entsoe_outputs(wildcards): "auxiliary/entsoe_api/" f"{batch_id}.parquet" ) - for batch_id in batch_ids + for batch_id in plan[ + "batch_ids_by_source" + ].get("entsoe_api", []) ] @@ -74,21 +97,16 @@ def get_auxiliary_opsd_batch( input, ) -> dict: """Return the OPSD batch for this job.""" - return get_auxiliary_batch( + return _get_batch( _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="opsd_api", ) -def auxiliary_opsd_outputs(wildcards): - """Return all OPSD outputs required by the acquisition plan.""" - plan = _read_auxiliary_plan(wildcards) - - batch_ids = get_source_batch_ids( - plan, - source="opsd_api", - ) +def auxiliary_opsd_outputs(_wildcards): + """Return all OPSD outputs required by the execution plan.""" + plan = _read_auxiliary_plan() return [ ( @@ -96,7 +114,9 @@ def auxiliary_opsd_outputs(wildcards): "auxiliary/opsd_api/" f"{batch_id}.parquet" ) - for batch_id in batch_ids + for batch_id in plan[ + "batch_ids_by_source" + ].get("opsd_api", []) ] @@ -105,7 +125,7 @@ def get_auxiliary_neso_batch( input, ) -> dict: """Return the NESO batch for this job.""" - return get_auxiliary_batch( + return _get_batch( _read_plan_file(input.plan), batch_id=wildcards.batch_id, source="neso", @@ -114,36 +134,26 @@ def get_auxiliary_neso_batch( def auxiliary_neso_raw_files(wildcards): """Return annual NESO files required by one auxiliary batch.""" - plan = _read_auxiliary_plan(wildcards) + plan = _read_auxiliary_plan() - batch = get_auxiliary_batch( + batch = _get_batch( plan, batch_id=wildcards.batch_id, source="neso", ) - years = _years_in_period( - batch["start"], - batch["end"], - ) - return [ ( "/automatic/neso/" f"historic_demand_{year}.csv" ) - for year in years + for year in batch["years"] ] -def auxiliary_neso_outputs(wildcards): - """Return all NESO outputs required by the acquisition plan.""" - plan = _read_auxiliary_plan(wildcards) - - batch_ids = get_source_batch_ids( - plan, - source="neso", - ) +def auxiliary_neso_outputs(_wildcards): + """Return all NESO outputs required by the execution plan.""" + plan = _read_auxiliary_plan() return [ ( @@ -151,18 +161,27 @@ def auxiliary_neso_outputs(wildcards): "auxiliary/neso/" f"{batch_id}.parquet" ) - for batch_id in batch_ids + for batch_id in plan[ + "batch_ids_by_source" + ].get("neso", []) ] def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" - plan = _read_auxiliary_plan(wildcards) + plan = _read_auxiliary_plan() - batches = get_auxiliary_group_batches( - plan, - group_id=wildcards.group_id, - ) + batch_ids = plan["groups"][ + wildcards.group_id + ] + + batches = [ + _get_batch( + plan, + batch_id=batch_id, + ) + for batch_id in batch_ids + ] return [ ( @@ -174,11 +193,9 @@ def auxiliary_group_source_files(wildcards): ] -def auxiliary_combined_outputs(wildcards): +def auxiliary_combined_outputs(_wildcards): """Return all combined auxiliary group outputs.""" - plan = _read_auxiliary_plan(wildcards) - - group_ids = get_auxiliary_group_ids(plan) + plan = _read_auxiliary_plan() return [ ( @@ -186,22 +203,17 @@ def auxiliary_combined_outputs(wildcards): "auxiliary/combined/" f"{group_id}.parquet" ) - for group_id in group_ids + for group_id in plan["groups"] ] def auxiliary_rule_cleaned_files(wildcards): """Return cleaned auxiliary files required by one advanced override.""" - plan = _read_auxiliary_plan(wildcards) + plan = _read_auxiliary_plan() - override = config["gap_filling"]["advanced"]["overrides"][ + group_ids = plan["rules"][ wildcards.rule_name - ] - - group_ids = get_required_auxiliary_group_ids( - plan, - override=override, - ) + ]["required_group_ids"] return [ ( @@ -213,19 +225,9 @@ def auxiliary_rule_cleaned_files(wildcards): ] -def advanced_constructed_profiles(wildcards): +def advanced_constructed_profiles(_wildcards): """Return constructed profiles required by active advanced overrides.""" - plan = _read_auxiliary_plan(wildcards) - - active_rule_names = set( - plan["active_rule_names"] - ) - - overrides = ( - config["gap_filling"] - ["advanced"] - ["overrides"] - ) + plan = _read_auxiliary_plan() return [ ( @@ -233,11 +235,9 @@ def advanced_constructed_profiles(wildcards): "auxiliary/constructed/" f"{rule_name}.parquet" ) - for rule_name, override in overrides.items() - if ( - rule_name in active_rule_names - and override["method"] == "construct_from_sources" - ) + for rule_name in plan[ + "constructed_profile_rule_names" + ] ] @@ -394,6 +394,7 @@ rule prepare_auxiliary_load_opsd: script: "../scripts/prepare_load_opsd.py" + rule prepare_auxiliary_load_neso: input: plan=auxiliary_acquisition_plan, @@ -505,6 +506,7 @@ rule clean_auxiliary_data: script: "../scripts/clean_auxiliary_data.py" + rule construct_auxiliary_profile: input: sources=auxiliary_rule_cleaned_files, @@ -561,4 +563,4 @@ rule apply_advanced_overrides: message: "Apply advanced electricity-demand overrides." script: - "../scripts/apply_advanced_overrides.py" \ No newline at end of file + "../scripts/apply_advanced_overrides.py" From 3cde5bd5fb9e73e357e8ea94d8291d72ab899026 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 10:54:34 +0200 Subject: [PATCH 057/241] Implemented manifest helper to reduce local python dependencies --- workflow/rules/advanced.smk | 156 +----------------- workflow/scripts/apply_advanced_overrides.py | 31 +--- .../cleaning/advanced/planning/manifest.py | 98 +++++++++++ .../scripts/construct_auxiliary_profile.py | 10 +- workflow/scripts/download_load_entsoe_api.py | 30 +++- workflow/scripts/prepare_load_neso.py | 28 +++- workflow/scripts/prepare_load_opsd.py | 33 +++- 7 files changed, 202 insertions(+), 184 deletions(-) create mode 100644 workflow/scripts/cleaning/advanced/planning/manifest.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 3e139db..7820454 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,11 +1,6 @@ import json -def _read_plan_file(plan_path): - with open(plan_path, encoding="utf-8") as file: - return json.load(file) - - def _read_auxiliary_plan(_wildcards=None): """Read the resolved advanced execution plan.""" plan_file = ( @@ -31,51 +26,6 @@ def auxiliary_acquisition_plan(_wildcards): ) -def _get_batch( - plan, - *, - batch_id, - source=None, -): - """Return one batch from the compiled execution plan.""" - matches = [ - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == batch_id - and ( - source is None - or batch["source"] == source - ) - ) - ] - - if len(matches) != 1: - source_text = ( - f" for source {source!r}" - if source is not None - else "" - ) - raise ValueError( - "Expected exactly one auxiliary batch " - f"{batch_id!r}{source_text}, found {len(matches)}." - ) - - return matches[0] - - -def get_auxiliary_entsoe_batch( - wildcards, - input, -) -> dict: - """Return the ENTSO-E batch for this job.""" - return _get_batch( - _read_plan_file(input.plan), - batch_id=wildcards.batch_id, - source="entsoe_api", - ) - - def auxiliary_entsoe_outputs(_wildcards): """Return all ENTSO-E outputs required by the execution plan.""" plan = _read_auxiliary_plan() @@ -92,18 +42,6 @@ def auxiliary_entsoe_outputs(_wildcards): ] -def get_auxiliary_opsd_batch( - wildcards, - input, -) -> dict: - """Return the OPSD batch for this job.""" - return _get_batch( - _read_plan_file(input.plan), - batch_id=wildcards.batch_id, - source="opsd_api", - ) - - def auxiliary_opsd_outputs(_wildcards): """Return all OPSD outputs required by the execution plan.""" plan = _read_auxiliary_plan() @@ -120,26 +58,17 @@ def auxiliary_opsd_outputs(_wildcards): ] -def get_auxiliary_neso_batch( - wildcards, - input, -) -> dict: - """Return the NESO batch for this job.""" - return _get_batch( - _read_plan_file(input.plan), - batch_id=wildcards.batch_id, - source="neso", - ) - - def auxiliary_neso_raw_files(wildcards): """Return annual NESO files required by one auxiliary batch.""" plan = _read_auxiliary_plan() - batch = _get_batch( - plan, - batch_id=wildcards.batch_id, - source="neso", + batch = next( + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == wildcards.batch_id + and batch["source"] == "neso" + ) ) return [ @@ -320,25 +249,6 @@ rule download_auxiliary_load_entsoe_api: "auxiliary/entsoe_api/" "{batch_id}.parquet" ), - params: - temporal_start=lambda wildcards, input: ( - get_auxiliary_entsoe_batch( - wildcards, - input, - )["start"] - ), - temporal_end=lambda wildcards, input: ( - get_auxiliary_entsoe_batch( - wildcards, - input, - )["end"] - ), - country_codes=lambda wildcards, input: ( - get_auxiliary_entsoe_batch( - wildcards, - input, - )["countries"] - ), log: ( "/auxiliary/" @@ -363,25 +273,6 @@ rule prepare_auxiliary_load_opsd: "auxiliary/opsd_api/" "{batch_id}.parquet" ), - params: - start=lambda wildcards, input: ( - get_auxiliary_opsd_batch( - wildcards, - input, - )["start"] - ), - end=lambda wildcards, input: ( - get_auxiliary_opsd_batch( - wildcards, - input, - )["end"] - ), - country_codes=lambda wildcards, input: ( - get_auxiliary_opsd_batch( - wildcards, - input, - )["countries"] - ), log: ( "/auxiliary/" @@ -405,25 +296,6 @@ rule prepare_auxiliary_load_neso: "auxiliary/neso/" "{batch_id}.parquet" ), - params: - start=lambda wildcards, input: ( - get_auxiliary_neso_batch( - wildcards, - input, - )["start"] - ), - end=lambda wildcards, input: ( - get_auxiliary_neso_batch( - wildcards, - input, - )["end"] - ), - country_codes=lambda wildcards, input: ( - get_auxiliary_neso_batch( - wildcards, - input, - )["countries"] - ), log: ( "/auxiliary/" @@ -509,6 +381,7 @@ rule clean_auxiliary_data: rule construct_auxiliary_profile: input: + plan=auxiliary_acquisition_plan, sources=auxiliary_rule_cleaned_files, output: profile=( @@ -516,13 +389,6 @@ rule construct_auxiliary_profile: "auxiliary/constructed/" "{rule_name}.parquet" ), - params: - override=lambda wildcards: ( - config["gap_filling"] - ["advanced"] - ["overrides"] - [wildcards.rule_name] - ), conda: "../envs/module.yaml" message: @@ -552,12 +418,6 @@ rule apply_advanced_overrides: "/automatic/" "load_advanced_cleaning_method.parquet" ), - params: - overrides=( - config["gap_filling"] - ["advanced"] - ["overrides"] - ), conda: "../envs/module.yaml" message: diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index c7a9bd8..ea3313a 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -3,6 +3,10 @@ import pandas as pd from cleaning.advanced.apply import apply_auxiliary_fill_rules +from cleaning.advanced.planning.manifest import ( + get_active_overrides, + load_execution_plan, +) load = pd.read_parquet( snakemake.input.demand @@ -12,33 +16,14 @@ snakemake.input.cleaning_method ) -with open( - snakemake.input.plan, - encoding="utf-8", -) as file: - plan = json.load(file) - -active_rule_names = set( - plan["active_rule_names"] +plan = load_execution_plan( + snakemake.input.plan ) -unknown_rule_names = ( - active_rule_names - - set(snakemake.params.overrides) +active_overrides = get_active_overrides( + plan ) -if unknown_rule_names: - raise ValueError( - "Auxiliary acquisition plan references unknown advanced " - f"overrides: {sorted(unknown_rule_names)}." - ) - -active_overrides = { - rule_name: override - for rule_name, override in snakemake.params.overrides.items() - if rule_name in active_rule_names -} - profiles = { Path(path).stem: pd.read_parquet(path).iloc[:, 0] for path in snakemake.input.profiles diff --git a/workflow/scripts/cleaning/advanced/planning/manifest.py b/workflow/scripts/cleaning/advanced/planning/manifest.py new file mode 100644 index 0000000..cbaa704 --- /dev/null +++ b/workflow/scripts/cleaning/advanced/planning/manifest.py @@ -0,0 +1,98 @@ +"""Read and query compiled advanced execution manifests.""" + +from __future__ import annotations + +import json +from collections.abc import Mapping +from pathlib import Path +from typing import Any + + +def load_execution_plan( + path: str | Path, +) -> dict[str, Any]: + """Load one compiled advanced execution plan.""" + with open(path, encoding="utf-8") as file: + plan = json.load(file) + + if not isinstance(plan, dict): + raise TypeError( + "Advanced execution plan must contain a JSON object." + ) + + return plan + + +def get_batch( + plan: Mapping[str, Any], + *, + batch_id: str, + source: str | None = None, +) -> Mapping[str, Any]: + """Return exactly one compiled auxiliary batch.""" + matches = [ + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == batch_id + and ( + source is None + or batch["source"] == source + ) + ) + ] + + if len(matches) != 1: + source_text = ( + f" for source {source!r}" + if source is not None + else "" + ) + raise ValueError( + "Expected exactly one auxiliary batch " + f"{batch_id!r}{source_text}, found {len(matches)}." + ) + + return matches[0] + + +def get_active_overrides( + plan: Mapping[str, Any], +) -> dict[str, Mapping[str, Any]]: + """Return active overrides in compiled execution order.""" + rule_names = plan["active_rule_names"] + rules = plan["rules"] + + unknown_rule_names = [ + rule_name + for rule_name in rule_names + if rule_name not in rules + ] + + if unknown_rule_names: + raise ValueError( + "Advanced execution plan references unknown compiled " + f"rules: {unknown_rule_names}." + ) + + return { + rule_name: rules[rule_name]["override"] + for rule_name in rule_names + } + + +def get_rule_override( + plan: Mapping[str, Any], + *, + rule_name: str, +) -> Mapping[str, Any]: + """Return one active rule's compiled override definition.""" + try: + rule = plan["rules"][rule_name] + except KeyError as error: + raise ValueError( + "Advanced execution plan does not contain active rule " + f"{rule_name!r}." + ) from error + + return rule["override"] diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index f02fa56..cc80aac 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,5 +1,6 @@ import pandas as pd from cleaning.advanced.methods.construct_from_sources import construct_from_sources +from cleaning.advanced.planning.manifest import get_rule_override, load_execution_plan from common.time import build_hourly_index loads = [ @@ -17,7 +18,14 @@ for load in loads[1:]: auxiliary = auxiliary.combine_first(load) -override = snakemake.params.override +plan = load_execution_plan( + snakemake.input.plan +) + +override = get_rule_override( + plan, + rule_name=snakemake.wildcards.rule_name, +) target_index = build_hourly_index( start=override["start"], diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index b42c8cb..e6fb430 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -1,7 +1,7 @@ """Download electricity load data from ENTSO-E using the entsoe-py library.""" import sys -from concurrent.futures import ThreadPoolExecutor, as_completed +from concurrent.futures import ThreadPoolExecutor, as_completed #TODO: Check this isnt overruled by snakemake assigning one thread from time import perf_counter from typing import TYPE_CHECKING, Any from warnings import warn @@ -9,6 +9,7 @@ import pandas as pd import pycountry import yaml +from cleaning.advanced.planning.manifest import get_batch, load_execution_plan from common.time import as_utc_timestamp, build_hourly_index from entsoe import EntsoePandasClient from entsoe.exceptions import NoMatchingDataError @@ -238,10 +239,31 @@ def main( buffering=1, ) + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="entsoe_api", + ) + start = batch["start"] + end = batch["end"] + country_codes = batch["countries"] + else: + start = snakemake.params.temporal_start + end = snakemake.params.temporal_end + country_codes = snakemake.params.country_codes + main( - start=snakemake.params.temporal_start, - end=snakemake.params.temporal_end, - country_codes=snakemake.params.country_codes, + start=start, + end=end, + country_codes=country_codes, token=snakemake.input.token_entsoe, output_load=snakemake.output.load, ) diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 7df0047..c2735f3 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Iterable import pandas as pd +from cleaning.advanced.planning.manifest import get_batch, load_execution_plan from cleaning.sources.neso import add_utc_timestamps from common.time import build_hourly_index @@ -240,13 +241,34 @@ def prepare_load_neso( format="%(levelname)s: %(message)s", ) + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="neso", + ) + temporal_start = batch["start"] + temporal_end = batch["end"] + countries = batch["countries"] + else: + temporal_start = snakemake.params.start + temporal_end = snakemake.params.end + countries = snakemake.params.country_codes + prepare_load_neso( input_paths=[ Path(path) for path in snakemake.input.annual_files ], output_path=snakemake.output.load, - temporal_start=snakemake.params.start, - temporal_end=snakemake.params.end, - countries=snakemake.params.country_codes, + temporal_start=temporal_start, + temporal_end=temporal_end, + countries=countries, ) diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index e473520..b106c41 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -1,16 +1,16 @@ """Prepare electricity-demand data downloaded from OPSD.""" import sys +from time import perf_counter from typing import TYPE_CHECKING, Any from warnings import warn import pandas as pd import pycountry +from cleaning.advanced.planning.manifest import get_batch, load_execution_plan from common.schemas import LoadENTSOE from common.time import as_utc_timestamp, build_hourly_index -from time import perf_counter - if TYPE_CHECKING: snakemake: Any @@ -116,10 +116,33 @@ def main( buffering=1, ) + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="opsd_api", + ) + start = batch["start"] + end = batch["end"] + country_codes = batch["countries"] + else: + start = snakemake.params.start + end = snakemake.params.end + country_codes = list( + snakemake.params.country_codes + ) + main( path_raw_load=snakemake.input.load, output_load=snakemake.output.load, - start=snakemake.params.start, - end=snakemake.params.end, - country_codes=list(snakemake.params.country_codes), + start=start, + end=end, + country_codes=country_codes, ) From 9bd8eff35f47940b4972b13aa4d93ec81af111e8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 10:59:04 +0200 Subject: [PATCH 058/241] isolate snakemake dispatch environment --- workflow/Snakefile | 8 -------- 1 file changed, 8 deletions(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index da9187c..60b8180 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -1,5 +1,3 @@ -import sys - import yaml from snakemake.utils import min_version, validate @@ -30,12 +28,6 @@ with open(workflow.source_path("internal/settings.yaml"), "r") as f: internal = yaml.safe_load(f) -# Make reusable workflow Python packages available to rule files. -scripts_path = workflow.source_path("scripts") - -if scripts_path not in sys.path: - sys.path.insert(0, scripts_path) - # Load python files in scripts that are used in other scripts, but not run itself. # This is necessary for integrating the snakemake module via reference to GitHub. workflow.source_path("scripts/common/plots.py") From 421048ee4fc110a949c4169f0c9f13db6f5c83af Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 11:23:29 +0200 Subject: [PATCH 059/241] Updated pixi toml and validated that code does not depend on local libraries. --- pixi.lock | 732 ++++++++++++++++++ pixi.toml | 6 + .../test_workflow_environment_boundary.py | 45 ++ workflow/envs/module.linux-64.pin.txt | 167 ++-- workflow/envs/module.osx-arm64.pin.txt | 72 +- workflow/envs/module.win-64.pin.txt | 120 +-- workflow/envs/module.yaml | 6 + 7 files changed, 1000 insertions(+), 148 deletions(-) create mode 100644 tests/unit/test_workflow_environment_boundary.py diff --git a/pixi.lock b/pixi.lock index 6fba675..90a5103 100644 --- a/pixi.lock +++ b/pixi.lock @@ -840,6 +840,7 @@ environments: packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.30-hec5e740_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hfd43aa1_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda @@ -872,6 +873,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.0.2-h07f6e7f_1.conda @@ -885,6 +889,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_hdf9ad27_105.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda @@ -908,10 +914,15 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda @@ -937,7 +948,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.28.0-h26d7fe4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.28.0-ha262f82_0.conda @@ -946,6 +960,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda @@ -953,7 +969,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda @@ -971,6 +989,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda @@ -979,9 +998,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py312h0a2e395_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda @@ -1004,9 +1026,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py312h9211aeb_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.11-py312hd177ed6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1024,14 +1048,30 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.3-ha02ee65_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.3-ha02ee65_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.3-hb03c661_0.conda @@ -1080,9 +1120,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda @@ -1104,9 +1147,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda @@ -1154,9 +1199,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda @@ -1178,9 +1226,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda @@ -1320,6 +1370,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.9.4-hb7217d7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py312h3093aea_1.conda @@ -1417,9 +1468,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda @@ -1442,9 +1496,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda @@ -1483,6 +1539,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py312he06e257_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.0.2-hf4da5c8_1.conda @@ -1493,6 +1550,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.12.2-h5a68840_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.3-h232476a_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.3-nompi_h2b43c12_105.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda @@ -1513,6 +1572,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda @@ -1574,6 +1634,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda @@ -1597,9 +1658,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.6.1-py312h6f27134_9.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.3.11-py312he4a2ebf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2023.09.01-hd3b24a8_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda @@ -1765,6 +1828,19 @@ packages: - _openmp_mutex >=4.5 size: 28948 timestamp: 1770939786096 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda + sha256: cf93ca0f1f107e95a35969a4622684e08fcb8cf37f8cf4a1e9e424828386c921 + md5: 8904e09bda369377b3dd07e2ac828c5d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + license_family: LGPL + run_exports: + weak: + - alsa-lib >=1.2.16.1,<1.3.0a0 + size: 592377 + timestamp: 1781521980743 - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.5.0-py310hd8a072f_1.conda noarch: python sha256: cf1cf3d0fa59fe0ab6bc3af722d820c1a85a9233c786f614f377c651fec6a7f9 @@ -2585,6 +2661,20 @@ packages: run_exports: {} size: 623770 timestamp: 1771855837505 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 + sha256: 8f5f995699a2d9dbdd62c61385bfeeb57c82a681a7c8c5313c395aa0ccab68a5 + md5: ecfff944ba3960ecb334b9a2663d708d + depends: + - expat >=2.4.2,<3.0a0 + - libgcc-ng >=9.4.0 + - libglib >=2.70.2,<3.0a0 + license: GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - dbus >=1.13.6,<2.0a0 + size: 618596 + timestamp: 1640112124844 - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 md5: ce96f2f470d39bd96ce03945af92e280 @@ -2611,6 +2701,20 @@ packages: license_family: MIT size: 2842115 timestamp: 1780390153580 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 + md5: bfd56492d8346d669010eccafe0ba058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - double-conversion >=3.3.1,<3.4.0a0 + size: 69544 + timestamp: 1739569648873 - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda sha256: a5b51e491fec22bcc1765f5b2c8fff8a97428e9a5a7ee6730095fb9d091b0747 md5: 057083b06ccf1c2778344b6dabace38b @@ -2633,6 +2737,20 @@ packages: license_family: MIT size: 411735 timestamp: 1758743520805 +- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda + sha256: bc1e8177a4ebbbfcda98b6f4a1575f3337e69f0d2f1025a84570533ca27b89eb + md5: e211a78613b9d50a096644b97cede8f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat 2.8.1 hecca717_1 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libexpat >=2.8.1,<3.0a0 + size: 147797 + timestamp: 1781203608158 - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda sha256: 319d05cd6d63c0b1fe5d155c42e1f97c2d1c50f35269e321cf627f9055638904 md5: 5d169c5bc70f98251c4802caf26cad53 @@ -2895,6 +3013,20 @@ packages: license_family: BSD size: 2008937 timestamp: 1775514521503 +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + sha256: 7fa3b6a9c081fa3e545573152a788d061a0a0ba57df7251cc0f4f75225fc93e7 + md5: f9fe2984587fa8235a6af6004760cd18 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: LGPL-2.0-or-later + license_family: LGPL + run_exports: + weak: + - graphite2 >=1.3.15,<2.0a0 + size: 102835 + timestamp: 1786118485753 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda sha256: 885fa7d1d7e2ad9ed0a700ee0d81ceb49de278253082d517959b22d6336eecce md5: cf09e9fc938518e91d0706572cadf17a @@ -2996,6 +3128,27 @@ packages: license_family: LGPL size: 318312 timestamp: 1686545244763 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda + sha256: 9d33201d3e12a61d4ea4b1252a3468afb18b11a418f095dceffdf09bc6792f59 + md5: 347cb348bfc8d77062daee11c326e518 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - freetype >=2.13.3,<3.0a0 + - graphite2 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.84.0,<3.0a0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - harfbuzz >=11.0.0,<12.0a0 + size: 1720702 + timestamp: 1743082646624 - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda sha256: da9901aa1e20cbc2369fda212039b294dd02bce95f005539bab840b7310bf7d0 md5: 21ee4640b7c2d94e584349fa12b29b9a @@ -3472,6 +3625,36 @@ packages: - libcblas >=3.11.0,<4.0a0 size: 18778 timestamp: 1779859107964 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda + sha256: 6b256303e6a1db40e4ce61a5bc69d0e78bf8946070309b665555248667aa23f3 + md5: feec6e0f138982e3f7b1ac372fb396cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm19 >=19.1.7,<19.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libclang-cpp19.1 >=19.1.7,<19.2.0a0 + size: 20824232 + timestamp: 1776984079535 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda + sha256: e6c0123b888d6abf03c66c52ed89f9de1798dde930c5fd558774f26e994afbc6 + md5: 327c78a8ce710782425a89df851392f7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.0,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libclang13 >=21.1.0 + size: 12358102 + timestamp: 1757383373129 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 md5: c965a5aa0d5c1c37ffc62dff36e28400 @@ -3498,6 +3681,22 @@ packages: license_family: Apache size: 4518030 timestamp: 1770902209173 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 + md5: d4a250da4737ee127fb1fa6452a9002e + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libcups >=2.3.3,<2.4.0a0 + size: 4523621 + timestamp: 1749905341688 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_0.conda sha256: 93ab25f02666eb8696fa70d9c920f2e3b370742ee17e2758705038a72e11147f md5: dcd79cabd5d435f48d75db3d81cb5874 @@ -3556,6 +3755,20 @@ packages: license_family: MIT size: 311505 timestamp: 1778975798004 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda + sha256: b4e74db8ed1d5f8b6c9edf9582953c063650d677b299e90585d00f127644eb87 + md5: 65514a7ba857e9dbffbffd641f293e33 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libdrm >=2.4.127,<2.5.0a0 + size: 311002 + timestamp: 1785984394604 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 md5: c277e0a4d549b03ac1e9d6cbbe3d017b @@ -3578,6 +3791,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + run_exports: {} size: 46500 timestamp: 1779728188901 - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda @@ -4015,6 +4229,7 @@ packages: - libglvnd 1.7.0 ha4b6fd6_3 - libglx 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + run_exports: {} size: 133469 timestamp: 1779728207669 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda @@ -4066,6 +4281,7 @@ packages: depends: - __glibc >=2.17,<3.0.a0 license: LicenseRef-libglvnd + run_exports: {} size: 133586 timestamp: 1779728183422 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda @@ -4076,6 +4292,7 @@ packages: - libglvnd 1.7.0 ha4b6fd6_3 - xorg-libx11 >=1.8.13,<2.0a0 license: LicenseRef-libglvnd + run_exports: {} size: 76586 timestamp: 1779728199059 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda @@ -4238,6 +4455,40 @@ packages: license_family: BSD size: 18824 timestamp: 1779859122364 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda + sha256: 22909d64038bdc87de61311c4ae615dc574a548a7340b963bb7c9eb61b191669 + md5: 6d2362046dce932eefbdeb0540de0c38 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libxml2 >=2.13.5,<2.14.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libllvm19 >=19.1.7,<19.2.0a0 + size: 40143643 + timestamp: 1737789465087 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda + sha256: d190f1bf322149321890908a534441ca2213a9a96c59819da6cabf2c5b474115 + md5: 9ad637a7ac380c442be142dfb0b1b955 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 >=2.13.8,<2.14.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libllvm21 >=21.1.0,<21.2.0a0 + size: 44363060 + timestamp: 1756291822911 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d md5: b88d90cad08e6bc8ad540cb310a761fb @@ -4433,6 +4684,16 @@ packages: - libopenblas >=0.3.33,<1.0a0 size: 5931919 timestamp: 1776993658641 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + sha256: 90777039b48529283df5f16383fc399866024257a8bd93de583f4730db1ab30a + md5: c2bd8055a2e2dce7a7f32cfd02101fb6 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_3 + license: LicenseRef-libglvnd + run_exports: {} + size: 51767 + timestamp: 1779728204026 - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda build_number: 13 sha256: 3c63b7391275cf6cf2a18d2dba3c30c16dd9d210373d206675e342b084cccdf4 @@ -4461,6 +4722,19 @@ packages: license_family: MIT size: 29147 timestamp: 1773533027610 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + sha256: addc80c69d362a9e6c40305c493139a8e9ee504b2f45a6687dbdaa9da3c6183c + md5: 35fa2b34bbced424e6976d30f5fde576 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libpciaccess >=0.19,<0.20.0a0 + size: 30070 + timestamp: 1785971678815 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 md5: eba48a68a1a2b9d3c0d9511548db85db @@ -4771,6 +5045,24 @@ packages: - libxcrypt >=4.4.36 size: 100393 timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda + sha256: 23f47e86cc1386e7f815fa9662ccedae151471862e971ea511c5c886aa723a54 + md5: 74e91c36d0eef3557915c68b6c2bef96 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libxml2 >=2.13.8,<2.14.0a0 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + run_exports: + weak: + - libxkbcommon >=1.11.0,<2.0a0 + size: 791328 + timestamp: 1754703902365 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda sha256: 046f2ff4acebd8729fac03e99c8c307dfb48b6a32894ba8c11576e78f6e76e43 md5: dc8b067e22b414172bedd8e3f03f3c95 @@ -4959,6 +5251,20 @@ packages: license_family: BSD size: 27424 timestamp: 1772445227915 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda + sha256: cdd59bb1a52b09979f11c68909d53120b2e749edd1992853a74e1604db19c8b0 + md5: 579c6a324b197594fabc9240bddf2d8b + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - pyside6 >=6.7.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 17831 + timestamp: 1777000588302 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda sha256: c7e133837376e53e6a52719c205a3067c42f05769bc3e8307417f8d817dfc63e md5: 7d499b5b6d150f133800dc3a582771c7 @@ -5063,6 +5369,35 @@ packages: license_family: MIT size: 22860519 timestamp: 1780315602311 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda + sha256: 9c2e3f9e9883e4b8d7e9e6abf7b235dc00bdcd5ef66640a360464a9f5756294d + md5: 94116b69829e90b72d566e64421e1bff + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.4.1,<4.0a0 + license: GPL-2.0-or-later + license_family: GPL + run_exports: {} + size: 616215 + timestamp: 1744124836761 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda + sha256: 274467a602944d12722f757f660ad034de6f5f5d7d2ea1b913ef6fd836c1b8ce + md5: 9802ae6d20982f42c0f5d69008988763 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - mysql-common 9.0.1 h266115a_6 + - openssl >=3.4.1,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: GPL-2.0-or-later + license_family: GPL + run_exports: {} + size: 1369369 + timestamp: 1744124916632 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda sha256: fc89f74bbe362fb29fa3c037697a89bec140b346a2469a90f7936d1d7ea4d8a3 md5: fc21868a1a5aacc937e7a18747acb8a5 @@ -5657,6 +5992,28 @@ packages: run_exports: {} size: 547934 timestamp: 1725436149519 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda + sha256: 6569b51ed41e46957e6587a705a930681375accd737c677acae883089f50a2a8 + md5: 8baf6a8672bf235afede64de7a7da1c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libclang13 >=19.1.7 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=13 + - libxml2 >=2.13.6,<2.14.0a0 + - libxslt >=1.1.39,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main 6.8.2.* + - qt6-main >=6.8.2,<6.9.0a0 + license: LGPL-3.0-only + license_family: LGPL + run_exports: {} + size: 10898802 + timestamp: 1740757230072 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e md5: 7eccb41177e15cc672e1babe9056018e @@ -5795,6 +6152,70 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 552937 timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda + sha256: 8776405bb5b256456fa1749bd26946944577f2d73b8c749f9d7c1a5c187bdca2 + md5: 4d483b12b9fc7169d112d4f7a250c05c + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.13,<1.3.0a0 + - dbus >=1.13.6,<2.0a0 + - double-conversion >=3.3.0,<3.4.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - harfbuzz >=10.2.0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang-cpp19.1 >=19.1.7,<19.2.0a0 + - libclang13 >=19.1.7 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.124,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libgcc >=13 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.82.2,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libllvm19 >=19.1.7,<19.2.0a0 + - libpng >=1.6.46,<1.7.0a0 + - libpq >=17.2,<18.0a0 + - libsqlite >=3.48.0,<4.0a0 + - libstdcxx >=13 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.5.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.7.0,<2.0a0 + - libxml2 >=2.13.5,<2.14.0a0 + - libzlib >=1.3.1,<2.0a0 + - mysql-libs >=9.0.1,<9.1.0a0 + - openssl >=3.4.0,<4.0a0 + - pcre2 >=10.44,<10.45.0a0 + - wayland >=1.23.1,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.5,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.5,<2.0a0 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.6,<1.6.0a0 + constrains: + - qt 6.8.2 + license: LGPL-3.0-only + license_family: LGPL + run_exports: + weak: + - qt6-main >=6.8.2,<6.9.0a0 + size: 51501033 + timestamp: 1738333763120 - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.11-py312hd177ed6_1.conda sha256: a2dbadde579d105c4484ae351aa792d691091b3c639e6bcdc3471629972fe7c8 md5: 246c5f31c607ecfe1ece1e8cc6ecc9c5 @@ -6215,6 +6636,22 @@ packages: license_family: MIT size: 334139 timestamp: 1773959575393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda + sha256: 6b9e182021ef3a64ab3bf788ebdab6de6775035612237c639ccafc941639eb13 + md5: b34c5559f45d8996e3bc0b6250a6cc84 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - wayland >=1.26.0,<2.0a0 + size: 340543 + timestamp: 1784249169392 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda sha256: e2b6545651aed5e7dead39b7ba3bf8c2669f194c71e89621343bd0bb321a87f1 md5: 82da729c870ada2f675689a39b4f697f @@ -6227,6 +6664,90 @@ packages: license_family: BSD size: 64997 timestamp: 1756851739706 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d + md5: fdc27cb255a7a2cc73b7919a968b48f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util >=0.4.1,<0.5.0a0 + size: 20772 + timestamp: 1750436796633 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 + md5: 4d1fc190b99912ed557a8236e958c559 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.13 + - libxcb >=1.17.0,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util-cursor >=0.1.6,<0.2.0a0 + size: 20829 + timestamp: 1763366954390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 + md5: a0901183f08b6c7107aab109733a3c91 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util-image >=0.4.0,<0.5.0a0 + size: 24551 + timestamp: 1718880534789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 + md5: ad748ccca349aec3e91743e08b5e2b50 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + size: 14314 + timestamp: 1718846569232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df + md5: 0e0cbe0564d03a99afd5fd7b362feecd + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + size: 16978 + timestamp: 1718848865819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a + md5: 608e0ef8256b81d04456e8d211eee3e8 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - xcb-util-wm >=0.4.2,<0.5.0a0 + size: 51689 + timestamp: 1718844051451 - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda sha256: 339ab0ff05170a295e59133cd0fa9a9c4ba32b6941c8a2a73484cc13f81e248a md5: 9dda9667feba914e0e80b95b82f7402b @@ -6252,6 +6773,7 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT + run_exports: {} size: 441670 timestamp: 1782027360439 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda @@ -6319,6 +6841,9 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxcomposite >=0.4.7,<1.0a0 size: 14415 timestamp: 1770044404696 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda @@ -6332,6 +6857,9 @@ packages: - xorg-libxrender >=0.9.11,<0.10.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxcursor >=1.2.3,<2.0a0 size: 32533 timestamp: 1730908305254 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda @@ -6345,6 +6873,9 @@ packages: - xorg-libxfixes >=6.0.1,<7.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxdamage >=1.1.6,<2.0a0 size: 13217 timestamp: 1727891438799 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda @@ -6383,6 +6914,9 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxfixes >=6.0.2,<7.0a0 size: 20071 timestamp: 1759282564045 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda @@ -6396,6 +6930,9 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxi >=1.8.3,<2.0a0 size: 47717 timestamp: 1779111857071 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda @@ -6422,6 +6959,9 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxrandr >=1.5.5,<2.0a0 size: 30456 timestamp: 1769445263457 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda @@ -6449,6 +6989,9 @@ packages: - xorg-libxi >=1.7.10,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxtst >=1.2.5,<2.0a0 size: 32808 timestamp: 1727964811275 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda @@ -6461,6 +7004,9 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxxf86vm >=1.1.7,<2.0a0 size: 18701 timestamp: 1769434732453 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda @@ -7873,6 +8419,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 11766 timestamp: 1745776666688 - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda @@ -7964,6 +8511,18 @@ packages: license_family: MIT size: 4431 timestamp: 1777000436203 +- conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + sha256: c686bea5de04fd6a93de31c9be18bfd24b9efe672a5e2e8692d34a35b282cbb8 + md5: f6b96a51cda3cf9a068fd748c8155ced + depends: + - pandera-base ==0.32.1 pyhcf101f3_0 + - pandas >=2.1.1 + - numpy >=1.24.4 + license: MIT + license_family: MIT + run_exports: {} + size: 4464 + timestamp: 1782916642380 - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.31.1-pyhcf101f3_0.conda sha256: d197f0ad887b9896f118e6bf9604f4ca955de49561b776e9c69b8e7cd4382cca md5: 531ff805a1551c6caaf976d577ea233c @@ -7979,6 +8538,22 @@ packages: license_family: MIT size: 230747 timestamp: 1777000436203 +- conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda + sha256: 81eb4d5f49a2f98b7dbeb60b134ecef4b90cdf21b2af8a024d5a0fe2858e914b + md5: c78daae943cb94e6e6b2eab16e16a18f + depends: + - python >=3.10 + - packaging >=20.0 + - pydantic + - typeguard + - typing_extensions + - typing_inspect >=0.6.0 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 268608 + timestamp: 1782916642380 - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda sha256: 611882f7944b467281c46644ffde6c5145d1a7730388bcde26e7e86819b0998e md5: 39894c952938276405a1bd30e4ce2caf @@ -8635,6 +9210,20 @@ packages: license_family: MIT size: 38297 timestamp: 1778779291237 +- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + sha256: 82cb76b4dd554d1da90a9e1ea5f99e24aaa4997bcd19ceef8233fc17d9da432e + md5: a61716f1640d9986d5d6de1c4d140b42 + depends: + - typing_extensions >=4.14.0 + - python >=3.10 + - python + constrains: + - pytest >=7 + license: MIT + license_family: MIT + run_exports: {} + size: 38525 + timestamp: 1785075519409 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c md5: edd329d7d3a4ab45dcf905899a7a6115 @@ -8677,6 +9266,7 @@ packages: - typing_extensions >=3.7.4 license: MIT license_family: MIT + run_exports: {} size: 14919 timestamp: 1733845966415 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda @@ -11611,6 +12201,19 @@ packages: license_family: BSD size: 27256 timestamp: 1772445397216 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda + sha256: 79d518d9556ce81ff4e55e3e947a5a8cb6b1c4a101536e86ab90c34d2c80efcf + md5: 94174d301ce2f5962197fb9b880ea8ff + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 17770 + timestamp: 1777001080046 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda sha256: c2dc997012fb8a901163cdad333ba5ba27733aa26d67a28a6501f4b4d69fcbce md5: e5d83f3ae6ada32d4e64e366a41f9ff6 @@ -13738,6 +14341,20 @@ packages: license_family: MIT size: 4022782 timestamp: 1780390190830 +- conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda + sha256: b1fee32ef36a98159f0a2a96c4e734dfc9adff73acd444940831b22c1fb6d5c0 + md5: e9a1402439c18a4e3c7a52e4246e9e1c + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - double-conversion >=3.3.1,<3.4.0a0 + size: 71355 + timestamp: 1739570178995 - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda sha256: 5fb4696e76cc9fce16223009915c14eff36cad190eca809fbb77f5a000a80cde md5: caa98e34aa51b84fc70effb79404a89d @@ -13955,6 +14572,20 @@ packages: license_family: BSD size: 2017538 timestamp: 1775514603704 +- conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda + sha256: 93a59bdf944fb6f947bd7ad2f293d5d80db3a90f8ff8dfabc591e3752141e46f + md5: 79538e7a7bc024084eda5989f73fde35 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: LGPL-2.0-or-later + license_family: LGPL + run_exports: + weak: + - graphite2 >=1.3.15,<2.0a0 + size: 98064 + timestamp: 1786118492029 - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-hac47afa_0.conda sha256: 88b6601f8edae59834b59b521e293ff3b58361dc1603240f5a8328c24e6936ad md5: ff9a9bfe791f56b0227597a7651a6af0 @@ -14011,6 +14642,27 @@ packages: license_family: LGPL size: 188688 timestamp: 1686545648050 +- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda + sha256: f1ab5960a52a11186f528249bec5ce5e43bb4c44c87ffa24334255f07c3fd4b8 + md5: b7648427f5b6797ae3904ad76e4c7f19 + depends: + - cairo >=1.18.4,<2.0a0 + - freetype >=2.13.3,<3.0a0 + - graphite2 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libglib >=2.84.0,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: MIT + license_family: MIT + run_exports: + weak: + - harfbuzz >=11.0.0,<12.0a0 + size: 1125019 + timestamp: 1743083466989 - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.2.1-h5a1b470_0.conda sha256: 55d6d483e089afe68bdbb38a003d7b76002e65341665b80f38e6ce4b494beef6 md5: 0bcbb7f911590beec914555c6b82050d @@ -14467,6 +15119,22 @@ packages: license_family: BSD size: 68443 timestamp: 1779859701498 +- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda + sha256: 1a2ead8255567347e6f9b2e941e07a1bcf3ce061d94d51f1b7d88089cd1a9552 + md5: 45a2834578a5f167258aa109af48b036 + depends: + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libclang13 >=22.1.8 + size: 30487117 + timestamp: 1781848941933 - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 sha256: 75e60fbe436ba8a11c170c89af5213e8bec0418f88b7771ab7e3d9710b70c54e md5: cd4cc2d0c610c8cb5419ccc979f2d6ce @@ -15733,6 +16401,20 @@ packages: license_family: BSD size: 30022 timestamp: 1772445159549 +- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda + sha256: a1864fb26f541716aff7c194d6399b17ec90ee4fded2f74034fe4db7c930c60d + md5: 779fa0e5531694e039385f739ceb88b4 + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - pyside6 >=6.7.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 18066 + timestamp: 1777000842638 - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda sha256: 539ca0eac473a1b7f9da1071ddcd6fe9a3bdd9e51eab0f1c498e6345c1898e8b md5: 3752482b0df88d7a08a0791f906e87ae @@ -16431,6 +17113,25 @@ packages: license_family: BSD size: 175241 timestamp: 1778848456926 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda + sha256: 808204eb911e20f4e58b0b6a90e424410a66668a57c08e2e6466b23137cb4f90 + md5: 52a05ba3f802633cb2234bb3edc45888 + depends: + - libclang13 >=20.1.2 + - libxml2 >=2.13.7,<2.14.0a0 + - libxslt >=1.1.39,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main 6.9.0.* + - qt6-main >=6.9.0,<6.10.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.42.34438 + license: LGPL-3.0-only + license_family: LGPL + run_exports: {} + size: 8918147 + timestamp: 1743761403797 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda sha256: a02b446d8b7b167b61733a3de3be5de1342250403e72a63b18dac89e99e6180e md5: 2956dff38eb9f8332ad4caeba941cfe7 @@ -16578,6 +17279,37 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 1377020 timestamp: 1720814433486 +- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda + sha256: 84ff37de3c72a612dfbf9b317d5501231a87582f2edf4959ff706b84b4aa9246 + md5: d92e5a0de3263315551d54d5574f5193 + depends: + - double-conversion >=3.3.1,<3.4.0a0 + - harfbuzz >=11.0.0,<12.0a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang13 >=20.1.1 + - libglib >=2.84.0,<3.0a0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libsqlite >=3.49.1,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + - libwebp-base >=1.5.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.1,<4.0a0 + - pcre2 >=10.44,<10.45.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.42.34438 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.9.0 + license: LGPL-3.0-only + license_family: LGPL + run_exports: + weak: + - qt6-main >=6.9.0,<6.10.0a0 + size: 94992566 + timestamp: 1743635306726 - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.3.11-py312he4a2ebf_1.conda sha256: aba65242801a337fb8d37d8ac71bc78f9c26adb8b02b9369daa64008c4271315 md5: e301914ba7afd52aef42e85d29a0f3ab diff --git a/pixi.toml b/pixi.toml index e512f3d..44d501d 100644 --- a/pixi.toml +++ b/pixi.toml @@ -33,6 +33,12 @@ dask = ">=2026.6.0" numpy = ">=1.26.4" gregor = ">=0.1.0" cmap = ">=0.7.2" +pandas = ">=3.0.3" +geopandas = ">=1.1.4" +matplotlib = ">=3.10.9" +rioxarray = ">=0.20.0" +pandera = ">=0.31.1" +pyyaml = ">=6.0.3" [environments] module = { features = ["module"], no-default-feature = true } diff --git a/tests/unit/test_workflow_environment_boundary.py b/tests/unit/test_workflow_environment_boundary.py new file mode 100644 index 0000000..3a4773b --- /dev/null +++ b/tests/unit/test_workflow_environment_boundary.py @@ -0,0 +1,45 @@ +from pathlib import Path +import re + + +FORBIDDEN_PATTERNS = ( + r"\bsys\.path\b", + r"^\s*from\s+cleaning\b", + r"^\s*import\s+cleaning\b", + r"^\s*from\s+common\b", + r"^\s*import\s+common\b", + r"^\s*import\s+pandas\b", + r"^\s*from\s+pandas\b", + r"^\s*import\s+numpy\b", + r"^\s*from\s+numpy\b", + r"^\s*import\s+geopandas\b", + r"^\s*from\s+geopandas\b", + r"^\s*import\s+rioxarray\b", + r"^\s*from\s+rioxarray\b", + r"^\s*import\s+pandera\b", + r"^\s*from\s+pandera\b", +) + + +def test_snakemake_host_code_has_no_module_runtime_dependencies(): + workflow_files = [ + Path("workflow/Snakefile"), + *Path("workflow/rules").glob("*.smk"), + ] + + violations = [] + + for path in workflow_files: + text = path.read_text(encoding="utf-8") + + for pattern in FORBIDDEN_PATTERNS: + if re.search(pattern, text, flags=re.MULTILINE): + violations.append( + f"{path}: {pattern}" + ) + + assert not violations, ( + "Snakemake host code must not depend on module runtime " + "packages:\n" + + "\n".join(violations) + ) diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 193d65e..5df7295 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -23,15 +23,20 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda#18335a698559cdbcd86150a48bf54ba6 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda#d2ffd7602c02f2b316fd921d39876885 https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda#7eccb41177e15cc672e1babe9056018e +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 +https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf +https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda#dfb9a57535eb8c35c6744da7043063f0 https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 +https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda#5794b3bdc38177caf969dabd3af08549 @@ -44,43 +49,128 @@ https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_open https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda#33a413f1095f8325e5c30fde3b0d2445 https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda#d8285bea2a350f63fab23bf460221f3f https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda#15c437bfa4cbddd379b95357c9aa4150 -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda#15995ecb2ef890778ba9a3750190f09d -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 -https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda#b345ea7f13e4cae9809c69b1d1af2c99 +https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda#55f526c3fb5302a1ce922612348442e1 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda#b2895afaf55bf96a8c8282a2e47a5de0 +https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e +https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda#861fb6ccbc677bb9a9fb2468430b9c6a +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda#34e54f03dfea3e7a2dcf1453a85f1085 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda#665d152b9c6e78da404086088077c844 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda#adba2e334082bb218db806d4c12277c9 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda#e192019153591938acf7322b6459d36e +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda#f2ba4192d38b6cef2bb2c25029071d90 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda#4d1fc190b99912ed557a8236e958c559 +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda#b34c5559f45d8996e3bc0b6250a6cc84 +https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda#94116b69829e90b72d566e64421e1bff +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda#9802ae6d20982f42c0f5d69008988763 +https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 +https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda#35eeb0a2add53b1e50218ed230fa6a02 +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda#b233b41be0bf210989d57160ed39b394 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda#74e91c36d0eef3557915c68b6c2bef96 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda#6178c6f2fb254558238ef4e6c56fb782 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda#6c77a605a7a689d17d4819c0f8ac9a00 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda#a752488c68f2e7c456bcbd8f16eec275 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927 +https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b +https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 +https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda#52dd46162c6fb2765b49e6fd06adf8d5 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda#6d2362046dce932eefbdeb0540de0c38 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda#40cdeafb789a5513415f7bdbef053cf5 +https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda#eb83f3f8cecc3e9bff9e250817fc69b6 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda#ec3c4350aa0261bf7f87b8ca15c8e80e +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda#f25206d7322c0e9648e8b83694d143ab +https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda#75e9f795be506c96dd43cb09c7c8d557 +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda#35fa2b34bbced424e6976d30f5fde576 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda#65514a7ba857e9dbffbffd641f293e33 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda#9ad637a7ac380c442be142dfb0b1b955 +https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda#327c78a8ce710782425a89df851392f7 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda#feec6e0f138982e3f7b1ac372fb396cf +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda#f9fe2984587fa8235a6af6004760cd18 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda#fb16b4b69e3f1dcfe79d80db8fd0c55d +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda#e289f3d17880e44b633ba911d57a321b +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda#8462b5322567212beeb025f3519fb3e2 +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda#c01af13bdc553d1a8fbfff6e8db075f0 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda#e0e050cfa9fa85fe39632ab11cb7f3e0 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda#347cb348bfc8d77062daee11c326e518 +https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 +https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda#e211a78613b9d50a096644b97cede8f7 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d +https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda#8904e09bda369377b3dd07e2ac828c5d +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda#4d483b12b9fc7169d112d4f7a250c05c +https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda#31059dc620fa57d787e3899ed0421e6d +https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda#c2bd8055a2e2dce7a7f32cfd02101fb6 +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda#8baf6a8672bf235afede64de7a7da1c2 +https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 +https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda#2aadb0d17215603a82a2a6b0afd9a4cb +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda#8b3ce45e929cd8e8e5f4d18586b56d8b +https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda#9e5609720e31213d4f39afe377f6217e +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda#cd74a9525dc74bbbf93cf8aa2fa9eb5b +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda#0b6c506ec1f272b685240e70a29261b8 +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda#2e55011fa483edb8bfe3fd92e860cd79 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda#5cb5a1c9a94a78f5b23684bcb845338d +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda#ca4ed8015764937c81b830f7f5b68543 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda#294fb524171e2a2748cb7fe708aba826 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 +https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda#43c2bc96af3ae5ed9e8a10ded942aa50 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda#7d499b5b6d150f133800dc3a582771c7 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda#579c6a324b197594fabc9240bddf2d8b +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda#15995ecb2ef890778ba9a3750190f09d +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda#b345ea7f13e4cae9809c69b1d1af2c99 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda#920bb03579f15389b9e512095ad995b7 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda#2a45e7f8af083626f009645a6481f12d -https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b -https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 -https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda#7da1d242ca3591e174a3c7d82230d3c0 https://conda.anaconda.org/conda-forge/linux-64/proj-9.4.1-h54d7996_1.conda#e479d1991c725e1a355f33c0e40dbc66 https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.3-hb03c661_0.conda#b62b615caa60812640f24db3a8d0fc87 https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.3-ha02ee65_0.conda#8f5e2c6726c1339287a3c76a2c138ac7 https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda#55c20edec8e90c4703787acaade60808 https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.3-ha02ee65_0.conda#6a1b6af49a334e4e06b9f103367762bf -https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda#9dda9667feba914e0e80b95b82f7402b -https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 -https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda#35eeb0a2add53b1e50218ed230fa6a02 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda#c2a01a08fc991620a74b32420e97868a https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda#aab9195bc018b82dc77a84584b36cce9 https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hc670b87_16.conda#3d9f3a2e5d7213c34997e4464d2f938c https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda#fe7b4ff5792fee512aad843294ea809a https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h15fa968_9.conda#4957a903bd6a68cc2e53e47476f9c6f4 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda#953b7cca897e21215302dbfe2af5cd0c https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c @@ -123,41 +213,12 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.379-h5a9005d_9. https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.26.0-h86fa3b2_0.conda#061175d9d4c046a1cf8bffe95a359fab https://conda.anaconda.org/conda-forge/linux-64/libgdal-tiledb-3.9.2-h4a3bace_2.conda#c3fac34ecba2fcf9d5d31a03b975d5a1 https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda#5c5f9b9bad2a0852ffa38833ba2a1c7d -https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 -https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda#31059dc620fa57d787e3899ed0421e6d -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda#52dd46162c6fb2765b49e6fd06adf8d5 https://conda.anaconda.org/conda-forge/linux-64/postgresql-17.2-h1122569_0.conda#848402b976b31bfecb3e476ea85cb285 https://conda.anaconda.org/conda-forge/linux-64/libgdal-postgisraster-3.9.2-h5e77dd0_7.conda#3392965ffc4e8b7c66a532750ce0e91f https://conda.anaconda.org/conda-forge/linux-64/libgdal-pg-3.9.2-h5e77dd0_7.conda#e86b26f53ae868565e95fde5b10753d3 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda#e235d5566c9cc8970eb2798dd4ecf62f https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda#567fbeed956c200c1db5782a424e58ee -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda#40cdeafb789a5513415f7bdbef053cf5 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda#8b3ce45e929cd8e8e5f4d18586b56d8b -https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda#fb16b4b69e3f1dcfe79d80db8fd0c55d -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda#e289f3d17880e44b633ba911d57a321b -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda#8462b5322567212beeb025f3519fb3e2 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda#e0e050cfa9fa85fe39632ab11cb7f3e0 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda#b2895afaf55bf96a8c8282a2e47a5de0 -https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e -https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda#861fb6ccbc677bb9a9fb2468430b9c6a -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda#34e54f03dfea3e7a2dcf1453a85f1085 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda#c01af13bdc553d1a8fbfff6e8db075f0 -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/poppler-24.08.0-h47131b8_1.conda#0854b9ff0cc10a1f6f67b0f352b8e75a https://conda.anaconda.org/conda-forge/linux-64/libgdal-pdf-3.9.2-h600f43f_7.conda#567066db0820f4983a6741e429c651d1 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda#a7b27c075c9b7f459f1c022090697cba @@ -191,21 +252,6 @@ https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.c https://conda.anaconda.org/conda-forge/linux-64/gdal-3.9.2-py312h1299960_7.conda#9cf27e3f9d97ea13f250db9253a25dc8 https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda#4a30f4277a1894928a7057d0e14c1c95 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 -https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda#2aadb0d17215603a82a2a6b0afd9a4cb -https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda#9e5609720e31213d4f39afe377f6217e -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda#cd74a9525dc74bbbf93cf8aa2fa9eb5b -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda#0b6c506ec1f272b685240e70a29261b8 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda#2e55011fa483edb8bfe3fd92e860cd79 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda#5cb5a1c9a94a78f5b23684bcb845338d -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda#ca4ed8015764937c81b830f7f5b68543 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda#294fb524171e2a2748cb7fe708aba826 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda#43c2bc96af3ae5ed9e8a10ded942aa50 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda#7d499b5b6d150f133800dc3a582771c7 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 @@ -247,7 +293,6 @@ https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf5413 https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda#5f7d505626cb057e1320bbd46dd02ef2 https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py312hb3f7f12_1.conda#b99d90ef4e77acdab74828f79705a919 https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda#55f526c3fb5302a1ce922612348442e1 https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index 4545170..76aab10 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -14,15 +14,20 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda#4 https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda#a915151d5d3c5bf039f5ccc8402a436f https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda#620b85a3f45526a8bc4d23fd78fc22f0 https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda#8e7608172fa4d1b90de9a745c2fd2b81 +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 +https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf +https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda#e38d54c5e4318faa79f71b713b059566 https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 +https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda#a9c118f6343fb6301b6f3b4e94c4c562 @@ -37,18 +42,45 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda# https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda#03a2ef3491da9e5b4d18c03e9f4b3109 https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda#d83fc83d589e2625a3451c9a7e21047c https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda#4581a32b837950217327fcab93214313 -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda#173d5eeba324363d9171946e86a81687 +https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda#d037e9adb0365ab53445f357bd9a035f +https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 -https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda#b9df2fcb7e9eba945c1c946c438c4586 +https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda#d99c2a23a31b0172e90f456f580b695e https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda#ab136e4c34e97f34fb621d2592a393d8 https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda#e5e7d467f80da752be17796b87fe6385 https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda#b8a7544c83a67258b0e8592ec6a5d322 https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda#a6130c709305cd9828b4e1bd9ba0000c https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda#095e5749868adab9cae42d4b460e5443 https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda#e2a72ab2fa54ecb6abab2b26cde93500 +https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda#2259ae0949dbe20c0665850365109b27 +https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda#4b5d3a91320976eec71678fad1e3569b +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda#9d1299ace1924aa8f4e0bc8e71dd0cf7 +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda#78b548eed8227a689f93775d5d23ae09 +https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda#415816daf82e0b23a736a069a75e9da7 +https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda#af523aae2eca6dfa1c8eec693f5b9a79 +https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda#a0bb0678f67c464938d3693fa96f6884 +https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda#0582e67cd14cfed773be2f3b1aba08e0 +https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda#d2f2c7c10e2957647d45589b7701a453 +https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda#0634560e556adb3e7924668e49ad53fc +https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda#58261af35f0d33fd28e2257b208a1be0 +https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda#7bd06ab4ed807154c2d9031eb5ebf025 +https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda#45b836f333fd3e282c16fff7dc82994e +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda#231cffe69d41716afe4525c5c1cc5ddd +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda#4ce5651ae5cd6eebc5899f9bfe0eac3c +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda#cb7e7fe96c9eee23a464afd57648d2cd +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda#ab57f389f304c4d2eb86d8ae46d219c3 +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda#ce8659623cea44cc812bc0bfae4041c5 +https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda#77e64a600d8426c3863942f67491f5fb +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 +https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda#f9cce0bc86b46533489a994a47d3c7d2 +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda#e5d83f3ae6ada32d4e64e366a41f9ff6 +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda#94174d301ce2f5962197fb9b880ea8ff +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb +https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda#173d5eeba324363d9171946e86a81687 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c +https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda#b9df2fcb7e9eba945c1c946c438c4586 https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda#b68e8f66b94b44aaa8de4583d3d4cc40 https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda#36d33e440c31857372a72137f78bacf5 https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda#bcb3cba70cf1eec964a03b4ba7775f01 @@ -73,7 +105,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-h31fb324_16.con https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda#fcd860469a8fa003e25d472b40b0ff81 https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda#dd655a29b40fe0d1bf95c64cf3cb348d https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf7a34df_9.conda#a0c631a4cf164b1688484db0da3072b5 -https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda#2259ae0949dbe20c0665850365109b27 https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802 https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda#3d8eeedb8e541d1cb593e8204fcc397d https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda#e56eaa1beab0e7fed559ae9c0264dd88 @@ -124,15 +155,10 @@ https://conda.anaconda.org/conda-forge/osx-arm64/postgresql-17.2-h393ceee_0.cond https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-postgisraster-3.9.2-h6a0b679_7.conda#f044c31cdd36806e627e23329c6089b0 https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pg-3.9.2-h6a0b679_7.conda#596b2a38085a9352856af7ab3bdefe41 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda#4b5d3a91320976eec71678fad1e3569b https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.38-heaf21c2_0.conda#0b528ead848386c8a53ee0b76fbf2ac1 https://conda.anaconda.org/conda-forge/osx-arm64/nss-3.118-h1c710a3_0.conda#ae07409126ced4f0d982dfeab0681016 https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda#5103f6a6b210a3912faf8d7db516918c https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.0-hdff4504_0.conda#86bdf23c648be3498294c4ab861e7090 -https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda#d2f2c7c10e2957647d45589b7701a453 -https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda#a0bb0678f67c464938d3693fa96f6884 -https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda#0582e67cd14cfed773be2f3b1aba08e0 -https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda#7bd06ab4ed807154c2d9031eb5ebf025 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 @@ -174,25 +200,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.10.0-py312hf9e36c7_0. https://conda.anaconda.org/conda-forge/osx-arm64/gdal-3.9.2-py312h936c49d_7.conda#305595575712364e99c69bf43d7078f0 https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py312hf3c922e_1.conda#268287f41c9d4ba5b426d5206986fbca https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924 -https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda#d99c2a23a31b0172e90f456f580b695e -https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda#9d1299ace1924aa8f4e0bc8e71dd0cf7 -https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda#78b548eed8227a689f93775d5d23ae09 -https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda#415816daf82e0b23a736a069a75e9da7 -https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda#af523aae2eca6dfa1c8eec693f5b9a79 -https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda#0634560e556adb3e7924668e49ad53fc -https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda#58261af35f0d33fd28e2257b208a1be0 -https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda#45b836f333fd3e282c16fff7dc82994e -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda#231cffe69d41716afe4525c5c1cc5ddd -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda#4ce5651ae5cd6eebc5899f9bfe0eac3c -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda#cb7e7fe96c9eee23a464afd57648d2cd -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda#ab57f389f304c4d2eb86d8ae46d219c3 -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda#ce8659623cea44cc812bc0bfae4041c5 -https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda#77e64a600d8426c3863942f67491f5fb -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda#f9cce0bc86b46533489a994a47d3c7d2 -https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda#e5d83f3ae6ada32d4e64e366a41f9ff6 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 @@ -234,7 +241,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-17.0.0-hbf8b https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda#04a90c4ce691f2e289658dd475f69631 https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.3.3-py312h11d1bbd_1.conda#4a9eb3dc0ba76484cae3084b99d008e5 https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e -https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda#d037e9adb0365ab53445f357bd9a035f https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index 12a1611..d97e799 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -16,15 +16,20 @@ https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda#720b https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda#ccc490c81ffe14181861beac0e8f3169 https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda#4cb8e6b48f67de0b018719cdf1136306 https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda#2956dff38eb9f8332ad4caeba941cfe7 +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 +https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf +https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda#edd458c91ab9bc198012c73289b0b0bc https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 +https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda#f6ad7450fc21e00ecc23812baed6d2e4 https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 @@ -41,36 +46,84 @@ https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.co https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda#9e4bf521c07f4d423cba9296b7927e3c https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda#f9ac74c3b07c396014434aca1e58d362 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda#9da394ea5e0ec5cc5edc1ad14f2a4d4d -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda#0b8c96ed1c04732ff8eb5d481b44c43c -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 -https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda#7a28cb97617f0bd6a650b4b88f1f44cb +https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda#1045d29f787812d3fac1fd80a1339710 https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda#053b84beec00b71ea8ff7a4f84b55207 +https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda#a912b2c4ff0f03101c751aa79a331831 +https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893 https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda#25a127bad5470852b30b239f030ec95b https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda#e77030e67343e28b084fabd7db0ce43e https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda#54b231d595bc1ff9bff668dd443ee012 https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda#549845d5133100142452812feb9ba2e8 -https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda#9dce2f112bfd3400f4f432b3d0ac07b2 +https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda#52f1280563f3b48b5f75414cd2d15dd1 +https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda#2cf0cf76cc15d360dfa2f17fd6cf9772 +https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.0-h7025463_0.conda#ea8df8a5c5c7adf4c03bf9e3db1637c3 +https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda#45a2834578a5f167258aa109af48b036 https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda#31aec030344e962fbd7dbbbbd68e60a9 +https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda#8579b6bb8d18be7c0b27fb08adeeeb40 +https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda#79538e7a7bc024084eda5989f73fde35 +https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda#5187ecf958be3c39110fe691cbd6873e +https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda#4e4d54f9f98383d977ba56ef39ebf46d +https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda#e45b52fb9a81c9e2708465a706e05952 +https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda#e77293b32225b136a8be300f93d0e89f +https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda#08c8fa3b419df480d985e304f7884d35 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab +https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda#abd79bad98c99c1a116154d6de74ea89 +https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda#20e32ced54300292aff690a69c5e7b97 +https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda#b7648427f5b6797ae3904ad76e4c7f19 +https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda#e9a1402439c18a4e3c7a52e4246e9e1c +https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda#d92e5a0de3263315551d54d5574f5193 +https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda#e84f36aa02735c140099d992d491968d +https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda#52a05ba3f802633cb2234bb3edc45888 +https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 +https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda#46a21c0a4e65f1a135251fc7c8663f83 +https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda#e723ab7cc2794c954e1b22fde51c16e4 +https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda#f1147651e3fdd585e2f442c0c2fc8f2d +https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda#1626967b574d1784b578b52eaeb071e7 +https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda#cc5d690fc1c629038f13c68e88e65f44 +https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda#a7c03e38aa9c0e84d41881b9236eacfb +https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda#8436cab9a76015dfe7208d3c9f97c156 +https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda#3c8f2573569bb816483e5cf57efbbe29 +https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c +https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda#1df4012c8a2478699d07bc26af66d41e +https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda#ba3bcb72a269e7751cadbdd784f84dec +https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py312h78d62e6_0.conda#4ff6f76c2c16c85806ee7533768f5e64 +https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py312he06e257_0.conda#9c46f390eb4b5c6e502df20378e442dd +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda#58aec7a295039d8614175eae3a4f8778 +https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda#37f4669f8ac2f04d826440a8f3f42300 +https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda#bf0ced5177fec8c18a7b51d568590b7c +https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda#ef022c8941d7dcc420c8533b0e419733 +https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda#441706c019985cf109ced06458e6f742 +https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda#2944f5f8ea7e2db9cea01ed951e09194 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 +https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda#475bd41a63e613f2f2a2764cd1cd3b25 +https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda#3752482b0df88d7a08a0791f906e87ae +https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda#779fa0e5531694e039385f739ceb88b4 +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb +https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda#0b8c96ed1c04732ff8eb5d481b44c43c +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c +https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda#7a28cb97617f0bd6a650b4b88f1f44cb +https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda#9dce2f112bfd3400f4f432b3d0ac07b2 https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda#099a1016d23baa4f41148a985351a7a8 https://conda.anaconda.org/conda-forge/win-64/proj-9.4.1-hd9569ee_1.conda#6e15f5054b179959d2410c2e53d5a3e4 https://conda.anaconda.org/conda-forge/win-64/xz-tools-5.8.3-hfd05255_0.conda#01b20ff45704b888d218f31a3aa3ea2a https://conda.anaconda.org/conda-forge/win-64/liblzma-devel-5.8.3-hfd05255_0.conda#7845201435d0c7c0a02269c2742da1cc https://conda.anaconda.org/conda-forge/win-64/xz-5.8.3-hb6c8415_0.conda#46f70d503d68c55c104a564928a39852 https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.2.5-he0c23c2_2.conda#82b6eac3c198271e98b48d52d79726d8 -https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda#a912b2c4ff0f03101c751aa79a331831 https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36231-h84cd919_39.conda#2ccc63d7b7d066a814ed9f99072832d7 https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda#e34720eb20a33fc3bfb8451dd837ab7a -https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893 -https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda#5187ecf958be3c39110fe691cbd6873e https://conda.anaconda.org/conda-forge/win-64/geos-3.12.2-h5a68840_1.conda#019e3460f99eb7c2198c532c50d08791 https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h6c42fcb_16.conda#4476d717f460b45f5033206bbb84f3f5 https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda#5d55038c4d640e5df06de1cfb4e8d741 https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda#d6a8059de245e53478b581742b53f71d https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-hab0cb6d_9.conda#934f10287da9c46f761abf0ee5f88dd3 -https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda#52f1280563f3b48b5f75414cd2d15dd1 https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda#28b4cf9065681f43cc567410edf8243d https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda#b44e0d9eb84c6c086d809787aab0a1da https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda#c5cb4159f0eea65663b31dd1e49bbb71 @@ -110,29 +163,11 @@ https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.28.2-h2ae5ca2_6.cond https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.379-h9ac9443_10.conda#65e55af6d94f870f7a1af70921d91296 https://conda.anaconda.org/conda-forge/win-64/tiledb-2.26.0-h98a567f_0.conda#451f161732757b5124fc3a320401c587 https://conda.anaconda.org/conda-forge/win-64/libgdal-tiledb-3.9.2-hb8b5d01_2.conda#acc3612fa26fab59d18d4075d12c15b0 -https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda#e84f36aa02735c140099d992d491968d -https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda#8579b6bb8d18be7c0b27fb08adeeeb40 https://conda.anaconda.org/conda-forge/win-64/libpq-17.2-h7ec079e_0.conda#cf036705ab68a8652529b944de7f86fe https://conda.anaconda.org/conda-forge/win-64/postgresql-17.2-heca7946_0.conda#e2fa6c7a89d528398dce60b398f05b42 https://conda.anaconda.org/conda-forge/win-64/libgdal-postgisraster-3.9.2-hfaa227e_7.conda#b24ac84c98145ebf555950076c0417da https://conda.anaconda.org/conda-forge/win-64/libgdal-pg-3.9.2-hfaa227e_7.conda#c37be85d5b9a3b1fc94a99d6f8327ab1 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda#e723ab7cc2794c954e1b22fde51c16e4 -https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda#2cf0cf76cc15d360dfa2f17fd6cf9772 -https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.0-h7025463_0.conda#ea8df8a5c5c7adf4c03bf9e3db1637c3 -https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda#1df4012c8a2478699d07bc26af66d41e -https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda#4e4d54f9f98383d977ba56ef39ebf46d -https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda#e45b52fb9a81c9e2708465a706e05952 -https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda#e77293b32225b136a8be300f93d0e89f -https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda#08c8fa3b419df480d985e304f7884d35 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda#abd79bad98c99c1a116154d6de74ea89 -https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda#20e32ced54300292aff690a69c5e7b97 https://conda.anaconda.org/conda-forge/win-64/poppler-24.08.0-h9415970_1.conda#4c7b7a4301314afed48c6544c34399e4 https://conda.anaconda.org/conda-forge/win-64/libgdal-pdf-3.9.2-ha1c78db_7.conda#27fb85b4f988b64aa9ab0e926ddd6b33 https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda#09066edc7810e4bd1b41ad01a6cc4706 @@ -166,28 +201,6 @@ https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.con https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda#4a093f053f2827cfa102304bf945e845 https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda#1b03775deb65180f956a7c1b0e6edc5f https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 -https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda#46a21c0a4e65f1a135251fc7c8663f83 -https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda#f1147651e3fdd585e2f442c0c2fc8f2d -https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda#1626967b574d1784b578b52eaeb071e7 -https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda#cc5d690fc1c629038f13c68e88e65f44 -https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda#a7c03e38aa9c0e84d41881b9236eacfb -https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda#8436cab9a76015dfe7208d3c9f97c156 -https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda#3c8f2573569bb816483e5cf57efbbe29 -https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c -https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda#ba3bcb72a269e7751cadbdd784f84dec -https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py312h78d62e6_0.conda#4ff6f76c2c16c85806ee7533768f5e64 -https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py312he06e257_0.conda#9c46f390eb4b5c6e502df20378e442dd -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda#58aec7a295039d8614175eae3a4f8778 -https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda#37f4669f8ac2f04d826440a8f3f42300 -https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda#bf0ced5177fec8c18a7b51d568590b7c -https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda#ef022c8941d7dcc420c8533b0e419733 -https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda#441706c019985cf109ced06458e6f742 -https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda#2944f5f8ea7e2db9cea01ed951e09194 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda#475bd41a63e613f2f2a2764cd1cd3b25 -https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda#3752482b0df88d7a08a0791f906e87ae https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 @@ -227,7 +240,6 @@ https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-17.0.0-h1f0e801 https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda#5601751f674c1ecc1629cdf76f22ab61 https://conda.anaconda.org/conda-forge/win-64/lz4-4.3.3-py312h0608a1d_1.conda#90e9d18bcbfe59ac7d6064a58432f365 https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e -https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda#1045d29f787812d3fac1fd80a1339710 https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index ee07bab..bb6ef16 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -14,3 +14,9 @@ dependencies: - numpy >=1.26.4 - gregor >=0.1.0 - cmap >=0.7.2 +- pandas >=3.0.3 +- geopandas >=1.1.4 +- matplotlib >=3.10.9 +- rioxarray >=0.20.0 +- pandera >=0.31.1 +- pyyaml >=6.0.3 From 62dc24a7f2e2e547eb2371ccd9b023d702243ef7 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 12:35:19 +0200 Subject: [PATCH 060/241] Fixed legacy bug in advanced.smk --- workflow/rules/advanced.smk | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 7820454..2d60a33 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -104,21 +104,18 @@ def auxiliary_group_source_files(wildcards): wildcards.group_id ] - batches = [ - _get_batch( - plan, - batch_id=batch_id, - ) - for batch_id in batch_ids - ] + batches_by_id = { + batch["batch_id"]: batch + for batch in plan["batches"] + } return [ ( "/automatic/" - f"auxiliary/{batch['source']}/" - f"{batch['batch_id']}.parquet" + f"auxiliary/{batches_by_id[batch_id]['source']}/" + f"{batch_id}.parquet" ) - for batch in batches + for batch_id in batch_ids ] From 52f5330d76a962639dfe3e00a0a73ae1ca78ea54 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 13:34:24 +0200 Subject: [PATCH 061/241] Finalised the removal of local python dependencies from snakemake code/rules. --- tests/unit/test_advanced_requirements.py | 29 -------- .../test_workflow_environment_boundary.py | 5 +- workflow/rules/advanced.smk | 2 +- workflow/scripts/apply_advanced_overrides.py | 1 - .../cleaning/advanced/planning/selection.py | 72 +------------------ workflow/scripts/prepare_load_neso.py | 3 +- workflow/scripts/prepare_load_opsd.py | 1 - 7 files changed, 7 insertions(+), 106 deletions(-) diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index b874cb1..bf57ec8 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -271,35 +271,6 @@ def test_expand_requirements_can_be_disabled() -> None: requirements, ) -def test_expand_requirements_can_be_disabled() -> None: - requirements = pd.DataFrame( - { - "country": ["GRC"], - "start": [pd.Timestamp("2020-01-01", tz="UTC")], - "end": [pd.Timestamp("2020-02-01", tz="UTC")], - } - ) - - rules = [ - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - } - ] - - result = expand_auxiliary_requirements( - requirements, - rules=rules, - enabled=False, - ) - - pd.testing.assert_frame_equal( - result, - requirements, - ) - def test_basic_context_for_copy_period() -> None: rules = [ diff --git a/tests/unit/test_workflow_environment_boundary.py b/tests/unit/test_workflow_environment_boundary.py index 3a4773b..4e9e811 100644 --- a/tests/unit/test_workflow_environment_boundary.py +++ b/tests/unit/test_workflow_environment_boundary.py @@ -1,6 +1,6 @@ -from pathlib import Path +"""Tests for the Snakemake host/module environment boundary.""" import re - +from pathlib import Path FORBIDDEN_PATTERNS = ( r"\bsys\.path\b", @@ -22,6 +22,7 @@ def test_snakemake_host_code_has_no_module_runtime_dependencies(): + """Ensure host-side workflow code avoids module runtime packages.""" workflow_files = [ Path("workflow/Snakefile"), *Path("workflow/rules").glob("*.smk"), diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 2d60a33..1efeab1 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -195,7 +195,7 @@ checkpoint plan_auxiliary_data: output: plan=( "/automatic/" - "auxiliary/acquisition_plan.json" + "auxiliary/advanced_execution_plan.json" ), params: gap_filling=config["gap_filling"], diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index ea3313a..da8a14e 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -1,4 +1,3 @@ -import json from pathlib import Path import pandas as pd diff --git a/workflow/scripts/cleaning/advanced/planning/selection.py b/workflow/scripts/cleaning/advanced/planning/selection.py index 6c6b71c..cc7bf3e 100644 --- a/workflow/scripts/cleaning/advanced/planning/selection.py +++ b/workflow/scripts/cleaning/advanced/planning/selection.py @@ -1,78 +1,8 @@ -from collections.abc import Mapping, Sequence +from collections.abc import Mapping from common.time import as_utc_timestamp -def get_auxiliary_batch( - plan: Mapping, - *, - batch_id: str, - source: str, -) -> Mapping: - """Return one source batch from an auxiliary acquisition plan.""" - matches = [ - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == batch_id - and batch["source"] == source - ) - ] - - if len(matches) != 1: - raise ValueError( - "Expected exactly one auxiliary batch for " - f"{source=} and {batch_id=}, found {len(matches)}." - ) - - return matches[0] - - -def get_auxiliary_group_batches( - plan: Mapping, - *, - group_id: str, -) -> list[Mapping]: - """Return acquisition batches belonging to one auxiliary group.""" - batches = [ - batch - for batch in plan["batches"] - if batch["group_id"] == group_id - ] - - if not batches: - raise ValueError( - f"No auxiliary batches found for group {group_id!r}." - ) - - return batches - - -def get_source_batch_ids( - plan: Mapping, - *, - source: str, -) -> list[str]: - """Return batch identifiers for one auxiliary source.""" - return [ - batch["batch_id"] - for batch in plan["batches"] - if batch["source"] == source - ] - - -def get_auxiliary_group_ids( - plan: Mapping, -) -> list[str]: - """Return all auxiliary group identifiers.""" - return sorted( - { - batch["group_id"] - for batch in plan["batches"] - } - ) - - def get_required_auxiliary_sources( override: Mapping, ) -> list[Mapping]: diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index c2735f3..9f52c3e 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -2,8 +2,9 @@ import logging import sys +from collections.abc import Iterable from pathlib import Path -from typing import TYPE_CHECKING, Any, Iterable +from typing import TYPE_CHECKING, Any import pandas as pd from cleaning.advanced.planning.manifest import get_batch, load_execution_plan diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index b106c41..da34a43 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -1,7 +1,6 @@ """Prepare electricity-demand data downloaded from OPSD.""" import sys -from time import perf_counter from typing import TYPE_CHECKING, Any from warnings import warn From fdc1cfeb4498946d65e1986f276118e05a7ba5b9 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 14:10:32 +0200 Subject: [PATCH 062/241] Changed advanced scope enums --- tests/integration/test_config.yaml | 6 ++-- tests/unit/test_advanced_apply.py | 16 ++++----- tests/unit/test_advanced_plan.py | 36 +++++++++---------- tests/unit/test_advanced_requirements.py | 4 +-- tests/unit/test_cleaning_pipeline.py | 8 ++--- tests/unit/test_plan_auxiliary_data.py | 4 +-- workflow/internal/config.schema.yaml | 7 ++-- workflow/scripts/cleaning/advanced/apply.py | 4 +-- .../cleaning/advanced/planning/plan.py | 4 +-- 9 files changed, 45 insertions(+), 44 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 685e7b8..c25f969 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -52,7 +52,7 @@ integration_test: country: ALB start: "2022-01-01" end: "2022-02-01" - scope: fill_gaps_within_period + scope: fill_gaps method: construct_from_sources sources: - country: GBR @@ -71,7 +71,7 @@ integration_test: country: ALB start: "2022-05-01" end: "2022-06-01" - scope: fill_gaps_within_period + scope: fill_gaps method: construct_from_sources sources: - country: GBR @@ -90,7 +90,7 @@ integration_test: country: MNE start: "2020-03-01" end: "2020-04-01" - scope: fill_gaps_within_period + scope: fill_gaps method: construct_from_sources sources: - country: SRB diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index cc4d7d5..05e79f5 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -102,7 +102,7 @@ def test_construct_from_sources_fills_gaps() -> None: "country": "ALB", "start": "2021-01-01T00:00:00+00:00", "end": "2021-01-01T03:00:00+00:00", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", } filled, methods = apply_auxiliary_fill_rule( @@ -144,7 +144,7 @@ def test_construct_from_sources_overwrites_entire_period() -> None: "country": "ALB", "start": "2021-01-01T00:00:00+00:00", "end": "2021-01-01T03:00:00+00:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", } filled, methods = apply_auxiliary_fill_rule( @@ -254,14 +254,14 @@ def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: "country": "ALB", "start": "2021-01-01T00:00:00+00:00", "end": "2021-01-01T03:00:00+00:00", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", }, "overwrite": { "method": "construct_from_sources", "country": "ALB", "start": "2021-01-01T00:00:00+00:00", "end": "2021-01-01T03:00:00+00:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", }, } @@ -313,7 +313,7 @@ def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: assert methods is not cleaning_method -def test_overwrite_entire_period_replaces_existing_values() -> None: +def test_overwrite_replaces_existing_values() -> None: index = pd.date_range( "2022-01-01", periods=4, @@ -346,7 +346,7 @@ def test_overwrite_entire_period_replaces_existing_values() -> None: "country": "ALB", "start": index[1], "end": index[3], - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -382,7 +382,7 @@ def test_overwrite_entire_period_replaces_existing_values() -> None: "observed_entsoe_api", ] -def test_fill_gaps_within_period_preserves_existing_values() -> None: +def test_fill_gaps_preserves_existing_values() -> None: index = pd.date_range( "2022-01-01", periods=4, @@ -420,7 +420,7 @@ def test_fill_gaps_within_period_preserves_existing_values() -> None: "country": "ALB", "start": index[1], "end": index[3], - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index a56b42f..42c9fa8 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -25,7 +25,7 @@ def test_validate_construct_from_sources_rule() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -53,7 +53,7 @@ def test_validate_external_profile_rule() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "external_profile", } @@ -68,7 +68,7 @@ def test_construct_from_sources_requires_sources() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", } @@ -87,7 +87,7 @@ def test_source_weight_must_be_positive() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -114,7 +114,7 @@ def test_validate_construct_from_sources_with_scaling() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -152,7 +152,7 @@ def test_match_energy_scaling_requires_target_sources() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -181,7 +181,7 @@ def test_rejects_unsupported_scaling_method() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -218,14 +218,14 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "country": "ALB", "start": "2024-01-01", "end": "2024-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "external_profile", }, "construct_albania": { "country": "ALB", "start": "2023-01-01", "end": "2023-12-31 23:00", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -291,8 +291,8 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: ), ], "scope": [ - "overwrite_entire_period", - "overwrite_entire_period", + "overwrite", + "overwrite", ], "method": [ "construct_from_sources", @@ -353,7 +353,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "country": "ALB", "start": "2023-01-01", "end": "2023-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -367,7 +367,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "country": "MNE", "start": "2023-01-01", "end": "2023-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -397,7 +397,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "country": "ALB", "start": "2020-01-01", "end": "2020-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -411,7 +411,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "country": "ALB", "start": "2026-01-01", "end": "2026-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -439,7 +439,7 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: "country": "ALB", "start": "2021-12-01", "end": "2022-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -499,7 +499,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "country": "ALB", "start": "2021-12-01", "end": "2022-01-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { @@ -513,7 +513,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "country": "ALB", "start": "2025-01-01", "end": "2025-02-01", - "scope": "overwrite_entire_period", + "scope": "overwrite", "method": "construct_from_sources", "sources": [ { diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index bf57ec8..938f3dd 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -16,7 +16,7 @@ def test_compile_auxiliary_requirements_collects_sources() -> None: "country": "ALB", "start": "2021-01-01", "end": "2021-01-03", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { @@ -68,7 +68,7 @@ def test_compile_auxiliary_requirements_includes_scaling_sources() -> None: "country": "ALB", "start": "2021-01-01", "end": "2021-01-03", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index ffcb339..f2dcfe6 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -622,7 +622,7 @@ def test_advanced_mode_builds_auxiliary_fill_plan() -> None: "country": "ALB", "start": "2021-01-01 01:00", "end": "2021-01-01 01:00", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { @@ -696,7 +696,7 @@ def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: "country": "ALB", "start": "2022-01-02", "end": "2022-01-03", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { @@ -711,7 +711,7 @@ def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: "country": "MNE", "start": "2022-01-02", "end": "2022-01-03", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { @@ -726,7 +726,7 @@ def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: "country": "ALB", "start": "2021-01-01", "end": "2021-02-01", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 35c7579..565df08 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -39,7 +39,7 @@ def _construct_override( "country": "ALB", "start": "2020-01-01", "end": "2020-02-01", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ { @@ -295,7 +295,7 @@ def test_non_construct_rule_requires_no_auxiliary_groups() -> None: "country": "ALB", "start": "2020-01-01", "end": "2020-02-01", - "scope": "fill_gaps_within_period", + "scope": "fill_gaps", "method": "leave_missing", } diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 6a0ff97..57f3a90 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -118,11 +118,12 @@ $defs: scope: type: string enum: - - fill_gaps_within_period - - overwrite_entire_period + - fill_gaps + - overwrite description: > Whether the instruction fills only missing values within the - target period or replaces all values in that period. + target period (fill_gaps) or replaces all values in that period + (overwrite). method: type: string diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 4810599..7b878af 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -96,13 +96,13 @@ def apply_constructed_profile( f"Target country {country!r} is not present in load data." ) - if scope == "fill_gaps_within_period": + if scope == "fill_gaps": replace_mask = filled.loc[ target_index, country, ].isna() - elif scope == "overwrite_entire_period": + elif scope == "overwrite": replace_mask = pd.Series( True, index=target_index, diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 4d05836..f3f4e4b 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -10,8 +10,8 @@ ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE -FILL_GAPS_WITHIN_PERIOD = "fill_gaps_within_period" -OVERWRITE_ENTIRE_PERIOD = "overwrite_entire_period" +FILL_GAPS_WITHIN_PERIOD = "fill_gaps" +OVERWRITE_ENTIRE_PERIOD = "overwrite" MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" From 7425dbceae993680a23141fafa0f102d08aaad06 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 14:34:25 +0200 Subject: [PATCH 063/241] Rewired overrides code to allow for external_profile alongside constructed_profile --- tests/unit/test_advanced_apply.py | 326 ++++++++++++++++-- workflow/internal/config.schema.yaml | 10 + workflow/rules/advanced.smk | 13 +- workflow/scripts/apply_advanced_overrides.py | 28 +- workflow/scripts/cleaning/advanced/apply.py | 90 ++++- .../advanced/methods/external_profile.py | 66 +++- 6 files changed, 483 insertions(+), 50 deletions(-) diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 05e79f5..7311b20 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -49,24 +49,6 @@ def _cleaning_method() -> pd.DataFrame: ) - -def test_external_profile_execution_is_not_implemented() -> None: - rule = { - "method": "external_profile", - } - - with pytest.raises( - NotImplementedError, - match="external_profile", - ): - apply_auxiliary_fill_rule( - _load(), - _cleaning_method(), - rule_name="external_albania", - rule=rule, - ) - - def test_construct_from_sources_requires_profile() -> None: rule = { "method": "construct_from_sources", @@ -274,7 +256,8 @@ def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: load, cleaning_method, overrides=overrides, - profiles=profiles, + constructed_profiles=profiles, + external_profiles={} ) assert filled["ALB"].tolist() == [ @@ -297,7 +280,8 @@ def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: load, cleaning_method, overrides={}, - profiles={}, + constructed_profiles={}, + external_profiles={}, ) pd.testing.assert_frame_equal( @@ -363,9 +347,10 @@ def test_overwrite_replaces_existing_values() -> None: load, cleaning_method, overrides=overrides, - profiles={ + constructed_profiles={ "replace_albania": profile, }, + external_profiles={} ) assert filled["ALB"].tolist() == [ @@ -437,9 +422,10 @@ def test_fill_gaps_preserves_existing_values() -> None: load, cleaning_method, overrides=overrides, - profiles={ + constructed_profiles={ "fill_albania": profile, }, + external_profiles={}, ) assert filled["ALB"].tolist() == [ @@ -457,3 +443,299 @@ def test_fill_gaps_preserves_existing_values() -> None: ] +def test_external_profile_fill_gaps_only_replaces_missing_values(): + index = pd.date_range( + "2025-01-01", + periods=4, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [100.0, None, 300.0, None], + }, + index=index, + ) + + cleaning_method = pd.DataFrame( + None, + index=index, + columns=["ALB"], + dtype=object, + ) + + profile = pd.Series( + [110.0, 220.0, 330.0, 440.0], + index=index, + ) + + overrides = { + "external_albania": { + "method": "external_profile", + "country": "ALB", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T04:00:00Z", + "scope": "fill_gaps", + } + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + constructed_profiles={}, + external_profiles={ + "external_albania": profile, + }, + ) + + expected = pd.Series( + [100.0, 220.0, 300.0, 440.0], + index=index, + name="ALB", + ) + + pd.testing.assert_series_equal( + filled["ALB"], + expected, + ) + + assert pd.isna(methods.loc[index[0], "ALB"]) + assert methods.loc[index[1], "ALB"] == "external_albania" + assert pd.isna(methods.loc[index[2], "ALB"]) + assert methods.loc[index[3], "ALB"] == "external_albania" + +def test_external_profile_overwrite_replaces_supplied_values(): + index = pd.date_range( + "2025-01-01", + periods=4, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [100.0, 200.0, 300.0, 400.0], + }, + index=index, + ) + + cleaning_method = pd.DataFrame( + None, + index=index, + columns=["ALB"], + dtype=object, + ) + + profile = pd.Series( + [110.0, 220.0, 330.0, 440.0], + index=index, + ) + + overrides = { + "external_albania": { + "method": "external_profile", + "country": "ALB", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T04:00:00Z", + "scope": "overwrite", + } + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + constructed_profiles={}, + external_profiles={ + "external_albania": profile, + }, + ) + + expected = pd.Series( + [110.0, 220.0, 330.0, 440.0], + index=index, + name="ALB", + ) + + pd.testing.assert_series_equal( + filled["ALB"], + expected, + ) + + assert ( + methods["ALB"] + == "external_albania" + ).all() + + +def test_external_profile_overwrite_only_replaces_supplied_timestamps(): + index = pd.date_range( + "2025-01-01", + periods=5, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + { + "ALB": [100.0, 200.0, 300.0, 400.0, 500.0], + }, + index=index, + ) + + cleaning_method = pd.DataFrame( + None, + index=index, + columns=["ALB"], + dtype=object, + ) + + profile = pd.Series( + [2200.0, 4400.0], + index=[ + index[1], + index[3], + ], + ) + + overrides = { + "external_albania": { + "method": "external_profile", + "country": "ALB", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T05:00:00Z", + "scope": "overwrite", + } + } + + filled, methods = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + constructed_profiles={}, + external_profiles={ + "external_albania": profile, + }, + ) + + expected = pd.Series( + [100.0, 2200.0, 300.0, 4400.0, 500.0], + index=index, + name="ALB", + ) + + pd.testing.assert_series_equal( + filled["ALB"], + expected, + ) + + assert methods.loc[index[1], "ALB"] == "external_albania" + assert methods.loc[index[3], "ALB"] == "external_albania" + + assert pd.isna(methods.loc[index[0], "ALB"]) + assert pd.isna(methods.loc[index[2], "ALB"]) + assert pd.isna(methods.loc[index[4], "ALB"]) + + +def test_external_profile_ignores_values_outside_rule_period(): + index = pd.date_range( + "2025-01-01", + periods=5, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + {"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, + index=index, + ) + + cleaning_method = pd.DataFrame( + None, + index=index, + columns=["ALB"], + dtype=object, + ) + + profile = pd.Series( + [1000.0, 2000.0, 3000.0, 4000.0, 5000.0], + index=index, + ) + + overrides = { + "external_albania": { + "method": "external_profile", + "country": "ALB", + "start": "2025-01-01T01:00:00Z", + "end": "2025-01-01T04:00:00Z", + "scope": "overwrite", + } + } + + filled, _ = apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + constructed_profiles={}, + external_profiles={ + "external_albania": profile, + }, + ) + + expected = pd.Series( + [100.0, 2000.0, 3000.0, 4000.0, 500.0], + index=index, + name="ALB", + ) + + pd.testing.assert_series_equal( + filled["ALB"], + expected, + ) + + +def test_external_profile_requires_profile(): + index = pd.date_range( + "2025-01-01", + periods=2, + freq="h", + tz="UTC", + ) + + load = pd.DataFrame( + {"ALB": [None, None]}, + index=index, + ) + + cleaning_method = pd.DataFrame( + None, + index=index, + columns=["ALB"], + dtype=object, + ) + + overrides = { + "external_albania": { + "method": "external_profile", + "country": "ALB", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T02:00:00Z", + "scope": "fill_gaps", + } + } + + with pytest.raises( + ValueError, + match="requires an external profile", + ): + apply_auxiliary_fill_rules( + load, + cleaning_method, + overrides=overrides, + constructed_profiles={}, + external_profiles={}, + ) + + diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 57f3a90..05d5423 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -168,6 +168,16 @@ $defs: method: const: external_profile + path: + type: string + minLength: 1 + description: > + Path to a local CSV containing timestamp and value + columns for the external series. + + required: + - path + unevaluatedProperties: false diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 1efeab1..b347678 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -189,6 +189,16 @@ def final_cleaning_method_input(_wildcards): return rules.clean_demand.output.cleaning_method +def advanced_external_profile_files(_wildcards): + plan = _read_auxiliary_plan() + + return list( + dict.fromkeys( + plan["external_profile_files"].values() + ) + ) + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -405,7 +415,8 @@ rule apply_advanced_overrides: "load_cleaning_method.parquet" ), plan=auxiliary_acquisition_plan, - profiles=advanced_constructed_profiles, + constructed_profiles=advanced_constructed_profiles, + external_profiles=advanced_external_profile_files, output: demand=( "/automatic/" diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index da8a14e..145bb0c 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -1,7 +1,10 @@ +"""Apply configured advanced electricity-demand overrides.""" + from pathlib import Path import pandas as pd from cleaning.advanced.apply import apply_auxiliary_fill_rules +from cleaning.advanced.methods.external_profile import read_external_profile from cleaning.advanced.planning.manifest import ( get_active_overrides, load_execution_plan, @@ -23,18 +26,37 @@ plan ) -profiles = { + +constructed_profiles = { Path(path).stem: pd.read_parquet(path).iloc[:, 0] - for path in snakemake.input.profiles + for path in snakemake.input.constructed_profiles +} + + +external_profiles_by_path = { + Path(path): read_external_profile(path) + for path in snakemake.input.external_profiles } + +external_profiles = { + rule_name: external_profiles_by_path[ + Path(path) + ] + for rule_name, path + in plan["external_profile_files"].items() +} + + filled, cleaning_method = apply_auxiliary_fill_rules( load, cleaning_method, overrides=active_overrides, - profiles=profiles, + constructed_profiles=constructed_profiles, + external_profiles=external_profiles, ) + filled.to_parquet( snakemake.output.demand ) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 7b878af..0c381bf 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -45,9 +45,21 @@ def apply_auxiliary_fill_rule( ) if method == EXTERNAL_PROFILE: - raise NotImplementedError( - "Advanced-fill method 'external_profile' is recognized " - "but has not yet been implemented." + if profile is None: + raise ValueError( + f"Advanced-fill rule {rule_name!r} requires " + "an external profile." + ) + + return apply_external_profile( + load, + cleaning_method, + profile, + country=rule["country"], + start=as_utc_timestamp(rule["start"]), + end=as_utc_timestamp(rule["end"]), + scope=rule["scope"], + rule_name=rule_name, ) if method == MANUAL_REVIEW: @@ -136,14 +148,24 @@ def apply_auxiliary_fill_rules( cleaning_method: pd.DataFrame, *, overrides: Mapping[str, Mapping[str, Any]], - profiles: Mapping[str, pd.Series], + constructed_profiles: Mapping[str, pd.Series], + external_profiles: Mapping[str, pd.Series], ) -> tuple[pd.DataFrame, pd.DataFrame]: """Apply validated advanced-fill rules in configuration order.""" filled = load.copy() methods = cleaning_method.copy() for rule_name, rule in overrides.items(): - profile = profiles.get(rule_name) + method = rule["method"] + + if method == CONSTRUCT_FROM_SOURCES: + profile = constructed_profiles.get(rule_name) + + elif method == EXTERNAL_PROFILE: + profile = external_profiles.get(rule_name) + + else: + profile = None filled, methods = apply_auxiliary_fill_rule( filled, @@ -154,3 +176,61 @@ def apply_auxiliary_fill_rules( ) return filled, methods + + +def apply_external_profile( + load: pd.DataFrame, + cleaning_method: pd.DataFrame, + profile: pd.Series, + *, + country: str, + start: pd.Timestamp, + end: pd.Timestamp, + scope: str, + rule_name: str, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply supplied external values to target demand.""" + filled = load.copy() + methods = cleaning_method.copy() + + if country not in filled.columns: + raise ValueError( + f"Target country {country!r} is not present in load data." + ) + + candidate = profile.loc[ + (profile.index >= start) + & (profile.index < end) + ] + + candidate = candidate.loc[ + candidate.index.intersection(filled.index) + ] + + if scope == "fill_gaps": + replace_index = candidate.index[ + filled.loc[ + candidate.index, + country, + ].isna() + ] + + elif scope == "overwrite": + replace_index = candidate.index + + else: + raise ValueError( + f"Unsupported advanced fill scope: {scope!r}" + ) + + filled.loc[ + replace_index, + country, + ] = candidate.loc[replace_index] + + methods.loc[ + replace_index, + country, + ] = rule_name + + return filled, methods diff --git a/workflow/scripts/cleaning/advanced/methods/external_profile.py b/workflow/scripts/cleaning/advanced/methods/external_profile.py index 0776b81..cdf5578 100644 --- a/workflow/scripts/cleaning/advanced/methods/external_profile.py +++ b/workflow/scripts/cleaning/advanced/methods/external_profile.py @@ -1,36 +1,64 @@ -"""A gap-filling method using an aligned external profile.""" +"""Load and validate locally supplied external demand profiles.""" + +from pathlib import Path import pandas as pd METHOD_NAME = "external_profile" +EXPECTED_COLUMNS = { + "timestamp", + "demand", +} + + +def read_external_profile( + path: str | Path, +) -> pd.Series: + """Read a timestamped external demand series from CSV.""" + profile = pd.read_csv(path) -def apply_external_profile( - load: pd.DataFrame, - *, - profile: pd.DataFrame, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Fill remaining missing cells from an aligned external profile.""" - if not profile.index.equals(load.index): + if set(profile.columns) != EXPECTED_COLUMNS: raise ValueError( - "External profile must use the same index as load." + "External profile must contain exactly the columns " + "'timestamp' and 'demand'." ) - if not profile.columns.equals(load.columns): + timestamps = pd.to_datetime( + profile["timestamp"], + utc=True, + errors="raise", + ) + + if timestamps.duplicated().any(): raise ValueError( - "External profile must use the same columns as load." + "External profile timestamps must be unique." ) - eligible = load.isna() & profile.notna() + if ( + (timestamps.dt.minute != 0).any() + or (timestamps.dt.second != 0).any() + or (timestamps.dt.microsecond != 0).any() + ): + raise ValueError( + "External profile timestamps must be aligned " + "to whole hours." + ) - filled = load.mask( - eligible, - profile, + values = pd.to_numeric( + profile["value"], + errors="raise", ) - newly_filled = ( - load.isna() - & filled.notna() + if values.isna().any(): + raise ValueError( + "External profile demand values must not be missing." + ) + + result = pd.Series( + values.to_numpy(), + index=pd.DatetimeIndex(timestamps), + dtype=float, ) - return filled, newly_filled \ No newline at end of file + return result.sort_index() From 7413ff4f81c5d54398bb954bf6714a5f68565f4d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 14:49:08 +0200 Subject: [PATCH 064/241] Fixed bug and added external profile read tests --- tests/unit/test_external_profile.py | 156 ++++++++++++++++++ .../advanced/methods/external_profile.py | 2 +- 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_external_profile.py diff --git a/tests/unit/test_external_profile.py b/tests/unit/test_external_profile.py new file mode 100644 index 0000000..fa74f0f --- /dev/null +++ b/tests/unit/test_external_profile.py @@ -0,0 +1,156 @@ +"""Tests for externally supplied demand profiles.""" + +import pandas as pd +import pytest +from cleaning.advanced.methods.external_profile import read_external_profile + + +def test_read_external_profile_reads_valid_csv(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:00:00Z,100.0\n" + "2025-01-01T01:00:00Z,110.0\n", + encoding="utf-8", + ) + + result = read_external_profile(path) + + expected = pd.Series( + [100.0, 110.0], + index=pd.DatetimeIndex( + [ + "2025-01-01T00:00:00Z", + "2025-01-01T01:00:00Z", + ], + name="timestamp", + ), + ) + + pd.testing.assert_series_equal( + result, + expected, + ) + + +def test_read_external_profile_allows_sparse_timestamps(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:00:00Z,100.0\n" + "2025-01-03T12:00:00Z,200.0\n", + encoding="utf-8", + ) + + result = read_external_profile(path) + + assert len(result) == 2 + + assert result.loc[ + pd.Timestamp("2025-01-01T00:00:00Z") + ] == 100.0 + + assert result.loc[ + pd.Timestamp("2025-01-03T12:00:00Z") + ] == 200.0 + + +def test_read_external_profile_rejects_duplicate_timestamps(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:00:00Z,100.0\n" + "2025-01-01T00:00:00Z,110.0\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="timestamps must be unique", + ): + read_external_profile(path) + + +def test_read_external_profile_rejects_non_hourly_timestamps(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:30:00Z,100.0\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="whole hours", + ): + read_external_profile(path) + + +def test_read_external_profile_rejects_non_numeric_values(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:00:00Z,not-a-number\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="Unable to parse string", + ): + read_external_profile(path) + + +def test_read_external_profile_rejects_wrong_columns(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "datetime,demand\n" + "2025-01-01T00:00:00Z,100.0\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="must contain exactly the columns", + ): + read_external_profile(path) + + +def test_read_external_profile_rejects_extra_columns(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand,comment\n" + "2025-01-01T00:00:00Z,100.0,test\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="must contain exactly the columns", + ): + read_external_profile(path) + + +def test_read_external_profile_rejects_missing_values(tmp_path): + path = tmp_path / "profile.csv" + + path.write_text( + "timestamp,demand\n" + "2025-01-01T00:00:00Z,\n", + encoding="utf-8", + ) + + with pytest.raises( + ValueError, + match="must not be missing", + ): + read_external_profile(path) + + diff --git a/workflow/scripts/cleaning/advanced/methods/external_profile.py b/workflow/scripts/cleaning/advanced/methods/external_profile.py index cdf5578..da0798a 100644 --- a/workflow/scripts/cleaning/advanced/methods/external_profile.py +++ b/workflow/scripts/cleaning/advanced/methods/external_profile.py @@ -46,7 +46,7 @@ def read_external_profile( ) values = pd.to_numeric( - profile["value"], + profile["demand"], errors="raise", ) From 8268be04854d518e82bb31c3358f85b933e2f2c0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 15:00:24 +0200 Subject: [PATCH 065/241] Added that aux data accepts external_profile changes --- tests/unit/test_plan_auxiliary_data.py | 98 +++++++++++++++++++ .../cleaning/advanced/planning/execution.py | 9 ++ 2 files changed, 107 insertions(+) diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 565df08..f5642a2 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -52,6 +52,21 @@ def _construct_override( } +def _external_profile_override( + *, + country: str = "ALB", + path: str = "resources/user/external_profiles/alb.csv", +) -> dict: + return { + "country": country, + "start": "2020-01-01", + "end": "2020-02-01", + "scope": "fill_gaps", + "method": "external_profile", + "path": path, + } + + def _fill_plan(rule_names: list[str]) -> pd.DataFrame: if not rule_names: return pd.DataFrame() @@ -73,6 +88,7 @@ def _empty_execution_plan() -> dict: "batch_ids_by_source": {}, "groups": {}, "constructed_profile_rule_names": [], + "external_profile_files": {}, } @@ -162,6 +178,7 @@ def test_plan_builds_complete_execution_manifest() -> None: group_id: [entsoe_batch_id, opsd_batch_id], }, "constructed_profile_rule_names": ["fill_albania"], + "external_profile_files": {}, } @@ -388,3 +405,84 @@ def test_plan_is_json_serializable() -> None: ) json.dumps(result) + +def test_plan_records_active_external_profile_file() -> None: + override = _external_profile_override( + path="resources/user/external_profiles/alb_2020.csv", + ) + + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["external_albania"]), + gap_filling_config=_config( + { + "external_albania": override, + } + ), + source_names=["entsoe_api"], + ) + + assert result["external_profile_files"] == { + "external_albania": ( + "resources/user/external_profiles/alb_2020.csv" + ), + } + + assert result["constructed_profile_rule_names"] == [] + assert result["batches"] == [] + + +def test_plan_excludes_inactive_external_profile_file() -> None: + result = build_advanced_execution_plan( + fill_plan=_fill_plan(["active"]), + gap_filling_config=_config( + { + "active": _external_profile_override( + path="resources/user/active.csv", + ), + "inactive": _external_profile_override( + path="resources/user/inactive.csv", + ), + } + ), + source_names=["entsoe_api"], + ) + + assert result["external_profile_files"] == { + "active": "resources/user/active.csv", + } + + +def test_plan_allows_external_profile_file_reuse() -> None: + shared_path = ( + "resources/user/external_profiles/" + "gbr_2000_2025.csv" + ) + + result = build_advanced_execution_plan( + fill_plan=_fill_plan( + [ + "gbr_period_one", + "gbr_period_two", + ] + ), + gap_filling_config=_config( + { + "gbr_period_one": _external_profile_override( + country="GBR", + path=shared_path, + ), + "gbr_period_two": _external_profile_override( + country="GBR", + path=shared_path, + ), + } + ), + source_names=["entsoe_api"], + ) + + assert result["external_profile_files"] == { + "gbr_period_one": shared_path, + "gbr_period_two": shared_path, + } + + diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index 846b00b..b4af543 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -8,6 +8,7 @@ from cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) +from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE from cleaning.advanced.planning.requirements import ( build_auxiliary_acquisition_requirements, ) @@ -61,6 +62,7 @@ def build_advanced_execution_plan( rules: dict[str, dict[str, object]] = {} constructed_profile_rule_names: list[str] = [] + external_profile_files: dict[str, str] = {} batch_plan = {"batches": batches} for rule_name in ordered_active_rule_names: @@ -74,6 +76,11 @@ def build_advanced_execution_plan( ) constructed_profile_rule_names.append(rule_name) + elif override["method"] == EXTERNAL_PROFILE: + external_profile_files[rule_name] = str( + override["path"] + ) + rules[rule_name] = { "override": override, "required_group_ids": required_group_ids, @@ -87,6 +94,7 @@ def build_advanced_execution_plan( "batch_ids_by_source": _index_batch_ids_by_source(batches), "groups": _index_batch_ids_by_group(batches), "constructed_profile_rule_names": constructed_profile_rule_names, + "external_profile_files": external_profile_files, } @@ -100,6 +108,7 @@ def _empty_execution_plan() -> dict[str, object]: "batch_ids_by_source": {}, "groups": {}, "constructed_profile_rule_names": [], + "external_profile_files": {}, } From 2655e40e10794a9a2869169d91e11e70e2a25300 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 15:42:18 +0200 Subject: [PATCH 066/241] Moved the threads assignment for entso-e into snakemake with a cap under internal/settings.json and limited by the number of countries requested. --- tests/integration/test_config.yaml | 10 +++++++++- workflow/internal/settings.yaml | 1 + workflow/rules/automatic.smk | 5 +++++ workflow/scripts/download_load_entsoe_api.py | 15 ++++++--------- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index c25f969..32822dd 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -96,4 +96,12 @@ integration_test: - country: SRB start: "2022-03-01" end: "2022-04-01" - weight: 1 \ No newline at end of file + weight: 1 + + test_external_profile_overwrite: + country: ALB + start: "2022-01-01 00:00" + end: "2022-01-08 00:00" + scope: overwrite + method: external_profile + path: resources/external_profiles/alb_external_test.csv \ No newline at end of file diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index b36a034..dea6c37 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -44,3 +44,4 @@ load_entsoe_api: - "SVN" - "SVK" - "TUR" + MAX_WORKERS: 3 \ No newline at end of file diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 47eab6a..7005ed2 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -48,6 +48,11 @@ rule download_load_entsoe_api: localrule: True conda: "../envs/module.yaml" + threads: + min( + internal["load_entsoe_api"]["MAX_WORKERS"], + len(internal["load_entsoe_api"]["countries"]), + ) params: country_codes=internal["load_entsoe_api"]["countries"], temporal_start=config["temporal_scope"]["start"], diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index e6fb430..817262a 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -1,7 +1,7 @@ """Download electricity load data from ENTSO-E using the entsoe-py library.""" import sys -from concurrent.futures import ThreadPoolExecutor, as_completed #TODO: Check this isnt overruled by snakemake assigning one thread +from concurrent.futures import ThreadPoolExecutor, as_completed from time import perf_counter from typing import TYPE_CHECKING, Any from warnings import warn @@ -17,11 +17,6 @@ if TYPE_CHECKING: snakemake: Any -# Defines how many parallel threads may make calls to ENTSOE. -# Setting this to 3, unsure how ENTSO-E would feel about more -# parallel requests. -MAX_WORKERS = 3 - def load_txt(filepath): """Load text file.""" @@ -101,7 +96,8 @@ def main( country_codes, token, output_load, -): + workers, + ): """Download load in MW via the ENTSO-E API.""" start = as_utc_timestamp(start) end = as_utc_timestamp(end) @@ -115,14 +111,14 @@ def main( print( f"Downloading ENTSO-E load for {total_countries} countries " f"from {start} to {end} using " - f"{MAX_WORKERS} parallel workers...", + f"{workers} parallel workers...", flush=True, ) data_by_country = {} with ThreadPoolExecutor( - max_workers=MAX_WORKERS + max_workers=workers ) as executor: futures = { executor.submit( @@ -266,4 +262,5 @@ def main( country_codes=country_codes, token=snakemake.input.token_entsoe, output_load=snakemake.output.load, + workers=snakemake.threads, ) From a43fe40f5059f86965ffaa0851d3dab8a4ecca6d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 15:56:58 +0200 Subject: [PATCH 067/241] Improved logging on entso-e downloads --- workflow/scripts/download_load_entsoe_api.py | 69 +++++++++++++------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 817262a..4f682bd 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -1,5 +1,6 @@ """Download electricity load data from ENTSO-E using the entsoe-py library.""" +import logging import sys from concurrent.futures import ThreadPoolExecutor, as_completed from time import perf_counter @@ -14,10 +15,35 @@ from entsoe import EntsoePandasClient from entsoe.exceptions import NoMatchingDataError +logger = logging.getLogger(__name__) + if TYPE_CHECKING: snakemake: Any +def configure_logging(log_path) -> None: + """Log downloader progress to both the console and rule log.""" + logger.setLevel(logging.INFO) + logger.handlers.clear() + + formatter = logging.Formatter( + "%(asctime)s | %(levelname)s | %(message)s" + ) + + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(formatter) + + file_handler = logging.FileHandler( + log_path, + mode="w", + encoding="utf-8", + ) + file_handler.setFormatter(formatter) + + logger.addHandler(console_handler) + logger.addHandler(file_handler) + + def load_txt(filepath): """Load text file.""" with open(filepath) as file: @@ -108,11 +134,13 @@ def main( download_start = perf_counter() - print( - f"Downloading ENTSO-E load for {total_countries} countries " - f"from {start} to {end} using " - f"{workers} parallel workers...", - flush=True, + logger.info( + "Downloading ENTSO-E load for %s countries " + "from %s to %s using %s parallel workers.", + total_countries, + start, + end, + workers, ) data_by_country = {} @@ -154,17 +182,17 @@ def main( country_alpha_3 ] = df_country - print( - f"[{completed}/{total_countries}] " - f"Finished {country_alpha_3} " - f"in {elapsed:.1f}s.", - flush=True, + logger.info( + "[%s/%s] Finished %s in %.1fs.", + completed, + total_countries, + country_alpha_3, + elapsed, ) - print( - f"Finished ENTSO-E downloads in " - f"{perf_counter() - download_start:.1f}s.", - flush=True, + logger.info( + "Finished ENTSO-E downloads in %.1fs.", + perf_counter() - download_start, ) # Restore configured country order because futures complete @@ -221,18 +249,15 @@ def main( df.to_parquet(output_load) - print( - f"ENTSO-E processing and write completed in " - f"{perf_counter() - processing_start:.1f}s.", - flush=True, + logger.info( + "ENTSO-E processing and write completed in %.1fs.", + perf_counter() - processing_start, ) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, + configure_logging( + snakemake.log[0] ) plan_path = getattr( From 9b078e3e9ce72f97ea17d21da0af232f7d0ebd9b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 16:11:24 +0200 Subject: [PATCH 068/241] Provided advanced.smk with the same threads determining capability as automatic.smk and fixed the logging of warnings in the entso-e download --- workflow/rules/advanced.smk | 21 ++++++++++++++++++++ workflow/scripts/download_load_entsoe_api.py | 13 ++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index b347678..601d6de 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -199,6 +199,25 @@ def advanced_external_profile_files(_wildcards): ) +def auxiliary_entsoe_threads(wildcards): + """Return useful ENTSO-E threads for one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == wildcards.batch_id + and batch["source"] == "entsoe_api" + ) + ) + + return min( + internal["load_entsoe_api"]["MAX_WORKERS"], + len(batch["countries"]), + ) + + checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, @@ -264,6 +283,8 @@ rule download_auxiliary_load_entsoe_api: localrule: True conda: "../envs/module.yaml" + threads: + auxiliary_entsoe_threads message: "Download auxiliary electricity load from ENTSO-E." script: diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 4f682bd..d3652f8 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -5,14 +5,13 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from time import perf_counter from typing import TYPE_CHECKING, Any -from warnings import warn import pandas as pd import pycountry import yaml from cleaning.advanced.planning.manifest import get_batch, load_execution_plan from common.time import as_utc_timestamp, build_hourly_index -from entsoe import EntsoePandasClient +from entsoe.entsoe import EntsoePandasClient from entsoe.exceptions import NoMatchingDataError logger = logging.getLogger(__name__) @@ -96,10 +95,12 @@ def download_country( df_country.name = country_alpha_3 except NoMatchingDataError: - warn( - f"No data found for " - f"{country_alpha_2}/{country_alpha_3} " - f"in the given period: {start} to {end}" + logger.warning( + "No data found for %s/%s in the given period: %s to %s.", + country_alpha_2, + country_alpha_3, + start, + end, ) df_country = pd.Series( From 4426810e8892cc3484a107516a3674c2ea198f56 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 19:39:08 +0200 Subject: [PATCH 069/241] Cleaned the repo up and generalised the code --- .github/workflows/pr-ci.yml | 8 +- pixi.lock | 10842 +++++++++++++++- pixi.toml | 2 + tests/integration/test_config.yaml | 6 +- tests/unit/test_advanced_combine_auxiliary.py | 2 +- .../test_advanced_construct_from_sources.py | 1 - tests/unit/test_advanced_plan.py | 2 +- tests/unit/test_cleaning_pipeline.py | 10 +- workflow/Snakefile | 1 - workflow/internal/config.schema.yaml | 3 +- workflow/rules/automatic.smk | 18 - workflow/rules/clean.smk | 57 +- workflow/rules/prepare_demand.smk | 2 +- .../cleaning/advanced/combine_auxiliary.py | 52 - .../scripts/cleaning/advanced/gap_report.py | 2 +- .../methods/construct_from_sources.py | 6 + .../cleaning/advanced/planning/execution.py | 58 +- .../cleaning/advanced/planning/manifest.py | 54 + .../cleaning/advanced/planning/plan.py | 13 +- .../cleaning/advanced/planning/selection.py | 53 - workflow/scripts/cleaning/combine_sources.py | 49 +- workflow/scripts/combine_auxiliary_sources.py | 2 +- workflow/scripts/common/schemas.py | 9 +- workflow/scripts/common/time.py | 25 - .../scripts/demand_electricity_polygon.py | 10 +- workflow/scripts/download_load_entsoe_api.py | 7 - workflow/scripts/plan_auxiliary_data.py | 32 +- workflow/scripts/prepare_load_opsd.py | 4 +- 28 files changed, 11018 insertions(+), 312 deletions(-) delete mode 100644 workflow/scripts/cleaning/advanced/combine_auxiliary.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/selection.py diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 09e6709..6df1aa6 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -23,6 +23,10 @@ jobs: id: tests run: pixi run test-integration continue-on-error: true + - name: Run unit tests + id: unit-tests + run: pixi run -e test pytest tests/unit + continue-on-error: true - name: Run snakemake linting id: linting run: | @@ -37,6 +41,6 @@ jobs: path: tests/integration/resources/module/logs if-no-files-found: ignore retention-days: 30 - - name: Fail if integration or linting failed - if: ${{ steps.tests.outcome == 'failure' || steps.linting.outcome == 'failure' }} + - name: Fail if tests or linting failed + if: ${{ steps.tests.outcome == 'failure' || steps.unit-tests.outcome == 'failure' || steps.linting.outcome == 'failure' }} run: exit 1 diff --git a/pixi.lock b/pixi.lock index 90a5103..f603ea6 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1693,6 +1693,1271 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda + test: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.anaconda.org/bioconda/ + packages: + linux-64: + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h77c0f9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cgl-0.60.10-hc46dffc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.0-py314h9e666f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py314h97ea11e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py314haf003cf_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py314hc02f841_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py314hbcf5174_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.7-h2b0a6b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h4916ab3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.13.1-hfc2019e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py314h97ea11e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_hc2c16d8_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.8-default_h08c5240_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.8-default_hd70ba2e_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-hd345f60_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h3a9caae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-h174a0a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-9_h6ae95b6_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h2840a7c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.3-h4c96295_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h9463b59_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-hf4e2dac_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py314hd4c109c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py314h815e797_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py314h9a9c090_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hb71707f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h97ea11e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.0-py314h518bba1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314h2b28147_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py314h8ec4b1a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py314hdafbbf9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py314h969be7f_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py314hbcf5174_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py314h7ce3bca_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py314h3987850_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-h242f9ac_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py314h91cf270_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-h50b1954_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h1bee95f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314h0f05182_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.2-h462bb3b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py314hf07bd8e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py314hbe3edd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.6-h171cf75_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h04a0ce9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-h1964d1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyha191276_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh01cf8df_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h3daef5d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314hd58f0ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cgl-0.60.10-h034796e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.0-py314hcec6336_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.7-h4e57454_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-hd20048c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-hd03a632_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.13.1-hf76c51c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.3.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py314hf8a3a22_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h2062a1b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h6fbacd7_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-h21e278e_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.3.0-h5a65909_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-h934fa54_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-9_h1b118fd_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h8daa630_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.3-he8aa2a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h7d962ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-h1ae2325_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-hdb7fadc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314hdf3942c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.0-py314h2fbedac_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314hb79c6fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-ha88f16d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py314h54f3292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314ha82c7e6_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-hf4d206d_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h784d473_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hcf2a89d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314he1d1ac0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314ha14b1ff_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.2-h828de30_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.6-h3feff0a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-h77b7338_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + win-64: + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh7428d3b_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyhe2676ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.14.2-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.3.2-h1da161f_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.7.1-h88938e3_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.11.0-h667fc28_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.27.3-hbdc738f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.16.0-hd7b40c3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.12.8-hd7ee1a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.2.7-h1da161f_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.2.10-h1da161f_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.40.1-he7c1723_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.16.3-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.13.3-hf9fdf8e_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.18.0-h4fb3251_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314he701e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cgl-0.60.10-h7c5f3e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.0-py314hb821551_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glib-2.88.3-h89924da_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/glib-tools-2.88.3-hf027272_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.13.1-h11686cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/graphviz-14.1.2-h4c50273_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.3.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h0eb2380_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_he24518a_100.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-9_h8455456_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-9_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_h2778606_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.1.0-h110b43a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.1.0-h8ee18e1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.3.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-devel-14.3.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-devel-0.22.5-h5728263_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-9_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h0ffbb96_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_234.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314hf309875_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.0-py314h13f4da2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-hd2b5f0e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py314h9f07db2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.1-py314h447aaf0_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h53f6dd8_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hcaaf0b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.1-pl5321hfcac499_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-h03e4b45_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314hc980628_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.2-h6b72154_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.6-h477610d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/wrapt-1.17.3-py314h5a2d7ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hba3369d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-h0e40799_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda sha256: 71fd599b42cd1352bb8a7d278eef9b681b8de5efe210f358101ce47de5a4d699 @@ -1707,6 +2972,22 @@ packages: license_family: MIT size: 47149 timestamp: 1781674515403 +- conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda + sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 + md5: e9f9d8d88a22521378dace6b7399d59b + depends: + - black >=26.3.1,<27.0 + - click >=8.2.0,<9.0 + - go-shfmt >=3.13,<4.0 + - pathspec >=1.0.0 + - python >=3.11,<4.0 + license: MIT + license_family: MIT + run_exports: + weak: + - snakefmt >=2.0.3,<2.1.0a0 + size: 47262 + timestamp: 1784295220199 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda sha256: 1e8393a8ef060c62ef963fa9e0b3cc8f69b1dec4bdb51599c0dfcdeb9b8c7d12 md5: a5ec344abd4b0cdda5a52e870e4d25a3 @@ -1717,6 +2998,9 @@ packages: - python >=3.8 license: MIT license_family: MIT + run_exports: + weak: + - snakemake-interface-common >=1.23.0,<2.0a0 size: 22751 timestamp: 1780096495612 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda @@ -1729,6 +3013,9 @@ packages: - throttler >=1.2.2,<2.0.0 license: MIT license_family: MIT + run_exports: + weak: + - snakemake-interface-executor-plugins >=9.4.0,<10.0a0 size: 25394 timestamp: 1772990565157 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda @@ -1738,6 +3025,9 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.17.4,<2.0.0 license: MIT + run_exports: + weak: + - snakemake-interface-logger-plugins >=2.1.0,<3.0a0 size: 21021 timestamp: 1779291141116 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda @@ -1747,6 +3037,9 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.16.0,<2.0.0 license: MIT + run_exports: + weak: + - snakemake-interface-report-plugins >=1.3.0,<2.0a0 size: 14490 timestamp: 1761910544502 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda @@ -1756,6 +3049,9 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.20.1,<2.0.0 license: MIT + run_exports: + weak: + - snakemake-interface-scheduler-plugins >=2.0.2,<3.0a0 size: 16446 timestamp: 1760984180933 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda @@ -1766,15 +3062,58 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.12.0,<2.0.0 - tenacity >=9.1.4,<10.0 - - throttler >=1.2.2,<2.0.0 - - wrapt >=1.15.0,<2.0.0 + - throttler >=1.2.2,<2.0.0 + - wrapt >=1.15.0,<2.0.0 + license: MIT + license_family: MIT + run_exports: + weak: + - snakemake-interface-storage-plugins >=4.4.1,<5.0a0 + size: 22783 + timestamp: 1773699846635 +- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda + sha256: 6d7a1d2e308506e867121edb6d258dbbb359ce3287631e54601e33edb110485d + md5: ff39f8e27137655553f9fbb842ee1aa2 + depends: + - conda-inject >=1.3.1,<2.0 + - configargparse + - connection_pool >=0.0.3 + - docutils >=0.20,<0.23 + - dpath >=2.1.6,<3.0.0 + - gitpython + - humanfriendly + - immutables + - jinja2 >=3.0,<4.0 + - jsonschema + - nbformat + - packaging >=24.0,<26 + - platformdirs + - psutil + - pulp >=2.3.1,<3.4 + - python >=3.11 + - pyyaml + - referencing + - requests >=2.8.1,<3.0 + - smart_open >=4.0,<8.0 + - snakemake-interface-common >=1.20.1,<2.0 + - snakemake-interface-executor-plugins >=9.3.2,<10.0 + - snakemake-interface-logger-plugins >=1.1.0,<3.0.0 + - snakemake-interface-report-plugins >=1.2.0,<2.0.0 + - snakemake-interface-scheduler-plugins >=2.0.0,<3.0.0 + - snakemake-interface-storage-plugins >=4.3.2,<5.0 + - sqlmodel >=0.0.37,<0.0.38 + - tabulate + - tenacity >=9.1.4,<10.0 + - throttler + - wrapt + - yte >=1.5.5,<2.0 license: MIT license_family: MIT - size: 22783 - timestamp: 1773699846635 -- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda - sha256: 6d7a1d2e308506e867121edb6d258dbbb359ce3287631e54601e33edb110485d - md5: ff39f8e27137655553f9fbb842ee1aa2 + size: 888303 + timestamp: 1781821379807 +- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda + sha256: 643957e2ec7dc2c7d77e4c5e9b89621d264b14d4b2cd83a23c0e645e5b004182 + md5: d639ba3846ef46c86a57bed676115deb depends: - conda-inject >=1.3.1,<2.0 - configargparse @@ -1801,7 +3140,7 @@ packages: - snakemake-interface-logger-plugins >=1.1.0,<3.0.0 - snakemake-interface-report-plugins >=1.2.0,<2.0.0 - snakemake-interface-scheduler-plugins >=2.0.0,<3.0.0 - - snakemake-interface-storage-plugins >=4.3.2,<5.0 + - snakemake-interface-storage-plugins >=4.4.1,<5.0 - sqlmodel >=0.0.37,<0.0.38 - tabulate - tenacity >=9.1.4,<10.0 @@ -1810,8 +3149,11 @@ packages: - yte >=1.5.5,<2.0 license: MIT license_family: MIT - size: 888303 - timestamp: 1781821379807 + run_exports: + weak: + - snakemake-minimal >=9.25.1,<10.0a0 + size: 889521 + timestamp: 1785882296560 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 @@ -1857,6 +3199,23 @@ packages: license_family: MIT size: 1125371 timestamp: 1780396651124 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda + noarch: python + sha256: 25c15e85cb03a1c4d43d02d0ff37c8c9a970c3e84abeaf420fc190bb053bbe3f + md5: e46fc69f093a840e98b9e2c29727862d + depends: + - python >=3.10 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + run_exports: {} + size: 1116952 + timestamp: 1786328964477 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -1868,6 +3227,9 @@ packages: - libglib >=2.68.1,<3.0a0 license: LGPL-2.1-or-later license_family: LGPL + run_exports: + weak: + - at-spi2-atk >=2.38.0,<3.0a0 size: 339899 timestamp: 1619122953439 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 @@ -1882,6 +3244,9 @@ packages: - xorg-libxtst license: LGPL-2.1-or-later license_family: LGPL + run_exports: + weak: + - at-spi2-core >=2.40.3,<2.41.0a0 size: 658390 timestamp: 1625848454791 - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda @@ -1895,8 +3260,29 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - atk-1.0 >=2.38.0 size: 355900 timestamp: 1713896169874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + sha256: 845fe32ee750d4a4d3efdb1d95a8c29c3388e3ea9787b77341ec4abe4e198b11 + md5: 4347d5ffdf34adf9c5edd10837727d96 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-auth >=0.10.4,<0.10.5.0a0 + size: 135073 + timestamp: 1784086842394 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.30-hec5e740_0.conda sha256: 5735b8ae76580e81662137c4dafca62cd9da8083b5b7bebe8ea7e9a806f1053f md5: bc1b9f70ea7fa533aefa6a8b6fbe8da7 @@ -1930,6 +3316,34 @@ packages: - aws-c-cal >=0.7.4,<0.7.5.0a0 size: 47532 timestamp: 1725829965837 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + sha256: a67c944be1728f576c02fe8f28b0cbb78b5353415075db3da4ef71bc9dd8c00c + md5: 9c6072e7b882d35ac956c07e493d6a9e + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - libgcc >=14 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-cal >=0.9.14,<0.9.15.0a0 + size: 56752 + timestamp: 1784043396713 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + sha256: 701398f1c9978c41e93cb0947869a66b7f8004e9d6e548d63d11aedae83cfb02 + md5: f3e0b2e044485ae90d4a59c77e7a0182 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-common >=0.14.2,<0.14.3.0a0 + size: 246263 + timestamp: 1783637234072 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda sha256: febe894ae2f5bfc4d65c51bd058433e9061d994ff06b30d5eca18919639c5083 md5: 1b53af320b24547ce0fb8196d2604542 @@ -1957,6 +3371,20 @@ packages: - aws-c-compression >=0.2.19,<0.2.20.0a0 size: 19116 timestamp: 1725829968483 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + sha256: 8e0a5513cfc42df8bd16adbd8e83a37d3ca89b8e04cb20eb04eb177bbbfea365 + md5: c9be3f5854f349ae77a1a174b59e11a8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-compression >=0.3.2,<0.3.3.0a0 + size: 22301 + timestamp: 1784042853709 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h235a6dd_1.conda sha256: 987b3654e7cbb8ead0227c2442a02b6c379d21bb1509a834c423d492a4862706 md5: c05358e3a231195f7f0b3f592078bb0c @@ -1974,6 +3402,40 @@ packages: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 size: 53989 timestamp: 1725856758424 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + sha256: b1f35f7b7a5e745e2d56cf93212770bb56f5207fa9f0e38f98b0c05a1b898a90 + md5: 204d1e8d60c3ae839bd1898e4c7742c8 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + size: 59633 + timestamp: 1784075699558 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + sha256: 4b939b4a76a11c9ec50c891ae9bf232da8dcaf8797701df1e79ae51c988be2d4 + md5: 97f2799ae6ff7b6f52dfa321fef9676b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-http >=0.11.0,<0.11.1.0a0 + size: 231294 + timestamp: 1784075685646 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.9-h5e77a74_0.conda sha256: 9eac64d76ba4a799689392eb03bbbc93fd169ccbcb7719bcccbe2e9100ff2075 md5: d7714013c40363f45850a25113e2cb05 @@ -2007,6 +3469,22 @@ packages: - aws-c-io >=0.14.18,<0.14.19.0a0 size: 158169 timestamp: 1728562824182 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + sha256: 94c32ca672ce290e250aea1cfb92582cca4658bdc3ec7cb1ceef254f34451595 + md5: bd19b685b88557830f1a617a6d404eb2 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - s2n >=1.7.5,<1.7.6.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-io >=0.27.3,<0.27.4.0a0 + size: 183100 + timestamp: 1784054829913 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.5-h0009854_0.conda sha256: 2345d19078aa7c40aaa6b95a7d0e019ddbf3518ff47dc09fadd69f4e1a7073a5 md5: d393d0a6c9b993771fbc67a998fccf6c @@ -2023,6 +3501,42 @@ packages: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 size: 194112 timestamp: 1726205708559 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + sha256: 0252f072e2f493f8348575938c69b48b2799f29f0489c4ad2c5a919ab7e3d02e + md5: 9728195c2f600ef427a1964ee193e707 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + size: 224933 + timestamp: 1784087527918 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + sha256: a423bffbb0e6cacb5e2a0861b918ba3180e5132509afb1faf225ee9f0ec8f981 + md5: 706aa99414d18db5b23475b45cc93a0b + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - openssl >=3.5.7,<4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + size: 156174 + timestamp: 1784100985258 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.5-hbaf354b_4.conda sha256: 03d8bd937ed49c857255483fa5d252064121adfbccab46aa0b3716de75022747 md5: 2cefeb144de7712995d1b52cc6a3864c @@ -2057,6 +3571,20 @@ packages: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 size: 55799 timestamp: 1725836731034 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + sha256: e419e179e04021fc680d98af23fac4b4c2369cea61171087e57a734e968dd9bd + md5: 816f62fa82532118ebb2398382090945 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + size: 68515 + timestamp: 1784044260935 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.18-h756ea98_11.conda sha256: c343bc670bdb52248fc039cbd1cba20fe1d18af81960ab43153d9b55dfb08bc1 md5: eadcc12bedac44f13223a2909c0e5bcc @@ -2071,6 +3599,20 @@ packages: - aws-checksums >=0.1.18,<0.1.19.0a0 size: 49962 timestamp: 1725836852149 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + sha256: 6cc8617854a43040291dea791fe111ba408e7a5bbd9ee9e5a99375da7a16846b + md5: 24ee781effc5779206a80139323c9caf + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-checksums >=0.2.10,<0.2.11.0a0 + size: 101904 + timestamp: 1784044248506 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.28.2-h6c0439f_6.conda sha256: 02b1494f6be3d8e5f40afea1ff2c24cb7f0b42d5d18761a99b43f0faa7c39d29 md5: 4e472c316d08af60faeb71f86d7563e1 @@ -2094,6 +3636,29 @@ packages: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 size: 350331 timestamp: 1726483498115 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + sha256: e0c3a119c7035a00e0da325187ee723e07e0e6337e50362349d178ab40961f97 + md5: 3315ce09371baf6d70248b8927aa9866 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + size: 416399 + timestamp: 1784113232967 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.379-h5a9005d_9.conda sha256: cc2227d97f5e7aed68aeb274a2bec0236af5c20519bde200c8ea7cba114ec978 md5: 5dc18b385893b7991a3bbeb135ad7c3e @@ -2115,6 +3680,25 @@ packages: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 size: 2934257 timestamp: 1725944617781 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + sha256: 0c15c036c3b71471eecc58bbe08a68c66fd6a051b548dca7675c7c924ed3972b + md5: c65efa87d532339a090c87093097bf09 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - libcurl >=8.21.0,<9.0a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + size: 3394321 + timestamp: 1784137257627 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.13.0-h935415a_0.conda sha256: b7e0a22295db2e1955f89c69cefc32810309b3af66df986d9fb75d89f98a80f7 md5: debd1677c2fea41eb2233a260f48a298 @@ -2131,6 +3715,38 @@ packages: - azure-core-cpp >=1.13.0,<1.13.1.0a0 size: 338134 timestamp: 1720853194547 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + sha256: fffc66e9be8806a92b314e27129c42b1298ea7065028c9e5d175dacb07829261 + md5: 0cabc152dc8896c3a4c4aebf2b308627 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.19.0,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + size: 349147 + timestamp: 1775238757304 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + sha256: ebca774f1ebaa24c150730603b418b33fc7862811a16d097716b1a29f34798c5 + md5: f4fc754ec17176046988c46a54962a8c + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + size: 250737 + timestamp: 1781268163278 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.8.0-hd126650_2.conda sha256: f85452eca3ae0e156b1d1a321a1a9f4f58d44ff45236c0d8602ab96aaad3c6ba md5: 36df3cf05459de5d0a41c77c4329634b @@ -2163,6 +3779,40 @@ packages: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 size: 523120 timestamp: 1721865032339 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + sha256: 04bb27dbf1d426b4447b28c3dc92ec01c807fa784eea86ffa1f8c2bd9b9e8076 + md5: 43151a3b0a8e037747d79a82dff9f967 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + size: 589716 + timestamp: 1781283775801 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + sha256: 8a806f9852d280926d7613df548889b49e2a1c5d836385bcb2cedb24e7b08c64 + md5: 7896f4a6ee78538e2d0261e3b36dfa69 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + size: 159394 + timestamp: 1781268171097 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.7.0-h10ac4d7_1.conda sha256: 1030fa54497a73eb78c509d451f25701e2e781dc182e7647f55719f1e1f9bee8 md5: ab6d507ad16dbe2157920451d662e4a1 @@ -2197,6 +3847,41 @@ packages: - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 size: 274492 timestamp: 1721925100762 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + sha256: 13e2e6eb942f65bf81e9089bf6b5926534d9245c781288c5270beb3a25c9156b + md5: 70972c9cb0893d6499dd4118415a966b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + size: 308699 + timestamp: 1781538835962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d + md5: 2c2fae981fd2afd00812c92ac47d023d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - blosc >=1.21.6,<2.0a0 + size: 48427 + timestamp: 1733513201413 - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda sha256: 6cc260f9c6d32c5e728a2099a52fdd7ee69a782fff7b400d0606fcd32e0f5fd1 md5: 54fe76ab3d0189acaef95156874db7f9 @@ -2232,6 +3917,24 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 19883 timestamp: 1756599394934 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 + md5: 8ccf913aaba749a5496c17629d859ed1 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.2.0 hb03c661_1 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 20103 + timestamp: 1764017231353 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda sha256: 444903c6e5c553175721a16b7c7de590ef754a15c28c99afbc8a963b35269517 md5: ca4ed8015764937c81b830f7f5b68543 @@ -2245,6 +3948,19 @@ packages: run_exports: {} size: 19615 timestamp: 1756599385418 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 + md5: af39b9a8711d4a8d437b52c1d78eb6a1 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: {} + size: 21021 + timestamp: 1764017221344 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda sha256: 52a9ac412512b418ecdb364ba21c0f3dc96f0abbdb356b3cfbb980020b663d9b md5: fd0e7746ed0676f008daacb706ce69e4 @@ -2274,8 +3990,22 @@ packages: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT + run_exports: {} size: 367376 timestamp: 1764017265553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 + md5: e675fabcf81499adc7edf58124fb1e01 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 257808 + timestamp: 1785906269155 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 md5: d2ffd7602c02f2b316fd921d39876885 @@ -2302,6 +4032,21 @@ packages: - c-ares >=1.34.6,<2.0a0 size: 207882 timestamp: 1765214722852 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda + sha256: 5139b6afbfaca91c47104ba9a6a40f81211e1c9200e96b73ce3a56f0ca1902f4 + md5: 2cef891b791040aab83e218c7d137679 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - c-ares-static <0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 226755 + timestamp: 1786116641939 - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 md5: 09262e66b19567aff4f592fb53b28760 @@ -2354,6 +4099,9 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.1-only or MPL-1.1 + run_exports: + weak: + - cairo >=1.18.4,<2.0a0 size: 989514 timestamp: 1766415934926 - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda @@ -2385,6 +4133,20 @@ packages: license_family: MIT size: 300271 timestamp: 1761203085220 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h77c0f9c_1.conda + sha256: f2be3de194658ef1733c11381fcec2853898d598e602ccbe5745d0a0700e0899 + md5: 452e8b9d54b4ba7ab6e8129bc8a7a3ee + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=14 + - pycparser + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + run_exports: {} + size: 305586 + timestamp: 1786528481627 - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.4.1-hf8ad068_0.conda sha256: 74ed4d8b327fa775d9c87e476a7221b74fb913aadcef207622596a99683c8faf md5: 1b7a01fd02d11efe0eb5a676842a7b7d @@ -2424,6 +4186,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cbc >=2.10.13,<2.11.0a0 size: 910148 timestamp: 1778586394891 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cgl-0.60.10-hc46dffc_1.conda @@ -2448,6 +4213,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cgl >=0.60,<0.61.0a0 size: 533910 timestamp: 1778571381173 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda @@ -2471,6 +4239,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-clp >=1.17,<1.18.0a0 size: 1153761 timestamp: 1778519879680 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda @@ -2493,6 +4264,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-osi >=0.108,<0.109.0a0 size: 378434 timestamp: 1778511053515 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda @@ -2512,6 +4286,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-utils >=2.11,<2.12.0a0 size: 664399 timestamp: 1778500281099 - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.5.3-py314hdafbbf9_0.conda @@ -2551,6 +4328,43 @@ packages: license_family: BSD size: 1357035 timestamp: 1781680868886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.0-py314h9e666f3_0.conda + sha256: 4c98ae3e43688b0b350c3eaa96b61725ee32f4ec0990fce848543c51efff407b + md5: ce8f5df54fb2508d175ff6aa558821b1 + depends: + - archspec >=0.2.3 + - boltons >=23.0.0 + - charset-normalizer + - conda-package-handling >=2.2.0 + - distro >=1.5.0 + - frozendict >=2.4.2 + - jsonpatch >=1.32 + - menuinst >=2 + - packaging >=23.0 + - platformdirs >=3.10.0 + - pluggy >=1.6.0 + - pycosat >=0.6.3 + - python + - requests >=2.28.0,<3 + - ruamel.yaml >=0.11.14,<0.19 + - setuptools >=60.0.0 + - tqdm >=4 + - truststore >=0.8.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python_abi 3.14.* *_cp314 + constrains: + - conda-build >=25.9 + - conda-content-trust >=0.3.0 + - conda-env >=2.6 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1464668 + timestamp: 1785769797579 - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.10.1-py314h9e666f3_2.conda sha256: 989d9ab43bf25e1acff1b7519c96ac156ccdf46a2df605aff8a60ad85dbd6356 md5: 4689abef13301a7556f0c3f6d3addee7 @@ -2570,6 +4384,27 @@ packages: license_family: MIT size: 318028 timestamp: 1781646467890 +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda + sha256: f864a806c98c44a0cdc4ede1b42fa6cdcb9d327d8d0013f51af9344f553d0cbf + md5: d8cc5cf325134981f65dd72c8e082433 + depends: + - python + - pip >=23.0.1 + - packaging + - unearth + - python-build + - python-installer >=1.0 + - platformdirs + - conda-index >=0.11.0 + - conda-package-streaming >=0.11 + - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 + license: MIT + license_family: MIT + run_exports: {} + size: 323834 + timestamp: 1784203010834 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 @@ -2585,6 +4420,21 @@ packages: run_exports: {} size: 320386 timestamp: 1769155979897 +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py314h97ea11e_4.conda + sha256: b0314a7f1fb4a294b1a8bcf5481d4a8d9412a9fee23b7e3f93fb10e4d504f2cc + md5: 95bede9cdb7a30a4b611223d52a01aa4 + depends: + - numpy >=1.25 + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 324013 + timestamp: 1769155968691 - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_0.conda sha256: 0d9405d9f2de5d4b15d746609d87807aac10e269072d6408b769159762ed113d md5: d17488e343e4c5c0bd0db18b3934d517 @@ -2595,6 +4445,17 @@ packages: license: CC0-1.0 size: 24283 timestamp: 1756734785482 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda + sha256: 6a905f317b93104444dc74875933a7cdf65fa14ae05dd6485ece6ffd9d0b5e89 + md5: 451367db888b29ad0db6b298669fbe77 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: CC0-1.0 + run_exports: {} + size: 24388 + timestamp: 1785920465106 - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda sha256: e3127e539763a0e16fb7977fac8fc42e5fe6df0517757f56283df1b7567ac6bd md5: 6f283e8bb11a748bc30af46258683ba8 @@ -2612,6 +4473,40 @@ packages: run_exports: {} size: 1817713 timestamp: 1763019879546 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py314haf003cf_2.conda + sha256: c0fa696e7120421450d7b35baeb94b905bf0e48b90a0f6287265ee4eeb5ba18e + md5: 24b0934613e9e0404436ee469629cb74 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + run_exports: {} + size: 1817375 + timestamp: 1763019875032 +- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda + sha256: 8f14ff477bf2cdf3cd4ff0ab2486b3e6490350da650e67be6c6a0b8a18994a90 + md5: a91f709770cddb5e7a027ab000c441a5 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libcurl 8.21.0 heca4667_4 + - libgcc >=14 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + run_exports: {} + size: 195067 + timestamp: 1785500114646 - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda sha256: 13d843b27791294f7e0f01a49de3a1fbc873bb8f193f93a7cd86ca012bfce172 md5: 2e7dedf73dfbfcee662e2a0f6175e4bb @@ -2629,6 +4524,24 @@ packages: run_exports: {} size: 169240 timestamp: 1722439933267 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 + md5: af491aae930edc096b58466c51c4126c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.5,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + run_exports: + weak: + - cyrus-sasl >=2.1.28,<3.0a0 + size: 210103 + timestamp: 1771943128249 - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f md5: cae723309a49399d2949362f4ab5c9e4 @@ -2661,6 +4574,20 @@ packages: run_exports: {} size: 623770 timestamp: 1771855837505 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py314h5bd0f2a_2.conda + sha256: 5edb3c0fbf5c39b66f71fef0264d4fbab561f6adbdd7ef88188b725a82fcd6da + md5: a6a32cab83d59c7812ddbb03220057e3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - toolz >=0.10.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 641304 + timestamp: 1771855845410 - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 sha256: 8f5f995699a2d9dbdd62c61385bfeeb57c82a681a7c8c5313c395aa0ccab68a5 md5: ecfff944ba3960ecb334b9a2663d708d @@ -2686,6 +4613,9 @@ packages: - libglib >=2.86.2,<3.0a0 - libexpat >=2.7.3,<3.0a0 license: AFL-2.1 OR GPL-2.0-or-later + run_exports: + weak: + - dbus >=1.16.2,<2.0a0 size: 447649 timestamp: 1764536047944 - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda @@ -2699,6 +4629,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 2842115 timestamp: 1780390153580 - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda @@ -2715,6 +4646,20 @@ packages: - double-conversion >=3.3.1,<3.4.0a0 size: 69544 timestamp: 1739569648873 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc + md5: dbe3ec0f120af456b3477743ffd99b74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - double-conversion >=3.4.0,<3.5.0a0 + size: 71809 + timestamp: 1765193127016 - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda sha256: a5b51e491fec22bcc1765f5b2c8fff8a97428e9a5a7ee6730095fb9d091b0747 md5: 057083b06ccf1c2778344b6dabace38b @@ -2735,6 +4680,9 @@ packages: - xorg-libxxf86vm >=1.1.6,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - epoxy >=1.5.10,<1.6.0a0 size: 411735 timestamp: 1758743520805 - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda @@ -2769,6 +4717,24 @@ packages: run_exports: {} size: 570988 timestamp: 1778864779103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py314hc02f841_0.conda + sha256: 344f92cb3e2e5e9d4d98219556b2987cbd6cead93e8c80be4fc824ba63e1911d + md5: 7eacd1ab0881bd50ebf2824ae227098d + depends: + - __glibc >=2.17,<3.0.a0 + - cramjam >=2.3 + - fsspec + - libgcc >=14 + - numpy >=1.23,<3 + - packaging + - pandas >=1.5.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 581290 + timestamp: 1778864786750 - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda sha256: 36f7f5d852a28c2a18d1cd31c02bb3ec0bfdb6b3ee1c3a0118827110e5457d18 md5: 4a30f4277a1894928a7057d0e14c1c95 @@ -2792,6 +4758,27 @@ packages: run_exports: {} size: 1171712 timestamp: 1726664552119 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py314hbcf5174_6.conda + sha256: 6a25e057b4fc912ea56accdfd8b083653861c2b61fb875584385c9c27223334b + md5: 9973d51b9bb91ae8cca8cfff05d9106b + depends: + - __glibc >=2.17,<3.0.a0 + - attrs >=19.2.0 + - click >=8.0,<9.dev0 + - click-plugins >=1.0 + - cligj >=0.5 + - libgcc >=14 + - libgdal-core >=3.12.1,<3.13.0a0 + - libstdcxx >=14 + - pyparsing + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - shapely + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1203455 + timestamp: 1767051530201 - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.0.2-h07f6e7f_1.conda sha256: f9eeb49752b250e035ed0ee957fd813f7d2212eccb814006bbf92b89711605b1 md5: fbbfeaa24a99c3da00c35334935b0d24 @@ -2806,6 +4793,20 @@ packages: - fmt >=11.0.2,<11.1.0a0 size: 197559 timestamp: 1743030768885 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + sha256: 25d14a197601fdd616f2e501431b52887e69b31e7defbc1679381d718357ceb7 + md5: a70bb6d42ad121aef456e0007e92bed6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - fmt >=12.1.0,<12.2.0a0 + size: 199072 + timestamp: 1785915412102 - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda sha256: d4e92ba7a7b4965341dc0fca57ec72d01d111b53c12d11396473115585a9ead6 md5: f7d7a4104082b39e3b3473fbd4a38229 @@ -2836,6 +4837,25 @@ packages: - fonts-conda-ecosystem size: 281880 timestamp: 1780450077431 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda + sha256: 05b57e2f5aaf3adcccc80653c9795d29974f24dcf9daa40c98b254c99b726227 + md5: bc8843ae1a655600014bb7a09d7b8b89 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libuuid >=2.42.2,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + size: 296480 + timestamp: 1786381145956 - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda sha256: d235ae7075642044ceb3d922ef2a710a82665755ac9bbb7e8dad7daa72bc6d87 md5: 294fb524171e2a2748cb7fe708aba826 @@ -2865,6 +4885,19 @@ packages: - libfreetype6 >=2.14.3 size: 173839 timestamp: 1774298173462 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda + sha256: d109354d4584aa1ef6e915a8f02cbe13a9708f384aa167c075953b6f8196111c + md5: 8dd74ae1edf2b6490af0e960be773431 + depends: + - libfreetype 2.14.3 ha770c72_1 + - libfreetype6 2.14.3 h73754d4_1 + license: GPL-2.0-only OR FTL + run_exports: + weak: + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + size: 174748 + timestamp: 1785641176027 - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda sha256: c8960e00a6db69b85c16c693ce05484facf20f1a80430552145f652a880e0d2a md5: ecb5d11305b8ba1801543002e69d2f2f @@ -2890,6 +4923,18 @@ packages: license: LGPL-2.1-or-later size: 61244 timestamp: 1757438574066 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + sha256: 4846a3ca0402f3fe33ad84ed50ab213c6aafde4a0faef3c5002f6bf753e21671 + md5: 1cd10eda5692519d01bb20e086e214c9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + run_exports: + weak: + - fribidi >=1.0.16,<2.0a0 + size: 61782 + timestamp: 1785912528684 - conda: https://conda.anaconda.org/conda-forge/linux-64/gdal-3.9.2-py312h1299960_7.conda sha256: 656eea557c38f713cb6d94efa798ecae9b70b807976d2e99fd7080462575098d md5: 9cf27e3f9d97ea13f250db9253a25dc8 @@ -2923,6 +4968,24 @@ packages: license_family: LGPL size: 577414 timestamp: 1774985848058 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.7-h2b0a6b4_0.conda + sha256: 1c22e37f9d7e06e9e0582ee5a55c2ddd19ea75f71f44eb13b56f504ef5c37aa5 + md5: 5d355db3e937086e22cf4cb5fe19787c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libglib >=2.88.2,<3.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + run_exports: + weak: + - gdk-pixbuf >=2.44.7,<3.0a0 + size: 581631 + timestamp: 1782591374199 - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda sha256: bc3860e6689be6968ca5bae3660f43dd3e22f4dd61c0bfc99ffd0d0daf4f7a73 md5: aab9195bc018b82dc77a84584b36cce9 @@ -2936,6 +4999,19 @@ packages: - geos >=3.12.2,<3.12.3.0a0 size: 1737633 timestamp: 1721746525671 +- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + sha256: 08896dcd94e14a83f247e91748444e610f344ab42d80cbf2b6082b481c3f8f4b + md5: 4d4efd0645cd556fab54617c4ad477ef + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.1-only + run_exports: + weak: + - geos >=3.14.1,<3.14.2.0a0 + size: 1974942 + timestamp: 1761593471198 - conda: https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.3-hf7fa9e8_2.conda sha256: 3ecd04a14cb3d64f0641828aa9e918895b508809aedf7b5b0ec712c6957b5815 md5: 1d6bdc6b2c62c8cc90c67b50142d7b7f @@ -2969,6 +5045,33 @@ packages: - gflags >=2.2.2,<2.3.0a0 size: 119654 timestamp: 1726600001928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda + sha256: f7026323ac769ef432c747a6c22abd39c583d0d4f6985a7100fb371e2167f214 + md5: 04be12e7dd050aaf364ece2393988e85 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - gflags >=2.3.1,<2.4.0a0 + size: 130991 + timestamp: 1785044715896 +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda + sha256: 3611c5d605cd56017704e6c01c7bd849e74a656a546d4c49d655fde49ddfeecc + md5: bd7c3a8586ed0101f094962100d30a63 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - giflib >=5.2.2,<5.3.0a0 + size: 77403 + timestamp: 1784694210187 - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff md5: 3bf7b9fd5a7136126e0234db4b87c8b6 @@ -2992,6 +5095,33 @@ packages: license: LGPL-2.1-or-later size: 236955 timestamp: 1778508800134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h4916ab3_1.conda + sha256: ffa59cd7b517ed0a5905cc83643d08117f073502b0bdb1cab1835805ecc8da4e + md5: 246c12ff18139b126586eb2d0e906c7e + depends: + - libglib ==2.88.3 h45c3219_1 + - libffi + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + run_exports: {} + size: 238159 + timestamp: 1786457663614 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + sha256: f24feeda3aeec3a2cbb9fe2e0fabc4785372e70b6cc838c96023e08e17f4bfa0 + md5: 6941f96b8a8056677d79b4f5a2c4aaca + depends: + - __glibc >=2.17,<3.0.a0 + - gflags >=2.3.0,<2.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - glog >=0.7.1,<0.8.0a0 + size: 149031 + timestamp: 1784094370091 - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 md5: ff862eebdfeb2fd048ae9dc92510baca @@ -3011,6 +5141,7 @@ packages: md5: 1e152fa4fe2a7b77d4b86c76a6a85b50 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 2008937 timestamp: 1775514521503 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda @@ -3060,6 +5191,9 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other + run_exports: + weak: + - graphviz >=14.1.2,<15.0a0 size: 2426455 timestamp: 1769427102743 - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.2-py314h42812f9_0.conda @@ -3075,6 +5209,20 @@ packages: license_family: MIT size: 267632 timestamp: 1781762129332 +- conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda + sha256: 8077b6042be044d2c571d5eb340a1ad7a1949534113fb8d9cafb213982b7c60b + md5: a0423baf08abf2f98136e8cc103e46dd + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 278368 + timestamp: 1786384047792 - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda sha256: c6bb4f06331bcb0a566d84e0f0fad7af4b9035a03b13e2d5ecfaf13be57e6e10 md5: bcaea22d85999a4f17918acfab877e61 @@ -3115,6 +5263,10 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - gtk3 >=3.24.52,<4.0a0 + - adwaita-icon-theme size: 5939083 timestamp: 1774288645605 - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda @@ -3126,6 +5278,9 @@ packages: - libstdcxx-ng >=12 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - gts >=0.7.6,<0.8.0a0 size: 318312 timestamp: 1686545244763 - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda @@ -3168,6 +5323,18 @@ packages: license_family: MIT size: 2362258 timestamp: 1780450503234 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda + sha256: 511813aaad42ed2494efc77452738fed7734985e069efc08c279a701b1708ba0 + md5: 7f42f814e1306f83e4cac267baa9acab + depends: + - libharfbuzz-devel 14.3.0 h17a8019_0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 11045 + timestamp: 1785770087614 - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda sha256: 0d09b6dc1ce5c4005ae1c6a19dc10767932ef9a5e9c755cfdbb5189ac8fb0684 md5: bd77f8da987968ec3927990495dc22e4 @@ -3207,6 +5374,7 @@ packages: md5: 129e404c5b001f3ef5581316971e3ea0 license: GPL-2.0-or-later license_family: GPL + run_exports: {} size: 17625 timestamp: 1771539597968 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda @@ -3234,6 +5402,19 @@ packages: license_family: MIT size: 12723451 timestamp: 1773822285671 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 + md5: 72a381cbad04f24b1c2a43ef707f45b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: MIT + run_exports: + weak: + - icu >=78.3,<79.0a0 + size: 14459115 + timestamp: 1786545741408 - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda sha256: 1c9163fd77943c55fe727f5399545141df54f696422bf101089a32526aea3234 md5: d4d65cfb5c2569d5dbd344039471a534 @@ -3244,6 +5425,7 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + run_exports: {} size: 55407 timestamp: 1757685419776 - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h1220068_1.conda @@ -3259,6 +5441,19 @@ packages: - json-c >=0.17,<0.18.0a0 size: 83682 timestamp: 1720812978049 +- conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda + sha256: 09e706cb388d3ea977fabcee8e28384bdaad8ce1fc49340df5f868a2bd95a7da + md5: 38f5dbc9ac808e31c00650f7be1db93f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + run_exports: + weak: + - json-c >=0.18,<0.19.0a0 + size: 82709 + timestamp: 1726487116178 - conda: https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.3-hf8d3e68_2.conda sha256: a45cb038fce2b6fa154cf0c71485a75b59cb1d8d6b0465bdcb23736aca6bf2ac md5: ffe68c611ae0ccfda4e7a605195e22b3 @@ -3300,6 +5495,20 @@ packages: run_exports: {} size: 77120 timestamp: 1773067050308 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py314h97ea11e_0.conda + sha256: e3488ea4a336f29e57de8f282bf40c0505cfc482e03004615e694b48e7d9c79f + md5: 7397e418cab519b8d789936cf2dde6f6 + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 77363 + timestamp: 1773067048780 - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 md5: 3f43953b7d3fb3aaa1d0d0723d91e368 @@ -3363,6 +5572,19 @@ packages: run_exports: {} size: 728002 timestamp: 1774197446916 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + sha256: 27d83f1188cd19bcb7754a078b3fa7f4cfb8527f8eb2fde54dd01fc529d1adec + md5: 449500f2c089da11c40f5c21312e3e07 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.46.1 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 745303 + timestamp: 1784214507189 - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda sha256: f84cb54782f7e9cea95e810ea8fef186e0652d0fa73d3009914fa2c1262594e1 md5: a752488c68f2e7c456bcbd8f16eec275 @@ -3377,6 +5599,20 @@ packages: - lerc >=4.1.0,<5.0a0 size: 261513 timestamp: 1773113328888 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + sha256: bf9fdebf55d8bc99d83531cdffda00703f4dc5f93a1a956768c147362c72feda + md5: fb9d356b1a57d6d54768be7ebd5fce09 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - lerc >=4.2.0,<5.0a0 + size: 271158 + timestamp: 1785036167977 - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda sha256: 945396726cadae174a661ce006e3f74d71dbd719219faf7cc74696b267f7b0b5 md5: c48fc56ec03229f294176923c3265c05 @@ -3395,6 +5631,24 @@ packages: - libabseil =*=cxx17* size: 1264712 timestamp: 1720857377573 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda + sha256: 32933de2d4fa6e6ffd949052815b49cb65a0649ad70007155c533ab97ea8cefd + md5: c4393db381bffa0a83a8d9e47b238106 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - abseil-cpp =20260526.0 + - libabseil-static =20260526.0=cxx17* + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 1437712 + timestamp: 1780524559298 - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 md5: 86f7414544ae606282352fa1e116b41f @@ -3449,6 +5703,28 @@ packages: license_family: BSD size: 867280 timestamp: 1782289011634 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_hc2c16d8_100.conda + sha256: 000b4baa9ce849b5fb259af9256331d0c7872361ac63a2bba0d0c48eb35663d0 + md5: 3988040b14a8083b1a5382d99f584e5f + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - liblzma >=5.8.3,<6.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - lzo >=2.10,<3.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + run_exports: + weak: + - libarchive >=3.8.9,<3.9.0a0 + size: 876197 + timestamp: 1785250447640 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-h8d2e343_13_cpu.conda build_number: 13 sha256: 91e639761f29ee1ca144e92110d47c8e68038f26201eef25585a48826e037fb2 @@ -3492,6 +5768,46 @@ packages: - libarrow >=17.0.0,<17.1.0a0 size: 8512685 timestamp: 1725214716301 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + build_number: 4 + sha256: b3b92d94fa20d1d195de702c4b61cd0c3438d26672cbaf6ebbf021114a840a77 + md5: c344b5608c65a09b341f2e784efb4422 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libgoogle-cloud >=3.8.0,<3.9.0a0 + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.1,<2.3.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow >=25.0.0,<25.1.0a0 + size: 6598422 + timestamp: 1786315080658 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_13_cpu.conda build_number: 13 sha256: cda9e38ad7af7ba72416031b089de5048f8526ae586149ff9f6506366689d699 @@ -3508,6 +5824,42 @@ packages: - libarrow-acero >=17.0.0,<17.1.0a0 size: 609649 timestamp: 1725214754397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: 886631b6e32f62e04cc314462c0fbbf9d461cfb6625d2384ae0f36b5a8a72890 + md5: 64fad2fca810b34f5bbc9738a6fb298f + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-compute 25.0.0 h53684a4_4_cpu + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-acero >=25.0.0,<25.1.0a0 + size: 590543 + timestamp: 1786315249993 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + build_number: 4 + sha256: ad642502977edceeb4ca385e42f085fb4391c409d8d098866bc4a5e3fda7a04a + md5: 05f6b378ae80d2d06e35dc794412da3a + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libgcc >=14 + - libre2-11 >=2025.11.5 + - libstdcxx >=14 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-compute >=25.0.0,<25.1.0a0 + size: 2974934 + timestamp: 1786315141259 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_13_cpu.conda build_number: 13 sha256: b3fac9bc9a399670d6993738d018324d6e1b0a85755b484204405bb72efabc4e @@ -3526,6 +5878,25 @@ packages: - libarrow-dataset >=17.0.0,<17.1.0a0 size: 582848 timestamp: 1725214820464 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: 6715d36e6aaf00986876e67e97ae8e9b60afb2a7f52ab79b89543c37cd914e28 + md5: 26f7b996f0674217f78d5c6432445b71 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-acero 25.0.0 h635bf11_4_cpu + - libarrow-compute 25.0.0 h53684a4_4_cpu + - libgcc >=14 + - libparquet 25.0.0 h7376487_4_cpu + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-dataset >=25.0.0,<25.1.0a0 + size: 591131 + timestamp: 1786315326056 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf54134d_13_cpu.conda build_number: 13 sha256: 01ff52d5b866f3174018c81dee808fbef1101f2cff05cc5f29c80ff68cc8796c @@ -3547,6 +5918,27 @@ packages: - libarrow-substrait >=17.0.0,<17.1.0a0 size: 550883 timestamp: 1725214851656 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda + build_number: 4 + sha256: 7478e785ad4b7aee72e685921d849b3fb11b0c477dfea76d9e861ee5056b35a6 + md5: 287c5915a8b8bf79613840e0df9dc7b7 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-acero 25.0.0 h635bf11_4_cpu + - libarrow-dataset 25.0.0 h635bf11_4_cpu + - libgcc >=14 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-substrait >=25.0.0,<25.1.0a0 + size: 500255 + timestamp: 1786315353425 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda build_number: 8 sha256: b2da6bfd72a1c9cb143ccf64bf5b28790cb4eb58bd1cb978f6537b2322f7d48b @@ -3567,6 +5959,26 @@ packages: - libblas >=3.11.0,<4.0a0 size: 18804 timestamp: 1779859100675 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda + build_number: 9 + sha256: 39c7b3c5427b435c9c059ede9da61d46d42574e5b846ad37fdc3af4a5eab1e48 + md5: f5c4b041925dea221dc4bad2e50569d9 + depends: + - libopenblas >=0.3.34,<0.3.35.0a0 + - libopenblas >=0.3.34,<1.0a0 + constrains: + - blas 2.309 openblas + - libcblas 3.11.0 9*_openblas + - liblapack 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + - mkl <2027 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libblas >=3.11.0,<4.0a0 + size: 18033 + timestamp: 1786059035239 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda sha256: 2338a92d1de71f10c8cf70f7bb9775b0144a306d75c4812276749f54925612b6 md5: 1d29d2e33fe59954af82ef54a8af3fe1 @@ -3580,6 +5992,19 @@ packages: - libbrotlicommon >=1.1.0,<1.2.0a0 size: 69333 timestamp: 1756599354727 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e + md5: 72c8fd1af66bd67bf580645b426513ed + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + size: 79965 + timestamp: 1764017188531 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda sha256: fcec0d26f67741b122f0d5eff32f0393d7ebd3ee6bb866ae2f17f3425a850936 md5: 5cb5a1c9a94a78f5b23684bcb845338d @@ -3594,6 +6019,20 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 33406 timestamp: 1756599364386 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b + md5: 366b40a69f0ad6072561c1d09301c886 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 34632 + timestamp: 1764017199083 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda sha256: d42c7f0afce21d5279a0d54ee9e64a2279d35a07a90e0c9545caae57d6d7dc57 md5: 2e55011fa483edb8bfe3fd92e860cd79 @@ -3608,6 +6047,20 @@ packages: - libbrotlienc >=1.1.0,<1.2.0a0 size: 289680 timestamp: 1756599375485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d + md5: 4ffbb341c8b616aa2494b6afb26a0c5f + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlienc >=1.2.0,<1.3.0a0 + size: 298378 + timestamp: 1764017210931 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda build_number: 8 sha256: 1a2bc77bb26520255904a3d9b1f40e6bf0bf9d8d3405c7709dd162282820915a @@ -3625,6 +6078,23 @@ packages: - libcblas >=3.11.0,<4.0a0 size: 18778 timestamp: 1779859107964 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda + build_number: 9 + sha256: 4c532a70ea9aeff2fa1aabaa4828ebc00c2ed12b22aa8ba19da5302b882fc82b + md5: 092c5649f3727af436ab0f67f48c3811 + depends: + - libblas 3.11.0 9_h4a7cf45_openblas + constrains: + - blas 2.309 openblas + - liblapack 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libcblas >=3.11.0,<4.0a0 + size: 17998 + timestamp: 1786059041397 - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda sha256: 6b256303e6a1db40e4ce61a5bc69d0e78bf8946070309b665555248667aa23f3 md5: feec6e0f138982e3f7b1ac372fb396cf @@ -3640,6 +6110,24 @@ packages: - libclang-cpp19.1 >=19.1.7,<19.2.0a0 size: 20824232 timestamp: 1776984079535 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.8-default_h08c5240_6.conda + sha256: fc2981a8e45dd232ae88cf2580f7e75c258f4dccdc96f879b68ef0a9b45c9a2b + md5: 4fd9026a57bb45cd15bd08ff8bd0578e + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libllvm22 >=22.1.8,<22.2.0a0 + license: Apache-2.0 WITH LLVM-exception + run_exports: + weak: + - libclang-cpp22.1 >=22.1.8,<22.2.0a0 + size: 24171067 + timestamp: 1786527767794 - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda sha256: e6c0123b888d6abf03c66c52ed89f9de1798dde930c5fd558774f26e994afbc6 md5: 327c78a8ce710782425a89df851392f7 @@ -3655,6 +6143,25 @@ packages: - libclang13 >=21.1.0 size: 12358102 timestamp: 1757383373129 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.8-default_hd70ba2e_6.conda + sha256: 8475136d3be6c2d16bdb801dcaf8769cb2ba372cad239c89ebd6a58912e14465 + md5: a8e147873eca89dc1c5d319b027a6cd3 + depends: + - libclang-cpp22.1 ==22.1.8 default_h08c5240_6 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libllvm22 >=22.1.8,<22.2.0a0 + license: Apache-2.0 WITH LLVM-exception + run_exports: + weak: + - libclang13 >=22.1.8 + size: 14275196 + timestamp: 1786527767794 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 md5: c965a5aa0d5c1c37ffc62dff36e28400 @@ -3679,6 +6186,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache + run_exports: + weak: + - libcups >=2.3.3,<2.4.0a0 size: 4518030 timestamp: 1770902209173 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda @@ -3713,6 +6223,26 @@ packages: license_family: MIT size: 479582 timestamp: 1782296544301 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda + sha256: aff8ef75636d0825ce34911e0bef2f26816e7afd090a9dcb4b9bacab75cbf584 + md5: 3f2fd5617cfacac49c85f6dc63842ea1 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libnghttp2 >=1.68.1,<2.0a0 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + run_exports: + weak: + - libcurl >=8.21.0,<9.0a0 + size: 480565 + timestamp: 1785500108494 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda sha256: 0ba60f83709068e9ec1ab543af998cb5a201c8379c871205447684a34b5abfd8 md5: 7da1d242ca3591e174a3c7d82230d3c0 @@ -3744,6 +6274,19 @@ packages: - libdeflate >=1.25,<1.26.0a0 size: 73490 timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + sha256: 82e134c8a08b1eed9a2ed8ab578b89aa1730dcde3dea8dd87645ed0637878e54 + md5: 40f9b31aa9cf007789867df0decd0492 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libdeflate >=1.25,<1.26.0a0 + size: 73710 + timestamp: 1785908694612 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda sha256: 7d3187c11b7ae66c5595a8afd5a7ce352a490527fdf6614cab129bc7f2c16ba3 md5: d8d16b9b32a3c5df7e5b3350e2cbe058 @@ -3803,8 +6346,24 @@ packages: - libgl-devel 1.7.0 ha4b6fd6_3 - xorg-libx11 license: LicenseRef-libglvnd + run_exports: + weak: + - libegl >=1.7.0,<2.0a0 size: 31718 timestamp: 1779728222280 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + sha256: e4418f68d01a6307c4431b585c71b584f40189877364e542ee87deb777f5e85b + md5: 7bc31538d6e3fb349e897353969237ec + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: BSD-2-Clause OR GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - libev >=4.33,<4.34.0a0 + size: 43220 + timestamp: 1785917328200 - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 md5: 172bf1cd1ff8629f2b1179945ed45055 @@ -3856,6 +6415,19 @@ packages: - libffi >=3.5.2,<3.6.0a0 size: 58592 timestamp: 1769456073053 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda + sha256: ac38603008bf1e99b8ed379b1a656a67a70e2841f2b6a069c630cdf6316012d2 + md5: 0abe40a9880086ca4d2e5daf09dceff9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 67576 + timestamp: 1783520858222 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda sha256: 38f014a7129e644636e46064ecd6b1945e729c2140e21d75bb476af39e692db2 md5: e289f3d17880e44b633ba911d57a321b @@ -3865,6 +6437,15 @@ packages: run_exports: {} size: 8049 timestamp: 1774298163029 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda + sha256: 9f09d889d1021fa0d99968e5f209a7a7b316dee48c97ed0d79e996e1272a6280 + md5: 12a05d10f2e4eadfd7b8f754a17f5e1a + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + run_exports: {} + size: 8419 + timestamp: 1785641173212 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda sha256: 16f020f96da79db1863fcdd8f2b8f4f7d52f177dd4c58601e38e9182e91adf1d md5: fb16b4b69e3f1dcfe79d80db8fd0c55d @@ -3879,6 +6460,20 @@ packages: run_exports: {} size: 384575 timestamp: 1774298162622 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda + sha256: 162f1736f9ec7b19915658cbb25a685748932f697418ab85657332de5da5f496 + md5: e63acf9b3849fd9fc2dba64b69849716 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + run_exports: {} + size: 386042 + timestamp: 1785641172605 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda sha256: 8e0a3b5e41272e5678499b5dfc4cddb673f9e935de01eb0767ce857001229f46 md5: 57736f29cc2b0ec0b6c2952d3f101b6a @@ -3893,6 +6488,20 @@ packages: run_exports: {} size: 1041084 timestamp: 1778269013026 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda + sha256: d5cb8475131c31680f8fd30512c418f373064e272e452063276a8fb14c9fa42f + md5: 5a7d954665c707c93311657cd779c705 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgomp 16.1.0 he0feb66_1 + - libgcc-ng ==16.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 1057877 + timestamp: 1785375436766 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda sha256: 9dcf54adfaa5e861123c2da4f2f0451a685464ea7e5a41ad91cf67b31d658d98 md5: 331ee9b72b9dff570d56b1302c5ab37d @@ -3905,6 +6514,18 @@ packages: - libgcc size: 27694 timestamp: 1778269016987 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda + sha256: 225275c562337a1cd61705da0ee4235dde7bba7504de1c34b74c894adb2b0eee + md5: 7ed870c014a6f23c7dfafda53d2763a9 + depends: + - libgcc 16.1.0 ha9f2e26_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - libgcc + size: 28210 + timestamp: 1785375440733 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda sha256: 245be793e831170504f36213134f4c24eedaf39e634679809fd5391ad214480b md5: 88c1c66987cd52a712eea89c27104be6 @@ -3924,6 +6545,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD + run_exports: + weak: + - libgd >=2.3.3,<2.4.0a0 size: 177306 timestamp: 1766331805898 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.9.2-ha770c72_7.conda @@ -3947,10 +6571,54 @@ packages: license_family: MIT run_exports: weak: - - libgdal-core >=3.9.2,<3.10.0a0 - - libgdal >=3.9.2,<3.10.0a0 - size: 422887 - timestamp: 1728295073003 + - libgdal-core >=3.9.2,<3.10.0a0 + - libgdal >=3.9.2,<3.10.0a0 + size: 422887 + timestamp: 1728295073003 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-hd345f60_4.conda + sha256: 710302e174e8aa65d808158dbd5ca89dd171d5ea9db9ca1b469a95199f020990 + md5: e219de8ffa44fdf2eaa036f3b614bf84 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - lerc >=4.2.0,<5.0a0 + - libcurl >=8.21.0,<9.0a0 + - proj >=9.8.1,<9.9.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libzlib >=1.3.2,<2.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libdeflate >=1.25,<1.26.0a0 + - liblzma >=5.8.3,<6.0a0 + - giflib >=5.2.2,<5.3.0a0 + - json-c >=0.18,<0.19.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - lz4-c >=1.10.0,<1.11.0a0 + - libexpat >=2.8.1,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libsqlite >=3.53.4,<4.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - openssl >=3.5.7,<4.0a0 + - libiconv >=1.18,<2.0a0 + - muparser >=2.3.5,<2.4.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libpng >=1.6.58,<1.7.0a0 + - blosc >=1.21.6,<2.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + constrains: + - libgdal 3.12.4.* + license: MIT + license_family: MIT + run_exports: + weak: + - libgdal-core >=3.12.4,<3.13.0a0 + size: 14098092 + timestamp: 1786090857358 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.9.2-h353785f_1.conda sha256: ddb6756cdf027e2b8d4886eb248ca0113af25d752a94844a13655121a9658fa6 md5: c363d0b330b4b21b4c1b10e0981d3a99 @@ -4196,6 +6864,18 @@ packages: run_exports: {} size: 27655 timestamp: 1778269042954 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda + sha256: 9e82d410a50bd4e5e47cbbb026454c3eb543954baca4db23929575b571bf56a3 + md5: 2fbed65cc90cf0724e1ec4de13696737 + depends: + - libgfortran5 16.1.0 h79bb938_1 + constrains: + - libgfortran-ng ==16.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 28134 + timestamp: 1785375470055 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda sha256: 9ca1d254a3e44e608abec6186b18d372cec21e5253e6da9750f4a8f4780ea0bb md5: 35d07243abf828674d273aecd1dd537e @@ -4221,6 +6901,19 @@ packages: run_exports: {} size: 2483673 timestamp: 1778269025089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda + sha256: 05078ab464d506dff971860cb1a553b35bc27c0b5ce8ec29b8bfaca5f2359652 + md5: dd51ed33e8c70995f8e33cc9dc537297 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=16.1.0 + constrains: + - libgfortran 16.1.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 2538696 + timestamp: 1785375448623 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda sha256: ec353b3076ed8e357ed961d0e9ff6997491cade0e603de5bd18a2e301ac78ebd md5: f25206d7322c0e9648e8b83694d143ab @@ -4240,6 +6933,9 @@ packages: - libgl 1.7.0 ha4b6fd6_3 - libglx-devel 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + run_exports: + weak: + - libgl >=1.7.0,<2.0a0 size: 115664 timestamp: 1779728218325 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda @@ -4275,6 +6971,24 @@ packages: license: LGPL-2.1-or-later size: 4754097 timestamp: 1778508800134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda + sha256: 4499458124bcf0bd45e8bd17576f9f007cb1373cb3b01659105443f522bab45c + md5: 533cb021c1bfeba9f720e669cd863fdf + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - pcre2 >=10.47,<10.48.0a0 + - libzlib >=1.3.2,<2.0a0 + - libiconv >=1.18,<2.0a0 + - libffi >=3.7.0,<3.8.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + run_exports: + weak: + - libglib >=2.88.3,<3.0a0 + size: 4755172 + timestamp: 1786457663614 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda sha256: e019ebe4e3f5cdf23e2f5e58ddf7ade27988c53820115b17b98f218ebcc87748 md5: eb83f3f8cecc3e9bff9e250817fc69b6 @@ -4304,6 +7018,9 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 - xorg-xorgproto license: LicenseRef-libglvnd + run_exports: + weak: + - libglx >=1.7.0,<2.0a0 size: 27363 timestamp: 1779728211402 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda @@ -4318,6 +7035,18 @@ packages: - _openmp_mutex >=4.5 size: 603817 timestamp: 1778268942614 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda + sha256: 62cb599ad0539d99386515326d9d5e8f51f75a60c69c2131b21df76edf35bd89 + md5: 88f2d91cb1533194c323534253094d23 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 640415 + timestamp: 1785375373755 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.28.0-h26d7fe4_0.conda sha256: d87b83d91a9fed749b80dea915452320598035949804db3be616b8c3d694c743 md5: 2c51703b4d775f8943c08a361788131b @@ -4340,6 +7069,29 @@ packages: - libgoogle-cloud >=2.28.0,<2.29.0a0 size: 1226849 timestamp: 1723370075980 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + sha256: 46126b858d173d6808262e39e3a8aa39946d39aebb5ffb720984dc44f74feafe + md5: 60ff8935e16bf2fd302289ec4f129df8 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgcc >=14 + - libgrpc >=1.82.1,<1.83.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + - openssl >=3.5.7,<4.0a0 + constrains: + - libgoogle-cloud 3.8.0 *_0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud >=3.8.0,<3.9.0a0 + size: 2663465 + timestamp: 1786226759530 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.28.0-ha262f82_0.conda sha256: 3237bc1ee88dab8d8fea0a1886e12a0262ff5e471944a234c314aa1da411588e md5: 9e7960f0b9ab3895ef73d92477c47dae @@ -4360,6 +7112,26 @@ packages: - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 size: 769298 timestamp: 1723370220027 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + sha256: 03437ce50129f9ca82a0400b7722cd2dd3ee3bfa50dcb557657f55a7de76ab6f + md5: 84a8d5a661792da2127ab908b5ae83ef + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=14 + - libgoogle-cloud 3.8.0 hbc29df5_0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + size: 810573 + timestamp: 1786226867433 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda sha256: 28241ed89335871db33cb6010e9ccb2d9e9b6bb444ddf6884f02f0857363c06a md5: 8dabe607748cb3d7002ad73cd06f1325 @@ -4383,6 +7155,87 @@ packages: - libgrpc >=1.62.2,<1.63.0a0 size: 7316832 timestamp: 1713390645548 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda + sha256: 1e657c1b802c111178bc1845fe06488db5f69a85e34e970ce0989810aa562d45 + md5: aa4571fc3bcf18ad12370429aa991223 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=14 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.82.1 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libgrpc >=1.82.1,<1.83.0a0 + size: 6957755 + timestamp: 1783588701960 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda + sha256: fb72d6f7e90d4927cb53a4e13592559ce74b65052323d5d6dd12499b026c680e + md5: f33637ebded146eafa6b2197c8f597a6 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 1333436 + timestamp: 1785770053613 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda + sha256: 7ce9326c2520ae758f523ae2d72a0762f7494d77fe8cc9a96065df541e1db8e2 + md5: 15634b3c7e5a68ca7c6e0bbf84cb2383 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz 14.3.0 h17a8019_0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 2081226 + timestamp: 1785770079548 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h3a9caae_0.conda + sha256: 02ab5e50c3921e88ad4dd0bc8f3fe282d2d7d03a20203d3281954b94641deb3d + md5: 9544a7225c8366ea2c397aad5fb53470 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 OR BSD-3-Clause + run_exports: + weak: + - libhwy >=1.4.0,<1.5.0a0 + size: 1431901 + timestamp: 1784325535334 - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f md5: 915f5995e94f60e9a4826e0b0920ee88 @@ -4409,6 +7262,37 @@ packages: - libjpeg-turbo >=3.1.4.1,<4.0a0 size: 633831 timestamp: 1775962768273 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + sha256: bba8538e6538ed58a8479b332337b96986561f975d06cfa2039a016c2d246ee4 + md5: 898d1c9793eaa52efc4727bd84d2e39a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + run_exports: + weak: + - libjpeg-turbo >=3.2.0,<4.0a0 + size: 650434 + timestamp: 1785896381946 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-h174a0a3_1.conda + sha256: 1811d6c6558fbfe89326616c207cc7584032b60bc6f4329d2a76b961e2936a15 + md5: 1fff55640e1f12d7606965915be37bbb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libhwy >=1.4.0,<1.5.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libjxl >=0.12.0,<0.13.0a0 + size: 1849836 + timestamp: 1783146019356 - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda sha256: f6348ac9cebbc03f765ea6d2da88d57d19531585c8f3a963b98635b208e8bef1 md5: 953b7cca897e21215302dbfe2af5cd0c @@ -4441,6 +7325,23 @@ packages: - liblapack >=3.11.0,<3.12.0a0 size: 18790 timestamp: 1779859115086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.conda + build_number: 9 + sha256: ea989e2dabd21d296a5a4ec515e695645aefcdf778ffdb5eeea515421d243ab5 + md5: e51473c2b7e1f9cb61daafccfd912abf + depends: + - libblas 3.11.0 9_h4a7cf45_openblas + constrains: + - blas 2.309 openblas + - libcblas 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - liblapack >=3.11.0,<3.12.0a0 + size: 18021 + timestamp: 1786059046733 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-8_h6ae95b6_openblas.conda build_number: 8 sha256: 0b982865888a73fe7c2e7d8e8ee3738ac378b83012a7da1a12264a473cb2382b @@ -4455,6 +7356,23 @@ packages: license_family: BSD size: 18824 timestamp: 1779859122364 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-9_h6ae95b6_openblas.conda + build_number: 9 + sha256: 7534e06a82865d3cfa050937c77887e8e85e7edeb4623972f6fe837cc7d13334 + md5: d76700bd66c92d6159bfcfd3c8f83bb8 + depends: + - libblas 3.11.0 9_h4a7cf45_openblas + - libcblas 3.11.0 9_h0358290_openblas + - liblapack 3.11.0 9_h47877c9_openblas + constrains: + - blas 2.309 openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - liblapacke >=3.11.0,<3.12.0a0 + size: 18034 + timestamp: 1786059054936 - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda sha256: 22909d64038bdc87de61311c4ae615dc574a548a7340b963bb7c9eb61b191669 md5: 6d2362046dce932eefbdeb0540de0c38 @@ -4489,6 +7407,24 @@ packages: - libllvm21 >=21.1.0,<21.2.0a0 size: 44363060 timestamp: 1756291822911 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda + sha256: e9b5f301d6b001a9b8ce782157f56b75c92c4fbc9eba95dc6345c1139251d13b + md5: 298bb2483fc7d15396147cf1c1465359 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + run_exports: + weak: + - libllvm22 >=22.1.8,<22.2.0a0 + size: 44320272 + timestamp: 1781788728739 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d md5: b88d90cad08e6bc8ad540cb310a761fb @@ -4503,6 +7439,20 @@ packages: - liblzma >=5.8.3,<6.0a0 size: 113478 timestamp: 1775825492909 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab + md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 112995 + timestamp: 1786348617826 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda sha256: 7858f6a173206bc8a5bdc8e75690483bb66c0dcc3809ac1cb43c561a4723623a md5: 55c20edec8e90c4703787acaade60808 @@ -4541,6 +7491,34 @@ packages: license_family: BSD size: 2808228 timestamp: 1780948903590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda + sha256: 1b9ca94f36f6072ff71550dfaa8dd4106a118bf01f352ed639aef134b462bab6 + md5: 444e55c79d67040779af3a0167963bea + depends: + - cpp-expected >=1.3.1,<1.3.2.0a0 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - simdjson >=4.6.6,<4.7.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libcurl >=8.21.0,<9.0a0 + - libmsgpack-c >=6.1.0,<7.0a0 + - reproc >=14.2.7.post0,<14.3.0a0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + - openssl >=3.5.7,<4.0a0 + - fmt >=12.1.0,<12.2.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - nlohmann_json-abi ==3.12.0 + - zstd >=1.5.7,<1.6.0a0 + - libsolv >=0.7.39,<0.8.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 2850628 + timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.8.1-h9b73f26_0.conda sha256: ef3d79e638dc1e294df239bf83a268dbea835c2c1f66001383473c22a8dc0ffd md5: 410217d65f86c0a60c9b11ecf1bf842e @@ -4553,6 +7531,19 @@ packages: license_family: BSD size: 20307 timestamp: 1780948903590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda + sha256: ddae16548197cf30cf72d700733427cb314ce6fa2921fea7e2922566288de42e + md5: 1066c66681f6a1d6e36d13dc3eecbd50 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libmamba >=2.9.0,<2.10.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 20529 + timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.8.1-py314hd8b5600_0.conda sha256: c35bf3900a533b2e8a2ad9f63ab645240208ad28c3532d2d46dd84cbddfc2d7e md5: 828f1c59aa72aa02de5b559c0428ebb8 @@ -4576,6 +7567,32 @@ packages: license_family: BSD size: 967437 timestamp: 1780948903590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda + sha256: 0c2f07dfcb61e14a357df8816abb236e1705b92998ba068784f87fd74070aa85 + md5: 231869ede4419e814ca7d3de6fabdf95 + depends: + - python + - libmamba ==2.9.0 py314h3b59866_0 + - libmamba-spdlog ==2.9.0 hf1e253f_0 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - openssl >=3.5.7,<4.0a0 + - fmt >=12.1.0,<12.2.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - python_abi 3.14.* *_cp314 + - yaml-cpp >=0.8.0,<0.9.0a0 + - nlohmann_json-abi ==3.12.0 + - zstd >=1.5.7,<1.6.0a0 + - libmamba >=2.9.0,<2.10.0a0 + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 967399 + timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843 md5: 2c21e66f50753a083cbe6b80f38268fa @@ -4584,6 +7601,7 @@ packages: - libgcc >=14 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 92400 timestamp: 1769482286018 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_6.conda @@ -4598,6 +7616,21 @@ packages: license: BSL-1.0 size: 40340 timestamp: 1770807484162 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda + sha256: c23ac21547d9fac6be14b6b3463dd29fb0fd4aba5c64c4a9876dfa6d2e3158a9 + md5: 9ab61308317dbf6609e2142b16b792a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + constrains: + - msgpack-c ==6.1.0 *_7 + license: BSL-1.0 + run_exports: + weak: + - libmsgpack-c >=6.1.0,<7.0a0 + size: 40166 + timestamp: 1786189331007 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda sha256: 055572a4c8a1c3f9ac60071ee678f5ea49cfd7ac60a636d817988a6f9d6de6ae md5: a908e463c710bd6b10a9eaa89fdf003c @@ -4684,6 +7717,23 @@ packages: - libopenblas >=0.3.33,<1.0a0 size: 5931919 timestamp: 1776993658641 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda + sha256: 23392fc4f4e5ba230fcd1ef825878ba5ca7ee4f6259fac0cbb13299134b7bf7a + md5: c282d68f272927612462b5d626838ef1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + constrains: + - openblas >=0.3.34,<0.3.35.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libopenblas >=0.3.34,<1.0a0 + size: 5952629 + timestamp: 1784287497473 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda sha256: 90777039b48529283df5f16383fc399866024257a8bd93de583f4730db1ab30a md5: c2bd8055a2e2dce7a7f32cfd02101fb6 @@ -4694,6 +7744,36 @@ packages: run_exports: {} size: 51767 timestamp: 1779728204026 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + sha256: 8f9281133322e9eb0bd4a5b11330db1c14f3b40c409639dd3805251151ca52e2 + md5: f749f223bede1bac7724e49d686ac598 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.0,<1.83.0a0 + - libopentelemetry-cpp-headers 1.27.0 ha770c72_1 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.27.0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + size: 948783 + timestamp: 1783133058845 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + sha256: 31349543e3c59c64f17e24494939a22b0e1d611bdb19d98d115775dba423cbf7 + md5: 6cc68cbbe88746be8beaf027d6c64ad3 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 394726 + timestamp: 1783133038109 - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda build_number: 13 sha256: 3c63b7391275cf6cf2a18d2dba3c30c16dd9d210373d206675e342b084cccdf4 @@ -4709,9 +7789,27 @@ packages: license_family: APACHE run_exports: weak: - - libparquet >=17.0.0,<17.1.0a0 - size: 1189824 - timestamp: 1725214804075 + - libparquet >=17.0.0,<17.1.0a0 + size: 1189824 + timestamp: 1725214804075 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda + build_number: 4 + sha256: dba948e05d62de3833caa065c1b7de960f921bb0a8a7687b8593df95d2d925a9 + md5: d736a4f89a3b63f9970152923e4c3016 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libgcc >=14 + - libstdcxx >=14 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libparquet >=25.0.0,<25.1.0a0 + size: 1412634 + timestamp: 1786315223543 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda sha256: f41721636a7c2e51bc2c642e1127955ab9c81145470714fdaac44d4d09e4af41 md5: 33082e13b4769b48cfeb648e15bfe3fc @@ -4764,6 +7862,22 @@ packages: - libpq >=17.2,<18.0a0 size: 2588868 timestamp: 1732204566030 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda + sha256: f7232cb79a31f5a5bcd499aea930b469cde8b96d26db9541022493fd274d2a6e + md5: 6c9103e7ea739a3bb3505da49a4708c1 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - openldap >=2.6.13,<2.7.0a0 + - openssl >=3.5.7,<4.0a0 + license: PostgreSQL + run_exports: + weak: + - libpq >=18.4,<19.0a0 + size: 2709008 + timestamp: 1782580447454 - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda sha256: 8b5e4e31ed93bf36fd14e9cf10cd3af78bb9184d0f1f87878b8d28c0374aa4dc md5: 06def97690ef90781a91b786cb48a0a9 @@ -4781,6 +7895,55 @@ packages: - libprotobuf >=4.25.3,<4.25.4.0a0 size: 2883090 timestamp: 1727161327039 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h2840a7c_2.conda + sha256: b5ac3938186516c1091b96414c34f90255da2a3bbd736a0712b22bbf4b5ebf02 + md5: 729db8acaadb9a5e5bb2dc3d9ac84ae4 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 3774596 + timestamp: 1783168720126 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda + sha256: 2e6405feb59e3f40a5a4641dfa73afda158046893aa73d478e23415e18997ece + md5: c8217f5bdd5b018087bdea081912cda5 + depends: + - libgcc >=14 + - libstdcxx >=14 + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libpsl >=0.23.1,<0.24.0a0 + size: 72505 + timestamp: 1786443494718 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda + sha256: ab3ccb9fd5816f76b18ee8974d359cd2605ca87d8ddef55369dc461b9986f95c + md5: 3ac89a48d224409739dbf6200e524373 + depends: + - libgcc >=14 + - __glibc >=2.28,<3.0.a0 + - fribidi >=1.0.16,<2.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libharfbuzz >=14.2.1 + license: MIT + license_family: MIT + run_exports: + weak: + - libraqm >=0.11.0,<0.12.0a0 + size: 33983 + timestamp: 1784850267262 - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda sha256: 3f3c65fe0e9e328b4c1ebc2b622727cef3e5b81b18228cfa6cf0955bc1ed8eff md5: 41c69fba59d495e8cf5ffda48a607e35 @@ -4798,6 +7961,24 @@ packages: - libre2-11 >=2023.9.1,<2024.0a0 size: 232603 timestamp: 1708946763521 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + sha256: 43132eb45a569b1f63199b0e7d5874d94227e9be950b327a323e7dcc1f8cd58c + md5: ee5c400d37b79db5d32a69ed1a79fc0b + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + size: 210598 + timestamp: 1781312565737 - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.3-h4c96295_0.conda sha256: 5571bd8239d71961d4e3ce972f865b3ea95a91ce0b53d5749fe2dd24254ddbda md5: 492c8d9b1c564c2e948b6cb4ba0f8261 @@ -4815,8 +7996,26 @@ packages: constrains: - __glibc >=2.17 license: LGPL-2.1-or-later + run_exports: + weak: + - librsvg >=2.62.3,<3.0a0 size: 3476570 timestamp: 1780450632624 +- conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda + sha256: eb4082a5135102f5ba9c302da13164d4ed1181d5f0db9d49e5e11a815a7b526f + md5: df81fd57eacf341588d728c97920e86d + depends: + - __glibc >=2.17,<3.0.a0 + - geos >=3.14.1,<3.14.2.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - librttopo >=1.1.0,<1.2.0a0 + size: 231670 + timestamp: 1761670395043 - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hc670b87_16.conda sha256: 65bfd9f8915b1fc2523c58bf556dc2b9ed6127b7c6877ed2841c67b717f6f924 md5: 3d9f3a2e5d7213c34997e4464d2f938c @@ -4839,6 +8038,9 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: ISC + run_exports: + weak: + - libsodium >=1.0.22,<1.0.23.0a0 size: 269272 timestamp: 1779163468406 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h9463b59_0.conda @@ -4851,8 +8053,37 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - libsolv >=0.7.39,<0.8.0a0 size: 521190 timestamp: 1780057179710 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda + sha256: 1226948565ea9fa4fbaefc8c5ff6bb4c66e3cb96a9db71d236dcc0be0bc319cb + md5: 5947bdda89ced07ab0d8a5d6503082c3 + depends: + - __glibc >=2.17,<3.0.a0 + - freexl >=2 + - freexl >=2.0.0,<3.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - libgcc >=14 + - librttopo >=1.1.0,<1.2.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libxml2-devel + - libzlib >=1.3.1,<2.0a0 + - proj >=9.8.0,<9.9.0a0 + - sqlite + - zlib + license: MPL-1.1 + license_family: MOZILLA + run_exports: + weak: + - libspatialite >=5.1.0,<5.2.0a0 + size: 4094822 + timestamp: 1772594360111 - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h15fa968_9.conda sha256: 541eadcc9f2e3f5c7f801265563412930c9c65e22c21634df96a8cd6465a385e md5: 4957a903bd6a68cc2e53e47476f9c6f4 @@ -4900,6 +8131,20 @@ packages: - libsqlite >=3.53.3,<4.0a0 size: 962119 timestamp: 1782519076616 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-hf4e2dac_0.conda + sha256: 72023efc207fe681e26b65fc9d668062cf0b4f0eacf3431e6eb099b95c1f2efd + md5: df088a279cd5e6fd2790b4c196434da1 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 964200 + timestamp: 1785016112246 - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 md5: eecce068c7e4eddeb169591baac20ac4 @@ -4928,6 +8173,19 @@ packages: run_exports: {} size: 5852044 timestamp: 1778269036376 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda + sha256: 79721dd08aeb0ab9e773f1f9ef41cf4e6c17477e3d72319147619045bce05a09 + md5: aed6cf89adc1e9b846e4367ac538e434 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 16.1.0 ha9f2e26_1 + constrains: + - libstdcxx-ng ==16.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 6631744 + timestamp: 1785375462643 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda sha256: 0672b6b6e1791c92e8eccad58081a99d614fcf82bca5841f9dfa3c3e658f83b9 md5: e5ce228e579726c07255dbf90dc62101 @@ -4940,6 +8198,18 @@ packages: - libstdcxx size: 27776 timestamp: 1778269074600 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + sha256: 2876ca4463d1b394eb969ce4a84d1620aa63fb8202a6397837d1e45ec76c1208 + md5: c94f06123272d8e129d4acf3a25ffb35 + depends: + - libstdcxx 16.1.0 h934c35e_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - libstdcxx + size: 28253 + timestamp: 1785375500257 - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.20.0-h0e7cc3e_1.conda sha256: 3e70dfda31a3ce28310c86cc0001f20abb78c917502e12c94285a1337fe5b9f0 md5: d0ed81c4591775b70384f4cc78e05cd1 @@ -4957,6 +8227,23 @@ packages: - libthrift >=0.20.0,<0.20.1.0a0 size: 417404 timestamp: 1724652349098 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda + sha256: af6025aa4a4fc3f4e71334000d2739d927e2f678607b109ec630cc17d716918a + md5: b6e326fbe1e3948da50ec29cee0380db + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libthrift >=0.22.0,<0.22.1.0a0 + size: 423861 + timestamp: 1777018957474 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 md5: cd5a90476766d53e901500df9215e927 @@ -4977,6 +8264,39 @@ packages: - libtiff >=4.7.1,<4.8.0a0 size: 435273 timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda + sha256: b31346e1c01ab40a170e91147092ee8fd92b1dee3c66ee47ef025571c879b159 + md5: c1fcb4a88bc15a9f77ad8d27d7af1df9 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.1.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + run_exports: + weak: + - libtiff >=4.7.2,<4.8.0a0 + size: 452337 + timestamp: 1783084902636 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + sha256: ecbf4b7520296ed580498dc66a72508b8a79da5126e1d6dc650a7087171288f9 + md5: 1247168fe4a0b8912e3336bccdbf98a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - libutf8proc >=2.11.3,<2.12.0a0 + size: 85969 + timestamp: 1768735071295 - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-hf23e847_1.conda sha256: 104cf5b427fc914fec63e55f685a39480abeb4beb34bdbc77dea084c8f5a55cb md5: b1aa0faa95017bca11369bd080487ec4 @@ -5003,6 +8323,24 @@ packages: - libuuid >=2.42.2,<3.0a0 size: 40017 timestamp: 1781625522462 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h5279c79_0.conda + sha256: 1e30138cff1e6ba5739ce3ec787b24ef22ac6e2008d8a2073df334e9e5f8690b + md5: 9d8c72f797f6f2d1c32897603d70824c + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + constrains: + - libvulkan-headers 1.4.357.0.* + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libvulkan-loader >=1.4.357.0,<2.0a0 + size: 203456 + timestamp: 1785311377294 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b md5: aea31d2e5b1091feca96fcfe945c3cf9 @@ -5018,6 +8356,21 @@ packages: - libwebp-base >=1.6.0,<2.0a0 size: 429011 timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + sha256: 8415001414f488c85b72b9d8cc2071dfb3981a47bc3c8eb56ef91a57d12eae7f + md5: 9332b53d0ea93c5d39e33be03a0c611a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libwebp-base >=1.6.0,<2.0a0 + size: 428430 + timestamp: 1785954557217 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa md5: 92ed62436b625154323d40d5f2f11dd7 @@ -5045,6 +8398,18 @@ packages: - libxcrypt >=4.4.36 size: 100393 timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + sha256: f7e9292dd219a6435bbb1223da9586c3e70d66d169c5a92f08db3f2127df04e9 + md5: f7a7ff5a6ab331e037abd34f379a631d + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - libxcrypt >=4.4.38 + size: 101957 + timestamp: 1785887123445 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda sha256: 23f47e86cc1386e7f815fa9662ccedae151471862e971ea511c5c886aa723a54 md5: 74e91c36d0eef3557915c68b6c2bef96 @@ -5077,6 +8442,9 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 license: MIT/X11 Derivative license_family: MIT + run_exports: + weak: + - libxkbcommon >=1.13.2,<2.0a0 size: 851166 timestamp: 1780213397575 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda @@ -5093,6 +8461,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + run_exports: {} size: 559775 timestamp: 1776376739004 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda @@ -5125,8 +8494,47 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 size: 46810 timestamp: 1776376751152 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda + sha256: adada9c781ea51faea3e3adcd6883dc294ff2fa044c7f4e199430a11862dfa40 + md5: be70cb50dd0ecc1531c58034d38f72e0 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2 2.15.3 h49c6c72_0 + - libxml2-16 2.15.3 hca6bf5a_0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 + size: 80529 + timestamp: 1776376762779 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 + md5: 87e6096ec6d542d1c1f8b33245fe8300 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT + license_family: MIT + run_exports: + weak: + - libxslt >=1.1.43,<2.0a0 + size: 245434 + timestamp: 1757963724977 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda sha256: 35ddfc0335a18677dd70995fa99b8f594da3beb05c11289c87b6de5b930b47a3 md5: 31059dc620fa57d787e3899ed0421e6d @@ -5171,6 +8579,20 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 63629 timestamp: 1774072609062 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 + md5: 0de0122d9570a8ab637c6b73db268389 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 63713 + timestamp: 1785362952714 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py312hb3f7f12_1.conda sha256: ea8151b4f55fa1f9b8d2220c7193c91f545aa15d44415cddbac9ea1f8782c117 md5: b99d90ef4e77acdab74828f79705a919 @@ -5185,6 +8607,21 @@ packages: run_exports: {} size: 39432 timestamp: 1725089587134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py314hd4c109c_1.conda + sha256: 7f3083018be486b73c82e5e2421ab882d5231fcd424843c96058b01ce5f3cbaf + md5: 2f6295571ea5e9278046efc3ef377a98 + depends: + - python + - lz4-c + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + - lz4-c >=1.10.0,<1.11.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 45224 + timestamp: 1765026391393 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 md5: 9de5350a85c4a20c685259b889aa6393 @@ -5194,6 +8631,9 @@ packages: - libstdcxx >=13 license: BSD-2-Clause license_family: BSD + run_exports: + weak: + - lz4-c >=1.10.0,<1.11.0a0 size: 167055 timestamp: 1733741040117 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda @@ -5249,6 +8689,7 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 27424 timestamp: 1772445227915 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda @@ -5265,6 +8706,20 @@ packages: run_exports: {} size: 17831 timestamp: 1777000588302 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py314h815e797_2.conda + sha256: 5dc6c469ed94cba223272ba70be250b5820e88640c8fdfe4ae85b2966b8d4b67 + md5: 19db05d03d18c1746c2212bc83e9c4c2 + depends: + - matplotlib-base >=3.11.1,<3.11.2.0a0 + - pyside6 >=6.7.2 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 15007 + timestamp: 1785211130117 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda sha256: c7e133837376e53e6a52719c205a3067c42f05769bc3e8307417f8d817dfc63e md5: 7d499b5b6d150f133800dc3a582771c7 @@ -5294,6 +8749,36 @@ packages: run_exports: {} size: 8336056 timestamp: 1777000573501 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py314h9a9c090_2.conda + sha256: d76657ece6fef30e44fca988a0a884cf5744f2fb1611814c5c993700148fbefa + md5: 57f668cbc4b70c11ea9d19ebed67295e + depends: + - __glibc >=2.17,<3.0.a0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.28.2 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libraqm >=0.11.0,<0.12.0a0 + - libstdcxx >=14 + - numpy >=1.25 + - numpy >=1.25,<3 + - packaging >=20.0 + - pillow >=9 + - pyparsing >=3 + - python >=3.14,<3.15.0a0 + - python-dateutil >=2.7 + - python_abi 3.14.* *_cp314 + - qhull >=2020.2,<2020.3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 9068707 + timestamp: 1785211110030 - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.0-py314h9e666f3_2.conda sha256: 6e20ef7149f06bcfc6342b90c617d074141d827128c27b2ccdcb5a79a4bc5dc4 md5: a9edd3780999970d02f3e0440cf688c5 @@ -5304,6 +8789,17 @@ packages: license: BSD-3-Clause AND MIT size: 207080 timestamp: 1782377704416 +- conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_1.conda + sha256: 8aa8c68ec9b5c37f8feb356f8e0640c885e70cbb2aae396211b181be48427eb9 + md5: 53d8848429e4c801b030e2bc8e084db6 + depends: + - python + - tomli-w + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause AND MIT + run_exports: {} + size: 207824 + timestamp: 1786008811631 - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda sha256: 41558b95a387ce2374260aa034a99c3a88cbb98e1a56510f4fa6839063de867b md5: fe7b4ff5792fee512aad843294ea809a @@ -5315,15 +8811,35 @@ packages: - liblzma >=5.8.3,<6.0a0 - libstdcxx >=14 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.6,<4.0a0 + - openssl >=3.5.6,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Zlib + license_family: Other + run_exports: + weak: + - minizip >=4.2.1,<5.0a0 + size: 520570 + timestamp: 1778002506337 +- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hb71707f_0.conda + sha256: 80398daac34dfe10d88c92ef64f6ff85f52950cab8b2bdfc2d00cc176ed7edff + md5: 88450ba743694055e218d03a2fdd561e + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other run_exports: weak: - - minizip >=4.2.1,<5.0a0 - size: 520570 - timestamp: 1778002506337 + - minizip >=4.2.2,<5.0a0 + size: 521106 + timestamp: 1782851456704 - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py312h0a2e395_1.conda sha256: c9764c77dd7f9c581c1d8a24c3024edf777cacfb5a4180de5badc6638dc8590f md5: a142257c1b69e37cfefc66dc228a4d93 @@ -5338,6 +8854,20 @@ packages: run_exports: {} size: 112800 timestamp: 1782460774570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h97ea11e_1.conda + sha256: f33032ef3dcb8759309e8a55b0cb989e377ce315122bb86b00c0adbcf6665abf + md5: 1c8698b2012012806ec17a062bb9f339 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 113343 + timestamp: 1782460776477 - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h9891dd4_0.conda sha256: bbbb210e90dbd8c9ddba24730aca81aefe23f5ecaf79a81b6740e0dbd25f7b21 md5: 9b67bf9c63607097b6e010a7b555a388 @@ -5351,6 +8881,20 @@ packages: license_family: Apache size: 103513 timestamp: 1782070374726 +- conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda + sha256: 320dfc59a94cb9e3635bda71b9e62278b34aa2fdaea0caa6832ddb9b37e9ccd5 + md5: ab3e3db511033340e75e7002e80ce8c0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: MIT + license_family: MIT + run_exports: + weak: + - muparser >=2.3.5,<2.4.0a0 + size: 203174 + timestamp: 1747116762269 - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.1.0-py314h518bba1_0.conda sha256: 9fcf9e7343c30fdeab75e8440caa2814798eeff22a6dc5f7a556df8372e1b0ca md5: 5625fce7423d7579e60f9cbcc5738d2b @@ -5369,6 +8913,25 @@ packages: license_family: MIT size: 22860519 timestamp: 1780315602311 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.0-py314h518bba1_0.conda + sha256: 77158133fc8c3fc95acc74742efab4ac40ccd070298c4a946ac8f99b5b7017a9 + md5: 862cee264de9cdba7dda3ccf6312f74e + depends: + - ast-serialize >=0.6.0,<1.0.0 + - mypy_extensions >=1.0.0 + - pathspec >=1.0.0 + - python + - python-librt >=0.13.0 + - typing_extensions >=4.6.0 + - psutil >=4.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 23233304 + timestamp: 1783986175097 - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda sha256: 9c2e3f9e9883e4b8d7e9e6abf7b235dc00bdcd5ef66640a360464a9f5756294d md5: 94116b69829e90b72d566e64421e1bff @@ -5410,6 +8973,28 @@ packages: - ncurses >=6.6,<7.0a0 size: 918956 timestamp: 1777422145199 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 + md5: ee6c0cd80a60961a1f48aa3e0b91f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 911196 + timestamp: 1786355078102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda + sha256: 8f8b554c74b032b3e63a8828fb0ecc7ae4f5c5a6bd0afcf75423d5ff79290cb0 + md5: fe93be7dd9209e6ae673962e3031ff51 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + run_exports: {} + size: 136372 + timestamp: 1785920438981 - conda: https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda sha256: e3664264bd936c357523b55c71ed5a30263c6ba278d726a75b1eb112e6fb0b64 md5: e235d5566c9cc8970eb2798dd4ecf62f @@ -5479,6 +9064,27 @@ packages: license_family: BSD size: 9075918 timestamp: 1782112541752 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314h2b28147_0.conda + sha256: 124b753583ea9c157301fe78de3e88aa5fa8806bd2da8abaa8808065d1b93d51 + md5: d77631addad93399a90157d2c597e7f3 + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + - libblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - numpy >=1.25,<3 + size: 9119694 + timestamp: 1786330625923 - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d md5: 11b3379b191f63139e29c0d19dee24cd @@ -5513,6 +9119,23 @@ packages: - openldap >=2.6.10,<2.7.0a0 size: 780253 timestamp: 1748010165522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda + sha256: 21c4f6c7f41dc9bec2ea2f9c80440d9a4d45a6f2ac13243e658f10dcf1044146 + md5: 680608784722880fbfe1745067570b00 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.28,<3.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.6,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + run_exports: + weak: + - openldap >=2.6.13,<2.7.0a0 + size: 786149 + timestamp: 1775741359582 - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_0.conda sha256: d48f5c22b9897c01e4dff3680f1f57ceb02711ab9c62f74339b080419dfad34b md5: 79dd2074b5cd5c5c6b2930514a11e22d @@ -5527,6 +9150,20 @@ packages: - openssl >=3.6.3,<4.0a0 size: 3159683 timestamp: 1781069855778 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda + sha256: 012096056b97abf1f68c46b7146bd2cbd68c1be762340b4f5dad4fbbe99177bc + md5: c5955c27917ff2234def47f075e71e02 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - openssl >=3.6.3,<4.0a0 + size: 3182423 + timestamp: 1785913583650 - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h669347b_0.conda sha256: 8a126e0be7f87c499f0a9b5229efa4321e60fc4ae46abdec9b13240631cb1746 md5: 1e6c10f7d749a490612404efeb179eb8 @@ -5547,6 +9184,28 @@ packages: - orc >=2.0.2,<2.0.3.0a0 size: 1066349 timestamp: 1723760593232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + sha256: ae96bc0895a2279f2ea296879e0475333e838cc88754b0c1a4903dd92a7e1b21 + md5: 1280a5f8270f30b89cc8373ecfe8899d + depends: + - tzdata + - libgcc >=14 + - libstdcxx >=14 + - __glibc >=2.17,<3.0.a0 + - snappy >=1.2.2,<1.3.0a0 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil * cxx17* + - libzlib >=1.3.2,<2.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - orc >=2.3.1,<2.3.2.0a0 + size: 1458252 + timestamp: 1784301236872 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda sha256: 009408dcfdc789b8a1748d6a63fd2134ea2edc8474231ea7beba0ac3ad772a37 md5: 15c437bfa4cbddd379b95357c9aa4150 @@ -5658,6 +9317,62 @@ packages: license_family: BSD size: 15303815 timestamp: 1778602611222 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda + sha256: b8197d816dfef2e237c45e875d7e37d125c1e12af819a911dcfd95c2cb98a010 + md5: 15cd0f375b79b47c4048e44396e6e6f1 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - numpy >=1.23,<3 + - python_abi 3.14.* *_cp314 + constrains: + - adbc-driver-postgresql >=1.2.0 + - adbc-driver-sqlite >=1.2.0 + - beautifulsoup4 >=4.12.3 + - blosc >=1.21.3 + - bottleneck >=1.4.2 + - fastparquet >=2024.11.0 + - fsspec >=2024.10.0 + - gcsfs >=2024.10.0 + - html5lib >=1.1 + - hypothesis >=6.116.0 + - jinja2 >=3.1.5 + - lxml >=5.3.0 + - matplotlib >=3.9.3 + - numba >=0.60.0 + - numexpr >=2.10.2 + - odfpy >=1.4.1 + - openpyxl >=3.1.5 + - psycopg2 >=2.9.10 + - pyarrow >=13.0.0 + - pyiceberg >=0.8.1 + - pymysql >=1.1.1 + - pyqt5 >=5.15.9 + - pyreadstat >=1.2.8 + - pytables >=3.10.1 + - pytest >=8.3.4 + - pytest-xdist >=3.6.1 + - python-calamine >=0.3.0 + - pytz >=2024.2 + - pyxlsb >=1.0.10 + - qtpy >=2.4.2 + - scipy >=1.14.1 + - s3fs >=2024.10.0 + - sqlalchemy >=2.0.36 + - tabulate >=0.9.0 + - xarray >=2024.10.0 + - xlrd >=2.0.1 + - xlsxwriter >=3.2.0 + - zstandard >=0.23.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 15360863 + timestamp: 1785276230218 - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hda50119_1.conda sha256: 315b52bfa6d1a820f4806f6490d472581438a28e21df175290477caec18972b0 md5: d53ffc0edc8eabf4253508008493c5bc @@ -5678,6 +9393,29 @@ packages: license: LGPL-2.1-or-later size: 458036 timestamp: 1774281947855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + sha256: 48f27a6c3e4062bc09dafe9c7f6b288c5de5655e81095ab7f1aad920b2163b7b + md5: 6a2822aaf9a34ac3708904a47ff3dd7e + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - fontconfig >=2.18.2,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.16,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - pango >=1.58.2,<2.0a0 + size: 469916 + timestamp: 1786107384537 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda sha256: 09717569649d89caafbf32f6cda1e65aef86e5a86c053d30e4ce77fca8d27b68 md5: 31614c73d7b103ef76faa4d83d261d34 @@ -5703,6 +9441,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - pcre2 >=10.47,<10.48.0a0 size: 1222481 timestamp: 1763655398280 - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda @@ -5727,6 +9468,28 @@ packages: run_exports: {} size: 1039561 timestamp: 1775060059882 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py314h8ec4b1a_0.conda + sha256: bcab128df8980514061a78b9c67f5004954048f584da20df8c5a78de9e3f5abb + md5: 233e62a8eb894b79b5c93f4f8dec4dcd + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libtiff >=4.7.1,<4.8.0a0 + - openjpeg >=2.5.4,<3.0a0 + - libxcb >=1.17.0,<2.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - python_abi 3.14.* *_cp314 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - lcms2 >=2.19.1,<3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: HPND + run_exports: {} + size: 1108174 + timestamp: 1782912080163 - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a md5: c01af13bdc553d1a8fbfff6e8db075f0 @@ -5742,6 +9505,20 @@ packages: - pixman >=0.46.4,<1.0a0 size: 450960 timestamp: 1754665235234 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + sha256: 829d8288764282de5a9f7b9169acb75cc7dc0b6c3fe2535cfe87dea3436bbc5d + md5: 0ee5bb30034b081a1386c1e2c98ab0a7 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + run_exports: + weak: + - pixman >=0.46.4,<1.0a0 + size: 376704 + timestamp: 1786106621354 - conda: https://conda.anaconda.org/conda-forge/linux-64/poppler-24.08.0-h47131b8_1.conda sha256: b32fe787525236908e21885fef8d77e8ebdbbe6694b2fb89ed799444ebda3178 md5: 0854b9ff0cc10a1f6f67b0f352b8e75a @@ -5815,6 +9592,45 @@ packages: - proj >=9.4.1,<9.5.0a0 size: 3050689 timestamp: 1722327846022 +- conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda + sha256: dff6f355025b9a510d9093e29fd970fa1091e758b848c9dec814d96ae63a09ba + md5: b23619e5e9009eaa070ead0342034027 + depends: + - sqlite + - libtiff + - libcurl + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libsqlite >=3.53.0,<4.0a0 + - libcurl >=8.19.0,<9.0a0 + constrains: + - proj4 ==999999999999 + license: MIT + license_family: MIT + run_exports: + weak: + - proj >=9.8.1,<9.9.0a0 + size: 3652144 + timestamp: 1775840249166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + sha256: 013669433eb447548f21c3c6b16b2ed64356f726b5f77c1b39d5ba17a8a4b8bc + md5: a83f6a2fdc079e643237887a37460668 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + run_exports: + weak: + - prometheus-cpp >=1.3.0,<1.4.0a0 + size: 199544 + timestamp: 1730769112346 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda sha256: d834fd656133c9e4eaf63ffe9a117c7d0917d86d89f7d64073f4e3a0020bd8a7 md5: dd94c506b119130aef5a9382aed648e7 @@ -5838,8 +9654,20 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 231303 timestamp: 1769678156552 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + sha256: afc3b27b2cbb0487c1d0e963f96e71181ecfb623a24fb393bb19ff974a6382a1 + md5: df2c27f36bdb0dde779f55b5df76a352 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: {} + size: 9115 + timestamp: 1786067714761 - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 md5: b3c17d95b5a10c6e64a21fa17573e70e @@ -5861,6 +9689,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 230470 timestamp: 1757853295064 - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_1.conda @@ -5880,6 +9709,24 @@ packages: license_family: BSD size: 12191860 timestamp: 1781020717312 +- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_2.conda + noarch: python + sha256: 1aa4eb372a408a8655327c3e304891028dce9ea3002cd3d68845e66e51ba7510 + md5: 8c845f47320cf9d61106989ab343250f + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - openssl >=3.5.7,<4.0a0 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __glibc >=2.17 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 12065493 + timestamp: 1784707900361 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda sha256: 9e1baddd1199e244f4f8be10c7250691088948583ab3955c510b90eb71c91a2c md5: 5f7d505626cb057e1320bbd46dd02ef2 @@ -5897,6 +9744,22 @@ packages: run_exports: {} size: 25538 timestamp: 1730169714708 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py314hdafbbf9_0.conda + sha256: fe0e85d85a9609c62a981ca4beb02a83c363fc86774fb49cbec6d854c46af664 + md5: 2cd4fd80bec78a128b26fe81c4d07ead + depends: + - libarrow-acero 25.0.0.* + - libarrow-dataset 25.0.0.* + - libarrow-substrait 25.0.0.* + - libparquet 25.0.0.* + - pyarrow-core 25.0.0 *_0_* + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 26040 + timestamp: 1784258391200 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h01725c0_2_cpu.conda build_number: 2 sha256: e5ed32ee7664a6322263e446d3504a35ff6f5452b17f1161bce7922d03f3cbd4 @@ -5917,6 +9780,26 @@ packages: run_exports: {} size: 4607408 timestamp: 1730169265797 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py314h969be7f_0_cpu.conda + sha256: 910e3c1920477e6d13589e106086f6fd65e40d6e846f88814da413d369c5fad3 + md5: 4ab67ab85baaa448bae03e6d9de3b4a7 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0.* *cpu + - libarrow-compute 25.0.0.* *cpu + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - numpy >=1.23,<3 + - apache-arrow-proc * cpu + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 5466581 + timestamp: 1784258329989 - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_3.conda sha256: ccef6174b89815de1ede61b3b20ebccfe301865f2954d789e0b5c1e52dd45f91 md5: be49bb746ad2cd2ba6737b4afd6cf32a @@ -5929,6 +9812,19 @@ packages: license_family: MIT size: 88644 timestamp: 1757744868118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda + sha256: 0d3cbeb41afef769fe2d8ab7e0e6a2140c8577018ae5e237839b2eae9d571747 + md5: 0fa51561c73d1f362997f8aceab87146 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 88896 + timestamp: 1784146233775 - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda sha256: b8260660d064fb947f4b573ec4a782696bc8b19042452eaa4e9bb1152b540555 md5: dfb9a57535eb8c35c6744da7043063f0 @@ -5958,6 +9854,7 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + run_exports: {} size: 1895020 timestamp: 1778084229247 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.conda @@ -5977,6 +9874,23 @@ packages: run_exports: {} size: 639343 timestamp: 1727771812389 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py314hbcf5174_0.conda + sha256: 53b72845bc9051b1b94c83ed06047788cdb07af529b9368393ac1a9eb720ac21 + md5: b6696a3d5c567d3b2015bf77f454f247 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgdal-core >=3.12.0,<3.13.0a0 + - libstdcxx >=14 + - numpy + - packaging + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 667940 + timestamp: 1764402531595 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py312h9211aeb_9.conda sha256: 9525e8363d4b5509c7520f8b47a7011c0d1e8f66b294f17679093a3d15af086c md5: 173afeb0d112c854fd1a9fcac4b5cce3 @@ -5992,6 +9906,46 @@ packages: run_exports: {} size: 547934 timestamp: 1725436149519 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py314h7ce3bca_5.conda + sha256: 63c864c34287c09518e6d3bb78443226939dce29308e152793ba864cef5c7b04 + md5: ebb3897b4acfe36f029e8f7c4a3e861c + depends: + - python + - proj + - certifi + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - proj >=9.8.1,<9.9.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 573847 + timestamp: 1786351881422 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py314h3987850_1.conda + sha256: e410d0d4151f418dc75ea2dc38dfb0e7a136090b6874e5ca1c699fa840b4994d + md5: 5d2051f0630a568926943fc53c0aaa4c + depends: + - python + - qt6-main 6.11.1.* + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libxslt >=1.1.43,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - python_abi 3.14.* *_cp314 + - qt6-main >=6.11.1,<6.12.0a0 + - libclang13 >=22.1.5 + - libxml2 + - libxml2-16 >=2.14.6 + license: LGPL-3.0-only + license_family: LGPL + run_exports: {} + size: 13821776 + timestamp: 1778933872780 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda sha256: 6569b51ed41e46957e6587a705a930681375accd737c677acae883089f50a2a8 md5: 8baf6a8672bf235afede64de7a7da1c2 @@ -6045,6 +9999,38 @@ packages: - python size: 31608571 timestamp: 1772730708989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-h242f9ac_102_cp314.conda + build_number: 102 + sha256: f5ff5c1fac471dfed4fc4288856a63eb9d771e6042d9f70420d75b9f488400d8 + md5: 9b6c336ef7195fbee1c10c09bfcf901b + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=14 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.2,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.7,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + run_exports: + weak: + - python_abi 3.14.* *_cp314 + noarch: + - python + size: 36866750 + timestamp: 1786444737142 + python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-habeac84_100_cp314.conda build_number: 100 sha256: 6d28ac2b061179deb434d3d57afa98ffd20ec3c5d44ab8048a1ca33424b22d38 @@ -6084,6 +10070,19 @@ packages: license_family: MIT size: 156252 timestamp: 1778511622812 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda + sha256: 2655bda3e0f53863c9ba1239aa9212b399141582074d0c043df6f449ff21b0f1 + md5: 102786d7a63373e0441273b7d863d4ea + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 162846 + timestamp: 1786328851615 - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda sha256: 17cd445cbdf0a33a247934497c867195edc2bf51faad8011c74332460d47634b md5: 711ddd4f498e10b56f687683ae76e059 @@ -6094,6 +10093,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 291078 timestamp: 1778688806850 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda @@ -6121,6 +10121,7 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + run_exports: {} size: 202391 timestamp: 1770223462836 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_3.conda @@ -6137,6 +10138,7 @@ packages: - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 210896 timestamp: 1779483879367 - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda @@ -6152,6 +10154,81 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 552937 timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda + sha256: aefbc43bde188ff4027d480da99c7fa9e8e6341e9762e065190239cb9b99bb1c + md5: 331d660aef48fec733a878dd1f8f4206 + depends: + - libxcb + - xcb-util + - xcb-util-wm + - xcb-util-keysyms + - xcb-util-image + - xcb-util-renderutil + - xcb-util-cursor + - libgl-devel + - libegl-devel + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - xcb-util >=0.4.1,<0.5.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - fontconfig >=2.18.1,<3.0a0 + - fonts-conda-ecosystem + - xorg-libxxf86vm >=1.1.7,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - libsqlite >=3.53.2,<4.0a0 + - libpq >=18.4,<19.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - wayland >=1.25.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - libpng >=1.6.58,<1.7.0a0 + - harfbuzz >=14.2.1 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - libcups >=2.3.3,<2.4.0a0 + - libxcb >=1.17.0,<2.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libdrm >=2.4.127,<2.5.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - zstd >=1.5.7,<1.6.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - icu >=78.3,<79.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - alsa-lib >=1.2.16,<1.3.0a0 + - openssl >=3.5.6,<4.0a0 + - libglib >=2.88.1,<3.0a0 + - libgl >=1.7.0,<2.0a0 + - libxkbcommon >=1.13.2,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - dbus >=1.16.2,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + constrains: + - qt ==6.11.1 + license: LGPL-3.0-only + license_family: LGPL + run_exports: + weak: + - qt6-main >=6.11.1,<7.0a0 + size: 60185421 + timestamp: 1780593127053 - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda sha256: 8776405bb5b256456fa1749bd26946944577f2d73b8c749f9d7c1a5c187bdca2 md5: 4d483b12b9fc7169d112d4f7a250c05c @@ -6242,6 +10319,31 @@ packages: run_exports: {} size: 7430252 timestamp: 1726176608886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py314h91cf270_1.conda + sha256: 6fe69aa2c0c213adf737f91036a50fe3ac0baeedb385916e4d116c787887fd1b + md5: 46740345de0ebe1dbba6ef6ed26282c0 + depends: + - __glibc >=2.17,<3.0.a0 + - affine + - attrs + - certifi + - click >=4,!=8.2.* + - click-plugins + - cligj >=0.5 + - libgcc >=14 + - libgdal-core >=3.12.4,<3.13.0a0 + - libstdcxx >=14 + - numpy >=1.25,<3 + - proj >=9.8.1,<9.9.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - setuptools >=0.9.8 + - snuggs >=1.4.1 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 7777425 + timestamp: 1785964760305 - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda sha256: f0f520f57e6b58313e8c41abc7dfa48742a05f1681f05654558127b667c769a8 md5: 8f70e36268dea8eb666ef14c29bd3cda @@ -6255,6 +10357,19 @@ packages: - re2 size: 26617 timestamp: 1708946796423 +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda + sha256: 7279908454f0c87b84ebc4aec6924f02f41a105d88420fb35dfaf5ecd976e0f5 + md5: d83f2ee212d217a6d745a00e5be84671 + depends: + - libre2-11 2025.11.05 h60473fc_2 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + - re2 + size: 27397 + timestamp: 1781312576962 - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 md5: d7d95fc8287ea7bf33e0e7116d2b95ec @@ -6269,6 +10384,19 @@ packages: - readline >=8.3,<9.0a0 size: 345073 timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-h54a6638_2.conda + sha256: d217fcb9c5a3a532b6e00896613a117004cdc48883ef5047642346614375a9a4 + md5: 8b45f3f47e08558e07ef2b96ab45f8b2 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc >=14.2.7.post0,<14.3.0a0 + size: 37536 + timestamp: 1785921754425 - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-hb03c661_1.conda sha256: b47cbe954b1522f944c1c17fae964d362dfcf7ef7516fda1c368971426a8e933 md5: 2e1edbf819157ca726ec65afb4eb2d5d @@ -6279,6 +10407,22 @@ packages: license_family: MIT size: 35494 timestamp: 1779092421531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-h50b1954_2.conda + sha256: d523da96c07cfde82b8c11cf84a25f79b8b6e8485298c54ee974e9fbad8b81fe + md5: f10296a0f976c49f24fdd9258fb3c9aa + depends: + - reproc ==14.2.7.post0 h54a6638_2 + - libgcc >=14 + - libstdcxx >=14 + - __glibc >=2.17,<3.0.a0 + - reproc >=14.2.7.post0,<14.3.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + size: 29216 + timestamp: 1785921754425 - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-hecca717_1.conda sha256: f29ba41cc71a01f1dd7eeba573b1535f0feec72e9ab6b4a390b4e71230d61ade md5: a0ef2594b3ab25a933ee8e7a1ac1e21b @@ -6305,6 +10449,21 @@ packages: license_family: MIT size: 309696 timestamp: 1779976994059 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h1bee95f_0.conda + sha256: 064e56d6c233cbcfac5b0183c0dd10b732668ff27aa1ea3580459acceae95473 + md5: add3cbcc5eb677f6c1720e01cd82aac5 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + run_exports: {} + size: 300129 + timestamp: 1782831350283 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda sha256: 59f7773ed8c6f2bcab3e953e2b4816c86cf71e5508dc571f8073b8c2294b5787 md5: 8ea76c64b49b16c37769ea7c4dca993b @@ -6316,6 +10475,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 308487 timestamp: 1766175778417 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314h0f05182_1.conda @@ -6328,6 +10488,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 150041 timestamp: 1766159514023 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.15.19-h6a952e8_0.conda @@ -6344,6 +10505,21 @@ packages: license_family: MIT size: 9506544 timestamp: 1782288610637 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.2-h462bb3b_0.conda + noarch: python + sha256: 2730d570f45ba836f5b85700776cc4527894c640ebacb536fb5db138c845ee63 + md5: 8a00805aae730fb6266343985496ee07 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + run_exports: {} + size: 9481279 + timestamp: 1786151870074 - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda sha256: a6fa0afa836f8f26dea0abc180ca2549bb517932d9a88a121e707135d4bcb715 md5: 334dba9982ab9f5d62033c61698a8683 @@ -6358,6 +10534,20 @@ packages: - s2n >=1.5.5,<1.5.6.0a0 size: 353081 timestamp: 1728534228471 +- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + sha256: 7369ac21f3561e2203f5b1600ef0671270057380783ca4b9e15f679ba25db575 + md5: fa1c00d999e83ec20173c9775f71a1f1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - s2n >=1.7.5,<1.7.6.0a0 + size: 392607 + timestamp: 1783164766248 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda sha256: 8c0cd0326b5a17ddcf189fc4f119bf6871b7853595c088075847c484a3ed567e md5: e6e9b5795bb495325c3b4ebd451519aa @@ -6379,6 +10569,27 @@ packages: run_exports: {} size: 10038167 timestamp: 1780401052981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda + sha256: 6a01f4403db746acd676e34e80e3a14d041f2261d658402ca13dae6407c35d44 + md5: 30883954413aad9e3ac42134bef91ffe + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.4.0 + - threadpoolctl >=3.5.0 + - narwhals >=2.0.1 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - numpy >=1.23,<3 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 10311253 + timestamp: 1780401051520 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda sha256: d5ac05ad45c0d48731eb189c2cbb2bb99f0e3cb7e1acaad373cb2f1f2597fc75 md5: 15995ecb2ef890778ba9a3750190f09d @@ -6401,6 +10612,28 @@ packages: run_exports: {} size: 16828243 timestamp: 1779874781187 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py314hf07bd8e_0.conda + sha256: 85503102237f8515ab92319fc14609e894ac9e95e3a1398b0c49db1f9ee50877 + md5: 62c390c1f8f51240f1ebc7ba782669ad + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=2.0.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 17260022 + timestamp: 1781912924009 - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.6-py312h6cab151_1.conda sha256: b7818c7264926401b78b0afc9a7d2c98ff0fc0ed637ad9e5c126da38a40382f7 md5: 5be02e05e1adaa42826cc6800ce399bc @@ -6416,6 +10649,21 @@ packages: run_exports: {} size: 571255 timestamp: 1725394110104 +- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py314hbe3edd8_2.conda + sha256: 17cb5cec9283f993072e8b6f5e1417d8d892cc5efa27029eae954ab06b33c7e2 + md5: 5963e6ee81772d450a35e6bc95522761 + depends: + - __glibc >=2.17,<3.0.a0 + - geos >=3.14.1,<3.14.2.0a0 + - libgcc >=14 + - numpy >=1.23,<3 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 652785 + timestamp: 1762523657698 - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.4-hb700be7_0.conda sha256: 7a1f81823ec7d5ad659024de878ec34ea90205743cd685790621d7c013cc0a75 md5: a0a159ba93ca0018f3d3e333470b045f @@ -6427,6 +10675,19 @@ packages: license_family: APACHE size: 317505 timestamp: 1778109124630 +- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.6-h171cf75_1.conda + sha256: 09adc403283de12da403a996a9ddd068f1b425dcdccefa2eeec4e2b684e114d4 + md5: 92b6b03d39f81d10467a6901ce85fc3b + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: Apache-2.0 + run_exports: + weak: + - simdjson >=4.6.6,<4.7.0a0 + size: 355874 + timestamp: 1786528156277 - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py312h4c3975b_0.conda sha256: 70a0ab8787576d55f23d114cc1d7569fd83f5b148c71a7f277180299e86ea8f1 md5: bb3b931588a7dc83cc7b82836b0017df @@ -6440,6 +10701,19 @@ packages: run_exports: {} size: 160254 timestamp: 1777112097179 +- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py314h5bd0f2a_0.conda + sha256: d4e5dff4086b8efda9e1f12d456a24a5d62a355b01a681eaa97a2ef95b91a246 + md5: 3f114d76f35141da9f4be18af7cba051 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 163682 + timestamp: 1777112100084 - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 md5: 98b6c9dc80eb87b2519b97bcf7e578dd @@ -6482,6 +10756,21 @@ packages: license_family: MIT size: 196681 timestamp: 1767781665629 +- conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda + sha256: 5fca627690d56c6f87a9dabafceee54cb75d7da392309bb080333948c048365a + md5: 5e32ceeb7b9514ce4730565052829dbd + depends: + - __glibc >=2.17,<3.0.a0 + - fmt >=12.1.0,<12.2.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 197438 + timestamp: 1785924346496 - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.51-py314h0f05182_0.conda sha256: 3c46e9af535a376c7269b61563f84c601235b00d50fc97f87ffbf3b68a51fe17 md5: aeb7447f3ea37b2bb32167267dcf0bfe @@ -6489,13 +10778,27 @@ packages: - python - greenlet !=0.4.17 - typing-extensions >=4.6.0 - - __glibc >=2.17,<3.0.a0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + size: 4034632 + timestamp: 1781547880247 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda + sha256: 0a3d82fb1650d364f8c2427d870a6c6684e02c9e5c4b0cccb6def83957f007fb + md5: c54cb6754823fe6710369511aab5c29c + depends: + - python + - greenlet !=0.4.17 + - typing-extensions >=4.6.0 - libgcc >=14 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT - size: 4034632 - timestamp: 1781547880247 + run_exports: {} + size: 4047549 + timestamp: 1786535232010 - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda sha256: ef17725138fa72aa5a0f8ccace9727831c4b333e39575f78fb5ad1755826b687 md5: b345ea7f13e4cae9809c69b1d1af2c99 @@ -6512,6 +10815,23 @@ packages: - libsqlite >=3.53.3,<4.0a0 size: 206481 timestamp: 1782519082269 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h04a0ce9_0.conda + sha256: 1f6c9638d64c51a35769219cc895e49af5cd0615aef896604c5472bc79b980f7 + md5: b6db30b9cfd0cb089910ccb47a2516f9 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=14 + - libsqlite 3.53.4 hf4e2dac_0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - readline >=8.3,<9.0a0 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 206694 + timestamp: 1785016118388 - conda: https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.26.0-h86fa3b2_0.conda sha256: 3e92cec15daed5e03d7fc676a021500fc92ac80716495504537d6e4bdb80138f md5: 061175d9d4c046a1cf8bffe95a359fab @@ -6561,6 +10881,22 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3301196 timestamp: 1769460227866 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda + build_number: 103 + sha256: 43624eab22f5f29df7d6ffe914cf442f28fd559b55b290906255492826e636e8 + md5: 48a1049e710857572fc2a832aa394d9f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + constrains: + - xorg-libx11 >=1.8.13,<2.0a0 + license: TCL + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3550916 + timestamp: 1784229071544 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda sha256: f54504d6eeef133ddc2b964b6a021f3faf085bb08bd70debc07f56d6b9b726f1 md5: 55f526c3fb5302a1ce922612348442e1 @@ -6586,6 +10922,19 @@ packages: license_family: Apache size: 918368 timestamp: 1781006801436 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda + sha256: ebec0d90f99fa7bbe91eabab41ee77d3f36dbd58ec2f08aa4220fdd713610b6d + md5: faea84d2f2ccadf70cf9e55381d75b3e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 923237 + timestamp: 1786226770518 - conda: https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda sha256: 35da5b89561ed93629f751f111a092abcaaa9fdc516e09e9b4ab9880756a1871 md5: 5c5f9b9bad2a0852ffa38833ba2a1c7d @@ -6610,6 +10959,19 @@ packages: run_exports: {} size: 410641 timestamp: 1770909099497 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py314h5bd0f2a_0.conda + sha256: ff1c1d7c23b91c9b0eb93a3e1380f4e2ac6c37ea2bba4f932a5484e9a55bba30 + md5: 494fdf358c152f9fdd0673c128c2f3dd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 409562 + timestamp: 1770909102180 - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda sha256: 2aad2aeff7c69a2d7eecd7b662eef756b27d6a6b96f3e2c2a7071340ce14543e md5: d71d3a66528853c0a1ac2c02d79a0284 @@ -6636,6 +10998,22 @@ packages: license_family: MIT size: 334139 timestamp: 1773959575393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-h1964d1d_1.conda + sha256: a8f1333274382d23721d6f72483ece364631231f8ac3f74389951b1ff2820148 + md5: 47e3c5d0b1e968ee49efc7c3fa8ebc0c + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - wayland >=1.26.0,<2.0a0 + size: 339462 + timestamp: 1786151675802 - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda sha256: 6b9e182021ef3a64ab3bf788ebdab6de6775035612237c639ccafc941639eb13 md5: b34c5559f45d8996e3bc0b6250a6cc84 @@ -6662,6 +11040,7 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 64997 timestamp: 1756851739706 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda @@ -6764,6 +11143,22 @@ packages: - xerces-c >=3.2.5,<3.3.0a0 size: 1648243 timestamp: 1727733890754 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda + sha256: 605980121ad3ee9393a9b53fb0996929c9732f8fc6b9f796d25244ca6fa23032 + md5: 66a1db55ecdb7377d2b91f54cd56eafa + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.1,<79.0a0 + - libgcc >=14 + - libnsl >=2.0.1,<2.1.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - xerces-c >=3.3.0,<3.4.0a0 + size: 1660075 + timestamp: 1766327494699 - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda sha256: 3b04afd5d1a65d2d27ac2d49a63b01ab8bcd875776779ec63e337370ed38afdc md5: b233b41be0bf210989d57160ed39b394 @@ -6776,6 +11171,18 @@ packages: run_exports: {} size: 441670 timestamp: 1782027360439 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + sha256: 49b532d1df875c6749d9078b56a76f3f5db49a5abe0ca620b593ed474ef0ebf1 + md5: 85c9442aec283b4e464fa9ecc484a2f3 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + run_exports: + weak: + - xorg-libice >=1.1.2,<2.0a0 + size: 62517 + timestamp: 1786474410404 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b md5: fb901ff28063514abb6046c9ec2c4a45 @@ -6789,6 +11196,20 @@ packages: - xorg-libice >=1.1.2,<2.0a0 size: 58628 timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + sha256: ef907caee0665cf3b0f775602cc50e388e3bade8ce22ecade0873ff26604b2fd + md5: aa7459ed9ad086ba11dda843d764b33d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libice >=1.1.2,<2.0a0 + - libuuid >=2.42.2,<3.0a0 + license: MIT + run_exports: + weak: + - xorg-libsm >=1.2.6,<2.0a0 + size: 30739 + timestamp: 1786545374265 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 md5: 1c74ff8c35dcadf952a16f752ca5aa49 @@ -6831,6 +11252,19 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 size: 15321 timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + sha256: cbf891f6cc1a859347680af1c8562bc6b033bf182a2a3bb536016932be3206de + md5: f06ef439c280a5f90b8bf62355008dbc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxau >=1.0.12,<2.0a0 + size: 16419 + timestamp: 1786381001122 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a md5: f2ba4192d38b6cef2bb2c25029071d90 @@ -6891,6 +11325,19 @@ packages: - xorg-libxdmcp >=1.1.5,<2.0a0 size: 20591 timestamp: 1762976546182 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda + sha256: c50a16c05ccd7fe7dd6d6cfb539f4e9a491d50f9ed7a5c902fec638f7d0d27be + md5: 2e66c929f3d879708335b6ea4557c838 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxdmcp >=1.1.5,<2.0a0 + size: 21120 + timestamp: 1786381006369 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f md5: 34e54f03dfea3e7a2dcf1453a85f1085 @@ -6946,6 +11393,9 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxinerama >=1.1.6,<1.2.0a0 size: 14818 timestamp: 1769432261050 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda @@ -7009,6 +11459,17 @@ packages: - xorg-libxxf86vm >=1.1.7,<2.0a0 size: 18701 timestamp: 1769434732453 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + sha256: 051c6088bf2381840fcf8764737b829cc6c5f793718d2417d097d6e3b153eba9 + md5: 3b51576511038b50fdbd05245e22e4b1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: {} + size: 594844 + timestamp: 1786114408394 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda sha256: 7a8c64938428c2bfd016359f9cb3c44f94acc256c6167dbdade9f2a1f5ca7a36 md5: aa8d21be4b461ce612d8f5fb791decae @@ -7088,6 +11549,20 @@ packages: license_family: MIT size: 223526 timestamp: 1745307989800 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda + sha256: 425808ebd2a20bfc460995293995e1fa0510e93c300535e2ec3622bd3d84288c + md5: 18bf4b05b458fb8135987e47364f3dcb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 242654 + timestamp: 1785923983284 - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda sha256: dc9f28dedcb5f35a127fad2d847674d2833369dd616d294e423b8997df31d8a8 md5: 96b08867e21d4694fa5c2c226e6581b0 @@ -7099,6 +11574,9 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA + run_exports: + weak: + - zeromq >=4.3.5,<4.4.0a0 size: 311184 timestamp: 1779123989774 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda @@ -7114,6 +11592,19 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 95931 timestamp: 1774072620848 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + sha256: 16080a1c7724f7d25727cdc23c7658e0cec2db52448c1dc0c33467ee2c6e1c62 + md5: 6acb86426229f96f93e5468d1df3a5e8 + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib 1.3.2 h25fd6f3_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 96132 + timestamp: 1785362957588 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f md5: 2aadb0d17215603a82a2a6b0afd9a4cb @@ -7157,6 +11648,7 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 473605 timestamp: 1762512687493 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda @@ -7182,6 +11674,17 @@ packages: license_family: MIT size: 8191 timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + sha256: 2a7204314663eeda5dec482a956f0e2eaf289bd5b9953eaaaad0e81aa64638f2 + md5: 3845f3d75991bae0fb90884662f4327c + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + run_exports: {} + size: 8144 + timestamp: 1784221492234 - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda sha256: a362b4f5c96a0bf4def96be1a77317e2730af38915eb9bec85e2a92836501ed7 md5: b3f0179590f3c0637b7eb5309898f79e @@ -7191,6 +11694,7 @@ packages: - librsvg license: LGPL-3.0-or-later OR CC-BY-SA-3.0 license_family: LGPL + run_exports: {} size: 631452 timestamp: 1758743294412 - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda @@ -7203,6 +11707,17 @@ packages: run_exports: {} size: 19164 timestamp: 1733762153202 +- conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + sha256: 5a72f7fbb1adb86817fde49acf972148638cc613edae9c15ac575d176da2746a + md5: 71dd867a02739013ae65894ca32d67b7 + depends: + - attrs + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 16762 + timestamp: 1786226684740 - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda sha256: a21c69c91d735faaf7b74dc2b3d013b14016951b66354461fc1d796caab9b1c4 md5: 69a01a9927733014f3955861e2969a7b @@ -7211,6 +11726,7 @@ packages: - pyparsing - python >=3.10 license: EPL-2.0 + run_exports: {} size: 26885 timestamp: 1782349642228 - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -7224,6 +11740,17 @@ packages: run_exports: {} size: 18074 timestamp: 1733247158254 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + sha256: b8fcb994134d3918d1c64a9d62f4168ff85d5bfb89e698102fe4ed679fe0df24 + md5: 108c928d2a8551832dbc762b535e90bb + depends: + - python >=3.10 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + run_exports: {} + size: 19461 + timestamp: 1784935220549 - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.1-pyhcf101f3_0.conda sha256: 6119355a0b2a33e7b0dd31a740dbe01df66ffee0a643f80968afb1d53e7dbdb3 md5: 7498bb2648f852c6a3cd5724ca80bdeb @@ -7240,6 +11767,24 @@ packages: license: MIT size: 161308 timestamp: 1782357087265 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + sha256: e36998c5e860e26b22e5dbcd5726dd0c4eabad949c84d383cad8512757bbf6a1 + md5: fb568fbae6908ba86a090a85a089d11f + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.10 + - typing_extensions >=4.5 + - python + constrains: + - trio >=0.32.0 + - uvloop >=0.22.1 + - winloop >=0.2.3 + license: MIT + license_family: MIT + run_exports: {} + size: 164465 + timestamp: 1783889660383 - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda sha256: 8f032b140ea4159806e4969a68b4a3c0a7cab1ad936eb958a2b5ffe5335e19bf md5: 54898d0f524c9dee622d44bbb081a8ab @@ -7247,6 +11792,7 @@ packages: - python >=3.9 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 10076 timestamp: 1733332433806 - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda @@ -7255,6 +11801,7 @@ packages: depends: - python >=3.9 license: MIT OR Apache-2.0 + run_exports: {} size: 50894 timestamp: 1737352715041 - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda @@ -7264,6 +11811,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 12806 timestamp: 1764079623900 - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda @@ -7277,6 +11825,18 @@ packages: license_family: Apache size: 28797 timestamp: 1763410017955 +- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + sha256: fbbd8ce60cbd5c16f3fe559eb644551f94285caff30985ea961ff851c1cf25ac + md5: 89d495168582cb00428dad699d149624 + depends: + - python >=3.10 + constrains: + - astroid >=2,<5 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 34639 + timestamp: 1783975742052 - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda sha256: 1b6124230bb4e571b1b9401537ecff575b7b109cc3a21ee019f65e083b8399ab md5: c6b0543676ecb1fb2d7643941fe375f2 @@ -7295,6 +11855,7 @@ packages: depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 + run_exports: {} size: 7526 timestamp: 1781450817767 - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda @@ -7322,6 +11883,7 @@ packages: - pytokens >=0.4 license: MIT license_family: MIT + run_exports: {} size: 176838 timestamp: 1779460936776 - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda @@ -7345,6 +11907,27 @@ packages: run_exports: {} size: 4526315 timestamp: 1781002115296 +- conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + sha256: 577c2614f3a59512697c1ffe2021c5194818b55f734006fcdeb6c6921b514c44 + md5: b2781afb860ae9ed84cb92bdebbc83ea + depends: + - contourpy >=1.2 + - jinja2 >=2.9 + - narwhals >=1.13 + - numpy >=1.16 + - packaging >=16.8 + - pillow >=7.1.0 + - python >=3.10 + - pyyaml >=3.10 + - tornado >=6.2 + - xyzservices >=2021.09.1 + constrains: + - panel >=1.8.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 4292921 + timestamp: 1785249803504 - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.0.0-pyhd8ed1ab_0.conda sha256: d3c8be0524a5223bfed89c4a5be90598bfca28e0c702014f7799b978027eb5e1 md5: 06c2cbdcfd20af2f72a95e09f8747c52 @@ -7354,6 +11937,16 @@ packages: license_family: BSD size: 307488 timestamp: 1781879511321 +- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + sha256: ca44f906756aaa908645c9bcef31a132fc82e7934777aa6bb45c12edada6a58c + md5: 92c88d22e52af7b672605b12a31bf0bb + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 309453 + timestamp: 1784706150046 - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda sha256: 1acf87c77d920edd098ddc91fa785efc10de871465dee0f463815b176e019e8b md5: 1fcdf88e7a8c296d3df8409bf0690db4 @@ -7383,6 +11976,24 @@ packages: run_exports: {} size: 128866 timestamp: 1781708962055 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda + sha256: 95e8e74062a5fe5f870ac8c90302b6e89945165fdaed7810606e84ddee6aac12 + md5: e27d2ac27b096dc51fedfcf775a53f9b + depends: + - __win + license: ISC + run_exports: {} + size: 132136 + timestamp: 1784754918886 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + sha256: 0a0544cf95f64394fe4959286f5c71f5444ad58feb0602e53becb27448d24da6 + md5: 0f51e2391ade309db462a55611263e9c + depends: + - __unix + license: ISC + run_exports: {} + size: 131780 + timestamp: 1784754889428 - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 noarch: python sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 @@ -7393,6 +12004,17 @@ packages: license_family: BSD size: 4134 timestamp: 1615209571450 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda + noarch: python + sha256: 0d00dd61cb91b1bd1536600c64c9db4f94f3c699fec42864d0a2f4bc2ad8c3e8 + md5: 1990eb3f49022846fbc7fc9624a0ee43 + depends: + - cached_property >=1.5.2,<1.5.3.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 6836 + timestamp: 1783242914545 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 md5: 576d629e47797577ab0f1b351297ef4a @@ -7402,6 +12024,16 @@ packages: license_family: BSD size: 11065 timestamp: 1615209567874 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda + sha256: b1808a7811b5688d045b204425bb7ee824b340b40025b4ad0a39b4db1f4f1c98 + md5: f71e6840332854fd2eac6c3229768a9c + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 14752 + timestamp: 1783242913845 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda sha256: 6c13620e458ba43278379d0cdacc30c497336bddfda81681fd50d114a65c702f md5: c13824fedced67005d3832c152fe9c2f @@ -7411,6 +12043,15 @@ packages: run_exports: {} size: 133877 timestamp: 1781719949728 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + sha256: fb167de4388e64e52aa3907ed099afab944c1fa6e5f74b281a312dae1bcf7f3b + md5: 37e13edbe3b48f1095a9d085ef9cd83b + depends: + - python >=3.10 + license: ISC + run_exports: {} + size: 137015 + timestamp: 1784717699092 - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 md5: a9167b9571f3baa9d448faa2139d1089 @@ -7421,6 +12062,16 @@ packages: run_exports: {} size: 58872 timestamp: 1775127203018 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + sha256: 8d8813ef655b4e75e4fb897abd83ad548882efad7b4e836b021b797f42780799 + md5: d154b40b109e503430979e8a8d099eaf + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 61418 + timestamp: 1783505332569 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda sha256: 5b8e8d8876ace41735f51ca43c43cdc9e1b4fbbae0f415d6b8441fec826d8c47 md5: f73f35eedcd8e89d6c4407df15101233 @@ -7446,6 +12097,31 @@ packages: run_exports: {} size: 104999 timestamp: 1779900548735 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda + sha256: 5b5c96afdd801dd9c3b78ebc2cd9a9f3ce34186257415d394dde1aa8468aa3c0 + md5: 8a0d65027e25e367f9f1754f0604e8de + depends: + - __win + - colorama + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 106227 + timestamp: 1783085395110 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + sha256: ccc4787f511964f9a1f2d2d2859c91c5d571fb60f7f09d4c4e092c9b7a94e671 + md5: 2c4bd6aeb90bb157456841c3270a0d92 + depends: + - __unix + - python + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 107155 + timestamp: 1783085363526 - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda sha256: ba1ee6e2b2be3da41d70d0d51d1159010de900aa3f33fceaea8c52e9bd30a26e md5: e9b05deb91c013e5224672a4ba9cf8d1 @@ -7525,6 +12201,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 14690 timestamp: 1753453984907 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda @@ -7541,6 +12218,7 @@ packages: - zstandard license: BSD-3-Clause license_family: BSD + run_exports: {} size: 210538 timestamp: 1782239750810 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda @@ -7551,6 +12229,7 @@ packages: - pyyaml >=6.0.0,<7.0.0 license: MIT license_family: MIT + run_exports: {} size: 10327 timestamp: 1717043667069 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.4.2-pyhd8ed1ab_0.conda @@ -7569,6 +12248,21 @@ packages: license_family: BSD size: 59960 timestamp: 1778768196921 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + sha256: b7cc67470a361330edd8d9e331cafd4807dd77bc766ca197a3783ed3db66dfeb + md5: 49592004caf4336815fd5282bbb72172 + depends: + - python >=3.10 + - libmambapy >=2.0.0,!=2.6.2 + - boltons >=23.0.0 + - python + constrains: + - conda >=26.5 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 46828 + timestamp: 1784200590190 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda sha256: 3a9934ecf483d46b56a59547c63bd40bef4c398bfc671acb2ba0f98a045ccb8b md5: 6248ea5c3194d00fb1db6d68fcdcfd3e @@ -7581,6 +12275,19 @@ packages: license_family: BSD size: 20396 timestamp: 1778023975685 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda + sha256: eb20fc63ce6438e10d7c64890bd23d0c68f62a479f2bae8b3c892e13949daf03 + md5: 7f1b127b170f9f0d01eca4b54a046ee5 + depends: + - conda >=26.3.0 + - pydantic >=2.12.5,<3 + - python >=3.10 + - ruamel.yaml + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 20633 + timestamp: 1783438353470 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda sha256: 63d5b510644a01b4c93738304cb0dc8a336542fc335b0b77f3b355ba7d119260 md5: 3f1854f1ec2090538fb49b7078dbb9de @@ -7592,6 +12299,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 256182 timestamp: 1781015592350 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda @@ -7602,6 +12310,7 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 23954 timestamp: 1781293054998 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda @@ -7614,6 +12323,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 37334 timestamp: 1781212602267 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.0-pyhd8ed1ab_0.conda @@ -7624,8 +12334,19 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - size: 22216 - timestamp: 1778152874928 + size: 22216 + timestamp: 1778152874928 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda + sha256: efd3b7b79a0e3993a174ab0140794479e2479b59fd053605acaaa25718f0f013 + md5: ac705504abf28bb924a021a3d5f4b55f + depends: + - conda >=26.1.1 + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 22510 + timestamp: 1784724984642 - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda sha256: 7f8ea42a8411b433ec7244dfd30637d90564256c13a114dbe42455fe032ae89c md5: 12389a21e7f69704b0ae77f44355e30b @@ -7635,6 +12356,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 45817 timestamp: 1773233306055 - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 @@ -7644,6 +12366,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 8331 timestamp: 1608581999360 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_100.conda @@ -7656,6 +12379,17 @@ packages: license: Python-2.0 size: 49333 timestamp: 1781254618863 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda + noarch: generic + sha256: 4d97fdae803c1ed7c704289d1f856d60e5502f24e93e92f9d45fbea37fd9e9a7 + md5: 6b344ff499096c0b873d202fea1e2fcd + depends: + - python >=3.14,<3.15.0a0 + - python_abi * *_cp314 + license: Python-2.0 + run_exports: {} + size: 49916 + timestamp: 1786444134021 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 md5: 4c2a8fef270f6c69591889b93f9f55c1 @@ -7689,6 +12423,28 @@ packages: run_exports: {} size: 11216 timestamp: 1781272553165 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda + sha256: 6fe2135c130f5832841d5fa5cd6dbadf82d84f61858ae8e2dfadf71ab1c8fb88 + md5: 07d4f6b76ccd0a3721c0964dac50db99 + depends: + - python >=3.10 + - dask-core >=2026.7.1,<2026.7.2.0a0 + - distributed >=2026.7.1,<2026.7.2.0a0 + - cytoolz >=0.11.2 + - lz4 >=4.3.2 + - numpy >=1.26 + - pandas >=2.0 + - bokeh >=3.1.0 + - jinja2 >=2.10.3 + - pyarrow >=16.0 + - python + constrains: + - openssl !=1.1.1e + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 10399 + timestamp: 1784056179014 - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda sha256: ce64f55e95912bff84119f05a296c90f8df57ecf9c3100bd8a6beca1cedabbb7 md5: 56a3cae23de84509452da890fb2bfd85 @@ -7708,6 +12464,25 @@ packages: run_exports: {} size: 1069053 timestamp: 1781221472758 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda + sha256: feef96841d636fc35c3fef7c7276fa9c175baf4444896073bee65f08abe6ac91 + md5: 7e9fcc61c119f585f2b7b491147df821 + depends: + - python >=3.10 + - click >=8.1 + - cloudpickle >=3.0.0 + - fsspec >=2021.9.0 + - packaging >=20.0 + - partd >=1.4.0 + - pyyaml >=5.4.1 + - toolz >=0.12.0 + - importlib-metadata >=4.13.0 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1074755 + timestamp: 1784031128995 - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda sha256: 430bd9d731b265f0bedb3183ac3ecfaa1656390c092b6e864ff8cc1229843c8c md5: 61dcf784d59ef0bd62c57d982b154ace @@ -7715,6 +12490,7 @@ packages: - python >=3.10 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 16102 timestamp: 1779115228886 - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda @@ -7745,6 +12521,34 @@ packages: run_exports: {} size: 838296 timestamp: 1781563082788 +- conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + sha256: bae0d85df264228cbd485b444bd1f5135e32093e32503aa72e4b4b61636d12b2 + md5: f06a1abc946ca9b6107851cf148621f3 + depends: + - python >=3.10 + - click >=8.0 + - cloudpickle >=3.0.0 + - cytoolz >=0.12.0 + - dask-core >=2026.7.1,<2026.7.2.0a0 + - jinja2 >=2.10.3 + - locket >=1.0.0 + - msgpack-python >=1.0.2 + - packaging >=20.0 + - psutil >=5.8.0 + - pyyaml >=5.4.1 + - sortedcontainers >=2.0.5 + - tblib >=1.6.0 + - toolz >=0.12.0 + - tornado >=6.2.0 + - zict >=3.0.0 + - python + constrains: + - openssl !=1.1.1e + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 843078 + timestamp: 1784043304773 - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda sha256: 5603c7d0321963bb9b4030eadabc3fd7ca6103a38475b4e0ed13ed6d97c86f4e md5: 0a2014fd9860f8b1eaa0b1f3d3771a08 @@ -7752,6 +12556,7 @@ packages: - python >=3.9 license: Apache-2.0 license_family: APACHE + run_exports: {} size: 41773 timestamp: 1734729953882 - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda @@ -7760,6 +12565,7 @@ packages: depends: - python >=3.10 license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + run_exports: {} size: 438002 timestamp: 1766092633160 - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda @@ -7769,6 +12575,7 @@ packages: - python >=3.10 license: MIT license_family: MIT + run_exports: {} size: 21853 timestamp: 1762165431693 - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda @@ -7792,6 +12599,7 @@ packages: - python >=3.10 - typing_extensions >=4.6.0 license: MIT and PSF-2.0 + run_exports: {} size: 21333 timestamp: 1763918099466 - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda @@ -7801,6 +12609,7 @@ packages: - python >=3.10 license: MIT license_family: MIT + run_exports: {} size: 30753 timestamp: 1756729456476 - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.4-pyhd8ed1ab_0.conda @@ -7811,6 +12620,15 @@ packages: license: Unlicense size: 36989 timestamp: 1781381078337 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda + sha256: 5476fe77cc2f59389dd348135462892dc20f42114301221648df90a6e9650186 + md5: 977e2b00d5329b6d56ebe94ae1f4b67c + depends: + - python >=3.10 + license: Unlicense + run_exports: {} + size: 77939 + timestamp: 1785376409053 - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda sha256: 782fa186d7677fd3bc1ff7adb4cc3585f7d2c7177c30bcbce21f8c177135c520 md5: a6997a7dcd6673c0692c61dfeaea14ab @@ -7881,6 +12699,21 @@ packages: run_exports: {} size: 4059 timestamp: 1762351264405 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + sha256: c9752235f1ff7061d834e5e4a3d0adf71ebeeff2b3fad82dab607edce7f70c91 + md5: 0509ee74d95e5b98eb6fe2a47760e399 + depends: + - brotli + - munkres + - python >=3.10 + - unicodedata2 >=15.1.0 + track_features: + - fonttools_no_compile + license: MIT + license_family: MIT + run_exports: {} + size: 846038 + timestamp: 1778770337113 - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda sha256: 32b75b9a3097c4156ba5070739bbfdcc0bc23890c0faa0dbff569ecb16b8f44f md5: 43fdb6bcc1227a22290d0bc02a9484c9 @@ -7889,6 +12722,7 @@ packages: - python license: LGPL-3.0-only license_family: LGPL + run_exports: {} size: 34300 timestamp: 1781203600051 - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda @@ -7901,6 +12735,16 @@ packages: run_exports: {} size: 149709 timestamp: 1781615868173 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda + sha256: 3cd1c985695d8114bdba2a4a38c87e86d633fadd7cfe7a6733ebb3fe807fdc86 + md5: b9176565976c773a0739bd83deaf06cc + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 151868 + timestamp: 1785325238671 - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda sha256: cd478c0835106afce2478d6dcc525854219c4e710c7b446e2dd5042bfc9da082 md5: 99d77e0f92f7b0b977f77cfb49c7eaf6 @@ -7940,6 +12784,7 @@ packages: - smmap >=3.0.1,<6 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 53136 timestamp: 1735887290843 - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.50-pyhd8ed1ab_0.conda @@ -7953,6 +12798,19 @@ packages: license_family: BSD size: 162193 timestamp: 1778065820061 +- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda + sha256: 967efdf5ebd1a0c78cf957d1b9ad85d0ceb4d9ebfb173b348cf4cab3086878b8 + md5: 2c5ddbfbac16be06c0e49acafe8691f5 + depends: + - python >=3.10 + - gitdb >=4.0.1,<5 + - typing_extensions >=3.10.0.2 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 177303 + timestamp: 1786443183227 - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda sha256: 4d3f8a3a42a88493cdae21f54efc57154c23403901d5d54d05b9fa6991b9bfc7 md5: 36a2d7837350f7e8da83bcaad552d0b5 @@ -7983,6 +12841,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 39069 timestamp: 1767729720872 - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda @@ -7998,6 +12857,19 @@ packages: run_exports: {} size: 95967 timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + sha256: 307dd6ec90140c3cf4171071b0e5e870abec314f4565c1edd5bc433e942cdcc0 + md5: e652ac7756069c456d0da2a922cd7df5 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.2,<5 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 100789 + timestamp: 1785796355216 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda sha256: fdcea5d7cb314485d3907192ef024c704311548c5b0cbeb390cd1951051e29d2 md5: b395909221b9bd1df066e5930e18855b @@ -8021,6 +12893,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 49483 timestamp: 1745602916758 - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda @@ -8034,6 +12907,7 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 63082 timestamp: 1733663449209 - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda @@ -8044,6 +12918,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 73563 timestamp: 1733928021866 - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh7428d3b_8.conda @@ -8055,6 +12930,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 74084 timestamp: 1733928364561 - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda @@ -8097,6 +12973,7 @@ packages: - python >=3.10 license: MIT license_family: MIT + run_exports: {} size: 13387 timestamp: 1760831448842 - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda @@ -8109,6 +12986,7 @@ packages: - toml >=0.10.2 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 18713 timestamp: 1734884952029 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh01cf8df_0.conda @@ -8135,6 +13013,7 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 137725 timestamp: 1781101860049 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh6dadd2b_0.conda @@ -8160,6 +13039,7 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 138046 timestamp: 1781101760172 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyha191276_0.conda @@ -8185,6 +13065,7 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 138635 timestamp: 1781101665847 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda @@ -8231,6 +13112,50 @@ packages: license_family: BSD size: 652076 timestamp: 1780654438137 +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda + sha256: 8470648b5e790d1881c09c02068d53e1bf0126f020db02a6dc2e73400cf1eeeb + md5: 23564ed27c9c7714905be96ac5786500 + depends: + - __unix + - ipython_pygments_lexers >=1.0.0 + - jedi >=0.18.2 + - matplotlib-inline >=0.1.6 + - prompt-toolkit >=3.0.41,<3.1.0 + - psutil >=7 + - pygments >=2.14.0 + - python >=3.11 + - stack_data >=0.6.0 + - traitlets >=5.13.0 + - typing_extensions >=4.6 + - pexpect >4.6 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 716078 + timestamp: 1785754203352 +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyhe2676ad_0.conda + sha256: 2a0cd24e5c0e1eb8b7baf06346906673f6b8abea151ce302d7d978a3c416aeec + md5: d7c95d926befcfa22bee69bb1980e2ce + depends: + - __win + - ipython_pygments_lexers >=1.0.0 + - jedi >=0.18.2 + - matplotlib-inline >=0.1.6 + - prompt-toolkit >=3.0.41,<3.1.0 + - psutil >=7 + - pygments >=2.14.0 + - python >=3.11 + - stack_data >=0.6.0 + - traitlets >=5.13.0 + - typing_extensions >=4.6 + - colorama >=0.4.4 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 715162 + timestamp: 1785754256301 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda sha256: 894682a42a7d659ae12878dbcb274516a7031bbea9104e92f8e88c1f2765a104 md5: bd80ba060603cc228d9d81c257093119 @@ -8239,6 +13164,7 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 13993 timestamp: 1737123723464 - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda @@ -8249,6 +13175,7 @@ packages: - parso >=0.8.6,<0.9.0 - python license: Apache-2.0 AND MIT + run_exports: {} size: 2715215 timestamp: 1782251948616 - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda @@ -8282,6 +13209,7 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 17311 timestamp: 1733814664790 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda @@ -8292,6 +13220,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 14190 timestamp: 1774311356147 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda @@ -8306,6 +13235,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 82356 timestamp: 1767839954256 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda @@ -8317,6 +13247,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 19236 timestamp: 1757335715225 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda @@ -8333,6 +13264,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 117954 timestamp: 1781019994076 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyh6dadd2b_0.conda @@ -8349,6 +13281,7 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 64679 timestamp: 1760643889625 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda @@ -8365,6 +13298,7 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 65503 timestamp: 1760643864586 - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 @@ -8392,6 +13326,21 @@ packages: run_exports: {} size: 810830 timestamp: 1752271625200 +- conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda + sha256: 791137f3e107c73315c74bb0c7be34e5f7c36da1828efbc4584544311121484c + md5: 0323c95182d828bb1bcf708c4077db3b + depends: + - networkx >=3.4 + - numpy >=2.0 + - pandas >=2.3 + - python >=3.12 + - scikit-learn >=1.5 + - scipy >=1.14 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 448635 + timestamp: 1786480374013 - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda sha256: 35b43d7343f74452307fd018a1cca92b8f68961ff8e2ab6a81ce0a703c9a3764 md5: 9acc1c385be401d533ff70ef5b50dae6 @@ -8400,6 +13349,7 @@ packages: - traitlets license: BSD-3-Clause license_family: BSD + run_exports: {} size: 15725 timestamp: 1778264403247 - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda @@ -8433,6 +13383,17 @@ packages: run_exports: {} size: 285532 timestamp: 1780672242196 +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + sha256: 57f525a1c55b08c3204fab05d2c74a3e9c2172d7f08f1ecaa07e19865cc1d7cf + md5: 42ef6cbb3e1d0e6689b9dd160f560eae + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 289946 + timestamp: 1783943716915 - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda sha256: 7a5bd30a2e7ddd7b85031a5e2e14f290898098dc85bea5b3a5bf147c25122838 md5: bbe1963f1e47f594070ffe87cdf612ea @@ -8446,6 +13407,21 @@ packages: license_family: BSD size: 100945 timestamp: 1733402844974 +- conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda + sha256: 4aeff72162e4b3f77238c859547275b9a989ed6873bffbdbc4d688291a60919b + md5: 3e7eb1cc4a342b2e7f68199b50fbe3cb + depends: + - jsonschema >=2.6 + - jupyter_core >=4.12,!=5.0.* + - python >=3.10 + - python-fastjsonschema >=2.15 + - traitlets >=5.1 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 107583 + timestamp: 1786372859684 - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda sha256: e6768ceef038f4d7e083de7e393f5dd7d672b937e2bda570b740f6399b686689 md5: fcd832bfd4749e9b246112b6894f97fc @@ -8454,6 +13430,7 @@ packages: - python license: BSD-2-Clause license_family: BSD + run_exports: {} size: 15903 timestamp: 1770973502283 - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda @@ -8479,6 +13456,16 @@ packages: license_family: MIT size: 4335 timestamp: 1758194464430 +- conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda + sha256: 30d271dd63391e053aa3c393c5d93fb622dec5d50a64107171ee28242660608e + md5: 849b8852621992c3f3a1901c939a0399 + license: MIT + license_family: MIT + run_exports: + weak: + - nlohmann_json-abi ==3.12.0 + size: 4365 + timestamp: 1785920576349 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 md5: 58335b26c38bf4a20f399384c33cbcf9 @@ -8487,6 +13474,7 @@ packages: - python license: Apache-2.0 license_family: APACHE + run_exports: {} size: 62477 timestamp: 1745345660407 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda @@ -8562,6 +13550,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 82472 timestamp: 1777722955579 - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda @@ -8583,6 +13572,7 @@ packages: - python >=3.10 license: MPL-2.0 license_family: MOZILLA + run_exports: {} size: 56559 timestamp: 1777271601895 - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda @@ -8592,6 +13582,7 @@ packages: - ptyprocess >=0.5 - python >=3.9 license: ISC + run_exports: {} size: 53561 timestamp: 1733302019362 - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh145f28c_0.conda @@ -8603,6 +13594,16 @@ packages: license_family: MIT size: 1202377 timestamp: 1780262809860 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + sha256: 0f7021cfb3ff454c91a5e28e1f5060906a52c6d1cde3f879a7d03ac33c28b1c3 + md5: 573cb3ed111004230ed3de650653cd4d + depends: + - python >=3.13.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 1198163 + timestamp: 1785914482439 - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda sha256: 9e5e1fd3506ccfc4d444fc4d2d39b0ed097d5d0e3bd3d4bdf6bcc81aaf66860d md5: 2c5ef45db85d34799771629bd5860fd7 @@ -8613,6 +13614,17 @@ packages: license_family: MIT size: 26308 timestamp: 1779972894916 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda + sha256: eea7ead8bcdc3b307241cbc8d038f8b95db9e351e52f2bf3c12ad55eb4d9a8d1 + md5: 15f2020d6b2ede94d80b2c884a1a6f3e + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 26869 + timestamp: 1786390868223 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -8621,6 +13633,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 25877 timestamp: 1764896838868 - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda @@ -8643,12 +13656,26 @@ packages: license_family: BSD size: 273927 timestamp: 1756321848365 +- conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + sha256: efe8def2c93aa34cd8d3c9af1dc4c7d312791cf769d8b2b615e32733e6df6051 + md5: 39c92a39517316e5001d645ae63d9ab9 + depends: + - python >=3.10 + - wcwidth + constrains: + - prompt_toolkit 3.0.53 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 276081 + timestamp: 1785160613307 - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda sha256: a7713dfe30faf17508ec359e0bc7e0983f5d94682492469bd462cdaae9c64d83 md5: 7d9daffbb8d8e0af0f769dbbcd173a54 depends: - python >=3.9 license: ISC + run_exports: {} size: 19457 timestamp: 1733302371990 - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda @@ -8658,6 +13685,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 16668 timestamp: 1733569518868 - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda @@ -8665,6 +13693,9 @@ packages: md5: f0599959a2447c1e544e216bddf393fa license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - pybind11-abi ==11 size: 14671 timestamp: 1752769938071 - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda @@ -8714,6 +13745,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 150656 timestamp: 1766345630713 - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda @@ -8723,6 +13755,7 @@ packages: - python >=3.10 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 893031 timestamp: 1774796815820 - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda @@ -8744,6 +13777,7 @@ packages: - tomli >=1.1.0 license: MIT license_family: MIT + run_exports: {} size: 15528 timestamp: 1733710122949 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda @@ -8786,6 +13820,7 @@ packages: - pytest-faulthandler >=2 license: MIT license_family: MIT + run_exports: {} size: 306724 timestamp: 1782127176429 - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda @@ -8803,6 +13838,7 @@ packages: - build <0 license: MIT license_family: MIT + run_exports: {} size: 28946 timestamp: 1778048948266 - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda @@ -8827,6 +13863,17 @@ packages: license_family: BSD size: 244628 timestamp: 1755304154927 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda + sha256: 2243b305387413fa716827dce44011ea121be002e7ec24404ba00651db0279cd + md5: d066c36f0c7658ef422c60d598ea8495 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 252180 + timestamp: 1785242896823 - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda sha256: 84c129bdd6abcecac42a948f2670d17fe735d02d3a5a483a9b1f1bc33ba38c28 md5: 224f69f177eb5aae6c9a6052846bf609 @@ -8836,6 +13883,16 @@ packages: license: Python-2.0 size: 49315 timestamp: 1781254664376 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda + sha256: b15fb3e5daae788ca78844ee4ee9f1a1b07911cd4e83c484d8c1ce2794c6c93b + md5: 364ec4bb854ab4ca9ca4e626a55ea8da + depends: + - cpython 3.14.6.* + - python_abi * *_cp314 + license: Python-2.0 + run_exports: {} + size: 49897 + timestamp: 1786444152084 - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda sha256: 9c18fa1ebd0c839b6ea12e99d510a0968c2288cc423185cb34bf7332b97684e9 md5: 5cc7e2e78962dc902cae9e29c2aa4b2f @@ -8843,6 +13900,7 @@ packages: - python >=3.10 license: MIT license_family: MIT + run_exports: {} size: 239330 timestamp: 1778554210561 - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda @@ -8855,6 +13913,16 @@ packages: run_exports: {} size: 146639 timestamp: 1777068997932 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda + sha256: 3f05db78cf8be33cf6dbc469664b8e3a01f3980d61d6d6bef48669b171896d8a + md5: eefc8d916bd2e708d76d40398ef9a1ee + depends: + - python >=3.10 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 146862 + timestamp: 1783704822814 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda build_number: 8 sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 @@ -8874,6 +13942,7 @@ packages: - python 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 6989 timestamp: 1752805904792 - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda @@ -8887,6 +13956,17 @@ packages: run_exports: {} size: 201747 timestamp: 1777892201250 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda + sha256: ad774abda71c759baef515983bfedbba34d56d6227974c23715c04612f812a90 + md5: eb2fb9070c0232e96ae5515d8b28e4f9 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 201126 + timestamp: 1785077087428 - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda sha256: 183b225db9a5da05aeb5240291aaa3494bff8322b196d75f5094ee6a7d4e654b md5: 290f97c3fcfab8323023b10cbb4f6cc2 @@ -8918,6 +13998,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 51788 timestamp: 1760379115194 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -8953,6 +14034,22 @@ packages: run_exports: {} size: 53405 timestamp: 1761337252983 +- conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + sha256: 26884204704cb0399339c50b12ba715a2ef426fb90afecd39d68d6617890f722 + md5: 3e549fac4b34fd5c8a89b9c0cc5bbd8e + depends: + - python >=3.12 + - packaging + - rasterio >=1.4.3 + - xarray >=2026.2 + - pyproj >=3.3 + - numpy >=2 + - python + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 65705 + timestamp: 1785242371731 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda sha256: 82088a6e4daa33329a30bc26dc19a98c7c1d3f05c0f73ce9845d4eab4924e9e1 md5: 8e194e7b992f99a5015edbd4ebd38efd @@ -8961,8 +14058,18 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 639697 - timestamp: 1773074868565 + size: 639697 + timestamp: 1773074868565 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + sha256: 9e200ee5f9ff19a4d94e4b51c4856d53dec849f91032f345cf0c6bc3d51a7183 + md5: 62ac906f1cd582c6c264c95625cb9d6f + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 524488 + timestamp: 1786282924579 - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d md5: 3339e3b65d58accf4ca4fb8748ab16b3 @@ -8985,6 +14092,18 @@ packages: license_family: MIT size: 57112 timestamp: 1778331246761 +- conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda + sha256: d0abe2d97b67d42aca30fcd34b3db6cd5de57b4a9ffcf4e04091dd9f4a0cf1e6 + md5: fd2e276dbbd1a2ae15ffdbdbabd908f1 + depends: + - python >=3.10 + - wrapt + - python + license: MIT + license_family: MIT + run_exports: {} + size: 57158 + timestamp: 1782573421136 - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda sha256: bc86861086db65c9b56dd6a1605755f41a9875b94fc3b69e137f686700d91aa1 md5: b899f1195be56d8215ed1973a8f409c3 @@ -8993,6 +14112,7 @@ packages: - python license: BSD-3-Clause license_family: BSD + run_exports: {} size: 27262 timestamp: 1781021238494 - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda @@ -9002,6 +14122,7 @@ packages: - python >=3.10 license: Apache-2.0 license_family: Apache + run_exports: {} size: 15698 timestamp: 1762941572482 - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda @@ -9036,6 +14157,16 @@ packages: run_exports: {} size: 38802 timestamp: 1779635534390 +- conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda + sha256: 056d7a2e91e303a9ee37e580f6dde0511fc3fb72476581cc337aacf2cc747613 + md5: ba33e6c8a46ee373fdf6dd8665212778 + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 39439 + timestamp: 1786202135509 - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda sha256: 9cbf4805021fd817fde2654ccc1a1bd0352647614819a28381e81098efe4da20 md5: 00e6147bef9a85139099c9861c3b976b @@ -9046,6 +14177,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 30854 timestamp: 1771872849343 - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda @@ -9058,6 +14190,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 26988 timestamp: 1733569565672 - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda @@ -9068,6 +14201,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 43964 timestamp: 1772732795746 - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda @@ -9089,6 +14223,7 @@ packages: - python license: Apache-2.0 license_family: APACHE + run_exports: {} size: 31404 timestamp: 1770510172846 - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda @@ -9108,6 +14243,7 @@ packages: - python >=3.6 license: MIT license_family: MIT + run_exports: {} size: 12341 timestamp: 1691135604942 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda @@ -9118,6 +14254,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 24017 timestamp: 1764486833072 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda @@ -9128,6 +14265,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 21561 timestamp: 1774492402955 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda @@ -9137,6 +14275,7 @@ packages: - python >=3.9 license: MIT license_family: MIT + run_exports: {} size: 12680 timestamp: 1736962345843 - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -9176,6 +14315,35 @@ packages: license: MPL-2.0 and MIT size: 94630 timestamp: 1781741323148 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + sha256: a4d9e9da15b13f1ab047e7db412e2ed564bc615832e80213cf129b973c3abf4a + md5: 00953c3b729e03b0ae21f49fdf3441bb + depends: + - python >=3.10 + - __unix + - python + constrains: + - envwrap >=0.2 + - ipywidgets >=6.0 + license: MPL-2.0 and MIT + run_exports: {} + size: 98184 + timestamp: 1785172528905 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda + sha256: 669cd06a4c7c1139f9c6b9850d16d71c1b4ce42375806976a03dba824283fef0 + md5: 7eac270516c8221cedc7f40a96d7fb8f + depends: + - python >=3.10 + - colorama + - __win + - python + constrains: + - envwrap >=0.2 + - ipywidgets >=6.0 + license: MPL-2.0 and MIT + run_exports: {} + size: 97602 + timestamp: 1785172567718 - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda sha256: b89a823edf524956b94a2a4db974866e4501f05c68976eff458c5dcf07f88431 md5: 37e3be7b6e2977d37b8fa5da229f5dc0 @@ -9186,6 +14354,17 @@ packages: license_family: BSD size: 115158 timestamp: 1780507822178 +- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + sha256: 03dba5917f944c6684ab44c81daacac1624cd148e4b2cae215dcec594a210c48 + md5: a79bf97561232a31447b6246c2153ab5 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 116935 + timestamp: 1785761789772 - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda sha256: eece5be81588c39a855a0b70da84e0febb878a6d91dd27d6d21370ce9e5c5a46 md5: c2db35b004913ec69bcac64fb0783de0 @@ -9194,6 +14373,7 @@ packages: - python license: MIT license_family: MIT + run_exports: {} size: 24279 timestamp: 1766494826559 - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda @@ -9234,6 +14414,16 @@ packages: run_exports: {} size: 91383 timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + sha256: b141933ece3518f6d7b75dfb59451e2f26b405a44c18e2518a83e9a02e09315c + md5: c680b5747e8c4c8f23dca0bb7042a8fc + depends: + - typing_extensions ==4.16.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 94080 + timestamp: 1783002732887 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda sha256: 8b90d2f19f9458b8c58a55e1fcdc1d90c1603a847a47654d8a454549413ba60a md5: 53f5409c5cfd6c5a66417d68e3f0a864 @@ -9246,6 +14436,18 @@ packages: run_exports: {} size: 20935 timestamp: 1777105465795 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + sha256: f9d821cb64f2bd33cff02d4969d6b8f8f299b6209abae946ac8aa92927dc217a + md5: 4934c9f5695514b925be8a5503bcd171 + depends: + - python >=3.10 + - typing_extensions >=4.15.0 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 20940 + timestamp: 1786372593154 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 md5: 0caa1af407ecff61170c9437a808404d @@ -9257,6 +14459,17 @@ packages: run_exports: {} size: 51692 timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + sha256: 2d888f90af0686044882c74193ec80a90ec1943145d94a7b1b048958acda1848 + md5: c70ad746c22219b9700931707482992c + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 52631 + timestamp: 1783002732887 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda sha256: a3fbdd31b509ff16c7314e8d01c41d9146504df632a360ab30dbc1d3ca79b7c0 md5: fa31df4d4193aabccaf09ce78a187faf @@ -9276,6 +14489,13 @@ packages: run_exports: {} size: 119135 timestamp: 1767016325805 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 + md5: fcb489df604d100968b737f2cb6076c6 + license: LicenseRef-Public-Domain + run_exports: {} + size: 118849 + timestamp: 1784250406640 - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda sha256: 2fe118c7c33571613cd12f7e9771049e19a46abaa6abfa4adc92d0defc54c61e md5: a85b53859c40d115022948c8f225d8d6 @@ -9287,6 +14507,7 @@ packages: - requests >=2.25 license: MIT license_family: MIT + run_exports: {} size: 286235 timestamp: 1766474788879 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda @@ -9314,6 +14535,7 @@ packages: - python >=3.10 license: MIT license_family: MIT + run_exports: {} size: 103560 timestamp: 1778188657149 - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.1-pyhd8ed1ab_0.conda @@ -9325,6 +14547,16 @@ packages: license_family: MIT size: 133769 timestamp: 1780932915297 +- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + sha256: 4acf845da404e84cef1acccc66cc0156af1b83a5b5d7077b2ca19b705c561e57 + md5: 99f7755ec8648a042b0dbe906234f888 + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 132415 + timestamp: 1782771807703 - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda sha256: 93807369ab91f230cf9e6e2a237eaa812492fe00face5b38068735858fba954f md5: 46e441ba871f524e2b067929da3051c2 @@ -9372,6 +14604,43 @@ packages: run_exports: {} size: 1017999 timestamp: 1776122774298 +- conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda + sha256: 47244c233aeb55de993fe79eebbbb2adc14b388bb0bf2f9dfbaa8aa6610b6345 + md5: f72531e6cf99c98d47306ec917f5e6b9 + depends: + - python >=3.11 + - numpy >=1.26 + - packaging >=24.2 + - pandas >=2.2 + - python + constrains: + - bottleneck >=1.4 + - cartopy >=0.23 + - cftime >=1.6 + - dask-core >=2024.6 + - distributed >=2024.6 + - flox >=0.9 + - h5netcdf >=1.3 + - h5py >=3.11 + - hdf5 >=1.14 + - iris >=3.9 + - matplotlib-base >=3.8 + - nc-time-axis >=1.4 + - netcdf4 >=1.6.0 + - numba >=0.60 + - numbagg >=0.8 + - pint >=0.24 + - pydap >=3.5.0 + - scipy >=1.13 + - seaborn-base >=0.13 + - sparse >=0.15 + - toolz >=0.12 + - zarr >=2.18 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 1027069 + timestamp: 1783633473025 - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda sha256: 663ea9b00d68c2da309114923924686ab6d3f59ef1b196c5029ba16799e7bb07 md5: 4487b9c371d0161d54b5c7bbd890c0fc @@ -9392,6 +14661,7 @@ packages: - pyyaml >=6.0,<7.0 license: MIT license_family: MIT + run_exports: {} size: 16215 timestamp: 1764250734338 - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda @@ -9443,6 +14713,22 @@ packages: license_family: MIT size: 1086020 timestamp: 1780396657560 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda + noarch: python + sha256: 51c8a631b36bf2eb2f27d49c0a0df9e269a76b91a447da7e75f9d0854cabb4a7 + md5: 5ce8b03801df5eff0088bdc85a19d09a + depends: + - python >=3.10 + - __osx >=11.0 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 1085485 + timestamp: 1786328966627 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda sha256: b0747f9b1bc03d1932b4d8c586f39a35ac97e7e72fe6e63f2b2a2472d466f3c1 md5: 57301986d02d30d6805fdce6c99074ee @@ -9455,8 +14741,28 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - atk-1.0 >=2.38.0 size: 347530 timestamp: 1713896411580 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + sha256: 8e503ab875683720c4ddcce216ad2aa96c32b90bc59e9a692c694e07ba4e5d8d + md5: c5d9f5796fb392f0984def10b26f1175 + depends: + - __osx >=11.0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-auth >=0.10.4,<0.10.5.0a0 + size: 117350 + timestamp: 1784086893887 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.7.30-h338687b_0.conda sha256: 2d650815e881ac24237a98ab33d6f6d7431ad52edafc18c748399e1d85ca8641 md5: 385fc8994159e570d0d45223f6b48aa9 @@ -9488,6 +14794,31 @@ packages: - aws-c-cal >=0.7.4,<0.7.5.0a0 size: 39881 timestamp: 1725829996108 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + sha256: 9760cd6c5cc16ecd989866e86fea1fbe23bff3279831ce6f13b83afb6f6f2075 + md5: 5f5ba0cd72e15095ede1ad38e8907f5f + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-cal >=0.9.14,<0.9.15.0a0 + size: 45835 + timestamp: 1784043800544 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + sha256: 0cc9a2a36387975a643cc33d7b60db894650efa707ff73a85e93f80f03904c97 + md5: 35d52f06a5eaaa66c62dd37a4d82d780 + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-common >=0.14.2,<0.14.3.0a0 + size: 228084 + timestamp: 1783637822478 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.9.28-hd74edd7_0.conda sha256: 4081ada22148dc500765aac106ed224829810fd5e5d6f942a842b0a40f53783e md5: 8dc8711c903ab57ead8ce99b65625a95 @@ -9513,6 +14844,19 @@ packages: - aws-c-compression >=0.2.19,<0.2.20.0a0 size: 17974 timestamp: 1725830013702 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + sha256: 8363308db46a84c5d7e9a309aaca8a18541f46f3b6e7b7027479a95ab910e19a + md5: 6e0883cca4143f456e019f751d0b9f3b + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-compression >=0.3.2,<0.3.3.0a0 + size: 21774 + timestamp: 1784042939907 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.4.3-hb2a355e_1.conda sha256: 848473eecd5a438f93072457614f8dabbc09e4f7f12e0512dd4ba51cb0b2a9f3 md5: b84f719ac7c5223ecd2471d86def6bf1 @@ -9529,6 +14873,38 @@ packages: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 size: 46990 timestamp: 1725856827197 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + sha256: d1742afa3329b3ea892b5f0a75b36b4217a7c3eb43250e827c56f122343e9317 + md5: 4ce3e032bfa6d72114e481c2dc99e2e2 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + size: 54275 + timestamp: 1784075773654 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + sha256: 851a01b9e6f0078107549ec1d1545cd27a50a073e4e9599d6f8f4ba69a8cba30 + md5: 9e435c44eafb6aba85f0133f4c579d1c + depends: + - __osx >=11.0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-http >=0.11.0,<0.11.1.0a0 + size: 177761 + timestamp: 1784075780838 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.8.9-hf5a2c8c_0.conda sha256: c2781a06bdd8a695296429c3bbe8dc528a93f426e45ebeb83f3a41de43213dd6 md5: 59efa3b4dc632c4fef6911be61ed1779 @@ -9559,6 +14935,21 @@ packages: - aws-c-io >=0.14.18,<0.14.19.0a0 size: 137133 timestamp: 1728562901503 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + sha256: 2b127f1ccd9f022c945363e1e8eb6fd4a5fbc89677ac678abde6dc022ba26765 + md5: c4d73dc09e972a895d5a3dc7ce158be9 + depends: + - __osx >=11.0 + - s2n >=1.7.5,<1.7.6.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-io >=0.27.3,<0.27.4.0a0 + size: 189938 + timestamp: 1784054954272 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.10.5-h9658b26_0.conda sha256: a43c24179ddeb980a9cd920354f866d10d1f6a70c00ca7fe18840cb79678ecb1 md5: 3736531fdfe90d4513b633d907aab907 @@ -9574,6 +14965,39 @@ packages: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 size: 133346 timestamp: 1726205300093 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + sha256: b352abb6f7cd42bdbbdef647409cf0d94f6edc841151dc3405bf3023b761e65b + md5: 742911742b564828dbaaffaa0f1a21eb + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + size: 158513 + timestamp: 1784087664452 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + sha256: f56014c02923a32cb3387eab782420ff40c9aae68c95cf08bfd2143a5c3c2cdd + md5: ea0e1e78de6375004530dd9f49d7ae46 + depends: + - __osx >=11.0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + size: 133754 + timestamp: 1784101134287 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.6.5-h663ac5c_4.conda sha256: 693463e8dbc86441f9a612c0556f200089aaa72e6e83479205072444d7e1fe34 md5: 1714f0dbabb1431a351c8babe6b75bd9 @@ -9605,6 +15029,19 @@ packages: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 size: 49674 timestamp: 1725836815498 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + sha256: 3ee2dcefcd45283508a3049bd4f5b508a46c16af906a0c3d351d0f9d81738464 + md5: de81e53b45b103470d3be61984dcc292 + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + size: 60771 + timestamp: 1784044312402 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.1.18-h41dd001_11.conda sha256: 246c91ee57fd417685f838958f8532e6ef2a610753f89a5b714f8ebe5d727318 md5: c7cd8fb206915662718006953228dbf7 @@ -9618,6 +15055,19 @@ packages: - aws-checksums >=0.1.18,<0.1.19.0a0 size: 49078 timestamp: 1725836952728 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + sha256: 13313afb01bf4f1c328695724d5b16263810f84d7572f6cb0b4d12f877d7f1d6 + md5: 99cd08b2a8261083e4ad9414cbe737df + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-checksums >=0.2.10,<0.2.11.0a0 + size: 92225 + timestamp: 1784044297006 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.28.2-h8f7a527_6.conda sha256: 47c3a71486f5889b46706e45158e1bc3c375aec9eca99b1edf8d719cdda5cf46 md5: 119a410e9b4fe4e31f91a32d7cb4a764 @@ -9640,6 +15090,28 @@ packages: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 size: 229739 timestamp: 1726483567299 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + sha256: aa4d21bdc3c891d2dca32121ef543aa7f2d8d83868b347207f04ebf65dd5d65d + md5: e850d4d6e349471c50fbed1f55fd3069 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + size: 275833 + timestamp: 1784113326407 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.379-h67f4a54_9.conda sha256: fe06825bfcc9fcbeb4b589e1d62b3621b1565a566ce7d231c198f89bbbfe2a41 md5: 6e081189763244df6240298be3f29823 @@ -9660,6 +15132,24 @@ packages: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 size: 2664996 timestamp: 1725944742080 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + sha256: 215efb2e64b92094c3229c7d43177873068d0a088d7e94ff2c1447360f17a6c8 + md5: 1748ae5f4015e2fec7009ff36cbfafa0 + depends: + - libcxx >=19 + - __osx >=11.0 + - libcurl >=8.21.0,<9.0a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + size: 2834544 + timestamp: 1784137336612 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.13.0-hd01fc5c_0.conda sha256: aff4af38416cf7a81c79e5a3b071ce5aa13ec48da28db0312bc1ebe62cf7273d md5: 2083f6313e623079db6ee67af00e6b27 @@ -9675,6 +15165,36 @@ packages: - azure-core-cpp >=1.13.0,<1.13.1.0a0 size: 287922 timestamp: 1720853302106 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + sha256: 9645073c4682ad3a334a2b06c5fc0ddc57fc9f0f537cc3123053634c92c28625 + md5: 4f42d997f42a8d34ff74cb677cf0c828 + depends: + - __osx >=11.0 + - libcurl >=8.19.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + size: 290309 + timestamp: 1775239330289 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + sha256: d950fb513311a15cd4ae663cdacb2c035122f609a9c790973661b38e0882e40e + md5: e1701f6b2ce6f47aeb6cbfab132403d8 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libcxx >=19 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + size: 168032 + timestamp: 1781268747743 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.8.0-h13ea094_2.conda sha256: 11b01715cae19390890f29ebb56d36d895feafd787ba929aa10b6ce712f3f4b9 md5: 383b72f2ee009992b21f4db08a708510 @@ -9705,6 +15225,38 @@ packages: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 size: 419976 timestamp: 1721865180569 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + sha256: 3f096d7cd6fd7788d57887c3d3e48be1ebc5c3a971666ddc0dd8a3493a5b7cb5 + md5: 0948246cb8e514e4ae1cfe7dbb4046c7 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + size: 436462 + timestamp: 1781284116423 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + sha256: 9a1b6e5284f1f242330dfa1e21408ffd823a76df424108d8c319c2a46df61dba + md5: 2af5d1b5365c4a3de8ed8ec8438fe6ec + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libcxx >=19 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + size: 128710 + timestamp: 1781268693348 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.7.0-hcf3b6fd_1.conda sha256: 3fdf9c0337c48706cffe2e4c761cdea4132fb6dbd1f144d969c28afd903cf256 md5: df7e01bcf8f3a9bfb0ab06778f915f29 @@ -9737,23 +15289,56 @@ packages: - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 size: 189065 timestamp: 1721925275724 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda + sha256: fb210d8d068df72a68c5fa655cf1f816792e278da3041c485478e7924d6e80eb + md5: 06ba52b8a66fd041f4910b547e3f3da1 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + size: 206698 + timestamp: 1781541197750 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h5499902_0.conda sha256: 5a1e635a371449a750b776cab64ad83f5218b58b3f137ebd33ad3ec17f1ce92e md5: e94ca7aec8544f700d45b24aff2dd4d7 depends: - __osx >=11.0 - - libcxx >=16 + - libcxx >=16 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.9.3,<1.10.0a0 + - snappy >=1.2.0,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - blosc >=1.21.6,<2.0a0 + size: 33201 + timestamp: 1719266149627 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + sha256: c3fe902114b9a3ac837e1a32408cc2142c147ec054c1038d37aec6814343f48a + md5: 925acfb50a750aa178f7a0aced77f351 + depends: + - __osx >=11.0 + - libcxx >=18 - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.0,<1.3.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - blosc >=1.21.6,<2.0a0 - size: 33201 - timestamp: 1719266149627 + size: 33602 + timestamp: 1733513285902 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda sha256: 8aa8ee52b95fdc3ef09d476cbfa30df722809b16e6dca4a4f80e581012035b7b md5: ce8659623cea44cc812bc0bfae4041c5 @@ -9771,6 +15356,23 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 20003 timestamp: 1756599758165 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + sha256: 422ac5c91f8ef07017c594d9135b7ae068157393d2a119b1908c7e350938579d + md5: 48ece20aa479be6ac9a284772827d00c + depends: + - __osx >=11.0 + - brotli-bin 1.2.0 hc919400_1 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 20237 + timestamp: 1764018058424 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda sha256: e57d402b02c9287b7c02d9947d7b7b55a4f7d73341c210c233f6b388d4641e08 md5: ab57f389f304c4d2eb86d8ae46d219c3 @@ -9783,6 +15385,18 @@ packages: run_exports: {} size: 17373 timestamp: 1756599741779 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + sha256: e2d142052a83ff2e8eab3fe68b9079cad80d109696dc063a3f92275802341640 + md5: 377d015c103ad7f3371be1777f8b584c + depends: + - __osx >=11.0 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + run_exports: {} + size: 18628 + timestamp: 1764018033635 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312h6b01ec3_4.conda sha256: e45f24660a89c734c3d54f185ecdc359e52a5604d7e0b371e35dce042fa3cf3a md5: 0d50ab05d6d8fa7a38213c809637ba6d @@ -9812,8 +15426,21 @@ packages: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT + run_exports: {} size: 359854 timestamp: 1764018178608 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 + md5: b50612e7d190b8061ab4e7dc119cf4d5 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 124965 + timestamp: 1785906749812 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df md5: 620b85a3f45526a8bc4d23fd78fc22f0 @@ -9838,6 +15465,20 @@ packages: - c-ares >=1.34.6,<2.0a0 size: 180327 timestamp: 1765215064054 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda + sha256: 104b41473845649101ba8ebf8221c7431256465d34ce380b10e9a90558ed33ac + md5: 7d9390a4d4b43f91652823d870f68065 + depends: + - __osx >=11.0 + constrains: + - c-ares-static <0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 197274 + timestamp: 1786116660078 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h6a3b0d2_0.conda sha256: 00439d69bdd94eaf51656fdf479e0c853278439d22ae151cabf40eb17399d95f md5: 38f6df8bc8c668417b904369a01ba2e2 @@ -9876,6 +15517,9 @@ packages: - libzlib >=1.3.1,<2.0a0 - pixman >=0.46.4,<1.0a0 license: LGPL-2.1-only or MPL-1.1 + run_exports: + weak: + - cairo >=1.18.4,<2.0a0 size: 900035 timestamp: 1766416416791 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda @@ -9907,6 +15551,19 @@ packages: license_family: MIT size: 292983 timestamp: 1761203354051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314hd58f0ed_1.conda + sha256: 438a5384d308f26ccd6f5f4f2b2153dfb240db4320d6fc8240036eb338a5355c + md5: 6a60fcbc6a6a079d4f47ffce008121e2 + depends: + - __osx >=11.0 + - libffi >=3.7.0,<3.8.0a0 + - pycparser + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + run_exports: {} + size: 297356 + timestamp: 1786528517794 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.4.1-h793ed5c_0.conda sha256: cad6c9f86f98f1ac980e8229ef76a9bb8f62d167a52d29770e0548c7f9a80eb1 md5: c2a9a79b58d2de021ad9295f53e1f40a @@ -9946,6 +15603,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cbc >=2.10.13,<2.11.0a0 size: 799849 timestamp: 1778586417864 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cgl-0.60.10-h034796e_1.conda @@ -9969,6 +15629,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cgl >=0.60,<0.61.0a0 size: 440213 timestamp: 1778571470907 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda @@ -9991,6 +15654,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-clp >=1.17,<1.18.0a0 size: 914273 timestamp: 1778520263707 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda @@ -10012,6 +15678,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-osi >=0.108,<0.109.0a0 size: 326662 timestamp: 1778511593409 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda @@ -10030,6 +15699,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-utils >=2.11,<2.12.0a0 size: 553075 timestamp: 1778500827484 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.5.3-py314h4dc9dd8_0.conda @@ -10070,6 +15742,44 @@ packages: license_family: BSD size: 1359398 timestamp: 1781681460540 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.0-py314hcec6336_0.conda + sha256: fe9412de3a6321fe69ab14f0660282e42fb300f524766cf258eb31f4142ab543 + md5: 538348170fcc3620c4f7f347dc288557 + depends: + - archspec >=0.2.3 + - boltons >=23.0.0 + - charset-normalizer + - conda-package-handling >=2.2.0 + - distro >=1.5.0 + - frozendict >=2.4.2 + - jsonpatch >=1.32 + - menuinst >=2 + - packaging >=23.0 + - platformdirs >=3.10.0 + - pluggy >=1.6.0 + - pycosat >=0.6.3 + - python + - requests >=2.28.0,<3 + - ruamel.yaml >=0.11.14,<0.19 + - setuptools >=60.0.0 + - tqdm >=4 + - truststore >=0.8.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - conda-build >=25.9 + - conda-content-trust >=0.3.0 + - conda-env >=2.6 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1468859 + timestamp: 1785769918540 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.10.1-py314hcec6336_2.conda sha256: 48c5bba381b7a9b6db4b70f39962fdeb12a53a0935d70d513437cb589b4a473d md5: 652a02b8973cd22717c108ffa6873744 @@ -10090,6 +15800,28 @@ packages: license_family: MIT size: 322322 timestamp: 1781646689091 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda + sha256: 4da4678a03488c985aed27956dd1a35281b515240ff33a3fe9238f3756fa6059 + md5: a123df8bb2f1dc4f85ba6bbddb744968 + depends: + - python + - pip >=23.0.1 + - packaging + - unearth + - python-build + - python-installer >=1.0 + - platformdirs + - conda-index >=0.11.0 + - conda-package-streaming >=0.11 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 + license: MIT + license_family: MIT + run_exports: {} + size: 328032 + timestamp: 1784203150047 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c md5: f9cce0bc86b46533489a994a47d3c7d2 @@ -10105,6 +15837,31 @@ packages: run_exports: {} size: 286084 timestamp: 1769156157865 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda + sha256: 754ab72f1c1ae99ef7c57995f59224dc9632cbd6731fe7e6277437fd01d43156 + md5: cddc851000ce131d757678c2f329eaad + depends: + - numpy >=1.25 + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - libcxx >=19 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 290405 + timestamp: 1769156069514 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda + sha256: 83819551a243ac95a96867977a0c7e2661d9e06ca268026c93d2c9f93969424f + md5: 93748a43e1a27821795af6701b7c06be + depends: + - libcxx >=19 + - __osx >=11.0 + license: CC0-1.0 + run_exports: {} + size: 25909 + timestamp: 1785920661291 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h4f10f1e_0.conda sha256: a7380056125a29ddc4c4efc4e39670bc8002609c70f143d92df801b42e0b486f md5: 5cb4f9b93055faf7b6ae76da6123f927 @@ -10130,6 +15887,39 @@ packages: run_exports: {} size: 1638054 timestamp: 1763019931024 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda + sha256: 5fdd6299bc379ccd89cd7732d8e4ae5cebd8e05babe6d7fe4660e16511091634 + md5: da101197ce8bfbdad65fc7bed572ab38 + depends: + - python + - __osx >=11.0 + - python 3.14.* *_cp314 + - libcxx >=19 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 1639272 + timestamp: 1763019908165 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda + sha256: ec606fd8e789f6a13c3d7c8c14b3ce616191ebd10a7d216d7149e8840aadfbb9 + md5: b6c94f8b09905110a958584e2436e497 + depends: + - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libcurl 8.21.0 hf618e03_4 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + run_exports: {} + size: 181023 + timestamp: 1785500142429 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.9.1-hbf5303f_0.conda sha256: 8dd4d08ca377f11a60fbf33d8350179d83dbf8bcbb3bb01b0c95779fd459419c md5: 93440b8d934e90496e1939ecd72cbf0c @@ -10176,6 +15966,20 @@ packages: run_exports: {} size: 591797 timestamp: 1771856474133 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda + sha256: 8deb1f8ef80525cccc68c73cdcd690cb8614035f6e1d0eb6880c0c9b377d29ac + md5: aae872d4d6847677924de8e023e00457 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + - toolz >=0.10.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 615096 + timestamp: 1771856205834 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda sha256: fd6df772cdb30d01fa0d405ed637f420a9f2194fe931f967e8c67d95b504f8b4 md5: da29307f59fb7a63f686ec20724fecf9 @@ -10187,6 +15991,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 2776045 timestamp: 1780390212997 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda @@ -10196,6 +16001,9 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + run_exports: + weak: + - epoxy >=1.5.10,<1.6.0a0 size: 296347 timestamp: 1758743805063 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py312hf57c059_0.conda @@ -10216,6 +16024,24 @@ packages: run_exports: {} size: 524384 timestamp: 1778865060800 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda + sha256: 91992021fe8f3416314198388729ef4cc9d364503ce0cfbd934f14a00ff75896 + md5: 9bcd9bf5df8950f1f661bdd6c9299c8e + depends: + - __osx >=11.0 + - cramjam >=2.3 + - fsspec + - numpy >=1.23,<3 + - packaging + - pandas >=1.5.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 535738 + timestamp: 1778865753466 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py312hf3c922e_1.conda sha256: 2dd68c3881e0dd33c2c8c2e09f7af92ed8ce60ea863c1910dc2524bb7d4c4dbc md5: 268287f41c9d4ba5b426d5206986fbca @@ -10239,6 +16065,27 @@ packages: run_exports: {} size: 1025443 timestamp: 1726664909376 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda + sha256: ba8da8eb94e2762116a6c8877814826d638f046210f23b9da1ef444f9a55046a + md5: d30d4527aacdf33b1e80ef858f33608c + depends: + - __osx >=11.0 + - attrs >=19.2.0 + - click >=8.0,<9.dev0 + - click-plugins >=1.0 + - cligj >=0.5 + - libcxx >=19 + - libgdal-core >=3.12.1,<3.13.0a0 + - pyparsing + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + - shapely + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1059210 + timestamp: 1767051558277 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-11.0.2-h440487c_1.conda sha256: afc3d556845e42112953279d3703c603c00bf6e139658778ea6b904c4fc1f50a md5: f90a1a21cef4c1e11f421ba29538d704 @@ -10262,6 +16109,19 @@ packages: license_family: MIT size: 180970 timestamp: 1767681372955 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + sha256: ca42427b99ff92228e907bed5f10a1df868180997571d29c230faad2c38e35fd + md5: 891b1202d7b835f215e26a4db685ec7c + depends: + - __osx >=11.0 + - libcxx >=19 + license: MIT + license_family: MIT + run_exports: + weak: + - fmt >=12.1.0,<12.2.0a0 + size: 181008 + timestamp: 1785915450316 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda sha256: 8607d8d0b32f9f6fc61ea8c06b537486b78428a04516658222fa4d1d521af765 md5: 9d928e6a62192141fb6540a3125b1345 @@ -10280,6 +16140,24 @@ packages: - fonts-conda-ecosystem size: 248677 timestamp: 1780450500773 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda + sha256: 6ec1937b910b2ac468459c2f7844b7c98238ada39ede05461dbd4dc764e519a5 + md5: d718960dea5abf5b582fe8ecf363c11d + depends: + - __osx >=11.0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libintl >=0.25.1,<1.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + size: 267950 + timestamp: 1786381516923 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda sha256: b78cc8df0d93ac0f0d59cb91cf54cc91effa6c124a78c8d2e8afc8011d9fb320 md5: 77e64a600d8426c3863942f67491f5fb @@ -10324,6 +16202,17 @@ packages: - freexl >=2.0.0,<3.0a0 size: 53378 timestamp: 1734014980768 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + sha256: 6dd18694340b84290bb1906cc1359502b974aada6a8476cfe6dec3ce0e860af8 + md5: 2bb7d7dd91116b8c85e805b0e08cc67b + depends: + - __osx >=11.0 + license: LGPL-2.1-or-later + run_exports: + weak: + - fribidi >=1.0.16,<2.0a0 + size: 60230 + timestamp: 1785912572097 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda sha256: d856dc6744ecfba78c5f7df3378f03a75c911aadac803fa2b41a583667b4b600 md5: 04bdce8d93a4ed181d1d726163c2d447 @@ -10365,6 +16254,24 @@ packages: license_family: LGPL size: 548309 timestamp: 1774986047281 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.7-h4e57454_0.conda + sha256: 69bb2e62a93f6407c9e9ccd4d21fe4d4e5373d64eecd6c5df318144ca4c80953 + md5: f717a22e13a1499c9552ffff02d22d64 + depends: + - __osx >=11.0 + - libglib >=2.88.2,<3.0a0 + - libintl >=0.25.1,<1.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + run_exports: + weak: + - gdk-pixbuf >=2.44.7,<3.0a0 + size: 553902 + timestamp: 1782591436963 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.12.2-h00cdb27_1.conda sha256: 1090c9b8e9a628eb2f9718d19e3cc6372c39cf763295c446a3da9288d0dbba11 md5: e07a7fa032741a056a72f46b43064ea2 @@ -10377,6 +16284,18 @@ packages: - geos >=3.12.2,<3.12.3.0a0 size: 1400339 timestamp: 1721747764174 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + sha256: 1ac5f5a3a35f2e4778025043c87993208d336e30539406e380e0952bb7ffd188 + md5: 4238412c29eff0bb2bb5c60a720c035a + depends: + - __osx >=11.0 + - libcxx >=19 + license: LGPL-2.1-only + run_exports: + weak: + - geos >=3.14.1,<3.14.2.0a0 + size: 1530844 + timestamp: 1761594597236 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.3-h7e5fb84_2.conda sha256: 2cd4db594cb9580c92ec40063a0160237d31fa633abf8b4c81730a0f2626ba7b md5: 5a9ebf4312dffc9b44f41af5d2a2332e @@ -10408,6 +16327,19 @@ packages: - gflags >=2.2.2,<2.3.0a0 size: 82090 timestamp: 1726600145480 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + sha256: 3028f8f857d438444d3fb56c9c74d46699cb04e676c6575747cf3ad8d9a308b1 + md5: 96aea99abfaf8d1e7731c03e3f70fb02 + depends: + - libcxx >=19 + - __osx >=11.0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - gflags >=2.3.1,<2.4.0a0 + size: 93701 + timestamp: 1785044718935 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda sha256: 843b3f364ff844137e37d5c0a181f11f6d51adcedd216f019d074e5aa5d7e09c md5: 95fa1486c77505330c20f7202492b913 @@ -10418,6 +16350,18 @@ packages: - giflib >=5.2.2,<5.3.0a0 size: 71613 timestamp: 1712692611426 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-hd20048c_1.conda + sha256: be9b4cf253c7d3bf20ccc9ff278ea7ec6f144ebbe8fdc9a131d18c35ff49692f + md5: 93963a54079e27fdb1bf8d289ad592d4 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - giflib >=5.2.2,<5.3.0a0 + size: 73137 + timestamp: 1784694527697 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.1-h37541a8_2.conda sha256: 414bdf86a8096d5706293d163359def2e61b8ffd3fe106bbf2028d79e58e6a97 md5: 8d4580a91948a6c3383a7c2fbfe5311c @@ -10429,6 +16373,32 @@ packages: license: LGPL-2.1-or-later size: 204902 timestamp: 1778508895255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-hd03a632_1.conda + sha256: 200aec53216f3323b2072fb994b10fbee018ff3b894162a37222f1f316c3e476 + md5: 842c53118ef3b15433627ee2c384c203 + depends: + - libglib ==2.88.3 ha531971_1 + - libffi + - __osx >=11.0 + - libintl >=0.25.1,<1.0a0 + license: LGPL-2.1-or-later + run_exports: {} + size: 204965 + timestamp: 1786457780347 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + sha256: 1ac5991fd354de81baf3892fee0ad09438623698fcad73758521dfe90711ed44 + md5: 68937f90f7930d49e106b94e95d4536c + depends: + - __osx >=11.0 + - gflags >=2.3.0,<2.4.0a0 + - libcxx >=19 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - glog >=0.7.1,<0.8.0a0 + size: 112670 + timestamp: 1784094909098 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 @@ -10448,8 +16418,22 @@ packages: md5: 55beafb01e44f9527026dbffcea260ee license: BSD-3-Clause license_family: BSD + run_exports: {} size: 1829675 timestamp: 1775514584688 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + sha256: 471f34a187fdb4f2df33e26f2e471b16c239a5b277903f643d9c3a8c9a9f44ec + md5: 0c7b78d9ffff4f5a6ca28d346f734d8f + depends: + - libcxx >=19 + - __osx >=11.0 + license: LGPL-2.0-or-later + license_family: LGPL + run_exports: + weak: + - graphite2 >=1.3.15,<2.0a0 + size: 86493 + timestamp: 1786118637573 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-hf6b4638_0.conda sha256: c0a060d7b7a05669043ef3f68c7a1025c8594e1ab73735afb64c35e8baa41da5 md5: 0d576cff278a2e60456d5b2c0a1ffda3 @@ -10481,6 +16465,9 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other + run_exports: + weak: + - graphviz >=14.1.2,<15.0a0 size: 2218284 timestamp: 1769427599940 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.2-py314he609de1_0.conda @@ -10496,6 +16483,20 @@ packages: license_family: MIT size: 263816 timestamp: 1781762277927 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda + sha256: d960aefe961553b75450b7584f2fad7693ed1fac423da0fc5a9d649c48d2a019 + md5: ae860c6208235503f82ef3019461d410 + depends: + - python + - __osx >=11.0 + - libcxx >=19 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 274201 + timestamp: 1786384265137 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda sha256: 26862a9898054b8552e55e609e5ce73c7ef1eb28bbe6fb87f0b9109d73cd09df md5: 5557a2433b1339b8e536c264afea41ef @@ -10519,6 +16520,10 @@ packages: - pango >=1.56.4,<2.0a0 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - gtk3 >=3.24.52,<4.0a0 + - adwaita-icon-theme size: 9385734 timestamp: 1774288504338 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda @@ -10529,6 +16534,9 @@ packages: - libglib >=2.76.3,<3.0a0 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - gts >=0.7.6,<0.8.0a0 size: 304331 timestamp: 1686545503242 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.2.1-h3103d1b_0.conda @@ -10549,6 +16557,18 @@ packages: license_family: MIT size: 1721040 timestamp: 1780451752518 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.3.0-hce30654_0.conda + sha256: 67042b0e8896f707c60981947ae22fe0c13942283c8d7ac9b8286cc01cc1c2d2 + md5: aab13bb027c8295f5b09ccb8cd084698 + depends: + - libharfbuzz-devel 14.3.0 h5a65909_0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 11005 + timestamp: 1785770060418 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda sha256: c3b01e3c3fe4ca1c4d28c287eaa5168a4f2fd3ffd76690082ac919244c22fa90 md5: ff5d749fd711dc7759e127db38005924 @@ -10588,6 +16608,7 @@ packages: md5: cfb39109ac5fa8601eb595d66d5bf156 license: GPL-2.0-or-later license_family: GPL + run_exports: {} size: 17616 timestamp: 1771539622983 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda @@ -10611,6 +16632,17 @@ packages: license_family: MIT size: 12361647 timestamp: 1773822915649 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + sha256: 6cdb5dee54c72e56ab189fb3ad33cb28533553d42590e7e831160248f4416a43 + md5: a5efc0b42bb8b42e97d0a29ae3e3c187 + depends: + - __osx >=11.0 + license: MIT + run_exports: + weak: + - icu >=78.3,<79.0a0 + size: 14070242 + timestamp: 1786545847761 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda sha256: 8fc009559f6fdce0bfc053898d62a48bdfcca1d4960f785922019e6999aca62f md5: 0749dc3555b919277586d96a8e3240f7 @@ -10621,6 +16653,7 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + run_exports: {} size: 52567 timestamp: 1757685575891 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.17-he54c16a_1.conda @@ -10635,6 +16668,18 @@ packages: - json-c >=0.17,<0.18.0a0 size: 74409 timestamp: 1720813255190 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda + sha256: 73179a1cd0b45c09d4f631cb359d9e755e6e573c5d908df42006728e0bf8297c + md5: 94f14ef6157687c30feb44e1abecd577 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - json-c >=0.18,<0.19.0a0 + size: 73715 + timestamp: 1726487214495 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kealib-1.5.3-h8edbb62_2.conda sha256: 29fef9ff99514a34d8026da4be5289bc4d2526974df459b63e92445fca7fd55e md5: d5c581103f5433dd862acbf24facdf9b @@ -10663,6 +16708,20 @@ packages: run_exports: {} size: 68490 timestamp: 1773067215781 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py314hf8a3a22_0.conda + sha256: 840de1b0ba2fa646475bc53ba0f723c8a13e66139633a070831b8279deaa7c64 + md5: eb1465d8a644ef290d18fb86af6e9bc4 + depends: + - python + - python 3.14.* *_cp314 + - libcxx >=19 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 69284 + timestamp: 1773067285911 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b md5: c6dc8a0fdec13a0565936655c33069a1 @@ -10722,6 +16781,19 @@ packages: - lerc >=4.1.0,<5.0a0 size: 164222 timestamp: 1773114244984 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + sha256: c97aa17d16d2ac332ba9f184e82ce8f72dcb10e9a10c5f299030be2d44e191b9 + md5: e429aec4037d5cb8fd34ded9f5dadd39 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - lerc >=4.2.0,<5.0a0 + size: 166477 + timestamp: 1785036480092 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda sha256: a9517c8683924f4b3b9380cdaa50fdd2009cd8d5f3918c92f64394238189d3cb md5: f16963d88aed907af8b90878b8d8a05c @@ -10739,6 +16811,23 @@ packages: - libabseil =*=cxx17* size: 1136123 timestamp: 1720857649214 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h2062a1b_1.conda + sha256: 450026eb01a52acd0ff122e331ec9b8546c93790143214b73e1c14bc2b075b22 + md5: 8adfdc0215e979a0ce31be676883e0b3 + depends: + - __osx >=11.0 + - libcxx >=19 + constrains: + - libabseil-static =20260526.0=cxx17* + - abseil-cpp =20260526.0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 1273408 + timestamp: 1780524599788 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 @@ -10792,6 +16881,28 @@ packages: license_family: BSD size: 796153 timestamp: 1782289667690 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h6fbacd7_100.conda + sha256: b58cfffd0774142d308a25f1bdc610c46d71cb67489f1b1eec0891e78ede5b11 + md5: 4963589c8bed67dd0540d890ba690f53 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - lzo >=2.10,<3.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + run_exports: + weak: + - libarchive >=3.8.9,<3.9.0a0 + size: 800281 + timestamp: 1785251408018 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-17.0.0-h20538ec_13_cpu.conda build_number: 13 sha256: fb77709a184e934b8662388f91bf9bd51a96eb3b11c53d0453e9bc43b01b4c27 @@ -10833,6 +16944,45 @@ packages: - libarrow >=17.0.0,<17.1.0a0 size: 5253411 timestamp: 1725214512114 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + build_number: 4 + sha256: 3c289bab2a8c4be0ef86363c97c5532dbcc289708b8523d6a3cfef35a2dd0b9b + md5: 6c06263450ab8f5f4580effead8b2ca4 + depends: + - __osx >=12.0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=3.8.0,<3.9.0a0 + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.1,<2.3.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow >=25.0.0,<25.1.0a0 + size: 4294348 + timestamp: 1786315141730 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-17.0.0-hf9b8971_13_cpu.conda build_number: 13 sha256: ea958d1947670dc913f1a0ee631c70d8ac8d6db5f08039002b233bf896815cb2 @@ -10848,6 +16998,48 @@ packages: - libarrow-acero >=17.0.0,<17.1.0a0 size: 477391 timestamp: 1725214612356 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + build_number: 4 + sha256: 154feae16991d0e50d4b4d45291904cfbf1c8c97955237f71a84975b363cdd04 + md5: 5810871245167355126d623aa5ae1096 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-compute 25.0.0 h93a2d87_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-acero >=25.0.0,<25.1.0a0 + size: 519885 + timestamp: 1786315467845 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + build_number: 4 + sha256: 5b725a3048bd53e9e2cee2577a26724467b35a956cc1207f5bde0321d128dbaf + md5: 1f47858e8a29295f723b63cd318a31c4 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-compute >=25.0.0,<25.1.0a0 + size: 2228808 + timestamp: 1786315247988 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-17.0.0-hf9b8971_13_cpu.conda build_number: 13 sha256: 3dac99549e4f9307bb4d35e94003f6e7c9052a957de122ec78c5d9750fd46096 @@ -10865,6 +17057,28 @@ packages: - libarrow-dataset >=17.0.0,<17.1.0a0 size: 482335 timestamp: 1725215502662 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + build_number: 4 + sha256: 580382aae415669e2ff4af670db9381462d5d966ecde7d35f041356344b77dd1 + md5: 5d2b0865a12a8a380c4ad11eae754f30 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-acero 25.0.0 heaff1e6_4_cpu + - libarrow-compute 25.0.0 h93a2d87_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libparquet 25.0.0 h0de02aa_4_cpu + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-dataset >=25.0.0,<25.1.0a0 + size: 519748 + timestamp: 1786315616734 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-17.0.0-hbf8b706_13_cpu.conda build_number: 13 sha256: 5fd22a610e14669f0f392aaf7b61511d9a7a5f99a23a3ce4bdf5b2880ddbd244 @@ -10885,6 +17099,26 @@ packages: - libarrow-substrait >=17.0.0,<17.1.0a0 size: 466501 timestamp: 1725215667169 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda + build_number: 4 + sha256: 4f5d1e1e1ecead58d80472e2965143a4d4daf37d401a527334116caf5b5cff9b + md5: e8627e23ad2343bdd0765b49ad7795df + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-acero 25.0.0 heaff1e6_4_cpu + - libarrow-dataset 25.0.0 heaff1e6_4_cpu + - libcxx >=21 + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-substrait >=25.0.0,<25.1.0a0 + size: 455505 + timestamp: 1786315676110 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda build_number: 8 sha256: 8f5ec18ead0619a9cf0f38b49796c22f6fc0f44850c0df2baea0f5277db16e75 @@ -10905,6 +17139,26 @@ packages: - libblas >=3.11.0,<4.0a0 size: 18949 timestamp: 1779859141315 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda + build_number: 9 + sha256: 0437866fe43b4c911470d3e7ddea18d78390bd7062d9563ce6d38c8ba5798405 + md5: cb1f85be9d88af453fcda3dfba995099 + depends: + - libopenblas >=0.3.34,<0.3.35.0a0 + - libopenblas >=0.3.34,<1.0a0 + constrains: + - blas 2.309 openblas + - libcblas 3.11.0 9*_openblas + - liblapack 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + - mkl <2027 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libblas >=3.11.0,<4.0a0 + size: 18162 + timestamp: 1786058887392 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda sha256: 023b609ecc35bfee7935d65fcc5aba1a3ba6807cbba144a0730198c0914f7c79 md5: 231cffe69d41716afe4525c5c1cc5ddd @@ -10917,6 +17171,18 @@ packages: - libbrotlicommon >=1.1.0,<1.2.0a0 size: 68938 timestamp: 1756599687687 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + sha256: a7cb9e660531cf6fbd4148cff608c85738d0b76f0975c5fc3e7d5e92840b7229 + md5: 006e7ddd8a110771134fcc4e1e3a6ffa + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + size: 79443 + timestamp: 1764017945924 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda sha256: 7f1cf83a00a494185fc087b00c355674a0f12e924b1b500d2c20519e98fdc064 md5: cb7e7fe96c9eee23a464afd57648d2cd @@ -10930,6 +17196,19 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 29015 timestamp: 1756599708339 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + sha256: 2eae444039826db0454b19b52a3390f63bfe24f6b3e63089778dd5a5bf48b6bf + md5: 079e88933963f3f149054eec2c487bc2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 29452 + timestamp: 1764017979099 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda sha256: a2f2c1c2369360147c46f48124a3a17f5122e78543275ff9788dc91a1d5819dc md5: 4ce5651ae5cd6eebc5899f9bfe0eac3c @@ -10943,6 +17222,19 @@ packages: - libbrotlienc >=1.1.0,<1.2.0a0 size: 275791 timestamp: 1756599724058 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + sha256: 01436c32bb41f9cb4bcf07dda647ce4e5deb8307abfc3abdc8da5317db8189d1 + md5: b2b7c8288ca1a2d71ff97a8e6a1e8883 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlienc >=1.2.0,<1.3.0a0 + size: 290754 + timestamp: 1764018009077 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda build_number: 8 sha256: f93efcd44bc24f97c2478c7474d3baa6801a057974f330e1d06bedc33e4c778f @@ -10960,6 +17252,23 @@ packages: - libcblas >=3.11.0,<4.0a0 size: 18911 timestamp: 1779859147634 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda + build_number: 9 + sha256: c4c71f20fdb20c86bf6f61c8c31bb349e4055bb4d19928e8580bb5615039cb4b + md5: a7b6ba94e3ca58bc7d4e1ae4ff95c215 + depends: + - libblas 3.11.0 9_h51639a9_openblas + constrains: + - blas 2.309 openblas + - liblapack 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libcblas >=3.11.0,<4.0a0 + size: 18110 + timestamp: 1786058893756 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 md5: 32bd82a6a625ea6ce090a81c3d34edeb @@ -10987,6 +17296,25 @@ packages: license_family: MIT size: 410874 timestamp: 1782297872451 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda + sha256: d25be36712d7f854a5f93513b9fbf1b0ee3219975b7fef4a3ee071b021b10167 + md5: 66cf9c5003ee81ecdb0dc9f9df17bbe5 + depends: + - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + run_exports: + weak: + - libcurl >=8.21.0,<9.0a0 + size: 411491 + timestamp: 1785500129743 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.9.1-hfd8ffcc_0.conda sha256: 4d6006c866844a39fb835436a48407f54f2310111a6f1d3e89efb16cf5c4d81b md5: be0f46c6362775504d8894bd788a45b2 @@ -11026,6 +17354,18 @@ packages: - libdeflate >=1.25,<1.26.0a0 size: 55420 timestamp: 1761980066242 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + sha256: d896f4aa4ce4c590c2838678cb1917356fdb461d2a189991c0280c818c362172 + md5: 78650d671cb56909bb3e5c13bce310f9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - libdeflate >=1.25,<1.26.0a0 + size: 55727 + timestamp: 1785909153744 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 md5: 44083d2d2c2025afca315c7a172eab2b @@ -11040,6 +17380,18 @@ packages: - libedit >=3.1.20250104,<3.2.0a0 size: 107691 timestamp: 1738479560845 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda + sha256: c0100064506ae8abb432c5a506d474f10af2cf48c33d62bc221fb28b6d6ff6ac + md5: 19e86c8a6a47e92bb2e70ca12e758c5c + depends: + - __osx >=11.0 + license: BSD-2-Clause OR GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - libev >=4.33,<4.34.0a0 + size: 39991 + timestamp: 1785917376956 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f md5: 36d33e440c31857372a72137f78bacf5 @@ -11086,6 +17438,18 @@ packages: - libffi >=3.5.2,<3.6.0a0 size: 40979 timestamp: 1769456747661 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda + sha256: 2c6ac9a6cd65af89b2bd448518bb1e13b44a2e48c0d469398e37bcfc0092e832 + md5: 92e8690d170d46d768c32553458c0105 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 43734 + timestamp: 1783521647536 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda sha256: d5637b01941c0fc8f5cbb1f170c238f4ee153b3c1708b9d50f4f1305438ff051 md5: 0582e67cd14cfed773be2f3b1aba08e0 @@ -11121,6 +17485,19 @@ packages: run_exports: {} size: 404080 timestamp: 1778273064154 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda + sha256: fdd1502babb50b802d090496586231f56236d7a6fc042a4e1ac2dee48da8366b + md5: 0124dc2e6f70f3e8ebea643b42b18abb + depends: + - _openmp_mutex + constrains: + - libgomp 16.1.0 1 + - libgcc-ng ==16.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 364162 + timestamp: 1785374452947 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda sha256: 269edce527e204a80d3d05673301e0207efcd0dbeebc036a118ceb52690d6341 md5: fa4a92cfaae9570d89700a292a9ca714 @@ -11140,6 +17517,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD + run_exports: + weak: + - libgd >=2.3.3,<2.4.0a0 size: 159247 timestamp: 1766331953491 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-3.9.2-hce30654_7.conda @@ -11167,6 +17547,49 @@ packages: - libgdal >=3.9.2,<3.10.0a0 size: 422965 timestamp: 1728296452637 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-h21e278e_4.conda + sha256: 83b437c6c6b3b5ac9d5570d7121f0ef7dc832446879ba6ad17cdfb5e02eefb41 + md5: c73f4453e35b1ca58ffcb9550821d15a + depends: + - libcxx >=19 + - __osx >=11.0 + - libdeflate >=1.25,<1.26.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - liblzma >=5.8.3,<6.0a0 + - openssl >=3.5.7,<4.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - json-c >=0.18,<0.19.0a0 + - libiconv >=1.18,<2.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - blosc >=1.21.6,<2.0a0 + - giflib >=5.2.2,<5.3.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - muparser >=2.3.5,<2.4.0a0 + - libcurl >=8.21.0,<9.0a0 + - proj >=9.8.1,<9.9.0a0 + - libexpat >=2.8.1,<3.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - lerc >=4.2.0,<5.0a0 + - libpng >=1.6.58,<1.7.0a0 + - geos >=3.14.1,<3.14.2.0a0 + constrains: + - libgdal 3.12.4.* + license: MIT + license_family: MIT + run_exports: + weak: + - libgdal-core >=3.12.4,<3.13.0a0 + size: 10819037 + timestamp: 1786090987756 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.9.2-hcf5b7dd_1.conda sha256: 3938fe06332eb7b79cdd452a344fc352b2528793156d8fcdd763f3a3ac99aa93 md5: 98dc71fff9381ffed515c519443b4111 @@ -11398,6 +17821,18 @@ packages: run_exports: {} size: 139675 timestamp: 1778273280875 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda + sha256: d7c6dd601dbbde495ab213a76d690b2fec19455d26c595ec0257cfde3e80166b + md5: d6f10dbb9c5830f540904c91ea5b252a + depends: + - libgfortran5 16.1.0 h32cdfcc_1 + constrains: + - libgfortran-ng ==16.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 98528 + timestamp: 1785374566402 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda sha256: d0a68b7a121d115b80c169e24d1265dcc25a3fe58d107df1bbc430797e226d88 md5: ba36d8c606a6a53fe0b8c12d47267b3d @@ -11410,6 +17845,18 @@ packages: run_exports: {} size: 599691 timestamp: 1778273075448 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda + sha256: 3e8cb79a421e0c350566717febdba9079574cbbe204591b4fd4b4081ea89cb92 + md5: c1c10ea48f95054aa9b93c2315a0a74a + depends: + - libgcc >=16.1.0 + constrains: + - libgfortran 16.1.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 556657 + timestamp: 1785374459225 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.0-hdff4504_0.conda sha256: 70a414faef075e11e7a51861e9e9c953d8373b0089070f98136a7578d8cda67e md5: 86bdf23c648be3498294c4ab861e7090 @@ -11443,6 +17890,24 @@ packages: license: LGPL-2.1-or-later size: 4439458 timestamp: 1778508895255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda + sha256: c9f7273bbe06d1693ff3a2b6f8b49b74e2ef6cbac2cead7aae767f2f5191b7aa + md5: 70a6bcf13dc0dd00fe3fe91190d38771 + depends: + - __osx >=11.0 + - pcre2 >=10.47,<10.48.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libintl >=0.25.1,<1.0a0 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + run_exports: + weak: + - libglib >=2.88.3,<3.0a0 + size: 4447961 + timestamp: 1786457780347 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.28.0-hfe08963_0.conda sha256: 8ac585e360937aaf9f323e7414c710bf00eec6cf742c15b521fd502e6e3abf2b md5: 68fb9b247b79e8ac3be37c2923a0cf8a @@ -11464,6 +17929,28 @@ packages: - libgoogle-cloud >=2.28.0,<2.29.0a0 size: 848880 timestamp: 1723369224404 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + sha256: a48050bcf3827e0fc10de5a7d251760b8b84011f3aeea23ae18b5e0ce25aff19 + md5: 4f34ca73f16b37fae583ce16c48b5492 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.82.1,<1.83.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - openssl >=3.5.7,<4.0a0 + constrains: + - libgoogle-cloud 3.8.0 *_0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud >=3.8.0,<3.9.0a0 + size: 1804429 + timestamp: 1786225612587 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.28.0-h1466eeb_0.conda sha256: c62d08339e98fd56d65390df1184d8c2929de2713d431a910c3bb59750daccac md5: 16874ac519f64d2199fab04fd9bd821d @@ -11480,9 +17967,28 @@ packages: license_family: Apache run_exports: weak: - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - size: 522700 - timestamp: 1723370053755 + - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 + size: 522700 + timestamp: 1723370053755 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + sha256: 13251d6a72028a13a9d142815b23818a56f6f88a67f9dd5f19a881eb2fcf5636 + md5: 36ba29b4cf02ed97ce269939b750961a + depends: + - __osx >=12.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 3.8.0 ha595bda_0 + - libzlib >=1.3.2,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + size: 541527 + timestamp: 1786225749476 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda sha256: d2c5b5a828f6f1242c11e8c91968f48f64446f7dd5cbfa1197545e465eb7d47a md5: e624fc11026dbb84c549435eccd08623 @@ -11505,6 +18011,83 @@ packages: - libgrpc >=1.62.2,<1.63.0a0 size: 5016525 timestamp: 1713392846329 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda + sha256: b9d0f510488074e889ca3cecd2b7490e8e830f7a60e2e91809a90355bf4d1a57 + md5: 5f7f645a15eedc24da7f271dc2086bcf + depends: + - __osx >=12.0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=19 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.82.1 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libgrpc >=1.82.1,<1.83.0a0 + size: 4898235 + timestamp: 1783587327477 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda + sha256: 5803da482e914fc5d7ea82b31789471973b03ca58e9caffedd86e175c3aee447 + md5: 19248fa96b4c573e46bfec7fa3c875fa + depends: + - __osx >=11.0 + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=19 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 942277 + timestamp: 1785770026085 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.3.0-h5a65909_0.conda + sha256: ec6822ad0101e1d627aebb04792a0a642a706ba3c12e2691a90c7c3995b09fc1 + md5: 9b9a765bb25ac591d79ced348d4dc340 + depends: + - __osx >=11.0 + - cairo >=1.18.4,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=19 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz 14.3.0 h5a65909_0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 1470616 + timestamp: 1785770054858 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda + sha256: e2b0108325d360961750bd35d975ca59694f9c1efff6ec241470c68ca09cacc0 + md5: 5b102fdc40afa0b6030c7867d939c00e + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 OR BSD-3-Clause + run_exports: + weak: + - libhwy >=1.4.0,<1.5.0a0 + size: 610044 + timestamp: 1784325884330 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03 md5: 4d5a7445f0b25b6a3ddbb56e790f5251 @@ -11541,6 +18124,35 @@ packages: - libjpeg-turbo >=3.1.4.1,<4.0a0 size: 555681 timestamp: 1775962975624 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + sha256: 05006418f9392c9b723e8428808db106de0350a497832f95f921b85ff1072310 + md5: b2f8c8e5a7651a1d7c05404c3a159517 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + run_exports: + weak: + - libjpeg-turbo >=3.2.0,<4.0a0 + size: 558459 + timestamp: 1785896382474 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-h934fa54_1.conda + sha256: 83b69f759845ddc12444f5a812bdf08782ab2b0156ae08b706b26777918416c3 + md5: a7040219e41397bf619b7062aaa88de1 + depends: + - __osx >=11.0 + - libcxx >=19 + - libhwy >=1.4.0,<1.5.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libjxl >=0.12.0,<0.13.0a0 + size: 1032881 + timestamp: 1783146206980 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda sha256: bfc6c0b1f30de524e3270eed2171d87e6b96552ff90cf2a5eb34c00d6bcb3dfa md5: 3d8eeedb8e541d1cb593e8204fcc397d @@ -11572,6 +18184,23 @@ packages: - liblapack >=3.11.0,<3.12.0a0 size: 18925 timestamp: 1779859153970 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda + build_number: 9 + sha256: db09e8e6a58415da1d866221cf518e53e84c6766a4500faae716cfa204f696ff + md5: ecc87ca1e25bd94a08c82904eb4e6846 + depends: + - libblas 3.11.0 9_h51639a9_openblas + constrains: + - blas 2.309 openblas + - libcblas 3.11.0 9*_openblas + - liblapacke 3.11.0 9*_openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - liblapack >=3.11.0,<3.12.0a0 + size: 18144 + timestamp: 1786058899889 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-8_h1b118fd_openblas.conda build_number: 8 sha256: 589d3218009b4002d486a7d3f88d3313e81c1e7720ddb3ee4ee2eff07fa34f9b @@ -11586,6 +18215,23 @@ packages: license_family: BSD size: 18968 timestamp: 1779859160325 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-9_h1b118fd_openblas.conda + build_number: 9 + sha256: 38b9e1b621ce246b4e0b961255e6d88aed867d8ba7e28d5aa7419ab975f23b77 + md5: 9671fc36704c91fbfe988ce9429e6c6a + depends: + - libblas 3.11.0 9_h51639a9_openblas + - libcblas 3.11.0 9_hb0561ab_openblas + - liblapack 3.11.0 9_hd9741b5_openblas + constrains: + - blas 2.309 openblas + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - liblapacke >=3.11.0,<3.12.0a0 + size: 18174 + timestamp: 1786058909286 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda sha256: 34878d87275c298f1a732c6806349125cebbf340d24c6c23727268184bba051e md5: b1fd823b5ae54fbec272cea0811bd8a9 @@ -11599,6 +18245,19 @@ packages: - liblzma >=5.8.3,<6.0a0 size: 92472 timestamp: 1775825802659 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + sha256: 23d0630046a3e8b164d8f80f2b74ed2605af2e7050ab9913018056402fae4311 + md5: 8ab10323068b107661a4b9a4af84f3b5 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.3.* + license: 0BSD + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 91720 + timestamp: 1786348695846 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.3-h8088a28_0.conda sha256: 3002be39c0e98ec6cd103b0dc2963dc9e0d7cab127fb2fe9a8de9707a76ed1f0 md5: ebe1f5418d6e2d4bbc26b2c906a0a470 @@ -11635,6 +18294,33 @@ packages: license_family: BSD size: 1819419 timestamp: 1780948977563 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda + sha256: 5291da681b85d4a12483b0f9a57bf18e191d0a52e749f18266922457b5bc200c + md5: 1646d42b756ad5a554903866fc2871a1 + depends: + - cpp-expected >=1.3.1,<1.3.2.0a0 + - libcxx >=19 + - __osx >=11.0 + - libmsgpack-c >=6.1.0,<7.0a0 + - simdjson >=4.6.6,<4.7.0a0 + - reproc >=14.2.7.post0,<14.3.0a0 + - libcurl >=8.21.0,<9.0a0 + - fmt >=12.1.0,<12.2.0a0 + - libsolv >=0.7.39,<0.8.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - openssl >=3.5.7,<4.0a0 + - nlohmann_json-abi ==3.12.0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 1846328 + timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.8.1-hb995d7d_0.conda sha256: b18cbec4a4c33ab0e47ecf5a6973c045e14c6e0e60d4c92ee7f7bafc0f0872e1 md5: 09a9c695f497c6f39a99ae6d0335837d @@ -11646,6 +18332,18 @@ packages: license_family: BSD size: 23141 timestamp: 1780948977563 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda + sha256: 7e379c5b7527d878e2617237b73d7866dbdf3d9dbaf36352ad39ab0a85802831 + md5: c0894144cb6c8d04f7699a5f5ead0c64 + depends: + - libcxx >=19 + - __osx >=11.0 + - libmamba >=2.9.0,<2.10.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 23751 + timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.8.1-py314h96ff89e_0.conda sha256: 87894c7d410ef8e96763aa0dd7774e5daeb411099da884ca29c1630a4f2ef7ab md5: b9dcc4e28de586704989de517ad499de @@ -11669,6 +18367,32 @@ packages: license_family: BSD size: 778735 timestamp: 1780948977563 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda + sha256: 785d75a82bf43c123222a3933f84a7c2d1f9d83eb5a07d2552c75725f17a6309 + md5: c57c6b1c9fe4b161997ce6a6a705faf6 + depends: + - python + - libmamba ==2.9.0 py314hf3ae1d6_0 + - libmamba-spdlog ==2.9.0 heb3ad76_0 + - __osx >=11.0 + - libcxx >=19 + - python 3.14.* *_cp314 + - zstd >=1.5.7,<1.6.0a0 + - libmamba >=2.9.0,<2.10.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - openssl >=3.5.7,<4.0a0 + - fmt >=12.1.0,<12.2.0a0 + - python_abi 3.14.* *_cp314 + - pybind11-abi ==11 + - nlohmann_json-abi ==3.12.0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 779694 + timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda sha256: 1089c7f15d5b62c622625ec6700732ece83be8b705da8c6607f4dabb0c4bd6d2 md5: 57c4be259f5e0b99a5983799a228ae55 @@ -11676,6 +18400,7 @@ packages: - __osx >=11.0 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 73690 timestamp: 1769482560514 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_6.conda @@ -11689,6 +18414,20 @@ packages: license: BSL-1.0 size: 39063 timestamp: 1770807536463 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda + sha256: 7eea97b70943a054f90405b1c17da0c8b25348c77bc815978fac05d99e574334 + md5: bfb208af98c6c65859211b972e45cafd + depends: + - libcxx >=19 + - __osx >=11.0 + constrains: + - msgpack-c ==6.1.0 *_7 + license: BSL-1.0 + run_exports: + weak: + - libmsgpack-c >=6.1.0,<7.0a0 + size: 39302 + timestamp: 1786189396450 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_he469be0_114.conda sha256: aeac591ba859f9cf775993e8b7f21e50803405d41ef363dc4981d114e8df88a8 md5: 8fd3ce6d910ed831c130c391c4364d3f @@ -11760,6 +18499,53 @@ packages: - libopenblas >=0.3.33,<1.0a0 size: 4304965 timestamp: 1776995497368 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda + sha256: bcf1967f12f1b1cc769dcc77b255fb1b27aaceb2f185450e9596d137bc6ede76 + md5: 89d28fb841cf16211318524fa985e384 + depends: + - __osx >=11.0 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + constrains: + - openblas >=0.3.34,<0.3.35.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libopenblas >=0.3.34,<1.0a0 + size: 4318474 + timestamp: 1784288246205 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + sha256: 9c1840af12f31643723a7ea7f6bde6a3a394783ced6aa65b75a9fc3ecb84f39f + md5: a6c2fc0c5ddf105b3d3e353c1383a492 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.0,<1.83.0a0 + - libopentelemetry-cpp-headers 1.27.0 hce30654_1 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.27.0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + size: 564868 + timestamp: 1783133075023 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + sha256: 01caeebb19f70563f4ab1b97fcdf06af13be8637e9776619c8840b49070b287c + md5: 7f61001d82fd50385d4f9fb57f936e95 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 394664 + timestamp: 1783133038717 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-17.0.0-hf0ba9ef_13_cpu.conda build_number: 13 sha256: fb7ee16e8f9bf60a1f136170231615c1a6f200087f12de4220e0c73f45edcdc6 @@ -11777,6 +18563,27 @@ packages: - libparquet >=17.0.0,<17.1.0a0 size: 859328 timestamp: 1725215440648 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda + build_number: 4 + sha256: 318412a665f7637598aef088a9dbc30c2a52614eebe1bde6e75be76708875271 + md5: ebf0d32466e8875bf91749744b97165a + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libparquet >=25.0.0,<25.1.0a0 + size: 1096341 + timestamp: 1786315419506 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda sha256: 66eae34546df1f098a67064970c92aa14ae7a7505091889e00468294d2882c36 md5: 2259ae0949dbe20c0665850365109b27 @@ -11820,6 +18627,52 @@ packages: - libprotobuf >=4.25.3,<4.25.4.0a0 size: 2177164 timestamp: 1727160770879 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h8daa630_2.conda + sha256: 782d30325e61249e3240979e0f76d9dbf67867b1f38424cfdb1abb3a0f2cfa95 + md5: 7f77d9a99dd9e79e1f5be50d6632f675 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=19 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 2900719 + timestamp: 1783168180812 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda + sha256: 0140cb0d059ac531e476b85a543668b69b7dffec16f0d26269ab6c9b70546921 + md5: 75a4cdf128d016141db61732ea462276 + depends: + - __osx >=11.0 + - libcxx >=19 + - icu >=78.3,<79.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libpsl >=0.23.1,<0.24.0a0 + size: 72950 + timestamp: 1786443660105 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda + sha256: b110f7f9b2cec61ea793c521950414e00cd64826e18aeb4ab40369acf05c0039 + md5: 46ea0eb4e71bffa35ab7070678bcb053 + depends: + - __osx >=11.0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libharfbuzz >=14.2.1 + - fribidi >=1.0.16,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libraqm >=0.11.0,<0.12.0a0 + size: 31333 + timestamp: 1784850348342 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda sha256: c8a0a6e7a627dc9c66ffb8858f8f6d499f67fd269b6636b25dc5169760610f05 md5: 0b7b2ced046d6b5fe6e9d46b1ee0324c @@ -11836,6 +18689,23 @@ packages: - libre2-11 >=2023.9.1,<2024.0a0 size: 171443 timestamp: 1708947163461 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + sha256: 06f49291605c379467987989ff08d44b470a23afeb690d7e078517871969bff7 + md5: b750c4f83563c7528634bdcfd28622ce + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=19 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + size: 166043 + timestamp: 1781312641918 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.3-he8aa2a2_0.conda sha256: f5b4fb7b6f13bbfca59613bff2e70b5a398e80727b9d0f814837ffcbc34185e1 md5: 6973724fadafe66ac6e4f1c55c191407 @@ -11852,6 +18722,9 @@ packages: constrains: - __osx >=11.0 license: LGPL-2.1-or-later + run_exports: + weak: + - librsvg >=2.62.3,<3.0a0 size: 2397567 timestamp: 1780452232118 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-h31fb324_16.conda @@ -11868,12 +18741,29 @@ packages: - librttopo >=1.1.0,<1.2.0a0 size: 191683 timestamp: 1720347975066 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda + sha256: 2b28c777889b1b638244f65d5bef4a8ba4624bdb740cecf26c845876653552c2 + md5: d07359797436cfc891b38e203cf0caac + depends: + - __osx >=11.0 + - geos >=3.14.1,<3.14.2.0a0 + - libcxx >=19 + license: GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - librttopo >=1.1.0,<1.2.0a0 + size: 192590 + timestamp: 1761670939075 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda sha256: 202be45db5726757a8ea1f374f85aacc18c504f5ff15b2558496dff4c8779c48 md5: 9ed5ab909c449bdcae72322e44875a18 depends: - __osx >=11.0 license: ISC + run_exports: + weak: + - libsodium >=1.0.22,<1.0.23.0a0 size: 247352 timestamp: 1779164136206 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h7d962ec_0.conda @@ -11885,8 +18775,37 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - libsolv >=0.7.39,<0.8.0a0 size: 430365 timestamp: 1780057267477 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda + sha256: 0ae7ae664900f278c89c5f7e7824f8189d03610b91b77fedc93bf6ae9d4da90c + md5: 8909f6e5df2f99065ef12a6e6c2db274 + depends: + - __osx >=11.0 + - freexl >=2 + - freexl >=2.0.0,<3.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - libcxx >=19 + - libiconv >=1.18,<2.0a0 + - librttopo >=1.1.0,<1.2.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxml2-devel + - libzlib >=1.3.1,<2.0a0 + - proj >=9.8.0,<9.9.0a0 + - sqlite + - zlib + license: MPL-1.1 + license_family: MOZILLA + run_exports: + weak: + - libspatialite >=5.1.0,<5.2.0a0 + size: 2711368 + timestamp: 1772594727365 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf7a34df_9.conda sha256: ee462007733b803a549acc1ebb2ea7b40cfd28efecdd6c17cadb48f0acd2e8ad md5: a0c631a4cf164b1688484db0da3072b5 @@ -11933,6 +18852,19 @@ packages: - libsqlite >=3.53.3,<4.0a0 size: 924912 timestamp: 1782519136322 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-h1ae2325_0.conda + sha256: 745662565e103f290e9dc4263bbd88285082f8cf699854fe2d5f1e35a4a0d326 + md5: 0e3477c0c3e718dcf2eb74ccc8f68570 + depends: + - __osx >=11.0 + - icu >=78.3,<79.0a0 + - libzlib >=1.3.2,<2.0a0 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 929203 + timestamp: 1785016131414 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a md5: b68e8f66b94b44aaa8de4583d3d4cc40 @@ -11962,6 +18894,22 @@ packages: - libthrift >=0.20.0,<0.20.1.0a0 size: 315041 timestamp: 1724657608736 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + sha256: 568bb23db02b050c3903bec05edbcab84960c8c7e5a1710dac3109df997ac7f1 + md5: d006875f9a58a44f92aec9a7ebeb7150 + depends: + - __osx >=11.0 + - libcxx >=19 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libthrift >=0.22.0,<0.22.1.0a0 + size: 323017 + timestamp: 1777019893083 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f md5: e2a72ab2fa54ecb6abab2b26cde93500 @@ -11973,14 +18921,45 @@ packages: - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libwebp-base >=1.6.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + run_exports: + weak: + - libtiff >=4.7.1,<4.8.0a0 + size: 373892 + timestamp: 1762022345545 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda + sha256: 253153cabf9469170e02b21a6bc9aa598048e7c34966b1103af52d27d2a708a4 + md5: 6fa87106b3c041ad6d965a9411e79b94 + depends: + - __osx >=11.0 + - lerc >=4.1.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND run_exports: weak: - - libtiff >=4.7.1,<4.8.0a0 - size: 373892 - timestamp: 1762022345545 + - libtiff >=4.7.2,<4.8.0a0 + size: 387825 + timestamp: 1783085754081 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda + sha256: ae1a82e62cd4e3c18e005ae7ff4358ed72b2bfbfe990d5a6a5587f81e9a100dc + md5: 2255add2f6ae77d0a96624a5cbde6d45 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - libutf8proc >=2.11.3,<2.12.0a0 + size: 87916 + timestamp: 1768735311947 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.8.0-hc098a78_1.conda sha256: 7807a98522477a8bf12460402845224f607ab6e1e73ac316b667169f5143cfe5 md5: ed89b8bf0d74d23ce47bcf566dd36608 @@ -12007,6 +18986,20 @@ packages: - libwebp-base >=1.6.0,<2.0a0 size: 294974 timestamp: 1752159906788 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + sha256: 0ff54650d470c7e54cbeffdd53a8c063e055a7bcf784388c0385ce5c4741b0f4 + md5: 168a13e329259710b28277abc1395b8e + depends: + - __osx >=11.0 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libwebp-base >=1.6.0,<2.0a0 + size: 294522 + timestamp: 1785955350410 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 md5: af523aae2eca6dfa1c8eec693f5b9a79 @@ -12035,6 +19028,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + run_exports: {} size: 466360 timestamp: 1776377102261 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.9-h4a9ca0c_0.conda @@ -12065,8 +19059,31 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 size: 41102 timestamp: 1776377119495 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda + sha256: 541980a15bf0acb7794754f1cde9cfeb74ca27c139d065c1c6541c73b4e07105 + md5: 469f4af737803bf7deda25d41c557593 + depends: + - __osx >=11.0 + - icu >=78.3,<79.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2 2.15.3 h5654f7c_0 + - libxml2-16 2.15.3 h5ef1a60_0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 + size: 80217 + timestamp: 1776377134719 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxslt-1.1.43-h429d6fd_0.conda sha256: 3491de18eb09c9d6298a7753bcc23b49a58886bd097d2653accaa1290f92c2c6 md5: f25eb0a9e2c2ecfd33a4b97bb1a84fb6 @@ -12109,6 +19126,20 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 47759 timestamp: 1774072956767 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 + md5: f39288f0ea63ae962e1a2e4f355a0d75 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 47822 + timestamp: 1785277049190 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda sha256: ccbaad6bbc88f135ab849bc36af5fa6eda36a9ed18ce6f58e3dde3d11784c156 md5: a9c118f6343fb6301b6f3b4e94c4c562 @@ -12138,6 +19169,21 @@ packages: run_exports: {} size: 108178 timestamp: 1725089672204 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda + sha256: fb105138b325e81f8dabc859cc47e9e29295b68cd6fd4dd333ed30e527e7c08b + md5: aea17e1b366b814eff15fc3c8c4c1e3c + depends: + - python + - lz4-c + - __osx >=11.0 + - python 3.14.* *_cp314 + - lz4-c >=1.10.0,<1.11.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 127471 + timestamp: 1765026415723 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 md5: 01511afc6cc1909c5303cf31be17b44f @@ -12146,6 +19192,9 @@ packages: - libcxx >=18 license: BSD-2-Clause license_family: BSD + run_exports: + weak: + - lz4-c >=1.10.0,<1.11.0a0 size: 148824 timestamp: 1733741047892 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.9.4-hb7217d7_0.conda @@ -12199,6 +19248,7 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 27256 timestamp: 1772445397216 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda @@ -12214,6 +19264,19 @@ packages: run_exports: {} size: 17770 timestamp: 1777001080046 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda + sha256: ea350f06df2581e68b9f5e1d029ad603c4044d9afff1294ca9315ac986b5d05f + md5: 710b3448473bb1bff68d2ba19cbc6c58 + depends: + - matplotlib-base >=3.11.1,<3.11.2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 14958 + timestamp: 1785211126869 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda sha256: c2dc997012fb8a901163cdad333ba5ba27733aa26d67a28a6501f4b4d69fcbce md5: e5d83f3ae6ada32d4e64e366a41f9ff6 @@ -12242,6 +19305,34 @@ packages: run_exports: {} size: 8191891 timestamp: 1777001043842 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda + sha256: 363b319e21cf4782fe264b96494c6be949db79d0d0ba126be1a1a94a5e96aae6 + md5: 41fb78906bf657af4fbdcf8c67f41c11 + depends: + - __osx >=11.0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.28.2 + - freetype + - kiwisolver >=1.3.1 + - libcxx >=19 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libraqm >=0.11.0,<0.12.0a0 + - numpy >=1.25 + - numpy >=1.25,<3 + - packaging >=20.0 + - pillow >=9 + - pyparsing >=3 + - python >=3.14,<3.15.0a0 + - python-dateutil >=2.7 + - python_abi 3.14.* *_cp314 + - qhull >=2020.2,<2020.3.0a0 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 8776291 + timestamp: 1785211103855 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.0-py314hcec6336_1.conda sha256: 0c6499e1ff5294fb33615e5d93990b7e29c8caa742d627016072b15f51becf1d md5: 5a2e8415a1cddef0de8461327bd96bf5 @@ -12253,6 +19344,17 @@ packages: license: BSD-3-Clause AND MIT size: 212016 timestamp: 1782062817762 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_1.conda + sha256: 3c314bb8dd4fcc430056bd8f5b1ace082c320d476d4ba3fa81bc087f4c02e99d + md5: b218a1fdb2e753116f1642f8d2c582c5 + depends: + - python + - tomli-w + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause AND MIT + run_exports: {} + size: 207074 + timestamp: 1786008880338 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda sha256: 4b1d4e4b17d27bbec50b1ad144e000c3b077cc4e0ef487ef11011c52ef8c86ab md5: fcd860469a8fa003e25d472b40b0ff81 @@ -12272,6 +19374,25 @@ packages: - minizip >=4.2.1,<5.0a0 size: 459816 timestamp: 1778003052237 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-hdb7fadc_0.conda + sha256: 9a59199ab5812dd7a237ab9cbd1091a29a9532a70035929fb7f255fd2ee5da0d + md5: 6bf8d76a71b4eb31138fc5c217a77af0 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libcxx >=19 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Zlib + license_family: Other + run_exports: + weak: + - minizip >=4.2.2,<5.0a0 + size: 462323 + timestamp: 1782852375098 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py312h3093aea_1.conda sha256: 1ace9b344734fda6d8c84d02c3efbf106fe68a90b6dfdb49cd46009f58e831ee md5: bcab4615e2f53affe6b34fc8887b3f12 @@ -12299,6 +19420,33 @@ packages: license_family: Apache size: 92074 timestamp: 1782070774684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314hdf3942c_1.conda + sha256: 81391bab68e075ddbcf2bfe035c069734ba12832a813b39897155008f431899b + md5: 709964d5659d3a2192395a7ac8768b15 + depends: + - python + - libcxx >=19 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 103113 + timestamp: 1786217418606 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda + sha256: 5533e7e3d4b0819b4426f8a1b3f680e6b9c922cdae2b7fabcd0e8c59df22772a + md5: 1cdbe54881794ee356d3cba7e3ed6668 + depends: + - __osx >=11.0 + - libcxx >=18 + - llvm-openmp >=18.1.8 + license: MIT + license_family: MIT + run_exports: + weak: + - muparser >=2.3.5,<2.4.0a0 + size: 154087 + timestamp: 1747117056226 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.1.0-py314h2fbedac_0.conda sha256: d205dc89fa338ca312ed54864ca377e9a52e4273509370c3678503115c95eb05 md5: 54f38ddefd5fbd675bc9ff69d7a31fd7 @@ -12316,6 +19464,24 @@ packages: license_family: MIT size: 13520137 timestamp: 1780315680928 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.0-py314h2fbedac_0.conda + sha256: 614b2b3941810a3747c521f496bd22dcd36f27749e065982e02d02f1f9aaa170 + md5: 416fa3630816cb0deb540caef775461a + depends: + - ast-serialize >=0.6.0,<1.0.0 + - mypy_extensions >=1.0.0 + - pathspec >=1.0.0 + - python + - python-librt >=0.13.0 + - typing_extensions >=4.6.0 + - psutil >=4.0 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 13696727 + timestamp: 1783986170854 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda sha256: 4ea6c620b87bd1d42bb2ccc2c87cd2483fa2d7f9e905b14c223f11ff3f4c455d md5: 343d10ed5b44030a2f67193905aea159 @@ -12327,6 +19493,27 @@ packages: - ncurses >=6.6,<7.0a0 size: 805509 timestamp: 1777423252320 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 + md5: 3dfa0d0316dc246cd44937a557de4501 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 804298 + timestamp: 1786355189145 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda + sha256: 3c7235b1574907737d09b46b3a0f7f0f5fcafc2380f2c8d02fc55aa23ce47c3c + md5: 0debf38c50bb3ea6f712c87310a00289 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + run_exports: {} + size: 137984 + timestamp: 1785920576349 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.38-heaf21c2_0.conda sha256: f8373ff02098179b9f9b2ce7b309e7ca4021c26180d07d67e1726e3d02e68e26 md5: 0b528ead848386c8a53ee0b76fbf2ac1 @@ -12393,6 +19580,26 @@ packages: license_family: BSD size: 7123654 timestamp: 1782112549976 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314hb79c6fa_0.conda + sha256: e70532da227635af2338676036c4a6b6acf8863e4dc9fc2cc55d68bd74e2f1f5 + md5: d050d2d7aac4d90c0dccf2b5829e2a7a + depends: + - python + - __osx >=11.0 + - libcxx >=19 + - python_abi 3.14.* *_cp314 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - numpy >=1.25,<3 + size: 7154942 + timestamp: 1786330664173 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 md5: 4b5d3a91320976eec71678fad1e3569b @@ -12438,6 +19645,19 @@ packages: - openssl >=3.6.3,<4.0a0 size: 3102584 timestamp: 1781069820667 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda + sha256: 66be2283b5b37dcda1332b5e74c1782a8cb14fd2e62e0d38017c2d35bf73c119 + md5: 65d1906712b85d1679263c518d011b5b + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - openssl >=3.6.3,<4.0a0 + size: 3109132 + timestamp: 1785913735357 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.2-h75dedd0_0.conda sha256: a23f3a88a6b16363bd13f964b4abd12be1576abac460126f3269cbed12d04840 md5: 9c89e09cede143716b479c5eacc924fb @@ -12457,6 +19677,27 @@ packages: - orc >=2.0.2,<2.0.3.0a0 size: 436164 timestamp: 1723760750932 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + sha256: 11b8e71ecdf9d50c9c5249e009b9f7d13d6df68091bd70fe202f3c3c16d0e3d8 + md5: d5626f645bea2fb646e12910c181fc79 + depends: + - tzdata + - __osx >=12.0 + - libcxx >=19 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.2,<1.3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil * cxx17* + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - orc >=2.3.1,<2.3.2.0a0 + size: 513679 + timestamp: 1784301269750 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda sha256: 7202013525593f57a452dac7e5fee9f26478822be3ba5c893643517b8627406d md5: 4581a32b837950217327fcab93214313 @@ -12568,6 +19809,62 @@ packages: license_family: BSD size: 14368928 timestamp: 1778602917992 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda + sha256: 9db72f83b107fab7393e02c4005003c97c7a7aaffc33417e78aadb5a82056cd5 + md5: b1dde16791eca59c8316d0ad1487bff0 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - python 3.14.* *_cp314 + - libcxx >=19 + - __osx >=11.0 + - numpy >=1.23,<3 + - python_abi 3.14.* *_cp314 + constrains: + - adbc-driver-postgresql >=1.2.0 + - adbc-driver-sqlite >=1.2.0 + - beautifulsoup4 >=4.12.3 + - blosc >=1.21.3 + - bottleneck >=1.4.2 + - fastparquet >=2024.11.0 + - fsspec >=2024.10.0 + - gcsfs >=2024.10.0 + - html5lib >=1.1 + - hypothesis >=6.116.0 + - jinja2 >=3.1.5 + - lxml >=5.3.0 + - matplotlib >=3.9.3 + - numba >=0.60.0 + - numexpr >=2.10.2 + - odfpy >=1.4.1 + - openpyxl >=3.1.5 + - psycopg2 >=2.9.10 + - pyarrow >=13.0.0 + - pyiceberg >=0.8.1 + - pymysql >=1.1.1 + - pyqt5 >=5.15.9 + - pyreadstat >=1.2.8 + - pytables >=3.10.1 + - pytest >=8.3.4 + - pytest-xdist >=3.6.1 + - python-calamine >=0.3.0 + - pytz >=2024.2 + - pyxlsb >=1.0.10 + - qtpy >=2.4.2 + - scipy >=1.14.1 + - s3fs >=2024.10.0 + - sqlalchemy >=2.0.36 + - tabulate >=0.9.0 + - xarray >=2024.10.0 + - xlrd >=2.0.1 + - xlsxwriter >=3.2.0 + - zstandard >=0.23.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 14402418 + timestamp: 1784821943837 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.56.4-hf80efc4_1.conda sha256: b57c59cf5abb06d407b3a79017b990ca5bfb10c15a10c62fc29e113f2b12d9a9 md5: 4b433508ebb295c05dd3d03daf27f7bb @@ -12587,6 +19884,28 @@ packages: license: LGPL-2.1-or-later size: 425743 timestamp: 1774282709773 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda + sha256: c300fba11c4cd7cdd7609b6f165981af24d2181d40280ca6af420710c4c7d42e + md5: 83c3d3d895dd96f87b0a1916e2c41f70 + depends: + - __osx >=11.0 + - cairo >=1.18.4,<2.0a0 + - fontconfig >=2.18.2,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.16,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - pango >=1.58.2,<2.0a0 + size: 444011 + timestamp: 1786108209790 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-ha881caa_2.conda sha256: 797411a2d748c11374b84329002f3c65db032cbf012b20d9b14dba9b6ac52d06 md5: 1a3f7708de0b393e6665c9f7494b055e @@ -12610,6 +19929,9 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - pcre2 >=10.47,<10.48.0a0 size: 850231 timestamp: 1763655726735 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda @@ -12634,6 +19956,41 @@ packages: run_exports: {} size: 965082 timestamp: 1775060469004 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda + sha256: dd7303ea135116cc1182c109825459a9eeb77d244d3899f03dd8d83a8db956f9 + md5: 4f25498ea1962ab24097fed8700e91ae + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - zlib-ng >=2.3.3,<2.4.0a0 + - openjpeg >=2.5.4,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libxcb >=1.17.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - python_abi 3.14.* *_cp314 + - tk >=8.6.13,<8.7.0a0 + - lcms2 >=2.19.1,<3.0a0 + license: HPND + run_exports: {} + size: 1019767 + timestamp: 1782912213683 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + sha256: 4779cd57231ce2e96fac643fcbdd4a6f51a57986264545445574e9c4acf526d3 + md5: 9a99c0b60efe41c194d01c182d000733 + depends: + - __osx >=11.0 + - libcxx >=19 + license: MIT + license_family: MIT + run_exports: + weak: + - pixman >=0.46.4,<1.0a0 + size: 198717 + timestamp: 1786106922508 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda sha256: 29c9b08a9b8b7810f9d4f159aecfd205fce051633169040005c0b7efad4bc718 md5: 17c3d745db6ea72ae2fce17e7338547f @@ -12718,6 +20075,43 @@ packages: - proj >=9.4.1,<9.5.0a0 size: 2708661 timestamp: 1722327880161 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-ha88f16d_0.conda + sha256: f5818ac1741aa91b43185989b35319e6dd819c7beab271fc2bb7fda4be089128 + md5: 1d135fa1ea825987507d1e14e4187b1e + depends: + - sqlite + - libtiff + - libcurl + - __osx >=11.0 + - libcxx >=19 + - libsqlite >=3.53.0,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libcurl >=8.19.0,<9.0a0 + constrains: + - proj4 ==999999999999 + license: MIT + license_family: MIT + run_exports: + weak: + - proj >=9.8.1,<9.9.0a0 + size: 3146690 + timestamp: 1775840276015 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + sha256: 851a77ae1a8e90db9b9f3c4466abea7afb52713c3d98ceb0d37ba6ff27df2eff + md5: 7172339b49c94275ba42fec3eaeda34f + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + run_exports: + weak: + - prometheus-cpp >=1.3.0,<1.4.0a0 + size: 173220 + timestamp: 1730769371051 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda sha256: 6d0e21c76436374635c074208cfeee62a94d3c37d0527ad67fd8a7615e546a05 md5: fd856899666759403b3c16dcba2f56ff @@ -12741,8 +20135,19 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 245502 timestamp: 1769678303655 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda + sha256: 1c2338b9b0486af86883b9593d2f3e2845bbf216ea453dfe5d9a228e8dbe4057 + md5: d4852e6054b74645cf49ca10f6349191 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 9238 + timestamp: 1786068031100 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 md5: 415816daf82e0b23a736a069a75e9da7 @@ -12764,6 +20169,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 229818 timestamp: 1757853438761 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_1.conda @@ -12782,6 +20188,23 @@ packages: license_family: BSD size: 10707077 timestamp: 1781020721263 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_2.conda + noarch: python + sha256: 42ebc11b1bc7adb0e1732eb0915e5078fb1d398587f3dcce5dcb9bda376db7cf + md5: e892ed39dc91aa9372ac09fd72e1439f + depends: + - python + - __osx >=11.0 + - openssl >=3.5.7,<4.0a0 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __osx >=11.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 10605428 + timestamp: 1784707901992 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda sha256: d6433c343120e723cad92b52ea05c16e05096845489275a697201ce0a50fc568 md5: 04a90c4ce691f2e289658dd475f69631 @@ -12799,6 +20222,22 @@ packages: run_exports: {} size: 25811 timestamp: 1730169125041 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda + sha256: cd50941d2b035acef8f54088108d464bfa1845c45b8ba63270f7cee70801157b + md5: 1a1b2c10602cd3dd7241e485e71d02a2 + depends: + - libarrow-acero 25.0.0.* + - libarrow-dataset 25.0.0.* + - libarrow-substrait 25.0.0.* + - libparquet 25.0.0.* + - pyarrow-core 25.0.0 *_0_* + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 25915 + timestamp: 1784258318884 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-17.0.0-py312hc40f475_2_cpu.conda build_number: 2 sha256: 708488e602a159fa38a7fd5fa4466e9f093761dc4a8538661f06be3df42f30a5 @@ -12819,6 +20258,25 @@ packages: run_exports: {} size: 3990396 timestamp: 1730169098217 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda + sha256: d205d9efa4ee35e7cfe5605c32354c010b1f3f1956cb85442f659bdcc32da8a5 + md5: d617d0524dd584ed2b4dba355cc3fc52 + depends: + - __osx >=11.0 + - libarrow 25.0.0.* *cpu + - libarrow-compute 25.0.0.* *cpu + - libcxx >=21 + - libzlib >=1.3.2,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - apache-arrow-proc * cpu + - numpy >=1.23,<3 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 4030783 + timestamp: 1784258302127 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hb84d1df_3.conda sha256: ec919254bca5ba7ef89ac1284582aa91297b89c3b640feda05a3cab0a0402db2 md5: b23019984b3399dada68581c20e6ef85 @@ -12831,6 +20289,18 @@ packages: license_family: MIT size: 92986 timestamp: 1757744788529 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda + sha256: 640d2d29a18e3a8dda02427616bd55af5670565fd780f7b5df4c045548471909 + md5: 99e06db67584235df895feda84a4790e + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 93498 + timestamp: 1784146274338 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda sha256: 480b10f3197270a98c2b564670ca6e201bc57b5005e9d58fd0d232338fd33ad7 md5: e38d54c5e4318faa79f71b713b059566 @@ -12860,6 +20330,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + run_exports: {} size: 1722694 timestamp: 1778084354332 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.10.0-py312hf9e36c7_0.conda @@ -12879,6 +20350,23 @@ packages: run_exports: {} size: 562964 timestamp: 1727771822945 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda + sha256: dcaaab4d8b539f7c4ee740e0242ae09c48f68e75949476ca36d9c67e61aafc3b + md5: 9b33fa020bd4da86a2dddfd0f63a43ba + depends: + - __osx >=11.0 + - libcxx >=19 + - libgdal-core >=3.12.0,<3.13.0a0 + - numpy + - packaging + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 599877 + timestamp: 1764402722213 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.6.1-py312hc2894cb_9.conda sha256: 642f46f2b3682b2327e1f05bdcdf0f25a57069d1d43525f62c62cbf83996faf5 md5: 1b6c7a6ae3d2ed8fc284c0015d2679aa @@ -12894,6 +20382,21 @@ packages: run_exports: {} size: 485985 timestamp: 1725436209323 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314ha82c7e6_5.conda + sha256: a05c48253c8f1fcf377be85859f411d0a9d811b9b08da093c92952e95f92fd33 + md5: 8ad655a1c8ac08ae0465ccf217745f76 + depends: + - python + - proj + - certifi + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + - proj >=9.8.1,<9.9.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 533238 + timestamp: 1786351900726 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a md5: 8e7608172fa4d1b90de9a745c2fd2b81 @@ -12944,6 +20447,35 @@ packages: size: 14059371 timestamp: 1781254578985 python_site_packages_path: lib/python3.14/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-hf4d206d_102_cp314.conda + build_number: 102 + sha256: 9767b5eee5cef50716708787bb3b4225d2a974bcd50653e856f9db5910a4b17e + md5: 8da4ea285021110e2617f7538c386afc + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.7,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + run_exports: + weak: + - python_abi 3.14.* *_cp314 + noarch: + - python + size: 13960847 + timestamp: 1786444540722 + python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.11.0-py314ha14b1ff_0.conda sha256: 483c34d3224b1d8209206d5eba7b0f36f33a891908eec46d12a7a621d0a39001 md5: 68be11fb4ea06efd0e13db21921afe56 @@ -12956,6 +20488,19 @@ packages: license_family: MIT size: 129714 timestamp: 1778511905677 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda + sha256: 07e71e940c1268e224c9a6a1f96480c10789ca521928cda30c20aa07f19aca21 + md5: e82ce608933c6feb7fc9ef479d2a2227 + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 134968 + timestamp: 1786328898613 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda sha256: 37d76254955eaa47df534ae8f3890c45f28aa4a8479597ff6fbacbd5a23810e6 md5: 45bfaa1961db6ace6fc35561ba893fae @@ -12966,6 +20511,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 175381 timestamp: 1778689065100 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda @@ -12993,6 +20539,7 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + run_exports: {} size: 189475 timestamp: 1770223788648 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_3.conda @@ -13008,6 +20555,7 @@ packages: - cpython >=3.12 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 191432 timestamp: 1779484184540 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda @@ -13048,6 +20596,31 @@ packages: run_exports: {} size: 7233883 timestamp: 1726176805073 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda + sha256: 10737f408812344475097991cf313492be966f477ff6553bfc81e7d182f3f705 + md5: d2ca5fbd28cd1fd50b2dabf9ffc96df0 + depends: + - __osx >=11.0 + - affine + - attrs + - certifi + - click >=4,!=8.2.* + - click-plugins + - cligj >=0.5 + - libcxx >=19 + - libgdal-core >=3.12.4,<3.13.0a0 + - numpy >=1.25,<3 + - proj >=9.8.1,<9.9.0a0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + - setuptools >=0.9.8 + - snuggs >=1.4.1 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 7369572 + timestamp: 1785965949045 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda sha256: 0e0d44414381c39a7e6f3da442cb41c637df0dcb383a07425f19c19ccffa0118 md5: 0342882197116478a42fa4ea35af79c1 @@ -13061,6 +20634,19 @@ packages: - re2 size: 26770 timestamp: 1708947220914 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda + sha256: ba1154085d70e8e8f94cfc38a018a13d1a734ff1bcc2ab384e4fc3e532b23066 + md5: a2578dc92c16ab5b6b183cfcdc8e8b79 + depends: + - libre2-11 2025.11.05 h5d15848_2 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + - re2 + size: 27372 + timestamp: 1781312662146 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 md5: f8381319127120ce51e081dce4865cf4 @@ -13074,6 +20660,18 @@ packages: - readline >=8.3,<9.0a0 size: 313930 timestamp: 1765813902568 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h784d473_2.conda + sha256: cebb569a3bda2a86754b2765f1927c6ac90b6e964c5bd6d58e053905b9982c97 + md5: 197a47dbf3f273e37d7d2ffde2bb0e37 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc >=14.2.7.post0,<14.3.0a0 + size: 37036 + timestamp: 1785921908208 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h84a0fba_1.conda sha256: f832ce7f2b3f6067ac2e086bff011d487fc2b18c3d2e2f9c5c2525f3ff21bff4 md5: 5a2e62653a06ab1b810e50bb02dae288 @@ -13083,6 +20681,21 @@ packages: license_family: MIT size: 33488 timestamp: 1779092919587 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hcf2a89d_2.conda + sha256: 8c6647329392efa2493e02203f4b372f8df6d393455c2e53915a7f84402fe57f + md5: 20ede03e20f3cfb712737d31f621c1bd + depends: + - reproc ==14.2.7.post0 h784d473_2 + - __osx >=11.0 + - libcxx >=19 + - reproc >=14.2.7.post0,<14.3.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + size: 29885 + timestamp: 1785921908208 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hf6b4638_1.conda sha256: f9710955247cd5890797a27db53474d682cd815f8d0c02795e60881d8f9791be md5: 9cbb9e0c4f09302ddd975887da013015 @@ -13107,6 +20720,20 @@ packages: license_family: MIT size: 292087 timestamp: 1779977082395 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314he1d1ac0_0.conda + sha256: 30bf9b362be5f04ccb7c0d9549f474763fb6fd11e7f0b78e5286b881f3c382cf + md5: 2e8e2fe25e9d6df3628b068fe7407299 + depends: + - python + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 285627 + timestamp: 1782831308617 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda sha256: 0f498e343be464219a99424260ff442144d0461a3a5eb30c9de6081af358b281 md5: 87fc3a204f105e7e61c60a72509cccbd @@ -13118,6 +20745,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 311922 timestamp: 1766175797575 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314ha14b1ff_1.conda @@ -13130,6 +20758,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 133016 timestamp: 1766159585543 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.15.19-h80928e0_0.conda @@ -13145,6 +20774,33 @@ packages: license_family: MIT size: 8644257 timestamp: 1782288982578 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.2-h828de30_0.conda + noarch: python + sha256: 8900877bfd18dcb27077a1dd9eaa7230ccae8bd6d7fada9b91c3bfbdf705f808 + md5: 57b0c0a8868a452c34904b01e99f3e45 + depends: + - python + - __osx >=11.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 8732289 + timestamp: 1786152041950 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + sha256: 47ec36bdd05117c974853a8dbaa9d89605a5f70b592d47c7d198b20252ddf1f4 + md5: 94b3b302cd6c07c5ec68648b25c8c525 + depends: + - __osx >=11.0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - s2n >=1.7.5,<1.7.6.0a0 + size: 276705 + timestamp: 1783165153651 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda sha256: e140dce2c42d0a121a113e29fbde4fd95a69cdf528ea195558ba25b809b439b7 md5: b2b777cdad320a08da92bb1f58040bfd @@ -13166,6 +20822,27 @@ packages: run_exports: {} size: 9400478 timestamp: 1780401354221 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda + sha256: c5dc417c26c46eecf7e8931c53a4c18bcd2c274c994ee80bae4767baeed4807c + md5: 72cd17b6f8016221faaa96123711f8c9 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.4.0 + - threadpoolctl >=3.5.0 + - narwhals >=2.0.1 + - python 3.14.* *_cp314 + - __osx >=11.0 + - llvm-openmp >=19.1.7 + - libcxx >=19 + - python_abi 3.14.* *_cp314 + - numpy >=1.23,<3 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 9667030 + timestamp: 1780401292916 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda sha256: c0ed2cbfa3485bac8570e52b577475778c603ee92d078a8b164d1ddec992e577 md5: 173d5eeba324363d9171946e86a81687 @@ -13187,6 +20864,27 @@ packages: run_exports: {} size: 13936510 timestamp: 1779874824714 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda + sha256: 7ce218a4e1c55775547835d21a7ead0d50e5ac348fd638ce6ba316c48c8547b7 + md5: e55fe08bb5d43e7120672338dd129030 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=2.0.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 14122215 + timestamp: 1781912992503 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.0.6-py312ha6b2466_1.conda sha256: ecba27a5a5fa5c708bb33516c580db609978550235668226c4096224db769c9d md5: 946eaf02209d9b1047d9cffb3167ce78 @@ -13202,6 +20900,21 @@ packages: run_exports: {} size: 531604 timestamp: 1725394221848 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda + sha256: 3d6f64391563dbe47f2e795ce99b2389c84c695df12170e0a1743b10963ebce7 + md5: 947d1f4e3160c83140a9c8bcb046fdac + depends: + - __osx >=11.0 + - geos >=3.14.1,<3.14.2.0a0 + - numpy >=1.23,<3 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 618928 + timestamp: 1762524178138 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.4-h4ddebb9_0.conda sha256: 3437bdc98b7a506bbf8207df8ed5f6f991c93e74b4eba635e8dfb681249e2225 md5: 14ac38259fb96102bc9d17ac9a94e887 @@ -13212,6 +20925,18 @@ packages: license_family: APACHE size: 275639 timestamp: 1778109594368 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.6-h3feff0a_1.conda + sha256: 3fe0b91319d7754a44aa8577abdb638add85d9408aecaa6c87a3c904720f8c43 + md5: 12e5ea9092b8563bc2575feda2ec9156 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 + run_exports: + weak: + - simdjson >=4.6.6,<4.7.0a0 + size: 311350 + timestamp: 1786528290494 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py312h2bbb03f_0.conda sha256: 24e5aeb84a6c58f4fb5a0c9b6966106f49af85d62c1b5789d94be00c24b16d6b md5: 85801137e6f7634e0fa9cd7a8070c2d5 @@ -13225,6 +20950,19 @@ packages: run_exports: {} size: 160192 timestamp: 1777112397342 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py314h6c2aa35_0.conda + sha256: 972c363d7df721f529e2a353f4acb7b3ed1498886700a3e1378a60fec38ee951 + md5: f96c48a75942b72af1246d789fa8ba29 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 163732 + timestamp: 1777112456003 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d md5: fca4a2222994acd7f691e57f94b750c5 @@ -13252,6 +20990,20 @@ packages: - spdlog >=1.14.1,<1.15.0a0 size: 164011 timestamp: 1722238482313 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda + sha256: 791d91a18647346c4318a4177add14abc546632fc2bf12a049cc328025368054 + md5: e4df5c4de3a06437b69ff6255c258ca2 + depends: + - __osx >=11.0 + - fmt >=12.1.0,<12.2.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 167030 + timestamp: 1785925098650 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-ha0f8610_1.conda sha256: 465e81abc0e662937046a2c6318d1a9e74baee0addd51234d36e08bae6811296 md5: 1885f7cface8cd627774407eeacb2caf @@ -13277,21 +21029,50 @@ packages: license_family: MIT size: 4036851 timestamp: 1781547978515 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda + sha256: 9c86803397cf137bf92d517b6a4395d059cfb3dbfc1def708ed5963c5848db48 + md5: 61db956083da31d178a6940043d176d2 + depends: + - python + - greenlet !=0.4.17 + - typing-extensions >=4.6.0 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: MIT + run_exports: {} + size: 4044830 + timestamp: 1786535242452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda sha256: 95482d44bef095da3c63be6e0c66a2e5b66a2a551e11d4c40949c075df13ec2c md5: b9df2fcb7e9eba945c1c946c438c4586 depends: - __osx >=11.0 - - libsqlite 3.53.3 h1b79a29_0 + - libsqlite 3.53.3 h1b79a29_0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - readline >=8.3,<9.0a0 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.3,<4.0a0 + size: 183088 + timestamp: 1782519149990 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-h77b7338_0.conda + sha256: fffebc6bbe45dd4462d3a701a0426c224ad75f8d8bcd174860d7f1b277d4ef45 + md5: 5b4d5422b8e681ce2619d0cdfe6ed161 + depends: + - __osx >=11.0 + - icu >=78.3,<79.0a0 + - libsqlite 3.53.4 h1ae2325_0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing run_exports: weak: - - libsqlite >=3.53.3,<4.0a0 - size: 183088 - timestamp: 1782519149990 + - libsqlite >=3.53.4,<4.0a0 + size: 183124 + timestamp: 1785016142653 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tiledb-2.26.0-h3c94177_0.conda sha256: 02c930cfec6a9b4a9a3837a1e5e723d1dc163cb9c60f186f6817c87ee028ad62 md5: ad1a0bc095a4e0d1d15eafd888c119b8 @@ -13337,6 +21118,18 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3127137 timestamp: 1769460817696 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda + sha256: 47186bc7ab8d7e8bee86bbd1a917196f8c21cf63f081fc33cd6d1221af087580 + md5: 8e3cf0e455e6b54519f0b1c72c61780a + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: TCL + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3338712 + timestamp: 1784229090530 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda sha256: 483350b0e4a3ebec90f6418e454d22b453f54d1bac803c2aaa7a9035cfcd7493 md5: d037e9adb0365ab53445f357bd9a035f @@ -13362,6 +21155,19 @@ packages: license_family: Apache size: 915857 timestamp: 1781007345425 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda + sha256: 4af8e4394d249619b4f1641c82fd6c0a8b97d51991ca2986095bed400c2e5eb9 + md5: ab1bd70ec9c95d199f8ce456823004ab + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 922067 + timestamp: 1786227125229 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tzcode-2026b-h1a92334_0.conda sha256: 9b81a80ffad060a6120ae7acf85c6d36e5bd988c286a4cd7ca523767d735e94e md5: 0b3d2910b2d5ac37f2c465fc59313b5d @@ -13385,6 +21191,19 @@ packages: run_exports: {} size: 415828 timestamp: 1770909782683 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda + sha256: 09bfbee5a2bcf4df06f21a2aa9eb40a7af97864a569beb5ea85fd6baf6e03ce7 + md5: 4fffb3ba871bb05f34ffb705534dfef5 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 416130 + timestamp: 1770909728445 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda sha256: fa0bcbfb20a508ca9bf482236fe799581cbd0eab016e47a865e9fa44dbe3c512 md5: e8ff9e11babbc8cd77af5a4258dc2802 @@ -13408,6 +21227,7 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 61800 timestamp: 1756851815321 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.2.5-h92fc2f4_2.conda @@ -13424,6 +21244,32 @@ packages: - xerces-c >=3.2.5,<3.3.0a0 size: 1277884 timestamp: 1727733870250 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda + sha256: 89152175f45b5e84e0f1575848f607e305ffc122ab59d9704ea77ce699b1bd2b + md5: 0b886d06130b774f086d3b2ce0b7277a + depends: + - __osx >=11.0 + - icu >=78.1,<79.0a0 + - libcxx >=19 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - xerces-c >=3.3.0,<3.4.0a0 + size: 1283088 + timestamp: 1766327630028 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda + sha256: 5c5ea38ceec008e4ff40111fc166b04086fca310b0aa9e5bd46f65e6b0dcb71d + md5: 31d71ce1b056f69b6ec67ee0712bdf97 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxau >=1.0.12,<2.0a0 + size: 15124 + timestamp: 1786381585975 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f md5: 78b548eed8227a689f93775d5d23ae09 @@ -13436,6 +21282,18 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 size: 14105 timestamp: 1762976976084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda + sha256: 2f6915f0897ace4a1b5d66d0463079f7bc9819b0048c03677e47764f0a743499 + md5: f51e9414bf1b7bb556aac1a0b74a75fe + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxdmcp >=1.1.5,<2.0a0 + size: 19486 + timestamp: 1786381546642 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 @@ -13501,6 +21359,19 @@ packages: - yaml >=0.2.5,<0.3.0a0 size: 83386 timestamp: 1753484079473 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda + sha256: 450d4c6ad26bc2c18eb9420261568b86918b118c96ab4a7f2176c352b94101a2 + md5: 04b9b2b8cc9e14758965758d9c423b34 + depends: + - libcxx >=19 + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 136591 + timestamp: 1785924101870 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-ha1acc90_0.conda sha256: 66ba31cfb8014fdd3456f2b3b394df123bbd05d95b75328b7c4131639e299749 md5: 30475b3d0406587cf90386a283bb3cd0 @@ -13521,6 +21392,9 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA + run_exports: + weak: + - zeromq >=4.3.5,<4.4.0a0 size: 245404 timestamp: 1779124076307 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda @@ -13536,6 +21410,19 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 81123 timestamp: 1774072974535 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda + sha256: ab46d85e4fcffff1b1c5cac517afa40b7ae784cf76fc9da1f55f6a6934291eb6 + md5: 2c966485853aa27985fbec7e3fcc4e77 + depends: + - __osx >=11.0 + - libzlib 1.3.2 h8088a28_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 81744 + timestamp: 1785277062753 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda sha256: a339606a6b224bb230ff3d711e801934f3b3844271df9720165e0353716580d4 md5: d99c2a23a31b0172e90f456f580b695e @@ -13578,6 +21465,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 397786 timestamp: 1762512730914 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda @@ -13625,6 +21513,41 @@ packages: license_family: MIT size: 1080282 timestamp: 1780396676915 +- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda + noarch: python + sha256: c52d4750dce807f24a4698fb3c8894b8c6e7cfca8000f5e4050a8e43d24eaf1f + md5: ecde03792069f1f16ff12c2395635035 + depends: + - python >=3.10 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - _python_abi3_support 1.* + - cpython >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 1071375 + timestamp: 1786328955132 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda + sha256: cf7ab8abaac6b462463310412cc37d087a767a95920809cbe6dc0a08fe4bcfbd + md5: f7069cdc81f7a5e781f1faf6aab6c171 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-auth >=0.10.4,<0.10.5.0a0 + size: 127842 + timestamp: 1784086873207 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.7.30-h4ab18a5_0.conda sha256: 7f80bf6b3a276cbef67c8ad2f7d1868fd20c789ca02bffb5aa1eb07e7c5e3351 md5: d48ee1d9eec32116fe1a0a5c92a4cdf7 @@ -13660,6 +21583,35 @@ packages: - aws-c-cal >=0.7.4,<0.7.5.0a0 size: 46848 timestamp: 1725830274457 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda + sha256: cda33360c71ab9a4b4e269004a5672d712fa1ae581ecca0404181805ce6762ce + md5: fda8fa85ef5d8570d5a0aadea688bb82 + depends: + - aws-c-common >=0.14.2,<0.14.3.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-cal >=0.9.14,<0.9.15.0a0 + size: 54285 + timestamp: 1784043506729 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.14.2-hfd05255_0.conda + sha256: 63fc3f34a3b3d21d5f6527ba5136e132f3ad50541d4d16e385d03f4e47e1db2b + md5: 75b4445fec6477b271920f87830d22a3 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - aws-c-common >=0.14.2,<0.14.3.0a0 + size: 241190 + timestamp: 1783637351552 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.9.28-h2466b09_0.conda sha256: 102e955695d4b996753773552820b18b6d0c1f8d77ac0412041341bece100815 md5: 3ffb0664a913a557bf89ed1834d0c12c @@ -13689,6 +21641,21 @@ packages: - aws-c-compression >=0.2.19,<0.2.20.0a0 size: 22447 timestamp: 1725830398597 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.3.2-h1da161f_4.conda + sha256: 97eaf03572b61d118616e8ee8459823c7b0f5dc26295bbab3ac3f6d671f1547a + md5: 996e5c7da8f9e255eac094d2413b40c3 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-compression >=0.3.2,<0.3.3.0a0 + size: 23355 + timestamp: 1784042894276 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.4.3-hd0ca3c1_2.conda sha256: be7815f98f210acc1e6cbac1d9a0cb05d6f91fe53c2dd62cab585c4da66359e3 md5: 93704218ce07e4d961299e170ed430b6 @@ -13706,6 +21673,41 @@ packages: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 size: 54331 timestamp: 1726327493766 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.7.1-h88938e3_4.conda + sha256: 9efd649371d7341a7a02107e7a22af304b9d9f2c16ccc534a7f93adbedc62e66 + md5: 6f00186d3ab808e5b04c6063f1e2b48c + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + size: 58249 + timestamp: 1784075740626 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.11.0-h667fc28_4.conda + sha256: 304587d77daccde630fbdbb8ae2f3994cf583427f710c60d62e88bc97c4ff013 + md5: c17a284b159959a8639298ed7da8ad0b + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-http >=0.11.0,<0.11.1.0a0 + size: 213342 + timestamp: 1784075730337 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.8.9-heca9ddf_0.conda sha256: b347d4d86850eb7f5089bb99cff00071997697d451835c4b976f2a245a93986c md5: c66174f469df56f4e2d6dbdcac7033e0 @@ -13740,6 +21742,22 @@ packages: - aws-c-io >=0.14.18,<0.14.19.0a0 size: 161275 timestamp: 1728563406574 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.27.3-hbdc738f_1.conda + sha256: 36816ff394ee736fcf705db5b1c1491c98f6f77ef84791d73b71e78154df402d + md5: 579600368b15fb523d69e2a3f8a9bc55 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-io >=0.27.3,<0.27.4.0a0 + size: 183828 + timestamp: 1784054860660 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.10.5-h8fec231_0.conda sha256: 2f851d2895d77b41c7ef0f8ce956fb14e85f307e1dfaa227d49de67af283e82a md5: ac525109aeb0571fe434e4e4d6ecbfef @@ -13757,6 +21775,43 @@ packages: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 size: 185093 timestamp: 1726206008091 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.16.0-hd7b40c3_2.conda + sha256: b48cdc8aea115b589377d9e3045005eae706119afd94de78dd9d47c39ff709e8 + md5: 87bbe253aafe6d570a176b6aa2ef9c68 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + size: 215006 + timestamp: 1784087564779 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.12.8-hd7ee1a1_1.conda + sha256: c79e62931e163b65a01786d5ed7156ecca2ab748b7c7835756d34e438035706a + md5: 4ac02fa15bf3809101e2eeb340b6078c + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + size: 146292 + timestamp: 1784101024241 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.6.5-he24745f_5.conda sha256: 53a93bc75e50410a8eca4235a360489cc7182cee2b236454bf18484cc1caa4b4 md5: f6571e7933236c29bcb992338d0f3090 @@ -13792,6 +21847,21 @@ packages: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 size: 55242 timestamp: 1725837225397 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.2.7-h1da161f_2.conda + sha256: d5da00123d9ceb082e61fd5bf82fff9cd5bec0b8e1ae91454f29155a59499bf5 + md5: 882d834f12e0abf4c7a0e9bb0c72e281 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + size: 64723 + timestamp: 1784044301184 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.1.20-hf1fc857_0.conda sha256: 446710cc7d12beddfe11bfd50a5d2a8f2418b66fb3a0a92a1a9031e041b101e9 md5: 1b66a8719c94d85fa6658d8f46600f21 @@ -13807,6 +21877,21 @@ packages: - aws-checksums >=0.1.20,<0.1.21.0a0 size: 75478 timestamp: 1726282558694 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.2.10-h1da161f_4.conda + sha256: 2227fa77f395f1b4699533709e7ef140a8292fccb31376ec5498565c7ad33225 + md5: a3c81085892f2983979836f2937291ce + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-checksums >=0.2.10,<0.2.11.0a0 + size: 117110 + timestamp: 1784044282001 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.28.2-h2ae5ca2_6.conda sha256: 8246682d553c794efcce331609f99515fd7c739b852a3c73cb7b482304f8daba md5: 61f5d43cfbabdbace9203a6fd0bd2267 @@ -13830,6 +21915,29 @@ packages: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 size: 254015 timestamp: 1726483967569 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.40.1-he7c1723_3.conda + sha256: de1ddb38c8b7455f10d81affe16a8baa88fdd16c4c938ce0cfeeb63c68f1169c + md5: 2b25be5d69a7ed4a63cde762e82cd1e0 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + size: 311706 + timestamp: 1784113271301 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.379-h9ac9443_10.conda sha256: 39bc5a9941e5a76eebc5ae52819f1212550fe18689dc7e31841aa15412cc047a md5: 65e55af6d94f870f7a1af70921d91296 @@ -13849,6 +21957,24 @@ packages: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 size: 2781287 timestamp: 1726515944980 +- conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.conda + sha256: a58fdb26e7268b00bf6a1c1ca34865e82a7fe41acc630791f835c413c06981e2 + md5: 0d6b7d24ad2b8454b061404b76382e0d + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + size: 21901375 + timestamp: 1784137301858 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.13.0-haf5610f_0.conda sha256: e3d828f79368057258140e46404892b0ed8983797c05c04eac3bd24dea71da41 md5: 14ed34c3091f89784d926cc7cf4b773b @@ -13864,6 +21990,35 @@ packages: - azure-core-cpp >=1.13.0,<1.13.1.0a0 size: 487099 timestamp: 1720853456727 +- conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.16.3-h49e36cd_0.conda + sha256: cfcbd49faf954343220ed5e5feb8be1f65070f9d077ad223c9b1958f3a73b075 + md5: 1e8ee0cbdb1822de68e488e98cd8a31e + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + size: 502079 + timestamp: 1775238920903 +- conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.13.3-hf9fdf8e_2.conda + sha256: a7809dbee931b757c7b4a3c5c9fa9fd6a7e7648e02e98216d97097364eadf1f9 + md5: d1842ddabf2087c50e5305fc4f613cec + depends: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + size: 425897 + timestamp: 1781268289981 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.8.0-h148e6f0_2.conda sha256: 1d5c52c0619d4ab1be47cd7958c5c9ecc327b0f5854ae0354b7c9cc60c73afe4 md5: 83ec332c6f07f9e48c8d5706cceab962 @@ -13895,6 +22050,37 @@ packages: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 size: 967558 timestamp: 1721865277797 +- conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.18.0-h4fb3251_1.conda + sha256: 51533ea83750b76c89dca67e2967f2ef20be5b26406f76ecb0e85537237b7a2e + md5: 7d38f326139cefa0a9c2ec505c0177b1 + depends: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + size: 800072 + timestamp: 1781283965517 +- conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda + sha256: 38dcabc3ec9d8a8f7cda50c1508aca09d0dfc27bd9f3cf9d681f0496896a6760 + md5: dbaa5d09d06d06a5febe0984667aa6d2 + depends: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + size: 256108 + timestamp: 1781268295342 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.7.0-h148e6f0_1.conda sha256: e65871ff5c3f6e19d21f9e98318de93fbed2ead70f1e6f379246c5e696bd87a7 md5: 9802dfd947dba7777ffcb25078c59c2d @@ -13910,6 +22096,23 @@ packages: - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 size: 239921 timestamp: 1721833165139 +- conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda + sha256: 51d41dd74824cfc3dd0312378bb6d7bd6a9672aaf6f105834ee63cfbfd2fecbb + md5: 59a1891edca316111ad277dc3535f2f0 + depends: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + size: 450656 + timestamp: 1781540588533 - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-h85f69ea_0.conda sha256: 1289853b41df5355f45664f1cb015c868df1f570cf743e9e4a5bda8efe8c42fa md5: 2390269374fded230fcbca8332a4adc0 @@ -13928,6 +22131,24 @@ packages: - blosc >=1.21.6,<2.0a0 size: 50135 timestamp: 1719266616208 +- conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda + sha256: 9303a7a0e03cf118eab3691013f6d6cbd1cbac66efbc70d89b20f5d0145257c0 + md5: 357d7be4146d5fec543bfaa96a8a40de + depends: + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - blosc >=1.21.6,<2.0a0 + size: 49840 + timestamp: 1733513605730 - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda sha256: df2a43cc4a99bd184cb249e62106dfa9f55b3d06df9b5fc67072b0336852ff65 md5: 441706c019985cf109ced06458e6f742 @@ -13947,6 +22168,25 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 20233 timestamp: 1756599828380 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + sha256: a4fffdf1c9b9d3d0d787e20c724cff3a284dfa3773f9ce609c93b1cfd0ce8933 + md5: bc58fdbced45bb096364de0fba1637af + depends: + - brotli-bin 1.2.0 hfd05255_1 + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 20342 + timestamp: 1764017988883 - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda sha256: e92c783502d95743b49b650c9276e9c56c7264da55429a5e45655150a6d1b0cf md5: ef022c8941d7dcc420c8533b0e419733 @@ -13961,6 +22201,20 @@ packages: run_exports: {} size: 21425 timestamp: 1756599802301 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + sha256: e76966232ef9612de33c2087e3c92c2dc42ea5f300050735a3c646f33bce0429 + md5: 6abd7089eb3f0c790235fe469558d190 + depends: + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: {} + size: 22714 + timestamp: 1764017952449 - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py312hbb81ca0_4.conda sha256: f3c7c9b0a41c0ec0c231b92fe944e1ab9e64cf0b4ae9d82e25994d3233baa20c md5: 3bb5cbb24258cc7ab83126976d36e711 @@ -13990,8 +22244,23 @@ packages: - libbrotlicommon 1.2.0 hfd05255_1 license: MIT license_family: MIT + run_exports: {} size: 335782 timestamp: 1764018443683 +- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + sha256: 04767466ee9227c9c57ab2c6503e0149177d34111c7418d2f420297acb1eb229 + md5: c3301c058362f340100d91cd8be0393f + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: bzip2-1.0.6 + license_family: BSD + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 55919 + timestamp: 1785906343696 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda sha256: 76dfb71df5e8d1c4eded2dbb5ba15bb8fb2e2b0fe42d94145d5eed4c75c35902 md5: 4cb8e6b48f67de0b018719cdf1136306 @@ -14020,6 +22289,22 @@ packages: - c-ares >=1.34.6,<2.0a0 size: 193550 timestamp: 1765215100218 +- conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_1.conda + sha256: 38920d53a152dabc4d932c3e015edac32a6a74547e4a7ffa2e4f471c3d6af046 + md5: c6cfed2bd3db1ebac70293aa99e7196a + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + constrains: + - c-ares-static <0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 210677 + timestamp: 1786116676848 - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda sha256: 9ee4ad706c5d3e1c6c469785d60e3c2b263eec569be0eac7be33fbaef978bccc md5: 52ea1beba35b69852d210242dd20f97d @@ -14038,6 +22323,9 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only or MPL-1.1 + run_exports: + weak: + - cairo >=1.18.4,<2.0a0 size: 1537783 timestamp: 1766416059188 - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda @@ -14091,6 +22379,20 @@ packages: license_family: MIT size: 294731 timestamp: 1761203441365 +- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_1.conda + sha256: e5b83726f0f3e69f9b06d582e3d8edbee0460be5df3e4f8e8b9a25c5b0f194f8 + md5: ef393e3c209458030510af02315d1762 + depends: + - pycparser + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + run_exports: {} + size: 347503 + timestamp: 1786528551392 - conda: https://conda.anaconda.org/conda-forge/win-64/cfitsio-4.4.1-hc2ea260_0.conda sha256: 97249ec67f115c05a2a452e62f6aed2e3f3a244ba1f33b0e9395a05f9d7f6fee md5: b3263858e6a924d05dc2e9ce335593ba @@ -14124,6 +22426,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cbc >=2.10.13,<2.11.0a0 size: 2865450 timestamp: 1778586787396 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cgl-0.60.10-h7c5f3e6_1.conda @@ -14143,6 +22448,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-cgl >=0.60,<0.61.0a0 size: 1001975 timestamp: 1778571631416 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda @@ -14161,6 +22469,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-clp >=1.17,<1.18.0a0 size: 2054075 timestamp: 1778520231713 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda @@ -14178,6 +22489,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-osi >=0.108,<0.109.0a0 size: 735122 timestamp: 1778511268456 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda @@ -14194,6 +22508,9 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + run_exports: + weak: + - coin-or-utils >=2.11,<2.12.0a0 size: 1091975 timestamp: 1778500610309 - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.5.3-py314h86ab7b2_0.conda @@ -14233,6 +22550,43 @@ packages: license_family: BSD size: 1362284 timestamp: 1781681191655 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.0-py314hb821551_0.conda + sha256: a4133e348175a541ea2e8a4bc6e5e123edca76548151fd1f33f44a24eb541cd9 + md5: 201bcb933dcd153c3dfd8c70709dd519 + depends: + - archspec >=0.2.3 + - boltons >=23.0.0 + - charset-normalizer + - conda-package-handling >=2.2.0 + - distro >=1.5.0 + - frozendict >=2.4.2 + - jsonpatch >=1.32 + - menuinst >=2 + - packaging >=23.0 + - platformdirs >=3.10.0 + - pluggy >=1.6.0 + - pycosat >=0.6.3 + - python + - requests >=2.28.0,<3 + - ruamel.yaml >=0.11.14,<0.19 + - setuptools >=60.0.0 + - tqdm >=4 + - truststore >=0.8.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python_abi 3.14.* *_cp314 + constrains: + - conda-build >=25.9 + - conda-content-trust >=0.3.0 + - conda-env >=2.6 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1472250 + timestamp: 1785769861157 - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.10.1-py314hb821551_2.conda sha256: f1440b3dea081d6fe76ddffba6e1f01a4d8b97f4a7b3f8fef889ffe3b0ee0e1c md5: 0c4992e5a5d69efdaa97ab733bc7f530 @@ -14252,6 +22606,27 @@ packages: license_family: MIT size: 317787 timestamp: 1781646513612 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda + sha256: af3766a095bd7b2f5f0a8958474d29c139e51628230b98b7e45ff4af1702c8ae + md5: bab6a2a990c12195bb6553216ab660ec + depends: + - python + - pip >=23.0.1 + - packaging + - unearth + - python-build + - python-installer >=1.0 + - platformdirs + - conda-index >=0.11.0 + - conda-package-streaming >=0.11 + - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 + license: MIT + license_family: MIT + run_exports: {} + size: 323375 + timestamp: 1784203061870 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda sha256: 5f0dd3a4243e8293acc40abf3b11bcb23401268a1ef2ed3bce4d5a060383c1da md5: 475bd41a63e613f2f2a2764cd1cd3b25 @@ -14267,6 +22642,21 @@ packages: run_exports: {} size: 244035 timestamp: 1769155978578 +- conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda + sha256: f141bcbf8e490b49b2f53f517173d13a64d75e43cfae170e0d931cb0b66f4bce + md5: c26934035616f7d578f9da0491aed3d8 + depends: + - numpy >=1.25 + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 247437 + timestamp: 1769155978556 - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_0.conda sha256: cd24ac6768812d53c3b14c29b468cc9a5516b71e1880d67f58d98d9787f4cc3a md5: 444e9f7d9b3f69006c3af5db59e11364 @@ -14280,6 +22670,17 @@ packages: license: CC0-1.0 size: 21733 timestamp: 1756734797622 +- conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda + sha256: ca7f86a8f5d91dffcbc0a720bb4c3be8b35ccfb8e1e44cfa43c95dd13b6c83fc + md5: 8f83a0d68492c0d19528ce750d6566b9 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: CC0-1.0 + run_exports: {} + size: 22027 + timestamp: 1785920525392 - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda sha256: ec5cce1b3a66fa8f154e9597d628988db79fb015d375351562376f80a7e965d1 md5: 7a20e585f458d44b5fd1e59bb7d95e32 @@ -14297,6 +22698,40 @@ packages: run_exports: {} size: 1645185 timestamp: 1763019902333 +- conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda + sha256: 6c87f747dac77a3a6db80d229d42d90f2864b984f612a722ceedc75d0dc09cee + md5: 3d84e97e6606a7e6bb8096afb53e9bfc + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 1644554 + timestamp: 1763019901149 +- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_4.conda + sha256: 07f77d576f2044860cca9490f1397d8d05f54adeb57eb2c4af8785639ba7f1f5 + md5: 8f6cf02d07b029c5f4716cb1bab3fcce + depends: + - krb5 >=1.22.2,<1.23.0a0 + - libcurl 8.21.0 hdb0ef4a_4 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: curl + license_family: MIT + run_exports: {} + size: 189174 + timestamp: 1785500250348 - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda sha256: e66a02405681ec973b96ea13a23c07a778c5d3a7fbf818779bda9ed088ea287e md5: 45d3504d24c6a853f22bb93d721d9d20 @@ -14328,6 +22763,21 @@ packages: run_exports: {} size: 563651 timestamp: 1771855915942 +- conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda + sha256: 04dca71339134390f2d76467070f02356efb7bf21408cdd45a868a94009e93b9 + md5: 8309b55a55dac8050260de935d31724a + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - toolz >=0.10.0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 582082 + timestamp: 1771855937157 - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda sha256: daa9397ffba6722f27c21b2f0a02b197f3ba9baedb484a155539743ec5ea3ac3 md5: 945786ac874b8e821a675fbdd885f844 @@ -14339,6 +22789,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 4022782 timestamp: 1780390190830 - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda @@ -14355,6 +22806,20 @@ packages: - double-conversion >=3.3.1,<3.4.0a0 size: 71355 timestamp: 1739570178995 +- conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda + sha256: 09e30a170e0da3e9847d449b594b5e55e6ae2852edd3a3680e05753a5e015605 + md5: 3d3caf4ccc6415023640af4b1b33060a + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - double-conversion >=3.4.0,<3.5.0a0 + size: 70943 + timestamp: 1765193243911 - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda sha256: 5fb4696e76cc9fce16223009915c14eff36cad190eca809fbb77f5a000a80cde md5: caa98e34aa51b84fc70effb79404a89d @@ -14374,6 +22839,25 @@ packages: run_exports: {} size: 511190 timestamp: 1778864902674 +- conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda + sha256: ebd6e87d44c3ced24a8f84e4a016ca31f691c6f2b5c58f84ed09787f1fc084f4 + md5: bfc434b47d15ca2f2afa329394fa138c + depends: + - cramjam >=2.3 + - fsspec + - numpy >=1.23,<3 + - packaging + - pandas >=1.5.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 523153 + timestamp: 1778864931353 - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda sha256: 0f91c93cff199371f665fe0807d4c871dd6e6534c8c9b5ede6897c867b055c23 md5: 1b03775deb65180f956a7c1b0e6edc5f @@ -14397,6 +22881,27 @@ packages: run_exports: {} size: 976102 timestamp: 1726664869512 +- conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda + sha256: 96a3ce5f2868a47e08d00a8fbcf68e1b8dbf9ca30a018120554ca26db3255c5e + md5: c1bae1515ff287d5bf6e287dddbc6646 + depends: + - attrs >=19.2.0 + - click >=8.0,<9.dev0 + - click-plugins >=1.0 + - cligj >=0.5 + - libgdal-core >=3.12.1,<3.13.0a0 + - pyparsing + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - shapely + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 985443 + timestamp: 1767051440390 - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.0.2-hf4da5c8_1.conda sha256: ecadffd95f09614aff9d520db9f7ccd677c90c3bfb4d3b2981c36ce61562f81b md5: bde3a8f593f24929b8a6e4be0ff009cf @@ -14411,6 +22916,20 @@ packages: - fmt >=11.0.2,<11.1.0a0 size: 190055 timestamp: 1743031131636 +- conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda + sha256: 1d6d6f1bf284e50cf617ce9a6250e0bf70bd0d30b068568364d6b1852aad9466 + md5: ca8fa0e01ed34bb161f4e8e86dfda22e + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - fmt >=12.1.0,<12.2.0a0 + size: 187511 + timestamp: 1785915492086 - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h7f4e812_0.conda sha256: cce96406ec353692ab46cd9d992eddb6923979c1a342cbdba33521a7c234176f md5: 6e226b58e18411571aaa57a16ad10831 @@ -14443,6 +22962,27 @@ packages: - fonts-conda-ecosystem size: 202630 timestamp: 1780450217840 +- conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_0.conda + sha256: 47263b05990c7ef4b52982a7ea8d2cfb74cd4bee0d72b363e20caf651d846fc8 + md5: 3bede9169c3a1a0a8fb1eee60aec27b7 + depends: + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libiconv >=1.18,<2.0a0 + - libintl >=0.22.5,<1.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + size: 218758 + timestamp: 1786381247448 - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda sha256: 4e31266b0ddacb2e2f48f00d999aa7d5005c62a5cc51a32f9ce0be5b11e9897e md5: 2944f5f8ea7e2db9cea01ed951e09194 @@ -14501,6 +23041,19 @@ packages: license: LGPL-2.1-or-later size: 64394 timestamp: 1757438741305 +- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + sha256: 274b3e4ae5dff527062039d1dcb5cfdd9f91fa7d8eaf61358c09450b361385de + md5: 66f5ce9d0d618332023619a899ceb26f + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LGPL-2.1-or-later + run_exports: + weak: + - fribidi >=1.0.16,<2.0a0 + size: 65318 + timestamp: 1785912637725 - conda: https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda sha256: 4247071c2216b9813f6732edc9c4728bb11f6877a1c87000b6695eebef741641 md5: 4a093f053f2827cfa102304bf945e845 @@ -14532,6 +23085,19 @@ packages: - geos >=3.12.2,<3.12.3.0a0 size: 1561663 timestamp: 1721747131206 +- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda + sha256: 032a16d78e69a20ffae6216191a66977bc50f6c21cb75f9853b37298b95308c4 + md5: 8c75d7e401a4d799ce8d4bb922320967 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LGPL-2.1-only + run_exports: + weak: + - geos >=3.14.1,<3.14.2.0a0 + size: 1772787 + timestamp: 1761593910217 - conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.3-h232476a_2.conda sha256: cf512663c8681e5e5a3d30046860ad06a8a4700b217d34c348f974ea481a0b18 md5: 8968032e8f14d84b40a20437707f8ec7 @@ -14563,13 +23129,50 @@ packages: - ucrt >=10.0.20348.0 license: LGPL-3.0-only license_family: LGPL + run_exports: + weak: + - getopt-win32 >=0.1,<0.1.1.0a0 size: 26238 timestamp: 1750744808182 +- conda: https://conda.anaconda.org/conda-forge/win-64/glib-2.88.3-h89924da_1.conda + sha256: 4aadcd822cee664e41f413fc73d58ee44e9f9609dfa8bae7fedd01d569b1447c + md5: ac5df4663035b908c8bdccfd7fe45e0e + depends: + - python * + - packaging + - libglib ==2.88.3 he810d59_1 + - glib-tools ==2.88.3 hf027272_1 + - libintl-devel + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libintl >=0.22.5,<1.0a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - libglib >=2.88.3,<3.0a0 + size: 76039 + timestamp: 1786457672418 +- conda: https://conda.anaconda.org/conda-forge/win-64/glib-tools-2.88.3-hf027272_1.conda + sha256: b4b637f6e3b408e21af8e98d635745146f7434ecf1b7ca73b8e510c1d3544ef1 + md5: a22de7dd4d070f48158307df1749c9b1 + depends: + - libglib ==2.88.3 he810d59_1 + - libffi + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libintl >=0.22.5,<1.0a0 + license: LGPL-2.1-or-later + run_exports: {} + size: 251656 + timestamp: 1786457672418 - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.13.1-h11686cb_0.conda sha256: f698800c168ba3185bbda4fbfb4a10bb625129f3ca2a6194616e7a6867234be4 md5: 354dc95ae9872158dc0f56a9b696d841 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 2017538 timestamp: 1775514603704 - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda @@ -14615,6 +23218,9 @@ packages: - vc14_runtime >=14.44.35208 license: EPL-1.0 license_family: Other + run_exports: + weak: + - graphviz >=14.1.2,<15.0a0 size: 1223547 timestamp: 1769427507016 - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.2-py314hb98de8c_0.conda @@ -14630,6 +23236,20 @@ packages: license_family: MIT size: 248927 timestamp: 1781762180310 +- conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda + sha256: c1f9d4b92bf255fc5cc92ea10448d812417fd26132856cefe9f41f2d5ccc2f8a + md5: 212518796fa1c60ca92e4d1c8ef80ce3 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 259251 + timestamp: 1786384106710 - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda sha256: b79755d2f9fc2113b6949bfc170c067902bc776e2c20da26e746e780f4f5a2d4 md5: a41f14768d5e377426ad60c613f2923b @@ -14640,6 +23260,9 @@ packages: - vc14_runtime >=14.29.30139 license: LGPL-2.0-or-later license_family: LGPL + run_exports: + weak: + - gts >=0.7.6,<0.8.0a0 size: 188688 timestamp: 1686545648050 - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda @@ -14682,6 +23305,18 @@ packages: license_family: MIT size: 1304897 timestamp: 1780450940279 +- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.3.0-h57928b3_0.conda + sha256: bcefb437e994108aaf768e52e5ee63b6eddc6dc4b91a83b2ad59363e49486709 + md5: a2696e807c11de8867ecfacc6fe467aa + depends: + - libharfbuzz-devel 14.3.0 h03b5201_0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 11509 + timestamp: 1785770514661 - conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda sha256: 52fa5dde69758c19c69ab68a3d7ebfb2c9042e3a55d405c29a59d3b0584fd790 md5: 84344a916a73727c1326841007b52ca8 @@ -14730,6 +23365,20 @@ packages: - icu >=75.1,<76.0a0 size: 14544252 timestamp: 1720853966338 +- conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda + sha256: 75c549b55b673e15de8785a8e5dd85bca7eb612eee0ff4dc8d7bdaa15eacbdbb + md5: e596942e8ee6ee17fdcf1e6a77757a66 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - icu >=78.3,<79.0a0 + size: 16835644 + timestamp: 1784916416303 - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h637d24d_0.conda sha256: 1bda728d70a619731b278c859eda364146cb5b4b8c739a64da8128353d81d1c4 md5: 0097b24800cb696915c3dbd1f5335d3f @@ -14752,6 +23401,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + run_exports: {} size: 56350 timestamp: 1757685649329 - conda: https://conda.anaconda.org/conda-forge/win-64/kealib-1.5.3-h6c43f9b_2.conda @@ -14783,6 +23433,20 @@ packages: run_exports: {} size: 73357 timestamp: 1773067062006 +- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py314hf309875_0.conda + sha256: 37cbc49fd7255532d09fb3bc9cc699554693e632fa90678a9b3d0ed12557d0d7 + md5: 0508c8dabeab91311e5c59b5e3f6d278 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 73330 + timestamp: 1773067062280 - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda sha256: 18e8b3430d7d232dad132f574268f56b3eb1a19431d6d5de8c53c29e6c18fa81 md5: 31aec030344e962fbd7dbbbbd68e60a9 @@ -14843,6 +23507,20 @@ packages: - lerc >=4.1.0,<5.0a0 size: 172395 timestamp: 1773113455582 +- conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda + sha256: 93d666f63f284ef77b87b0b1f77b70f7d36d315a132f9afa64bc0012d937ba39 + md5: add59e2b60ac9d4299d17c938185c75a + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - lerc >=4.2.0,<5.0a0 + size: 175297 + timestamp: 1785036247761 - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20240116.2-cxx17_he0c23c2_1.conda sha256: aafa7993698420ef786c145f660e6822139c02cf9230fbad43efff6d4828defc md5: 19725e54b7f996e0a5748ec5e9e37ae9 @@ -14861,6 +23539,24 @@ packages: - libabseil =*=cxx17* size: 1802886 timestamp: 1720857653184 +- conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h0eb2380_1.conda + sha256: 103c3c49368204d26c1c4ac82cd29b6c39adf0492501b0145f6a853f0c600b3f + md5: 50a46018a50bb1feeb4496dc98deae5b + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - libabseil-static =20260526.0=cxx17* + - abseil-cpp =20260526.0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 2007071 + timestamp: 1780524734178 - conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.5-haf901d7_0.conda sha256: e54c08964262c73671d9e80e400333e59c617e0b454476ad68933c0c458156c8 md5: 43b6385cfad52a7083f2c41984eb4e91 @@ -14917,6 +23613,29 @@ packages: license_family: BSD size: 1117061 timestamp: 1782289351052 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_he24518a_100.conda + sha256: d27338b30444c82b7958c833ecd6db905d45ee4d88df6e29faa7c65140b1a601 + md5: 3267f5cd28472841f357865668e17c10 + depends: + - bzip2 >=1.0.8,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - lzo >=2.10,<3.0a0 + - openssl >=3.5.7,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + run_exports: + weak: + - libarchive >=3.8.9,<3.9.0a0 + size: 1149153 + timestamp: 1785250768915 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-17.0.0-h29daf90_13_cpu.conda build_number: 13 sha256: 1a0f66e822f4cde398b15fe7ac94cb4197635798da9feebcb88c900637e05f77 @@ -14956,6 +23675,46 @@ packages: - libarrow >=17.0.0,<17.1.0a0 size: 5128979 timestamp: 1725215183038 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda + build_number: 3 + sha256: 84de044b7639d9b5a344015cf064830be75ec4acfc08b7a07ca776f534ea9579 + md5: 082ecab7d62fcf79ce26236b80e76578 + depends: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgoogle-cloud >=3.7.0,<3.8.0a0 + - libgoogle-cloud-storage >=3.7.0,<3.8.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.1,<2.3.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow >=25.0.0,<25.1.0a0 + size: 4444092 + timestamp: 1784543154690 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-17.0.0-he0c23c2_13_cpu.conda build_number: 13 sha256: 850b28abba3e40302cb5425ffb96f085d2089decafb2e80d85b4f8b44c2c777d @@ -14972,6 +23731,42 @@ packages: - libarrow-acero >=17.0.0,<17.1.0a0 size: 445286 timestamp: 1725215254997 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda + build_number: 3 + sha256: 22dc95af3d3122cd3b40c26e4fb8f8fdfac9758d9b81f15acd7e549e2f6870a2 + md5: 1f948e54f605402994877c1df640c668 + depends: + - libarrow 25.0.0 h20c36f3_3_cpu + - libarrow-compute 25.0.0 h081cd8e_3_cpu + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-acero >=25.0.0,<25.1.0a0 + size: 445884 + timestamp: 1784543398446 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda + build_number: 3 + sha256: 9609d708ba7ba4663f609d7ad8a7f6f0e6f9fab2dfbb4ae67364dd05561ce3ec + md5: 5231ee28028c0e342550873d25d21d07 + depends: + - libarrow 25.0.0 h20c36f3_3_cpu + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-compute >=25.0.0,<25.1.0a0 + size: 1750569 + timestamp: 1784543230425 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-17.0.0-he0c23c2_13_cpu.conda build_number: 13 sha256: 12b0395dc22a2c3fb03e8b8ab32bcf4ff08947b8611b2a1e9c49644d8391893c @@ -14990,6 +23785,25 @@ packages: - libarrow-dataset >=17.0.0,<17.1.0a0 size: 427535 timestamp: 1725215469376 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda + build_number: 3 + sha256: d3c84cd4ce691c0d2018b1422fd7b176a814a8b3d54803aea32c3e92f4748a54 + md5: 592863e63dbcec46b876042faf6158b2 + depends: + - libarrow 25.0.0 h20c36f3_3_cpu + - libarrow-acero 25.0.0 h7d8d6a5_3_cpu + - libarrow-compute 25.0.0 h081cd8e_3_cpu + - libparquet 25.0.0 h7051d1f_3_cpu + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-dataset >=25.0.0,<25.1.0a0 + size: 428449 + timestamp: 1784543502973 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-17.0.0-h1f0e801_13_cpu.conda build_number: 13 sha256: 637c2652cfe676d6949f7953de7d51e90bc35863c3a114c29795b5b0e119699c @@ -15011,6 +23825,27 @@ packages: - libarrow-substrait >=17.0.0,<17.1.0a0 size: 382757 timestamp: 1725215569161 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda + build_number: 3 + sha256: 7a2895e041ae4f22829b6ba6d65e7d28198e0fe56d336d671456901d8a34c66a + md5: d28db517034293dbb393d649c5063b68 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 h20c36f3_3_cpu + - libarrow-acero 25.0.0 h7d8d6a5_3_cpu + - libarrow-dataset 25.0.0 h7d8d6a5_3_cpu + - libprotobuf >=7.35.1,<7.35.2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libarrow-substrait >=25.0.0,<25.1.0a0 + size: 366569 + timestamp: 1784543539794 - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda build_number: 6 sha256: 10c8054f007adca8c780cd8bb9335fa5d990f0494b825158d3157983a25b1ea2 @@ -15044,6 +23879,24 @@ packages: license_family: BSD size: 68103 timestamp: 1779859688049 +- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-9_h8455456_mkl.conda + build_number: 9 + sha256: a99c640f10a3f77efe5c6605676ddc8d365d020eec4fdf1c803d34bdaf49b357 + md5: 17b26a4ad064259983bec1eaa36f40d3 + depends: + - mkl >=2026.1.0,<2027.0a0 + constrains: + - blas 2.309 mkl + - libcblas 3.11.0 9*_mkl + - liblapack 3.11.0 9*_mkl + - liblapacke 3.11.0 9*_mkl + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libblas >=3.11.0,<4.0a0 + size: 67235 + timestamp: 1786059219098 - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda sha256: 65d0aaf1176761291987f37c8481be132060cc3dbe44b1550797bc27d1a0c920 md5: 58aec7a295039d8614175eae3a4f8778 @@ -15058,6 +23911,20 @@ packages: - libbrotlicommon >=1.1.0,<1.2.0a0 size: 71243 timestamp: 1756599708777 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + sha256: 5097303c2fc8ebf9f9ea9731520aa5ce4847d0be41764edd7f6dee2100b82986 + md5: 444b0a45bbd1cb24f82eedb56721b9c4 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + size: 82042 + timestamp: 1764017799966 - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda sha256: aa03aff197ed503e38145d0d0f17c30382ac1c6d697535db24c98c272ef57194 md5: bf0ced5177fec8c18a7b51d568590b7c @@ -15073,6 +23940,21 @@ packages: - libbrotlidec >=1.1.0,<1.2.0a0 size: 33430 timestamp: 1756599740173 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + sha256: 3239ce545cf1c32af6fffb7fc7c75cb1ef5b6ea8221c66c85416bb2d46f5cccb + md5: 450e3ae947fc46b60f1d8f8f318b40d4 + depends: + - libbrotlicommon 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 34449 + timestamp: 1764017851337 - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda sha256: a593cde3e728a1e0486a19537846380e3ce90ae9d6c22c1412466a49474eeeed md5: 37f4669f8ac2f04d826440a8f3f42300 @@ -15088,6 +23970,21 @@ packages: - libbrotlienc >=1.1.0,<1.2.0a0 size: 245418 timestamp: 1756599770744 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + sha256: 3226df6b7df98734440739f75527d585d42ca2bfe912fbe8d1954c512f75341a + md5: ccd93cfa8e54fd9df4e83dbe55ff6e8c + depends: + - libbrotlicommon 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libbrotlienc >=1.2.0,<1.3.0a0 + size: 252903 + timestamp: 1764017901735 - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda build_number: 6 sha256: 02b2a2225f4899c6aaa1dc723e06b3f7a4903d2129988f91fc1527409b07b0a5 @@ -15119,6 +24016,41 @@ packages: license_family: BSD size: 68443 timestamp: 1779859701498 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-9_h2a3cdd5_mkl.conda + build_number: 9 + sha256: 8c20714bbb85c8a109e9002e76c32be64a82d9867beb1a35a20c85258cc2b535 + md5: 9d1d0c22e9ed8c31ec2efc0d063d6f48 + depends: + - libblas 3.11.0 9_h8455456_mkl + constrains: + - blas 2.309 mkl + - liblapack 3.11.0 9*_mkl + - liblapacke 3.11.0 9*_mkl + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libcblas >=3.11.0,<4.0a0 + size: 67587 + timestamp: 1786059232952 +- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_h2778606_6.conda + sha256: ddab96250fe45da60cf3d94df81d9d0988db157e5feffa29f8e15aea53b50b16 + md5: a49a33080bc3aa65784b563934b324a0 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - llvm-openmp >=22.1.8 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + run_exports: + weak: + - libclang13 >=22.1.8 + size: 34918557 + timestamp: 1786527644245 - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda sha256: 1a2ead8255567347e6f9b2e941e07a1bcf3ce061d94d51f1b7d88089cd1a9552 md5: 45a2834578a5f167258aa109af48b036 @@ -15162,6 +24094,24 @@ packages: license_family: MIT size: 403550 timestamp: 1782296631338 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_4.conda + sha256: af17c990ec52b6aba29e052c438d7ac61bfd541d6d183a8f09aa4f2c532c9bab + md5: 5e9c5b83929cf7ab2c04121af771e7b5 + depends: + - krb5 >=1.22.2,<1.23.0a0 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: curl + license_family: MIT + run_exports: + weak: + - libcurl >=8.21.0,<9.0a0 + size: 404586 + timestamp: 1785500241182 - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda sha256: 024be133aed5f100c0b222761e747cc27a2bdf94af51947ad5f70e88cf824988 md5: 099a1016d23baa4f41148a985351a7a8 @@ -15179,6 +24129,20 @@ packages: - libcurl >=8.9.1,<9.0a0 size: 339298 timestamp: 1722440239161 +- conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda + sha256: af1cda21d4653f594fbef20aa4e1ff158a546902b3307ef8af9a9b44b43862d2 + md5: e4e122e124676a49eebf241399ad8393 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libdeflate >=1.25,<1.26.0a0 + size: 157828 + timestamp: 1785908793271 - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda sha256: 834e4881a18b690d5ec36f44852facd38e13afe599e369be62d29bd675f107ee md5: e77030e67343e28b084fabd7db0ce43e @@ -15236,6 +24200,20 @@ packages: - libffi >=3.5.2,<3.6.0a0 size: 45831 timestamp: 1769456418774 +- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_0.conda + sha256: 2ea8d2fe7b84ca37653777e15ac1e7abd35f0c90d3efbe7f6c4de9b489606369 + md5: 92bdfc0e5012660892b0e0eaf3069a5c + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 50247 + timestamp: 1783521107166 - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda sha256: 035d0c67bf9f7a16f4a1764f420c120f1a995d071bb265fcc66ef688ef709d7b md5: e45b52fb9a81c9e2708465a706e05952 @@ -15275,6 +24253,21 @@ packages: run_exports: {} size: 821854 timestamp: 1778273037795 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.1.0-h110b43a_1.conda + sha256: 9d12bae84edf872eefcddef28677b80d3550e1ef42b319122659aaf732c4ee53 + md5: 0b5cc3373b5f925934421259463397a4 + depends: + - _openmp_mutex >=4.5 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + constrains: + - libgomp 16.1.0 h8ee18e1_1 + - libgcc-ng ==16.1.0=*_1 + - msys2-conda-epoch <0.0a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 819817 + timestamp: 1785377219855 - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda sha256: 9ab562c718bd3fcef5f6189c8e2730c3d9321e05f13749a611630475d41207fc md5: 3a5b40267fcd31f1ba3a24014fe92044 @@ -15296,6 +24289,9 @@ packages: - xorg-libxpm >=3.5.17,<4.0a0 license: GD license_family: BSD + run_exports: + weak: + - libgd >=2.3.3,<2.4.0a0 size: 166711 timestamp: 1766331770351 - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-3.9.2-h57928b3_7.conda @@ -15323,6 +24319,48 @@ packages: - libgdal >=3.9.2,<3.10.0a0 size: 423224 timestamp: 1728298647854 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_4.conda + sha256: a80c324901a7f6f3c587aad6ee7cc6416a88887fdc55df3393bb3cec38851ff0 + md5: 633568a818a3b8a2cc81ed9adfb8fe16 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libdeflate >=1.25,<1.26.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - libexpat >=2.8.1,<3.0a0 + - pcre2 >=10.47,<10.48.0a0 + - muparser >=2.3.5,<2.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - proj >=9.8.1,<9.9.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - blosc >=1.21.6,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - libpng >=1.6.58,<1.7.0a0 + - lerc >=4.2.0,<5.0a0 + - libsqlite >=3.53.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libiconv >=1.18,<2.0a0 + - libcurl >=8.21.0,<9.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - liblzma >=5.8.3,<6.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + constrains: + - libgdal 3.12.4.* + license: MIT + license_family: MIT + run_exports: + weak: + - libgdal-core >=3.12.4,<3.13.0a0 + size: 10741934 + timestamp: 1786090940755 - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.9.2-h6b59ad6_1.conda sha256: 1da89a5daf80c6b30a407c9c84e7d92f235abf79b594b3ca682ac29a6e5fe278 md5: 79a9d077a3adaede1031b0b09368577a @@ -15590,6 +24628,26 @@ packages: license: LGPL-2.1-or-later size: 4522891 timestamp: 1778508851933 +- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_1.conda + sha256: fd94edec4f945c82ba6b983aae2bec21dd08209a5b387fb7a713534bb3db51af + md5: d8b3236ab45b58a0c4f4aa0a286cee13 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - pcre2 >=10.47,<10.48.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libintl >=0.22.5,<1.0a0 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + run_exports: + weak: + - libglib >=2.88.3,<3.0a0 + size: 4518977 + timestamp: 1786457672418 - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda sha256: 4dc958ced2fc7f42bc675b07e2c9abe3e150875ffdf62ca551d94fc6facf1fd7 md5: f1147651e3fdd585e2f442c0c2fc8f2d @@ -15605,6 +24663,21 @@ packages: - libgomp >=15.2.0 size: 664640 timestamp: 1778272979661 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.1.0-h8ee18e1_1.conda + sha256: ab1b70f492c859e2e76bb2c2a0725880f5ed5aa0e142a7650b0b78d6e64487de + md5: baa80f69f3a43e4ec7e1cfb3addeae56 + depends: + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + constrains: + - msys2-conda-epoch <0.0a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - _openmp_mutex >=4.5 + - libgomp >=16.1.0 + size: 682053 + timestamp: 1785377156260 - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.28.0-h5e7cea3_0.conda sha256: 30c5eb3509d0a4b5418e58da7cda7cfee7d06b8759efaec1f544f7fcb54bcac0 md5: 78a31d951ca2e524c6c223d865edd7ae @@ -15626,6 +24699,28 @@ packages: - libgoogle-cloud >=2.28.0,<2.29.0a0 size: 14358 timestamp: 1723371187491 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda + sha256: 7caeceff8cc78b6ccdf416c3950ee7c86390be9c43969e6f0df11ffe46db8d32 + md5: 4957e887b8ff6e7c1108e217b879ebc3 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.1,<1.83.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - libgoogle-cloud 3.7.0 *_0 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud >=3.7.0,<3.8.0a0 + size: 17240 + timestamp: 1784213003080 - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-2.28.0-he5eb982_0.conda sha256: 6318a81a6ef2a72b70c2ddfdadaa5ac79fce431ffa1125e7ca0f9286fa9d9342 md5: c60153238c7fcdda236b51248220c4bb @@ -15642,9 +24737,28 @@ packages: license_family: Apache run_exports: weak: - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - size: 14259 - timestamp: 1723371607596 + - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 + size: 14259 + timestamp: 1723371607596 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda + sha256: 43f9ab1e9055f7fa4981a5da7b359901cda8fc5607e1c489b69152ce23adc3c4 + md5: 58728b379859ceb2d41642f49411e440 + depends: + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgoogle-cloud 3.7.0 hfe3f7e9_0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - libgoogle-cloud-storage >=3.7.0,<3.8.0a0 + size: 17200 + timestamp: 1784213272078 - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.62.2-h5273850_0.conda sha256: 08794bf5ea0e19ac23ed47d0f8699b5c05c46f14334b41f075e53bac9bbf97d8 md5: 2939e4b5baecfeac1e8dee5c4f579f1a @@ -15669,6 +24783,77 @@ packages: - libgrpc >=1.62.2,<1.63.0a0 size: 16097674 timestamp: 1713392821679 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda + sha256: 56db0effd12badb6075a2b5877ed72c06e61211b8c9a42843711c7b67a3324e1 + md5: 343241db44cbda08b4b41689608a07af + depends: + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - re2 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - grpc-cpp =1.82.1 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libgrpc >=1.82.1,<1.83.0a0 + size: 11674380 + timestamp: 1783586253244 +- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.3.0-h03b5201_0.conda + sha256: 43e09008ce97186b41cf8b8a5ac455005b6c5e7586b36368cfe98e1f4353f6af + md5: 3d81338de690051645d0aabeb2631441 + depends: + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: {} + size: 1041066 + timestamp: 1785770469568 +- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-devel-14.3.0-h03b5201_0.conda + sha256: 54fea2e201eeb6940ff050ff4847c1021b69ad984881369470f1b2f0446c6340 + md5: caa9cfa4e495ec225e5ccde3cab32a88 + depends: + - cairo >=1.18.4,<2.0a0 + - freetype + - glib + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz 14.3.0 h03b5201_0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.3.0 + size: 324303 + timestamp: 1785770503649 - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda sha256: 2fb437b82912c74b4869b66c601d52c77bb3ee8cb4812eab346d379f1c823225 md5: e6298294e7612eccf57376a0683ddc80 @@ -15697,8 +24882,24 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - libhwloc >=2.13.0,<2.13.1.0a0 size: 2396128 timestamp: 1770954127918 +- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_0.conda + sha256: ade3e4e8e09051bad9c75ea9e96b09e3c635216c409c87c369e6f8566a528cb1 + md5: 430378e206cf5a148fc8da7603b2466e + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 OR BSD-3-Clause + run_exports: + weak: + - libhwy >=1.4.0,<1.5.0a0 + size: 564121 + timestamp: 1784325614001 - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 md5: 64571d1dd6cdcfa25d0664a5950fdaa2 @@ -15723,6 +24924,18 @@ packages: - libintl >=0.22.5,<1.0a0 size: 95568 timestamp: 1723629479451 +- conda: https://conda.anaconda.org/conda-forge/win-64/libintl-devel-0.22.5-h5728263_3.conda + sha256: be1f3c48bc750bca7e68955d57180dfd826d6f9fa7eb32994f6cb61b813f9a6a + md5: 7537784e9e35399234d4007f45cdb744 + depends: + - libiconv >=1.17,<2.0a0 + - libintl 0.22.5 h5728263_3 + license: LGPL-2.1-or-later + run_exports: + weak: + - libintl >=0.22.5,<1.0a0 + size: 40746 + timestamp: 1723629745649 - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda sha256: 698d57b5b90120270eaa401298319fcb25ea186ae95b340c2f4813ed9171083d md5: 25a127bad5470852b30b239f030ec95b @@ -15738,6 +24951,38 @@ packages: - libjpeg-turbo >=3.1.4.1,<4.0a0 size: 842806 timestamp: 1775962811457 +- conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda + sha256: df78ab4c0eecb3dd9331898f96baeed8e5ca1c363346332517abeb0b614b9a53 + md5: fc2c23bacefd1e733f1e1d6cd3b2aaeb + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + run_exports: + weak: + - libjpeg-turbo >=3.2.0,<4.0a0 + size: 990125 + timestamp: 1785896494014 +- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_1.conda + sha256: 141310ec747808be57ba9e7501fcfab75b260803233dd9ba818fb85f6080aad9 + md5: adb20d060513fe9655d28a780bafafaa + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libhwy >=1.4.0,<1.5.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libjxl >=0.12.0,<0.13.0a0 + size: 1199695 + timestamp: 1783146089449 - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda sha256: 6b2c6506dc7d7bb3bc4128d575e4ae6c2cf18d3f9828a4be331e0b5e49edf108 md5: b44e0d9eb84c6c086d809787aab0a1da @@ -15784,6 +25029,23 @@ packages: license_family: BSD size: 81027 timestamp: 1779859714698 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-9_hf9ab0e9_mkl.conda + build_number: 9 + sha256: 62d0a7a70ee13c554d5d7ff94a2ba758c4111439822dcc81324dd4cfaf576071 + md5: bee6f13ab945c4034bdd0f722ad14dd5 + depends: + - libblas 3.11.0 9_h8455456_mkl + constrains: + - blas 2.309 mkl + - libcblas 3.11.0 9*_mkl + - liblapacke 3.11.0 9*_mkl + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - liblapack >=3.11.0,<3.12.0a0 + size: 79963 + timestamp: 1786059243440 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda sha256: d636d1a25234063642f9c531a7bb58d84c1c496411280a36ea000bd122f078f1 md5: 8f83619ab1588b98dd99c90b0bfc5c6d @@ -15799,6 +25061,21 @@ packages: - liblzma >=5.8.3,<6.0a0 size: 106486 timestamp: 1775825663227 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + sha256: d36c4a1e1f80fd08e18a407e03622ff2f34dfdd022da6488ad19603dea19e6d5 + md5: 880a0c8549479b198af21ba5dc49b109 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - xz 5.8.3.* + license: 0BSD + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 105809 + timestamp: 1786348717883 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-devel-5.8.3-hfd05255_0.conda sha256: 875f0535e135b9949720b80057508e14a9cb9351d4117760dacc6296f5b5704a md5: 7845201435d0c7c0a02269c2742da1cc @@ -15838,6 +25115,34 @@ packages: license_family: BSD size: 5316504 timestamp: 1780948946071 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda + sha256: a95ff44e76be486507973dda39600f8309194f61a7b80d9ba65967aa90077de5 + md5: ab365478ee65fbdb478e51c855fa481b + depends: + - cpp-expected >=1.3.1,<1.3.2.0a0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - reproc >=14.2.7.post0,<14.3.0a0 + - fmt >=12.1.0,<12.2.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - nlohmann_json-abi ==3.12.0 + - simdjson >=4.6.6,<4.7.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - zstd >=1.5.7,<1.6.0a0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + - libmsgpack-c >=6.1.0,<7.0a0 + - libsolv >=0.7.39,<0.8.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libcurl >=8.21.0,<9.0a0 + - openssl >=3.5.7,<4.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 5416175 + timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.8.1-h7dcf005_0.conda sha256: 64e869c54e328ca5b184932c43fad9a4172975fdf8a088da89f8182f9a7b6c9a md5: c3424255d9c261f649e6d1851102b458 @@ -15850,6 +25155,19 @@ packages: license_family: BSD size: 18083 timestamp: 1780948946071 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda + sha256: 9b43a18a720163f746b26c1178a198cb5a8a829ecdc3ac8d66ad5b4116b0e5ab + md5: 42e4a919086813b2c14aaed13ec73aec + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libmamba >=2.9.0,<2.10.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 18480 + timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.8.1-py314h40f010b_0.conda sha256: 5106c6952325c512715aadca45dd93febac92008cf907207c97ff4bbdb36f913 md5: 23d746a541606c9142fc7518fadcda2f @@ -15873,6 +25191,32 @@ packages: license_family: BSD size: 594639 timestamp: 1780948946071 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda + sha256: cc735d3f77c5cf2f8b2263c41f60fd2365015a7eddecdab95aa913f7e32fc3a4 + md5: 41fe4e717b819c816fe498fb37317ab7 + depends: + - python + - libmamba ==2.9.0 py314h08176f6_0 + - libmamba-spdlog ==2.9.0 h9ac7702_0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - nlohmann_json-abi ==3.12.0 + - pybind11-abi ==11 + - openssl >=3.5.7,<4.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - python_abi 3.14.* *_cp314 + - yaml-cpp >=0.8.0,<0.9.0a0 + - libmamba >=2.9.0,<2.10.0a0 + - fmt >=12.1.0,<12.2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 596336 + timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda sha256: 40dcd0b9522a6e0af72a9db0ced619176e7cfdb114855c7a64f278e73f8a7514 md5: e4a9fc2bba3b022dad998c78856afe47 @@ -15882,6 +25226,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 89411 timestamp: 1769482314283 - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_6.conda @@ -15896,6 +25241,21 @@ packages: license: BSL-1.0 size: 40694 timestamp: 1770807535968 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda + sha256: 9b81aaa83ae15710a3466053bdf9090b1a4ba9ec40c8231b90f0efd5e3d02855 + md5: 7928697b653d56d99504b533b02f3c64 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + constrains: + - msgpack-c ==6.1.0 *_7 + license: BSL-1.0 + run_exports: + weak: + - libmsgpack-c >=6.1.0,<7.0a0 + size: 40862 + timestamp: 1786189315611 - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_h92078aa_114.conda sha256: 111fb98bf02e717c69eb78388a5b03dc7af05bfa840ac51c2b31beb70bf42318 md5: 819507db3802d9a179de4d161285c22f @@ -15921,6 +25281,36 @@ packages: - libnetcdf >=4.9.2,<4.9.3.0a0 size: 624793 timestamp: 1717672198533 +- conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda + sha256: 453e42fcdc1770ef1e2f3f159f5b333c8b836aeaa5e141642fb2e34d80c7e5c4 + md5: 300e6113eae2004627a2c2233bbd5a55 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.0,<1.83.0a0 + - libopentelemetry-cpp-headers 1.27.0 h57928b3_1 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.27.0 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + size: 3700486 + timestamp: 1783133622370 +- conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda + sha256: be65204142e24fdaf48701747cf603a9f6c9b1efa6ea518113f2c7b1fa27e94e + md5: 85e4342d7e798c99a828d59303909d09 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 436532 + timestamp: 1783133530257 - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-17.0.0-ha915800_13_cpu.conda build_number: 13 sha256: 8cf6d193600b4dd6cb1a8fbdea168ef6bddbf8ca1ee57d08ce6992df71a62670 @@ -15939,6 +25329,24 @@ packages: - libparquet >=17.0.0,<17.1.0a0 size: 805417 timestamp: 1725215420059 +- conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda + build_number: 3 + sha256: 83398d0ef3b752f94fc99e766a8febeab0547993e55803a267fe618d697eba92 + md5: 6fd6233ec7bd1ee2423a28eab21e8f75 + depends: + - libarrow 25.0.0 h20c36f3_3_cpu + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.7,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libparquet >=25.0.0,<25.1.0a0 + size: 968008 + timestamp: 1784543362314 - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda sha256: 218913aeee391460bd0e341b834dbd9c6fa6ae0a4276c0c300266cc99a816a28 md5: 52f1280563f3b48b5f75414cd2d15dd1 @@ -15986,6 +25394,56 @@ packages: - libprotobuf >=4.25.3,<4.25.4.0a0 size: 5487058 timestamp: 1727162016965 +- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_2.conda + sha256: 0901c04727c76556a0afb1bf3f32e4ba9f4f8b50e8a6f96d02085f47875b70e0 + md5: 86341febfb82a8beb84058167f41c3d5 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 7373575 + timestamp: 1783170105520 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_0.conda + sha256: 1f92f663cb283c4fd90db8a8a3851a4f17f1e9778746adcbfb7736fde8e5455f + md5: 16ccdcddc776dbb14cd866ab480561ff + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - icu >=78.3,<79.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libpsl >=0.23.1,<0.24.0a0 + size: 73424 + timestamp: 1786443553911 +- conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda + sha256: 1bc52f781355e233411a7aaebbb155d9fd53efa2a1fc6c621250833d80cc5ab1 + md5: df92be5685f712989830bdb7ee39383a + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libharfbuzz >=14.2.1 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - fribidi >=1.0.16,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libraqm >=0.11.0,<0.12.0a0 + size: 33471 + timestamp: 1784850324004 - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2023.09.01-hf8d8778_2.conda sha256: 04331dad30a076ebb24c683197a5feabf4fd9be0fa0e06f416767096f287f900 md5: cf54cb5077a60797d53a132d37af25fc @@ -16004,6 +25462,24 @@ packages: - libre2-11 >=2023.9.1,<2024.0a0 size: 256561 timestamp: 1708947458481 +- conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda + sha256: 255d32d01d72d7d89ab5c60c20b124216e0731428af350e40ed0e001d249bcb5 + md5: 52d096d535c65d26ffb4b84a4bba2aa1 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + size: 266747 + timestamp: 1781312690043 - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h6c42fcb_16.conda sha256: 417d468a42860bee6d487a39603740c3650fb7eae03b694a9bddada9ef5d1017 md5: 4476d717f460b45f5033206bbb84f3f5 @@ -16019,6 +25495,21 @@ packages: - librttopo >=1.1.0,<1.2.0a0 size: 407420 timestamp: 1720347953921 +- conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda + sha256: d8d091afa0e5009b71e9a931da862a60104b75ca13c3021edf036620eebaf2d0 + md5: 7eeb5aed49853f8b3e1ca0463ef55a8e + depends: + - geos >=3.14.1,<3.14.2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: GPL-2.0-or-later + license_family: GPL + run_exports: + weak: + - librttopo >=1.1.0,<1.2.0a0 + size: 403088 + timestamp: 1761671197546 - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_1.conda sha256: de45b71224da77a1c3a7dd48d8885eb957c9f05455d4f0828463293e7144330f md5: 7d5abf7ca1bd00b43d273f44d93d05dc @@ -16027,6 +25518,9 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: ISC + run_exports: + weak: + - libsodium >=1.0.22,<1.0.23.0a0 size: 280234 timestamp: 1779164124739 - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_0.conda @@ -16039,8 +25533,37 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - libsolv >=0.7.39,<0.8.0a0 size: 469479 timestamp: 1780057217183 +- conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda + sha256: 951ae336443a7a568032f928bcd62f549f6a55a6ae9200237c3737e1e00720c3 + md5: effdac4d017409986c5861d19775eef1 + depends: + - freexl >=2 + - freexl >=2.0.0,<3.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - librttopo >=1.1.0,<1.2.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxml2-devel + - libzlib >=1.3.1,<2.0a0 + - proj >=9.8.0,<9.9.0a0 + - sqlite + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zlib + license: MPL-1.1 + license_family: MOZILLA + run_exports: + weak: + - libspatialite >=5.1.0,<5.2.0a0 + size: 8132506 + timestamp: 1772594244069 - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-hab0cb6d_9.conda sha256: 2b58e62603334b7d3951b93cdee9dd1fe3cd3c18aaafa65ea0f132f780adeb6e md5: 934f10287da9c46f761abf0ee5f88dd3 @@ -16088,6 +25611,19 @@ packages: - libsqlite >=3.53.3,<4.0a0 size: 1315909 timestamp: 1782519131898 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_0.conda + sha256: 62e1c45ec71ab2e5deeeb0e47e7df6a609991e91d46348f16df50c68fee145c8 + md5: ca0d59f40a02a15e9b5d0ff8db0f85e3 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 1313790 + timestamp: 1785016158097 - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda sha256: cbdf93898f2e27cefca5f3fe46519335d1fab25c4ea2a11b11502ff63e602c09 md5: 9dce2f112bfd3400f4f432b3d0ac07b2 @@ -16118,9 +25654,26 @@ packages: license_family: APACHE run_exports: weak: - - libthrift >=0.20.0,<0.20.1.0a0 - size: 612714 - timestamp: 1724653005481 + - libthrift >=0.20.0,<0.20.1.0a0 + size: 612714 + timestamp: 1724653005481 +- conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda + sha256: 7ffb48755c4fc4a7cca454e4afea286e4fb47e50e153df1b006b14691f0f43d0 + md5: 42856184560e5cf901551fd414ad25c1 + depends: + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libthrift >=0.22.0,<0.22.1.0a0 + size: 634136 + timestamp: 1777019194906 - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda sha256: f1b8cccaaeea38a28b9cd496694b2e3d372bb5be0e9377c9e3d14b330d1cba8a md5: 549845d5133100142452812feb9ba2e8 @@ -16140,6 +25693,39 @@ packages: - libtiff >=4.7.1,<4.8.0a0 size: 993166 timestamp: 1762022118895 +- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_0.conda + sha256: ec6d66308a6d6abaf3225f2f185113e6172e77eb0fa8622af982d7a5d6d47a2c + md5: e83f459471905a04ebe15e21d063c49d + depends: + - lerc >=4.1.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + run_exports: + weak: + - libtiff >=4.7.2,<4.8.0a0 + size: 1014598 + timestamp: 1783085017197 +- conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda + sha256: 5d82af0779eab283416240da792a0d2fe4f8213c447e9f04aeaab1801468a90c + md5: 5f34fcb6578ea9bdbfd53cc2cfb88200 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libutf8proc >=2.11.3,<2.12.0a0 + size: 89061 + timestamp: 1768735187639 - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.8.0-hb602f4b_1.conda sha256: b9e55f0be8ea5bee960565fd18c232a0ef62af7f007d1d102a3b66c496489d68 md5: 4dce7215af5e642fe84a07321c0628f6 @@ -16154,6 +25740,22 @@ packages: - libutf8proc >=2.8.0,<2.9.0a0 size: 83847 timestamp: 1732830082137 +- conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_0.conda + sha256: 432d4796261bc4a4525e8ecf262361c3f69d815af823ebf60242093e47333674 + md5: 63d913ed8ee946e25b1ebf7d9f477b67 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + constrains: + - libvulkan-headers 1.4.357.0.* + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - libvulkan-loader >=1.4.357.0,<2.0a0 + size: 286754 + timestamp: 1785311500125 - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda sha256: 7b6316abfea1007e100922760e9b8c820d6fc19df3f42fb5aca684cfacb31843 md5: f9bbae5e2537e3b06e0f7310ba76c893 @@ -16170,6 +25772,22 @@ packages: - libwebp-base >=1.6.0,<2.0a0 size: 279176 timestamp: 1752159543911 +- conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda + sha256: 4470d98d3178b0d45492eed4808afe421d2e7808352b8d06c2dc29166b75d94d + md5: 35a9475e4cc999d52921f57c181979fc + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libwebp-base >=1.6.0,<2.0a0 + size: 278886 + timestamp: 1785954716703 - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda sha256: 0fccf2d17026255b6e10ace1f191d0a2a18f2d65088fd02430be17c701f8ffe0 md5: 8a86073cf3b343b87d03f41790d8b4e5 @@ -16214,6 +25832,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + run_exports: {} size: 519871 timestamp: 1776376969852 - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.9-h741aa76_0.conda @@ -16246,8 +25865,49 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 size: 43684 timestamp: 1776376992865 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_0.conda + sha256: 1991b4e5e19b877b4cf01d9980957df1cc00dec94d7b93354a919ddaf8469582 + md5: 97b52e0d228b549e289445ab1238af7e + depends: + - icu >=78.3,<79.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2 2.15.3 h8ef44ab_0 + - libxml2-16 2.15.3 h3cfd58e_0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 + size: 123614 + timestamp: 1776377012113 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + sha256: 13da38939c2c20e7112d683ab6c9f304bfaf06230a2c6a7cf00359da1a003ec7 + md5: 46034d9d983edc21e84c0b36f1b4ba61 + depends: + - libxml2 + - libxml2-16 >=2.14.6 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - libxslt >=1.1.43,<2.0a0 + size: 420223 + timestamp: 1757963935611 - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda sha256: 20857f1adb91cc59826e146ee6cb1157c6abf2901a93d359b1106ba87c8e770b md5: e84f36aa02735c140099d992d491968d @@ -16296,6 +25956,22 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 58347 timestamp: 1774072851498 +- conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + sha256: 0629c2cc0404d3bb29d6baa7b4ba62da80797015e86de050db81ea5a07050527 + md5: 5d2ff29d465097458cc3ff6569151991 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 58529 + timestamp: 1785276664143 - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda sha256: 50c02902bb516eeb56680358f052be38b5bf74b40e78ea4b2a675e84957e7307 md5: de3551bf6508d45ca46b714639e52823 @@ -16328,6 +26004,22 @@ packages: run_exports: {} size: 76980 timestamp: 1725090008004 +- conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda + sha256: 764da65f49c2520ff5e2fdc8aaa57e94ca884dac58da2bdc294cb56950c63cec + md5: 8e9b2740fe44700706e8e4ca7e4fb8b6 + depends: + - python + - lz4-c + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + - lz4-c >=1.10.0,<1.11.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 47054 + timestamp: 1765026429237 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda sha256: 632cf3bdaf7a7aeb846de310b6044d90917728c73c77f138f08aa9438fc4d6b5 md5: 0b69331897a92fac3d8923549d48d092 @@ -16337,6 +26029,9 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-2-Clause license_family: BSD + run_exports: + weak: + - lz4-c >=1.10.0,<1.11.0a0 size: 139891 timestamp: 1733741168264 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda @@ -16399,6 +26094,7 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 30022 timestamp: 1772445159549 - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda @@ -16415,6 +26111,20 @@ packages: run_exports: {} size: 18066 timestamp: 1777000842638 +- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda + sha256: 3599b3ae70c4fd210e83d3e04920b70e763a3c3cc6a589f7a46776a339cf79c0 + md5: d8f7f4bb221698dda04dd955eb6f7e52 + depends: + - matplotlib-base >=3.11.1,<3.11.2.0a0 + - pyside6 >=6.7.2 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 15333 + timestamp: 1785211311397 - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda sha256: 539ca0eac473a1b7f9da1071ddcd6fe9a3bdd9e51eab0f1c498e6345c1898e8b md5: 3752482b0df88d7a08a0791f906e87ae @@ -16443,6 +26153,35 @@ packages: run_exports: {} size: 8033620 timestamp: 1777000825433 +- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda + sha256: b3f19b71ea556c79b67a599929a4c05618fd4ae03d7412e65adc95ec627670c5 + md5: 88c095cee1bcd48178328c93920870a2 + depends: + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.28.2 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libraqm >=0.11.0,<0.12.0a0 + - numpy >=1.25 + - numpy >=1.25,<3 + - packaging >=20.0 + - pillow >=9 + - pyparsing >=3 + - python >=3.14,<3.15.0a0 + - python-dateutil >=2.7 + - python_abi 3.14.* *_cp314 + - qhull >=2020.2,<2020.3.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: PSF-2.0 + license_family: PSF + run_exports: {} + size: 8810362 + timestamp: 1785211292854 - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.0-py314hb98de8c_1.conda sha256: 60b54e04e997471f697872c64cbab4d52bca06286a74b3ef898ced58bcdaff78 md5: bc069b5b1bd007235f46cf21989ae558 @@ -16456,6 +26195,20 @@ packages: license: BSD-3-Clause AND MIT size: 198561 timestamp: 1782062732702 +- conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_1.conda + sha256: a16fdf28db19ba4636dbfc7b64021cca818a08510d92df74a5cd3009d814fe5e + md5: 00a6619238f1da07faec8f357cbe7733 + depends: + - python + - tomli-w + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause AND MIT + run_exports: {} + size: 199350 + timestamp: 1786008821495 - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda sha256: 208b235b20232891d1dc9f468d08b00c53e7ca65a18b9bc0ff4c4867680b9a75 md5: 5d55038c4d640e5df06de1cfb4e8d741 @@ -16474,6 +26227,24 @@ packages: - minizip >=4.2.1,<5.0a0 size: 106290 timestamp: 1778002682910 +- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h0ffbb96_0.conda + sha256: 1c0c9b720b5dc860cc8a127d925c161d234a8a3ca4a10cea5038bfa548315557 + md5: 431ad376b1c4e5306a8ceb6b7278de41 + depends: + - bzip2 >=1.0.8,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + license: Zlib + license_family: Other + run_exports: + weak: + - minizip >=4.2.2,<5.0a0 + size: 106476 + timestamp: 1782851531134 - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda sha256: d0ec759eed591693d4b51d9e31f43e9ec701484a012b14f28f5d6fd75317f481 md5: c058cdc8796d5eb260a1aef2cbb7fbbf @@ -16503,6 +26274,20 @@ packages: license_family: Proprietary size: 114354729 timestamp: 1779293121860 +- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_234.conda + sha256: f7568a5ebf9f4a401a6d9da7be4f82a8794cf305e870e77923a5712ba7f4b964 + md5: f0510f9d5e501462d72a5c0c798dbcc3 + depends: + - llvm-openmp >=22.1.8 + - tbb >=2023.0.0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + run_exports: {} + size: 114429478 + timestamp: 1786086389935 - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py312h78d62e6_1.conda sha256: f43cc72ae92759d661187cb4bc4fb4721b97128d150f336b9b0ebf24b8600a27 md5: 0ac1766b278a8547f22cd0820b8a7e96 @@ -16530,6 +26315,34 @@ packages: license_family: Apache size: 90559 timestamp: 1782070458677 +- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314hf309875_1.conda + sha256: 548f4b23229abf8503b1e28438682e79a82ed0f2081a0c5ddfadb49e721b5022 + md5: 2a6b2dce5b76d32c67d11fcdd88ef037 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 89771 + timestamp: 1782460807585 +- conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda + sha256: 57f78d8cd9a282d03cd7a7ffb1f42d570e1bbfb42d606e99de5c16e089067185 + md5: 013aabb169d59009bdf7d70319360e9b + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: MIT + license_family: MIT + run_exports: + weak: + - muparser >=2.3.5,<2.4.0a0 + size: 148557 + timestamp: 1747117340968 - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.1.0-py314h13f4da2_0.conda sha256: 8099ce0e8f971f6a393df52c2d5abca56209d47c1d3da020ca4ee0625539b933 md5: 26de46b296a732183242a77ac4e6a30f @@ -16549,6 +26362,36 @@ packages: license_family: MIT size: 10734309 timestamp: 1780315652597 +- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.0-py314h13f4da2_0.conda + sha256: 04cbd9e2a351eae6b2039e3c1ad10961fa879899fc430d2b88ba2c6b39021089 + md5: 4c9b071b4bf13daa16c940be2699e6e7 + depends: + - ast-serialize >=0.6.0,<1.0.0 + - mypy_extensions >=1.0.0 + - pathspec >=1.0.0 + - python + - python-librt >=0.13.0 + - typing_extensions >=4.6.0 + - psutil >=4.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 10913265 + timestamp: 1783986254963 +- conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda + sha256: 460d5a644bd7f195784f7e17d550274986ddffa1d534b18195322de1f81cf42d + md5: faf4e3f7981777629dd7cbef578834ad + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + run_exports: {} + size: 135270 + timestamp: 1785920493388 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda sha256: 73570817a5109d396b4ebbe5124a89525959269fd33fa33fd413700289fbe0ef md5: f9ac74c3b07c396014434aca1e58d362 @@ -16588,6 +26431,27 @@ packages: license_family: BSD size: 7436159 timestamp: 1782112573833 +- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_0.conda + sha256: 14067f2c7628320708f73531c98c16753ee3991230c6ae32873baed911771b09 + md5: bd342de8875a168535a553191b405821 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - numpy >=1.25,<3 + size: 7462504 + timestamp: 1786330617237 - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda sha256: 4ca711784724992014a2ba1bfc2e4798182f374d1f11a05f33d636e9f10ca6ca md5: eded4ef8a94852a2e3e4c779b3b6fdda @@ -16635,6 +26499,21 @@ packages: - openssl >=3.6.3,<4.0a0 size: 9414790 timestamp: 1781071745579 +- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_1.conda + sha256: 2ebff5a1b5793e82495bf33c91fba040e11ff23333c2385ac66d0c3aee2cc14c + md5: a978392692a910ba1c8920ccb1e784b3 + depends: + - ca-certificates + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - openssl >=3.6.3,<4.0a0 + size: 9427535 + timestamp: 1785915614585 - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda sha256: f083c8f49430ca80b6d8a776c37bc1021075dc5f826527c44a85f90607a5c652 md5: dbb01d6e4f992ea4f0dcb049ab926cc7 @@ -16655,6 +26534,28 @@ packages: - orc >=2.0.2,<2.0.3.0a0 size: 999325 timestamp: 1723761049521 +- conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda + sha256: 6097e6facda0184c7be60eb0daaf05a03d6d4b9725ea5901553a1d01ba91271c + md5: d0e00e318dceb35f9a66f52225609ed2 + depends: + - tzdata + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - zstd >=1.5.7,<1.6.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - libzlib >=1.3.2,<2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil * cxx17* + license: Apache-2.0 + license_family: APACHE + run_exports: + weak: + - orc >=2.3.1,<2.3.2.0a0 + size: 1465743 + timestamp: 1784301260606 - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda sha256: f1f67623997699885c7668b039314f5c9a663eb94dcaf284574fdc6dd248b2b8 md5: 9da394ea5e0ec5cc5edc1ad14f2a4d4d @@ -16667,7 +26568,64 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - numpy >=1.23,<3 - - python_abi 3.12.* *_cp312 + - python_abi 3.12.* *_cp312 + constrains: + - adbc-driver-postgresql >=1.2.0 + - adbc-driver-sqlite >=1.2.0 + - beautifulsoup4 >=4.12.3 + - blosc >=1.21.3 + - bottleneck >=1.4.2 + - fastparquet >=2024.11.0 + - fsspec >=2024.10.0 + - gcsfs >=2024.10.0 + - html5lib >=1.1 + - hypothesis >=6.116.0 + - jinja2 >=3.1.5 + - lxml >=5.3.0 + - matplotlib >=3.9.3 + - numba >=0.60.0 + - numexpr >=2.10.2 + - odfpy >=1.4.1 + - openpyxl >=3.1.5 + - psycopg2 >=2.9.10 + - pyarrow >=13.0.0 + - pyiceberg >=0.8.1 + - pymysql >=1.1.1 + - pyqt5 >=5.15.9 + - pyreadstat >=1.2.8 + - pytables >=3.10.1 + - pytest >=8.3.4 + - pytest-xdist >=3.6.1 + - python-calamine >=0.3.0 + - pytz >=2024.2 + - pyxlsb >=1.0.10 + - qtpy >=2.4.2 + - scipy >=1.14.1 + - s3fs >=2024.10.0 + - sqlalchemy >=2.0.36 + - tabulate >=0.9.0 + - xarray >=2024.10.0 + - xlrd >=2.0.1 + - xlsxwriter >=3.2.0 + - zstandard >=0.23.0 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 13644207 + timestamp: 1778602674307 +- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py314hf700ef7_0.conda + sha256: 7f9912ba70e53805432f8e3a980fec5d13fe851989f68a70889394a2b4438ac2 + md5: 33451badee17d4162840339efdab40ad + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - python-tzdata + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + - numpy >=1.23,<3 constrains: - adbc-driver-postgresql >=1.2.0 - adbc-driver-sqlite >=1.2.0 @@ -16709,12 +26667,11 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - run_exports: {} - size: 13644207 - timestamp: 1778602674307 -- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py314hf700ef7_0.conda - sha256: 7f9912ba70e53805432f8e3a980fec5d13fe851989f68a70889394a2b4438ac2 - md5: 33451badee17d4162840339efdab40ad + size: 14062915 + timestamp: 1778602665890 +- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda + sha256: 399c1d7f7918986d9c1a646f4e789ac24749149598d16b529febc3361e9aad23 + md5: a519b50147401cf6bad2fd188028ad38 depends: - python - numpy >=1.26.0 @@ -16766,8 +26723,9 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - size: 14062915 - timestamp: 1778602665890 + run_exports: {} + size: 14103773 + timestamp: 1785276287020 - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h13911b6_1.conda sha256: 3d4e6e541e633f6fd22fc2c1d79ad5ec39503dea3ba04fc3e01d5be904ec7cea md5: 1f1cf3772ba7d4eef989e4679ddf97f7 @@ -16789,6 +26747,30 @@ packages: license: LGPL-2.1-or-later size: 454919 timestamp: 1774282149607 +- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda + sha256: 85fc9c2a3b805d7ad784b6b307f4cf4833d3525ab409a7f86262d476a67d441f + md5: 22d750d2ebf4109bc66c036f1e52b982 + depends: + - cairo >=1.18.4,<2.0a0 + - fontconfig >=2.18.2,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.16,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: LGPL-2.1-or-later + run_exports: + weak: + - pango >=1.58.2,<2.0a0 + size: 466294 + timestamp: 1786107518608 - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda sha256: 15dffc9a2d6bb6b8ccaa7cbd26b229d24f1a0a1c4f5685b308a63929c56b381f md5: a912b2c4ff0f03101c751aa79a331831 @@ -16816,6 +26798,9 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + run_exports: + weak: + - pcre2 >=10.47,<10.48.0a0 size: 995992 timestamp: 1763655708300 - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda @@ -16841,6 +26826,29 @@ packages: run_exports: {} size: 944998 timestamp: 1775060119774 +- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda + sha256: c9dc3212ed0021974541072ee3765a5097a0721191c34ee485d7d7e94449648f + md5: 09b8e6ac8f4a257b59e5d3025f3c9c1d + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libwebp-base >=1.6.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - openjpeg >=2.5.4,<3.0a0 + - lcms2 >=2.19.1,<3.0a0 + - libxcb >=1.17.0,<2.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + license: HPND + run_exports: {} + size: 989058 + timestamp: 1782912129930 - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda sha256: 246fce4706b3f8b247a7d6142ba8d732c95263d3c96e212b9d63d6a4ab4aff35 md5: 08c8fa3b419df480d985e304f7884d35 @@ -16858,6 +26866,20 @@ packages: - pixman >=0.46.4,<1.0a0 size: 542795 timestamp: 1754665193489 +- conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda + sha256: cad8b94c2b00264a15d469eed6dc53aac1c59c6d46f27ad1d91bfaf227cf136a + md5: 27c5be39d9e4d25fe85b89a069360d1e + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - pixman >=0.46.4,<1.0a0 + size: 257473 + timestamp: 1786106677325 - conda: https://conda.anaconda.org/conda-forge/win-64/poppler-24.08.0-h9415970_1.conda sha256: e0066265fefd6954b68f4ab74d133ad49c5ed8c5d4c668e0450ba3e7b9a9501c md5: 4c7b7a4301314afed48c6544c34399e4 @@ -16925,6 +26947,45 @@ packages: - proj >=9.4.1,<9.5.0a0 size: 2726576 timestamp: 1722328352769 +- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_0.conda + sha256: 10ac4a9cdd99d3c2dc4695c5db51d9aa263a3b7551604f57801679b81c785d12 + md5: bbf5692c63b2f280975b3430ed3e37fd + depends: + - sqlite + - libtiff + - libcurl + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libcurl >=8.19.0,<9.0a0 + - libsqlite >=3.53.0,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + constrains: + - proj4 ==999999999999 + license: MIT + license_family: MIT + run_exports: + weak: + - proj >=9.8.1,<9.9.0a0 + size: 3129512 + timestamp: 1775840349774 +- conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda + sha256: ed08acd2ce6c69063693193450df89e8695e8b1251b399d34fb56ab45d900cbc + md5: 128297355faf0afcb84e22e43d472101 + depends: + - libcurl >=8.10.1,<9.0a0 + - libzlib >=1.3.1,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + - zlib + license: MIT + license_family: MIT + run_exports: + weak: + - prometheus-cpp >=1.3.0,<1.4.0a0 + size: 183665 + timestamp: 1730769570131 - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda sha256: edffc84c001a05b996b5f8607c8164432754e86ec9224e831cd00ebabdec04e7 md5: a2724c93b745fc7861948eb8b9f6679a @@ -16950,6 +27011,7 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 249950 timestamp: 1769678167309 - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda @@ -16964,6 +27026,18 @@ packages: run_exports: {} size: 9389 timestamp: 1726802555076 +- conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + sha256: 5546927a2e337d2903d6cdb028fb33723cdbcc45bf9abe1770fbde2c4ea8bce6 + md5: bc97d497656c9c661ffd3043aca90aa4 + depends: + - libgcc >=14 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: {} + size: 10120 + timestamp: 1786067833280 - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda sha256: b74d98c204593715d08863833a37317a108d2ef6f5464f93e5b6c7f013a85104 md5: 98d68f979d9c387b5147ce4a8e51fc10 @@ -16974,6 +27048,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 14442928 timestamp: 1757853584977 - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_1.conda @@ -16991,6 +27066,22 @@ packages: license_family: BSD size: 13201940 timestamp: 1781020758113 +- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_2.conda + noarch: python + sha256: c34345f682fba5bc0e985418205eab6b16f76e8fc8172b1354ecf3ac6985687a + md5: 7b14c85bd80ca0ddadacdf19c658823c + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - _python_abi3_support 1.* + - cpython >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 13161702 + timestamp: 1784707929736 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda sha256: bc8553befae5d33ddf0d99262de4b7ffbe798741e01b202b7d8d6336d4f2142b md5: 5601751f674c1ecc1629cdf76f22ab61 @@ -17008,6 +27099,22 @@ packages: run_exports: {} size: 26129 timestamp: 1730169358866 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda + sha256: 050a5a54d49f5877f15566cd4df55233b2c78bb240f97a53c3a5828f68f5c5c9 + md5: 7c6d60d53f7b0b09e424b58cd9fdfd37 + depends: + - libarrow-acero 25.0.0.* + - libarrow-dataset 25.0.0.* + - libarrow-substrait 25.0.0.* + - libparquet 25.0.0.* + - pyarrow-core 25.0.0 *_0_* + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 26584 + timestamp: 1784258906758 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-17.0.0-py312h6a9c419_2_cpu.conda build_number: 2 sha256: 38acc61d435c6c9a30e9c9609a40da356b606c7c3f4ee7e5150ced3416b3f88b @@ -17028,6 +27135,27 @@ packages: run_exports: {} size: 3551041 timestamp: 1730169341038 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda + sha256: 2e0422f3b9cc1aa067638cccb0a8e917809550a91eb781cfbf64d5a7e2b8e7ab + md5: 54155aba0ef1fe72c52022617e453d52 + depends: + - libarrow 25.0.0.* *cpu + - libarrow-compute 25.0.0.* *cpu + - libzlib >=1.3.2,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - apache-arrow-proc * cpu + - numpy >=1.23,<3 + - libprotobuf >=6.33.5 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 3689897 + timestamp: 1784258892556 - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_3.conda sha256: e14a8c506e699c968596f1a07e925d63dcc49ee9f3623c4c8a5541ad186e9071 md5: 4e6be0e1a7a9cd7dde994137b8ebd5cf @@ -17041,6 +27169,20 @@ packages: license_family: MIT size: 79551 timestamp: 1757744726713 +- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda + sha256: 38d9062eca5d85ac5de080b3512edc4e0f89c64199afcd2da34cd432b5d2514d + md5: c79e745df2f185732d9cdb5763452440 + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: {} + size: 80121 + timestamp: 1784146283080 - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda sha256: 2b251fb56d07efd36815732477d22bcdd2c7a6e7500012eb024d8c59413d2e24 md5: edd458c91ab9bc198012c73289b0b0bc @@ -17068,6 +27210,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 1885001 timestamp: 1778084256232 - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.conda @@ -17087,6 +27230,23 @@ packages: run_exports: {} size: 806834 timestamp: 1727772248889 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda + sha256: 3ed86ebf3d68f9ae52b5a74f9db919d367af33acf26d481fcdb00fff7079e350 + md5: da6ef5e9a5931e73a7638b60fd82fc63 + depends: + - libgdal-core >=3.12.0,<3.13.0a0 + - numpy + - packaging + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: {} + size: 899343 + timestamp: 1764402737921 - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.6.1-py312h6f27134_9.conda sha256: 42c2eaa5d75ad0e184e8b957ae07c9d9438fa611c8b896a9a387aaafe10dc3b2 md5: a7414c734b08e74d22581a9a07686301 @@ -17103,6 +27263,23 @@ packages: run_exports: {} size: 740323 timestamp: 1725436440016 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda + sha256: 84d335aabc168c716b7989e8b97c07697e5e21083a7e8bfe9be168cf4300c7f7 + md5: b7503bde09c3bce5c09d81485855f5a8 + depends: + - python + - proj + - certifi + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + - proj >=9.8.1,<9.9.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 893897 + timestamp: 1786351867522 - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda sha256: 692abac5636f9a1fb10639ccef0f66cbfb38c20d88cdf33132fd8458c82793f3 md5: 518a7112a2457d2e3ab13b749306490d @@ -17111,8 +27288,30 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 175241 timestamp: 1778848456926 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.1-py314h447aaf0_1.conda + sha256: 070802d5e1e1c1feb24d481efbd90b300fb0ecc1ce4312a3bbcbaae4393c05f9 + md5: 638be6b8674e7acf7a84132903cf4c8e + depends: + - python + - qt6-main 6.11.1.* + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libxslt >=1.1.43,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - qt6-main >=6.11.1,<6.12.0a0 + - python_abi 3.14.* *_cp314 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - libclang13 >=22.1.5 + license: LGPL-3.0-only + license_family: LGPL + run_exports: {} + size: 11579652 + timestamp: 1778933912020 - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda sha256: 808204eb911e20f4e58b0b6a90e424410a66668a57c08e2e6466b23137cb4f90 md5: 52a05ba3f802633cb2234bb3edc45888 @@ -17182,6 +27381,35 @@ packages: size: 18481352 timestamp: 1781256034828 python_site_packages_path: Lib/site-packages +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h53f6dd8_102_cp314.conda + build_number: 102 + sha256: 9faac11f2b7813585f428310df3768e2a465205d76c3829f8ccbd05e6b98764f + md5: 048ad2c1b1faf11cda7bb8c7ec998b0c + depends: + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - python_abi 3.14.* *_cp314 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + run_exports: + weak: + - python_abi 3.14.* *_cp314 + noarch: + - python + size: 17903528 + timestamp: 1786444689733 + python_site_packages_path: Lib/site-packages - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.11.0-py314hc5dbbe4_0.conda sha256: 3269977dd171fdff6eef27db83dac87847d91501539c317acea354c61cc1cb6d md5: 7cdfc5fda782933981f50c7a9a746039 @@ -17195,6 +27423,20 @@ packages: license_family: MIT size: 104479 timestamp: 1778511743226 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda + sha256: f69e11b4025a7467a1820ce348e39af70d4eb0508e89375d3c2e501c4b8e532f + md5: e43c348d1855554ddd3ccd385940386f + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 108739 + timestamp: 1786328843266 - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda sha256: 2c97a59ce98b9d29a30bc49f3001b9206ce789edaee7d2773cde8a8a7f78abc4 md5: 02632a618ced2283cf9a7c4a70a43026 @@ -17206,6 +27448,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 120434 timestamp: 1778688861070 - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hcaaf0b2_0.conda @@ -17219,6 +27462,7 @@ packages: - python_abi 3.14.* *_cp314 license: PSF-2.0 license_family: PSF + run_exports: {} size: 4472831 timestamp: 1781362876741 - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda @@ -17248,6 +27492,7 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + run_exports: {} size: 181257 timestamp: 1770223460931 - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_3.conda @@ -17264,6 +27509,7 @@ packages: - zeromq >=4.3.5,<4.3.6.0a0 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 182831 timestamp: 1779483925948 - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda @@ -17279,6 +27525,42 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 1377020 timestamp: 1720814433486 +- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.1-pl5321hfcac499_1.conda + sha256: c0f0552a879e18282799431c7d2769b269839ac3b3735082e754df3c6fa0728d + md5: a8d735f3faf356a24acf9eea0a940a0f + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - krb5 >=1.22.2,<1.23.0a0 + - libglib >=2.88.1,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - openssl >=3.5.6,<4.0a0 + - icu >=78.3,<79.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libvulkan-loader >=1.4.341.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libsqlite >=3.53.1,<4.0a0 + - harfbuzz >=14.2.0 + constrains: + - qt ==6.11.1 + license: LGPL-3.0-only + license_family: LGPL + run_exports: + weak: + - qt6-main >=6.11.1,<7.0a0 + size: 89576886 + timestamp: 1780400596481 - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda sha256: 84ff37de3c72a612dfbf9b317d5501231a87582f2edf4959ff706b84b4aa9246 md5: d92e5a0de3263315551d54d5574f5193 @@ -17329,13 +27611,38 @@ packages: - setuptools >=0.9.8 - snuggs >=1.4.1 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 7448190 + timestamp: 1726177323954 +- conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda + sha256: e7236607a5f8abda3c1e22afcdb8ece4a466c4120a83a6162fb623bab5f7ce2d + md5: 195d18c86203797871e15b2907579a84 + depends: + - affine + - attrs + - certifi + - click >=4,!=8.2.* + - click-plugins + - cligj >=0.5 + - libgdal-core >=3.12.4,<3.13.0a0 + - numpy >=1.25,<3 + - proj >=9.8.1,<9.9.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - setuptools >=0.9.8 + - snuggs >=1.4.1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 7448190 - timestamp: 1726177323954 + size: 8483184 + timestamp: 1785964794642 - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2023.09.01-hd3b24a8_2.conda sha256: 929744a982215ea19f6f9a9d00c782969cd690bfddeeb650a39df1536af577fe md5: ffeb985810bc7d103662e1465c758847 @@ -17349,6 +27656,33 @@ packages: - re2 size: 207315 timestamp: 1708947529390 +- conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda + sha256: eb11c0e948c2576378a8e7f22768a967044d54bfbdea9ef358d31074b91c21f0 + md5: 5d51319ef836277672d665c2fc04bab0 + depends: + - libre2-11 2025.11.05 h45f70d9_2 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libre2-11 >=2025.11.5 + - re2 + size: 222198 + timestamp: 1781312702688 +- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-h5112557_2.conda + sha256: 74b9dfb94e5fe2a2d2b6467ea3e91e83d66e174bf58790443bd74218784be395 + md5: 494d01b7baff75c04252ff035cb44e79 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc >=14.2.7.post0,<14.3.0a0 + size: 38715 + timestamp: 1785921791755 - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-hfd05255_1.conda sha256: a7060320c3f6c9ec7d8222a2a217f02bcf3f41ecf5a5d1a375c6280b604962f2 md5: 9f428ede23b9a119bd2c0330bfc51851 @@ -17360,6 +27694,22 @@ packages: license_family: MIT size: 38712 timestamp: 1779092487585 +- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-h03e4b45_2.conda + sha256: d87e5fa1e009110bd2905d0e1bc2404da23b365d2ebbc1b3696c7705a36898ac + md5: b905cc386fe540a24ab701c9b8344050 + depends: + - reproc ==14.2.7.post0 h5112557_2 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - reproc >=14.2.7.post0,<14.3.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + size: 31831 + timestamp: 1785921791755 - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-hac47afa_1.conda sha256: 4cbe19c86a5c515724ebfbce34f1bf2a9f08229e704c99d811c00629e27a4671 md5: 6bf1a32350e40dafb15d85080ad84408 @@ -17385,6 +27735,20 @@ packages: license_family: MIT size: 229575 timestamp: 1779977051010 +- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314hc980628_0.conda + sha256: ac1047c2a9bb3dc7cac31b7c995a7b25f9a4eb52ee8ae106ca6e6c15679f34fc + md5: 52429bdb1752b43f8364bc482cf65fda + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 217789 + timestamp: 1782831449604 - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda sha256: 62b73874b6c264a73e7904b33322128c4881c78dfe2fb9131becfbf84343ee2d md5: 027ff6055e5c82ba4e085857f09302cb @@ -17397,6 +27761,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 306951 timestamp: 1766175833786 - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_1.conda @@ -17410,6 +27775,7 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + run_exports: {} size: 105668 timestamp: 1766159584330 - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.15.19-h45713df_0.conda @@ -17425,6 +27791,20 @@ packages: license_family: MIT size: 9926237 timestamp: 1782288657493 +- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.2-h6b72154_0.conda + noarch: python + sha256: 903a24f58856048ff6cfc0a227e91b164fada1de60f94d68457bbe3ac5882263 + md5: 57ad51fa21d6220a94012eccea8c62aa + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: {} + size: 9898546 + timestamp: 1786151875428 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda sha256: c89cfc61bd6c690154ff7a7c9029b9dd83aae1049dc1f84c25c37cafa0b5f69d md5: 0637562978d4231902754645e4c28640 @@ -17445,6 +27825,26 @@ packages: run_exports: {} size: 9153589 timestamp: 1780413519467 +- conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda + sha256: 3e30cc784bd5af6aa035807e5c2f12a1ecbc298d755561f6ce968b3b598b940a + md5: 74bafde39f688cb95c111e74bfad6669 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.4.0 + - threadpoolctl >=3.5.0 + - narwhals >=2.0.1 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - numpy >=1.23,<3 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 9411356 + timestamp: 1780401101875 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda sha256: b8a13caeb04f2b943e7c617a4b76805450618716b38688ee8d3f79b60d529e84 md5: 0b8c96ed1c04732ff8eb5d481b44c43c @@ -17465,6 +27865,26 @@ packages: run_exports: {} size: 15078490 timestamp: 1779876221509 +- conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda + sha256: 952db1642d707d2572b511b1207bfd8e8c114fdc99930c400b547eb8ab92ac19 + md5: a925cfb1429da2b1312c86516ae7c418 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=2.0.0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 15353018 + timestamp: 1781914001107 - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.0.6-py312h3a88d77_1.conda sha256: 1af8c26dc5507f60e4459975d228e7e329119ec9311ebb88c98603838a9871d2 md5: 4f5c4f3160397a63c7b15c81f6c0e9b3 @@ -17481,6 +27901,22 @@ packages: run_exports: {} size: 534109 timestamp: 1725394700590 +- conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda + sha256: ffc9c4b2fae82b4dfb14fe6d4b95382d9e6d88fd01f8eb9fbf6f7d2bef65ea35 + md5: c5a64ac49215efe79837adbd7e42b605 + depends: + - geos >=3.14.1,<3.14.2.0a0 + - numpy >=1.23,<3 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 614463 + timestamp: 1762523809485 - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.4-h49e36cd_0.conda sha256: 1ce4cffa9a3057b734e08bcde34da3afb0205523f9c6721a30727912b0103633 md5: 89d891f85334c927c5fceb64c4e7ba2e @@ -17492,6 +27928,19 @@ packages: license_family: APACHE size: 374444 timestamp: 1778109163936 +- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.6-h477610d_1.conda + sha256: 8f259ce61c5fe375ccdfe5032da603a36d56a2b52c5b411712a6b1523ea78fb3 + md5: 72a5da10425d23a62418d535332ff755 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: Apache-2.0 + run_exports: + weak: + - simdjson >=4.6.6,<4.7.0a0 + size: 417359 + timestamp: 1786528186117 - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py312he06e257_0.conda sha256: 690fd79162c01ef910763c746d46281a128e73a6b42b5bde6a1d6d20c91edcca md5: ed32da1950de9f6ee034afdeab563b25 @@ -17506,6 +27955,20 @@ packages: run_exports: {} size: 158103 timestamp: 1777112188130 +- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py314h5a2d7ad_0.conda + sha256: 3d32bb1a6d773a2f80693ad2079cce24ec63248a997da872f14ad057da47dde8 + md5: 4f9a825716d3e01b6f3089a68b877e4d + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: {} + size: 161975 + timestamp: 1777112198099 - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda sha256: d2deda1350abf8c05978b73cf7fe9147dd5c7f2f9b312692d1b98e52efad53c3 md5: 3075846de68f942150069d4289aaad63 @@ -17550,6 +28013,21 @@ packages: license_family: MIT size: 174787 timestamp: 1767781882230 +- conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda + sha256: bd14e9166389be0c654e724e16290772b65745aee8d8bb8bacc65d3179330387 + md5: 0d1e29b811d1fea9ba0fe8c869fa5e96 + depends: + - fmt >=12.1.0,<12.2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 174998 + timestamp: 1785924400857 - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.51-py314hc5dbbe4_0.conda sha256: 55e98a0f4e491151ffad7c274d22dfb2d999d50cafa606ec649eff29f079591b md5: 920cf560266c77de21a8f55e7d0e0329 @@ -17565,6 +28043,21 @@ packages: license_family: MIT size: 3986755 timestamp: 1781547909164 +- conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda + sha256: d3ab49c60febcc9edd55ec7f65a9e02b631e137fe609643c590e8151becacca7 + md5: caa67c9335b6f97b708186a6474a306b + depends: + - python + - greenlet !=0.4.17 + - typing-extensions >=4.6.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + run_exports: {} + size: 3998837 + timestamp: 1786535220662 - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda sha256: 700391bf405117eb5c766c4b506f23d3a31f56191ca6619d1b8e5c715376bb7f md5: 7a28cb97617f0bd6a650b4b88f1f44cb @@ -17579,6 +28072,20 @@ packages: - libsqlite >=3.53.3,<4.0a0 size: 426871 timestamp: 1782519137996 +- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_0.conda + sha256: 1883446b33b220ced706e1a90b3e62159402087d5e720a0734b3a47609b5c21f + md5: dafb42fc12203b56f5574658613d256e + depends: + - libsqlite 3.53.4 hf5d6505_0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 426903 + timestamp: 1785016164714 - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda sha256: c31cac57913a699745d124cdc016a63e31c5749f16f60b3202414d071fc50573 md5: 17c38aaf14c640b85c4617ccb59c1146 @@ -17602,6 +28109,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + run_exports: {} size: 156515 timestamp: 1778673901757 - conda: https://conda.anaconda.org/conda-forge/win-64/tiledb-2.26.0-h98a567f_0.conda @@ -17652,6 +28160,19 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3526350 timestamp: 1769460339384 +- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_3.conda + sha256: 13fa29257d43f8e630a1e591ed77fae9bbbb236b011432f01e2034cf36e6bf03 + md5: aaf79e2af50a151fb5b5a3e3f38b7a69 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: TCL + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3782314 + timestamp: 1784229072899 - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda sha256: 110cf0c741e181ed929a2acdb488f2095badad973c50dd696af36c4162e063cf md5: 1045d29f787812d3fac1fd80a1339710 @@ -17679,6 +28200,20 @@ packages: license_family: Apache size: 919275 timestamp: 1781006902968 +- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda + sha256: 1cac25c190b60a4b6fbe2853c4989a4ce086564ee5f995c17d4b32885993068d + md5: 94dfb208db7d79437cfe9b4390706b5c + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 925990 + timestamp: 1786226866739 - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda sha256: 3005729dce6f3d3f5ec91dfc49fc75a0095f9cd23bab49efb899657297ac91a5 md5: 71b24316859acd00bdb8b38f5e2ce328 @@ -17703,6 +28238,20 @@ packages: run_exports: {} size: 405896 timestamp: 1770909182518 +- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda + sha256: 9041e463044944460f73f9528f2ec491180f0ffe857e3555aa8160b81050b8d9 + md5: d6b580a13384df5155c6ca19ee66854e + depends: + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: {} + size: 406126 + timestamp: 1770909191618 - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda sha256: ed0eed8ed0343d29cdbfaeb1bfd141f090af696547d69f91c18f46350299f00d md5: 28b4cf9065681f43cc567410edf8243d @@ -17729,6 +28278,18 @@ packages: run_exports: {} size: 20362 timestamp: 1781320968457 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda + sha256: 35444c55a92e2f7f7ba26bc70f81e56e52344f7d064c0fd4b40a46a58517b79c + md5: aa805b5522c2a98fa286e551a1f48546 + depends: + - vc14_runtime >=14.51.36247 + track_features: + - vc14 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 21383 + timestamp: 1785359368566 - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda sha256: 8153ed849c92e891eacac0f2f8d7ecb79f9b5fd7f7917fbb896f252a60a40390 md5: 06a5bf5a1ca16cce0df6eaa91fc42bc2 @@ -17742,6 +28303,19 @@ packages: run_exports: {} size: 737434 timestamp: 1781320964561 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda + sha256: 4e4cb599cdc41bf2109d1464c127b5bcbddf548ce3e322e612afb691338b48f8 + md5: ac5333bb3d429361f23adf704cc49a78 + depends: + - ucrt >=10.0.20348.0 + - vcomp14 14.51.36247 habf1de7_41 + constrains: + - vs2015_runtime 14.51.36247.* *_41 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + run_exports: {} + size: 767955 + timestamp: 1785359364369 - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda sha256: 07fb14713c4bc62e2533a2e23a363abfb0e65650681fba0ae4c840e2219350f3 md5: 8b53a83fda40ec679e4d63fa32fae989 @@ -17756,6 +28330,20 @@ packages: - vcomp14 >=14.51.36231 size: 120684 timestamp: 1781320948530 +- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda + sha256: 731e043390c9457299484d39e427221fc868a9249540a498a5a4f6456c7744d1 + md5: 350bb67a5c8e5f1c53347ac544ab6600 + depends: + - ucrt >=10.0.20348.0 + constrains: + - vs2015_runtime 14.51.36247.* *_41 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + run_exports: + strong: + - vcomp14 >=14.51.36247 + size: 155910 + timestamp: 1785359349999 - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36231-h84cd919_39.conda sha256: 6de6c2cf008fc2dce61060b583f2d8494c83883106952b201381b6b0505f03d7 md5: 2ccc63d7b7d066a814ed9f99072832d7 @@ -17766,6 +28354,16 @@ packages: run_exports: {} size: 20355 timestamp: 1781320968804 +- conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda + sha256: ee0ae67c96b80b9fdb50c3f617c6329dbcdf1562d0267604f6c0e24f494431d6 + md5: 21504569fa34b5d3a67760219e3ff1cc + depends: + - vc14_runtime >=14.51.36247 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 21386 + timestamp: 1785359368990 - conda: https://conda.anaconda.org/conda-forge/win-64/wrapt-1.17.3-py314h5a2d7ad_1.conda sha256: ecbee7584bc5dfcabed36240059a156dab0d6dd87a0246c71b32b82640558a78 md5: 0172693b00f64c34667a5bdda0449eb9 @@ -17777,6 +28375,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + run_exports: {} size: 63873 timestamp: 1756852097390 - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.2.5-he0c23c2_2.conda @@ -17793,6 +28392,20 @@ packages: - xerces-c >=3.2.5,<3.3.0a0 size: 3574017 timestamp: 1727734520239 +- conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda + sha256: 9583a8fcf01c59b26a4285bc151b6315fd0bd504e1628f004519dc871eb17073 + md5: d1097e01041cfed41c81f1e3d1f52572 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - xerces-c >=3.3.0,<3.4.0a0 + size: 3598939 + timestamp: 1766327729418 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-h0e40799_0.conda sha256: bf1d34142b1bf9b5a4eed96bcc77bc4364c0e191405fd30d2f9b48a04d783fd3 md5: 105cb93a47df9c548e88048dc9cbdbc9 @@ -17805,6 +28418,20 @@ packages: license_family: MIT size: 236306 timestamp: 1734228116846 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda + sha256: 85832f409756e78d6d0a92756cd5463fa253219041e40a681d5e8d4c9d21ec8b + md5: a1629a20e5b128c4512b80a93bb1ae55 + depends: + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - libgcc >=14 + - ucrt >=10.0.20348.0 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + run_exports: + weak: + - xorg-libice >=1.1.2,<2.0a0 + size: 168856 + timestamp: 1786474456144 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-h0e40799_0.conda sha256: 065d49b0d1e6873ed1238e962f56cb8204c585cdc5c9bd4ae2bf385cadb5bd65 md5: 570c9a6d9b4909e45d49e9a5daa528de @@ -17817,6 +28444,20 @@ packages: license_family: MIT size: 97096 timestamp: 1741896840170 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda + sha256: bc043a818313abfd836e4e662df1cc9edd6d81994c87a8b35004ca286cebe7c8 + md5: edb94e6121d977d7bc2cc0f56d656c14 + depends: + - libgcc >=14 + - ucrt >=10.0.20348.0 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + run_exports: + weak: + - xorg-libsm >=1.2.6,<2.0a0 + size: 116838 + timestamp: 1786545424783 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda sha256: eadb12d4597b577cf9bde82a8a2a502a331bd5bfdd60ce508cea93912478e255 md5: 5a823e21e090f8bc43dbfba00cd2f0e2 @@ -17827,6 +28468,9 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libx11 >=1.8.13,<2.0a0 size: 954604 timestamp: 1770819901886 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda @@ -17843,6 +28487,20 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 size: 109246 timestamp: 1762977105140 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + sha256: 892ad69b1a9ecc3903ed5a1b18fa097013eaf462eeed3c6ff31bf5c12e71e84b + md5: 87aee04978ab77bf4c0f42b983c216cc + depends: + - libgcc >=14 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxau >=1.0.12,<2.0a0 + size: 110446 + timestamp: 1786381242485 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda sha256: 366b8ae202c3b48958f0b8784bbfdc37243d3ee1b1cd4b8e76c10abe41fa258b md5: a7c03e38aa9c0e84d41881b9236eacfb @@ -17857,6 +28515,20 @@ packages: - xorg-libxdmcp >=1.1.5,<2.0a0 size: 70691 timestamp: 1762977015220 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda + sha256: 21b11bb98c41ece4ce6069d86d826b50b3d73020fb631b97e59a0521dd9d08a1 + md5: 0e21a45f1bb429b6e35e82631e60554a + depends: + - libgcc >=14 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - xorg-libxdmcp >=1.1.5,<2.0a0 + size: 71362 + timestamp: 1786381239727 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hba3369d_0.conda sha256: 5966dff3ea3f805e11b5fb466107d64704eb94f00d28818f6891a3ecd075d08e md5: 74bc8e26c2716e9b1542bef908887b82 @@ -17867,6 +28539,9 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxext >=1.3.7,<2.0a0 size: 286083 timestamp: 1769445495320 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda @@ -17881,6 +28556,9 @@ packages: - xorg-libxt >=1.3.1,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxpm >=3.5.19,<4.0a0 size: 237565 timestamp: 1776790287445 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-h0e40799_0.conda @@ -17895,6 +28573,9 @@ packages: - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT + run_exports: + weak: + - xorg-libxt >=1.3.1,<2.0a0 size: 918674 timestamp: 1731861024233 - conda: https://conda.anaconda.org/conda-forge/win-64/xz-5.8.3-hb6c8415_0.conda @@ -17944,6 +28625,20 @@ packages: - yaml >=0.2.5,<0.3.0a0 size: 63944 timestamp: 1753484092156 +- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda + sha256: 643e9f439d6446c28e85f30b089e0fafbc937e57b7443f9e66d1dc2ef6bce6ff + md5: 04e7e743edaa756d863aaa3bf6c89aad + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + run_exports: + weak: + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 150033 + timestamp: 1785924043282 - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-he0c23c2_0.conda sha256: 031642d753e0ebd666a76cea399497cc7048ff363edf7d76a630ee0a19e341da md5: 9bb5064a9fca5ca8e7d7f1ae677354b6 @@ -17969,6 +28664,9 @@ packages: - krb5 >=1.22.2,<1.23.0a0 license: MPL-2.0 license_family: MOZILLA + run_exports: + weak: + - zeromq >=4.3.5,<4.3.6.0a0 size: 265717 timestamp: 1779124031378 - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda @@ -17986,6 +28684,21 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 850351 timestamp: 1774072891049 +- conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda + sha256: 5a0b55df66ff07b4342e04967cef69f8bf348f6a0fb1cc1eca741c7b015dfe77 + md5: 945092a9bc1d0f250f7d5ecf51ecd471 + depends: + - libzlib 1.3.2 hfd05255_3 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 851288 + timestamp: 1785276674755 - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda sha256: 71332532332d13b5dbe57074ddcf82ae711bdc132affa5a2982a29ffa06dc234 md5: 46a21c0a4e65f1a135251fc7c8663f83 @@ -18037,6 +28750,7 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + run_exports: {} size: 381179 timestamp: 1762512709971 - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda diff --git a/pixi.toml b/pixi.toml index 44d501d..ebcd924 100644 --- a/pixi.toml +++ b/pixi.toml @@ -42,9 +42,11 @@ pyyaml = ">=6.0.3" [environments] module = { features = ["module"], no-default-feature = true } +test = { features = ["module"] } [tasks] test-integration = {cmd = "pytest tests/integration_test.py"} +test-unit = { cmd = "pytest tests/unit" } [tasks.export-snakemake-env] diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 32822dd..e63e807 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -48,7 +48,7 @@ integration_test: # Explicit country-period instructions for unresolved cases. overrides: - build_ALB_from_GBR_ALB_winter: + build_alb_from_gbr_alb_winter: country: ALB start: "2022-01-01" end: "2022-02-01" @@ -67,7 +67,7 @@ integration_test: end: "2024-02-01" weight: 1 - build_ALB_from_GBR_summer: + build_alb_from_gbr_summer: country: ALB start: "2022-05-01" end: "2022-06-01" @@ -86,7 +86,7 @@ integration_test: end: "2024-06-01" weight: 1 - example_inactive_MNE_override: + example_inactive_mne_override: country: MNE start: "2020-03-01" end: "2020-04-01" diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py index 20eb930..b311ce6 100644 --- a/tests/unit/test_advanced_combine_auxiliary.py +++ b/tests/unit/test_advanced_combine_auxiliary.py @@ -1,5 +1,5 @@ import pandas as pd -from cleaning.advanced.combine_auxiliary import combine_auxiliary_sources +from cleaning.combine_sources import combine_auxiliary_sources def test_combine_auxiliary_sources_aligns_country_columns() -> None: diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index e95bb55..bdbb450 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -352,4 +352,3 @@ def test_match_energy_uses_weighted_target_energy() -> None: ) assert result.sum() == 150.0 - diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index 42c9fa8..5c0625a 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -300,7 +300,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: ], "status": [ "ready", - "not_implemented", + "ready", ], "source_count": [ 2, diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index f2dcfe6..51ef161 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -428,7 +428,7 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: assert row["country"] == "AAA" assert row["gap_start"] == index[0] - assert row["gap_end"] == index[3] + assert row["gap_end"] == index[4] assert row["gap_hours"] == 4 assert bool(row["touches_start_boundary"]) assert not bool(row["touches_end_boundary"]) @@ -499,10 +499,10 @@ def test_build_gap_report_identifies_contiguous_gaps_and_boundaries() -> None: index[0], ], "gap_end": [ - index[0], - index[4], - index[7], - index[7], + index[1], + index[5], + index[7] + pd.Timedelta(hours=1), + index[7] + pd.Timedelta(hours=1), ], "gap_hours": [ 1, diff --git a/workflow/Snakefile b/workflow/Snakefile index 60b8180..2210789 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -35,7 +35,6 @@ workflow.source_path("scripts/common/schemas.py") # Add all your includes here. -#important tha automatic.smk is included first, because clean.smk uses parameters defined within automatic.smk include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/prepare_demand.smk" diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 05d5423..c401f8b 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -411,7 +411,8 @@ properties: description: > Named country-period instructions that override general advanced gap-handling decisions. - + propertyNames: + pattern: '^[a-z0-9_]+$' additionalProperties: $ref: "#/$defs/auxiliary_fill_rule" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 7005ed2..24e5dc6 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -99,24 +99,6 @@ rule download_load_neso_year: "../scripts/download_load_neso.py" -rule download_synthetic_electricity_demand: - output: - csv=( - "/automatic/" - "synthetic_electricity_demand/" - "demand_hourly.csv" - ), - params: - url=..., - md5="a9b59e5a32ad422bcd9e12fea5dc291a", - log: - "/download_synthetic_electricity_demand.log", - conda: - "../envs/module.yaml" - script: - "../scripts/download_synthetic_electricity_demand.py" - - rule download_population: output: population="/automatic/population.zip", diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index f9b221e..12f7acd 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,3 +1,37 @@ +from datetime import datetime, timedelta + +def neso_raw_files(_wildcards): + """Return annual NESO input files for the configured period.""" + start = datetime.fromisoformat( + config["temporal_scope"]["start"] + ) + end = datetime.fromisoformat( + config["temporal_scope"]["end"] + ) + + if end <= start: + raise ValueError( + "Period end must be later than period start." + ) + + final_included_time = end - timedelta( + microseconds=1 + ) + + years = range( + start.year, + final_included_time.year + 1, + ) + + return [ + ( + "/automatic/neso/" + f"historic_demand_{year}.csv" + ) + for year in years + ] + + rule prepare_load_opsd: input: load="/automatic/load_entsoe_opsd.csv", @@ -19,7 +53,7 @@ rule prepare_load_opsd: rule prepare_load_neso: input: - annual_files=NESO_RAW_FILES, + annual_files=neso_raw_files, output: load="/automatic/load_neso.parquet", params: @@ -34,27 +68,6 @@ rule prepare_load_neso: "Prepare electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" - -rule prepare_synthetic_electricity_demand: - input: - csv=rules.download_synthetic_electricity_demand.output.csv, - output: - load=( - "/automatic/" - "load_synthetic_electricity_demand.parquet" - ), - params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe_api"]["countries"], - log: - "/prepare_load_synthetic.log", - conda: - "../envs/module.yaml" - message: - "Prepare electricity-demand data from PyPSA synthetic profile." - script: - "../scripts/prepare_load_synthetic.py" LOAD_SOURCE_PATHS = { diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 8e7cff5..5aabee1 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -7,7 +7,7 @@ rule demand_electricity_raster: output_data="/automatic/{shape}/demand_electricity_raster.tif", output_profiles="/automatic/{shape}/demand_electricity_countries_profiles.parquet", plot_raster="/{shape}/demand_electricity_raster_map.png", - plot_profiles="/{shape}/raw_load_entsoe_profiles.png", + plot_profiles="/{shape}/national_demand_profiles.png", log: "/{shape}/demand_electricity_raster.log", conda: diff --git a/workflow/scripts/cleaning/advanced/combine_auxiliary.py b/workflow/scripts/cleaning/advanced/combine_auxiliary.py deleted file mode 100644 index ef14c55..0000000 --- a/workflow/scripts/cleaning/advanced/combine_auxiliary.py +++ /dev/null @@ -1,52 +0,0 @@ -from collections.abc import Mapping, Sequence - -import pandas as pd - -from cleaning.combine_sources import combine_sources - - -def combine_auxiliary_sources( - loads: Mapping[str, pd.DataFrame], - *, - priority: Sequence[str], -) -> tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, -]: - """Combine available auxiliary sources using configured source priority.""" - if not loads: - empty = pd.DataFrame() - return empty, empty.copy(), empty.copy() - - unexpected_sources = set(loads) - set(priority) - - if unexpected_sources: - raise ValueError( - "Auxiliary sources were supplied but are not configured in " - f"source priority: {sorted(unexpected_sources)}." - ) - - available_priority = [ - source - for source in priority - if source in loads - ] - - columns = sorted( - { - column - for load in loads.values() - for column in load.columns - } - ) - - aligned = { - source: load.reindex(columns=columns) - for source, load in loads.items() - } - - return combine_sources( - aligned, - priority=available_priority, - ) diff --git a/workflow/scripts/cleaning/advanced/gap_report.py b/workflow/scripts/cleaning/advanced/gap_report.py index 315b49d..77f6122 100644 --- a/workflow/scripts/cleaning/advanced/gap_report.py +++ b/workflow/scripts/cleaning/advanced/gap_report.py @@ -58,7 +58,7 @@ def build_gap_report( { "country": country, "gap_start": timestamps[0], - "gap_end": timestamps[-1], + "gap_end": timestamps[-1] + pd.Timedelta(hours=1), "gap_hours": len(timestamps), "touches_start_boundary": ( timestamps[0] == first_timestamp diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py index 0d85ec1..e84f7fe 100644 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py @@ -207,6 +207,12 @@ def construct_from_sources( f"contains {len(target_index)}." ) + if source_values.isna().any(): + raise ValueError( + "Auxiliary source period contains missing values. " + f"Source {country!r}: {start} to {end}." + ) + remapped = pd.Series( source_values.to_numpy(), index=target_index, diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index b4af543..5e4d53e 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -4,22 +4,21 @@ from typing import Any import pandas as pd +from common.time import as_utc_timestamp from cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE +from cleaning.advanced.planning.manifest import EXECUTION_PLAN_VERSION from cleaning.advanced.planning.requirements import ( build_auxiliary_acquisition_requirements, ) -from cleaning.advanced.planning.selection import get_required_auxiliary_group_ids from cleaning.advanced.planning.source_requests import ( build_auxiliary_source_batches, build_auxiliary_source_requests, ) -EXECUTION_PLAN_VERSION = 1 - def build_advanced_execution_plan( *, @@ -70,7 +69,7 @@ def build_advanced_execution_plan( required_group_ids: list[str] = [] if override["method"] == CONSTRUCT_FROM_SOURCES: - required_group_ids = get_required_auxiliary_group_ids( + required_group_ids = _get_required_auxiliary_group_ids( batch_plan, override=override, ) @@ -207,3 +206,54 @@ def _index_batch_ids_by_group( ).append(str(batch["batch_id"])) return result + + + +def _get_required_auxiliary_sources( + override: Mapping, +) -> list[Mapping]: + """Return all auxiliary sources consumed by an override.""" + sources = list(override["sources"]) + + scaling = override.get("scaling") + if scaling is not None: + sources.extend( + scaling.get("target_sources", []) + ) + + return sources + + +def _get_required_auxiliary_group_ids( + plan: Mapping, + *, + override: Mapping, +) -> list[str]: + """Return auxiliary groups required to execute one override.""" + group_ids: set[str] = set() + + for source in _get_required_auxiliary_sources(override): + country = source["country"] + start = as_utc_timestamp(source["start"]) + end = as_utc_timestamp(source["end"]) + + matching_groups = { + batch["group_id"] + for batch in plan["batches"] + if ( + country in batch["countries"] + and as_utc_timestamp(batch["start"]) <= start + and as_utc_timestamp(batch["end"]) >= end + ) + } + + if len(matching_groups) != 1: + raise ValueError( + "Expected exactly one auxiliary group covering " + f"{country!r} from {start} to {end}, " + f"found {sorted(matching_groups)}." + ) + + group_ids.update(matching_groups) + + return sorted(group_ids) diff --git a/workflow/scripts/cleaning/advanced/planning/manifest.py b/workflow/scripts/cleaning/advanced/planning/manifest.py index cbaa704..fa7bc66 100644 --- a/workflow/scripts/cleaning/advanced/planning/manifest.py +++ b/workflow/scripts/cleaning/advanced/planning/manifest.py @@ -7,6 +7,52 @@ from pathlib import Path from typing import Any +EXECUTION_PLAN_VERSION = 1 + +def write_execution_plan( + plan: Mapping[str, Any], + path: str | Path, +) -> None: + """Write one compiled advanced execution plan.""" + path = Path(path) + path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with path.open( + "w", + encoding="utf-8", + ) as file: + json.dump( + plan, + file, + indent=2, + ) + + +def write_advanced_execution_plan( + *, + plan: dict[str, object], + output_path: str | Path, +) -> None: + """Write the advanced execution plan as JSON.""" + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with output_path.open( + "w", + encoding="utf-8", + ) as file: + json.dump( + plan, + file, + indent=2, + ) + def load_execution_plan( path: str | Path, @@ -20,6 +66,14 @@ def load_execution_plan( "Advanced execution plan must contain a JSON object." ) + version = plan.get("version") + + if version != EXECUTION_PLAN_VERSION: + raise ValueError( + "Unsupported advanced execution plan version: " + f"{version!r}. Expected {EXECUTION_PLAN_VERSION}." + ) + return plan diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index f3f4e4b..5d1d4bf 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -10,8 +10,8 @@ ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE -FILL_GAPS_WITHIN_PERIOD = "fill_gaps" -OVERWRITE_ENTIRE_PERIOD = "overwrite" +FILL_GAPS= "fill_gaps" +OVERWRITE = "overwrite" MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" @@ -71,8 +71,8 @@ def validate_auxiliary_fill_rule( ) supported_scopes = { - FILL_GAPS_WITHIN_PERIOD, - OVERWRITE_ENTIRE_PERIOD, + FILL_GAPS, + OVERWRITE, } if scope not in supported_scopes: @@ -118,8 +118,7 @@ def validate_auxiliary_fill_rule( f"{method!r} and must not define 'sources'." ) - # External-profile-specific configuration will be added when - # acquisition of external profiles is implemented. + def _validate_sources( rule: Mapping[str, Any], @@ -421,7 +420,7 @@ def build_auxiliary_fill_plan( if method == CONSTRUCT_FROM_SOURCES: status = "ready" elif method == EXTERNAL_PROFILE: - status = "not_implemented" + status = "ready" elif method == MANUAL_REVIEW: status = "manual_review" elif method == LEAVE_MISSING: diff --git a/workflow/scripts/cleaning/advanced/planning/selection.py b/workflow/scripts/cleaning/advanced/planning/selection.py deleted file mode 100644 index cc7bf3e..0000000 --- a/workflow/scripts/cleaning/advanced/planning/selection.py +++ /dev/null @@ -1,53 +0,0 @@ -from collections.abc import Mapping - -from common.time import as_utc_timestamp - - -def get_required_auxiliary_sources( - override: Mapping, -) -> list[Mapping]: - """Return all auxiliary sources consumed by an override.""" - sources = list(override["sources"]) - - scaling = override.get("scaling") - if scaling is not None: - sources.extend( - scaling.get("target_sources", []) - ) - - return sources - - -def get_required_auxiliary_group_ids( - plan: Mapping, - *, - override: Mapping, -) -> list[str]: - """Return auxiliary groups required to execute one override.""" - group_ids: set[str] = set() - - for source in get_required_auxiliary_sources(override): - country = source["country"] - start = as_utc_timestamp(source["start"]) - end = as_utc_timestamp(source["end"]) - - matching_groups = { - batch["group_id"] - for batch in plan["batches"] - if ( - country in batch["countries"] - and as_utc_timestamp(batch["start"]) <= start - and as_utc_timestamp(batch["end"]) >= end - ) - } - - if len(matching_groups) != 1: - raise ValueError( - "Expected exactly one auxiliary group covering " - f"{country!r} from {start} to {end}, " - f"found {sorted(matching_groups)}." - ) - - group_ids.update(matching_groups) - - return sorted(group_ids) diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py index c1cf6b3..8534c36 100644 --- a/workflow/scripts/cleaning/combine_sources.py +++ b/workflow/scripts/cleaning/combine_sources.py @@ -108,4 +108,51 @@ def _validate_source_alignment( raise ValueError( f"Demand source {source_name!r} does not use the " f"same country columns as {reference_name!r}." - ) \ No newline at end of file + ) + + +def combine_auxiliary_sources( + loads: Mapping[str, pd.DataFrame], + *, + priority: Sequence[str], +) -> tuple[ + pd.DataFrame, + pd.DataFrame, + pd.DataFrame, +]: + """Combine available auxiliary sources using configured source priority.""" + if not loads: + empty = pd.DataFrame() + return empty, empty.copy(), empty.copy() + + unexpected_sources = set(loads) - set(priority) + + if unexpected_sources: + raise ValueError( + "Auxiliary sources were supplied but are not configured in " + f"source priority: {sorted(unexpected_sources)}." + ) + + available_priority = [ + source + for source in priority + if source in loads + ] + + columns = sorted( + { + column + for load in loads.values() + for column in load.columns + } + ) + + aligned = { + source: load.reindex(columns=columns) + for source, load in loads.items() + } + + return combine_sources( + aligned, + priority=available_priority, + ) diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py index d0711b2..72bfeb1 100644 --- a/workflow/scripts/combine_auxiliary_sources.py +++ b/workflow/scripts/combine_auxiliary_sources.py @@ -1,7 +1,7 @@ from pathlib import Path import pandas as pd -from cleaning.advanced.combine_auxiliary import combine_auxiliary_sources +from cleaning.combine_sources import combine_auxiliary_sources def _source_name(path: str | Path) -> str: diff --git a/workflow/scripts/common/schemas.py b/workflow/scripts/common/schemas.py index ef1cdd5..c3b2dae 100644 --- a/workflow/scripts/common/schemas.py +++ b/workflow/scripts/common/schemas.py @@ -2,19 +2,12 @@ # ruff: noqa: UP007 import pandas as pd -import yaml from pandera.pandas import DataFrameModel, Field from pandera.typing.geopandas import GeoSeries from pandera.typing.pandas import Series -def read_yaml(path): - """Read a YAML file.""" - with open(path) as file: - return yaml.safe_load(file) - - -class LoadENTSOE(DataFrameModel): +class OPSDLoad(DataFrameModel): class Config: coerce = True strict = False diff --git a/workflow/scripts/common/time.py b/workflow/scripts/common/time.py index 1212f5f..d1595f6 100644 --- a/workflow/scripts/common/time.py +++ b/workflow/scripts/common/time.py @@ -36,28 +36,3 @@ def build_hourly_index( inclusive="left", name="time", ) - -def years_in_period( - *, - start: object, - end: object, -) -> tuple[int, ...]: - """Return calendar years intersecting an end-exclusive period.""" - start_timestamp = as_utc_timestamp(start) - end_timestamp = as_utc_timestamp(end) - - if end_timestamp <= start_timestamp: - raise ValueError( - "The period end must be later than its start." - ) - - final_included_time = ( - end_timestamp - pd.Timedelta(microseconds=1) - ) - - return tuple( - range( - start_timestamp.year, - final_included_time.year + 1, - ) - ) diff --git a/workflow/scripts/demand_electricity_polygon.py b/workflow/scripts/demand_electricity_polygon.py index 8beccdd..d91f99b 100644 --- a/workflow/scripts/demand_electricity_polygon.py +++ b/workflow/scripts/demand_electricity_polygon.py @@ -51,9 +51,13 @@ def apply_profiles(demand_polygon, shapes, demand_profiles): demand_polygon_covered = demand_polygon.loc[ ~demand_polygon.index.isin(regions_not_covered) ] - warn( - f"Regions {regions_not_covered} are not covered by any demand profile and have been dropped." - ) + if regions_not_covered: + warn( + "Regions " + f"{regions_not_covered} " + "are not covered by any demand profile " + "and have been dropped." + ) # assign profiles to regions demand_profiles_mapped = pd.DataFrame( diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index d3652f8..992af85 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -8,7 +8,6 @@ import pandas as pd import pycountry -import yaml from cleaning.advanced.planning.manifest import get_batch, load_execution_plan from common.time import as_utc_timestamp, build_hourly_index from entsoe.entsoe import EntsoePandasClient @@ -51,12 +50,6 @@ def load_txt(filepath): return data -def load_yaml(path): - """Load a YAML file.""" - with open(path) as file: - return yaml.safe_load(file) - - def download_country( *, country_alpha_3: str, diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index fe9a56a..a17fe8c 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -1,39 +1,15 @@ """Write the compiled advanced electricity-demand execution plan.""" -import json -from pathlib import Path from typing import TYPE_CHECKING, Any import pandas as pd from cleaning.advanced.planning.execution import build_advanced_execution_plan +from cleaning.advanced.planning.manifest import write_execution_plan if TYPE_CHECKING: snakemake: Any -def write_advanced_execution_plan( - *, - plan: dict[str, object], - output_path: str | Path, -) -> None: - """Write the advanced execution plan as JSON.""" - output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) - - with output_path.open( - "w", - encoding="utf-8", - ) as file: - json.dump( - plan, - file, - indent=2, - ) - - if __name__ == "__main__": fill_plan = pd.read_parquet( snakemake.input.fill_plan @@ -45,7 +21,7 @@ def write_advanced_execution_plan( source_names=snakemake.params.source_names, ) - write_advanced_execution_plan( - plan=plan, - output_path=snakemake.output.plan, + write_execution_plan( + plan, + snakemake.output.plan, ) diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index da34a43..13dc062 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -7,7 +7,7 @@ import pandas as pd import pycountry from cleaning.advanced.planning.manifest import get_batch, load_execution_plan -from common.schemas import LoadENTSOE +from common.schemas import OPSDLoad from common.time import as_utc_timestamp, build_hourly_index if TYPE_CHECKING: @@ -43,7 +43,7 @@ def main( ): """Prepare OPSD demand for the configured scope.""" load = pd.read_csv(path_raw_load) - load = LoadENTSOE.validate(load) + load = OPSDLoad.validate(load) load = load.loc[load["variable"] == "load"] load = load.loc[ From 86ae72c37add4465ec3dc0750485af2b17327748 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 12 Aug 2026 19:42:07 +0200 Subject: [PATCH 070/241] Added proper resource path for the external profiles --- .../integration/inputs/external_profiles/alb_external_test.csv | 3 +++ tests/integration/test_config.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/integration/inputs/external_profiles/alb_external_test.csv diff --git a/tests/integration/inputs/external_profiles/alb_external_test.csv b/tests/integration/inputs/external_profiles/alb_external_test.csv new file mode 100644 index 0000000..5bf6621 --- /dev/null +++ b/tests/integration/inputs/external_profiles/alb_external_test.csv @@ -0,0 +1,3 @@ +timestamp,demand +2022-01-01T00:00:00Z,98765.0 +2022-01-01T02:00:00Z,87654.0 diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index e63e807..40b2511 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -104,4 +104,4 @@ integration_test: end: "2022-01-08 00:00" scope: overwrite method: external_profile - path: resources/external_profiles/alb_external_test.csv \ No newline at end of file + path: inputs/external_profiles/alb_external_test.csv \ No newline at end of file From 479daf4d1c451cd074c8cdaeb9a8d61f74bd8fca Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 10:49:23 +0200 Subject: [PATCH 071/241] Updated README --- README.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 361e946..236b3b8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # European electricity demand -This module prepares electricity demand timeseries for Europe at arbitrary resolution, based on ENTSO-E historical load data. +This module prepares hourly electricity-demand time series for European regions at arbitrary spatial resolution. National demand data from multiple sources are combined and cleaned before being spatially disaggregated using population data and aggregated to user-provided target regions. + +The module supports configurable gap filling, including deterministic cleaning rules and advanced country- and period-specific strategies, whilst retaining provenance for observed and filled demand values. +

@@ -19,15 +22,44 @@ and the `snakemake` [documentation](https://snakemake.readthedocs.io/en/stable/s ## Overview -Data processing steps: - -- Download ENTSO-E historical load profiles. -- Download a gridded population dataset that serves as a disaggregation proxy. -- Filter and clean the raw load profile data. -- Clip the population raster to the ENTSO-E area. -- Disaggregate the national annual load to raster, using population as weight. -- Re-aggregate the annual load raster data to the target shapes. -- Assign the corresponding national load profile to each region to get the final load profiles for the target shapes. +The workflow first builds a cleaned national electricity-demand time series and then spatially distributes that demand to the user-provided target regions. + +The main processing stages are: + +1. Download electricity-demand data from the configured sources. Current sources include: ENTSO-E, OPSD, and NESO. +2. Combine the available demand sources according to their configured priority. +3. Apply basic gap-filling rules to gaps that can be resolved using deterministic rules. +4. In advanced mode, identify remaining gaps and apply explicitly configured country- and period-specific strategies. +5. Finalise the cleaned national demand time series and retain provenance for observed and filled values. +6. Download and prepare gridded population data as a spatial disaggregation proxy. +7. Disaggregate national demand to a population-weighted raster. +8. Re-aggregate the raster to the target shapes and assign the corresponding national demand profile to each region. + +A simplified representation of the workflow is: + +```text +Demand sources + │ + ▼ +Combine sources + │ + ▼ +Basic gap filling + │ + ├── basic mode ──────────────────────┐ + │ │ + └── advanced mode ─► Advanced rules ─┤ + ▼ + Final national demand + + provenance + │ + ▼ + Population-weighted + spatial disaggregation + │ + ▼ + Regional hourly demand +``` ## Configuration @@ -37,8 +69,57 @@ Please consult the configuration [README](./config/README.md) and the [configura ## Input / output structure +The module requires user-provided target shapes and, when ENTSO-E data is used, a valid ENTSO-E API token. Optional external electricity-demand profiles can also be supplied for use in advanced gap-filling rules. + +Intermediate data, including downloaded demand sources, cleaned national demand, provenance information, and auxiliary gap-filling data, are stored under `resources/automatic/`. + +The final output contains hourly electricity demand in MW for the requested target regions and is written to the module results directory. + +The workflow also produces diagnostic outputs, including a gap report describing unresolved missing periods and a cleaning timeline showing how observed and filled values contribute to the final national demand series. See [Provenance and diagnostics](#provenance-and-diagnostics) for more information. + Please consult the [interface file](./INTERFACE.yaml) for more information. +## Cleaning and Gap Handling + +After the available electricity-demand sources have been combined, the resulting national time series are checked for missing values and cleaned according to the configured gap-filling mode. + +Three modes are available: + +- `off`: no gap filling is applied. +- `basic`: deterministic gap-filling rules are applied in the configured order. +- `advanced`: basic gap filling is applied first, after which remaining gaps can be handled using explicitly configured country- and period-specific rules. + +### Basic gap filling + +Basic gap filling is intended for gaps that can be resolved using simple and reproducible rules, such as interpolation or copying values from a comparable period. + +Rules are applied sequentially in the order in which they are configured. Values filled by an earlier rule are therefore available to subsequent rules. + +Any gaps that remain after basic cleaning are retained rather than filled automatically with increasingly speculative values. These unresolved periods are reported in the [gap report](#provenance-and-diagnostics). + +### Advanced gap filling + +Advanced mode provides explicit strategies for gaps that cannot be resolved appropriately using the basic rules. Advanced rules are defined for a specific country and time period and can use one of the following methods: + +- `construct_from_sources`: construct a demand profile from one or more alternative country or time-period sources. +- `external_profile`: use a user-provided electricity-demand profile. +- `leave_missing`: explicitly retain the remaining gap. + +Advanced rules can either fill only missing values (`fill_gaps`) or replace all supplied values within the configured period (`overwrite`). + +Configured time periods follow a half-open interval convention, `[start, end)`: the start timestamp is included and the end timestamp is excluded. + +## Provenance and diagnostics + +The workflow retains provenance information throughout the cleaning process so that observed electricity-demand values can be distinguished from values introduced by basic or advanced gap-filling rules. + +Two diagnostic outputs are particularly useful when assessing data quality and configuring gap handling: + +- **Gap report**: lists periods that remain unresolved after cleaning, including the affected country and time interval. This can be used to identify where additional advanced rules or external data may be required. +- **Cleaning timeline**: visualises the origin and cleaning method of demand values over time, making it easier to inspect source coverage, basic fills, advanced overrides, and remaining gaps. + +These diagnostics are intended to support transparent gap handling rather than hide missing data behind automatic imputation. + ## Development @@ -87,6 +168,7 @@ This module is based on the following research and datasets: * ENTSOE Transparency Platform (https://transparency.entsoe.eu) * Open Power System Data (https://data.open-power-system-data.org) +* NESO Data Portal (https://www.neso.energy/data-portal/historic-demand-data) * Schiavina M., Freire S., Carioli A., MacManus K. (2023): GHS-POP R2023A - GHS population grid multitemporal (1975-2030).European Commission, Joint Research Centre (JRC) PID: http://data.europa.eu/89h/2ff68a52-5b5b-4a22-8f40-c41da8332cfe, doi:10.2905/2FF68A52-5B5B-4A22-8F40-C41DA8332CFE From 80a6f64784aeaba388cde89058a39d95462cee17 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 10:49:49 +0200 Subject: [PATCH 072/241] Removed TUR from ENTSO-E Grab given the non-existent data --- tests/integration/test_config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 40b2511..67e6320 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,7 +1,7 @@ integration_test: temporal_scope: - start: "2022-01-01" - end: "2025-01-01" + start: "2016-01-01" + end: "2026-01-01" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api @@ -10,7 +10,7 @@ integration_test: gap_filling: # Sets the data-cleaning mode. Options: off, basic, advanced. - mode: advanced + mode: basic basic: # Basic gap-filling rules are applied in the listed order. From 9cce016ae9a7a24610999b7029fd47d9cf80d166 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 10:50:11 +0200 Subject: [PATCH 073/241] Removed TURKEY from ENTSO-E Download given lack of data. --- workflow/internal/settings.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index dea6c37..85b873d 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -43,5 +43,5 @@ load_entsoe_api: - "SWE" - "SVN" - "SVK" - - "TUR" + # - "TUR" #Data is unavailable for Turkey MAX_WORKERS: 3 \ No newline at end of file From 049922eb32364c082070d623db09e0c3b0b2ac10 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 11:06:32 +0200 Subject: [PATCH 074/241] Removed manual_review as an advanced method. --- tests/unit/test_advanced_apply.py | 17 ----------------- tests/unit/test_advanced_requirements.py | 2 +- workflow/internal/config.schema.yaml | 13 ------------- workflow/scripts/cleaning/advanced/apply.py | 7 ------- .../scripts/cleaning/advanced/planning/plan.py | 6 ------ 5 files changed, 1 insertion(+), 44 deletions(-) diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 7311b20..6867e35 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -150,23 +150,6 @@ def test_construct_from_sources_overwrites_entire_period() -> None: ] -def test_manual_review_cannot_be_applied_automatically() -> None: - rule = { - "method": "manual_review", - } - - with pytest.raises( - ValueError, - match="requires manual review", - ): - apply_auxiliary_fill_rule( - _load(), - _cleaning_method(), - rule_name="review_albania", - rule=rule, - ) - - def test_leave_missing_returns_unchanged_copies() -> None: load = _load() cleaning_method = _cleaning_method() diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index 938f3dd..55c9936 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -132,7 +132,7 @@ def test_compile_auxiliary_requirements_deduplicates_sources() -> None: def test_compile_auxiliary_requirements_ignores_other_methods() -> None: overrides = { "manual_case": { - "method": "manual_review", + "method": "leave_missing", }, "leave_case": { "method": "leave_missing", diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index c401f8b..950f17a 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -181,18 +181,6 @@ $defs: unevaluatedProperties: false - manual_review_rule: - allOf: - - $ref: "#/$defs/auxiliary_fill_rule_base" - - - type: object - properties: - method: - const: manual_review - - unevaluatedProperties: false - - leave_missing_rule: allOf: - $ref: "#/$defs/auxiliary_fill_rule_base" @@ -208,7 +196,6 @@ $defs: oneOf: - $ref: "#/$defs/construct_from_sources_rule" - $ref: "#/$defs/external_profile_rule" - - $ref: "#/$defs/manual_review_rule" - $ref: "#/$defs/leave_missing_rule" auxiliary_basic_cleaning: diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 0c381bf..74f98b8 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -11,7 +11,6 @@ ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE -MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" @@ -62,12 +61,6 @@ def apply_auxiliary_fill_rule( rule_name=rule_name, ) - if method == MANUAL_REVIEW: - raise ValueError( - f"Advanced-fill rule {rule_name!r} requires manual " - "review and cannot be applied automatically." - ) - if method == LEAVE_MISSING: return load.copy(), cleaning_method.copy() diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 5d1d4bf..1399395 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -12,8 +12,6 @@ FILL_GAPS= "fill_gaps" OVERWRITE = "overwrite" - -MANUAL_REVIEW = "manual_review" LEAVE_MISSING = "leave_missing" @@ -85,7 +83,6 @@ def validate_auxiliary_fill_rule( supported_methods = { CONSTRUCT_FROM_SOURCES, EXTERNAL_PROFILE, - MANUAL_REVIEW, LEAVE_MISSING, } @@ -109,7 +106,6 @@ def validate_auxiliary_fill_rule( ) elif method in { - MANUAL_REVIEW, LEAVE_MISSING, }: if "sources" in rule: @@ -421,8 +417,6 @@ def build_auxiliary_fill_plan( status = "ready" elif method == EXTERNAL_PROFILE: status = "ready" - elif method == MANUAL_REVIEW: - status = "manual_review" elif method == LEAVE_MISSING: status = "leave_missing" else: From fdf57c76a1be5faf318c36d47528cf5f0ab1b3df Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 12:34:57 +0200 Subject: [PATCH 075/241] Updated config README and INTERFACE.yaml --- INTERFACE.yaml | 20 ++- config/README.md | 426 ++++++++++++++++++++++++++++++++++++++++++++- config/config.yaml | 78 +++++++-- 3 files changed, 500 insertions(+), 24 deletions(-) diff --git a/INTERFACE.yaml b/INTERFACE.yaml index 3d6773b..229d494 100644 --- a/INTERFACE.yaml +++ b/INTERFACE.yaml @@ -5,26 +5,30 @@ pathvars: snakemake_defaults: logs: default: "" - description: location of snakemake log files. + description: "Location of Snakemake log files." resources: default: "" - description: "location of module resource (input) files." + description: "Location of module resource (input) files." results: default: "" - description: "location of module result (output) files." + description: "Location of module result (output) files." + user_resources: token_entsoe: default: "/user/token_entsoe.txt" description: "User token for ENTSO-E API access." + shapes: default: "/user/{shape}/shapes.parquet" description: > - Shapes to aggregate the output data to. - IMPORTANT: they must add up to the full spatial extent of the country. - The module's processing requires it. + Target shapes used to spatially aggregate electricity demand. + The supplied shapes must collectively cover the full spatial extent + of the corresponding country. + results: demand_electricity_MW: default: "/{shape}/demand_electricity_MW.parquet" - description: Electricity demand profile per custom shape in units of MW. + description: "Hourly electricity demand per custom shape, in MW." + wildcards: - shape: Name of the custom shape is a wildcard to allow multiple outputs based on different shapes. + shape: "Name of the custom target shape set. This wildcard allows multiple outputs for different shape definitions." \ No newline at end of file diff --git a/config/README.md b/config/README.md index 720820c..806262d 100644 --- a/config/README.md +++ b/config/README.md @@ -1,8 +1,422 @@ -We recommend consulting the following before using this module: -- `config/config.yaml`: a generic example configuration of this module. -- `workflow/internal/config.schema.yaml`: a schematic overview of all the configuration options of this module. -- `INTERFACE.yaml`: lists module input and output files, and their default locations. -- `tests/integration/Snakefile`: an example of how to call this module from another workflow. +# Configuration + +This module is configured through `config/config.yaml`. + +We recommend consulting the following alongside this file: + +- [`config/config.yaml`](./config.yaml): example configuration for this module. +- [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml): complete schema defining valid configuration options. +- [`INTERFACE.yaml`](../INTERFACE.yaml): module input and output files and their default locations. +- [`tests/integration/Snakefile`](../tests/integration/Snakefile): example of how to import and call this module from another workflow. This data module is part of the [Modelblocks](https://www.modelblocks.org/) project. -Please consult the [Modelblocks documentation](https://modelblocks.readthedocs.io/) for more details. +Please consult the [Modelblocks documentation](https://modelblocks.readthedocs.io/) for more information. + +## Temporal scope + +The requested electricity-demand period is configured using `temporal_scope`. + +```yaml +temporal_scope: + start: "2016-01-01" + end: "2026-01-01" +``` + +## Demand sources + +The load_sources setting defines which demand datasets are used and their priority order. + +```yaml +load_sources: + - entsoe_api + - neso + - opsd_api +``` + +Sources are combined in the order listed. Where more than one source provides a value for the same country and timestamp, the higher-priority source is retained. + +Available sources are: + +- `entsoe_api`: ENTSO-E Transparency Platform data +- `neso`: historical demand data from the National Energy System Operator for Great Britain. +- `opsd_api`: historical demand data from Open Power System Data. + +## Gap filling +Gap handling is configured under `gap_filling`. + +```yaml +gap_filling: + mode: basic +``` + +### Modes +Three modes are available: + +- `off`: do not apply gap filling. +- `basic`: apply deterministic gap-filling rules in the configured order. +- `advanced`: apply basic gap filling first, then process remaining gaps using explicitly configured advanced rules. + + +## Basic gap filling +Basic rules are listed under `gap_filling.basic.rules` and are applied sequentially in the order in which they appear. + +```yaml +gap_filling: + mode: basic + + basic: + rules: + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + + - name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h + + - name: copy_following_week + method: copy_period + max_gap: 168h + source_offset: 168h +``` +Each rule must have a unique `name`. The rule name is retained in the cleaning provenance and diagnostic plots, and should therefore be descriptive. + +### linear_interpolation + +Interpolates across missing periods up to the configured `max_gap`. + +```yaml +- name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h +``` + +### average_periods + +Fills a gap using the average of one or more periods offset from the missing interval. + +```yaml +- name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d +``` + +In this example, the value from the same hour one week earlier and one week later is averaged. + +### copy_period + +Copies values from a period offset from the missing interval. + +```yaml +- name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h +``` +A positive offset copies from a later period; a negative offset copies from an earlier period. + +## Advanced gap filling +Advanced mode is intended for gaps that cannot be resolved appropriately using the deterministic basic rules. + +Advanced rules are defined under: + +```yaml +gap_filling: + mode: advanced + + advanced: + auxiliary_data: + overrides: + + example_override_rule_name: + ... +``` + +Each override targets a specific country and time period and defines: + +- `country`: ISO3 country code. +- `start`: start of the target period. +- `end`: end of the target period. +- `scope`: whether to fill only missing values or overwrite supplied values. +- `method`: the advanced strategy to apply. + +Override names must be unique and use lowercase letters, numbers, and underscores. + +### Active and inactive overrides + +Advanced overrides may be kept in the configuration even when they are not relevant to the current model run. + +An override is **active** when its target country and time period overlap with the demand being processed. Only active overrides are included in the advanced gap-filling workflow. Overrides that fall outside the current target countries or `temporal_scope` are considered **inactive** and are not executed. + +This allows a configuration to maintain a reusable collection of known gap-handling rules across countries and time periods. For example, a project may keep established overrides for Albania, Cyprus, and North Macedonia in the same configuration while running a model instance that only requires Albania. The rules for the other countries remain available but do not trigger unnecessary auxiliary data acquisition or processing. + +In the example below, if the current run covers Albania in 2022 but not Montenegro in 2020, fill_alb_2022 is active while fill_mne_2020 remains inactive. + +```yaml +advanced: + overrides: + fill_alb_2022: + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + method: construct_from_sources + ... + + fill_mne_2020: + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps + method: construct_from_sources + ... +``` + +> [!IMPORTANT] +> All configured overrides must still be valid according to the configuration schema. An inactive rule is ignored because it is outside the current model scope, not because invalid configuration is tolerated. + + +### Scope + +Two scopes are supported: + +- `fill_gaps`: only missing target values are replaced. +- `overwrite`: all values supplied by the advanced rule within the configured target period are replaced. Note, this method forces existing values to be overwritten. + +### Method +Three methods are currently supported: +- `construct_from_sources`: builds a synthetic profile from one or more alternative country-period sources using a weighted-average method configured by the user. +- `external_profile`: reads a profile from a user-provided CSV file. +- `leave_missing`: explicitly leaves the specified period unresolved. + +### Timestamps + +All configured timestamps refer to the module's hourly UTC time index. + +For YAML configuration, timestamps may be written in one of the following forms: + +```yaml +start: "2022-01-01" +end: "2022-02-01" +``` +or, when an hour must be specified explicitly: + +```yaml +start: "2022-01-01 00:00" +end: "2022-02-01 00:00" +``` + +A date without an explicit time represents 00:00 at the start of that date. + +Configured periods follow a half-open interval convention, [start, end): the start timestamp is included and the end timestamp is excluded. + +> [!Important] +> Naive timestamps in the configuration are interpreted consistently with the module's UTC hourly time index; external profiles should use ISO 8601 timestamps and are therefore recommended to be supplied explicitly in UTC using `Z`. See [Advanced Method: `external_profile`](#advanced-method-external_profile). + +## Advanced Method: `construct_from_sources` + +`construct_from_sources` builds a synthetic profile from one or more alternative country-period sources using a weighted-average method configured by the user. + +```yaml +example_rule_construct_from_sources: + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + method: construct_from_sources + + sources: + - country: GRC + start: "2022-01-01" + end: "2022-02-01" + weight: 1 + - country: MKD + start: "2022-01-01" + end: "2022-02-01" + weight: 3 + + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-01-01" + end: "2024-02-01" + weight: 1 +``` + +The source period must describe the same number of hourly timestamps as the target period. + +Multiple sources may be supplied. Their weight values determine their relative contribution to the constructed profile. + +> [!IMPORTANT] +> Weighting is relative and values provided are normalised such that all weights sum to 1, i.e. in the example above, `GRC` has a relative contribution of `0.25` and `MKD` has a relative contribution of `0.75`. + +> [!NOTE] +> The combination of `scope: overwrite` and providing a single source, effectively provides a broad copy-paste function. + +### Scaling + +A constructed profile can optionally be scaled before it is applied. This is helpful where sources are from other countries with different average energy consumptions. + +The currently supported scaling method is: + +```yaml +scaling: + method: match_energy +``` + +`match_energy` scales the constructed profile so that its total energy matches the weighted energy of the configured target_sources. + +This allows the temporal shape of one country or period to be used while matching the overall demand level of a more representative target period. + +## Advanced Method: `external_profile` +`external_profile` applies demand values supplied in a user-provided .CSV file. + +```yaml +external_alb_profile: + country: ALB + start: "2022-01-01 00:00" + end: "2022-01-08 00:00" + scope: overwrite + method: external_profile + path: inputs/external_profiles/alb_external.csv +``` + +External profiles must contain exactly two columns and use ISO 8601 timestamps with an explicit UTC designator: + +```csv +timestamp,demand +2022-01-01T00:00:00Z,723.0 +2022-01-01T01:00:00Z,716.0 +``` + +Requirements: + +- timestamp must contain parseable hourly timestamps. +- timestamps must be unique. +- timestamps must be aligned to whole hours. +- demand must be numeric and non-missing. +- sparse profiles are allowed. + +Only timestamps present in both the external profile and the configured target period are applied. + +## Advanced Method: `leave_missing` + +`leave_missing` explicitly accepts that a target period remains unresolved. + +```yaml +leave_alb_gap: + country: ALB + start: "2016-01-01" + end: "2016-02-01" + scope: fill_gaps + method: leave_missing +``` + +No replacement values are generated. The missing period remains visible in the cleaned demand series and diagnostic outputs. + +## Auxiliary Data +Advanced rules that construct profiles from other countries or periods may require additional demand data outside the main requested target period. + +Auxiliary acquisition is configured under: +```yaml +advanced: + auxiliary_data: + basic_cleaning: + enabled: true +``` +When enabled, the same basic gap-filling logic is applied to auxiliary demand before it is used to construct an advanced profile. + + +## Complete example + +```yaml +temporal_scope: + start: "2020-01-01" + end: "2025-01-01" + +load_sources: + - entsoe_api + - neso + - opsd_api + +gap_filling: + mode: advanced + + basic: + rules: + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + + - name: copy_previous_week + method: copy_period + max_gap: 168h + source_offset: -168h + + advanced: + auxiliary_data: + basic_cleaning: + enabled: true + + overrides: + + # This rule constructs a synthetic profile from GRC + # and MKD Jan 2022, rescales to ALB Jan 2024 average + # energy levels, and fill gaps in ALB Jan 2022. It + # does not overwrite existing values. + build_alb_winter: + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + method: construct_from_sources + sources: + - country: GRC + start: "2022-01-01" + end: "2022-02-01" + weight: 1 + - country: MKD + start: "2022-01-01" + end: "2022-02-01" + weight: 3 + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-01-01" + end: "2024-02-01" + weight: 1 + + # This rule overwrites ALB 2021 using an external + # profile, including any values that do exist from + # the original ENTSO-E/NESO/OPSD download. + external_alb_profile: + country: ALB + start: "2021-01-01 00:00" + end: "2022-01-01 00:00" + scope: overwrite + method: external_profile + path: inputs/external_profiles/alb_external.csv + + # This rule intentionally leaves missing values. + # This rule is inactive because its target period lies + # outside the configured temporal_scope. + leave_alb_gap: + country: ALB + start: "2016-01-01" + end: "2017-01-01" + scope: fill_gaps + method: leave_missing +``` \ No newline at end of file diff --git a/config/config.yaml b/config/config.yaml index fab6b20..f781128 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,20 +1,19 @@ # A minimal example of how to configure this module temporal_scope: - start: "2017-01-01" - end: "2018-01-01" - -# Provide a list of sources in priority order. -# Options: entsoe_api, opsd_api -load_sources: + start: "2016-01-01" + end: "2026-01-01" +# provide a list of sources and set their priority order. +# Options include: entsoe_api, neso, opsd_api +load_sources: - entsoe_api - neso - opsd_api -# Gap-filling rules are applied in the listed order. gap_filling: # Sets the data-cleaning mode. Options: off, basic, advanced. - mode: advanced - + mode: basic + + # Basic gap-filling configuration. basic: # Basic gap-filling rules are applied in the listed order. rules: @@ -43,10 +42,69 @@ gap_filling: max_gap: 168h source_offset: 168h + # Advanced gap-filling configuration. advanced: auxiliary_data: basic_cleaning: enabled: true # Explicit country-period instructions for unresolved cases. - overrides: {} \ No newline at end of file + overrides: + + build_alb_from_gbr_alb_winter: + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + method: construct_from_sources + sources: + - country: GBR + start: "2024-01-01" + end: "2024-02-01" + weight: 1 + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-01-01" + end: "2024-02-01" + weight: 1 + + build_alb_from_gbr_summer: + country: ALB + start: "2022-05-01" + end: "2022-06-01" + scope: fill_gaps + method: construct_from_sources + sources: + - country: GBR + start: "2022-05-01" + end: "2022-06-01" + weight: 1 + scaling: + method: match_energy + target_sources: + - country: ALB + start: "2024-05-01" + end: "2024-06-01" + weight: 1 + + example_inactive_mne_override: + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps + method: construct_from_sources + sources: + - country: SRB + start: "2022-03-01" + end: "2022-04-01" + weight: 1 + + test_external_profile_overwrite: + country: ALB + start: "2022-01-01 00:00" + end: "2022-01-08 00:00" + scope: overwrite + method: external_profile + path: inputs/external_profiles/alb_external_test.csv \ No newline at end of file From 0349a1900a66bf2f2770ecb0d41cd4e6610566f0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 13:29:31 +0200 Subject: [PATCH 076/241] Updated README with new illustrative figure --- README.md | 6 +- figures/readme_cleaning_timeline.png | Bin 0 -> 640451 bytes .../scripts/plot_readme_cleaning_example.py | 236 ++++++++++++++++++ 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 figures/readme_cleaning_timeline.png create mode 100644 workflow/scripts/plot_readme_cleaning_example.py diff --git a/README.md b/README.md index 236b3b8..349faa1 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,12 @@ The module supports configurable gap filling, including deterministic cleaning r + +

+ +

- + Example diagnostic showing electricity-demand provenance and basic gap filling for selected countries (May–October 2021).

## About diff --git a/figures/readme_cleaning_timeline.png b/figures/readme_cleaning_timeline.png new file mode 100644 index 0000000000000000000000000000000000000000..812eb4d1d7b80f762bb37a37a2f86eaf26642715 GIT binary patch literal 640451 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 z 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

22s9}BnOmFtVlMRfj{c2Jf)?v5>f9-{6 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

X{{%KPC4*g>XvPvA^cX0BuNiX)%qjY+9T!T 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%0D=?`W5RY&$K2@N~BUUjATatl+YwR??gye5# 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

@@ -24,105 +21,144 @@ the [integration example](./tests/integration/Snakefile), and the `snakemake` [documentation](https://snakemake.readthedocs.io/en/stable/snakefiles/modularization.html). ## Overview - -The workflow first builds a cleaned national electricity-demand time series and then spatially distributes that demand to the user-provided target regions. +The workflow first prepares a cleaned national electricity-demand time series on the configured time grid and then spatially distributes that demand to the requested target regions. The main processing stages are: -1. Download electricity-demand data from the configured sources. Current sources include: ENTSO-E, OPSD, and NESO. -2. Combine the available demand sources according to their configured priority. -3. Apply basic gap-filling rules to gaps that can be resolved using deterministic rules. -4. In advanced mode, identify remaining gaps and apply explicitly configured country- and period-specific strategies. -5. Finalise the cleaned national demand time series and retain provenance for observed and filled values. -6. Download and prepare gridded population data as a spatial disaggregation proxy. -7. Disaggregate national demand to a population-weighted raster. -8. Re-aggregate the raster to the target shapes and assign the corresponding national demand profile to each region. +1. Download demand data from the configured providers: ENTSO-E, NESO, and/or OPSD. +2. Prepare each provider dataset on the configured time grid. +3. Combine available providers according to the configured source-priority order. +4. Apply deterministic basic cleaning rules. +5. In `advanced` mode, determine which configured advanced rules are active for the current countries and time grid, acquire any required auxiliary demand data, construct or read advanced profiles, and apply them. +6. Finalise national demand together with cleaning provenance. +7. Download and prepare gridded population data. +8. Spatially disaggregate national demand using population weights and aggregate it to the user-provided target shapes. -A simplified representation of the workflow is: +A simplified representation is: ```text -Demand sources +Demand providers │ ▼ -Combine sources +Prepare and combine │ ▼ -Basic gap filling +Basic cleaning │ - ├── basic mode ──────────────────────┐ - │ │ - └── advanced mode ─► Advanced rules ─┤ - ▼ - Final national demand - + provenance - │ - ▼ - Population-weighted - spatial disaggregation - │ - ▼ - Regional hourly demand + ├── off/basic ────────────────────────────┐ + │ │ + └── advanced ─► plan auxiliary data │ + │ │ + ▼ │ + acquire / prepare │ + │ │ + ▼ │ + advanced rules ────────────────┤ + ▼ + Final national demand + + provenance + │ + ▼ + Population-weighted + spatial disaggregation + │ + ▼ + Regional demand ``` ## Configuration - -Please consult the configuration [README](./config/README.md) and the [configuration example](./config/config.yaml) for a general overview on the configuration options of this module. +The module is configured through `config/config.yaml`. -## Input / output structure - +The key configuration groups are: -The module requires user-provided target shapes and, when ENTSO-E data is used, a valid ENTSO-E API token. Optional external electricity-demand profiles can also be supplied for use in advanced gap-filling rules. +- `temporal_scope`: grid start, grid end, and fixed frequency; +- `load_sources`: demand-provider priority; +- `gap_filling`: cleaning mode plus basic and advanced rules. -Intermediate data, including downloaded demand sources, cleaned national demand, provenance information, and auxiliary gap-filling data, are stored under `resources/automatic/`. +See the [configuration README](./config/README.md), the [example configuration](./config/config.yaml), and the authoritative [configuration schema](./workflow/internal/config.schema.yaml). -The final output contains hourly electricity demand in MW for the requested target regions and is written to the module results directory. +## Time grid -The workflow also produces diagnostic outputs, including a gap report describing unresolved missing periods and a cleaning timeline showing how observed and filled values contribute to the final national demand series. See [Provenance and diagnostics](#provenance-and-diagnostics) for more information. +All national demand cleaning is performed on an explicit regular time grid defined by: -Please consult the [interface file](./INTERFACE.yaml) for more information. +```yaml +temporal_scope: + start: "2017-01-01" + end: "2017-01-03" + frequency: "1h" +``` -## Cleaning and Gap Handling +`start` is inclusive and `end` is exclusive. The difference between `start` and `end` must be an integer multiple of `frequency`. -After the available electricity-demand sources have been combined, the resulting national time series are checked for missing values and cleaned according to the configured gap-filling mode. +The configured start timestamp also defines the phase of the grid. Provider and auxiliary timestamps used by the workflow must align with that phase. -Three modes are available: +## Demand sources -- `off`: no gap filling is applied. -- `basic`: deterministic gap-filling rules are applied in the configured order. -- `advanced`: basic gap filling is applied first, after which remaining gaps can be handled using explicitly configured country- and period-specific rules. +`load_sources` defines both the providers to use and their priority order: -### Basic gap filling +```yaml +load_sources: + - entsoe + - neso + - opsd +``` + +Where multiple providers supply a value for the same country and timestamp, the earlier provider in this list has priority. + +Available provider identifiers are: -Basic gap filling is intended for gaps that can be resolved using simple and reproducible rules, such as interpolation or copying values from a comparable period. +- `entsoe`: ENTSO-E Transparency Platform; +- `neso`: National Energy System Operator historic demand; +- `opsd`: Open Power System Data. -Rules are applied sequentially in the order in which they are configured. Values filled by an earlier rule are therefore available to subsequent rules. +## Cleaning and gap handling + +Three modes are available: -Any gaps that remain after basic cleaning are retained rather than filled automatically with increasingly speculative values. These unresolved periods are reported in the [gap report](#provenance-and-diagnostics). +- `off`: do not fill gaps; +- `basic`: apply configured deterministic rules; +- `advanced`: run basic cleaning first, then execute active advanced rules. -### Advanced gap filling +Basic rules are applied sequentially in configuration order. Supported basic methods include `linear_interpolation`, `average_periods`, and `copy_periods`. -Advanced mode provides explicit strategies for gaps that cannot be resolved appropriately using the basic rules. Advanced rules are defined for a specific country and time period and can use one of the following methods: +Advanced configuration separates reusable **sources** from target **rules**. A source describes how an advanced profile is obtained, for example by constructing it from one or more country-period source profiles or reading an external CSV. A rule states where that source should be applied. -- `construct_from_sources`: construct a demand profile from one or more alternative country or time-period sources. -- `external_profile`: use a user-provided electricity-demand profile. -- `leave_missing`: explicitly retain the remaining gap. +An advanced rule is active when both its target country and target period are relevant to the current model run. Rules outside the requested countries or `temporal_scope` remain valid configuration but do not trigger unnecessary auxiliary-data acquisition. -Advanced rules can either fill only missing values (`fill_gaps`) or replace all supplied values within the configured period (`overwrite`). +Advanced rule scopes are: -Configured time periods follow a half-open interval convention, `[start, end)`: the start timestamp is included and the end timestamp is excluded. +- `fill_gaps`: use the advanced profile only where target values are missing; +- `overwrite`: replace target values throughout the configured rule period. + +Configured periods use half-open intervals, `[start, end)`. + +See [Configuration: Advanced gap filling](./config/README.md#advanced-gap-filling) for full examples. ## Provenance and diagnostics -The workflow retains provenance information throughout the cleaning process so that observed electricity-demand values can be distinguished from values introduced by basic or advanced gap-filling rules. +The workflow retains cleaning provenance alongside national demand so observed values can be distinguished from values introduced by basic or advanced rules. + +Important diagnostic outputs include: + +- **Gap report**: unresolved missing periods after cleaning; +- **Cleaning method**: the source or rule responsible for each output value; +- **Cleaning-method rank**: numeric ordering used to represent cleaning provenance consistently; +- **Cleaning timeline**: a visual diagnostic of demand and provenance through time. + +These diagnostics are intended to make gap handling explicit rather than conceal unresolved data behind automatic imputation. + +## Input / output structure + +The module requires user-provided target shapes. A valid ENTSO-E API token is additionally required when `entsoe` is configured. + +Advanced `external_profile` sources may reference user-provided CSV files. -Two diagnostic outputs are particularly useful when assessing data quality and configuring gap handling: +Intermediate provider data, cleaned national demand, provenance, execution plans, and auxiliary data are stored below the module resources path. Final regional electricity demand is written to the configured module results path. -- **Gap report**: lists periods that remain unresolved after cleaning, including the affected country and time interval. This can be used to identify where additional advanced rules or external data may be required. -- **Cleaning timeline**: visualises the origin and cleaning method of demand values over time, making it easier to inspect source coverage, basic fills, advanced overrides, and remaining gaps. +Please consult [`INTERFACE.yaml`](./INTERFACE.yaml) for the module's formal input/output interface. -These diagnostics are intended to support transparent gap handling rather than hide missing data behind automatic imputation. ## Development diff --git a/config/README.md b/config/README.md index 7f7bd76..6d9c717 100644 --- a/config/README.md +++ b/config/README.md @@ -2,63 +2,76 @@ This module is configured through `config/config.yaml`. -We recommend consulting the following alongside this file: +The configuration schema is intentionally strict: malformed or unsupported configuration should fail validation rather than silently falling back to defaults. -- [`config/config.yaml`](./config.yaml): example configuration for this module. -- [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml): complete schema defining valid configuration options. -- [`INTERFACE.yaml`](../INTERFACE.yaml): module input and output files and their default locations. -- [`tests/integration/Snakefile`](../tests/integration/Snakefile): example of how to import and call this module from another workflow. +Useful references are: -This data module is part of the [Modelblocks](https://www.modelblocks.org/) project. -Please consult the [Modelblocks documentation](https://modelblocks.readthedocs.io/) for more information. +- [`config/config.yaml`](./config.yaml): example configuration; +- [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml): authoritative configuration schema; +- [`INTERFACE.yaml`](../INTERFACE.yaml): module input/output interface; +- [`tests/integration/test_config.yaml`](../tests/integration/test_config.yaml): a richer integration configuration; +- [`tests/integration/Snakefile`](../tests/integration/Snakefile): example module import. ## Temporal scope -The requested electricity-demand period is configured using `temporal_scope`. +`temporal_scope` defines the regular target time grid used for demand cleaning. ```yaml temporal_scope: - start: "2016-01-01" - end: "2026-01-01" + start: "2017-01-01" + end: "2017-01-03" + frequency: "1h" ``` +The grid follows a half-open interval: + +```text +[start, end) +``` + +so `start` is included and `end` is excluded. + +The period length must be an integer multiple of `frequency`. The `start` timestamp also anchors the grid phase, so timestamps used by provider data, auxiliary data, and external profiles must align with the configured grid. + +Date-only timestamps represent midnight. Date-time strings may be used when the grid needs a non-midnight start or another explicit offset. + ## Demand sources -The load_sources setting defines which demand datasets are used and their priority order. +`load_sources` selects the national demand providers and defines their priority. ```yaml load_sources: - - entsoe_api + - entsoe - neso - - opsd_api + - opsd ``` -Sources are combined in the order listed. Where more than one source provides a value for the same country and timestamp, the higher-priority source is retained. +Available identifiers are: -Available sources are: +- `entsoe`: ENTSO-E Transparency Platform; +- `neso`: National Energy System Operator historic demand; +- `opsd`: Open Power System Data. -- `entsoe_api`: ENTSO-E Transparency Platform data -- `neso`: historical demand data from the National Energy System Operator for Great Britain. -- `opsd_api`: historical demand data from Open Power System Data. +Sources are combined in the listed order. When more than one provider supplies a value for the same country and timestamp, the higher-priority provider is retained. ## Gap filling -Gap handling is configured under `gap_filling`. + +Gap handling is configured below `gap_filling`. ```yaml gap_filling: mode: basic ``` -### Modes Three modes are available: -- `off`: do not apply gap filling. -- `basic`: apply deterministic gap-filling rules in the configured order. -- `advanced`: apply basic gap filling first, then process remaining gaps using explicitly configured advanced rules. - +- `off`: no gap filling; +- `basic`: apply deterministic basic rules in configured order; +- `advanced`: run basic cleaning first and then apply configured advanced rules that are active for the current target countries and time grid. ## Basic gap filling -Basic rules are listed under `gap_filling.basic.rules` and are applied sequentially in the order in which they appear. + +Basic rules are listed under `gap_filling.basic.rules`. ```yaml gap_filling: @@ -78,20 +91,25 @@ gap_filling: - 7d - name: copy_previous_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: -168h + require_complete_source: true - name: copy_following_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: 168h + require_complete_source: true ``` -Each rule must have a unique `name`. The rule name is retained in the cleaning provenance and diagnostic plots, and should therefore be descriptive. -### linear_interpolation +Rules are applied sequentially. Values filled by an earlier rule are therefore available to later rules. + +Each rule requires a unique, descriptive `name`. Rule names are retained in cleaning provenance and diagnostic outputs. -Interpolates across missing periods up to the configured `max_gap`. +### `linear_interpolation` + +Interpolates across missing periods up to `max_gap`. ```yaml - name: interpolate_short_gaps @@ -99,9 +117,9 @@ Interpolates across missing periods up to the configured `max_gap`. max_gap: 3h ``` -### average_periods +### `average_periods` -Fills a gap using the average of one or more periods offset from the missing interval. +Uses the mean of corresponding values from one or more offset periods. ```yaml - name: average_adjacent_weeks @@ -112,244 +130,218 @@ Fills a gap using the average of one or more periods offset from the missing int - 7d ``` -In this example, the value from the same hour one week earlier and one week later is averaged. +In this example, corresponding values one week before and one week after the gap are averaged. -### copy_period +### `copy_periods` -Copies values from a period offset from the missing interval. +Copies corresponding values from a configured offset period. ```yaml - name: copy_previous_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: -168h + require_complete_source: true ``` -A positive offset copies from a later period; a negative offset copies from an earlier period. + +A negative `source_offset` uses an earlier period; a positive offset uses a later period. + +`require_complete_source: true` requires the source period needed for the copy to be complete before that rule can fill the target gap. ## Advanced gap filling -Advanced mode is intended for gaps that cannot be resolved appropriately using the deterministic basic rules. -Advanced rules are defined under: +Advanced mode separates two concepts: + +1. **sources** describe how an advanced profile is obtained; +2. **rules** describe the target country, period, scope, and source to apply. + +This keeps reusable source definitions separate from their application. + +The overall structure is: ```yaml gap_filling: mode: advanced + basic: + rules: [...] + advanced: auxiliary_data: - overrides: - - example_override_rule_name: - ... -``` - -Each override targets a specific country and time period and defines: - -- `country`: ISO3 country code. -- `start`: start of the target period. -- `end`: end of the target period. -- `scope`: whether to fill only missing values or overwrite supplied values. -- `method`: the advanced strategy to apply. - -Override names must be unique and use lowercase letters, numbers, and underscores. + basic_cleaning: + enabled: true -### Active and inactive overrides + sources: + example_source: + method: construct_from_sources + periods: [...] -Advanced overrides may be kept in the configuration even when they are not relevant to the current model run. + rules: + - name: example_rule + country: ALB + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite + source: example_source +``` -An override is **active** when its target country and time period overlap with the demand being processed. Only active overrides are included in the advanced gap-filling workflow. Overrides that fall outside the current target countries or `temporal_scope` are considered **inactive** and are not executed. +### Active and inactive rules -This allows a configuration to maintain a reusable collection of known gap-handling rules across countries and time periods. For example, a project may keep established overrides for Albania, Cyprus, and North Macedonia in the same configuration while running a model instance that only requires Albania. The rules for the other countries remain available but do not trigger unnecessary auxiliary data acquisition or processing. +Advanced rules may remain in a reusable configuration even when they do not apply to a particular run. -In the example below, if the current run covers Albania in 2022 but not Montenegro in 2020, fill_alb_2022 is active while fill_mne_2020 remains inactive. +A rule is **active** when: -```yaml -advanced: - overrides: - fill_alb_2022: - country: ALB - start: "2022-01-01" - end: "2022-02-01" - scope: fill_gaps - method: construct_from_sources - ... +- its target country is part of the demand being processed; and +- its target interval overlaps the configured target time grid. - fill_mne_2020: - country: MNE - start: "2020-03-01" - end: "2020-04-01" - scope: fill_gaps - method: construct_from_sources - ... -``` +A rule outside the current countries or time grid is **inactive** and does not trigger auxiliary-data acquisition or profile construction. -> [!IMPORTANT] -> All configured overrides must still be valid according to the configuration schema. An inactive rule is ignored because it is outside the current model scope, not because invalid configuration is tolerated. +Activity is determined from target scope, not from whether a matching gap happens to remain after basic cleaning. In particular, a `fill_gaps` rule can be active even when there is ultimately nothing for it to fill. +All configured rules must still be valid according to the schema. Inactivity does not make invalid configuration acceptable. -### Scope +### Rule scopes Two scopes are supported: -- `fill_gaps`: only missing target values are replaced. -- `overwrite`: all values supplied by the advanced rule within the configured target period are replaced. Note, this method forces existing values to be overwritten. - -### Method -Three methods are currently supported: -- `construct_from_sources`: builds a synthetic profile from one or more alternative country-period sources using a weighted-average method configured by the user. -- `external_profile`: reads a profile from a user-provided CSV file. -- `leave_missing`: explicitly leaves the specified period unresolved. - -### Timestamps - -All configured timestamps refer to the module's hourly UTC time index. +- `fill_gaps`: replace missing target values only; +- `overwrite`: replace target values throughout the rule period. -For YAML configuration, timestamps may be written in one of the following forms: +### Advanced periods -```yaml -start: "2022-01-01" -end: "2022-02-01" -``` -or, when an hour must be specified explicitly: +Advanced target and source periods follow the same half-open convention as the model grid: -```yaml -start: "2022-01-01 00:00" -end: "2022-02-01 00:00" +```text +[start, end) ``` -A date without an explicit time represents 00:00 at the start of that date. - -Configured periods follow a half-open interval convention, [start, end): the start timestamp is included and the end timestamp is excluded. +Source periods used to construct a target profile must have the temporal length required by the target construction. -> [!Important] -> Naive timestamps in the configuration are interpreted consistently with the module's UTC hourly time index; external profiles should use ISO 8601 timestamps and are therefore recommended to be supplied explicitly in UTC using `Z`. See [Advanced Method: `external_profile`](#advanced-method-external_profile). +## Advanced source: `construct_from_sources` -## Advanced Method: `construct_from_sources` - -`construct_from_sources` builds a synthetic profile from one or more alternative country-period sources using a weighted-average method configured by the user. +A `construct_from_sources` source builds a profile from one or more configured country-period profiles. ```yaml -example_rule_construct_from_sources: - country: ALB - start: "2022-01-01" - end: "2022-02-01" - scope: fill_gaps - method: construct_from_sources - +advanced: sources: - - country: GRC - start: "2022-01-01" - end: "2022-02-01" - weight: 1 - - country: MKD - start: "2022-01-01" - end: "2022-02-01" - weight: 3 - - scaling: - method: match_energy - target_sources: - - country: ALB - start: "2024-01-01" - end: "2024-02-01" - weight: 1 + alb_from_gbr_alb_winter: + method: construct_from_sources + periods: + - country: GBR + start: "2024-01-01" + end: "2024-02-01" + weight: 1 + + scaling: + method: match_energy + periods: + - country: ALB + start: "2024-01-01" + end: "2024-02-01" + weight: 1 ``` -The source period must describe the same number of hourly timestamps as the target period. +Each `periods` entry identifies: -Multiple sources may be supplied. Their weight values determine their relative contribution to the constructed profile. +- `country`; +- `start`; +- `end`; +- `weight`. -> [!IMPORTANT] -> Weighting is relative and values provided are normalised such that all weights sum to 1, i.e. in the example above, `GRC` has a relative contribution of `0.25` and `MKD` has a relative contribution of `0.75`. +Weights are relative contributions and must be finite and positive. -> [!NOTE] -> The combination of `scope: overwrite` and providing a single source, effectively provides a broad copy-paste function. +Multiple source periods can be combined. Their relative weights determine their contribution to the constructed profile. -### Scaling +### Scaling constructed profiles -A constructed profile can optionally be scaled before it is applied. This is helpful where sources are from other countries with different average energy consumptions. +A constructed profile may optionally be rescaled. -The currently supported scaling method is: +The currently configured scaling strategy is: ```yaml scaling: method: match_energy + periods: + - country: ALB + start: "2024-01-01" + end: "2024-02-01" + weight: 1 ``` -`match_energy` scales the constructed profile so that its total energy matches the weighted energy of the configured target_sources. +`match_energy` uses the configured scaling periods to align the overall energy level of the constructed profile with a more representative reference. + +Scaling periods can require auxiliary demand data outside the main target grid; the workflow includes them when compiling acquisition requirements. -This allows the temporal shape of one country or period to be used while matching the overall demand level of a more representative target period. +## Advanced source: `external_profile` -## Advanced Method: `external_profile` -`external_profile` applies demand values supplied in a user-provided .CSV file. +An `external_profile` source reads a user-supplied CSV. ```yaml -external_alb_profile: - country: ALB - start: "2022-01-01 00:00" - end: "2022-01-08 00:00" - scope: overwrite - method: external_profile - path: inputs/external_profiles/alb_external.csv +advanced: + sources: + alb_external: + method: external_profile + file: inputs/external_profiles/alb_external.csv ``` -External profiles must contain exactly two columns and use ISO 8601 timestamps with an explicit UTC designator: +The target country, period, and application scope belong to the **rule**, not to the source: + +```yaml +advanced: + rules: + - name: use_alb_external_profile + country: ALB + start: "2022-01-01 00:00" + end: "2022-01-08 00:00" + scope: overwrite + source: alb_external +``` + +External profile CSVs use the generic T-Clean column contract: ```csv -timestamp,demand +timestamp,value 2022-01-01T00:00:00Z,723.0 2022-01-01T01:00:00Z,716.0 ``` -Requirements: - -- timestamp must contain parseable hourly timestamps. -- timestamps must be unique. -- timestamps must be aligned to whole hours. -- demand must be numeric and non-missing. -- sparse profiles are allowed. +The profile must use valid, unique timestamps aligned with the target grid, and numeric non-missing values. Sparse profiles are permitted where supported by the configured rule/application behavior. -Only timestamps present in both the external profile and the configured target period are applied. +## Explicitly leaving values missing -## Advanced Method: `leave_missing` +Advanced configuration can explicitly retain unresolved values rather than fabricate a profile. This is useful when missing data is known and accepted. -`leave_missing` explicitly accepts that a target period remains unresolved. +Consult the authoritative configuration schema for the exact `leave_missing` source/rule form supported by the current module version. -```yaml -leave_alb_gap: - country: ALB - start: "2016-01-01" - end: "2016-02-01" - scope: fill_gaps - method: leave_missing -``` +## Auxiliary data -No replacement values are generated. The missing period remains visible in the cleaned demand series and diagnostic outputs. +Advanced constructed profiles can require demand observations from countries or periods outside the main target grid. -## Auxiliary Data -Advanced rules that construct profiles from other countries or periods may require additional demand data outside the main requested target period. +Auxiliary behavior is configured under: -Auxiliary acquisition is configured under: ```yaml advanced: auxiliary_data: basic_cleaning: enabled: true ``` -When enabled, the same basic gap-filling logic is applied to auxiliary demand before it is used to construct an advanced profile. +When enabled, the configured basic cleaning rules are also applied to auxiliary demand before it is used in advanced profile construction. + +The module determines auxiliary acquisition requirements only for **active** advanced rules. Provider acquisition and preparation remain Modelblocks responsibilities; generic planning and cleaning behavior is delegated to T-Clean. ## Complete example ```yaml temporal_scope: - start: "2020-01-01" - end: "2025-01-01" + start: "2017-01-01" + end: "2017-01-03" + frequency: "1h" load_sources: - - entsoe_api + - entsoe - neso - - opsd_api + - opsd gap_filling: mode: advanced @@ -360,63 +352,88 @@ gap_filling: method: linear_interpolation max_gap: 3h + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + - name: copy_previous_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: -168h + require_complete_source: true + + - name: copy_following_week + method: copy_periods + max_gap: 168h + source_offset: 168h + require_complete_source: true advanced: auxiliary_data: basic_cleaning: enabled: true - overrides: - - # This rule constructs a synthetic profile from GRC - # and MKD Jan 2022, rescales to ALB Jan 2024 average - # energy levels, and fill gaps in ALB Jan 2022. It - # does not overwrite existing values. - build_alb_winter: - country: ALB - start: "2022-01-01" - end: "2022-02-01" - scope: fill_gaps + sources: + alb_from_alb_2024: method: construct_from_sources - sources: - - country: GRC - start: "2022-01-01" - end: "2022-02-01" - weight: 1 - - country: MKD - start: "2022-01-01" - end: "2022-02-01" - weight: 3 - scaling: - method: match_energy - target_sources: + periods: - country: ALB start: "2024-01-01" - end: "2024-02-01" + end: "2024-01-03" weight: 1 + scaling: + method: match_energy + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-03" + weight: 1 - # This rule overwrites ALB 2021 using an external - # profile, including any values that do exist from - # the original ENTSO-E/NESO/OPSD download. - external_alb_profile: - country: ALB - start: "2021-01-01 00:00" - end: "2022-01-01 00:00" - scope: overwrite + alb_external: method: external_profile - path: inputs/external_profiles/alb_external.csv + file: inputs/external_profiles/alb_external.csv - # This rule intentionally leaves missing values. - # This rule is inactive because its target period lies - # outside the configured temporal_scope. - leave_alb_gap: + mne_from_srb: + method: construct_from_sources + periods: + - country: SRB + start: "2022-03-01" + end: "2022-04-01" + weight: 1 + + rules: + - name: overwrite_alb_from_alb_2024 country: ALB - start: "2016-01-01" - end: "2017-01-01" + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024 + + - name: example_inactive_mne_rule + country: MNE + start: "2020-03-01" + end: "2020-04-01" scope: fill_gaps - method: leave_missing + source: mne_from_srb ``` + +In this example: + +- the target grid is hourly from 1 January to 3 January 2017; +- the Albania rule is active for an Albania target and overwrites the requested 2017 period with a profile constructed from 2024 data; +- the Montenegro rule is outside the target period and is therefore inactive; +- auxiliary acquisition is planned only where active advanced sources require it. + +## Validation + +Configuration is checked in two layers: + +1. the YAML schema checks structure, permitted values, required fields, and basic types; +2. semantic validation checks constraints that depend on relationships between fields, such as time-grid alignment, unique rule/source names, valid source references, and compatible advanced periods. + +Invalid configuration should be corrected at source rather than handled through silent fallbacks. + +For the complete accepted configuration contract, refer to [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml). From d088981c79f95a2c81f687ab80ed6f40c65dbf79 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 13:41:51 +0200 Subject: [PATCH 122/241] Removed the redundant readme plot function. --- .../scripts/plot_readme_cleaning_example.py | 161 ------------------ 1 file changed, 161 deletions(-) delete mode 100644 workflow/scripts/plot_readme_cleaning_example.py diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py deleted file mode 100644 index 436bbad..0000000 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Create a curated cleaning-timeline figure for the README. - -Temporary development utility. Run from tests/integration/. -""" - -from pathlib import Path - -import matplotlib.pyplot as plt -import pandas as pd -from _plot_timeline import ( - _add_mean_load_labels, - _add_normalised_demand_traces, - _build_cleaning_method_metadata, - _build_legend_handles, - _build_rank_colours, - _encode_rank_background, - _plot_cleaning_background, - _validate_alignment, -) - -DEMAND_PATH = Path("resources/module/resources/automatic/load_cleaned.parquet") - -CLEANING_METHOD_PATH = Path( - "resources/module/resources/automatic/load_final_cleaning_method.parquet" -) - -CLEANING_METHOD_RANK_PATH = Path( - "resources/module/resources/automatic/load_final_cleaning_method_rank.parquet" -) - -OUTPUT_PATH = Path("tmp/readme_cleaning_timeline.png") - -# Curated subset for the README figure. -COUNTRIES = ["ALB", "GBR", "IRL", "MKD", "DEU"] - -START = "2021-05-01" -END = "2021-11-01" - -# Must correspond to the configuration used for the long run. -SOURCE_NAMES = ["entsoe", "neso", "opsd"] - -GAP_FILLING_CONFIG = { - "mode": "basic", - "basic": { - "rules": [ - { - "name": "interpolate_short_gaps", - "method": "linear_interpolation", - "max_gap": "3h", - }, - { - "name": "average_adjacent_weeks", - "method": "average_periods", - "max_gap": "326h", - "source_offsets": ["-7d", "7d"], - }, - { - "name": "copy_previous_week", - "method": "copy_periods", - "max_gap": "168h", - "source_offset": "-168h", - }, - { - "name": "copy_following_week", - "method": "copy_periods", - "max_gap": "168h", - "source_offset": "168h", - }, - ] - }, -} - - -def main() -> None: - """Main function for generating README figure.""" - demand = pd.read_parquet(DEMAND_PATH) - cleaning_method = pd.read_parquet(CLEANING_METHOD_PATH) - cleaning_method_rank = pd.read_parquet(CLEANING_METHOD_RANK_PATH) - - print("Available period:", demand.index.min(), "to", demand.index.max()) - print("Available countries:", ", ".join(demand.columns)) - - missing_countries = [ - country for country in COUNTRIES if country not in demand.columns - ] - - if missing_countries: - raise ValueError( - "Requested README countries are unavailable: " - + ", ".join(missing_countries) - ) - - start = pd.Timestamp(START, tz="UTC") - end = pd.Timestamp(END, tz="UTC") - - mask = (demand.index >= start) & (demand.index < end) - - demand = demand.loc[mask, COUNTRIES] - cleaning_method = cleaning_method.loc[mask, COUNTRIES] - cleaning_method_rank = cleaning_method_rank.loc[mask, COUNTRIES] - - if len(demand) < 2: - raise ValueError(f"No usable data found between {START} and {END}.") - - print(f"Plotting {len(demand):,} hourly timestamps for {len(COUNTRIES)} countries.") - - # Useful while choosing the README window/countries. - print("\nCleaning-method counts:") - for country in COUNTRIES: - counts = cleaning_method[country].value_counts(dropna=False) - print(f"\n{country}") - print(counts.to_string()) - - _validate_alignment( - demand=demand, - cleaning_method=cleaning_method, - cleaning_method_rank=cleaning_method_rank, - ) - - metadata = _build_cleaning_method_metadata( - source_names=SOURCE_NAMES, gap_filling_config=GAP_FILLING_CONFIG - ) - - rank_colours = _build_rank_colours(metadata) - - background, background_cmap = _encode_rank_background( - cleaning_method_rank=cleaning_method_rank, - metadata=metadata, - rank_colours=rank_colours, - ) - - figure, axis = _plot_cleaning_background( - demand=demand, background=background, background_cmap=background_cmap - ) - - mean_load_gw = _add_normalised_demand_traces(axis=axis, demand=demand) - - _add_mean_load_labels( - axis=axis, mean_load_gw=mean_load_gw, countries=demand.columns - ) - - legend_handles = _build_legend_handles(metadata, rank_colours) - - figure.legend( - handles=legend_handles, - loc="center left", - bbox_to_anchor=(1.01, 0.5), - frameon=False, - ) - - OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) - - figure.savefig(OUTPUT_PATH, dpi=200, bbox_inches="tight") - - plt.close(figure) - - print(f"\nSaved README figure to: {OUTPUT_PATH.resolve()}") - - -if __name__ == "__main__": - main() From d4514a7f7a175537b4a1a7bc331516aa573cf34a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 14:21:53 +0200 Subject: [PATCH 123/241] Align test environment with module runtime --- README.md | 2 + config/README.md | 100 +- pixi.lock | 2469 ++-------------------------------------------- pixi.toml | 13 +- 4 files changed, 79 insertions(+), 2505 deletions(-) diff --git a/README.md b/README.md index 478bf48..8d12ae2 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ pixi install --all Please be aware that this is a multi-environment project (see [pixi.toml](./pixi.toml) for details). - `default`: used for development and integration testing. Because it contains `Snakemake`, `conda` and `pytest` as dependencies it **should not be used** in `Snakemake` rules. +- `test`: used for unit testing. It combines the `module` environment with test-only dependencies such as `pytest`. - `module`: contains minimal dependencies used in `Snakemake` rules. If modified, be sure to export it to `Snakemake` so it can be recreated by module users: @@ -190,6 +191,7 @@ pixi run export-snakemake-env module For testing, simply run: ```shell +pixi run test-unit pixi run test-integration ``` diff --git a/config/README.md b/config/README.md index 6d9c717..20bbc2f 100644 --- a/config/README.md +++ b/config/README.md @@ -330,102 +330,14 @@ When enabled, the configured basic cleaning rules are also applied to auxiliary The module determines auxiliary acquisition requirements only for **active** advanced rules. Provider acquisition and preparation remain Modelblocks responsibilities; generic planning and cleaning behavior is delegated to T-Clean. -## Complete example +## End-to-end tested example -```yaml -temporal_scope: - start: "2017-01-01" - end: "2017-01-03" - frequency: "1h" - -load_sources: - - entsoe - - neso - - opsd - -gap_filling: - mode: advanced - - basic: - rules: - - name: interpolate_short_gaps - method: linear_interpolation - max_gap: 3h - - - name: average_adjacent_weeks - method: average_periods - max_gap: 326h - source_offsets: - - -7d - - 7d - - - name: copy_previous_week - method: copy_periods - max_gap: 168h - source_offset: -168h - require_complete_source: true - - - name: copy_following_week - method: copy_periods - max_gap: 168h - source_offset: 168h - require_complete_source: true - - advanced: - auxiliary_data: - basic_cleaning: - enabled: true - - sources: - alb_from_alb_2024: - method: construct_from_sources - periods: - - country: ALB - start: "2024-01-01" - end: "2024-01-03" - weight: 1 - scaling: - method: match_energy - periods: - - country: ALB - start: "2024-01-01" - end: "2024-01-03" - weight: 1 - - alb_external: - method: external_profile - file: inputs/external_profiles/alb_external.csv - - mne_from_srb: - method: construct_from_sources - periods: - - country: SRB - start: "2022-03-01" - end: "2022-04-01" - weight: 1 - - rules: - - name: overwrite_alb_from_alb_2024 - country: ALB - start: "2017-01-01" - end: "2017-01-03" - scope: overwrite - source: alb_from_alb_2024 - - - name: example_inactive_mne_rule - country: MNE - start: "2020-03-01" - end: "2020-04-01" - scope: fill_gaps - source: mne_from_srb -``` - -In this example: +For a complete configuration used by the integration workflow, see +[`tests/integration/test_config.yaml`](../tests/integration/test_config.yaml). -- the target grid is hourly from 1 January to 3 January 2017; -- the Albania rule is active for an Albania target and overwrites the requested 2017 period with a profile constructed from 2024 data; -- the Montenegro rule is outside the target period and is therefore inactive; -- auxiliary acquisition is planned only where active advanced sources require it. +This configuration is exercised by the integration test suite and therefore +serves as the canonical end-to-end example. The examples above are intentionally +focused on individual configuration features. ## Validation diff --git a/pixi.lock b/pixi.lock index c4f192e..fc9e3f7 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1706,20 +1706,8 @@ environments: - https://pypi.org/simple packages: linux-64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda @@ -1745,47 +1733,26 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h77c0f9c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cgl-0.60.10-hc46dffc_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.0-py314h9e666f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py314h97ea11e_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py314haf003cf_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py314h5bd0f2a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py314hc02f841_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py314hbcf5174_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.7-h2b0a6b4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h4916ab3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.13.1-hfc2019e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py314h97ea11e_0.conda @@ -1823,7 +1790,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-hd345f60_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda @@ -1845,14 +1811,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-h174a0a3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-9_h6ae95b6_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda @@ -1868,10 +1829,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.3-h4c96295_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h9463b59_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda @@ -1897,11 +1855,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py314h815e797_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py314h9a9c090_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hb71707f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h97ea11e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.0-py314h518bba1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314h2b28147_0.conda @@ -1910,7 +1866,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py314h8ec4b1a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda @@ -1918,47 +1873,32 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py314hdafbbf9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py314h969be7f_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py314hbcf5174_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py314h7ce3bca_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py314h3987850_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-h242f9ac_102_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py314h91cf270_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-h54a6638_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-h50b1954_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h1bee95f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314h0f05182_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.2-h462bb3b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py314hf07bd8e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py314hbe3edd8_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.6-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py314h5bd0f2a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h04a0ce9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py314h5bd0f2a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-h1964d1d_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda @@ -1978,71 +1918,37 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -2051,74 +1957,38 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyha191276_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda @@ -2127,103 +1997,48 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ../../tclean osx-arm64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -2232,74 +2047,38 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh01cf8df_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda @@ -2308,43 +2087,25 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda @@ -2370,44 +2131,22 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314hd58f0ed_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cgl-0.60.10-h034796e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.0-py314hcec6336_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.7-h4e57454_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-hd20048c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-hd03a632_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.13.1-hf76c51c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.3.0-hce30654_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py314hf8a3a22_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda @@ -2437,7 +2176,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-h21e278e_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda @@ -2446,7 +2184,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.3.0-h5a65909_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda @@ -2454,13 +2191,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-h934fa54_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-9_h1b118fd_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda @@ -2471,10 +2203,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.3-he8aa2a2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h7d962ec_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda @@ -2494,11 +2223,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-hdb7fadc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314hdf3942c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.0-py314h2fbedac_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314hb79c6fa_0.conda @@ -2506,7 +2233,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda @@ -2514,115 +2240,60 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py314h54f3292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314ha82c7e6_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-hf4d206d_102_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h784d473_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hcf2a89d_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314he1d1ac0_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314ha14b1ff_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.2-h828de30_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.6-h3feff0a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-h77b7338_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ../../tclean win-64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -2631,72 +2302,38 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh6dadd2b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyhe2676ad_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda @@ -2706,43 +2343,26 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/throttler-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.14.2-hfd05255_0.conda @@ -2768,40 +2388,23 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cgl-0.60.10-h7c5f3e6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.0-py314hb821551_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/glib-2.88.3-h89924da_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/glib-tools-2.88.3-hf027272_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.13.1-h11686cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/graphviz-14.1.2-h4c50273_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.3.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py314hf309875_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda @@ -2828,7 +2431,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.1.0-h110b43a_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.1.0-h8ee18e1_1.conda @@ -2847,11 +2449,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-9_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda @@ -2861,8 +2459,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda @@ -2885,19 +2481,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h0ffbb96_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_234.conda - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314hf309875_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.0-py314h13f4da2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-hd2b5f0e_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda @@ -2905,40 +2498,24 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py314h9f07db2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.1-py314h447aaf0_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h53f6dd8_102_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hcaaf0b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.1-pl5321hfcac499_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-h03e4b45_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314hc980628_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.2-h6b72154_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.6-h477610d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_3.conda @@ -2950,22 +2527,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/wrapt-1.17.3-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hba3369d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-h0e40799_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: ../../tclean packages: @@ -2982,24 +2549,6 @@ packages: license_family: MIT size: 47149 timestamp: 1781674515403 -- conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 - md5: e9f9d8d88a22521378dace6b7399d59b - depends: - - black >=26.3.1,<27.0 - - click >=8.2.0,<9.0 - - go-shfmt >=3.13,<4.0 - - pathspec >=1.0.0 - - python >=3.11,<4.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/snakefmt?source=hash-mapping - run_exports: - weak: - - snakefmt >=2.0.3,<2.1.0a0 - size: 47262 - timestamp: 1784295220199 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda sha256: 1e8393a8ef060c62ef963fa9e0b3cc8f69b1dec4bdb51599c0dfcdeb9b8c7d12 md5: a5ec344abd4b0cdda5a52e870e4d25a3 @@ -3135,51 +2684,6 @@ packages: license_family: MIT size: 888303 timestamp: 1781821379807 -- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.25.1-pyhdfd78af_0.conda - sha256: 643957e2ec7dc2c7d77e4c5e9b89621d264b14d4b2cd83a23c0e645e5b004182 - md5: d639ba3846ef46c86a57bed676115deb - depends: - - conda-inject >=1.3.1,<2.0 - - configargparse - - connection_pool >=0.0.3 - - docutils >=0.20,<0.23 - - dpath >=2.1.6,<3.0.0 - - gitpython - - humanfriendly - - immutables - - jinja2 >=3.0,<4.0 - - jsonschema - - nbformat - - packaging >=24.0,<26 - - platformdirs - - psutil - - pulp >=2.3.1,<3.4 - - python >=3.11 - - pyyaml - - referencing - - requests >=2.8.1,<3.0 - - smart_open >=4.0,<8.0 - - snakemake-interface-common >=1.20.1,<2.0 - - snakemake-interface-executor-plugins >=9.3.2,<10.0 - - snakemake-interface-logger-plugins >=1.1.0,<3.0.0 - - snakemake-interface-report-plugins >=1.2.0,<2.0.0 - - snakemake-interface-scheduler-plugins >=2.0.0,<3.0.0 - - snakemake-interface-storage-plugins >=4.4.1,<5.0 - - sqlmodel >=0.0.37,<0.0.38 - - tabulate - - tenacity >=9.1.4,<10.0 - - throttler - - wrapt - - yte >=1.5.5,<2.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/snakemake?source=hash-mapping - run_exports: - weak: - - snakemake-minimal >=9.25.1,<10.0a0 - size: 889521 - timestamp: 1785882296560 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 @@ -3227,25 +2731,6 @@ packages: license_family: MIT size: 1125371 timestamp: 1780396651124 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda - noarch: python - sha256: 25c15e85cb03a1c4d43d02d0ff37c8c9a970c3e84abeaf420fc190bb053bbe3f - md5: e46fc69f093a840e98b9e2c29727862d - depends: - - python >=3.10 - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - _python_abi3_support 1.* - - cpython >=3.10 - constrains: - - __glibc >=2.17 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ast-serialize?source=compressed-mapping - run_exports: {} - size: 1116952 - timestamp: 1786328964477 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -4220,22 +3705,6 @@ packages: license_family: MIT size: 300271 timestamp: 1761203085220 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h77c0f9c_1.conda - sha256: f2be3de194658ef1733c11381fcec2853898d598e602ccbe5745d0a0700e0899 - md5: 452e8b9d54b4ba7ab6e8129bc8a7a3ee - depends: - - __glibc >=2.17,<3.0.a0 - - libffi >=3.7.0,<3.8.0a0 - - libgcc >=14 - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - purls: - - pkg:pypi/cffi?source=compressed-mapping - run_exports: {} - size: 305586 - timestamp: 1786528481627 - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.4.1-hf8ad068_0.conda sha256: 74ed4d8b327fa775d9c87e476a7221b74fb913aadcef207622596a99683c8faf md5: 1b7a01fd02d11efe0eb5a676842a7b7d @@ -4423,45 +3892,6 @@ packages: license_family: BSD size: 1357035 timestamp: 1781680868886 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.0-py314h9e666f3_0.conda - sha256: 4c98ae3e43688b0b350c3eaa96b61725ee32f4ec0990fce848543c51efff407b - md5: ce8f5df54fb2508d175ff6aa558821b1 - depends: - - archspec >=0.2.3 - - boltons >=23.0.0 - - charset-normalizer - - conda-package-handling >=2.2.0 - - distro >=1.5.0 - - frozendict >=2.4.2 - - jsonpatch >=1.32 - - menuinst >=2 - - packaging >=23.0 - - platformdirs >=3.10.0 - - pluggy >=1.6.0 - - pycosat >=0.6.3 - - python - - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 - - setuptools >=60.0.0 - - tqdm >=4 - - truststore >=0.8.0 - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - - conda-pypi >=0.10.1 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - - python_abi 3.14.* *_cp314 - constrains: - - conda-build >=25.9 - - conda-content-trust >=0.3.0 - - conda-env >=2.6 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda?source=hash-mapping - run_exports: {} - size: 1464668 - timestamp: 1785769797579 - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.10.1-py314h9e666f3_2.conda sha256: 989d9ab43bf25e1acff1b7519c96ac156ccdf46a2df605aff8a60ad85dbd6356 md5: 4689abef13301a7556f0c3f6d3addee7 @@ -4481,29 +3911,6 @@ packages: license_family: MIT size: 318028 timestamp: 1781646467890 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda - sha256: f864a806c98c44a0cdc4ede1b42fa6cdcb9d327d8d0013f51af9344f553d0cbf - md5: d8cc5cf325134981f65dd72c8e082433 - depends: - - python - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/conda-pypi?source=hash-mapping - run_exports: {} - size: 323834 - timestamp: 1784203010834 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 @@ -4548,18 +3955,6 @@ packages: license: CC0-1.0 size: 24283 timestamp: 1756734785482 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda - sha256: 6a905f317b93104444dc74875933a7cdf65fa14ae05dd6485ece6ffd9d0b5e89 - md5: 451367db888b29ad0db6b298669fbe77 - depends: - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: CC0-1.0 - purls: [] - run_exports: {} - size: 24388 - timestamp: 1785920465106 - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda sha256: e3127e539763a0e16fb7977fac8fc42e5fe6df0517757f56283df1b7567ac6bd md5: 6f283e8bb11a748bc30af46258683ba8 @@ -4926,21 +4321,6 @@ packages: - fmt >=11.0.2,<11.1.0a0 size: 197559 timestamp: 1743030768885 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - sha256: 25d14a197601fdd616f2e501431b52887e69b31e7defbc1679381d718357ceb7 - md5: a70bb6d42ad121aef456e0007e92bed6 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=12.1.0,<12.2.0a0 - size: 199072 - timestamp: 1785915412102 - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda sha256: d4e92ba7a7b4965341dc0fca57ec72d01d111b53c12d11396473115585a9ead6 md5: f7d7a4104082b39e3b3473fbd4a38229 @@ -5112,25 +4492,6 @@ packages: license_family: LGPL size: 577414 timestamp: 1774985848058 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.7-h2b0a6b4_0.conda - sha256: 1c22e37f9d7e06e9e0582ee5a55c2ddd19ea75f71f44eb13b56f504ef5c37aa5 - md5: 5d355db3e937086e22cf4cb5fe19787c - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libglib >=2.88.2,<3.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblzma >=5.8.3,<6.0a0 - - libpng >=1.6.58,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - license: LGPL-2.1-or-later - license_family: LGPL - purls: [] - run_exports: - weak: - - gdk-pixbuf >=2.44.7,<3.0a0 - size: 581631 - timestamp: 1782591374199 - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda sha256: bc3860e6689be6968ca5bae3660f43dd3e22f4dd61c0bfc99ffd0d0daf4f7a73 md5: aab9195bc018b82dc77a84584b36cce9 @@ -5247,19 +4608,6 @@ packages: license: LGPL-2.1-or-later size: 236955 timestamp: 1778508800134 -- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h4916ab3_1.conda - sha256: ffa59cd7b517ed0a5905cc83643d08117f073502b0bdb1cab1835805ecc8da4e - md5: 246c12ff18139b126586eb2d0e906c7e - depends: - - libglib ==2.88.3 h45c3219_1 - - libffi - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 238159 - timestamp: 1786457663614 - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda sha256: f24feeda3aeec3a2cbb9fe2e0fabc4785372e70b6cc838c96023e08e17f4bfa0 md5: 6941f96b8a8056677d79b4f5a2c4aaca @@ -5367,22 +4715,6 @@ packages: license_family: MIT size: 267632 timestamp: 1781762129332 -- conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda - sha256: 8077b6042be044d2c571d5eb340a1ad7a1949534113fb8d9cafb213982b7c60b - md5: a0423baf08abf2f98136e8cc103e46dd - depends: - - python - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/greenlet?source=compressed-mapping - run_exports: {} - size: 278368 - timestamp: 1786384047792 - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda sha256: c6bb4f06331bcb0a566d84e0f0fad7af4b9035a03b13e2d5ecfaf13be57e6e10 md5: bcaea22d85999a4f17918acfab877e61 @@ -7641,24 +6973,6 @@ packages: license_family: BSD size: 18824 timestamp: 1779859122364 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-9_h6ae95b6_openblas.conda - build_number: 9 - sha256: 7534e06a82865d3cfa050937c77887e8e85e7edeb4623972f6fe837cc7d13334 - md5: d76700bd66c92d6159bfcfd3c8f83bb8 - depends: - - libblas 3.11.0 9_h4a7cf45_openblas - - libcblas 3.11.0 9_h0358290_openblas - - liblapack 3.11.0 9_h47877c9_openblas - constrains: - - blas 2.309 openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - liblapacke >=3.11.0,<3.12.0a0 - size: 18034 - timestamp: 1786059054936 - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda sha256: 22909d64038bdc87de61311c4ae615dc574a548a7340b963bb7c9eb61b191669 md5: 6d2362046dce932eefbdeb0540de0c38 @@ -7783,35 +7097,6 @@ packages: license_family: BSD size: 2808228 timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda - sha256: 1b9ca94f36f6072ff71550dfaa8dd4106a118bf01f352ed639aef134b462bab6 - md5: 444e55c79d67040779af3a0167963bea - depends: - - cpp-expected >=1.3.1,<1.3.2.0a0 - - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - simdjson >=4.6.6,<4.7.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - - libarchive >=3.8.9,<3.9.0a0 - - libcurl >=8.21.0,<9.0a0 - - libmsgpack-c >=6.1.0,<7.0a0 - - reproc >=14.2.7.post0,<14.3.0a0 - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - - openssl >=3.5.7,<4.0a0 - - fmt >=12.1.0,<12.2.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - nlohmann_json-abi ==3.12.0 - - zstd >=1.5.7,<1.6.0a0 - - libsolv >=0.7.39,<0.8.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libmamba >=2.9.0,<2.10.0a0 - size: 2850628 - timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.8.1-h9b73f26_0.conda sha256: ef3d79e638dc1e294df239bf83a268dbea835c2c1f66001383473c22a8dc0ffd md5: 410217d65f86c0a60c9b11ecf1bf842e @@ -7824,20 +7109,6 @@ packages: license_family: BSD size: 20307 timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda - sha256: ddae16548197cf30cf72d700733427cb314ce6fa2921fea7e2922566288de42e - md5: 1066c66681f6a1d6e36d13dc3eecbd50 - depends: - - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - libmamba >=2.9.0,<2.10.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 20529 - timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.8.1-py314hd8b5600_0.conda sha256: c35bf3900a533b2e8a2ad9f63ab645240208ad28c3532d2d46dd84cbddfc2d7e md5: 828f1c59aa72aa02de5b559c0428ebb8 @@ -7861,34 +7132,6 @@ packages: license_family: BSD size: 967437 timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda - sha256: 0c2f07dfcb61e14a357df8816abb236e1705b92998ba068784f87fd74070aa85 - md5: 231869ede4419e814ca7d3de6fabdf95 - depends: - - python - - libmamba ==2.9.0 py314h3b59866_0 - - libmamba-spdlog ==2.9.0 hf1e253f_0 - - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - openssl >=3.5.7,<4.0a0 - - fmt >=12.1.0,<12.2.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - python_abi 3.14.* *_cp314 - - yaml-cpp >=0.8.0,<0.9.0a0 - - nlohmann_json-abi ==3.12.0 - - zstd >=1.5.7,<1.6.0a0 - - libmamba >=2.9.0,<2.10.0a0 - - pybind11-abi ==11 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/libmambapy?source=hash-mapping - run_exports: - weak: - - libmambapy >=2.9.0,<2.10.0a0 - size: 967399 - timestamp: 1786125663902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843 md5: 2c21e66f50753a083cbe6b80f38268fa @@ -7913,22 +7156,6 @@ packages: license: BSL-1.0 size: 40340 timestamp: 1770807484162 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda - sha256: c23ac21547d9fac6be14b6b3463dd29fb0fd4aba5c64c4a9876dfa6d2e3158a9 - md5: 9ab61308317dbf6609e2142b16b792a5 - depends: - - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - constrains: - - msgpack-c ==6.1.0 *_7 - license: BSL-1.0 - purls: [] - run_exports: - weak: - - libmsgpack-c >=6.1.0,<7.0a0 - size: 40166 - timestamp: 1786189331007 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda sha256: 055572a4c8a1c3f9ac60071ee678f5ea49cfd7ac60a636d817988a6f9d6de6ae md5: a908e463c710bd6b10a9eaa89fdf003c @@ -9163,19 +8390,6 @@ packages: license: BSD-3-Clause AND MIT size: 207080 timestamp: 1782377704416 -- conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_1.conda - sha256: 8aa8c68ec9b5c37f8feb356f8e0640c885e70cbb2aae396211b181be48427eb9 - md5: 53d8848429e4c801b030e2bc8e084db6 - depends: - - python - - tomli-w - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause AND MIT - purls: - - pkg:pypi/menuinst?source=hash-mapping - run_exports: {} - size: 207824 - timestamp: 1786008811631 - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda sha256: 41558b95a387ce2374260aa034a99c3a88cbb98e1a56510f4fa6839063de867b md5: fe7b4ff5792fee512aad843294ea809a @@ -9293,30 +8507,9 @@ packages: - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT - size: 22860519 - timestamp: 1780315602311 -- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.0-py314h518bba1_0.conda - sha256: 77158133fc8c3fc95acc74742efab4ac40ccd070298c4a946ac8f99b5b7017a9 - md5: 862cee264de9cdba7dda3ccf6312f74e - depends: - - ast-serialize >=0.6.0,<1.0.0 - - mypy_extensions >=1.0.0 - - pathspec >=1.0.0 - - python - - python-librt >=0.13.0 - - typing_extensions >=4.6.0 - - psutil >=4.0 - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/mypy?source=hash-mapping - run_exports: {} - size: 23233304 - timestamp: 1783986175097 + license_family: MIT + size: 22860519 + timestamp: 1780315602311 - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda sha256: 9c2e3f9e9883e4b8d7e9e6abf7b235dc00bdcd5ef66640a360464a9f5756294d md5: 94116b69829e90b72d566e64421e1bff @@ -9800,30 +8993,6 @@ packages: license: LGPL-2.1-or-later size: 458036 timestamp: 1774281947855 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda - sha256: 48f27a6c3e4062bc09dafe9c7f6b288c5de5655e81095ab7f1aad920b2163b7b - md5: 6a2822aaf9a34ac3708904a47ff3dd7e - depends: - - __glibc >=2.17,<3.0.a0 - - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 - - fonts-conda-ecosystem - - fribidi >=1.0.16,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libgcc >=14 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - pango >=1.58.2,<2.0a0 - size: 469916 - timestamp: 1786107384537 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda sha256: 09717569649d89caafbf32f6cda1e65aef86e5a86c053d30e4ce77fca8d27b68 md5: 31614c73d7b103ef76faa4d83d261d34 @@ -10138,26 +9307,6 @@ packages: license_family: BSD size: 12191860 timestamp: 1781020717312 -- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_2.conda - noarch: python - sha256: 1aa4eb372a408a8655327c3e304891028dce9ea3002cd3d68845e66e51ba7510 - md5: 8c845f47320cf9d61106989ab343250f - depends: - - python - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - openssl >=3.5.7,<4.0a0 - - _python_abi3_support 1.* - - cpython >=3.10 - constrains: - - __glibc >=2.17 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/py-rattler?source=hash-mapping - run_exports: {} - size: 12065493 - timestamp: 1784707900361 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda sha256: 9e1baddd1199e244f4f8be10c7250691088948583ab3955c510b90eb71c91a2c md5: 5f7d505626cb057e1320bbd46dd02ef2 @@ -10249,21 +9398,6 @@ packages: license_family: MIT size: 88644 timestamp: 1757744868118 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda - sha256: 0d3cbeb41afef769fe2d8ab7e0e6a2140c8577018ae5e237839b2eae9d571747 - md5: 0fa51561c73d1f362997f8aceab87146 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pycosat?source=hash-mapping - run_exports: {} - size: 88896 - timestamp: 1784146233775 - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda sha256: b8260660d064fb947f4b573ec4a782696bc8b19042452eaa4e9bb1152b540555 md5: dfb9a57535eb8c35c6744da7043063f0 @@ -10529,21 +9663,6 @@ packages: license_family: MIT size: 156252 timestamp: 1778511622812 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda - sha256: 2655bda3e0f53863c9ba1239aa9212b399141582074d0c043df6f449ff21b0f1 - md5: 102786d7a63373e0441273b7d863d4ea - depends: - - python - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/librt?source=compressed-mapping - run_exports: {} - size: 162846 - timestamp: 1786328851615 - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda sha256: 17cd445cbdf0a33a247934497c867195edc2bf51faad8011c74332460d47634b md5: 711ddd4f498e10b56f687683ae76e059 @@ -10863,20 +9982,6 @@ packages: - readline >=8.3,<9.0a0 size: 345073 timestamp: 1765813471974 -- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-h54a6638_2.conda - sha256: d217fcb9c5a3a532b6e00896613a117004cdc48883ef5047642346614375a9a4 - md5: 8b45f3f47e08558e07ef2b96ab45f8b2 - depends: - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc >=14.2.7.post0,<14.3.0a0 - size: 37536 - timestamp: 1785921754425 - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-hb03c661_1.conda sha256: b47cbe954b1522f944c1c17fae964d362dfcf7ef7516fda1c368971426a8e933 md5: 2e1edbf819157ca726ec65afb4eb2d5d @@ -10887,23 +9992,6 @@ packages: license_family: MIT size: 35494 timestamp: 1779092421531 -- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-h50b1954_2.conda - sha256: d523da96c07cfde82b8c11cf84a25f79b8b6e8485298c54ee974e9fbad8b81fe - md5: f10296a0f976c49f24fdd9258fb3c9aa - depends: - - reproc ==14.2.7.post0 h54a6638_2 - - libgcc >=14 - - libstdcxx >=14 - - __glibc >=2.17,<3.0.a0 - - reproc >=14.2.7.post0,<14.3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - size: 29216 - timestamp: 1785921754425 - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-hecca717_1.conda sha256: f29ba41cc71a01f1dd7eeba573b1535f0feec72e9ab6b4a390b4e71230d61ade md5: a0ef2594b3ab25a933ee8e7a1ac1e21b @@ -10930,23 +10018,23 @@ packages: license_family: MIT size: 309696 timestamp: 1779976994059 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h1bee95f_0.conda - sha256: 064e56d6c233cbcfac5b0183c0dd10b732668ff27aa1ea3580459acceae95473 - md5: add3cbcc5eb677f6c1720e01cd82aac5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda + sha256: 3425007379dc5b709b02699fa4329f1b82880a42f8dec40003cad7e7b6e2adac + md5: aefc39100f5d6c043d02a38c3b90ff47 depends: - python - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 - python_abi 3.14.* *_cp314 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - - pkg:pypi/rpds-py?source=hash-mapping + - pkg:pypi/rpds-py?source=compressed-mapping run_exports: {} - size: 300129 - timestamp: 1782831350283 + size: 300155 + timestamp: 1787344359780 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda sha256: 59f7773ed8c6f2bcab3e953e2b4816c86cf71e5508dc571f8073b8c2294b5787 md5: 8ea76c64b49b16c37769ea7c4dca993b @@ -10992,23 +10080,6 @@ packages: license_family: MIT size: 9506544 timestamp: 1782288610637 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.2-h462bb3b_0.conda - noarch: python - sha256: 2730d570f45ba836f5b85700776cc4527894c640ebacb536fb5db138c845ee63 - md5: 8a00805aae730fb6266343985496ee07 - depends: - - python - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - constrains: - - __glibc >=2.17 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ruff?source=hash-mapping - run_exports: {} - size: 9481279 - timestamp: 1786151870074 - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda sha256: a6fa0afa836f8f26dea0abc180ca2549bb517932d9a88a121e707135d4bcb715 md5: 334dba9982ab9f5d62033c61698a8683 @@ -11178,20 +10249,6 @@ packages: license_family: APACHE size: 317505 timestamp: 1778109124630 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.6-h171cf75_1.conda - sha256: 09adc403283de12da403a996a9ddd068f1b425dcdccefa2eeec4e2b684e114d4 - md5: 92b6b03d39f81d10467a6901ce85fc3b - depends: - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: Apache-2.0 - purls: [] - run_exports: - weak: - - simdjson >=4.6.6,<4.7.0a0 - size: 355874 - timestamp: 1786528156277 - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py312h4c3975b_0.conda sha256: 70a0ab8787576d55f23d114cc1d7569fd83f5b148c71a7f277180299e86ea8f1 md5: bb3b931588a7dc83cc7b82836b0017df @@ -11266,22 +10323,6 @@ packages: license_family: MIT size: 196681 timestamp: 1767781665629 -- conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda - sha256: 5fca627690d56c6f87a9dabafceee54cb75d7da392309bb080333948c048365a - md5: 5e32ceeb7b9514ce4730565052829dbd - depends: - - __glibc >=2.17,<3.0.a0 - - fmt >=12.1.0,<12.2.0a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.17.0,<1.18.0a0 - size: 197438 - timestamp: 1785924346496 - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.51-py314h0f05182_0.conda sha256: 3c46e9af535a376c7269b61563f84c601235b00d50fc97f87ffbf3b68a51fe17 md5: aeb7447f3ea37b2bb32167267dcf0bfe @@ -11296,22 +10337,6 @@ packages: license_family: MIT size: 4034632 timestamp: 1781547880247 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda - sha256: 0a3d82fb1650d364f8c2427d870a6c6684e02c9e5c4b0cccb6def83957f007fb - md5: c54cb6754823fe6710369511aab5c29c - depends: - - python - - greenlet !=0.4.17 - - typing-extensions >=4.6.0 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: MIT - purls: - - pkg:pypi/sqlalchemy?source=compressed-mapping - run_exports: {} - size: 4047549 - timestamp: 1786535232010 - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda sha256: ef17725138fa72aa5a0f8ccace9727831c4b333e39575f78fb5ad1755826b687 md5: b345ea7f13e4cae9809c69b1d1af2c99 @@ -12115,21 +11140,6 @@ packages: license_family: MIT size: 223526 timestamp: 1745307989800 -- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda - sha256: 425808ebd2a20bfc460995293995e1fa0510e93c300535e2ec3622bd3d84288c - md5: 18bf4b05b458fb8135987e47364f3dcb - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - yaml-cpp >=0.8.0,<0.9.0a0 - size: 242654 - timestamp: 1785923983284 - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda sha256: dc9f28dedcb5f35a127fad2d847674d2833369dd616d294e423b8997df31d8a8 md5: 96b08867e21d4694fa5c2c226e6581b0 @@ -12250,18 +11260,6 @@ packages: license_family: MIT size: 8191 timestamp: 1744137672556 -- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - sha256: 2a7204314663eeda5dec482a956f0e2eaf289bd5b9953eaaaad0e81aa64638f2 - md5: 3845f3d75991bae0fb90884662f4327c - depends: - - cpython - - python-gil - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 8144 - timestamp: 1784221492234 - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda sha256: a362b4f5c96a0bf4def96be1a77317e2730af38915eb9bec85e2a92836501ed7 md5: b3f0179590f3c0637b7eb5309898f79e @@ -12355,26 +11353,6 @@ packages: license: MIT size: 161308 timestamp: 1782357087265 -- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - sha256: e36998c5e860e26b22e5dbcd5726dd0c4eabad949c84d383cad8512757bbf6a1 - md5: fb568fbae6908ba86a090a85a089d11f - depends: - - exceptiongroup >=1.0.2 - - idna >=2.8 - - python >=3.10 - - typing_extensions >=4.5 - - python - constrains: - - trio >=0.32.0 - - uvloop >=0.22.1 - - winloop >=0.2.3 - license: MIT - license_family: MIT - purls: - - pkg:pypi/anyio?source=hash-mapping - run_exports: {} - size: 164465 - timestamp: 1783889660383 - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda sha256: 8f032b140ea4159806e4969a68b4a3c0a7cab1ad936eb958a2b5ffe5335e19bf md5: 54898d0f524c9dee622d44bbb081a8ab @@ -12421,20 +11399,6 @@ packages: license_family: Apache size: 28797 timestamp: 1763410017955 -- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - sha256: fbbd8ce60cbd5c16f3fe559eb644551f94285caff30985ea961ff851c1cf25ac - md5: 89d495168582cb00428dad699d149624 - depends: - - python >=3.10 - constrains: - - astroid >=2,<5 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/asttokens?source=hash-mapping - run_exports: {} - size: 34639 - timestamp: 1783975742052 - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda sha256: 1b6124230bb4e571b1b9401537ecff575b7b109cc3a21ee019f65e083b8399ab md5: c6b0543676ecb1fb2d7643941fe375f2 @@ -12546,18 +11510,6 @@ packages: license_family: BSD size: 307488 timestamp: 1781879511321 -- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda - sha256: ca44f906756aaa908645c9bcef31a132fc82e7934777aa6bb45c12edada6a58c - md5: 92c88d22e52af7b672605b12a31bf0bb - depends: - - python >=3.10 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/boltons?source=hash-mapping - run_exports: {} - size: 309453 - timestamp: 1784706150046 - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda sha256: 1acf87c77d920edd098ddc91fa785efc10de871465dee0f463815b176e019e8b md5: 1fcdf88e7a8c296d3df8409bf0690db4 @@ -12621,18 +11573,6 @@ packages: license_family: BSD size: 4134 timestamp: 1615209571450 -- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_2.conda - noarch: python - sha256: 0d00dd61cb91b1bd1536600c64c9db4f94f3c699fec42864d0a2f4bc2ad8c3e8 - md5: 1990eb3f49022846fbc7fc9624a0ee43 - depends: - - cached_property >=1.5.2,<1.5.3.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 6836 - timestamp: 1783242914545 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 md5: 576d629e47797577ab0f1b351297ef4a @@ -12642,18 +11582,6 @@ packages: license_family: BSD size: 11065 timestamp: 1615209567874 -- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_2.conda - sha256: b1808a7811b5688d045b204425bb7ee824b340b40025b4ad0a39b4db1f4f1c98 - md5: f71e6840332854fd2eac6c3229768a9c - depends: - - python >=3.9 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/cached-property?source=hash-mapping - run_exports: {} - size: 14752 - timestamp: 1783242913845 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda sha256: 6c13620e458ba43278379d0cdacc30c497336bddfda81681fd50d114a65c702f md5: c13824fedced67005d3832c152fe9c2f @@ -12902,23 +11830,6 @@ packages: license_family: BSD size: 59960 timestamp: 1778768196921 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - sha256: b7cc67470a361330edd8d9e331cafd4807dd77bc766ca197a3783ed3db66dfeb - md5: 49592004caf4336815fd5282bbb72172 - depends: - - python >=3.10 - - libmambapy >=2.0.0,!=2.6.2 - - boltons >=23.0.0 - - python - constrains: - - conda >=26.5 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda-libmamba-solver?source=hash-mapping - run_exports: {} - size: 46828 - timestamp: 1784200590190 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda sha256: 3a9934ecf483d46b56a59547c63bd40bef4c398bfc671acb2ba0f98a045ccb8b md5: 6248ea5c3194d00fb1db6d68fcdcfd3e @@ -12931,21 +11842,6 @@ packages: license_family: BSD size: 20396 timestamp: 1778023975685 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - sha256: eb20fc63ce6438e10d7c64890bd23d0c68f62a479f2bae8b3c892e13949daf03 - md5: 7f1b127b170f9f0d01eca4b54a046ee5 - depends: - - conda >=26.3.0 - - pydantic >=2.12.5,<3 - - python >=3.10 - - ruamel.yaml - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda-lockfiles?source=hash-mapping - run_exports: {} - size: 20633 - timestamp: 1783438353470 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda sha256: 63d5b510644a01b4c93738304cb0dc8a336542fc335b0b77f3b355ba7d119260 md5: 3f1854f1ec2090538fb49b7078dbb9de @@ -13000,19 +11896,6 @@ packages: license_family: BSD size: 22216 timestamp: 1778152874928 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - sha256: efd3b7b79a0e3993a174ab0140794479e2479b59fd053605acaaa25718f0f013 - md5: ac705504abf28bb924a021a3d5f4b55f - depends: - - conda >=26.1.1 - - python >=3.10 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda-self?source=hash-mapping - run_exports: {} - size: 22510 - timestamp: 1784724984642 - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda sha256: 7f8ea42a8411b433ec7244dfd30637d90564256c13a114dbe42455fe032ae89c md5: 12389a21e7f69704b0ae77f44355e30b @@ -13049,18 +11932,6 @@ packages: license: Python-2.0 size: 49333 timestamp: 1781254618863 -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_102.conda - noarch: generic - sha256: 4d97fdae803c1ed7c704289d1f856d60e5502f24e93e92f9d45fbea37fd9e9a7 - md5: 6b344ff499096c0b873d202fea1e2fcd - depends: - - python >=3.14,<3.15.0a0 - - python_abi * *_cp314 - license: Python-2.0 - purls: [] - run_exports: {} - size: 49916 - timestamp: 1786444134021 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 md5: 4c2a8fef270f6c69591889b93f9f55c1 @@ -13317,17 +12188,6 @@ packages: license: Unlicense size: 36989 timestamp: 1781381078337 -- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.2-pyhd8ed1ab_0.conda - sha256: 5476fe77cc2f59389dd348135462892dc20f42114301221648df90a6e9650186 - md5: 977e2b00d5329b6d56ebe94ae1f4b67c - depends: - - python >=3.10 - license: Unlicense - purls: - - pkg:pypi/filelock?source=hash-mapping - run_exports: {} - size: 77939 - timestamp: 1785376409053 - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda sha256: 782fa186d7677fd3bc1ff7adb4cc3585f7d2c7177c30bcbce21f8c177135c520 md5: a6997a7dcd6673c0692c61dfeaea14ab @@ -13518,21 +12378,6 @@ packages: license_family: BSD size: 162193 timestamp: 1778065820061 -- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.59-pyhcf101f3_0.conda - sha256: 967efdf5ebd1a0c78cf957d1b9ad85d0ceb4d9ebfb173b348cf4cab3086878b8 - md5: 2c5ddbfbac16be06c0e49acafe8691f5 - depends: - - python >=3.10 - - gitdb >=4.0.1,<5 - - typing_extensions >=3.10.0.2 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/gitpython?source=compressed-mapping - run_exports: {} - size: 177303 - timestamp: 1786443183227 - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda sha256: 4d3f8a3a42a88493cdae21f54efc57154c23403901d5d54d05b9fa6991b9bfc7 md5: 36a2d7837350f7e8da83bcaad552d0b5 @@ -13868,54 +12713,6 @@ packages: license_family: BSD size: 652076 timestamp: 1780654438137 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyh53cf698_0.conda - sha256: 8470648b5e790d1881c09c02068d53e1bf0126f020db02a6dc2e73400cf1eeeb - md5: 23564ed27c9c7714905be96ac5786500 - depends: - - __unix - - ipython_pygments_lexers >=1.0.0 - - jedi >=0.18.2 - - matplotlib-inline >=0.1.6 - - prompt-toolkit >=3.0.41,<3.1.0 - - psutil >=7 - - pygments >=2.14.0 - - python >=3.11 - - stack_data >=0.6.0 - - traitlets >=5.13.0 - - typing_extensions >=4.6 - - pexpect >4.6 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/ipython?source=hash-mapping - run_exports: {} - size: 716078 - timestamp: 1785754203352 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.16.1-pyhe2676ad_0.conda - sha256: 2a0cd24e5c0e1eb8b7baf06346906673f6b8abea151ce302d7d978a3c416aeec - md5: d7c95d926befcfa22bee69bb1980e2ce - depends: - - __win - - ipython_pygments_lexers >=1.0.0 - - jedi >=0.18.2 - - matplotlib-inline >=0.1.6 - - prompt-toolkit >=3.0.41,<3.1.0 - - psutil >=7 - - pygments >=2.14.0 - - python >=3.11 - - stack_data >=0.6.0 - - traitlets >=5.13.0 - - typing_extensions >=4.6 - - colorama >=0.4.4 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/ipython?source=hash-mapping - run_exports: {} - size: 715162 - timestamp: 1785754256301 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda sha256: 894682a42a7d659ae12878dbcb274516a7031bbea9104e92f8e88c1f2765a104 md5: bd80ba060603cc228d9d81c257093119 @@ -14006,12 +12803,28 @@ packages: - rpds-py >=0.25.0 - python license: MIT - license_family: MIT + license_family: MIT + purls: + - pkg:pypi/jsonschema?source=hash-mapping + run_exports: {} + size: 82356 + timestamp: 1767839954256 +- conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda + sha256: 328756a4555941d57af4a5f553e5d8598e9f3b37db028f557e30f9f0b3086db6 + md5: 204b5ca91eba7738790ecc333facbbbe + depends: + - attrs >=22.2.0 + - jsonschema-specifications >=2023.3.6 + - python >=3.10 + - referencing >=0.28.4 + - rpds-py >=0.25.0 + - python + license: MIT purls: - - pkg:pypi/jsonschema?source=hash-mapping + - pkg:pypi/jsonschema?source=compressed-mapping run_exports: {} - size: 82356 - timestamp: 1767839954256 + size: 82084 + timestamp: 1787579299493 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda sha256: 0a4f3b132f0faca10c89fdf3b60e15abb62ded6fa80aebfc007d05965192aa04 md5: 439cd0f567d697b20a8f45cb70a1005a @@ -14205,23 +13018,6 @@ packages: license_family: BSD size: 100945 timestamp: 1733402844974 -- conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.0-pyhcf101f3_1.conda - sha256: 4aeff72162e4b3f77238c859547275b9a989ed6873bffbdbc4d688291a60919b - md5: 3e7eb1cc4a342b2e7f68199b50fbe3cb - depends: - - jsonschema >=2.6 - - jupyter_core >=4.12,!=5.0.* - - python >=3.10 - - python-fastjsonschema >=2.15 - - traitlets >=5.1 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/nbformat?source=compressed-mapping - run_exports: {} - size: 107583 - timestamp: 1786372859684 - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda sha256: e6768ceef038f4d7e083de7e393f5dd7d672b937e2bda570b740f6399b686689 md5: fcd832bfd4749e9b246112b6894f97fc @@ -14260,17 +13056,6 @@ packages: license_family: MIT size: 4335 timestamp: 1758194464430 -- conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - sha256: 30d271dd63391e053aa3c393c5d93fb622dec5d50a64107171ee28242660608e - md5: 849b8852621992c3f3a1901c939a0399 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - nlohmann_json-abi ==3.12.0 - size: 4365 - timestamp: 1785920576349 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 md5: 58335b26c38bf4a20f399384c33cbcf9 @@ -14414,18 +13199,6 @@ packages: license_family: MIT size: 1202377 timestamp: 1780262809860 -- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - sha256: 0f7021cfb3ff454c91a5e28e1f5060906a52c6d1cde3f879a7d03ac33c28b1c3 - md5: 573cb3ed111004230ed3de650653cd4d - depends: - - python >=3.13.0a0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pip?source=hash-mapping - run_exports: {} - size: 1198163 - timestamp: 1785914482439 - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda sha256: 9e5e1fd3506ccfc4d444fc4d2d39b0ed097d5d0e3bd3d4bdf6bcc81aaf66860d md5: 2c5ef45db85d34799771629bd5860fd7 @@ -14436,19 +13209,6 @@ packages: license_family: MIT size: 26308 timestamp: 1779972894916 -- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.2-pyhcf101f3_0.conda - sha256: eea7ead8bcdc3b307241cbc8d038f8b95db9e351e52f2bf3c12ad55eb4d9a8d1 - md5: 15f2020d6b2ede94d80b2c884a1a6f3e - depends: - - python >=3.10 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/platformdirs?source=compressed-mapping - run_exports: {} - size: 26869 - timestamp: 1786390868223 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -14483,21 +13243,6 @@ packages: license_family: BSD size: 273927 timestamp: 1756321848365 -- conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - sha256: efe8def2c93aa34cd8d3c9af1dc4c7d312791cf769d8b2b615e32733e6df6051 - md5: 39c92a39517316e5001d645ae63d9ab9 - depends: - - python >=3.10 - - wcwidth - constrains: - - prompt_toolkit 3.0.53 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/prompt-toolkit?source=hash-mapping - run_exports: {} - size: 276081 - timestamp: 1785160613307 - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda sha256: a7713dfe30faf17508ec359e0bc7e0983f5d94682492469bd462cdaae9c64d83 md5: 7d9daffbb8d8e0af0f769dbbcd173a54 @@ -14721,19 +13466,6 @@ packages: license_family: BSD size: 244628 timestamp: 1755304154927 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.1-pyhcf101f3_0.conda - sha256: 2243b305387413fa716827dce44011ea121be002e7ec24404ba00651db0279cd - md5: d066c36f0c7658ef422c60d598ea8495 - depends: - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fastjsonschema?source=hash-mapping - run_exports: {} - size: 252180 - timestamp: 1785242896823 - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda sha256: 84c129bdd6abcecac42a948f2670d17fe735d02d3a5a483a9b1f1bc33ba38c28 md5: 224f69f177eb5aae6c9a6052846bf609 @@ -14743,17 +13475,6 @@ packages: license: Python-2.0 size: 49315 timestamp: 1781254664376 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_102.conda - sha256: b15fb3e5daae788ca78844ee4ee9f1a1b07911cd4e83c484d8c1ce2794c6c93b - md5: 364ec4bb854ab4ca9ca4e626a55ea8da - depends: - - cpython 3.14.6.* - - python_abi * *_cp314 - license: Python-2.0 - purls: [] - run_exports: {} - size: 49897 - timestamp: 1786444152084 - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda sha256: 9c18fa1ebd0c839b6ea12e99d510a0968c2288cc423185cb34bf7332b97684e9 md5: 5cc7e2e78962dc902cae9e29c2aa4b2f @@ -14981,20 +13702,6 @@ packages: license_family: MIT size: 57112 timestamp: 1778331246761 -- conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - sha256: d0abe2d97b67d42aca30fcd34b3db6cd5de57b4a9ffcf4e04091dd9f4a0cf1e6 - md5: fd2e276dbbd1a2ae15ffdbdbabd908f1 - depends: - - python >=3.10 - - wrapt - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/smart-open?source=hash-mapping - run_exports: {} - size: 57158 - timestamp: 1782573421136 - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda sha256: bc86861086db65c9b56dd6a1605755f41a9875b94fc3b69e137f686700d91aa1 md5: b899f1195be56d8215ed1973a8f409c3 @@ -15240,39 +13947,6 @@ packages: license: MPL-2.0 and MIT size: 94630 timestamp: 1781741323148 -- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda - sha256: a4d9e9da15b13f1ab047e7db412e2ed564bc615832e80213cf129b973c3abf4a - md5: 00953c3b729e03b0ae21f49fdf3441bb - depends: - - python >=3.10 - - __unix - - python - constrains: - - envwrap >=0.2 - - ipywidgets >=6.0 - license: MPL-2.0 and MIT - purls: - - pkg:pypi/tqdm?source=hash-mapping - run_exports: {} - size: 98184 - timestamp: 1785172528905 -- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda - sha256: 669cd06a4c7c1139f9c6b9850d16d71c1b4ce42375806976a03dba824283fef0 - md5: 7eac270516c8221cedc7f40a96d7fb8f - depends: - - python >=3.10 - - colorama - - __win - - python - constrains: - - envwrap >=0.2 - - ipywidgets >=6.0 - license: MPL-2.0 and MIT - purls: - - pkg:pypi/tqdm?source=hash-mapping - run_exports: {} - size: 97602 - timestamp: 1785172567718 - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda sha256: b89a823edf524956b94a2a4db974866e4501f05c68976eff458c5dcf07f88431 md5: 37e3be7b6e2977d37b8fa5da229f5dc0 @@ -15283,19 +13957,6 @@ packages: license_family: BSD size: 115158 timestamp: 1780507822178 -- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - sha256: 03dba5917f944c6684ab44c81daacac1624cd148e4b2cae215dcec594a210c48 - md5: a79bf97561232a31447b6246c2153ab5 - depends: - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/traitlets?source=hash-mapping - run_exports: {} - size: 116935 - timestamp: 1785761789772 - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda sha256: eece5be81588c39a855a0b70da84e0febb878a6d91dd27d6d21370ce9e5c5a46 md5: c2db35b004913ec69bcac64fb0783de0 @@ -15502,18 +14163,6 @@ packages: license_family: MIT size: 133769 timestamp: 1780932915297 -- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - sha256: 4acf845da404e84cef1acccc66cc0156af1b83a5b5d7077b2ca19b705c561e57 - md5: 99f7755ec8648a042b0dbe906234f888 - depends: - - python >=3.10 - license: MIT - license_family: MIT - purls: - - pkg:pypi/wcwidth?source=hash-mapping - run_exports: {} - size: 132415 - timestamp: 1782771807703 - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda sha256: 93807369ab91f230cf9e6e2a237eaa812492fe00face5b38068735858fba954f md5: 46e441ba871f524e2b067929da3051c2 @@ -15685,24 +14334,6 @@ packages: license_family: MIT size: 1086020 timestamp: 1780396657560 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda - noarch: python - sha256: 51c8a631b36bf2eb2f27d49c0a0df9e269a76b91a447da7e75f9d0854cabb4a7 - md5: 5ce8b03801df5eff0088bdc85a19d09a - depends: - - python >=3.10 - - __osx >=11.0 - - _python_abi3_support 1.* - - cpython >=3.10 - constrains: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ast-serialize?source=compressed-mapping - run_exports: {} - size: 1085485 - timestamp: 1786328966627 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda sha256: b0747f9b1bc03d1932b4d8c586f39a35ac97e7e72fe6e63f2b2a2472d466f3c1 md5: 57301986d02d30d6805fdce6c99074ee @@ -16580,21 +15211,6 @@ packages: license_family: MIT size: 292983 timestamp: 1761203354051 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314hd58f0ed_1.conda - sha256: 438a5384d308f26ccd6f5f4f2b2153dfb240db4320d6fc8240036eb338a5355c - md5: 6a60fcbc6a6a079d4f47ffce008121e2 - depends: - - __osx >=11.0 - - libffi >=3.7.0,<3.8.0a0 - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - purls: - - pkg:pypi/cffi?source=compressed-mapping - run_exports: {} - size: 297356 - timestamp: 1786528517794 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.4.1-h793ed5c_0.conda sha256: cad6c9f86f98f1ac980e8229ef76a9bb8f62d167a52d29770e0548c7f9a80eb1 md5: c2a9a79b58d2de021ad9295f53e1f40a @@ -16779,46 +15395,6 @@ packages: license_family: BSD size: 1359398 timestamp: 1781681460540 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.0-py314hcec6336_0.conda - sha256: fe9412de3a6321fe69ab14f0660282e42fb300f524766cf258eb31f4142ab543 - md5: 538348170fcc3620c4f7f347dc288557 - depends: - - archspec >=0.2.3 - - boltons >=23.0.0 - - charset-normalizer - - conda-package-handling >=2.2.0 - - distro >=1.5.0 - - frozendict >=2.4.2 - - jsonpatch >=1.32 - - menuinst >=2 - - packaging >=23.0 - - platformdirs >=3.10.0 - - pluggy >=1.6.0 - - pycosat >=0.6.3 - - python - - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 - - setuptools >=60.0.0 - - tqdm >=4 - - truststore >=0.8.0 - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - - conda-pypi >=0.10.1 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - - python 3.14.* *_cp314 - - python_abi 3.14.* *_cp314 - constrains: - - conda-build >=25.9 - - conda-content-trust >=0.3.0 - - conda-env >=2.6 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda?source=hash-mapping - run_exports: {} - size: 1468859 - timestamp: 1785769918540 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.10.1-py314hcec6336_2.conda sha256: 48c5bba381b7a9b6db4b70f39962fdeb12a53a0935d70d513437cb589b4a473d md5: 652a02b8973cd22717c108ffa6873744 @@ -16839,30 +15415,6 @@ packages: license_family: MIT size: 322322 timestamp: 1781646689091 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda - sha256: 4da4678a03488c985aed27956dd1a35281b515240ff33a3fe9238f3756fa6059 - md5: a123df8bb2f1dc4f85ba6bbddb744968 - depends: - - python - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python 3.14.* *_cp314 - - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/conda-pypi?source=hash-mapping - run_exports: {} - size: 328032 - timestamp: 1784203150047 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c md5: f9cce0bc86b46533489a994a47d3c7d2 @@ -16897,17 +15449,6 @@ packages: run_exports: {} size: 290405 timestamp: 1769156069514 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda - sha256: 83819551a243ac95a96867977a0c7e2661d9e06ca268026c93d2c9f93969424f - md5: 93748a43e1a27821795af6701b7c06be - depends: - - libcxx >=19 - - __osx >=11.0 - license: CC0-1.0 - purls: [] - run_exports: {} - size: 25909 - timestamp: 1785920661291 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h4f10f1e_0.conda sha256: a7380056125a29ddc4c4efc4e39670bc8002609c70f143d92df801b42e0b486f md5: 5cb4f9b93055faf7b6ae76da6123f927 @@ -17178,20 +15719,6 @@ packages: license_family: MIT size: 180970 timestamp: 1767681372955 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda - sha256: ca42427b99ff92228e907bed5f10a1df868180997571d29c230faad2c38e35fd - md5: 891b1202d7b835f215e26a4db685ec7c - depends: - - __osx >=11.0 - - libcxx >=19 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=12.1.0,<12.2.0a0 - size: 181008 - timestamp: 1785915450316 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda sha256: 8607d8d0b32f9f6fc61ea8c06b537486b78428a04516658222fa4d1d521af765 md5: 9d928e6a62192141fb6540a3125b1345 @@ -17333,25 +15860,6 @@ packages: license_family: LGPL size: 548309 timestamp: 1774986047281 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.7-h4e57454_0.conda - sha256: 69bb2e62a93f6407c9e9ccd4d21fe4d4e5373d64eecd6c5df318144ca4c80953 - md5: f717a22e13a1499c9552ffff02d22d64 - depends: - - __osx >=11.0 - - libglib >=2.88.2,<3.0a0 - - libintl >=0.25.1,<1.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblzma >=5.8.3,<6.0a0 - - libpng >=1.6.58,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - license: LGPL-2.1-or-later - license_family: LGPL - purls: [] - run_exports: - weak: - - gdk-pixbuf >=2.44.7,<3.0a0 - size: 553902 - timestamp: 1782591436963 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.12.2-h00cdb27_1.conda sha256: 1090c9b8e9a628eb2f9718d19e3cc6372c39cf763295c446a3da9288d0dbba11 md5: e07a7fa032741a056a72f46b43064ea2 @@ -17460,19 +15968,6 @@ packages: license: LGPL-2.1-or-later size: 204902 timestamp: 1778508895255 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-hd03a632_1.conda - sha256: 200aec53216f3323b2072fb994b10fbee018ff3b894162a37222f1f316c3e476 - md5: 842c53118ef3b15433627ee2c384c203 - depends: - - libglib ==2.88.3 ha531971_1 - - libffi - - __osx >=11.0 - - libintl >=0.25.1,<1.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 204965 - timestamp: 1786457780347 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda sha256: 1ac5991fd354de81baf3892fee0ad09438623698fcad73758521dfe90711ed44 md5: 68937f90f7930d49e106b94e95d4536c @@ -17576,22 +16071,6 @@ packages: license_family: MIT size: 263816 timestamp: 1781762277927 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda - sha256: d960aefe961553b75450b7584f2fad7693ed1fac423da0fc5a9d649c48d2a019 - md5: ae860c6208235503f82ef3019461d410 - depends: - - python - - __osx >=11.0 - - libcxx >=19 - - python 3.14.* *_cp314 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/greenlet?source=compressed-mapping - run_exports: {} - size: 274201 - timestamp: 1786384265137 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda sha256: 26862a9898054b8552e55e609e5ce73c7ef1eb28bbe6fb87f0b9109d73cd09df md5: 5557a2433b1339b8e536c264afea41ef @@ -17654,19 +16133,6 @@ packages: license_family: MIT size: 1721040 timestamp: 1780451752518 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.3.0-hce30654_0.conda - sha256: 67042b0e8896f707c60981947ae22fe0c13942283c8d7ac9b8286cc01cc1c2d2 - md5: aab13bb027c8295f5b09ccb8cd084698 - depends: - - libharfbuzz-devel 14.3.0 h5a65909_0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libharfbuzz >=14.3.0 - size: 11005 - timestamp: 1785770060418 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda sha256: c3b01e3c3fe4ca1c4d28c287eaa5168a4f2fd3ffd76690082ac919244c22fa90 md5: ff5d749fd711dc7759e127db38005924 @@ -19240,30 +17706,6 @@ packages: run_exports: {} size: 942277 timestamp: 1785770026085 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.3.0-h5a65909_0.conda - sha256: ec6822ad0101e1d627aebb04792a0a642a706ba3c12e2691a90c7c3995b09fc1 - md5: 9b9a765bb25ac591d79ced348d4dc340 - depends: - - __osx >=11.0 - - cairo >=1.18.4,<2.0a0 - - freetype - - graphite2 >=1.3.15,<2.0a0 - - icu >=78.3,<79.0a0 - - libcxx >=19 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz 14.3.0 h5a65909_0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libharfbuzz >=14.3.0 - size: 1470616 - timestamp: 1785770054858 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda sha256: e2b0108325d360961750bd35d975ca59694f9c1efff6ec241470c68ca09cacc0 md5: 5b102fdc40afa0b6030c7867d939c00e @@ -19408,28 +17850,10 @@ packages: - liblapack 3.11.0 8_hd9741b5_openblas constrains: - blas 2.308 openblas - license: BSD-3-Clause - license_family: BSD - size: 18968 - timestamp: 1779859160325 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-9_h1b118fd_openblas.conda - build_number: 9 - sha256: 38b9e1b621ce246b4e0b961255e6d88aed867d8ba7e28d5aa7419ab975f23b77 - md5: 9671fc36704c91fbfe988ce9429e6c6a - depends: - - libblas 3.11.0 9_h51639a9_openblas - - libcblas 3.11.0 9_hb0561ab_openblas - - liblapack 3.11.0 9_hd9741b5_openblas - constrains: - - blas 2.309 openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - liblapacke >=3.11.0,<3.12.0a0 - size: 18174 - timestamp: 1786058909286 + license: BSD-3-Clause + license_family: BSD + size: 18968 + timestamp: 1779859160325 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda sha256: 34878d87275c298f1a732c6806349125cebbf340d24c6c23727268184bba051e md5: b1fd823b5ae54fbec272cea0811bd8a9 @@ -19495,34 +17919,6 @@ packages: license_family: BSD size: 1819419 timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda - sha256: 5291da681b85d4a12483b0f9a57bf18e191d0a52e749f18266922457b5bc200c - md5: 1646d42b756ad5a554903866fc2871a1 - depends: - - cpp-expected >=1.3.1,<1.3.2.0a0 - - libcxx >=19 - - __osx >=11.0 - - libmsgpack-c >=6.1.0,<7.0a0 - - simdjson >=4.6.6,<4.7.0a0 - - reproc >=14.2.7.post0,<14.3.0a0 - - libcurl >=8.21.0,<9.0a0 - - fmt >=12.1.0,<12.2.0a0 - - libsolv >=0.7.39,<0.8.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - zstd >=1.5.7,<1.6.0a0 - - libarchive >=3.8.9,<3.9.0a0 - - openssl >=3.5.7,<4.0a0 - - nlohmann_json-abi ==3.12.0 - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libmamba >=2.9.0,<2.10.0a0 - size: 1846328 - timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.8.1-hb995d7d_0.conda sha256: b18cbec4a4c33ab0e47ecf5a6973c045e14c6e0e60d4c92ee7f7bafc0f0872e1 md5: 09a9c695f497c6f39a99ae6d0335837d @@ -19534,19 +17930,6 @@ packages: license_family: BSD size: 23141 timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda - sha256: 7e379c5b7527d878e2617237b73d7866dbdf3d9dbaf36352ad39ab0a85802831 - md5: c0894144cb6c8d04f7699a5f5ead0c64 - depends: - - libcxx >=19 - - __osx >=11.0 - - libmamba >=2.9.0,<2.10.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 23751 - timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.8.1-py314h96ff89e_0.conda sha256: 87894c7d410ef8e96763aa0dd7774e5daeb411099da884ca29c1630a4f2ef7ab md5: b9dcc4e28de586704989de517ad499de @@ -19570,34 +17953,6 @@ packages: license_family: BSD size: 778735 timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda - sha256: 785d75a82bf43c123222a3933f84a7c2d1f9d83eb5a07d2552c75725f17a6309 - md5: c57c6b1c9fe4b161997ce6a6a705faf6 - depends: - - python - - libmamba ==2.9.0 py314hf3ae1d6_0 - - libmamba-spdlog ==2.9.0 heb3ad76_0 - - __osx >=11.0 - - libcxx >=19 - - python 3.14.* *_cp314 - - zstd >=1.5.7,<1.6.0a0 - - libmamba >=2.9.0,<2.10.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - - openssl >=3.5.7,<4.0a0 - - fmt >=12.1.0,<12.2.0a0 - - python_abi 3.14.* *_cp314 - - pybind11-abi ==11 - - nlohmann_json-abi ==3.12.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/libmambapy?source=hash-mapping - run_exports: - weak: - - libmambapy >=2.9.0,<2.10.0a0 - size: 779694 - timestamp: 1786125740624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda sha256: 1089c7f15d5b62c622625ec6700732ece83be8b705da8c6607f4dabb0c4bd6d2 md5: 57c4be259f5e0b99a5983799a228ae55 @@ -19620,21 +17975,6 @@ packages: license: BSL-1.0 size: 39063 timestamp: 1770807536463 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda - sha256: 7eea97b70943a054f90405b1c17da0c8b25348c77bc815978fac05d99e574334 - md5: bfb208af98c6c65859211b972e45cafd - depends: - - libcxx >=19 - - __osx >=11.0 - constrains: - - msgpack-c ==6.1.0 *_7 - license: BSL-1.0 - purls: [] - run_exports: - weak: - - libmsgpack-c >=6.1.0,<7.0a0 - size: 39302 - timestamp: 1786189396450 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_he469be0_114.conda sha256: aeac591ba859f9cf775993e8b7f21e50803405d41ef363dc4981d114e8df88a8 md5: 8fd3ce6d910ed831c130c391c4364d3f @@ -20613,19 +18953,6 @@ packages: license: BSD-3-Clause AND MIT size: 212016 timestamp: 1782062817762 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_1.conda - sha256: 3c314bb8dd4fcc430056bd8f5b1ace082c320d476d4ba3fa81bc087f4c02e99d - md5: b218a1fdb2e753116f1642f8d2c582c5 - depends: - - python - - tomli-w - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause AND MIT - purls: - - pkg:pypi/menuinst?source=hash-mapping - run_exports: {} - size: 207074 - timestamp: 1786008880338 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda sha256: 4b1d4e4b17d27bbec50b1ad144e000c3b077cc4e0ef487ef11011c52ef8c86ab md5: fcd860469a8fa003e25d472b40b0ff81 @@ -20742,26 +19069,6 @@ packages: license_family: MIT size: 13520137 timestamp: 1780315680928 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.0-py314h2fbedac_0.conda - sha256: 614b2b3941810a3747c521f496bd22dcd36f27749e065982e02d02f1f9aaa170 - md5: 416fa3630816cb0deb540caef775461a - depends: - - ast-serialize >=0.6.0,<1.0.0 - - mypy_extensions >=1.0.0 - - pathspec >=1.0.0 - - python - - python-librt >=0.13.0 - - typing_extensions >=4.6.0 - - psutil >=4.0 - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/mypy?source=hash-mapping - run_exports: {} - size: 13696727 - timestamp: 1783986170854 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda sha256: 4ea6c620b87bd1d42bb2ccc2c87cd2483fa2d7f9e905b14c223f11ff3f4c455d md5: 343d10ed5b44030a2f67193905aea159 @@ -21183,29 +19490,6 @@ packages: license: LGPL-2.1-or-later size: 425743 timestamp: 1774282709773 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - sha256: c300fba11c4cd7cdd7609b6f165981af24d2181d40280ca6af420710c4c7d42e - md5: 83c3d3d895dd96f87b0a1916e2c41f70 - depends: - - __osx >=11.0 - - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 - - fonts-conda-ecosystem - - fribidi >=1.0.16,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - pango >=1.58.2,<2.0a0 - size: 444011 - timestamp: 1786108209790 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-ha881caa_2.conda sha256: 797411a2d748c11374b84329002f3c65db032cbf012b20d9b14dba9b6ac52d06 md5: 1a3f7708de0b393e6665c9f7494b055e @@ -21509,25 +19793,6 @@ packages: license_family: BSD size: 10707077 timestamp: 1781020721263 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_2.conda - noarch: python - sha256: 42ebc11b1bc7adb0e1732eb0915e5078fb1d398587f3dcce5dcb9bda376db7cf - md5: e892ed39dc91aa9372ac09fd72e1439f - depends: - - python - - __osx >=11.0 - - openssl >=3.5.7,<4.0a0 - - _python_abi3_support 1.* - - cpython >=3.10 - constrains: - - __osx >=11.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/py-rattler?source=hash-mapping - run_exports: {} - size: 10605428 - timestamp: 1784707901992 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda sha256: d6433c343120e723cad92b52ea05c16e05096845489275a697201ce0a50fc568 md5: 04a90c4ce691f2e289658dd475f69631 @@ -21618,20 +19883,6 @@ packages: license_family: MIT size: 92986 timestamp: 1757744788529 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda - sha256: 640d2d29a18e3a8dda02427616bd55af5670565fd780f7b5df4c045548471909 - md5: 99e06db67584235df895feda84a4790e - depends: - - __osx >=11.0 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pycosat?source=hash-mapping - run_exports: {} - size: 93498 - timestamp: 1784146274338 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda sha256: 480b10f3197270a98c2b564670ca6e201bc57b5005e9d58fd0d232338fd33ad7 md5: e38d54c5e4318faa79f71b713b059566 @@ -21833,21 +20084,6 @@ packages: license_family: MIT size: 129714 timestamp: 1778511905677 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda - sha256: 07e71e940c1268e224c9a6a1f96480c10789ca521928cda30c20aa07f19aca21 - md5: e82ce608933c6feb7fc9ef479d2a2227 - depends: - - python - - python 3.14.* *_cp314 - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/librt?source=compressed-mapping - run_exports: {} - size: 134968 - timestamp: 1786328898613 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda sha256: 37d76254955eaa47df534ae8f3890c45f28aa4a8479597ff6fbacbd5a23810e6 md5: 45bfaa1961db6ace6fc35561ba893fae @@ -22023,19 +20259,6 @@ packages: - readline >=8.3,<9.0a0 size: 313930 timestamp: 1765813902568 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h784d473_2.conda - sha256: cebb569a3bda2a86754b2765f1927c6ac90b6e964c5bd6d58e053905b9982c97 - md5: 197a47dbf3f273e37d7d2ffde2bb0e37 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc >=14.2.7.post0,<14.3.0a0 - size: 37036 - timestamp: 1785921908208 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h84a0fba_1.conda sha256: f832ce7f2b3f6067ac2e086bff011d487fc2b18c3d2e2f9c5c2525f3ff21bff4 md5: 5a2e62653a06ab1b810e50bb02dae288 @@ -22045,22 +20268,6 @@ packages: license_family: MIT size: 33488 timestamp: 1779092919587 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hcf2a89d_2.conda - sha256: 8c6647329392efa2493e02203f4b372f8df6d393455c2e53915a7f84402fe57f - md5: 20ede03e20f3cfb712737d31f621c1bd - depends: - - reproc ==14.2.7.post0 h784d473_2 - - __osx >=11.0 - - libcxx >=19 - - reproc >=14.2.7.post0,<14.3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - size: 29885 - timestamp: 1785921908208 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hf6b4638_1.conda sha256: f9710955247cd5890797a27db53474d682cd815f8d0c02795e60881d8f9791be md5: 9cbb9e0c4f09302ddd975887da013015 @@ -22085,9 +20292,9 @@ packages: license_family: MIT size: 292087 timestamp: 1779977082395 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314he1d1ac0_0.conda - sha256: 30bf9b362be5f04ccb7c0d9549f474763fb6fd11e7f0b78e5286b881f3c382cf - md5: 2e8e2fe25e9d6df3628b068fe7407299 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda + sha256: 7d4b49a7542735c63f0ef3379f83dfed1870a5d62ae4c3e357f859014fb1fcaf + md5: aa2a7e9c0ab5322b9bd6e75a384b48a1 depends: - python - __osx >=11.0 @@ -22099,8 +20306,8 @@ packages: purls: - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} - size: 285627 - timestamp: 1782831308617 + size: 284937 + timestamp: 1787344325551 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda sha256: 0f498e343be464219a99424260ff442144d0461a3a5eb30c9de6081af358b281 md5: 87fc3a204f105e7e61c60a72509cccbd @@ -22145,22 +20352,6 @@ packages: license_family: MIT size: 8644257 timestamp: 1782288982578 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.2-h828de30_0.conda - noarch: python - sha256: 8900877bfd18dcb27077a1dd9eaa7230ccae8bd6d7fada9b91c3bfbdf705f808 - md5: 57b0c0a8868a452c34904b01e99f3e45 - depends: - - python - - __osx >=11.0 - constrains: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ruff?source=hash-mapping - run_exports: {} - size: 8732289 - timestamp: 1786152041950 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda sha256: 47ec36bdd05117c974853a8dbaa9d89605a5f70b592d47c7d198b20252ddf1f4 md5: 94b3b302cd6c07c5ec68648b25c8c525 @@ -22311,19 +20502,6 @@ packages: license_family: APACHE size: 275639 timestamp: 1778109594368 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.6-h3feff0a_1.conda - sha256: 3fe0b91319d7754a44aa8577abdb638add85d9408aecaa6c87a3c904720f8c43 - md5: 12e5ea9092b8563bc2575feda2ec9156 - depends: - - __osx >=11.0 - - libcxx >=19 - license: Apache-2.0 - purls: [] - run_exports: - weak: - - simdjson >=4.6.6,<4.7.0a0 - size: 311350 - timestamp: 1786528290494 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py312h2bbb03f_0.conda sha256: 24e5aeb84a6c58f4fb5a0c9b6966106f49af85d62c1b5789d94be00c24b16d6b md5: 85801137e6f7634e0fa9cd7a8070c2d5 @@ -22383,21 +20561,6 @@ packages: - spdlog >=1.14.1,<1.15.0a0 size: 164011 timestamp: 1722238482313 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda - sha256: 791d91a18647346c4318a4177add14abc546632fc2bf12a049cc328025368054 - md5: e4df5c4de3a06437b69ff6255c258ca2 - depends: - - __osx >=11.0 - - fmt >=12.1.0,<12.2.0a0 - - libcxx >=19 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.17.0,<1.18.0a0 - size: 167030 - timestamp: 1785925098650 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-ha0f8610_1.conda sha256: 465e81abc0e662937046a2c6318d1a9e74baee0addd51234d36e08bae6811296 md5: 1885f7cface8cd627774407eeacb2caf @@ -22423,21 +20586,6 @@ packages: license_family: MIT size: 4036851 timestamp: 1781547978515 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda - sha256: 9c86803397cf137bf92d517b6a4395d059cfb3dbfc1def708ed5963c5848db48 - md5: 61db956083da31d178a6940043d176d2 - depends: - - python - - greenlet !=0.4.17 - - typing-extensions >=4.6.0 - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - license: MIT - purls: - - pkg:pypi/sqlalchemy?source=compressed-mapping - run_exports: {} - size: 4044830 - timestamp: 1786535242452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda sha256: 95482d44bef095da3c63be6e0c66a2e5b66a2a551e11d4c40949c075df13ec2c md5: b9df2fcb7e9eba945c1c946c438c4586 @@ -22782,20 +20930,6 @@ packages: - yaml >=0.2.5,<0.3.0a0 size: 83386 timestamp: 1753484079473 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda - sha256: 450d4c6ad26bc2c18eb9420261568b86918b118c96ab4a7f2176c352b94101a2 - md5: 04b9b2b8cc9e14758965758d9c423b34 - depends: - - libcxx >=19 - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - yaml-cpp >=0.8.0,<0.9.0a0 - size: 136591 - timestamp: 1785924101870 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-ha1acc90_0.conda sha256: 66ba31cfb8014fdd3456f2b3b394df123bbd05d95b75328b7c4131639e299749 md5: 30475b3d0406587cf90386a283bb3cd0 @@ -22947,24 +21081,6 @@ packages: license_family: MIT size: 1080282 timestamp: 1780396676915 -- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda - noarch: python - sha256: c52d4750dce807f24a4698fb3c8894b8c6e7cfca8000f5e4050a8e43d24eaf1f - md5: ecde03792069f1f16ff12c2395635035 - depends: - - python >=3.10 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - _python_abi3_support 1.* - - cpython >=3.10 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ast-serialize?source=compressed-mapping - run_exports: {} - size: 1071375 - timestamp: 1786328955132 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda sha256: cf7ab8abaac6b462463310412cc37d087a767a95920809cbe6dc0a08fe4bcfbd md5: f7069cdc81f7a5e781f1faf6aab6c171 @@ -23868,22 +21984,6 @@ packages: license_family: MIT size: 294731 timestamp: 1761203441365 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_1.conda - sha256: e5b83726f0f3e69f9b06d582e3d8edbee0460be5df3e4f8e8b9a25c5b0f194f8 - md5: ef393e3c209458030510af02315d1762 - depends: - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - purls: - - pkg:pypi/cffi?source=compressed-mapping - run_exports: {} - size: 347503 - timestamp: 1786528551392 - conda: https://conda.anaconda.org/conda-forge/win-64/cfitsio-4.4.1-hc2ea260_0.conda sha256: 97249ec67f115c05a2a452e62f6aed2e3f3a244ba1f33b0e9395a05f9d7f6fee md5: b3263858e6a924d05dc2e9ce335593ba @@ -24042,74 +22142,17 @@ packages: constrains: - conda-build >=25.9 - conda-env >=2.6 - - conda-content-trust >=0.3.0 - license: BSD-3-Clause - license_family: BSD - size: 1362284 - timestamp: 1781681191655 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.0-py314hb821551_0.conda - sha256: a4133e348175a541ea2e8a4bc6e5e123edca76548151fd1f33f44a24eb541cd9 - md5: 201bcb933dcd153c3dfd8c70709dd519 - depends: - - archspec >=0.2.3 - - boltons >=23.0.0 - - charset-normalizer - - conda-package-handling >=2.2.0 - - distro >=1.5.0 - - frozendict >=2.4.2 - - jsonpatch >=1.32 - - menuinst >=2 - - packaging >=23.0 - - platformdirs >=3.10.0 - - pluggy >=1.6.0 - - pycosat >=0.6.3 - - python - - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 - - setuptools >=60.0.0 - - tqdm >=4 - - truststore >=0.8.0 - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - - conda-pypi >=0.10.1 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - - python_abi 3.14.* *_cp314 - constrains: - - conda-build >=25.9 - - conda-content-trust >=0.3.0 - - conda-env >=2.6 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/conda?source=hash-mapping - run_exports: {} - size: 1472250 - timestamp: 1785769861157 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.10.1-py314hb821551_2.conda - sha256: f1440b3dea081d6fe76ddffba6e1f01a4d8b97f4a7b3f8fef889ffe3b0ee0e1c - md5: 0c4992e5a5d69efdaa97ab733bc7f530 - depends: - - python - - conda >=26.1.0 - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - size: 317787 - timestamp: 1781646513612 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda - sha256: af3766a095bd7b2f5f0a8958474d29c139e51628230b98b7e45ff4af1702c8ae - md5: bab6a2a990c12195bb6553216ab660ec + - conda-content-trust >=0.3.0 + license: BSD-3-Clause + license_family: BSD + size: 1362284 + timestamp: 1781681191655 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.10.1-py314hb821551_2.conda + sha256: f1440b3dea081d6fe76ddffba6e1f01a4d8b97f4a7b3f8fef889ffe3b0ee0e1c + md5: 0c4992e5a5d69efdaa97ab733bc7f530 depends: - python + - conda >=26.1.0 - pip >=23.0.1 - packaging - unearth @@ -24119,15 +22162,10 @@ packages: - conda-index >=0.11.0 - conda-package-streaming >=0.11 - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 license: MIT license_family: MIT - purls: - - pkg:pypi/conda-pypi?source=hash-mapping - run_exports: {} - size: 323375 - timestamp: 1784203061870 + size: 317787 + timestamp: 1781646513612 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda sha256: 5f0dd3a4243e8293acc40abf3b11bcb23401268a1ef2ed3bce4d5a060383c1da md5: 475bd41a63e613f2f2a2764cd1cd3b25 @@ -24175,18 +22213,6 @@ packages: license: CC0-1.0 size: 21733 timestamp: 1756734797622 -- conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda - sha256: ca7f86a8f5d91dffcbc0a720bb4c3be8b35ccfb8e1e44cfa43c95dd13b6c83fc - md5: 8f83a0d68492c0d19528ce750d6566b9 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: CC0-1.0 - purls: [] - run_exports: {} - size: 22027 - timestamp: 1785920525392 - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda sha256: ec5cce1b3a66fa8f154e9597d628988db79fb015d375351562376f80a7e965d1 md5: 7a20e585f458d44b5fd1e59bb7d95e32 @@ -24445,21 +22471,6 @@ packages: - fmt >=11.0.2,<11.1.0a0 size: 190055 timestamp: 1743031131636 -- conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda - sha256: 1d6d6f1bf284e50cf617ce9a6250e0bf70bd0d30b068568364d6b1852aad9466 - md5: ca8fa0e01ed34bb161f4e8e86dfda22e - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=12.1.0,<12.2.0a0 - size: 187511 - timestamp: 1785915492086 - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h7f4e812_0.conda sha256: cce96406ec353692ab46cd9d992eddb6923979c1a342cbdba33521a7c234176f md5: 6e226b58e18411571aaa57a16ad10831 @@ -24784,22 +22795,6 @@ packages: license_family: MIT size: 248927 timestamp: 1781762180310 -- conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda - sha256: c1f9d4b92bf255fc5cc92ea10448d812417fd26132856cefe9f41f2d5ccc2f8a - md5: 212518796fa1c60ca92e4d1c8ef80ce3 - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/greenlet?source=compressed-mapping - run_exports: {} - size: 259251 - timestamp: 1786384106710 - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda sha256: b79755d2f9fc2113b6949bfc170c067902bc776e2c20da26e746e780f4f5a2d4 md5: a41f14768d5e377426ad60c613f2923b @@ -26766,35 +24761,6 @@ packages: license_family: BSD size: 5316504 timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda - sha256: a95ff44e76be486507973dda39600f8309194f61a7b80d9ba65967aa90077de5 - md5: ab365478ee65fbdb478e51c855fa481b - depends: - - cpp-expected >=1.3.1,<1.3.2.0a0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - reproc >=14.2.7.post0,<14.3.0a0 - - fmt >=12.1.0,<12.2.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - nlohmann_json-abi ==3.12.0 - - simdjson >=4.6.6,<4.7.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - - zstd >=1.5.7,<1.6.0a0 - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - - libmsgpack-c >=6.1.0,<7.0a0 - - libsolv >=0.7.39,<0.8.0a0 - - libarchive >=3.8.9,<3.9.0a0 - - libcurl >=8.21.0,<9.0a0 - - openssl >=3.5.7,<4.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libmamba >=2.9.0,<2.10.0a0 - size: 5416175 - timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.8.1-h7dcf005_0.conda sha256: 64e869c54e328ca5b184932c43fad9a4172975fdf8a088da89f8182f9a7b6c9a md5: c3424255d9c261f649e6d1851102b458 @@ -26807,20 +24773,6 @@ packages: license_family: BSD size: 18083 timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda - sha256: 9b43a18a720163f746b26c1178a198cb5a8a829ecdc3ac8d66ad5b4116b0e5ab - md5: 42e4a919086813b2c14aaed13ec73aec - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - libmamba >=2.9.0,<2.10.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 18480 - timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.8.1-py314h40f010b_0.conda sha256: 5106c6952325c512715aadca45dd93febac92008cf907207c97ff4bbdb36f913 md5: 23d746a541606c9142fc7518fadcda2f @@ -26844,34 +24796,6 @@ packages: license_family: BSD size: 594639 timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda - sha256: cc735d3f77c5cf2f8b2263c41f60fd2365015a7eddecdab95aa913f7e32fc3a4 - md5: 41fe4e717b819c816fe498fb37317ab7 - depends: - - python - - libmamba ==2.9.0 py314h08176f6_0 - - libmamba-spdlog ==2.9.0 h9ac7702_0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - nlohmann_json-abi ==3.12.0 - - pybind11-abi ==11 - - openssl >=3.5.7,<4.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - python_abi 3.14.* *_cp314 - - yaml-cpp >=0.8.0,<0.9.0a0 - - libmamba >=2.9.0,<2.10.0a0 - - fmt >=12.1.0,<12.2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/libmambapy?source=compressed-mapping - run_exports: - weak: - - libmambapy >=2.9.0,<2.10.0a0 - size: 596336 - timestamp: 1786125710299 - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda sha256: 40dcd0b9522a6e0af72a9db0ced619176e7cfdb114855c7a64f278e73f8a7514 md5: e4a9fc2bba3b022dad998c78856afe47 @@ -26897,22 +24821,6 @@ packages: license: BSL-1.0 size: 40694 timestamp: 1770807535968 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda - sha256: 9b81aaa83ae15710a3466053bdf9090b1a4ba9ec40c8231b90f0efd5e3d02855 - md5: 7928697b653d56d99504b533b02f3c64 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - constrains: - - msgpack-c ==6.1.0 *_7 - license: BSL-1.0 - purls: [] - run_exports: - weak: - - libmsgpack-c >=6.1.0,<7.0a0 - size: 40862 - timestamp: 1786189315611 - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_h92078aa_114.conda sha256: 111fb98bf02e717c69eb78388a5b03dc7af05bfa840ac51c2b31beb70bf42318 md5: 819507db3802d9a179de4d161285c22f @@ -27912,22 +25820,6 @@ packages: license: BSD-3-Clause AND MIT size: 198561 timestamp: 1782062732702 -- conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_1.conda - sha256: a16fdf28db19ba4636dbfc7b64021cca818a08510d92df74a5cd3009d814fe5e - md5: 00a6619238f1da07faec8f357cbe7733 - depends: - - python - - tomli-w - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause AND MIT - purls: - - pkg:pypi/menuinst?source=hash-mapping - run_exports: {} - size: 199350 - timestamp: 1786008821495 - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda sha256: 208b235b20232891d1dc9f468d08b00c53e7ca65a18b9bc0ff4c4867680b9a75 md5: 5d55038c4d640e5df06de1cfb4e8d741 @@ -28090,28 +25982,6 @@ packages: license_family: MIT size: 10734309 timestamp: 1780315652597 -- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.0-py314h13f4da2_0.conda - sha256: 04cbd9e2a351eae6b2039e3c1ad10961fa879899fc430d2b88ba2c6b39021089 - md5: 4c9b071b4bf13daa16c940be2699e6e7 - depends: - - ast-serialize >=0.6.0,<1.0.0 - - mypy_extensions >=1.0.0 - - pathspec >=1.0.0 - - python - - python-librt >=0.13.0 - - typing_extensions >=4.6.0 - - psutil >=4.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/mypy?source=hash-mapping - run_exports: {} - size: 10913265 - timestamp: 1783986254963 - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda sha256: 460d5a644bd7f195784f7e17d550274986ddffa1d534b18195322de1f81cf42d md5: faf4e3f7981777629dd7cbef578834ad @@ -28492,31 +26362,6 @@ packages: license: LGPL-2.1-or-later size: 454919 timestamp: 1774282149607 -- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda - sha256: 85fc9c2a3b805d7ad784b6b307f4cf4833d3525ab409a7f86262d476a67d441f - md5: 22d750d2ebf4109bc66c036f1e52b982 - depends: - - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 - - fonts-conda-ecosystem - - fribidi >=1.0.16,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - pango >=1.58.2,<2.0a0 - size: 466294 - timestamp: 1786107518608 - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda sha256: 15dffc9a2d6bb6b8ccaa7cbd26b229d24f1a0a1c4f5685b308a63929c56b381f md5: a912b2c4ff0f03101c751aa79a331831 @@ -28833,24 +26678,6 @@ packages: license_family: BSD size: 13201940 timestamp: 1781020758113 -- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_2.conda - noarch: python - sha256: c34345f682fba5bc0e985418205eab6b16f76e8fc8172b1354ecf3ac6985687a - md5: 7b14c85bd80ca0ddadacdf19c658823c - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - _python_abi3_support 1.* - - cpython >=3.10 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/py-rattler?source=hash-mapping - run_exports: {} - size: 13161702 - timestamp: 1784707929736 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda sha256: bc8553befae5d33ddf0d99262de4b7ffbe798741e01b202b7d8d6336d4f2142b md5: 5601751f674c1ecc1629cdf76f22ab61 @@ -28944,22 +26771,6 @@ packages: license_family: MIT size: 79551 timestamp: 1757744726713 -- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda - sha256: 38d9062eca5d85ac5de080b3512edc4e0f89c64199afcd2da34cd432b5d2514d - md5: c79e745df2f185732d9cdb5763452440 - depends: - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pycosat?source=hash-mapping - run_exports: {} - size: 80121 - timestamp: 1784146283080 - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda sha256: 2b251fb56d07efd36815732477d22bcdd2c7a6e7500012eb024d8c59413d2e24 md5: edd458c91ab9bc198012c73289b0b0bc @@ -29222,22 +27033,6 @@ packages: license_family: MIT size: 104479 timestamp: 1778511743226 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda - sha256: f69e11b4025a7467a1820ce348e39af70d4eb0508e89375d3c2e501c4b8e532f - md5: e43c348d1855554ddd3ccd385940386f - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/librt?source=compressed-mapping - run_exports: {} - size: 108739 - timestamp: 1786328843266 - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda sha256: 2c97a59ce98b9d29a30bc49f3001b9206ce789edaee7d2773cde8a8a7f78abc4 md5: 02632a618ced2283cf9a7c4a70a43026 @@ -29489,21 +27284,6 @@ packages: - re2 size: 222198 timestamp: 1781312702688 -- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-h5112557_2.conda - sha256: 74b9dfb94e5fe2a2d2b6467ea3e91e83d66e174bf58790443bd74218784be395 - md5: 494d01b7baff75c04252ff035cb44e79 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc >=14.2.7.post0,<14.3.0a0 - size: 38715 - timestamp: 1785921791755 - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-hfd05255_1.conda sha256: a7060320c3f6c9ec7d8222a2a217f02bcf3f41ecf5a5d1a375c6280b604962f2 md5: 9f428ede23b9a119bd2c0330bfc51851 @@ -29515,23 +27295,6 @@ packages: license_family: MIT size: 38712 timestamp: 1779092487585 -- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-h03e4b45_2.conda - sha256: d87e5fa1e009110bd2905d0e1bc2404da23b365d2ebbc1b3696c7705a36898ac - md5: b905cc386fe540a24ab701c9b8344050 - depends: - - reproc ==14.2.7.post0 h5112557_2 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - reproc >=14.2.7.post0,<14.3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - size: 31831 - timestamp: 1785921791755 - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-hac47afa_1.conda sha256: 4cbe19c86a5c515724ebfbce34f1bf2a9f08229e704c99d811c00629e27a4671 md5: 6bf1a32350e40dafb15d85080ad84408 @@ -29557,9 +27320,9 @@ packages: license_family: MIT size: 229575 timestamp: 1779977051010 -- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314hc980628_0.conda - sha256: ac1047c2a9bb3dc7cac31b7c995a7b25f9a4eb52ee8ae106ca6e6c15679f34fc - md5: 52429bdb1752b43f8364bc482cf65fda +- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda + sha256: d297e7ef5f062194cf76dcf7a2e0449af24012b895a06aa942805f2933a0256c + md5: 07602f253d1ad9436bcac564c9fe183f depends: - python - vc >=14.3,<15 @@ -29569,10 +27332,10 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/rpds-py?source=hash-mapping + - pkg:pypi/rpds-py?source=compressed-mapping run_exports: {} - size: 217789 - timestamp: 1782831449604 + size: 218916 + timestamp: 1787344535083 - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda sha256: 62b73874b6c264a73e7904b33322128c4881c78dfe2fb9131becfbf84343ee2d md5: 027ff6055e5c82ba4e085857f09302cb @@ -29619,22 +27382,6 @@ packages: license_family: MIT size: 9926237 timestamp: 1782288657493 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.2-h6b72154_0.conda - noarch: python - sha256: 903a24f58856048ff6cfc0a227e91b164fada1de60f94d68457bbe3ac5882263 - md5: 57ad51fa21d6220a94012eccea8c62aa - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/ruff?source=hash-mapping - run_exports: {} - size: 9898546 - timestamp: 1786151875428 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda sha256: c89cfc61bd6c690154ff7a7c9029b9dd83aae1049dc1f84c25c37cafa0b5f69d md5: 0637562978d4231902754645e4c28640 @@ -29770,20 +27517,6 @@ packages: license_family: APACHE size: 374444 timestamp: 1778109163936 -- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.6-h477610d_1.conda - sha256: 8f259ce61c5fe375ccdfe5032da603a36d56a2b52c5b411712a6b1523ea78fb3 - md5: 72a5da10425d23a62418d535332ff755 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: Apache-2.0 - purls: [] - run_exports: - weak: - - simdjson >=4.6.6,<4.7.0a0 - size: 417359 - timestamp: 1786528186117 - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py312he06e257_0.conda sha256: 690fd79162c01ef910763c746d46281a128e73a6b42b5bde6a1d6d20c91edcca md5: ed32da1950de9f6ee034afdeab563b25 @@ -29862,22 +27595,6 @@ packages: license_family: MIT size: 174787 timestamp: 1767781882230 -- conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda - sha256: bd14e9166389be0c654e724e16290772b65745aee8d8bb8bacc65d3179330387 - md5: 0d1e29b811d1fea9ba0fe8c869fa5e96 - depends: - - fmt >=12.1.0,<12.2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.17.0,<1.18.0a0 - size: 174998 - timestamp: 1785924400857 - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.51-py314hc5dbbe4_0.conda sha256: 55e98a0f4e491151ffad7c274d22dfb2d999d50cafa606ec649eff29f079591b md5: 920cf560266c77de21a8f55e7d0e0329 @@ -29893,23 +27610,6 @@ packages: license_family: MIT size: 3986755 timestamp: 1781547909164 -- conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda - sha256: d3ab49c60febcc9edd55ec7f65a9e02b631e137fe609643c590e8151becacca7 - md5: caa67c9335b6f97b708186a6474a306b - depends: - - python - - greenlet !=0.4.17 - - typing-extensions >=4.6.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - purls: - - pkg:pypi/sqlalchemy?source=hash-mapping - run_exports: {} - size: 3998837 - timestamp: 1786535220662 - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda sha256: 700391bf405117eb5c766c4b506f23d3a31f56191ca6619d1b8e5c715376bb7f md5: 7a28cb97617f0bd6a650b4b88f1f44cb @@ -30299,21 +27999,6 @@ packages: license_family: MIT size: 236306 timestamp: 1734228116846 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda - sha256: 85832f409756e78d6d0a92756cd5463fa253219041e40a681d5e8d4c9d21ec8b - md5: a1629a20e5b128c4512b80a93bb1ae55 - depends: - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - libgcc >=14 - - ucrt >=10.0.20348.0 - - xorg-libx11 >=1.8.13,<2.0a0 - license: MIT - purls: [] - run_exports: - weak: - - xorg-libice >=1.1.2,<2.0a0 - size: 168856 - timestamp: 1786474456144 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-h0e40799_0.conda sha256: 065d49b0d1e6873ed1238e962f56cb8204c585cdc5c9bd4ae2bf385cadb5bd65 md5: 570c9a6d9b4909e45d49e9a5daa528de @@ -30326,21 +28011,6 @@ packages: license_family: MIT size: 97096 timestamp: 1741896840170 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda - sha256: bc043a818313abfd836e4e662df1cc9edd6d81994c87a8b35004ca286cebe7c8 - md5: edb94e6121d977d7bc2cc0f56d656c14 - depends: - - libgcc >=14 - - ucrt >=10.0.20348.0 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - xorg-libice >=1.1.2,<2.0a0 - license: MIT - purls: [] - run_exports: - weak: - - xorg-libsm >=1.2.6,<2.0a0 - size: 116838 - timestamp: 1786545424783 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda sha256: eadb12d4597b577cf9bde82a8a2a502a331bd5bfdd60ce508cea93912478e255 md5: 5a823e21e090f8bc43dbfba00cd2f0e2 @@ -30519,21 +28189,6 @@ packages: - yaml >=0.2.5,<0.3.0a0 size: 63944 timestamp: 1753484092156 -- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda - sha256: 643e9f439d6446c28e85f30b089e0fafbc937e57b7443f9e66d1dc2ef6bce6ff - md5: 04e7e743edaa756d863aaa3bf6c89aad - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - yaml-cpp >=0.8.0,<0.9.0a0 - size: 150033 - timestamp: 1785924043282 - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-he0c23c2_0.conda sha256: 031642d753e0ebd666a76cea399497cc7048ff363edf7d76a630ee0a19e341da md5: 9bb5064a9fca5ca8e7d7f1ae677354b6 diff --git a/pixi.toml b/pixi.toml index 3c6997b..ab76f2c 100644 --- a/pixi.toml +++ b/pixi.toml @@ -15,7 +15,7 @@ ipdb = ">=0.13.13" ipykernel = ">=6.29.5" jsonschema = ">=4.0.0" mypy = ">=1.15.0" -pytest = ">=8.3.5" +pytest = ">=8.3.5" # integration/development tests python = ">=3.12" ruff = ">=0.9.9" snakefmt = ">=0.10.2" @@ -43,14 +43,19 @@ pyyaml = ">=6.0.3" [feature.module.pypi-dependencies] tclean = { path = "../../tclean", editable = true } +[feature.test-module.dependencies] +pytest = ">=8.3.5" +jsonschema = ">=4.0.0" + [environments] module = { features = ["module"], no-default-feature = true } -test = { features = ["module"] } +test = { features = ["module", "test-module"], no-default-feature = true } + +[feature.test-module.tasks] +test-unit = "pytest tests/unit" [tasks] test-integration = {cmd = "pytest tests/integration_test.py"} -test-unit = { cmd = "pytest tests/unit" } - [tasks.export-snakemake-env] description = "Export one Pixi environment as Snakemake-compatible conda files" From be26c69a4bb082ed379ff8d09bd61be4c62fde1a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 14:46:29 +0200 Subject: [PATCH 124/241] Refactored some of the validation into a _utils rule and ensured it comes before other rules. --- workflow/Snakefile | 5 +++ workflow/rules/_utils.smk | 43 ++++++++++++++++++++ workflow/rules/automatic.smk | 4 ++ workflow/scripts/validate_config.py | 63 ----------------------------- 4 files changed, 52 insertions(+), 63 deletions(-) create mode 100644 workflow/rules/_utils.smk diff --git a/workflow/Snakefile b/workflow/Snakefile index 33345b2..ac570f6 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -35,6 +35,11 @@ workflow.source_path("scripts/_schemas.py") # Add all your includes here. + +include: "rules/_utils.smk" + +additional_config_validation() + include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/prepare_demand.smk" diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk new file mode 100644 index 0000000..59f9d58 --- /dev/null +++ b/workflow/rules/_utils.smk @@ -0,0 +1,43 @@ +"""Collection of auxiliary functions for this module.""" + + +def additional_config_validation(): + """Validate configuration relationships that require no module dependencies.""" + gap_filling = config["gap_filling"] + + if gap_filling["mode"] != "advanced": + return + + advanced = gap_filling["advanced"] + sources = advanced["sources"] + rules = advanced["rules"] + + seen_rule_names = set() + duplicate_rule_names = set() + + for rule in rules: + rule_name = rule["name"] + + if rule_name in seen_rule_names: + duplicate_rule_names.add(rule_name) + + seen_rule_names.add(rule_name) + + if duplicate_rule_names: + raise ValueError( + "Advanced cleaning rule names must be unique. " + f"Duplicate names: {sorted(duplicate_rule_names)}." + ) + + for rule in rules: + source_name = rule.get("source") + + # A rule without a source explicitly leaves the target values missing. + if source_name is None: + continue + + if source_name not in sources: + raise ValueError( + f"Advanced rule {rule['name']!r} references unknown " + f"advanced source {source_name!r}." + ) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index fa3babe..efc7715 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -41,6 +41,8 @@ rule download_load_entsoe: "../scripts/download_load_entsoe.py" rule download_load_opsd: + input: + validation="/automatic/config_validation.json", #OPSD is not affected by validation, but we still want to check validation passes before triggering this 1GB+ download. output: load=update("/automatic/load_opsd.csv"), log: @@ -75,6 +77,8 @@ rule download_load_neso_year: rule download_population: + input: + validation="/automatic/config_validation.json", output: population=update("/automatic/population.zip"), log: diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 516fa72..76c635c 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -1,6 +1,5 @@ """Validate semantic constraints of the module configuration.""" import json -from collections import Counter from collections.abc import Mapping, Sequence from pathlib import Path from typing import TYPE_CHECKING, Any @@ -80,13 +79,6 @@ def _validate_advanced_config( source_definitions = advanced["sources"] rules = advanced["rules"] - _validate_unique_rule_names(rules) - - _validate_advanced_rule_sources( - rules, - source_definitions=source_definitions, - ) - _validate_advanced_rule_periods( rules, grid=grid, @@ -102,61 +94,6 @@ def _validate_advanced_config( build_advanced_rules(gap_filling) -def _validate_unique_rule_names( - rules: Sequence[Mapping[str, Any]], -) -> None: - """Require advanced rule names to be unique.""" - counts = Counter( - rule["name"] - for rule in rules - ) - - duplicates = sorted( - name - for name, count in counts.items() - if count > 1 - ) - - if duplicates: - raise ValueError( - "Advanced cleaning rule names must be unique. " - f"Duplicate names: {duplicates}." - ) - - -def _validate_advanced_rule_sources( - rules: Sequence[Mapping[str, Any]], - *, - source_definitions: Mapping[str, Mapping[str, Any]], -) -> None: - """Validate references from advanced rules to advanced sources.""" - for rule in rules: - rule_name = rule["name"] - - if rule.get("method") == "leave_missing": - if "source" in rule: - raise ValueError( - f"Advanced rule {rule_name!r} uses " - "'leave_missing' and must not define a source." - ) - - continue - - source_name = rule.get("source") - - if source_name is None: - raise ValueError( - f"Advanced rule {rule_name!r} must reference " - "an advanced source." - ) - - if source_name not in source_definitions: - raise ValueError( - f"Advanced rule {rule_name!r} references unknown " - f"advanced source {source_name!r}." - ) - - def _validate_advanced_rule_periods( rules: Sequence[Mapping[str, Any]], *, From 5ebc7d0905a4c4666ac0d5373f32631b8d7db5e0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 15:18:24 +0200 Subject: [PATCH 125/241] Forcing the test .csv for external_profiles functionality into the code. --- .../resources/user/external_profiles/alb_external_test.csv | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/integration/resources/user/external_profiles/alb_external_test.csv diff --git a/tests/integration/resources/user/external_profiles/alb_external_test.csv b/tests/integration/resources/user/external_profiles/alb_external_test.csv new file mode 100644 index 0000000..02ffa2d --- /dev/null +++ b/tests/integration/resources/user/external_profiles/alb_external_test.csv @@ -0,0 +1,5 @@ +timestamp,demand +2017-01-01T00:00:00Z,98765.0 +2017-01-01T01:00:00Z,87654.0 +2017-01-01T02:00:00Z,87654.0 +2017-01-01T03:00:00Z,87654.0 From 46913ade499d55aefccfcaa253a663b8f568707e Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 15:18:33 +0200 Subject: [PATCH 126/241] Changed config for external profiles. --- config/config.yaml | 2 +- .../inputs/external_profiles/alb_external_test.csv | 3 --- tests/integration/test_config.yaml | 6 +++--- workflow/Snakefile | 1 + workflow/internal/config.schema.yaml | 5 +++-- workflow/rules/advanced.smk | 7 ++++++- 6 files changed, 14 insertions(+), 10 deletions(-) delete mode 100644 tests/integration/inputs/external_profiles/alb_external_test.csv diff --git a/config/config.yaml b/config/config.yaml index 21ec320..f40963f 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -82,7 +82,7 @@ gap_filling: alb_external_test: method: external_profile - file: inputs/external_profiles/alb_external_test.csv + file: alb_external_test.csv rules: - name: build_alb_from_gbr_alb_winter diff --git a/tests/integration/inputs/external_profiles/alb_external_test.csv b/tests/integration/inputs/external_profiles/alb_external_test.csv deleted file mode 100644 index 5bf6621..0000000 --- a/tests/integration/inputs/external_profiles/alb_external_test.csv +++ /dev/null @@ -1,3 +0,0 @@ -timestamp,demand -2022-01-01T00:00:00Z,98765.0 -2022-01-01T02:00:00Z,87654.0 diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index b3828b5..a852a5a 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -83,7 +83,7 @@ integration_test: alb_external_test: method: external_profile - file: inputs/external_profiles/alb_external_test.csv + file: alb_external_test.csv rules: - name: build_alb_from_gbr_alb_winter @@ -109,7 +109,7 @@ integration_test: - name: test_external_profile_overwrite country: ALB - start: "2022-01-01 00:00" - end: "2022-01-08 00:00" + start: "2017-01-01 00:00" + end: "2017-01-01 03:00" scope: overwrite source: alb_external_test \ No newline at end of file diff --git a/workflow/Snakefile b/workflow/Snakefile index ac570f6..74c2797 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -12,6 +12,7 @@ pathvars: # User resources shapes="/user/{shape}/shapes.parquet", token_entsoe="/user/token_entsoe.txt", + external_profiles="/user/external_profiles", # Module results output_data="/{shape}/demand_electricity_MW.parquet", diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index eaf9cf5..00f557b 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -235,9 +235,10 @@ $defs: file: type: string minLength: 1 + pattern: '^[^/\\]+\.csv$' description: > - Path to a local CSV containing timestamp and value columns for - the external profile. + Filename of the format ".csv" of a local CSV containing timestamp and value columns for + the external profile. The file is resolved relative to the external-profiles pathvar. required: - method diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 4112f98..b60890d 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -89,7 +89,12 @@ def final_cleaning_method_input(_wildcards): def advanced_external_profile_files(_wildcards): plan = _read_auxiliary_plan() - return list(dict.fromkeys(plan["external_profile_files"].values())) + return [ + f"/{filename}" + for filename in dict.fromkeys( + plan["external_profile_files"].values() + ) + ] def auxiliary_entsoe_threads(wildcards): From f301b33928622ac434ef556c8d1deef2f33a9576 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 26 Aug 2026 17:02:46 +0200 Subject: [PATCH 127/241] Fixed bugs with external_profiles --- .../user/external_profiles/alb_external_test.csv | 5 ----- workflow/envs/module.win-64.pin.txt | 1 + workflow/scripts/apply_advanced_overrides.py | 9 ++++++++- workflow/scripts/plan_auxiliary_data.py | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) delete mode 100644 tests/integration/resources/user/external_profiles/alb_external_test.csv diff --git a/tests/integration/resources/user/external_profiles/alb_external_test.csv b/tests/integration/resources/user/external_profiles/alb_external_test.csv deleted file mode 100644 index 02ffa2d..0000000 --- a/tests/integration/resources/user/external_profiles/alb_external_test.csv +++ /dev/null @@ -1,5 +0,0 @@ -timestamp,demand -2017-01-01T00:00:00Z,98765.0 -2017-01-01T01:00:00Z,87654.0 -2017-01-01T02:00:00Z,87654.0 -2017-01-01T03:00:00Z,87654.0 diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index d97e799..768b8e6 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -1,6 +1,7 @@ # Generated by `pixi workspace export` # platform: win-64 @EXPLICIT +file:///C:/dev/staged-recipes/build_artifacts/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda#71b24316859acd00bdb8b38f5e2ce328 https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda#8b53a83fda40ec679e4d63fa32fae989 https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda#06a5bf5a1ca16cce0df6eaa91fc42bc2 diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 957b56c..6e170c9 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -53,7 +53,14 @@ advanced_sources[source_name] = profile.iloc[:, 0] -for source_name, path in plan["external_profile_files"].items(): +external_profile_paths = { + Path(path).name: path + for path in snakemake.input.external_profiles +} + +for source_name, filename in plan["external_profile_files"].items(): + path = external_profile_paths[filename] + advanced_sources[source_name] = read_external_profile( path, grid=grid, diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index ba5eeb7..159d762 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -305,7 +305,7 @@ def _build_external_profile_files( "is not an external-profile source." ) - result[str(rule.rule_name)] = str( + result[source_name] = str( definition["file"] ) From 5a20a51dd6df5bf24b0a9b1c1105abfa0495f5d3 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 09:47:39 +0200 Subject: [PATCH 128/241] Removed OPSD download dependency on validation. Whilst we want fail-fast, the OPSD download does not use the config and requiring it, means the 1GB+ download executes everytime the user changes something (even just a date). --- workflow/rules/automatic.smk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index efc7715..fa3babe 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -41,8 +41,6 @@ rule download_load_entsoe: "../scripts/download_load_entsoe.py" rule download_load_opsd: - input: - validation="/automatic/config_validation.json", #OPSD is not affected by validation, but we still want to check validation passes before triggering this 1GB+ download. output: load=update("/automatic/load_opsd.csv"), log: @@ -77,8 +75,6 @@ rule download_load_neso_year: rule download_population: - input: - validation="/automatic/config_validation.json", output: population=update("/automatic/population.zip"), log: From 6680dc5002f28e8d4cf8b7d8a91afb9932a4c80a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 09:52:58 +0200 Subject: [PATCH 129/241] Added the external_profiles override to the test/integration snakefile. --- tests/integration/Snakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/Snakefile b/tests/integration/Snakefile index 724f9b1..28937b3 100644 --- a/tests/integration/Snakefile +++ b/tests/integration/Snakefile @@ -9,6 +9,7 @@ module module_demand_electricity: pathvars: shapes="resources/user/{shape}/shapes.parquet", token_entsoe="resources/user/token_entsoe.txt", + external_profiles="resources/user/external_profiles", # test the re-routing of resources, results and logs resources="resources/module/resources", results="resources/module/results", From 2a36b2545f87377b5f6c28f553f62d83bb3b0295 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 10:01:29 +0200 Subject: [PATCH 130/241] Added tests/integration external profile example to git repo despite .ignores --- .../resources/user/external_profiles/alb_external_test.csv | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/integration/resources/user/external_profiles/alb_external_test.csv diff --git a/tests/integration/resources/user/external_profiles/alb_external_test.csv b/tests/integration/resources/user/external_profiles/alb_external_test.csv new file mode 100644 index 0000000..816e671 --- /dev/null +++ b/tests/integration/resources/user/external_profiles/alb_external_test.csv @@ -0,0 +1,5 @@ +timestamp,value +2017-01-01T00:00:00Z,98765.0 +2017-01-01T01:00:00Z,87654.0 +2017-01-01T02:00:00Z,87654.0 +2017-01-01T03:00:00Z,87654.0 From 5b11e4eba2d1372330bd29e733a24793a3c0eb57 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 11:07:53 +0200 Subject: [PATCH 131/241] Renamed variable to avoid interactions with a snakemake global by the same name --- workflow/rules/_utils.smk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 59f9d58..0089db2 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -10,12 +10,12 @@ def additional_config_validation(): advanced = gap_filling["advanced"] sources = advanced["sources"] - rules = advanced["rules"] + advanced_rules = advanced["rules"] seen_rule_names = set() duplicate_rule_names = set() - for rule in rules: + for rule in advanced_rules: rule_name = rule["name"] if rule_name in seen_rule_names: @@ -29,7 +29,7 @@ def additional_config_validation(): f"Duplicate names: {sorted(duplicate_rule_names)}." ) - for rule in rules: + for rule in advanced_rules: source_name = rule.get("source") # A rule without a source explicitly leaves the target values missing. From f1c2a249d531d98bf7eaa4156899a8e3cedec3a9 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 11:08:02 +0200 Subject: [PATCH 132/241] Updated read me --- config/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/README.md b/config/README.md index 20bbc2f..c3d9483 100644 --- a/config/README.md +++ b/config/README.md @@ -10,6 +10,7 @@ Useful references are: - [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml): authoritative configuration schema; - [`INTERFACE.yaml`](../INTERFACE.yaml): module input/output interface; - [`tests/integration/test_config.yaml`](../tests/integration/test_config.yaml): a richer integration configuration; +- [`tests/integration/resources/user/external_profiles`](../tests/integration/user/external_profiles): example of an external profile. - [`tests/integration/Snakefile`](../tests/integration/Snakefile): example module import. ## Temporal scope @@ -274,14 +275,14 @@ Scaling periods can require auxiliary demand data outside the main target grid; ## Advanced source: `external_profile` -An `external_profile` source reads a user-supplied CSV. +An `external_profile` source reads a user-supplied CSV. By default, external profiles are resolved from `resources/user/external_profiles/`. This location can re-wired through the `external_profiles` pathvar when importing the module. ```yaml advanced: sources: alb_external: method: external_profile - file: inputs/external_profiles/alb_external.csv + file: alb_external.csv ``` The target country, period, and application scope belong to the **rule**, not to the source: From 9f0cb5781fcf83725176c9288d066295b2d8aaef Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 11:38:52 +0200 Subject: [PATCH 133/241] Remade the validation functions to separate temporal scope and gap filling to minimise rerun triggers. --- workflow/rules/automatic.smk | 32 +++++++++++++--- workflow/rules/clean.smk | 6 ++- workflow/scripts/validate_config.py | 57 +++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index fa3babe..311ccfb 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,22 +1,42 @@ """Rules used to download automatic resource files.""" -rule validate_config_semantics: +rule validate_temporal_config_semantics: output: - "/automatic/config_validation.json", + "/automatic/temporal_config_validation.json", conda: "../envs/module.yaml" params: - validation_config=config, + validation_kind="temporal", + validation_config={ + "temporal_scope": config["temporal_scope"], + }, message: - "Validate module configuration semantics." + "Validate temporal configuration semantics." + script: + "../scripts/validate_config.py" + + +rule validate_gap_filling_config_semantics: + output: + "/automatic/gap_filling_config_validation.json", + conda: + "../envs/module.yaml" + params: + validation_kind="gap_filling", + validation_config={ + "temporal_scope": config["temporal_scope"], + "gap_filling": config["gap_filling"], + }, + message: + "Validate gap-filling configuration semantics." script: "../scripts/validate_config.py" rule download_load_entsoe: input: - validation="/automatic/config_validation.json", + validation="/automatic/temporal_config_validation.json", token_entsoe="", output: raw_load="/automatic/entsoe/raw_load.parquet", @@ -58,7 +78,7 @@ rule download_load_opsd: rule download_load_neso_year: input: - validation="/automatic/config_validation.json", + validation="/automatic/temporal_config_validation.json", output: annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), log: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index c515fe3..a26cd9b 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -19,6 +19,7 @@ def neso_raw_files(_wildcards): rule prepare_load_entsoe: input: + validation="/automatic/temporal_config_validation.json", raw_load=rules.download_load_entsoe.output.raw_load, output: load="/automatic/load_entsoe.parquet", @@ -39,7 +40,7 @@ rule prepare_load_entsoe: rule prepare_load_opsd: input: - validation="/automatic/config_validation.json", + validation="/automatic/temporal_config_validation.json", load="/automatic/load_opsd.csv", output: load="/automatic/load_opsd.parquet", @@ -60,7 +61,7 @@ rule prepare_load_opsd: rule prepare_load_neso: input: - validation="/automatic/config_validation.json", + validation="/automatic/temporal_config_validation.json", annual_files=neso_raw_files, output: load="/automatic/load_neso.parquet", @@ -93,6 +94,7 @@ def configured_load_inputs(_wildcards): rule clean_demand: input: configured_load_inputs, + validation="/automatic/gap_filling_config_validation.json", output: demand=("/automatic/load_basic_cleaned.parquet"), data_source=("/automatic/load_data_source.parquet"), diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 76c635c..10ac68e 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -1,4 +1,5 @@ """Validate semantic constraints of the module configuration.""" +import hashlib import json from collections.abc import Mapping, Sequence from pathlib import Path @@ -19,10 +20,19 @@ snakemake: Any -def validate_config_semantics( +def validate_temporal_config_semantics( config: Mapping[str, Any], ) -> None: - """Validate module configuration semantics.""" + """Validate temporal configuration semantics.""" + build_time_grid( + config["temporal_scope"] + ) + + +def validate_gap_filling_config_semantics( + config: Mapping[str, Any], +) -> None: + """Validate gap-filling configuration semantics.""" gap_filling = config["gap_filling"] grid = build_time_grid( @@ -40,6 +50,19 @@ def validate_config_semantics( ) +def config_hash( + config: Mapping[str, Any], +) -> str: + """Return a deterministic hash of validated configuration.""" + serialised = json.dumps( + config, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + + return hashlib.sha256(serialised).hexdigest() + + def _validate_basic_config( gap_filling: Mapping[str, Any], *, @@ -231,6 +254,8 @@ def _validate_equal_period_lengths( def write_validation_marker( output_path: str | Path, + *, + validated_config: Mapping[str, Any], ) -> None: """Write a marker file when semantic validation succeeds.""" output_path = Path(output_path) @@ -245,17 +270,35 @@ def write_validation_marker( encoding="utf-8", ) as file: json.dump( - {"valid": True}, + { + "valid": True, + "config_hash": config_hash(validated_config), + }, file, indent=2, ) if __name__ == "__main__": - validate_config_semantics( - snakemake.params.validation_config - ) + validation_kind = snakemake.params.validation_kind + validation_config = snakemake.params.validation_config + + if validation_kind == "temporal": + validate_temporal_config_semantics( + validation_config + ) + + elif validation_kind == "gap_filling": + validate_gap_filling_config_semantics( + validation_config + ) + + else: + raise ValueError( + f"Unsupported validation kind {validation_kind!r}." + ) write_validation_marker( - snakemake.output[0] + snakemake.output[0], + validated_config=validation_config, ) From 02fc3f32f4cac25bd4676904eb67692b1a8085ed Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 11:56:06 +0200 Subject: [PATCH 134/241] Fixed param wiring issue --- workflow/rules/clean.smk | 2 +- workflow/scripts/clean_demand.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index a26cd9b..42b4b51 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -93,7 +93,7 @@ def configured_load_inputs(_wildcards): rule clean_demand: input: - configured_load_inputs, + load_inputs=configured_load_inputs, validation="/automatic/gap_filling_config_validation.json", output: demand=("/automatic/load_basic_cleaned.parquet"), diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 974d911..adfca81 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -24,7 +24,7 @@ def main(snakemake: Any) -> None: ) input_paths = list( - snakemake.input + snakemake.input.load_inputs ) if len(input_paths) != len(source_names): From 6900fca6a68100435b119ec4794659d8111189c6 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 14:24:15 +0200 Subject: [PATCH 135/241] Environment configured to use tclean's development anaconda package --- pixi.lock | 36 ++++++++++++++------------ pixi.toml | 13 +++++++--- tests/integration/Snakefile | 4 +-- workflow/envs/module.linux-64.pin.txt | 5 ++-- workflow/envs/module.osx-arm64.pin.txt | 5 ++-- workflow/envs/module.win-64.pin.txt | 6 ++--- workflow/envs/module.yaml | 2 ++ 7 files changed, 42 insertions(+), 29 deletions(-) diff --git a/pixi.lock b/pixi.lock index fc9e3f7..c3d930d 100644 --- a/pixi.lock +++ b/pixi.lock @@ -8,6 +8,7 @@ environments: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ + - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda @@ -837,8 +838,7 @@ environments: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ - indexes: - - https://pypi.org/simple + - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda @@ -1160,7 +1160,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1429,7 +1429,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1697,13 +1697,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda test: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ - indexes: - - https://pypi.org/simple + - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda @@ -2015,7 +2014,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2270,7 +2269,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2534,7 +2533,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - pypi: ../../tclean + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda sha256: 71fd599b42cd1352bb8a7d278eef9b681b8de5efe210f358101ce47de5a4d699 @@ -28327,9 +28326,14 @@ packages: - zstd >=1.5.7,<1.6.0a0 size: 388453 timestamp: 1764777142545 -- pypi: ../../tclean - name: tclean - requires_dist: - - pandas>=3.0.3 - - pandera>=0.31.1 - requires_python: '>=3.12' +- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + sha256: 67bb604c9abd137099fe0e02da2dd5af2f2a3ce8781688267886f85b79789982 + md5: 3530431367468b4a8adc15f0eb08d021 + depends: + - python >=3.12 + - pandas >=3.0.3 + - pandera >=0.31.1 + - python + license: MIT + size: 33199 + timestamp: 1787731237339 diff --git a/pixi.toml b/pixi.toml index ab76f2c..90344d0 100644 --- a/pixi.toml +++ b/pixi.toml @@ -4,7 +4,11 @@ authors = ["See AUTHORS file"] description = "This module prepares electricity demand timeseries for Europe at arbitrary resolution" license = "Apache-2.0" readme = "README.md" -channels = ["conda-forge", "bioconda"] +channels = [ + "conda-forge", + "bioconda", + "https://conda.anaconda.org/ddahawkins/label/dev", +] platforms = ["win-64", "linux-64", "osx-arm64"] homepage = "https://www.modelblocks.org/" @@ -39,9 +43,10 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" - -[feature.module.pypi-dependencies] -tclean = { path = "../../tclean", editable = true } +tclean = { + version = "==0.1.0", + channel = "https://conda.anaconda.org/ddahawkins/label/dev", +} [feature.test-module.dependencies] pytest = ">=8.3.5" diff --git a/tests/integration/Snakefile b/tests/integration/Snakefile index 28937b3..ec2fb0a 100644 --- a/tests/integration/Snakefile +++ b/tests/integration/Snakefile @@ -8,8 +8,8 @@ configfile: workflow.source_path("./test_config.yaml") module module_demand_electricity: pathvars: shapes="resources/user/{shape}/shapes.parquet", - token_entsoe="resources/user/token_entsoe.txt", - external_profiles="resources/user/external_profiles", + token_entsoe=workflow.source_path("resources/user/token_entsoe.txt"), + external_profiles=workflow.source_path("resources/user/external_profiles"), # test the re-routing of resources, results and logs resources="resources/module/resources", results="resources/module/results", diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 5df7295..b3fa95a 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -23,8 +23,6 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda#18335a698559cdbcd86150a48bf54ba6 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda#d2ffd7602c02f2b316fd921d39876885 https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda#7eccb41177e15cc672e1babe9056018e -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf @@ -50,6 +48,9 @@ https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openb https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda#d8285bea2a350f63fab23bf460221f3f https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda#15c437bfa4cbddd379b95357c9aa4150 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda#55f526c3fb5302a1ce922612348442e1 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda#b2895afaf55bf96a8c8282a2e47a5de0 diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index 76aab10..fe61044 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -14,8 +14,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda#4 https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda#a915151d5d3c5bf039f5ccc8402a436f https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda#620b85a3f45526a8bc4d23fd78fc22f0 https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda#8e7608172fa4d1b90de9a745c2fd2b81 -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf @@ -43,6 +41,9 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_open https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda#d83fc83d589e2625a3451c9a7e21047c https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda#4581a32b837950217327fcab93214313 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda#d037e9adb0365ab53445f357bd9a035f https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index 768b8e6..db360af 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -1,7 +1,6 @@ # Generated by `pixi workspace export` # platform: win-64 @EXPLICIT -file:///C:/dev/staged-recipes/build_artifacts/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda#71b24316859acd00bdb8b38f5e2ce328 https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda#8b53a83fda40ec679e4d63fa32fae989 https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda#06a5bf5a1ca16cce0df6eaa91fc42bc2 @@ -17,8 +16,6 @@ https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda#720b https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda#ccc490c81ffe14181861beac0e8f3169 https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda#4cb8e6b48f67de0b018719cdf1136306 https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda#2956dff38eb9f8332ad4caeba941cfe7 -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd -https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf @@ -48,6 +45,9 @@ https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.con https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda#f9ac74c3b07c396014434aca1e58d362 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda#9da394ea5e0ec5cc5edc1ad14f2a4d4d https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda#1045d29f787812d3fac1fd80a1339710 https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda#053b84beec00b71ea8ff7a4f84b55207 https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda#a912b2c4ff0f03101c751aa79a331831 diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index bb6ef16..c2ba66c 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -2,6 +2,7 @@ name: module channels: - conda-forge - bioconda +- https://conda.anaconda.org/ddahawkins/label/dev - nodefaults dependencies: - curl >=8.9.1 @@ -20,3 +21,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 +- ddahawkins/label/dev::tclean ==0.1.0 From bf2d74ec871412b18de6574228285b3ca4b6f242 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 14:43:50 +0200 Subject: [PATCH 136/241] Added comments to test_config.yaml --- tests/integration/test_config.yaml | 44 ++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index a852a5a..456667a 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,23 +1,33 @@ integration_test: + # The integration test uses a deliberately short hourly period to keep + # provider downloads and workflow execution reasonably small. temporal_scope: start: "2017-01-01" end: "2017-01-03" frequency: "1h" - # provide a list of sources and set their priority order. Options include: entsoe, neso, opsd + + # Load sources are listed in priority order. Where multiple sources provide + # data for the same country and timestamp, the earlier source takes priority. + # Supported sources include: entsoe, neso, opsd. load_sources: - entsoe - neso - opsd + # Exercise the full advanced gap-filling workflow. See config/README.md for + # the complete configuration reference and method descriptions. gap_filling: mode: advanced basic: rules: + # Fill short internal gaps by linear interpolation. - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h + # Fill longer gaps using the mean of the corresponding periods one + # week before and one week after the missing period. - name: average_adjacent_weeks method: average_periods max_gap: 326h @@ -25,12 +35,16 @@ integration_test: - -7d - 7d + # If averaging cannot fill the gap, try copying the corresponding + # period from the previous week. - name: copy_previous_week method: copy_periods max_gap: 168h source_offset: -168h require_complete_source: true + # Finally, try copying the corresponding period from the following + # week when a complete source period is available. - name: copy_following_week method: copy_periods max_gap: 168h @@ -39,10 +53,16 @@ integration_test: advanced: auxiliary_data: + # Apply the same basic cleaning rules to acquired auxiliary data before + # it is used to construct advanced replacement profiles. basic_cleaning: enabled: true sources: + # Construct an Albanian replacement profile from a winter period of + # British data, then scale it to match Albanian energy over the + # specified scaling period. This source is defined to exercise + # cross-country construction and scaling. alb_from_gbr_alb_winter: method: construct_from_sources periods: @@ -58,6 +78,10 @@ integration_test: end: "2024-02-01" weight: 1 + # Construct an Albanian profile from a later period of Albanian data. + # The corresponding rule below overwrites the integration-test target + # period, ensuring construction, auxiliary acquisition, scaling, and + # overwrite behavior are exercised end to end. alb_from_alb_2024: method: construct_from_sources periods: @@ -73,6 +97,9 @@ integration_test: end: "2024-01-03" weight: 1 + # Define a Montenegro profile constructed from Serbian data. Its rule + # deliberately lies outside the target temporal scope, testing that + # inactive advanced rules do not trigger unnecessary acquisition. mne_from_srb: method: construct_from_sources periods: @@ -81,11 +108,16 @@ integration_test: end: "2022-04-01" weight: 1 + # Read a small committed CSV fixture to exercise the external_profile + # source type and external_profiles pathvar resolution. alb_external_test: method: external_profile file: alb_external_test.csv rules: + # Fill Albanian gaps in January 2022 using the cross-country constructed + # winter profile. This rule is outside the integration-test target + # period and therefore tests inactive-rule selection. - name: build_alb_from_gbr_alb_winter country: ALB start: "2022-01-01" @@ -93,6 +125,9 @@ integration_test: scope: fill_gaps source: alb_from_gbr_alb_winter + # Overwrite the full Albanian integration-test period using the + # constructed 2024 Albanian profile. This is the main active + # construct_from_sources integration case. - name: build_alb_from_alb_2024 country: ALB start: "2017-01-01" @@ -100,6 +135,9 @@ integration_test: scope: overwrite source: alb_from_alb_2024 + # Deliberately inactive rule: both its target period and source data are + # outside the integration-test scope. It verifies that an inactive rule + # does not create unnecessary auxiliary acquisition jobs. - name: example_inactive_mne_override country: MNE start: "2020-03-01" @@ -107,9 +145,11 @@ integration_test: scope: fill_gaps source: mne_from_srb + # Overwrite the first three Albanian hours from the external CSV fixture, + # exercising external_profile loading and overwrite application. - name: test_external_profile_overwrite country: ALB start: "2017-01-01 00:00" end: "2017-01-01 03:00" scope: overwrite - source: alb_external_test \ No newline at end of file + source: alb_external_test From 82fca8ac650fb3ba12b5dae790ceb5fab5ab75d0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 14:52:48 +0200 Subject: [PATCH 137/241] Fixed bug in tclean package declaration --- pixi.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pixi.toml b/pixi.toml index 90344d0..086b44a 100644 --- a/pixi.toml +++ b/pixi.toml @@ -43,10 +43,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = { - version = "==0.1.0", - channel = "https://conda.anaconda.org/ddahawkins/label/dev", -} +tclean = {version = "==0.1.0", channel = "https://conda.anaconda.org/ddahawkins/label/dev"} [feature.test-module.dependencies] pytest = ">=8.3.5" From 236f75ae5f2c8e89000f2b313868bbbc57b15325 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 15:00:53 +0200 Subject: [PATCH 138/241] Make integration test retain resources/user/external_profiles/alb_external_test.csv --- tests/integration_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_test.py b/tests/integration_test.py index 6e6cb8f..c931ade 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -56,6 +56,7 @@ def integration_path(user_path: Path, module_path: Path, token_entsoe: Path): files_to_copy = { "EUROPE_S_C1_ADM1/shapes.parquet": Path("EUROPE_S_C1_ADM1/shapes.parquet"), "token_entsoe.txt": Path("token_entsoe.txt"), + "external_profiles/alb_external_test.csv": Path("external_profiles/alb_external_test.csv"), } for source_file, destination in files_to_copy.items(): destination_file = user_integ_dir / destination From de937e0c5ec7133f79a015d8af6a561150e33047 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 15:01:19 +0200 Subject: [PATCH 139/241] Add alb_external_test.csv to resources/user/external_profiles --- resources/user/external_profiles/alb_external_test.csv | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 resources/user/external_profiles/alb_external_test.csv diff --git a/resources/user/external_profiles/alb_external_test.csv b/resources/user/external_profiles/alb_external_test.csv new file mode 100644 index 0000000..816e671 --- /dev/null +++ b/resources/user/external_profiles/alb_external_test.csv @@ -0,0 +1,5 @@ +timestamp,value +2017-01-01T00:00:00Z,98765.0 +2017-01-01T01:00:00Z,87654.0 +2017-01-01T02:00:00Z,87654.0 +2017-01-01T03:00:00Z,87654.0 From cac38f2f2b030e4d2c4bcc2d61ab22332629cd41 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 16:03:27 +0200 Subject: [PATCH 140/241] Updated enstoe logging so granular country level download info is debug only (log file, not terminal) --- workflow/scripts/download_load_entsoe.py | 29 +++++++++++++++++++-- workflow/scripts/sources/entsoe/download.py | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index 43e41a3..0b46e7f 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -1,6 +1,7 @@ """Snakemake entry point for downloading ENTSO-E load data.""" import logging +from pathlib import Path from typing import TYPE_CHECKING, Any from _advanced_execution import get_batch, load_execution_plan @@ -56,9 +57,33 @@ def main(snakemake: Any) -> None: if __name__ == "__main__": + formatter = logging.Formatter( + "%(levelname)s: %(message)s" + ) + + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + console_handler.setFormatter(formatter) + + log_path = Path(snakemake.log[0]) + log_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + file_handler = logging.FileHandler( + log_path, + mode="w", + ) + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(formatter) + logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", + level=logging.DEBUG, + handlers=[ + console_handler, + file_handler, + ], ) main(snakemake) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index 5668f9a..0c043a9 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -138,7 +138,7 @@ def download_entsoe( country_data ) - logger.info( + logger.debug( "[%s/%s] Finished %s in %.1fs.", completed, total_countries, From acb55abd45701e3cd9f0de62afeafa39449bdeff Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 16:03:43 +0200 Subject: [PATCH 141/241] Updated opsd downloader to stream directly to parquet and skip csv. --- workflow/rules/automatic.smk | 2 +- workflow/rules/clean.smk | 2 +- workflow/scripts/sources/opsd/download.py | 62 +++++++++++++++++------ workflow/scripts/sources/opsd/prepare.py | 2 +- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 311ccfb..12e845f 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -62,7 +62,7 @@ rule download_load_entsoe: rule download_load_opsd: output: - load=update("/automatic/load_opsd.csv"), + load=update("/automatic/opsd/raw_load.parquet"), log: "/download_load_opsd.log", localrule: True diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 42b4b51..fd10b50 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -41,7 +41,7 @@ rule prepare_load_entsoe: rule prepare_load_opsd: input: validation="/automatic/temporal_config_validation.json", - load="/automatic/load_opsd.csv", + load="/automatic/opsd/raw_load.parquet", output: load="/automatic/load_opsd.parquet", log: diff --git a/workflow/scripts/sources/opsd/download.py b/workflow/scripts/sources/opsd/download.py index 2cc3471..5787b05 100644 --- a/workflow/scripts/sources/opsd/download.py +++ b/workflow/scripts/sources/opsd/download.py @@ -5,7 +5,9 @@ from pathlib import Path from urllib.request import Request, urlopen -import pandas as pd +import pyarrow as pa +import pyarrow.csv as pacsv +import pyarrow.parquet as pq logger = logging.getLogger(__name__) @@ -21,18 +23,40 @@ def _is_valid_cached_snapshot(path: Path) -> bool: - """Return whether an existing OPSD snapshot is suitable for reuse.""" + """Return whether an existing OPSD Parquet snapshot is suitable for reuse.""" if not path.exists() or path.stat().st_size == 0: return False try: - columns = set(pd.read_csv(path, nrows=0).columns) - except (OSError, ValueError): + columns = set(pq.read_schema(path).names) + except (OSError, pa.ArrowInvalid): return False return REQUIRED_COLUMNS.issubset(columns) +def _convert_csv_to_parquet( + csv_path: Path, + parquet_path: Path, +) -> None: + """Convert an OPSD CSV snapshot to Parquet without loading it fully into memory.""" + reader = pacsv.open_csv(csv_path) + + if not REQUIRED_COLUMNS.issubset(reader.schema.names): + raise RuntimeError( + "Downloaded OPSD snapshot does not contain " + "the expected CSV structure." + ) + + with pq.ParquetWriter( + parquet_path, + reader.schema, + compression="zstd", + ) as writer: + for batch in reader: + writer.write_batch(batch) + + def download_opsd( *, url: str, @@ -56,10 +80,11 @@ def download_opsd( exist_ok=True, ) - temporary_path = output_path.with_suffix( - output_path.suffix + ".part" - ) - temporary_path.unlink(missing_ok=True) + temporary_csv_path = output_path.with_suffix(".csv.part") + temporary_parquet_path = output_path.with_suffix(output_path.suffix + ".part") + + temporary_csv_path.unlink(missing_ok=True) + temporary_parquet_path.unlink(missing_ok=True) logger.info( "Downloading OPSD snapshot from %s.", @@ -74,7 +99,7 @@ def download_opsd( try: with ( urlopen(request, timeout=300) as response, - temporary_path.open("wb") as output_file, + temporary_csv_path.open("wb") as output_file, ): content_length = response.headers.get( "Content-Length" @@ -85,7 +110,7 @@ def download_opsd( output_file, ) - downloaded_size = temporary_path.stat().st_size + downloaded_size = temporary_csv_path.stat().st_size if ( content_length is not None @@ -97,18 +122,25 @@ def download_opsd( f"of {content_length} bytes." ) + _convert_csv_to_parquet( + temporary_csv_path, + temporary_parquet_path, + ) + if not _is_valid_cached_snapshot( - temporary_path + temporary_parquet_path ): raise RuntimeError( - "Downloaded OPSD snapshot does not contain " - "the expected CSV structure." + "Converted OPSD snapshot does not contain " + "the expected Parquet structure." ) - temporary_path.replace(output_path) + temporary_parquet_path.replace(output_path) + temporary_csv_path.unlink() except Exception: - temporary_path.unlink(missing_ok=True) + temporary_csv_path.unlink(missing_ok=True) + temporary_parquet_path.unlink(missing_ok=True) raise logger.info( diff --git a/workflow/scripts/sources/opsd/prepare.py b/workflow/scripts/sources/opsd/prepare.py index 00c0ff2..8e929b1 100644 --- a/workflow/scripts/sources/opsd/prepare.py +++ b/workflow/scripts/sources/opsd/prepare.py @@ -39,7 +39,7 @@ def prepare_opsd( country_codes: list[str], ) -> None: """Prepare OPSD demand on the requested canonical target index.""" - load = pd.read_csv(input_path) + load = pd.read_parquet(input_path) load = OPSDLoad.validate(load) load = load.loc[ From ed5cb83ebd47496be39882b2a95dacb5571be2b4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:27:08 +0000 Subject: [PATCH 142/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- config/config.yaml | 2 +- pixi.toml | 4 +- tests/integration_test.py | 4 +- tests/unit/test_advanced_execution.py | 4 +- workflow/Snakefile | 3 + workflow/rules/advanced.smk | 80 ++---- workflow/rules/automatic.smk | 3 +- workflow/rules/clean.smk | 6 +- workflow/scripts/_advanced_execution.py | 163 +++-------- workflow/scripts/_plot_timeline.py | 13 +- workflow/scripts/_tclean_config.py | 148 ++-------- workflow/scripts/apply_advanced_overrides.py | 8 +- workflow/scripts/clean_auxiliary_group.py | 116 ++------ workflow/scripts/clean_demand.py | 164 +++-------- .../scripts/construct_auxiliary_profile.py | 28 +- workflow/scripts/download_load_entsoe.py | 46 +--- workflow/scripts/download_load_neso.py | 14 +- workflow/scripts/download_load_opsd.py | 18 +- workflow/scripts/finalise_clean_demand.py | 29 +- workflow/scripts/plan_auxiliary_data.py | 256 ++++-------------- workflow/scripts/prepare_load_entsoe.py | 24 +- workflow/scripts/prepare_load_neso.py | 34 +-- workflow/scripts/prepare_load_opsd.py | 30 +- workflow/scripts/sources/entsoe/download.py | 66 +---- workflow/scripts/sources/entsoe/prepare.py | 15 +- workflow/scripts/sources/neso/download.py | 121 ++------- workflow/scripts/sources/neso/prepare.py | 115 ++------ workflow/scripts/sources/opsd/download.py | 74 +---- workflow/scripts/sources/opsd/prepare.py | 68 +---- workflow/scripts/validate_config.py | 178 +++--------- 30 files changed, 414 insertions(+), 1420 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index f40963f..0ae4a79 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -111,4 +111,4 @@ gap_filling: start: "2022-01-01 00:00" end: "2022-01-08 00:00" scope: overwrite - source: alb_external_test \ No newline at end of file + source: alb_external_test diff --git a/pixi.toml b/pixi.toml index 086b44a..56abdcf 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,7 +5,7 @@ description = "This module prepares electricity demand timeseries for Europe at license = "Apache-2.0" readme = "README.md" channels = [ - "conda-forge", + "conda-forge", "bioconda", "https://conda.anaconda.org/ddahawkins/label/dev", ] @@ -46,7 +46,7 @@ pyyaml = ">=6.0.3" tclean = {version = "==0.1.0", channel = "https://conda.anaconda.org/ddahawkins/label/dev"} [feature.test-module.dependencies] -pytest = ">=8.3.5" +pytest = ">=8.3.5" jsonschema = ">=4.0.0" [environments] diff --git a/tests/integration_test.py b/tests/integration_test.py index c931ade..547588f 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -56,7 +56,9 @@ def integration_path(user_path: Path, module_path: Path, token_entsoe: Path): files_to_copy = { "EUROPE_S_C1_ADM1/shapes.parquet": Path("EUROPE_S_C1_ADM1/shapes.parquet"), "token_entsoe.txt": Path("token_entsoe.txt"), - "external_profiles/alb_external_test.csv": Path("external_profiles/alb_external_test.csv"), + "external_profiles/alb_external_test.csv": Path( + "external_profiles/alb_external_test.csv" + ), } for source_file, destination in files_to_copy.items(): destination_file = user_integ_dir / destination diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py index 590c1a8..29fd59e 100644 --- a/tests/unit/test_advanced_execution.py +++ b/tests/unit/test_advanced_execution.py @@ -99,9 +99,7 @@ def test_load_execution_plan_and_get_batch(tmp_path) -> None: path.write_text(json.dumps(plan), encoding="utf-8") loaded = load_execution_plan(path) - selected = get_batch( - loaded, batch_id=batch["batch_id"], source=batch["source"] - ) + selected = get_batch(loaded, batch_id=batch["batch_id"], source=batch["source"]) assert selected == batch diff --git a/workflow/Snakefile b/workflow/Snakefile index 74c2797..747ac98 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -37,10 +37,13 @@ workflow.source_path("scripts/_schemas.py") # Add all your includes here. + include: "rules/_utils.smk" + additional_config_validation() + include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/prepare_demand.smk" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index b60890d..04e2f79 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -91,9 +91,7 @@ def advanced_external_profile_files(_wildcards): return [ f"/{filename}" - for filename in dict.fromkeys( - plan["external_profile_files"].values() - ) + for filename in dict.fromkeys(plan["external_profile_files"].values()) ] @@ -117,16 +115,13 @@ checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, output: - plan=( - "/automatic/" - "auxiliary/advanced_execution_plan.json" - ), + plan=("/automatic/" "auxiliary/advanced_execution_plan.json"), + conda: + "../envs/module.yaml" params: temporal_scope=config["temporal_scope"], gap_filling=config["gap_filling"], source_names=config["load_sources"], - conda: - "../envs/module.yaml" message: "Plan auxiliary electricity-demand acquisition." script: @@ -159,21 +154,13 @@ rule download_auxiliary_load_entsoe: token_entsoe="", plan=auxiliary_acquisition_plan, output: - raw_load=( - "/automatic/" - "auxiliary/entsoe/raw/" - "{batch_id}.parquet" - ), + raw_load=("/automatic/" "auxiliary/entsoe/raw/" "{batch_id}.parquet"), log: - ( - "/auxiliary/" - "entsoe/download_{batch_id}.log" - ), + ("/auxiliary/" "entsoe/download_{batch_id}.log"), localrule: True conda: "../envs/module.yaml" - threads: - auxiliary_entsoe_threads + threads: auxiliary_entsoe_threads params: frequency=config["temporal_scope"]["frequency"], message: @@ -187,16 +174,9 @@ rule prepare_auxiliary_load_entsoe: plan=auxiliary_acquisition_plan, raw_load=rules.download_auxiliary_load_entsoe.output.raw_load, output: - load=( - "/automatic/" - "auxiliary/entsoe/" - "{batch_id}.parquet" - ), + load=("/automatic/" "auxiliary/entsoe/" "{batch_id}.parquet"), log: - ( - "/auxiliary/" - "entsoe/prepare_{batch_id}.log" - ), + ("/auxiliary/" "entsoe/prepare_{batch_id}.log"), conda: "../envs/module.yaml" params: @@ -230,11 +210,7 @@ rule prepare_auxiliary_load_neso: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=( - "/automatic/" - "auxiliary/neso/" - "{batch_id}.parquet" - ), + load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), log: "/auxiliary/neso/{batch_id}.log", conda: @@ -252,11 +228,7 @@ rule clean_auxiliary_group: plan=auxiliary_acquisition_plan, sources=auxiliary_group_source_files, output: - demand=( - "/automatic/" - "auxiliary/cleaned/" - "{group_id}.parquet" - ), + demand=("/automatic/" "auxiliary/cleaned/" "{group_id}.parquet"), data_source=( "/automatic/" "auxiliary/cleaned/" @@ -267,18 +239,16 @@ rule clean_auxiliary_group: "auxiliary/cleaned/" "{group_id}_cleaning_method.parquet" ), + conda: + "../envs/module.yaml" params: frequency=config["temporal_scope"]["frequency"], basic_rules=config["gap_filling"]["basic"]["rules"], basic_cleaning_enabled=( - config["gap_filling"] - ["advanced"] - ["auxiliary_data"] - ["basic_cleaning"] - ["enabled"] + config["gap_filling"]["advanced"]["auxiliary_data"]["basic_cleaning"][ + "enabled" + ] ), - conda: - "../envs/module.yaml" message: "Combine and clean auxiliary electricity-demand sources." script: @@ -291,19 +261,13 @@ rule construct_auxiliary_profile: sources=auxiliary_rule_cleaned_files, output: profile=( - "/automatic/" - "auxiliary/constructed/" - "{rule_name}.parquet" - ), - params: - frequency=config["temporal_scope"]["frequency"], - advanced_sources=( - config["gap_filling"] - ["advanced"] - ["sources"] + "/automatic/" "auxiliary/constructed/" "{rule_name}.parquet" ), conda: "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + advanced_sources=(config["gap_filling"]["advanced"]["sources"]), message: "Construct auxiliary demand profile for {wildcards.rule_name}." script: @@ -323,10 +287,10 @@ rule apply_advanced_overrides: cleaning_method=( "/automatic/" "load_advanced_cleaning_method.parquet" ), - params: - temporal_scope=config["temporal_scope"], conda: "../envs/module.yaml" + params: + temporal_scope=config["temporal_scope"], message: "Apply advanced electricity-demand overrides." script: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 12e845f..de9ba24 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -49,7 +49,7 @@ rule download_load_entsoe: min( internal["load_entsoe"]["MAX_WORKERS"], len(internal["load_entsoe"]["countries"]), - ), + ) params: temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], @@ -60,6 +60,7 @@ rule download_load_entsoe: script: "../scripts/download_load_entsoe.py" + rule download_load_opsd: output: load=update("/automatic/opsd/raw_load.parquet"), diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index fd10b50..92999d7 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -17,17 +17,18 @@ def neso_raw_files(_wildcards): ("/automatic/neso/" f"historic_demand_{year}.csv") for year in years ] + rule prepare_load_entsoe: input: validation="/automatic/temporal_config_validation.json", raw_load=rules.download_load_entsoe.output.raw_load, output: load="/automatic/load_entsoe.parquet", + log: + "/prepare_load_entsoe.log", localrule: True conda: "../envs/module.yaml" - log: - "/prepare_load_entsoe.log", params: temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], @@ -38,6 +39,7 @@ rule prepare_load_entsoe: script: "../scripts/prepare_load_entsoe.py" + rule prepare_load_opsd: input: validation="/automatic/temporal_config_validation.json", diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py index da8b23c..08479e8 100644 --- a/workflow/scripts/_advanced_execution.py +++ b/workflow/scripts/_advanced_execution.py @@ -13,23 +13,14 @@ EXECUTION_PLAN_VERSION = 1 -def build_source_batches( - requests: pd.DataFrame, -) -> list[dict[str, object]]: +def build_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: """Group T-Clean source requests into executable provider batches.""" if requests.empty: return [] - required_columns = { - "source", - "context", - "start", - "end", - } + required_columns = {"source", "context", "start", "end"} - missing_columns = ( - required_columns - set(requests.columns) - ) + missing_columns = required_columns - set(requests.columns) if missing_columns: raise ValueError( @@ -39,45 +30,21 @@ def build_source_batches( batches: list[dict[str, object]] = [] - grouped = requests.groupby( - [ - "source", - "start", - "end", - ], - sort=False, - ) + grouped = requests.groupby(["source", "start", "end"], sort=False) - for ( - source, - start, - end, - ), group in grouped: + for (source, start, end), group in grouped: start = pd.Timestamp(start) end = pd.Timestamp(end) if end <= start: - raise ValueError( - "Auxiliary batch end must be later than " - "its start." - ) + raise ValueError("Auxiliary batch end must be later than its start.") - countries = sorted( - group["context"] - .drop_duplicates() - .tolist() - ) + countries = sorted(group["context"].drop_duplicates().tolist()) - group_id = build_group_id( - start=start, - end=end, - ) + group_id = build_group_id(start=start, end=end) batch_id = build_batch_id( - source=str(source), - start=start, - end=end, - countries=countries, + source=str(source), start=start, end=end, countries=countries ) batches.append( @@ -94,63 +61,36 @@ def build_source_batches( return batches -def serialize_batch( - batch: Mapping[str, object], -) -> dict[str, object]: +def serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: """Convert one auxiliary batch to JSON-compatible values.""" start = pd.Timestamp(batch["start"]) end = pd.Timestamp(batch["end"]) if end <= start: - raise ValueError( - "Auxiliary batch end must be later than " - "its start." - ) + raise ValueError("Auxiliary batch end must be later than its start.") - final_included_time = ( - end - pd.Timedelta(nanoseconds=1) - ) + final_included_time = end - pd.Timedelta(nanoseconds=1) return { **batch, "start": start.isoformat(), "end": end.isoformat(), - "years": list( - range( - start.year, - final_included_time.year + 1, - ) - ), + "years": list(range(start.year, final_included_time.year + 1)), } -def build_group_id( - *, - start: pd.Timestamp, - end: pd.Timestamp, -) -> str: +def build_group_id(*, start: pd.Timestamp, end: pd.Timestamp) -> str: """Build a deterministic identifier for one auxiliary period.""" - return ( - f"{start.strftime('%Y%m%dT%H%M')}__" - f"{end.strftime('%Y%m%dT%H%M')}" - ) + return f"{start.strftime('%Y%m%dT%H%M')}__{end.strftime('%Y%m%dT%H%M')}" def build_batch_id( - *, - source: str, - start: pd.Timestamp, - end: pd.Timestamp, - countries: Sequence[str], + *, source: str, start: pd.Timestamp, end: pd.Timestamp, countries: Sequence[str] ) -> str: """Build a deterministic identifier for one provider batch.""" - countries_key = ",".join( - sorted(countries) - ) + countries_key = ",".join(sorted(countries)) - countries_hash = hashlib.sha1( - countries_key.encode("utf-8") - ).hexdigest()[:8] + countries_hash = hashlib.sha1(countries_key.encode("utf-8")).hexdigest()[:8] return ( f"{source}__" @@ -170,10 +110,7 @@ def index_batch_ids_by_source( source = str(batch["source"]) batch_id = str(batch["batch_id"]) - result.setdefault( - source, - [], - ).append(batch_id) + result.setdefault(source, []).append(batch_id) return result @@ -188,32 +125,21 @@ def index_batch_ids_by_group( group_id = str(batch["group_id"]) batch_id = str(batch["batch_id"]) - result.setdefault( - group_id, - [], - ).append(batch_id) + result.setdefault(group_id, []).append(batch_id) return result def resolve_required_group_ids( - batches: Sequence[Mapping[str, object]], - *, - source_periods: pd.DataFrame, + batches: Sequence[Mapping[str, object]], *, source_periods: pd.DataFrame ) -> list[str]: """Resolve source periods to acquired auxiliary period groups.""" if source_periods.empty: return [] - required_columns = { - "context", - "start", - "end", - } + required_columns = {"context", "start", "end"} - missing_columns = ( - required_columns - set(source_periods.columns) - ) + missing_columns = required_columns - set(source_periods.columns) if missing_columns: raise ValueError( @@ -223,9 +149,7 @@ def resolve_required_group_ids( group_ids: list[str] = [] - for period in source_periods.itertuples( - index=False - ): + for period in source_periods.itertuples(index=False): start = pd.Timestamp(period.start) end = pd.Timestamp(period.end) @@ -233,12 +157,9 @@ def resolve_required_group_ids( str(batch["group_id"]) for batch in batches if ( - period.context - in batch["countries"] - and pd.Timestamp(batch["start"]) - <= start - and pd.Timestamp(batch["end"]) - >= end + period.context in batch["countries"] + and pd.Timestamp(batch["start"]) <= start + and pd.Timestamp(batch["end"]) >= end ) } @@ -250,9 +171,7 @@ def resolve_required_group_ids( f"{sorted(matching_group_ids)}." ) - group_id = next( - iter(matching_group_ids) - ) + group_id = next(iter(matching_group_ids)) if group_id not in group_ids: group_ids.append(group_id) @@ -274,17 +193,13 @@ def empty_execution_plan() -> dict[str, object]: } -def load_execution_plan( - path: str | Path, -) -> dict[str, Any]: +def load_execution_plan(path: str | Path) -> dict[str, Any]: """Load one compiled advanced execution plan.""" with open(path, encoding="utf-8") as file: plan = json.load(file) if not isinstance(plan, dict): - raise TypeError( - "Advanced execution plan must contain a JSON object." - ) + raise TypeError("Advanced execution plan must contain a JSON object.") if plan.get("version") != EXECUTION_PLAN_VERSION: raise ValueError( @@ -297,10 +212,7 @@ def load_execution_plan( def get_batch( - plan: Mapping[str, Any], - *, - batch_id: str, - source: str | None = None, + plan: Mapping[str, Any], *, batch_id: str, source: str | None = None ) -> Mapping[str, Any]: """Return exactly one compiled auxiliary batch.""" matches = [ @@ -308,19 +220,12 @@ def get_batch( for batch in plan["batches"] if ( batch["batch_id"] == batch_id - and ( - source is None - or batch["source"] == source - ) + and (source is None or batch["source"] == source) ) ] if len(matches) != 1: - source_text = ( - f" for source {source!r}" - if source is not None - else "" - ) + source_text = f" for source {source!r}" if source is not None else "" raise ValueError( "Expected exactly one auxiliary batch " @@ -328,4 +233,4 @@ def get_batch( f"found {len(matches)}." ) - return matches[0] \ No newline at end of file + return matches[0] diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index fd845c4..07f8b2d 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -353,24 +353,17 @@ def _build_cleaning_method_metadata( basic_rules = build_basic_rules(gap_filling_config) advanced_rules = build_advanced_rules(gap_filling_config) - rule_names = [ - rule["name"] - for rule in basic_rules - ] + rule_names = [rule["name"] for rule in basic_rules] if not advanced_rules.empty: - rule_names.extend( - advanced_rules["rule_name"].tolist() - ) + rule_names.extend(advanced_rules["rule_name"].tolist()) for rule_name in rule_names: rows.append( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": ( - f"Rank {rank}: {_format_rule_name(rule_name)}" - ), + "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), "category": "imputed", } ) diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 1667fec..170233b 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -9,9 +9,7 @@ from tclean import TCleanConfig, TimeGrid -def build_time_grid( - temporal_scope: Mapping[str, Any], -) -> TimeGrid: +def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: """Build the canonical T-Clean time grid.""" return TimeGrid( start=temporal_scope["start"], @@ -20,41 +18,22 @@ def build_time_grid( ) -def build_tclean_config( - temporal_scope: Mapping[str, Any], -) -> TCleanConfig: +def build_tclean_config(temporal_scope: Mapping[str, Any]) -> TCleanConfig: """Build the T-Clean configuration used for demand cleaning.""" - return TCleanConfig( - grid=build_time_grid(temporal_scope) - ) + return TCleanConfig(grid=build_time_grid(temporal_scope)) -def build_basic_rules( - gap_filling_config: Mapping[str, Any], -) -> list[dict[str, Any]]: +def build_basic_rules(gap_filling_config: Mapping[str, Any]) -> list[dict[str, Any]]: """Return configured basic-cleaning rules for T-Clean.""" if gap_filling_config["mode"] == "off": return [] - return [ - dict(rule) - for rule in gap_filling_config["basic"]["rules"] - ] + return [dict(rule) for rule in gap_filling_config["basic"]["rules"]] -def build_advanced_rules( - gap_filling_config: Mapping[str, Any], -) -> pd.DataFrame: +def build_advanced_rules(gap_filling_config: Mapping[str, Any]) -> pd.DataFrame: """Build the canonical T-Clean advanced-rule table.""" - columns = [ - "rule_name", - "method", - "source", - "context", - "start", - "end", - "scope", - ] + columns = ["rule_name", "method", "source", "context", "start", "end", "scope"] if gap_filling_config["mode"] != "advanced": return pd.DataFrame(columns=columns) @@ -85,22 +64,13 @@ def build_advanced_rules( "method": method, "source": source_name, "context": rule["country"], - "start": pd.to_datetime( - rule["start"], - utc=True, - ), - "end": pd.to_datetime( - rule["end"], - utc=True, - ), + "start": pd.to_datetime(rule["start"], utc=True), + "end": pd.to_datetime(rule["end"], utc=True), "scope": rule["scope"], } ) - return pd.DataFrame( - rows, - columns=columns, - ) + return pd.DataFrame(rows, columns=columns) def build_constructed_source_periods( @@ -108,13 +78,9 @@ def build_constructed_source_periods( ) -> pd.DataFrame: """Build T-Clean source periods for one constructed source.""" if source_definition["method"] != "construct_from_sources": - raise ValueError( - "Source definition is not a construct_from_sources source." - ) + raise ValueError("Source definition is not a construct_from_sources source.") - return _build_source_periods( - source_definition["periods"] - ) + return _build_source_periods(source_definition["periods"]) def build_scaling_source_periods( @@ -127,28 +93,19 @@ def build_scaling_source_periods( return None if scaling["method"] != "match_energy": - raise ValueError( - "Unsupported scaling method: " - f"{scaling['method']!r}." - ) + raise ValueError(f"Unsupported scaling method: {scaling['method']!r}.") - return _build_source_periods( - scaling["periods"] - ) + return _build_source_periods(scaling["periods"]) def build_all_constructed_source_periods( - gap_filling_config: Mapping[str, Any], - *, - source_names: Sequence[str] | None = None, + gap_filling_config: Mapping[str, Any], *, source_names: Sequence[str] | None = None ) -> dict[str, pd.DataFrame]: """Build source-period tables for configured constructed sources.""" if gap_filling_config["mode"] != "advanced": return {} - source_definitions = ( - gap_filling_config["advanced"]["sources"] - ) + source_definitions = gap_filling_config["advanced"]["sources"] if source_names is None: selected_names = list(source_definitions) @@ -159,75 +116,43 @@ def build_all_constructed_source_periods( for source_name in selected_names: if source_name not in source_definitions: - raise ValueError( - f"Unknown advanced source {source_name!r}." - ) + raise ValueError(f"Unknown advanced source {source_name!r}.") definition = source_definitions[source_name] if definition["method"] != "construct_from_sources": continue - frames = [ - build_constructed_source_periods(definition) - ] + frames = [build_constructed_source_periods(definition)] - scaling_periods = ( - build_scaling_source_periods(definition) - ) + scaling_periods = build_scaling_source_periods(definition) if scaling_periods is not None: frames.append(scaling_periods) - result[source_name] = pd.concat( - frames, - ignore_index=True, - ) + result[source_name] = pd.concat(frames, ignore_index=True) return result -def build_source_capabilities( - source_names: Sequence[str], -) -> pd.DataFrame: +def build_source_capabilities(source_names: Sequence[str]) -> pd.DataFrame: """Describe which contexts configured providers can supply.""" if len(source_names) != len(set(source_names)): - raise ValueError( - "Configured load source names must be unique." - ) + raise ValueError("Configured load source names must be unique.") capabilities: list[dict[str, object]] = [] for source_name in source_names: if source_name == "neso": - capabilities.append( - { - "source": "neso", - "context": "GBR", - } - ) + capabilities.append({"source": "neso", "context": "GBR"}) elif source_name in {"entsoe", "opsd"}: - capabilities.append( - { - "source": source_name, - "context": None, - } - ) + capabilities.append({"source": source_name, "context": None}) else: - raise ValueError( - "Unsupported electricity-demand source: " - f"{source_name!r}." - ) + raise ValueError(f"Unsupported electricity-demand source: {source_name!r}.") - return pd.DataFrame( - capabilities, - columns=[ - "source", - "context", - ], - ) + return pd.DataFrame(capabilities, columns=["source", "context"]) def get_advanced_source_definitions( @@ -240,30 +165,17 @@ def get_advanced_source_definitions( return gap_filling_config["advanced"]["sources"] -def _build_source_periods( - periods: Sequence[Mapping[str, Any]], -) -> pd.DataFrame: +def _build_source_periods(periods: Sequence[Mapping[str, Any]]) -> pd.DataFrame: """Convert Modelblocks country periods to generic T-Clean periods.""" return pd.DataFrame( [ { "context": period["country"], - "start": pd.to_datetime( - period["start"], - utc=True, - ), - "end": pd.to_datetime( - period["end"], - utc=True, - ), + "start": pd.to_datetime(period["start"], utc=True), + "end": pd.to_datetime(period["end"], utc=True), "weight": period["weight"], } for period in periods ], - columns=[ - "context", - "start", - "end", - "weight", - ], + columns=["context", "start", "end", "weight"], ) diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 6e170c9..f07dd69 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -54,17 +54,13 @@ advanced_sources[source_name] = profile.iloc[:, 0] external_profile_paths = { - Path(path).name: path - for path in snakemake.input.external_profiles + Path(path).name: path for path in snakemake.input.external_profiles } for source_name, filename in plan["external_profile_files"].items(): path = external_profile_paths[filename] - advanced_sources[source_name] = read_external_profile( - path, - grid=grid, - ) + advanced_sources[source_name] = read_external_profile(path, grid=grid) filled, _, cleaning_method = apply_advanced_rules( data, diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index 130aef4..d89d17d 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -13,66 +13,38 @@ def main(snakemake: Any) -> None: """Combine and basic-clean one auxiliary source group.""" - plan = _read_plan( - snakemake.input.plan - ) + plan = _read_plan(snakemake.input.plan) - group_id = str( - snakemake.wildcards.group_id - ) + group_id = str(snakemake.wildcards.group_id) batch_ids = plan["groups"][group_id] - batches_by_id = { - batch["batch_id"]: batch - for batch in plan["batches"] - } + batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} - batches = [ - batches_by_id[batch_id] - for batch_id in batch_ids - ] + batches = [batches_by_id[batch_id] for batch_id in batch_ids] if not batches: - raise ValueError( - f"Auxiliary group {group_id!r} " - "contains no source batches." - ) + raise ValueError(f"Auxiliary group {group_id!r} contains no source batches.") - starts = { - pd.Timestamp(batch["start"]) - for batch in batches - } + starts = {pd.Timestamp(batch["start"]) for batch in batches} - ends = { - pd.Timestamp(batch["end"]) - for batch in batches - } + ends = {pd.Timestamp(batch["end"]) for batch in batches} if len(starts) != 1 or len(ends) != 1: raise ValueError( - f"Auxiliary group {group_id!r} " - "contains inconsistent batch periods." + f"Auxiliary group {group_id!r} contains inconsistent batch periods." ) group_start = next(iter(starts)) group_end = next(iter(ends)) grid = TimeGrid( - start=group_start, - end=group_end, - frequency=( - snakemake.params.frequency - ), + start=group_start, end=group_end, frequency=(snakemake.params.frequency) ) - config = TCleanConfig( - grid=grid - ) + config = TCleanConfig(grid=grid) - source_paths = list( - snakemake.input.sources - ) + source_paths = list(snakemake.input.sources) if len(source_paths) != len(batches): raise ValueError( @@ -83,14 +55,8 @@ def main(snakemake: Any) -> None: sources: dict[str, pd.DataFrame] = {} - for batch, path in zip( - batches, - source_paths, - strict=True, - ): - source_name = str( - batch["source"] - ) + for batch, path in zip(batches, source_paths, strict=True): + source_name = str(batch["source"]) if source_name in sources: raise ValueError( @@ -99,9 +65,7 @@ def main(snakemake: Any) -> None: f"{source_name!r}." ) - sources[source_name] = ( - _read_prepared_source(path) - ) + sources[source_name] = _read_prepared_source(path) basic_rules = ( list(snakemake.params.basic_rules) @@ -109,63 +73,35 @@ def main(snakemake: Any) -> None: else [] ) - ( - cleaned, - data_source, - cleaning_method, - ) = clean( - sources, - config=config, - basic_rules=basic_rules, + (cleaned, data_source, cleaning_method) = clean( + sources, config=config, basic_rules=basic_rules ) - cleaned.to_parquet( - snakemake.output.demand - ) + cleaned.to_parquet(snakemake.output.demand) - data_source.to_parquet( - snakemake.output.data_source - ) + data_source.to_parquet(snakemake.output.data_source) - cleaning_method.to_parquet( - snakemake.output.cleaning_method - ) + cleaning_method.to_parquet(snakemake.output.cleaning_method) -def _read_plan( - path: str | Path, -) -> dict[str, Any]: +def _read_plan(path: str | Path) -> dict[str, Any]: """Read the advanced execution manifest.""" - with Path(path).open( - encoding="utf-8" - ) as file: + with Path(path).open(encoding="utf-8") as file: return json.load(file) -def _read_prepared_source( - path: str | Path, -) -> pd.DataFrame: +def _read_prepared_source(path: str | Path) -> pd.DataFrame: """Read one prepared auxiliary provider frame.""" data = pd.read_parquet(path) - if not isinstance( - data.index, - pd.DatetimeIndex, - ): - data.index = pd.to_datetime( - data.index, - utc=True, - ) + if not isinstance(data.index, pd.DatetimeIndex): + data.index = pd.to_datetime(data.index, utc=True) elif data.index.tz is None: - data.index = ( - data.index.tz_localize("UTC") - ) + data.index = data.index.tz_localize("UTC") else: - data.index = ( - data.index.tz_convert("UTC") - ) + data.index = data.index.tz_convert("UTC") data.index.name = "timestamp" diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index adfca81..6d0c2e6 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -19,13 +19,9 @@ def main(snakemake: Any) -> None: """Run the main electricity-demand cleaning stage.""" - source_names = list( - snakemake.params.source_names - ) + source_names = list(snakemake.params.source_names) - input_paths = list( - snakemake.input.load_inputs - ) + input_paths = list(snakemake.input.load_inputs) if len(input_paths) != len(source_names): raise ValueError( @@ -35,188 +31,102 @@ def main(snakemake: Any) -> None: sources = { source_name: _read_prepared_source(path) - for source_name, path in zip( - source_names, - input_paths, - strict=True, - ) + for source_name, path in zip(source_names, input_paths, strict=True) } - config = build_tclean_config( - snakemake.params.temporal_scope - ) + config = build_tclean_config(snakemake.params.temporal_scope) - basic_rules = build_basic_rules( - snakemake.params.gap_filling - ) + basic_rules = build_basic_rules(snakemake.params.gap_filling) - ( - cleaned, - data_source, - cleaning_method, - ) = clean( - sources, - config=config, - basic_rules=basic_rules, + (cleaned, data_source, cleaning_method) = clean( + sources, config=config, basic_rules=basic_rules ) - basic_rule_names = [ - rule["name"] - for rule in basic_rules - ] + basic_rule_names = [rule["name"] for rule in basic_rules] cleaning_method_ranks = build_cleaning_method_ranks( - source_names, - basic_rule_names=basic_rule_names, + source_names, basic_rule_names=basic_rule_names ) cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, - ranks=cleaning_method_ranks, + cleaning_method=cleaning_method, ranks=cleaning_method_ranks ) gap_report = build_gap_report( cleaned, grid=config.grid, - enabled=( - snakemake.params.gap_filling["mode"] - == "advanced" - ), + enabled=(snakemake.params.gap_filling["mode"] == "advanced"), ) - cleaned.to_parquet( - snakemake.output.demand - ) + cleaned.to_parquet(snakemake.output.demand) - data_source.to_parquet( - snakemake.output.data_source - ) + data_source.to_parquet(snakemake.output.data_source) - cleaning_method.to_parquet( - snakemake.output.cleaning_method - ) + cleaning_method.to_parquet(snakemake.output.cleaning_method) - cleaning_method_rank.to_parquet( - snakemake.output.cleaning_method_rank - ) + cleaning_method_rank.to_parquet(snakemake.output.cleaning_method_rank) - gap_report.to_parquet( - snakemake.output.gap_report, - index=False, - ) + gap_report.to_parquet(snakemake.output.gap_report, index=False) - _log_source_counts( - data_source - ) + _log_source_counts(data_source) - _log_cleaning_method_counts( - cleaning_method - ) + _log_cleaning_method_counts(cleaning_method) - _log_gap_report( - gap_report - ) + _log_gap_report(gap_report) -def _read_prepared_source( - path: str | Path, -) -> pd.DataFrame: +def _read_prepared_source(path: str | Path) -> pd.DataFrame: """Read one prepared electricity-demand source.""" data = pd.read_parquet(path) - if not isinstance( - data.index, - pd.DatetimeIndex, - ): - data.index = pd.to_datetime( - data.index, - utc=True, - ) + if not isinstance(data.index, pd.DatetimeIndex): + data.index = pd.to_datetime(data.index, utc=True) elif data.index.tz is None: - data.index = data.index.tz_localize( - "UTC" - ) + data.index = data.index.tz_localize("UTC") else: - data.index = data.index.tz_convert( - "UTC" - ) + data.index = data.index.tz_convert("UTC") data.index.name = "timestamp" return data -def _log_source_counts( - data_source: pd.DataFrame, -) -> None: +def _log_source_counts(data_source: pd.DataFrame) -> None: """Log observed-value counts by source.""" - counts = ( - data_source - .stack() - .value_counts() - ) + counts = data_source.stack().value_counts() if counts.empty: - logger.info( - "No observed source values were recorded." - ) + logger.info("No observed source values were recorded.") return for source_name, count in counts.items(): - logger.info( - "%s supplied %s values.", - source_name, - int(count), - ) + logger.info("%s supplied %s values.", source_name, int(count)) -def _log_cleaning_method_counts( - cleaning_method: pd.DataFrame, -) -> None: +def _log_cleaning_method_counts(cleaning_method: pd.DataFrame) -> None: """Log value counts by cleaning method.""" - counts = ( - cleaning_method - .stack() - .value_counts() - ) + counts = cleaning_method.stack().value_counts() if counts.empty: - logger.info( - "No cleaning methods were recorded." - ) + logger.info("No cleaning methods were recorded.") return for method, count in counts.items(): - logger.info( - "%s supplied %s values.", - method, - int(count), - ) + logger.info("%s supplied %s values.", method, int(count)) -def _log_gap_report( - gap_report: pd.DataFrame, -) -> None: +def _log_gap_report(gap_report: pd.DataFrame) -> None: """Log unresolved-gap counts by context.""" if gap_report.empty: - logger.info( - "No unresolved gaps remain after basic cleaning." - ) + logger.info("No unresolved gaps remain after basic cleaning.") return - logger.info( - "Gap report contains %s contiguous unresolved gaps.", - len(gap_report), - ) + logger.info("Gap report contains %s contiguous unresolved gaps.", len(gap_report)) - for context, context_gaps in gap_report.groupby( - "context" - ): - total_duration = ( - context_gaps["gap_duration"].sum() - ) + for context, context_gaps in gap_report.groupby("context"): + total_duration = context_gaps["gap_duration"].sum() logger.info( "%s has %s unresolved gaps covering %s.", diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index bcc3fe3..0a4263f 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -17,9 +17,7 @@ rule = plan["rules"][rule_name] if rule["method"] != "construct_from_sources": - raise ValueError( - f"Rule {rule_name!r} is not a construct_from_sources rule." - ) + raise ValueError(f"Rule {rule_name!r} is not a construct_from_sources rule.") source_name = rule["source"] source_definition = snakemake.params.advanced_sources[source_name] @@ -28,20 +26,13 @@ scaling_sources = build_scaling_source_periods(source_definition) grid = TimeGrid( - start=rule["start"], - end=rule["end"], - frequency=snakemake.params.frequency, + start=rule["start"], end=rule["end"], frequency=snakemake.params.frequency ) -loads = [ - pd.read_parquet(path) - for path in snakemake.input.sources -] +loads = [pd.read_parquet(path) for path in snakemake.input.sources] if not loads: - raise ValueError( - f"No cleaned auxiliary data were supplied for rule {rule_name!r}." - ) + raise ValueError(f"No cleaned auxiliary data were supplied for rule {rule_name!r}.") source_data = loads[0].copy() @@ -52,10 +43,7 @@ source_end = source_data.index.max() + grid.frequency source_data = source_data.reindex( - grid.index_for_period( - start=source_start, - end=source_end, - ) + grid.index_for_period(start=source_start, end=source_end) ) profile = construct_from_sources( @@ -66,8 +54,4 @@ grid=grid, ) -profile.to_frame( - name=rule["context"] -).to_parquet( - snakemake.output.profile -) +profile.to_frame(name=rule["context"]).to_parquet(snakemake.output.profile) diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index 0b46e7f..8f1efab 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -14,20 +14,12 @@ def main(snakemake: Any) -> None: """Download ENTSO-E data for the requested workflow period.""" - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) - batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="entsoe", - ) + batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="entsoe") start = batch["start"] end = batch["end"] @@ -36,15 +28,9 @@ def main(snakemake: Any) -> None: else: start = snakemake.params.temporal_start end = snakemake.params.temporal_end - country_codes = list( - snakemake.params.country_codes - ) - - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + country_codes = list(snakemake.params.country_codes) + + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) download_entsoe( start=grid.start, @@ -57,33 +43,19 @@ def main(snakemake: Any) -> None: if __name__ == "__main__": - formatter = logging.Formatter( - "%(levelname)s: %(message)s" - ) + formatter = logging.Formatter("%(levelname)s: %(message)s") console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(formatter) log_path = Path(snakemake.log[0]) - log_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + log_path.parent.mkdir(parents=True, exist_ok=True) - file_handler = logging.FileHandler( - log_path, - mode="w", - ) + file_handler = logging.FileHandler(log_path, mode="w") file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(formatter) - logging.basicConfig( - level=logging.DEBUG, - handlers=[ - console_handler, - file_handler, - ], - ) + logging.basicConfig(level=logging.DEBUG, handlers=[console_handler, file_handler]) main(snakemake) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index cd07170..9e2820b 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -13,21 +13,13 @@ def main(snakemake: Any) -> None: """Download one annual NESO historic-demand file.""" download_annual_file( - year=int(snakemake.params.year), - output_path=snakemake.output.annual_file, + year=int(snakemake.params.year), output_path=snakemake.output.annual_file ) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main(snakemake) diff --git a/workflow/scripts/download_load_opsd.py b/workflow/scripts/download_load_opsd.py index c756076..91d95b0 100644 --- a/workflow/scripts/download_load_opsd.py +++ b/workflow/scripts/download_load_opsd.py @@ -12,22 +12,12 @@ def main(snakemake: Any) -> None: """Download the configured OPSD snapshot.""" - download_opsd( - url=snakemake.params.url, - output_path=snakemake.output.load, - ) + download_opsd(url=snakemake.params.url, output_path=snakemake.output.load) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) - - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) + + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main(snakemake) diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 90f765c..3c7c423 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -6,29 +6,19 @@ from _tclean_config import build_advanced_rules, build_basic_rules from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -shutil.copyfile( - snakemake.input.demand, - snakemake.output.demand, -) +shutil.copyfile(snakemake.input.demand, snakemake.output.demand) -cleaning_method = pd.read_parquet( - snakemake.input.cleaning_method -) +cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) gap_filling = snakemake.params.gap_filling basic_rules = build_basic_rules(gap_filling) advanced_rules = build_advanced_rules(gap_filling) -basic_rule_names = [ - rule["name"] - for rule in basic_rules -] +basic_rule_names = [rule["name"] for rule in basic_rules] advanced_rule_names = ( - advanced_rules["rule_name"].tolist() - if not advanced_rules.empty - else [] + advanced_rules["rule_name"].tolist() if not advanced_rules.empty else [] ) ranks = build_cleaning_method_ranks( @@ -38,14 +28,9 @@ ) cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, - ranks=ranks, + cleaning_method=cleaning_method, ranks=ranks ) -cleaning_method.to_parquet( - snakemake.output.cleaning_method -) +cleaning_method.to_parquet(snakemake.output.cleaning_method) -cleaning_method_rank.to_parquet( - snakemake.output.cleaning_method_rank -) +cleaning_method_rank.to_parquet(snakemake.output.cleaning_method_rank) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 159d762..999661c 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -44,136 +44,76 @@ def build_advanced_execution_plan( if gap_filling_config["mode"] != "advanced": return empty_execution_plan() - grid = build_time_grid( - temporal_scope - ) + grid = build_time_grid(temporal_scope) - advanced_rules = build_advanced_rules( - gap_filling_config - ) + advanced_rules = build_advanced_rules(gap_filling_config) active_rules = select_active_advanced_rules( - advanced_rules, - target_contexts=target_contexts, - grid=grid, + advanced_rules, target_contexts=target_contexts, grid=grid ) if active_rules.empty: return empty_execution_plan() - source_definitions = ( - get_advanced_source_definitions( - gap_filling_config - ) - ) + source_definitions = get_advanced_source_definitions(gap_filling_config) - active_source_names = _get_active_source_names( - active_rules - ) + active_source_names = _get_active_source_names(active_rules) - constructed_source_periods = ( - build_all_constructed_source_periods( - gap_filling_config, - source_names=active_source_names, - ) + constructed_source_periods = build_all_constructed_source_periods( + gap_filling_config, source_names=active_source_names ) - requirements = ( - build_auxiliary_acquisition_requirements( - list( - constructed_source_periods.values() - ), - basic_rules=build_basic_rules( - gap_filling_config - ), - grid=grid, - basic_cleaning_enabled=( - gap_filling_config[ - "advanced" - ][ - "auxiliary_data" - ][ - "basic_cleaning" - ][ - "enabled" - ] - ), - ) + requirements = build_auxiliary_acquisition_requirements( + list(constructed_source_periods.values()), + basic_rules=build_basic_rules(gap_filling_config), + grid=grid, + basic_cleaning_enabled=( + gap_filling_config["advanced"]["auxiliary_data"]["basic_cleaning"][ + "enabled" + ] + ), ) - source_capabilities = ( - build_source_capabilities( - source_names - ) - ) + source_capabilities = build_source_capabilities(source_names) requests = build_auxiliary_source_requests( - requirements, - source_capabilities=source_capabilities, - grid=grid, + requirements, source_capabilities=source_capabilities, grid=grid ) - raw_batches = build_source_batches( - requests - ) + raw_batches = build_source_batches(requests) - batches = [ - serialize_batch(batch) - for batch in raw_batches - ] + batches = [serialize_batch(batch) for batch in raw_batches] rules = _build_rule_manifest( active_rules, source_definitions=source_definitions, - constructed_source_periods=( - constructed_source_periods - ), + constructed_source_periods=(constructed_source_periods), batches=raw_batches, ) constructed_profile_rule_names = [ rule_name for rule_name, rule in rules.items() - if rule["method"] - == "construct_from_sources" + if rule["method"] == "construct_from_sources" ] - external_profile_files = ( - _build_external_profile_files( - active_rules, - source_definitions=source_definitions, - ) + external_profile_files = _build_external_profile_files( + active_rules, source_definitions=source_definitions ) return { "version": EXECUTION_PLAN_VERSION, - "active_rule_names": ( - active_rules["rule_name"].tolist() - ), + "active_rule_names": (active_rules["rule_name"].tolist()), "rules": rules, "batches": batches, - "batch_ids_by_source": ( - index_batch_ids_by_source( - batches - ) - ), - "groups": ( - index_batch_ids_by_group( - batches - ) - ), - "constructed_profile_rule_names": ( - constructed_profile_rule_names - ), - "external_profile_files": ( - external_profile_files - ), + "batch_ids_by_source": (index_batch_ids_by_source(batches)), + "groups": (index_batch_ids_by_group(batches)), + "constructed_profile_rule_names": (constructed_profile_rule_names), + "external_profile_files": (external_profile_files), } -def _get_active_source_names( - active_rules: pd.DataFrame, -) -> list[str]: +def _get_active_source_names(active_rules: pd.DataFrame) -> list[str]: """Return referenced advanced sources in rule order.""" source_names: list[str] = [] @@ -192,177 +132,99 @@ def _get_active_source_names( def _build_rule_manifest( active_rules: pd.DataFrame, *, - source_definitions: Mapping[ - str, - Mapping[str, Any], - ], - constructed_source_periods: Mapping[ - str, - pd.DataFrame, - ], + source_definitions: Mapping[str, Mapping[str, Any]], + constructed_source_periods: Mapping[str, pd.DataFrame], batches: Sequence[Mapping[str, object]], ) -> dict[str, dict[str, object]]: """Build manifest metadata for active advanced rules.""" rules: dict[str, dict[str, object]] = {} - for rule in active_rules.itertuples( - index=False - ): - source_name = ( - None - if pd.isna(rule.source) - else str(rule.source) - ) + for rule in active_rules.itertuples(index=False): + source_name = None if pd.isna(rule.source) else str(rule.source) required_group_ids: list[str] = [] - if ( - source_name is not None - and rule.method - == "construct_from_sources" - ): - if ( - source_name - not in constructed_source_periods - ): + if source_name is not None and rule.method == "construct_from_sources": + if source_name not in constructed_source_periods: raise ValueError( "No constructed-source periods were " f"planned for advanced source " f"{source_name!r}." ) - required_group_ids = ( - resolve_required_group_ids( - batches, - source_periods=( - constructed_source_periods[ - source_name - ] - ), - ) + required_group_ids = resolve_required_group_ids( + batches, source_periods=(constructed_source_periods[source_name]) ) rules[str(rule.rule_name)] = { "method": str(rule.method), "source": source_name, "context": str(rule.context), - "start": pd.Timestamp( - rule.start - ).isoformat(), - "end": pd.Timestamp( - rule.end - ).isoformat(), + "start": pd.Timestamp(rule.start).isoformat(), + "end": pd.Timestamp(rule.end).isoformat(), "scope": str(rule.scope), - "required_group_ids": ( - required_group_ids - ), + "required_group_ids": (required_group_ids), } return rules def _build_external_profile_files( - active_rules: pd.DataFrame, - *, - source_definitions: Mapping[ - str, - Mapping[str, Any], - ], + active_rules: pd.DataFrame, *, source_definitions: Mapping[str, Mapping[str, Any]] ) -> dict[str, str]: """Map active external-profile rules to their files.""" result: dict[str, str] = {} - for rule in active_rules.itertuples( - index=False - ): + for rule in active_rules.itertuples(index=False): if rule.method != "external_profile": continue if pd.isna(rule.source): - raise ValueError( - "External-profile rule " - f"{rule.rule_name!r} has no source." - ) + raise ValueError(f"External-profile rule {rule.rule_name!r} has no source.") source_name = str(rule.source) if source_name not in source_definitions: - raise ValueError( - f"Unknown advanced source " - f"{source_name!r}." - ) + raise ValueError(f"Unknown advanced source {source_name!r}.") - definition = ( - source_definitions[source_name] - ) + definition = source_definitions[source_name] - if ( - definition["method"] - != "external_profile" - ): + if definition["method"] != "external_profile": raise ValueError( - f"Advanced source {source_name!r} " - "is not an external-profile source." + f"Advanced source {source_name!r} is not an external-profile source." ) - result[source_name] = str( - definition["file"] - ) + result[source_name] = str(definition["file"]) return result def write_execution_plan( - *, - plan: Mapping[str, object], - output_path: str | Path, + *, plan: Mapping[str, object], output_path: str | Path ) -> None: """Write the execution manifest as JSON.""" output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - with output_path.open( - "w", - encoding="utf-8", - ) as file: - json.dump( - plan, - file, - indent=2, - ) + with output_path.open("w", encoding="utf-8") as file: + json.dump(plan, file, indent=2) def main(snakemake: Any) -> None: """Build and write the advanced execution manifest.""" - demand = pd.read_parquet( - snakemake.input.demand - ) + demand = pd.read_parquet(snakemake.input.demand) - target_contexts = list( - demand.columns - ) + target_contexts = list(demand.columns) plan = build_advanced_execution_plan( target_contexts=target_contexts, - temporal_scope=( - snakemake.params.temporal_scope - ), - gap_filling_config=( - snakemake.params.gap_filling - ), - source_names=( - snakemake.params.source_names - ), + temporal_scope=(snakemake.params.temporal_scope), + gap_filling_config=(snakemake.params.gap_filling), + source_names=(snakemake.params.source_names), ) - write_execution_plan( - plan=plan, - output_path=snakemake.output.plan, - ) + write_execution_plan(plan=plan, output_path=snakemake.output.plan) if __name__ == "__main__": diff --git a/workflow/scripts/prepare_load_entsoe.py b/workflow/scripts/prepare_load_entsoe.py index fb77fca..0a2d276 100644 --- a/workflow/scripts/prepare_load_entsoe.py +++ b/workflow/scripts/prepare_load_entsoe.py @@ -12,20 +12,12 @@ def main(snakemake: Any) -> None: """Prepare ENTSO-E demand for the requested workflow period.""" - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) - batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="entsoe", - ) + batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="entsoe") start = batch["start"] end = batch["end"] @@ -34,15 +26,9 @@ def main(snakemake: Any) -> None: else: start = snakemake.params.temporal_start end = snakemake.params.temporal_end - country_codes = list( - snakemake.params.country_codes - ) - - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + country_codes = list(snakemake.params.country_codes) + + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_entsoe( input_path=snakemake.input.raw_load, diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index bf0932e..ccdb6b9 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -15,20 +15,12 @@ def main(snakemake: Any) -> None: """Prepare NESO demand for the requested workflow period.""" - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) - batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="neso", - ) + batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="neso") start = batch["start"] end = batch["end"] @@ -38,17 +30,10 @@ def main(snakemake: Any) -> None: end = snakemake.params.end countries = snakemake.params.country_codes - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_neso( - input_paths=[ - Path(path) - for path in snakemake.input.annual_files - ], + input_paths=[Path(path) for path in snakemake.input.annual_files], output_path=snakemake.output.load, target_index=grid.target_index, countries=countries, @@ -56,15 +41,8 @@ def main(snakemake: Any) -> None: if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main(snakemake) diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 677a889..17f64c0 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -13,20 +13,12 @@ def main(snakemake: Any) -> None: """Prepare OPSD demand for the requested workflow period.""" - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) - batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="opsd", - ) + batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="opsd") start = batch["start"] end = batch["end"] @@ -35,15 +27,9 @@ def main(snakemake: Any) -> None: else: start = snakemake.params.start end = snakemake.params.end - country_codes = list( - snakemake.params.country_codes - ) - - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + country_codes = list(snakemake.params.country_codes) + + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_opsd( input_path=snakemake.input.load, @@ -54,10 +40,6 @@ def main(snakemake: Any) -> None: if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) main(snakemake) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index 0c043a9..dc47562 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -19,38 +19,22 @@ def load_token(filepath: str | Path) -> str: def download_country( - *, - country_alpha_3: str, - start: pd.Timestamp, - end: pd.Timestamp, - token: str, + *, country_alpha_3: str, start: pd.Timestamp, end: pd.Timestamp, token: str ) -> tuple[str, pd.Series, float]: """Download ENTSO-E load for one country.""" - country = pycountry.countries.get( - alpha_3=country_alpha_3 - ) + country = pycountry.countries.get(alpha_3=country_alpha_3) if country is None: - raise ValueError( - f"Unknown ISO alpha-3 country code: " - f"{country_alpha_3!r}." - ) + raise ValueError(f"Unknown ISO alpha-3 country code: {country_alpha_3!r}.") country_alpha_2 = country.alpha_2 - client = EntsoePandasClient( - api_key=token, - timeout=60, - ) + client = EntsoePandasClient(api_key=token, timeout=60) country_start = perf_counter() try: - data = client.query_load( - country_code=country_alpha_2, - start=start, - end=end, - ) + data = client.query_load(country_code=country_alpha_2, start=start, end=end) data = data["Actual Load"] data.name = country_alpha_3 @@ -64,10 +48,7 @@ def download_country( end, ) - data = pd.Series( - name=country_alpha_3, - dtype=float, - ) + data = pd.Series(name=country_alpha_3, dtype=float) elapsed = perf_counter() - country_start @@ -101,9 +82,7 @@ def download_entsoe( data_by_country: dict[str, pd.Series] = {} - with ThreadPoolExecutor( - max_workers=workers - ) as executor: + with ThreadPoolExecutor(max_workers=workers) as executor: futures = { executor.submit( download_country, @@ -115,28 +94,18 @@ def download_entsoe( for country_code in country_codes } - for completed, future in enumerate( - as_completed(futures), - start=1, - ): + for completed, future in enumerate(as_completed(futures), start=1): country_code = futures[future] try: - ( - country_code, - country_data, - elapsed, - ) = future.result() + (country_code, country_data, elapsed) = future.result() except Exception as exc: raise RuntimeError( - "Failed to download ENTSO-E load for " - f"{country_code!r}." + f"Failed to download ENTSO-E load for {country_code!r}." ) from exc - data_by_country[country_code] = ( - country_data - ) + data_by_country[country_code] = country_data logger.debug( "[%s/%s] Finished %s in %.1fs.", @@ -146,23 +115,16 @@ def download_entsoe( elapsed, ) - data = [ - data_by_country[country_code] - for country_code in country_codes - ] + data = [data_by_country[country_code] for country_code in country_codes] raw = pd.concat(data, axis=1) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) raw.to_parquet(output_path) logger.info( - "Finished ENTSO-E downloads in %.1fs. " - "Saved raw data to %s.", + "Finished ENTSO-E downloads in %.1fs. Saved raw data to %s.", perf_counter() - download_start, output_path, ) diff --git a/workflow/scripts/sources/entsoe/prepare.py b/workflow/scripts/sources/entsoe/prepare.py index e912cfc..bce2940 100644 --- a/workflow/scripts/sources/entsoe/prepare.py +++ b/workflow/scripts/sources/entsoe/prepare.py @@ -16,24 +16,15 @@ def prepare_entsoe( """Prepare ENTSO-E demand on the configured target grid.""" data = pd.read_parquet(input_path) - data.index = pd.to_datetime( - data.index, - utc=True, - ) + data.index = pd.to_datetime(data.index, utc=True) data = data.resample("1h").mean() - data = data.reindex( - index=grid.target_index, - columns=country_codes, - ) + data = data.reindex(index=grid.target_index, columns=country_codes) data = data.astype(float) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) data.to_parquet(output_path) diff --git a/workflow/scripts/sources/neso/download.py b/workflow/scripts/sources/neso/download.py index 8f83460..ac118d6 100644 --- a/workflow/scripts/sources/neso/download.py +++ b/workflow/scripts/sources/neso/download.py @@ -14,57 +14,38 @@ USER_AGENT = "modelblocks-module-demand-electricity/NESO historic demand downloader" -def _request_json( - endpoint: str, - parameters: dict[str, str | int], -) -> dict[str, Any]: +def _request_json(endpoint: str, parameters: dict[str, str | int]) -> dict[str, Any]: """Request one JSON response from the NESO CKAN API.""" query = urlencode(parameters) url = f"{NESO_API_BASE}/{endpoint}?{query}" - request = Request( - url, - headers={"User-Agent": USER_AGENT}, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) with urlopen(request, timeout=120) as response: payload = json.load(response) if not payload.get("success", False): - raise RuntimeError( - f"NESO API request failed for {endpoint}: {payload}" - ) + raise RuntimeError(f"NESO API request failed for {endpoint}: {payload}") result = payload.get("result") if not isinstance(result, dict): - raise RuntimeError( - f"NESO API returned an unexpected result for {endpoint}." - ) + raise RuntimeError(f"NESO API returned an unexpected result for {endpoint}.") return result def _get_historic_demand_dataset() -> dict[str, Any]: """Return the NESO Historic Demand Data dataset.""" - return _request_json( - "package_show", - {"id": "historic-demand-data"}, - ) + return _request_json("package_show", {"id": "historic-demand-data"}) -def _select_csv_resource( - dataset: dict[str, Any], - *, - year: int, -) -> dict[str, Any]: +def _select_csv_resource(dataset: dict[str, Any], *, year: int) -> dict[str, Any]: """Select the annual NESO historic-demand CSV resource.""" resources = dataset.get("resources", []) if not isinstance(resources, list): - raise RuntimeError( - "NESO Historic Demand Data has no valid resource list." - ) + raise RuntimeError("NESO Historic Demand Data has no valid resource list.") expected_filename = f"demanddata_{year}.csv" expected_title = f"historic demand data {year}" @@ -72,29 +53,17 @@ def _select_csv_resource( matching_resources: list[dict[str, Any]] = [] for resource in resources: - name = str( - resource.get("name", "") - ).strip().casefold() + name = str(resource.get("name", "")).strip().casefold() - url = str( - resource.get("url", "") - ).strip() + url = str(resource.get("url", "")).strip() - format_name = str( - resource.get("format", "") - ).strip().casefold() + format_name = str(resource.get("format", "")).strip().casefold() url_lower = url.casefold() - is_csv = ( - format_name == "csv" - or url_lower.endswith(".csv") - ) + is_csv = format_name == "csv" or url_lower.endswith(".csv") - matches_year = ( - expected_title in name - or expected_filename in url_lower - ) + matches_year = expected_title in name or expected_filename in url_lower if is_csv and matches_year and url: matching_resources.append(resource) @@ -119,42 +88,25 @@ def _select_csv_resource( return matching_resources[0] -def _download_file( - *, - url: str, - output_path: Path, -) -> None: +def _download_file(*, url: str, output_path: Path) -> None: """Download one file atomically.""" - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - temporary_path = output_path.with_suffix( - output_path.suffix + ".part" - ) + temporary_path = output_path.with_suffix(output_path.suffix + ".part") temporary_path.unlink(missing_ok=True) - request = Request( - url, - headers={"User-Agent": USER_AGENT}, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) try: with ( urlopen(request, timeout=300) as response, temporary_path.open("wb") as output_file, ): - shutil.copyfileobj( - response, - output_file, - ) + shutil.copyfileobj(response, output_file) if temporary_path.stat().st_size == 0: - raise RuntimeError( - f"NESO download produced an empty file: {url}" - ) + raise RuntimeError(f"NESO download produced an empty file: {url}") temporary_path.replace(output_path) @@ -163,41 +115,20 @@ def _download_file( raise -def download_annual_file( - *, - year: int, - output_path: str | Path, -) -> None: +def download_annual_file(*, year: int, output_path: str | Path) -> None: """Discover and download one annual NESO historic-demand CSV.""" output_path = Path(output_path) dataset = _get_historic_demand_dataset() - logger.info( - "Selecting NESO historic-demand resource for %s.", - year, - ) + logger.info("Selecting NESO historic-demand resource for %s.", year) - resource = _select_csv_resource( - dataset, - year=year, - ) + resource = _select_csv_resource(dataset, year=year) url = str(resource["url"]) - logger.info( - "Downloading NESO historic demand for %s from %s.", - year, - url, - ) - - _download_file( - url=url, - output_path=output_path, - ) - - logger.info( - "Saved NESO historic demand for %s to %s.", - year, - output_path, - ) + logger.info("Downloading NESO historic demand for %s from %s.", year, url) + + _download_file(url=url, output_path=output_path) + + logger.info("Saved NESO historic demand for %s to %s.", year, output_path) diff --git a/workflow/scripts/sources/neso/prepare.py b/workflow/scripts/sources/neso/prepare.py index b73d6e4..0b8a2f1 100644 --- a/workflow/scripts/sources/neso/prepare.py +++ b/workflow/scripts/sources/neso/prepare.py @@ -16,29 +16,20 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: prepared = data.copy() prepared["SETTLEMENT_DATE"] = pd.to_datetime( - prepared["SETTLEMENT_DATE"], - errors="raise", + prepared["SETTLEMENT_DATE"], errors="raise" ).dt.normalize() prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( - prepared["SETTLEMENT_PERIOD"], - errors="raise", + prepared["SETTLEMENT_PERIOD"], errors="raise" ).astype(int) timestamp_parts: list[pd.Series] = [] - for settlement_date, day in prepared.groupby( - "SETTLEMENT_DATE", - sort=True, - ): + for settlement_date, day in prepared.groupby("SETTLEMENT_DATE", sort=True): day = day.sort_values("SETTLEMENT_PERIOD").copy() - expected_periods = list( - range(1, len(day) + 1) - ) - observed_periods = day[ - "SETTLEMENT_PERIOD" - ].tolist() + expected_periods = list(range(1, len(day) + 1)) + observed_periods = day["SETTLEMENT_PERIOD"].tolist() if observed_periods != expected_periods: raise ValueError( @@ -47,19 +38,11 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: f"Expected 1-{len(day)}." ) - local_start = pd.Timestamp( - settlement_date, - tz="Europe/London", - ) - local_end = ( - local_start + pd.DateOffset(days=1) - ) + local_start = pd.Timestamp(settlement_date, tz="Europe/London") + local_end = local_start + pd.DateOffset(days=1) expected_index = pd.date_range( - start=local_start, - end=local_end, - freq="30min", - inclusive="left", + start=local_start, end=local_end, freq="30min", inclusive="left" ) if len(day) != len(expected_index): @@ -72,22 +55,12 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: ) timestamp_parts.append( - pd.Series( - expected_index, - index=day.index, - name="timestamp", - ) + pd.Series(expected_index, index=day.index, name="timestamp") ) - prepared["timestamp"] = ( - pd.concat(timestamp_parts) - .sort_index() - ) + prepared["timestamp"] = pd.concat(timestamp_parts).sort_index() - prepared["timestamp"] = ( - prepared["timestamp"] - .dt.tz_convert("UTC") - ) + prepared["timestamp"] = prepared["timestamp"].dt.tz_convert("UTC") return prepared.sort_values("timestamp") @@ -121,73 +94,44 @@ def _prepare_half_hourly_demand(raw: pd.DataFrame) -> pd.Series: """Convert raw NESO records to a UTC half-hourly demand series.""" prepared = add_utc_timestamps(raw) - prepared["ND"] = pd.to_numeric( - prepared["ND"], - errors="coerce", - ) + prepared["ND"] = pd.to_numeric(prepared["ND"], errors="coerce") - invalid_demand_count = int( - prepared["ND"].isna().sum() - ) + invalid_demand_count = int(prepared["ND"].isna().sum()) if invalid_demand_count: logger.warning( - "NESO contains %s missing or non-numeric ND values.", - invalid_demand_count, + "NESO contains %s missing or non-numeric ND values.", invalid_demand_count ) - half_hourly = ( - prepared.set_index("timestamp")["ND"] - .sort_index() - .rename("GBR") - ) + half_hourly = prepared.set_index("timestamp")["ND"].sort_index().rename("GBR") - duplicate_mask = half_hourly.index.duplicated( - keep=False - ) + duplicate_mask = half_hourly.index.duplicated(keep=False) if duplicate_mask.any(): duplicate_timestamps = ( - half_hourly.index[duplicate_mask] - .unique() - .astype(str) - .tolist() + half_hourly.index[duplicate_mask].unique().astype(str).tolist() ) raise ValueError( - "NESO data contain duplicate UTC timestamps: " - f"{duplicate_timestamps[:10]}" + f"NESO data contain duplicate UTC timestamps: {duplicate_timestamps[:10]}" ) return half_hourly -def _aggregate_hourly( - half_hourly: pd.Series, -) -> pd.Series: +def _aggregate_hourly(half_hourly: pd.Series) -> pd.Series: """Aggregate half-hourly MW observations to hourly mean MW.""" hourly_counts = half_hourly.resample("1h").count() - incomplete_hours = hourly_counts.loc[ - hourly_counts.between( - 1, - 1, - inclusive="both", - ) - ] + incomplete_hours = hourly_counts.loc[hourly_counts.between(1, 1, inclusive="both")] if not incomplete_hours.empty: logger.warning( - "NESO contains %s hours with only one valid " - "half-hourly ND observation.", + "NESO contains %s hours with only one valid half-hourly ND observation.", len(incomplete_hours), ) - return ( - half_hourly.resample("1h") - .mean() - .rename("GBR") - ) + return half_hourly.resample("1h").mean().rename("GBR") def prepare_neso( @@ -200,11 +144,7 @@ def prepare_neso( """Prepare NESO demand on the requested canonical target index.""" target_countries = list(countries) - result = pd.DataFrame( - index=target_index, - columns=target_countries, - dtype=float, - ) + result = pd.DataFrame(index=target_index, columns=target_countries, dtype=float) if "GBR" not in target_countries: logger.info( @@ -229,15 +169,10 @@ def prepare_neso( ) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) result.to_parquet(output_path) logger.info( - "Saved prepared NESO demand to %s with shape %s.", - output_path, - result.shape, + "Saved prepared NESO demand to %s with shape %s.", output_path, result.shape ) diff --git a/workflow/scripts/sources/opsd/download.py b/workflow/scripts/sources/opsd/download.py index 5787b05..0734f37 100644 --- a/workflow/scripts/sources/opsd/download.py +++ b/workflow/scripts/sources/opsd/download.py @@ -13,13 +13,7 @@ USER_AGENT = "modelblocks-module-demand-electricity/OPSD downloader" -REQUIRED_COLUMNS = { - "utc_timestamp", - "region", - "variable", - "attribute", - "data", -} +REQUIRED_COLUMNS = {"utc_timestamp", "region", "variable", "attribute", "data"} def _is_valid_cached_snapshot(path: Path) -> bool: @@ -35,33 +29,21 @@ def _is_valid_cached_snapshot(path: Path) -> bool: return REQUIRED_COLUMNS.issubset(columns) -def _convert_csv_to_parquet( - csv_path: Path, - parquet_path: Path, -) -> None: +def _convert_csv_to_parquet(csv_path: Path, parquet_path: Path) -> None: """Convert an OPSD CSV snapshot to Parquet without loading it fully into memory.""" reader = pacsv.open_csv(csv_path) if not REQUIRED_COLUMNS.issubset(reader.schema.names): raise RuntimeError( - "Downloaded OPSD snapshot does not contain " - "the expected CSV structure." + "Downloaded OPSD snapshot does not contain the expected CSV structure." ) - with pq.ParquetWriter( - parquet_path, - reader.schema, - compression="zstd", - ) as writer: + with pq.ParquetWriter(parquet_path, reader.schema, compression="zstd") as writer: for batch in reader: writer.write_batch(batch) -def download_opsd( - *, - url: str, - output_path: str | Path, -) -> None: +def download_opsd(*, url: str, output_path: str | Path) -> None: """Download the OPSD snapshot unless a valid cached copy exists.""" output_path = Path(output_path) @@ -70,15 +52,10 @@ def download_opsd( return if output_path.exists(): - logger.warning( - "Cached OPSD snapshot is invalid; downloading a replacement." - ) + logger.warning("Cached OPSD snapshot is invalid; downloading a replacement.") output_path.unlink() - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) temporary_csv_path = output_path.with_suffix(".csv.part") temporary_parquet_path = output_path.with_suffix(output_path.suffix + ".part") @@ -86,50 +63,31 @@ def download_opsd( temporary_csv_path.unlink(missing_ok=True) temporary_parquet_path.unlink(missing_ok=True) - logger.info( - "Downloading OPSD snapshot from %s.", - url, - ) + logger.info("Downloading OPSD snapshot from %s.", url) - request = Request( - url, - headers={"User-Agent": USER_AGENT}, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) try: with ( urlopen(request, timeout=300) as response, temporary_csv_path.open("wb") as output_file, ): - content_length = response.headers.get( - "Content-Length" - ) + content_length = response.headers.get("Content-Length") - shutil.copyfileobj( - response, - output_file, - ) + shutil.copyfileobj(response, output_file) downloaded_size = temporary_csv_path.stat().st_size - if ( - content_length is not None - and downloaded_size != int(content_length) - ): + if content_length is not None and downloaded_size != int(content_length): raise RuntimeError( "OPSD download is incomplete: " f"received {downloaded_size} " f"of {content_length} bytes." ) - _convert_csv_to_parquet( - temporary_csv_path, - temporary_parquet_path, - ) + _convert_csv_to_parquet(temporary_csv_path, temporary_parquet_path) - if not _is_valid_cached_snapshot( - temporary_parquet_path - ): + if not _is_valid_cached_snapshot(temporary_parquet_path): raise RuntimeError( "Converted OPSD snapshot does not contain " "the expected Parquet structure." @@ -144,7 +102,5 @@ def download_opsd( raise logger.info( - "Saved OPSD snapshot to %s (%s bytes).", - output_path, - output_path.stat().st_size, + "Saved OPSD snapshot to %s (%s bytes).", output_path, output_path.stat().st_size ) diff --git a/workflow/scripts/sources/opsd/prepare.py b/workflow/scripts/sources/opsd/prepare.py index 8e929b1..9c28be6 100644 --- a/workflow/scripts/sources/opsd/prepare.py +++ b/workflow/scripts/sources/opsd/prepare.py @@ -9,24 +9,17 @@ from tclean import TimeGrid -def get_map_alpha2_to_alpha3( - countries_alpha_2, -) -> dict[str, str]: +def get_map_alpha2_to_alpha3(countries_alpha_2) -> dict[str, str]: """Map ISO alpha-2 country codes to alpha-3 codes.""" mapping = {} for alpha2 in countries_alpha_2: - country = pycountry.countries.get( - alpha_2=alpha2 - ) + country = pycountry.countries.get(alpha_2=alpha2) if country is not None: mapping[alpha2] = country.alpha_3 else: - warn( - f"Country with alpha-2 code " - f"'{alpha2}' not found in pycountry." - ) + warn(f"Country with alpha-2 code '{alpha2}' not found in pycountry.") return mapping @@ -42,67 +35,36 @@ def prepare_opsd( load = pd.read_parquet(input_path) load = OPSDLoad.validate(load) - load = load.loc[ - load["variable"] == "load" - ] + load = load.loc[load["variable"] == "load"] - load = load.loc[ - load["attribute"] - == "actual_entsoe_power_statistics" - ].copy() + load = load.loc[load["attribute"] == "actual_entsoe_power_statistics"].copy() - load["utc_timestamp"] = pd.to_datetime( - load["utc_timestamp"], - utc=True, - ) + load["utc_timestamp"] = pd.to_datetime(load["utc_timestamp"], utc=True) # target_index is end-exclusive in conceptual terms, so # derive the exclusive bound from its frequency externally # or simply filter to timestamps represented in the index. load = load.loc[ - (load["utc_timestamp"] >= grid.start) - & (load["utc_timestamp"] < grid.end) + (load["utc_timestamp"] >= grid.start) & (load["utc_timestamp"] < grid.end) ].copy() - country_mapping = get_map_alpha2_to_alpha3( - load["region"].unique() - ) + country_mapping = get_map_alpha2_to_alpha3(load["region"].unique()) - load = load.loc[ - load["region"].isin(country_mapping) - ].copy() + load = load.loc[load["region"].isin(country_mapping)].copy() - load.loc[:, "region"] = load[ - "region" - ].map(country_mapping) + load.loc[:, "region"] = load["region"].map(country_mapping) - load = load.loc[ - load["region"].isin(country_codes) - ].copy() + load = load.loc[load["region"].isin(country_codes)].copy() - load.loc[:, "data"] = pd.to_numeric( - load["data"], - errors="raise", - ) + load.loc[:, "data"] = pd.to_numeric(load["data"], errors="raise") - prepared = pd.pivot( - load, - index="utc_timestamp", - columns="region", - values="data", - ) + prepared = pd.pivot(load, index="utc_timestamp", columns="region", values="data") - prepared = prepared.reindex( - index=grid.target_index, - columns=country_codes, - ) + prepared = prepared.reindex(index=grid.target_index, columns=country_codes) prepared = prepared.astype(float) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) prepared.to_parquet(output_path) diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 10ac68e..5a79a2b 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -1,4 +1,5 @@ """Validate semantic constraints of the module configuration.""" + import hashlib import json from collections.abc import Mapping, Sequence @@ -20,54 +21,32 @@ snakemake: Any -def validate_temporal_config_semantics( - config: Mapping[str, Any], -) -> None: +def validate_temporal_config_semantics(config: Mapping[str, Any]) -> None: """Validate temporal configuration semantics.""" - build_time_grid( - config["temporal_scope"] - ) + build_time_grid(config["temporal_scope"]) -def validate_gap_filling_config_semantics( - config: Mapping[str, Any], -) -> None: +def validate_gap_filling_config_semantics(config: Mapping[str, Any]) -> None: """Validate gap-filling configuration semantics.""" gap_filling = config["gap_filling"] - grid = build_time_grid( - config["temporal_scope"] - ) + grid = build_time_grid(config["temporal_scope"]) - _validate_basic_config( - gap_filling, - grid=grid, - ) + _validate_basic_config(gap_filling, grid=grid) - _validate_advanced_config( - gap_filling, - grid=grid, - ) + _validate_advanced_config(gap_filling, grid=grid) -def config_hash( - config: Mapping[str, Any], -) -> str: +def config_hash(config: Mapping[str, Any]) -> str: """Return a deterministic hash of validated configuration.""" - serialised = json.dumps( - config, - sort_keys=True, - separators=(",", ":"), - ).encode("utf-8") + serialised = json.dumps(config, sort_keys=True, separators=(",", ":")).encode( + "utf-8" + ) return hashlib.sha256(serialised).hexdigest() -def _validate_basic_config( - gap_filling: Mapping[str, Any], - *, - grid: TimeGrid, -) -> None: +def _validate_basic_config(gap_filling: Mapping[str, Any], *, grid: TimeGrid) -> None: """Validate the configured basic-cleaning rules.""" mode = gap_filling["mode"] @@ -78,20 +57,14 @@ def _validate_basic_config( if not rules: raise ValueError( - f"Gap-filling mode is {mode!r}, but no basic " - "cleaning rules are configured." + f"Gap-filling mode is {mode!r}, but no basic cleaning rules are configured." ) - validate_basic_rules( - rules, - grid=grid, - ) + validate_basic_rules(rules, grid=grid) def _validate_advanced_config( - gap_filling: Mapping[str, Any], - *, - grid: TimeGrid, + gap_filling: Mapping[str, Any], *, grid: TimeGrid ) -> None: """Validate advanced source definitions and application rules.""" if gap_filling["mode"] != "advanced": @@ -102,15 +75,9 @@ def _validate_advanced_config( source_definitions = advanced["sources"] rules = advanced["rules"] - _validate_advanced_rule_periods( - rules, - grid=grid, - ) + _validate_advanced_rule_periods(rules, grid=grid) - _validate_advanced_source_definitions( - source_definitions, - grid=grid, - ) + _validate_advanced_source_definitions(source_definitions, grid=grid) # Ensure that the Modelblocks configuration can be represented by the # canonical T-Clean advanced-rule contract. @@ -118,105 +85,71 @@ def _validate_advanced_config( def _validate_advanced_rule_periods( - rules: Sequence[Mapping[str, Any]], - *, - grid: TimeGrid, + rules: Sequence[Mapping[str, Any]], *, grid: TimeGrid ) -> None: """Validate advanced target periods against the configured grid.""" for rule in rules: try: grid.validate_period( - start=pd.Timestamp(rule["start"]), - end=pd.Timestamp(rule["end"]), + start=pd.Timestamp(rule["start"]), end=pd.Timestamp(rule["end"]) ) except (TypeError, ValueError) as error: raise ValueError( - f"Invalid target period for advanced rule " - f"{rule['name']!r}: {error}" + f"Invalid target period for advanced rule {rule['name']!r}: {error}" ) from error def _validate_advanced_source_definitions( - source_definitions: Mapping[str, Mapping[str, Any]], - *, - grid: TimeGrid, + source_definitions: Mapping[str, Mapping[str, Any]], *, grid: TimeGrid ) -> None: """Validate every configured advanced source.""" for source_name, definition in source_definitions.items(): method = definition["method"] if method == "construct_from_sources": - _validate_constructed_source( - source_name, - definition, - grid=grid, - ) + _validate_constructed_source(source_name, definition, grid=grid) elif method == "external_profile": continue else: raise ValueError( - f"Advanced source {source_name!r} uses " - f"unsupported method {method!r}." + f"Advanced source {source_name!r} uses unsupported method {method!r}." ) def _validate_constructed_source( - source_name: str, - definition: Mapping[str, Any], - *, - grid: TimeGrid, + source_name: str, definition: Mapping[str, Any], *, grid: TimeGrid ) -> None: """Validate one constructed advanced source.""" - source_periods = build_constructed_source_periods( - definition - ) + source_periods = build_constructed_source_periods(definition) _validate_source_periods( - source_name, - source_periods, - period_kind="construction", - grid=grid, + source_name, source_periods, period_kind="construction", grid=grid ) _validate_equal_period_lengths( - source_name, - source_periods, - period_kind="construction", - grid=grid, + source_name, source_periods, period_kind="construction", grid=grid ) - scaling_periods = build_scaling_source_periods( - definition - ) + scaling_periods = build_scaling_source_periods(definition) if scaling_periods is None: return _validate_source_periods( - source_name, - scaling_periods, - period_kind="scaling", - grid=grid, + source_name, scaling_periods, period_kind="scaling", grid=grid ) def _validate_source_periods( - source_name: str, - periods: pd.DataFrame, - *, - period_kind: str, - grid: TimeGrid, + source_name: str, periods: pd.DataFrame, *, period_kind: str, grid: TimeGrid ) -> None: """Validate advanced source periods against the configured grid.""" for period in periods.itertuples(index=False): try: - grid.validate_period( - start=period.start, - end=period.end, - ) + grid.validate_period(start=period.start, end=period.end) except (TypeError, ValueError) as error: raise ValueError( @@ -227,20 +160,11 @@ def _validate_source_periods( def _validate_equal_period_lengths( - source_name: str, - periods: pd.DataFrame, - *, - period_kind: str, - grid: TimeGrid, + source_name: str, periods: pd.DataFrame, *, period_kind: str, grid: TimeGrid ) -> None: """Require construction periods to contain equal numbers of values.""" lengths = { - len( - grid.index_for_period( - start=period.start, - end=period.end, - ) - ) + len(grid.index_for_period(start=period.start, end=period.end)) for period in periods.itertuples(index=False) } @@ -253,27 +177,16 @@ def _validate_equal_period_lengths( def write_validation_marker( - output_path: str | Path, - *, - validated_config: Mapping[str, Any], + output_path: str | Path, *, validated_config: Mapping[str, Any] ) -> None: """Write a marker file when semantic validation succeeds.""" output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - with output_path.open( - "w", - encoding="utf-8", - ) as file: + with output_path.open("w", encoding="utf-8") as file: json.dump( - { - "valid": True, - "config_hash": config_hash(validated_config), - }, + {"valid": True, "config_hash": config_hash(validated_config)}, file, indent=2, ) @@ -284,21 +197,12 @@ def write_validation_marker( validation_config = snakemake.params.validation_config if validation_kind == "temporal": - validate_temporal_config_semantics( - validation_config - ) + validate_temporal_config_semantics(validation_config) elif validation_kind == "gap_filling": - validate_gap_filling_config_semantics( - validation_config - ) + validate_gap_filling_config_semantics(validation_config) else: - raise ValueError( - f"Unsupported validation kind {validation_kind!r}." - ) + raise ValueError(f"Unsupported validation kind {validation_kind!r}.") - write_validation_marker( - snakemake.output[0], - validated_config=validation_config, - ) + write_validation_marker(snakemake.output[0], validated_config=validation_config) From 396dc8fee20a406569d7a3040f666b0ec0a3316b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 27 Aug 2026 16:29:35 +0200 Subject: [PATCH 143/241] Updated Interface.yaml to reflect new external_profiles directory --- INTERFACE.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/INTERFACE.yaml b/INTERFACE.yaml index f32b4e5..9041fc6 100644 --- a/INTERFACE.yaml +++ b/INTERFACE.yaml @@ -24,6 +24,11 @@ pathvars: Target shapes used to spatially aggregate electricity demand. The supplied shapes must collectively cover the full spatial extent of the corresponding country. + + external_profiles: + default: "/user/external_profiles" + description: > + Directory of user-provided .csv to feed into advanced tclean gap-filling. results: demand_electricity_MW: From 7dbd200b0f99f5178bb10bbc312521bc07241605 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:29:58 +0000 Subject: [PATCH 144/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- INTERFACE.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INTERFACE.yaml b/INTERFACE.yaml index 9041fc6..26f8e93 100644 --- a/INTERFACE.yaml +++ b/INTERFACE.yaml @@ -24,7 +24,7 @@ pathvars: Target shapes used to spatially aggregate electricity demand. The supplied shapes must collectively cover the full spatial extent of the corresponding country. - + external_profiles: default: "/user/external_profiles" description: > From e67a8ccf3ffcf4b650e6fb0a31bfe5ef28ed7b42 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 28 Aug 2026 10:44:40 +0200 Subject: [PATCH 145/241] Added the final plot to the demand_electricity_polygon rule inputs so it definitely generates as a useful diagnostic for users. --- workflow/rules/prepare_demand.smk | 1 + 1 file changed, 1 insertion(+) diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 5aabee1..6390e18 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -22,6 +22,7 @@ rule demand_electricity_polygon: input: demand_raster="/automatic/{shape}/demand_electricity_raster.tif", demand_profiles="/automatic/{shape}/demand_electricity_countries_profiles.parquet", + cleaning_timeline="/{shape}/load_cleaning_timeline.pdf", shapes="", output: output_data="", From 5c3c112ec2a5d7b706083c94e3c6a7e0a25f1b56 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 28 Aug 2026 10:46:03 +0200 Subject: [PATCH 146/241] Updated the module to use tclean 0.1.1 API which allows for additional scaling methods (normalise_mean, normalise_max). --- config/README.md | 6 +- config/config.yaml | 71 ++++++++- pixi.lock | 22 +-- pixi.toml | 2 +- ruff.toml | 1 - tests/integration/test_config.yaml | 27 +++- tests/unit/test_advanced_execution.py | 8 + tests/unit/test_config_schema.py | 120 ++++++++++++++ tests/unit/test_schemas.py | 2 + tests/unit/test_tclean_config.py | 148 +++++++++++++++++- .../test_workflow_environment_boundary.py | 2 + workflow/envs/module.linux-64.pin.txt | 2 +- workflow/envs/module.osx-arm64.pin.txt | 2 +- workflow/envs/module.win-64.pin.txt | 2 +- workflow/envs/module.yaml | 2 +- workflow/internal/config.schema.yaml | 48 ++++-- workflow/scripts/_tclean_config.py | 18 +-- .../scripts/construct_auxiliary_profile.py | 4 + 18 files changed, 437 insertions(+), 50 deletions(-) diff --git a/config/README.md b/config/README.md index c3d9483..0863d75 100644 --- a/config/README.md +++ b/config/README.md @@ -234,7 +234,7 @@ advanced: weight: 1 scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" @@ -261,7 +261,7 @@ The currently configured scaling strategy is: ```yaml scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" @@ -269,7 +269,7 @@ scaling: weight: 1 ``` -`match_energy` uses the configured scaling periods to align the overall energy level of the constructed profile with a more representative reference. +`match_total` uses the configured scaling periods to align the overall energy level of the constructed profile with a more representative reference. Scaling periods can require auxiliary demand data outside the main target grid; the workflow includes them when compiling acquisition requirements. diff --git a/config/config.yaml b/config/config.yaml index 0ae4a79..002a495 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,21 +2,29 @@ temporal_scope: start: "2017-01-01" end: "2017-01-03" frequency: "1h" -# provide a list of sources and set their priority order. Options include: entsoe, neso, opsd + +# Load sources are listed in priority order. Where multiple sources provide +# data for the same country and timestamp, the earlier source takes priority. +# Supported sources include: entsoe, neso, opsd. load_sources: - entsoe - neso - opsd +# Exercise the full advanced gap-filling workflow. See config/README.md for +# the complete configuration reference and method descriptions. gap_filling: mode: advanced basic: rules: + # Fill short internal gaps by linear interpolation. - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h + # Fill longer gaps using the mean of the corresponding periods one + # week before and one week after the missing period. - name: average_adjacent_weeks method: average_periods max_gap: 326h @@ -24,12 +32,16 @@ gap_filling: - -7d - 7d + # If averaging cannot fill the gap, try copying the corresponding + # period from the previous week. - name: copy_previous_week method: copy_periods max_gap: 168h source_offset: -168h require_complete_source: true + # Finally, try copying the corresponding period from the following + # week when a complete source period is available. - name: copy_following_week method: copy_periods max_gap: 168h @@ -38,10 +50,16 @@ gap_filling: advanced: auxiliary_data: + # Apply the same basic cleaning rules to acquired auxiliary data before + # it is used to construct advanced replacement profiles. basic_cleaning: enabled: true sources: + # Construct an Albanian replacement profile from a winter period of + # British data, then scale it to match Albanian energy over the + # specified scaling period. This source is defined to exercise + # cross-country construction and scaling. alb_from_gbr_alb_winter: method: construct_from_sources periods: @@ -50,13 +68,17 @@ gap_filling: end: "2024-02-01" weight: 1 scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" end: "2024-02-01" weight: 1 + # Construct an Albanian profile from a later period of Albanian data. + # The corresponding rule below overwrites the integration-test target + # period, ensuring construction, auxiliary acquisition, scaling, and + # overwrite behavior are exercised end to end. alb_from_alb_2024: method: construct_from_sources periods: @@ -65,13 +87,29 @@ gap_filling: end: "2024-01-03" weight: 1 scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" end: "2024-01-03" weight: 1 + + # Construct a second Albanian profile from the same 2024 source period, + # normalised so that its arithmetic mean equals one. This exercises + # profile normalisation without requiring additional scaling data. + alb_from_alb_2024_normalised: + method: construct_from_sources + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-03" + weight: 1 + scaling: + method: normalise_mean + # Define a Montenegro profile constructed from Serbian data. Its rule + # deliberately lies outside the target temporal scope, testing that + # inactive advanced rules do not trigger unnecessary acquisition. mne_from_srb: method: construct_from_sources periods: @@ -80,11 +118,16 @@ gap_filling: end: "2022-04-01" weight: 1 + # Read a small committed CSV fixture to exercise the external_profile + # source type and external_profiles pathvar resolution. alb_external_test: method: external_profile file: alb_external_test.csv rules: + # Fill Albanian gaps in January 2022 using the cross-country constructed + # winter profile. This rule is outside the integration-test target + # period and therefore tests inactive-rule selection. - name: build_alb_from_gbr_alb_winter country: ALB start: "2022-01-01" @@ -92,13 +135,29 @@ gap_filling: scope: fill_gaps source: alb_from_gbr_alb_winter + # Overwrite the full Albanian integration-test period using the + # constructed 2024 Albanian profile. This is the main active + # construct_from_sources integration case. - name: build_alb_from_alb_2024 country: ALB start: "2017-01-01" end: "2017-01-03" scope: overwrite source: alb_from_alb_2024 + + # Overwrite one target hour with a mean-normalised constructed profile. + # This exercises normalise_mean end to end while leaving the main + # match_total construction case in place. + - name: test_normalise_mean + country: ALB + start: "2017-01-01 03:00" + end: "2017-01-01 04:00" + scope: overwrite + source: alb_from_alb_2024_normalised + # Deliberately inactive rule: both its target period and source data are + # outside the integration-test scope. It verifies that an inactive rule + # does not create unnecessary auxiliary acquisition jobs. - name: example_inactive_mne_override country: MNE start: "2020-03-01" @@ -106,9 +165,11 @@ gap_filling: scope: fill_gaps source: mne_from_srb + # Overwrite the first three Albanian hours from the external CSV fixture, + # exercising external_profile loading and overwrite application. - name: test_external_profile_overwrite country: ALB - start: "2022-01-01 00:00" - end: "2022-01-08 00:00" + start: "2017-01-01 00:00" + end: "2017-01-01 03:00" scope: overwrite source: alb_external_test diff --git a/pixi.lock b/pixi.lock index c3d930d..d850887 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1160,7 +1160,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1429,7 +1429,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1697,7 +1697,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda test: channels: - url: https://conda.anaconda.org/conda-forge/ @@ -2014,7 +2014,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2269,7 +2269,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2533,7 +2533,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda sha256: 71fd599b42cd1352bb8a7d278eef9b681b8de5efe210f358101ce47de5a4d699 @@ -28326,14 +28326,14 @@ packages: - zstd >=1.5.7,<1.6.0a0 size: 388453 timestamp: 1764777142545 -- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda - sha256: 67bb604c9abd137099fe0e02da2dd5af2f2a3ce8781688267886f85b79789982 - md5: 3530431367468b4a8adc15f0eb08d021 +- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda + sha256: 25fcea0c5103b25ae9535f92ffa631c9b06b5959b09d5911f84947219dc514db + md5: 7baaf81dfa2ac2c3b3ba073bb1b85758 depends: - python >=3.12 - pandas >=3.0.3 - pandera >=0.31.1 - python license: MIT - size: 33199 - timestamp: 1787731237339 + size: 44569 + timestamp: 1787902306138 diff --git a/pixi.toml b/pixi.toml index 56abdcf..2971a4d 100644 --- a/pixi.toml +++ b/pixi.toml @@ -43,7 +43,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = {version = "==0.1.0", channel = "https://conda.anaconda.org/ddahawkins/label/dev"} +tclean = {version = "==0.1.1", channel = "https://conda.anaconda.org/ddahawkins/label/dev"} [feature.test-module.dependencies] pytest = ">=8.3.5" diff --git a/ruff.toml b/ruff.toml index a0d2a0c..97d5205 100644 --- a/ruff.toml +++ b/ruff.toml @@ -20,7 +20,6 @@ max-complexity = 10 [lint.per-file-ignores] "__init__.py" = ["E402", "F401", "D104"] "*.ipynb" = ["E402"] -"tests/**/*.py" = ["D100", "D103"] [lint.flake8-quotes] docstring-quotes = "double" diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 456667a..1a2165b 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -71,7 +71,7 @@ integration_test: end: "2024-02-01" weight: 1 scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" @@ -90,12 +90,25 @@ integration_test: end: "2024-01-03" weight: 1 scaling: - method: match_energy + method: match_total periods: - country: ALB start: "2024-01-01" end: "2024-01-03" weight: 1 + + # Construct a second Albanian profile from the same 2024 source period, + # normalised so that its arithmetic mean equals one. This exercises + # profile normalisation without requiring additional scaling data. + alb_from_alb_2024_normalised: + method: construct_from_sources + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-02" + weight: 1 + scaling: + method: normalise_mean # Define a Montenegro profile constructed from Serbian data. Its rule # deliberately lies outside the target temporal scope, testing that @@ -134,6 +147,16 @@ integration_test: end: "2017-01-03" scope: overwrite source: alb_from_alb_2024 + + # Overwrite one target hour with a mean-normalised constructed profile. + # This exercises normalise_mean end to end while leaving the main + # match_total construction case in place. + - name: test_normalise_mean + country: ALB + start: "2017-01-02" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024_normalised # Deliberately inactive rule: both its target period and source data are # outside the integration-test scope. It verifies that an inactive rule diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py index 29fd59e..a6ace8c 100644 --- a/tests/unit/test_advanced_execution.py +++ b/tests/unit/test_advanced_execution.py @@ -32,12 +32,14 @@ def _requests() -> pd.DataFrame: def test_group_id_depends_only_on_period() -> None: + """Test group id depends only on period.""" start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") assert build_group_id(start=start, end=end) == "20200101T0000__20200201T0000" def test_batch_id_is_independent_of_country_order() -> None: + """Test Batch ignores country order.""" start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") first = build_batch_id( @@ -51,6 +53,7 @@ def test_batch_id_is_independent_of_country_order() -> None: def test_source_batches_group_countries_by_source_and_period() -> None: + """Test sources group countries by source and period.""" batches = build_source_batches(_requests()) assert len(batches) == 2 assert batches[0]["source"] == "entsoe" @@ -61,6 +64,7 @@ def test_source_batches_group_countries_by_source_and_period() -> None: def test_batch_indexes_preserve_compiled_ids() -> None: + """Test indexes preserve ids.""" batches = build_source_batches(_requests()) by_source = index_batch_ids_by_source(batches) by_group = index_batch_ids_by_group(batches) @@ -71,6 +75,7 @@ def test_batch_indexes_preserve_compiled_ids() -> None: def test_serialize_batch_produces_json_safe_values() -> None: + """Test for json safety.""" batch = build_source_batches(_requests())[0] serialized = serialize_batch(batch) json.dumps(serialized) @@ -79,6 +84,7 @@ def test_serialize_batch_produces_json_safe_values() -> None: def test_empty_execution_plan_has_stable_contract() -> None: + """Test empty execution plan.""" assert empty_execution_plan() == { "version": EXECUTION_PLAN_VERSION, "active_rule_names": [], @@ -92,6 +98,7 @@ def test_empty_execution_plan_has_stable_contract() -> None: def test_load_execution_plan_and_get_batch(tmp_path) -> None: + """Test load execution plan.""" batch = serialize_batch(build_source_batches(_requests())[0]) plan = empty_execution_plan() plan["batches"] = [batch] @@ -104,5 +111,6 @@ def test_load_execution_plan_and_get_batch(tmp_path) -> None: def test_get_batch_requires_exactly_one_match() -> None: + """Test get batch requires a single match.""" with pytest.raises(ValueError, match="Expected exactly one auxiliary batch"): get_batch(empty_execution_plan(), batch_id="missing") diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 646ba19..2b26132 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -23,6 +23,7 @@ def _validate(config: dict) -> list[ValidationError]: def test_default_config_matches_schema() -> None: + """Check default config matches schema.""" with CONFIG_PATH.open(encoding="utf-8") as config_file: config = yaml.safe_load(config_file) errors = _validate(config) @@ -30,6 +31,7 @@ def test_default_config_matches_schema() -> None: def test_advanced_mode_allows_no_sources_or_rules() -> None: + """Check advanced mode permits no arguments.""" config = { "temporal_scope": { "start": "2021-01-01", @@ -49,3 +51,121 @@ def test_advanced_mode_allows_no_sources_or_rules() -> None: } errors = _validate(config) assert not errors, "\n".join(_format_validation_error(error) for error in errors) + + +def _advanced_config_with_scaling(scaling: dict) -> dict: + return { + "temporal_scope": { + "start": "2021-01-01", + "end": "2022-01-01", + "frequency": "1h", + }, + "load_sources": ["entsoe"], + "gap_filling": { + "mode": "advanced", + "basic": {"rules": []}, + "advanced": { + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, + "sources": { + "constructed": { + "method": "construct_from_sources", + "periods": [ + { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-01-02", + "weight": 1, + } + ], + "scaling": scaling, + } + }, + "rules": [], + }, + }, + } + + +def test_constructed_source_allows_match_total_scaling() -> None: + """Check for match_total method.""" + config = _advanced_config_with_scaling( + { + "method": "match_total", + "periods": [ + { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-01-02", + "weight": 1, + } + ], + } + ) + + assert not _validate(config) + + +def test_constructed_source_allows_normalise_mean_scaling() -> None: + """Check normalise mean method.""" + config = _advanced_config_with_scaling({"method": "normalise_mean"}) + + assert not _validate(config) + + +def test_constructed_source_allows_normalise_max_scaling() -> None: + """Check normalise max method.""" + config = _advanced_config_with_scaling({"method": "normalise_max"}) + + assert not _validate(config) + + +def test_match_total_scaling_requires_periods() -> None: + """Check match_total required periods.""" + config = _advanced_config_with_scaling({"method": "match_total"}) + + assert _validate(config) + + +def test_normalise_mean_scaling_rejects_periods() -> None: + """Check norm mean rejects additional args.""" + config = _advanced_config_with_scaling( + { + "method": "normalise_mean", + "periods": [ + { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-01-02", + "weight": 1, + } + ], + } + ) + + assert _validate(config) + + +def test_normalise_max_scaling_rejects_periods() -> None: + """Check norm max rejects additional args.""" + config = _advanced_config_with_scaling( + { + "method": "normalise_max", + "periods": [ + { + "country": "ALB", + "start": "2020-01-01", + "end": "2020-01-02", + "weight": 1, + } + ], + } + ) + + assert _validate(config) + + +def test_constructed_source_rejects_unknown_scaling_method() -> None: + """Check rejection of unknown methods.""" + config = _advanced_config_with_scaling({"method": "something_else"}) + + assert _validate(config) diff --git a/tests/unit/test_schemas.py b/tests/unit/test_schemas.py index d9fe518..7d19b57 100644 --- a/tests/unit/test_schemas.py +++ b/tests/unit/test_schemas.py @@ -7,6 +7,7 @@ def test_opsd_schema_accepts_provider_rows() -> None: + """Test OPSD schema accepts provider rows.""" frame = pd.DataFrame( { "region": ["GB"], @@ -21,6 +22,7 @@ def test_opsd_schema_accepts_provider_rows() -> None: def test_shapes_schema_rejects_unknown_shape_class() -> None: + """Tests shapes schema rejects unknown shape class.""" frame = pd.DataFrame( { "shape_id": ["x"], diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index 4e953c7..cbe31a1 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -1,11 +1,18 @@ """Tests for the Modelblocks-to-T-Clean configuration adapter.""" import pandas as pd -from _tclean_config import build_advanced_rules, build_basic_rules, build_time_grid +from _tclean_config import ( + build_advanced_rules, + build_all_constructed_source_periods, + build_basic_rules, + build_scaling_source_periods, + build_time_grid, +) from tclean import TimeGrid def test_build_time_grid_translates_temporal_scope() -> None: + """Test build time grid accepts temporal scope.""" grid = build_time_grid( { "start": "2020-01-01T00:30:00Z", @@ -20,11 +27,13 @@ def test_build_time_grid_translates_temporal_scope() -> None: def test_build_basic_rules_returns_no_rules_when_mode_is_off() -> None: + """Test basic rules returns nothing when disabled.""" config = {"mode": "off", "basic": {"rules": []}} assert build_basic_rules(config) == [] def test_build_basic_rules_preserves_configured_rule_order() -> None: + """Test basic rules preserves order.""" rules = [ {"name": "first", "method": "linear_interpolation", "max_gap": "2h"}, { @@ -39,6 +48,7 @@ def test_build_basic_rules_preserves_configured_rule_order() -> None: def test_build_advanced_rules_returns_canonical_columns_when_empty() -> None: + """Test advanced returns column headers even when empty.""" config = { "mode": "advanced", "basic": {"rules": []}, @@ -55,3 +65,139 @@ def test_build_advanced_rules_returns_canonical_columns_when_empty() -> None: "end", "scope", ] + +def test_build_scaling_source_periods_builds_match_total_periods() -> None: + """Check match_total scaling periods are translated for T-Clean.""" + definition = { + "method": "construct_from_sources", + "periods": [ + { + "country": "GBR", + "start": "2024-01-01", + "end": "2024-02-01", + "weight": 1, + } + ], + "scaling": { + "method": "match_total", + "periods": [ + { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-02-01", + "weight": 1, + } + ], + }, + } + + result = build_scaling_source_periods(definition) + + assert result is not None + assert result["context"].tolist() == ["ALB"] + assert result["weight"].tolist() == [1] + assert result["start"].tolist() == [ + pd.Timestamp("2023-01-01", tz="UTC") + ] + assert result["end"].tolist() == [ + pd.Timestamp("2023-02-01", tz="UTC") + ] + + +def test_build_scaling_source_periods_returns_none_for_normalise_mean() -> None: + """Check mean normalisation requires no auxiliary scaling periods.""" + definition = { + "method": "construct_from_sources", + "periods": [], + "scaling": {"method": "normalise_mean"}, + } + + assert build_scaling_source_periods(definition) is None + + +def test_build_scaling_source_periods_returns_none_for_normalise_max() -> None: + """Check maximum normalisation requires no auxiliary scaling periods.""" + definition = { + "method": "construct_from_sources", + "periods": [], + "scaling": {"method": "normalise_max"}, + } + + assert build_scaling_source_periods(definition) is None + +def test_normalisation_scaling_does_not_add_auxiliary_periods() -> None: + """Check normalisation methods do not add scaling acquisition periods.""" + gap_filling_config = { + "mode": "advanced", + "advanced": { + "sources": { + "mean_source": { + "method": "construct_from_sources", + "periods": [ + { + "country": "GBR", + "start": "2024-01-01", + "end": "2024-02-01", + "weight": 1, + } + ], + "scaling": {"method": "normalise_mean"}, + }, + "max_source": { + "method": "construct_from_sources", + "periods": [ + { + "country": "FRA", + "start": "2024-01-01", + "end": "2024-02-01", + "weight": 1, + } + ], + "scaling": {"method": "normalise_max"}, + }, + } + }, + } + + result = build_all_constructed_source_periods(gap_filling_config) + + assert set(result) == {"mean_source", "max_source"} + assert result["mean_source"]["context"].tolist() == ["GBR"] + assert result["max_source"]["context"].tolist() == ["FRA"] + +def test_match_total_scaling_adds_auxiliary_periods() -> None: + """Check match_total adds reference periods to auxiliary acquisition.""" + gap_filling_config = { + "mode": "advanced", + "advanced": { + "sources": { + "scaled_source": { + "method": "construct_from_sources", + "periods": [ + { + "country": "GBR", + "start": "2024-01-01", + "end": "2024-02-01", + "weight": 1, + } + ], + "scaling": { + "method": "match_total", + "periods": [ + { + "country": "ALB", + "start": "2023-01-01", + "end": "2023-02-01", + "weight": 1, + } + ], + }, + } + } + }, + } + + result = build_all_constructed_source_periods(gap_filling_config) + + assert set(result) == {"scaled_source"} + assert result["scaled_source"]["context"].tolist() == ["GBR", "ALB"] diff --git a/tests/unit/test_workflow_environment_boundary.py b/tests/unit/test_workflow_environment_boundary.py index f9fdff3..d7ab9c8 100644 --- a/tests/unit/test_workflow_environment_boundary.py +++ b/tests/unit/test_workflow_environment_boundary.py @@ -21,6 +21,7 @@ def test_snakemake_host_code_has_no_module_runtime_dependencies() -> None: + """Check for runtime dependencies.""" workflow_files = [Path("workflow/Snakefile"), *Path("workflow/rules").glob("*.smk")] violations = [] for path in workflow_files: @@ -35,6 +36,7 @@ def test_snakemake_host_code_has_no_module_runtime_dependencies() -> None: def test_removed_legacy_packages_are_not_reintroduced() -> None: + """Test legacy package removal.""" assert not Path("workflow/scripts/cleaning").exists() assert not Path("workflow/scripts/common").exists() diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index b3fa95a..1a8bf92 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -48,7 +48,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openb https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda#d8285bea2a350f63fab23bf460221f3f https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda#15c437bfa4cbddd379b95357c9aa4150 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda#55f526c3fb5302a1ce922612348442e1 diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index fe61044..602d287 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -41,7 +41,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_open https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda#d83fc83d589e2625a3451c9a7e21047c https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda#4581a32b837950217327fcab93214313 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda#d037e9adb0365ab53445f357bd9a035f diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index db360af..97688ac 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -45,7 +45,7 @@ https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.con https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda#f9ac74c3b07c396014434aca1e58d362 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda#9da394ea5e0ec5cc5edc1ad14f2a4d4d https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.0-pyh129be3f_0.conda#3530431367468b4a8adc15f0eb08d021 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda#1045d29f787812d3fac1fd80a1339710 diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index c2ba66c..8aa9d16 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -21,4 +21,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 -- ddahawkins/label/dev::tclean ==0.1.0 +- ddahawkins/label/dev::tclean ==0.1.1 diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 00f557b..e4bdb6f 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -189,22 +189,44 @@ $defs: source. constructed_source_scaling: - type: object - properties: - method: - const: match_energy + oneOf: - periods: - $ref: "#/$defs/source_periods" - description: > - Reference periods whose weighted-mean energy determines the - energy total of the constructed profile. + - type: object + properties: + method: + const: match_total - required: - - method - - periods + periods: + $ref: "#/$defs/source_periods" + description: > + Reference periods whose weighted-mean total determines the + total of the constructed profile. - additionalProperties: false + required: + - method + - periods + + additionalProperties: false + + - type: object + properties: + method: + const: normalise_mean + + required: + - method + + additionalProperties: false + + - type: object + properties: + method: + const: normalise_max + + required: + - method + + additionalProperties: false construct_from_sources_definition: type: object diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 170233b..bfa5c81 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -84,18 +84,18 @@ def build_constructed_source_periods( def build_scaling_source_periods( - source_definition: Mapping[str, Any], -) -> pd.DataFrame | None: - """Build optional T-Clean scaling periods for a constructed source.""" - scaling = source_definition.get("scaling") + source_definition: Mapping[str, Any], + ) -> pd.DataFrame | None: + """Build scaling source periods for a constructed advanced source.""" + scaling = source_definition.get("scaling") - if scaling is None: - return None + if scaling is None: + return None - if scaling["method"] != "match_energy": - raise ValueError(f"Unsupported scaling method: {scaling['method']!r}.") + if scaling["method"] != "match_total": + return None - return _build_source_periods(scaling["periods"]) + return _build_source_periods(scaling["periods"]) def build_all_constructed_source_periods( diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 0a4263f..be5dac2 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -25,6 +25,9 @@ sources = build_constructed_source_periods(source_definition) scaling_sources = build_scaling_source_periods(source_definition) +scaling = source_definition.get("scaling") +scaling_method = None if scaling is None else scaling["method"] + grid = TimeGrid( start=rule["start"], end=rule["end"], frequency=snakemake.params.frequency ) @@ -50,6 +53,7 @@ source_data, target_index=grid.target_index, sources=sources, + scaling_method=scaling_method, scaling_sources=scaling_sources, grid=grid, ) From 370ef1b902e6a014a57feb1c0065534c3915a989 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 08:46:18 +0000 Subject: [PATCH 147/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- config/config.yaml | 4 ++-- tests/integration/test_config.yaml | 4 ++-- tests/unit/test_tclean_config.py | 18 ++++++------------ workflow/scripts/_tclean_config.py | 18 +++++++++--------- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 002a495..75f46cc 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -93,7 +93,7 @@ gap_filling: start: "2024-01-01" end: "2024-01-03" weight: 1 - + # Construct a second Albanian profile from the same 2024 source period, # normalised so that its arithmetic mean equals one. This exercises # profile normalisation without requiring additional scaling data. @@ -144,7 +144,7 @@ gap_filling: end: "2017-01-03" scope: overwrite source: alb_from_alb_2024 - + # Overwrite one target hour with a mean-normalised constructed profile. # This exercises normalise_mean end to end while leaving the main # match_total construction case in place. diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 1a2165b..d005eab 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -96,7 +96,7 @@ integration_test: start: "2024-01-01" end: "2024-01-03" weight: 1 - + # Construct a second Albanian profile from the same 2024 source period, # normalised so that its arithmetic mean equals one. This exercises # profile normalisation without requiring additional scaling data. @@ -147,7 +147,7 @@ integration_test: end: "2017-01-03" scope: overwrite source: alb_from_alb_2024 - + # Overwrite one target hour with a mean-normalised constructed profile. # This exercises normalise_mean end to end while leaving the main # match_total construction case in place. diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index cbe31a1..63126a8 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -66,17 +66,13 @@ def test_build_advanced_rules_returns_canonical_columns_when_empty() -> None: "scope", ] + def test_build_scaling_source_periods_builds_match_total_periods() -> None: """Check match_total scaling periods are translated for T-Clean.""" definition = { "method": "construct_from_sources", "periods": [ - { - "country": "GBR", - "start": "2024-01-01", - "end": "2024-02-01", - "weight": 1, - } + {"country": "GBR", "start": "2024-01-01", "end": "2024-02-01", "weight": 1} ], "scaling": { "method": "match_total", @@ -96,12 +92,8 @@ def test_build_scaling_source_periods_builds_match_total_periods() -> None: assert result is not None assert result["context"].tolist() == ["ALB"] assert result["weight"].tolist() == [1] - assert result["start"].tolist() == [ - pd.Timestamp("2023-01-01", tz="UTC") - ] - assert result["end"].tolist() == [ - pd.Timestamp("2023-02-01", tz="UTC") - ] + assert result["start"].tolist() == [pd.Timestamp("2023-01-01", tz="UTC")] + assert result["end"].tolist() == [pd.Timestamp("2023-02-01", tz="UTC")] def test_build_scaling_source_periods_returns_none_for_normalise_mean() -> None: @@ -125,6 +117,7 @@ def test_build_scaling_source_periods_returns_none_for_normalise_max() -> None: assert build_scaling_source_periods(definition) is None + def test_normalisation_scaling_does_not_add_auxiliary_periods() -> None: """Check normalisation methods do not add scaling acquisition periods.""" gap_filling_config = { @@ -165,6 +158,7 @@ def test_normalisation_scaling_does_not_add_auxiliary_periods() -> None: assert result["mean_source"]["context"].tolist() == ["GBR"] assert result["max_source"]["context"].tolist() == ["FRA"] + def test_match_total_scaling_adds_auxiliary_periods() -> None: """Check match_total adds reference periods to auxiliary acquisition.""" gap_filling_config = { diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index bfa5c81..2ae7868 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -84,18 +84,18 @@ def build_constructed_source_periods( def build_scaling_source_periods( - source_definition: Mapping[str, Any], - ) -> pd.DataFrame | None: - """Build scaling source periods for a constructed advanced source.""" - scaling = source_definition.get("scaling") + source_definition: Mapping[str, Any], +) -> pd.DataFrame | None: + """Build scaling source periods for a constructed advanced source.""" + scaling = source_definition.get("scaling") - if scaling is None: - return None + if scaling is None: + return None - if scaling["method"] != "match_total": - return None + if scaling["method"] != "match_total": + return None - return _build_source_periods(scaling["periods"]) + return _build_source_periods(scaling["periods"]) def build_all_constructed_source_periods( From 2cee6612f2eb5a4b32c47c8b4bea8f9d75e19321 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 10:56:39 +0200 Subject: [PATCH 148/241] Rebuilt the entsoe source pipeline to download separate country-year files to minimise re-calls to the API. --- workflow/rules/_utils.smk | 42 +++++++++++++ workflow/rules/advanced.smk | 56 +++++------------ workflow/rules/automatic.smk | 32 +++++----- workflow/rules/clean.smk | 26 ++++---- workflow/scripts/download_load_entsoe.py | 38 ++++-------- workflow/scripts/prepare_load_entsoe.py | 3 +- workflow/scripts/sources/entsoe/prepare.py | 70 ++++++++++++++++++++-- 7 files changed, 166 insertions(+), 101 deletions(-) diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 0089db2..96098fb 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -1,5 +1,47 @@ """Collection of auxiliary functions for this module.""" +from datetime import datetime, timedelta, timezone + + +def _as_utc(value): + """Interpret naive timestamps as UTC and convert aware timestamps to UTC. Mirros tclean logic.""" + parsed = datetime.fromisoformat(str(value)) + + if parsed.tzinfo is None: + return parsed.replace(tzinfo=timezone.utc) + + return parsed.astimezone(timezone.utc) + + +def years_for_period(start, end): + """Return UTC calendar years intersected by the half-open period [start, end). Mirros tclean logic.""" + start = _as_utc(start) + end = _as_utc(end) + + if end <= start: + raise ValueError("Temporal period end must be later than its start.") + + final_included_time = end - timedelta(microseconds=1) + + return list(range(start.year, final_included_time.year + 1)) + + +def neso_annual_files(years): + """Return reusable annual NESO raw-file paths for the requested years.""" + return [ + "/automatic/neso/" f"historic_demand_{int(year)}.csv" + for year in years + ] + + +def entsoe_annual_files(countries, years): + """Return reusable ENTSO-E country-year raw-file paths.""" + return [ + "/automatic/entsoe/raw/" f"{country}/{int(year)}.parquet" + for country in countries + for year in years + ] + def additional_config_validation(): """Validate configuration relationships that require no module dependencies.""" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 04e2f79..ba1df57 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -24,10 +24,20 @@ def auxiliary_neso_raw_files(wildcards): if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") ) - return [ - ("/automatic/neso/" f"historic_demand_{year}.csv") - for year in batch["years"] - ] + return neso_annual_files(batch["years"]) + + +def auxiliary_entsoe_raw_files(wildcards): + """Return ENTSO-E country-year files required by one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") + ) + + return entsoe_annual_files(batch["countries"], batch["years"]) def auxiliary_group_source_files(wildcards): @@ -95,22 +105,6 @@ def advanced_external_profile_files(_wildcards): ] -def auxiliary_entsoe_threads(wildcards): - """Return useful ENTSO-E threads for one auxiliary batch.""" - plan = _read_auxiliary_plan() - - batch = next( - batch - for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") - ) - - return min( - internal["load_entsoe"]["MAX_WORKERS"], - len(batch["countries"]), - ) - - checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, @@ -149,30 +143,10 @@ rule finalise_clean_demand: "../scripts/finalise_clean_demand.py" -rule download_auxiliary_load_entsoe: - input: - token_entsoe="", - plan=auxiliary_acquisition_plan, - output: - raw_load=("/automatic/" "auxiliary/entsoe/raw/" "{batch_id}.parquet"), - log: - ("/auxiliary/" "entsoe/download_{batch_id}.log"), - localrule: True - conda: - "../envs/module.yaml" - threads: auxiliary_entsoe_threads - params: - frequency=config["temporal_scope"]["frequency"], - message: - "Download auxiliary electricity load from ENTSO-E." - script: - "../scripts/download_load_entsoe.py" - - rule prepare_auxiliary_load_entsoe: input: plan=auxiliary_acquisition_plan, - raw_load=rules.download_auxiliary_load_entsoe.output.raw_load, + annual_files=auxiliary_entsoe_raw_files, output: load=("/automatic/" "auxiliary/entsoe/" "{batch_id}.parquet"), log: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index de9ba24..b8e8648 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -34,29 +34,31 @@ rule validate_gap_filling_config_semantics: "../scripts/validate_config.py" -rule download_load_entsoe: +rule download_load_entsoe_country_year: input: - validation="/automatic/temporal_config_validation.json", token_entsoe="", output: - raw_load="/automatic/entsoe/raw_load.parquet", + annual_file=( + "/automatic/entsoe/raw/" + "{country}/{year}.parquet" + ), log: - "/download_load_entsoe.log", + "/download_load_entsoe_{country}_{year}.log", + wildcard_constraints: + country="[A-Z]{3}", + year="[0-9]{4}", localrule: True conda: "../envs/module.yaml" - threads: - min( - internal["load_entsoe"]["MAX_WORKERS"], - len(internal["load_entsoe"]["countries"]), - ) + threads: 1 params: - temporal_start=config["temporal_scope"]["start"], - temporal_end=config["temporal_scope"]["end"], - frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], + country_code=lambda wildcards: wildcards.country, + year=lambda wildcards: int(wildcards.year), message: - "Download electricity load from ENTSOE." + ( + "Download ENTSO-E electricity load for " + "{wildcards.country} in {wildcards.year}." + ) script: "../scripts/download_load_entsoe.py" @@ -78,8 +80,6 @@ rule download_load_opsd: rule download_load_neso_year: - input: - validation="/automatic/temporal_config_validation.json", output: annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), log: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 92999d7..5585787 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,27 +1,27 @@ -from datetime import datetime, timedelta +def entsoe_raw_files(_wildcards): + """Return ENTSO-E country-year files required by the configured period.""" + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], + ) + + return entsoe_annual_files(internal["load_entsoe"]["countries"], years) def neso_raw_files(_wildcards): """Return annual NESO input files for the configured period.""" - start = datetime.fromisoformat(config["temporal_scope"]["start"]) - end = datetime.fromisoformat(config["temporal_scope"]["end"]) - - final_included_time = end - timedelta(microseconds=1) - - years = range( - start.year, - final_included_time.year + 1, + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], ) - return [ - ("/automatic/neso/" f"historic_demand_{year}.csv") for year in years - ] + return neso_annual_files(years) rule prepare_load_entsoe: input: validation="/automatic/temporal_config_validation.json", - raw_load=rules.download_load_entsoe.output.raw_load, + annual_files=entsoe_raw_files, output: load="/automatic/load_entsoe.parquet", log: diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index 8f1efab..e32dfc6 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -1,44 +1,32 @@ -"""Snakemake entry point for downloading ENTSO-E load data.""" +"""Snakemake entry point for downloading one ENTSO-E country-year chunk.""" import logging from pathlib import Path from typing import TYPE_CHECKING, Any -from _advanced_execution import get_batch, load_execution_plan +import pandas as pd + from sources.entsoe.download import download_entsoe -from tclean import TimeGrid if TYPE_CHECKING: snakemake: Any def main(snakemake: Any) -> None: - """Download ENTSO-E data for the requested workflow period.""" - plan_path = getattr(snakemake.input, "plan", None) - - if plan_path is not None: - plan = load_execution_plan(plan_path) - - batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="entsoe") - - start = batch["start"] - end = batch["end"] - country_codes = list(batch["countries"]) - - else: - start = snakemake.params.temporal_start - end = snakemake.params.temporal_end - country_codes = list(snakemake.params.country_codes) + """Download one UTC calendar year of ENTSO-E data for one country.""" + year = int(snakemake.params.year) + country_code = str(snakemake.params.country_code) - grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) + start = pd.Timestamp(year=year, month=1, day=1, tz="UTC") + end = pd.Timestamp(year=year + 1, month=1, day=1, tz="UTC") download_entsoe( - start=grid.start, - end=grid.end, - country_codes=country_codes, + start=start, + end=end, + country_codes=[country_code], token_path=snakemake.input.token_entsoe, - output_path=snakemake.output.raw_load, - workers=snakemake.threads, + output_path=snakemake.output.annual_file, + workers=1, ) diff --git a/workflow/scripts/prepare_load_entsoe.py b/workflow/scripts/prepare_load_entsoe.py index 0a2d276..884d7b8 100644 --- a/workflow/scripts/prepare_load_entsoe.py +++ b/workflow/scripts/prepare_load_entsoe.py @@ -1,5 +1,6 @@ """Snakemake entry point for preparing ENTSO-E load data.""" +from pathlib import Path from typing import TYPE_CHECKING, Any from _advanced_execution import get_batch, load_execution_plan @@ -31,7 +32,7 @@ def main(snakemake: Any) -> None: grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_entsoe( - input_path=snakemake.input.raw_load, + input_paths=[Path(path) for path in snakemake.input.annual_files], output_path=snakemake.output.load, grid=grid, country_codes=country_codes, diff --git a/workflow/scripts/sources/entsoe/prepare.py b/workflow/scripts/sources/entsoe/prepare.py index bce2940..7d8b5c9 100644 --- a/workflow/scripts/sources/entsoe/prepare.py +++ b/workflow/scripts/sources/entsoe/prepare.py @@ -1,27 +1,87 @@ """Prepare downloaded ENTSO-E electricity-demand data.""" +from collections import defaultdict +from collections.abc import Iterable from pathlib import Path import pandas as pd from tclean import TimeGrid +def _read_country_year_files( + input_paths: Iterable[str | Path], +) -> dict[str, pd.Series]: + """Read and combine reusable ENTSO-E country-year files by country.""" + annual_series: dict[str, list[pd.Series]] = defaultdict(list) + + for input_path in input_paths: + input_path = Path(input_path) + frame = pd.read_parquet(input_path) + + if len(frame.columns) != 1: + raise ValueError( + "Expected each ENTSO-E country-year file to contain exactly one " + f"country column, but {input_path} contains {list(frame.columns)}." + ) + + country_code = str(frame.columns[0]) + series = frame.iloc[:, 0].copy() + series.name = country_code + series.index = pd.to_datetime(series.index, utc=True) + + annual_series[country_code].append(series) + + combined: dict[str, pd.Series] = {} + + for country_code, series_parts in annual_series.items(): + series = pd.concat(series_parts).sort_index() + + duplicate_mask = series.index.duplicated(keep=False) + + if duplicate_mask.any(): + duplicate_timestamps = ( + series.index[duplicate_mask].unique().astype(str).tolist() + ) + + raise ValueError( + f"ENTSO-E data for {country_code} contain duplicate UTC timestamps " + f"across country-year files: {duplicate_timestamps[:10]}" + ) + + combined[country_code] = series + + return combined + + def prepare_entsoe( *, - input_path: str | Path, + input_paths: Iterable[str | Path], output_path: str | Path, grid: TimeGrid, country_codes: list[str], ) -> None: """Prepare ENTSO-E demand on the configured target grid.""" - data = pd.read_parquet(input_path) + raw_by_country = _read_country_year_files(input_paths) - data.index = pd.to_datetime(data.index, utc=True) + missing_countries = [ + country_code + for country_code in country_codes + if country_code not in raw_by_country + ] - data = data.resample("1h").mean() + if missing_countries: + raise ValueError( + "Missing ENTSO-E country-year inputs for configured countries: " + f"{missing_countries}." + ) - data = data.reindex(index=grid.target_index, columns=country_codes) + hourly_by_country = { + country_code: raw_by_country[country_code].resample("1h").mean() + for country_code in country_codes + } + data = pd.DataFrame(hourly_by_country) + data = data.reindex(index=grid.target_index, columns=country_codes) data = data.astype(float) output_path = Path(output_path) From 40a33ff52f10484520db9738ffa59be5dc95ea47 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 11:56:40 +0200 Subject: [PATCH 149/241] Adding a snakemake config file to the integration tests as these are not being picked up by the tests. --- tests/integration/profiles/defaults/config.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/integration/profiles/defaults/config.yaml diff --git a/tests/integration/profiles/defaults/config.yaml b/tests/integration/profiles/defaults/config.yaml new file mode 100644 index 0000000..4c2f606 --- /dev/null +++ b/tests/integration/profiles/defaults/config.yaml @@ -0,0 +1,12 @@ +# snakemake settings for module cross-compatibility +# DO NOT MODIFY +software-deployment-method: conda +use-conda: True + +#Cores: conservative but useful default value +cores: 2 + +#Download Resources: conservative defaults to minimise API traffic. +resources: + entsoe_download: 2 + neso_download: 2 \ No newline at end of file From d9d019f85c9283d69a2a77bd5feed6f4aa6a2e5c Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 11:57:11 +0200 Subject: [PATCH 150/241] Added default cores and downloader resource caps to the snakemake config --- workflow/profiles/default/config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/workflow/profiles/default/config.yaml b/workflow/profiles/default/config.yaml index 2923460..4c2f606 100644 --- a/workflow/profiles/default/config.yaml +++ b/workflow/profiles/default/config.yaml @@ -2,3 +2,11 @@ # DO NOT MODIFY software-deployment-method: conda use-conda: True + +#Cores: conservative but useful default value +cores: 2 + +#Download Resources: conservative defaults to minimise API traffic. +resources: + entsoe_download: 2 + neso_download: 2 \ No newline at end of file From 799fdeef9954c9c9fdec99a4c71dab34f746a922 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 11:57:43 +0200 Subject: [PATCH 151/241] path rename --- tests/integration/profiles/{defaults => default}/config.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/integration/profiles/{defaults => default}/config.yaml (100%) diff --git a/tests/integration/profiles/defaults/config.yaml b/tests/integration/profiles/default/config.yaml similarity index 100% rename from tests/integration/profiles/defaults/config.yaml rename to tests/integration/profiles/default/config.yaml From 0964697f8294187c3ec18593daaebb24cb0450e8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:01:08 +0200 Subject: [PATCH 152/241] Added resource/thread constraints to download rules and --- workflow/rules/automatic.smk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index b8e8648..c32e3c5 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -54,6 +54,8 @@ rule download_load_entsoe_country_year: params: country_code=lambda wildcards: wildcards.country, year=lambda wildcards: int(wildcards.year), + resources: + entsoe_download=1 message: ( "Download ENTSO-E electricity load for " @@ -89,6 +91,10 @@ rule download_load_neso_year: "../envs/module.yaml" params: year=lambda wildcards: int(wildcards.year), + threads: + 1 + resources: + neso_download=1 message: "Download NESO historic electricity demand for {wildcards.year}." script: From 2f3cf4a55b2a459c7c6c711e5abe3e7fc2744c06 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:01:21 +0200 Subject: [PATCH 153/241] Removed MAX_WORKERS thread constraint --- workflow/internal/settings.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index 9a6f9ef..fc2a317 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -44,4 +44,3 @@ load_entsoe: - "SVN" - "SVK" # - "TUR" #Data is unavailable for Turkey - MAX_WORKERS: 3 From 1391368d64e0001e0a47d9a6b4a8a40868e37246 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:08:36 +0200 Subject: [PATCH 154/241] Removed legacy logging message from entsoe download. --- workflow/scripts/sources/entsoe/download.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index dc47562..460b7c8 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -71,7 +71,7 @@ def download_entsoe( total_countries = len(country_codes) download_start = perf_counter() - logger.info( + logger.debug( "Downloading ENTSO-E load for %s countries " "from %s to %s using %s parallel workers.", total_countries, From b24961d64020c820b559f224378398dc072a60e5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:15:36 +0200 Subject: [PATCH 155/241] Imports tclean 0.1.1 via conda --- pixi.lock | 20496 +++++------------------ pixi.toml | 3 +- workflow/envs/module.linux-64.pin.txt | 456 +- workflow/envs/module.osx-arm64.pin.txt | 414 +- workflow/envs/module.win-64.pin.txt | 392 +- workflow/envs/module.yaml | 3 +- 6 files changed, 5179 insertions(+), 16585 deletions(-) diff --git a/pixi.lock b/pixi.lock index d850887..972a7f1 100644 --- a/pixi.lock +++ b/pixi.lock @@ -8,213 +8,217 @@ environments: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ - - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.5.0-py310hd8a072f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314h4a8dc5f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h8d76f0c_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cgl-0.60.10-hc46dffc_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.5.3-py314hdafbbf9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.10.1-py314h9e666f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.1-py314h9e666f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.6-h2b0a6b4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.1-hee1de02_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.13.1-hfc2019e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h67ed8a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.0-hfc2019e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.2-py314h42812f9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.8-gpl_hc2c16d8_100.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-8_h6ae95b6_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.8.1-hd28c85e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.8.1-h9b73f26_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.8.1-py314hd8b5600_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-10_h6ae95b6_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.3-h4c96295_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h9463b59_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h72ddc62_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.0-py314h9e666f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h9891dd4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.1.0-py314h518bba1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py314h2b28147_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py314hb4ffadd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hda50119_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hc824af9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314hd3a7d6b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-habeac84_100_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.11.0-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py314h7e8cd81_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.5.1-py314h1bee95f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.2.0-py312h8a5ba0d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.8.post0-hee9eb32_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.8.post0-h1c70be6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314h0f05182_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.15.19-h6a952e8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.4-hb700be7_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hab81395_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.51-py314h0f05182_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py314h5bd0f2a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.5-hdfdb5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.9-h4dbf13b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314hfe1a184_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -223,68 +227,66 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.50-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyha191276_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.31.1-h27fc3a9_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.31.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda @@ -295,69 +297,69 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.68.3-pyh8f84b5b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda osx-arm64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-1.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -366,68 +368,66 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.50-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh01cf8df_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.31.1-h27fc3a9_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.31.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda @@ -438,185 +438,190 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.68.3-pyh8f84b5b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.5.0-py310h3b8a9b8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h3daef5d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py314h44086f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314h618e29d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cgl-0.60.10-h034796e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.5.3-py314h4dc9dd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.10.1-py314hcec6336_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h4f10f1e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.1-py314hcec6336_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h403dcb5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.6-h4e57454_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.1-h37541a8_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.13.1-hf76c51c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h026c20a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.0-hf76c51c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.2-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.2.1-h3103d1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.8-gpl_h6fbacd7_100.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hd5a2499_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.1-ha08bb59_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-8_h1b118fd_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.8.1-h7950639_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.8.1-hb995d7d_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.8.1-py314h96ff89e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-10_h1b118fd_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.3-he8aa2a2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h7d962ec_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h74c22ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h626c152_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.0-py314hcec6336_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314h6cfcd04_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.1.0-py314h2fbedac_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py314hb79c6fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.56.4-hf80efc4_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hf2636f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hb84d1df_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py314h54f3292_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-h156bc91_100_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.11.0-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hd89b7e0_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hf6b4638_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.5.1-py314he1d1ac0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.2.0-py312hcedbef1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.8.post0-hdca3b7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.8.post0-h2fd3d4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314ha14b1ff_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.15.19-h80928e0_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.4-h4ddebb9_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-ha0f8610_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.51-py314ha14b1ff_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.5-h4e39fff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.9-hfbe1efa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-ha1acc90_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314hd98292b_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda win-64: - - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-executor-plugins-9.4.0-pyh84498cf_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-logger-plugins-2.1.0-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-report-plugins-1.3.0-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-scheduler-plugins-2.0.2-pyhd4c3c12_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.0.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -625,67 +630,65 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.50-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh6dadd2b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyhe2676ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyhe2676ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.31.1-h27fc3a9_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.31.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda @@ -696,410 +699,388 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.68.3-pyha7b4d00_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.5.0-py310ha413424_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314he701e3d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py314h5a2d7ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cgl-0.60.10-h7c5f3e6_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.5.3-py314h86ab7b2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.10.1-py314hb821551_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.1-py314hb821551_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h7f4e812_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.13.1-h11686cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.0-h11686cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphviz-14.1.2-h4c50273_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.2-py314hb98de8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.2.1-h5a1b470_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h637d24d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.8-gpl_he24518a_100.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-h8206538_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.1-h7ce1215_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.8.1-h06825f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.8.1-h7dcf005_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.8.1-py314h40f010b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_6.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.0-py314hb98de8c_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314h909e829_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.1.0-py314h13f4da2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.0-py314h02f10f6_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py314hf700ef7_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h13911b6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-hd2b5f0e_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314h13f4da2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py314h9f07db2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h4b44e0e_100_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.11.0-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hcaaf0b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hf700ef7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-hac47afa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.5.1-py314hc980628_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.2.0-py312h343a6d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.8.post0-h5112557_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.8.post0-hf5d2508_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.15.19-h45713df_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.4-h49e36cd_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-h9f585f1_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.51-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.5-h6bcfbb0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.9-h477610d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/wrapt-1.17.3-py314h5a2d7ad_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-h0e40799_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-h0e40799_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hba3369d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hfa92662_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-h0e40799_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-hfa92662_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda module: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ - - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.30-hec5e740_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hfd43aa1_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.19-h756ea98_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h235a6dd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.9-h5e77a74_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.18-h2af50b2_12.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.5-h0009854_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.5-hbaf354b_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.19-h756ea98_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.18-h756ea98_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.28.2-h6c0439f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.379-h5a9005d_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.13.0-h935415a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.8.0-hd126650_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.12.0-hd2e3451_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.7.0-h10ac4d7_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.11.0-h325d260_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.4.1-hf8ad068_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.0.2-h07f6e7f_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gdal-3.9.2-py312h1299960_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.3-hf7fa9e8_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_hdf9ad27_105.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h1220068_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.3-hf8d3e68_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.7.7-hadbb8c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-h8d2e343_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf54134d_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.9.2-ha770c72_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.9.2-h353785f_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-fits-3.9.2-h2db6552_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-grib-3.9.2-hc3b29a1_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf4-3.9.2-hd5ecb85_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf5-3.9.2-h6283f77_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-jp2openjpeg-3.9.2-h1b2c38e_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-kea-3.9.2-h1df15e4_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-netcdf-3.9.2-hf2d2f32_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-pdf-3.9.2-h600f43f_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-pg-3.9.2-h5e77dd0_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-postgisraster-3.9.2-h5e77dd0_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-tiledb-3.9.2-h4a3bace_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-xls-3.9.2-h03c987c_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.28.0-h26d7fe4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.28.0-ha262f82_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hc670b87_16.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h15fa968_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.3-h0c1763c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.20.0-h0e7cc3e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-hf23e847_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py312hb3f7f12_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py312h0a2e395_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h669347b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/poppler-24.08.0-h47131b8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/postgresql-17.2-h1122569_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.4.1-h54d7996_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h01725c0_2_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py312h9211aeb_9.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.11-py312hd177ed6_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.6-py312h6cab151_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.14.1-hed91bc2_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.26.0-h86fa3b2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-h78fed68_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.3-ha02ee65_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.3-ha02ee65_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -1108,78 +1089,77 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda osx-arm64: - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -1188,268 +1168,244 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.7.30-h338687b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.7.4-h41dd001_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.9.28-hd74edd7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.2.19-h41dd001_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.4.3-hb2a355e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.8.9-hf5a2c8c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.14.18-hc3cb426_12.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.10.5-h9658b26_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.6.5-h663ac5c_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.1.19-h41dd001_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.1.18-h41dd001_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.28.2-h8f7a527_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.379-h67f4a54_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.13.0-hd01fc5c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.8.0-h13ea094_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.12.0-hfde595f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.7.0-hcf3b6fd_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.11.0-h082e32e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h5499902_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312h6b01ec3_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h6a3b0d2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.4.1-h793ed5c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py312h8eba7c0_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.9.1-hbf5303f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cyrus-sasl-2.1.28-ha1cbb27_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py312hf57c059_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py312hf3c922e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-11.0.2-h440487c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdal-3.9.2-py312h936c49d_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.12.2-h00cdb27_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.3-h7e5fb84_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_hec07895_105.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.17-he54c16a_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kealib-1.5.3-h8edbb62_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.7.7-h7c07d2a_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-17.0.0-h20538ec_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-17.0.0-hf9b8971_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-17.0.0-hf9b8971_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-17.0.0-hbf8b706_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.9.1-hfd8ffcc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-3.9.2-hce30654_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.9.2-hcf5b7dd_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-fits-3.9.2-h248c7bc_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-grib-3.9.2-h6d3d72d_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf4-3.9.2-h3847bb8_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf5-3.9.2-h2def128_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-jp2openjpeg-3.9.2-hd61e619_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-kea-3.9.2-h7b2de0b_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-netcdf-3.9.2-h5e0d008_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pdf-3.9.2-h587d690_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pg-3.9.2-h6a0b679_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-postgisraster-3.9.2-h6a0b679_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-tiledb-3.9.2-h27a95ea_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-xls-3.9.2-habc1c91_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.0-hdff4504_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.28.0-hfe08963_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.28.0-h1466eeb_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-hb71b141_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.3-h8088a28_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_he469be0_114.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libntlm-1.8-h5505292_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-17.0.0-hf0ba9ef_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpq-17.2-h9b1ab17_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-4.25.3-hc39d83c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-h31fb324_16.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf7a34df_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.3-h1b79a29_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.20.0-h64651cc_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.8.0-hc098a78_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.9-h4a9ca0c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxslt-1.1.43-h429d6fd_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzip-1.11.2-h1336266_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.3.3-py312h11d1bbd_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.9.4-hb7217d7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py312h3093aea_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.38-heaf21c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nss-3.118-h1c710a3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.10-hbe55e7a_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.2-h75dedd0_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-ha881caa_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/poppler-24.08.0-h37b219d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/postgresql-17.2-h393ceee_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.4.1-hfb94cee_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-17.0.0-py312hc40f475_2_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.10.0-py312hf9e36c7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.6.1-py312hc2894cb_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.3.11-py312h9302994_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.0.6-py312ha6b2466_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.14.1-h6d8af72_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tiledb-2.26.0-h3c94177_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tzcode-2026b-h1a92334_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.2.5-h92fc2f4_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.8.3-hd0f0c4f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.8.3-hd0f0c4f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.8.3-h8088a28_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda win-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -1458,255 +1414,235 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.7.30-h4ab18a5_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.7.4-hf1fc857_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.9.28-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.2.19-hf1fc857_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.4.3-hd0ca3c1_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.8.9-heca9ddf_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.14.18-h3831a8d_12.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.10.5-h8fec231_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.6.5-he24745f_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.1.19-hf1fc857_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.1.20-hf1fc857_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.28.2-h2ae5ca2_6.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.379-h9ac9443_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.13.0-haf5610f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.8.0-h148e6f0_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.12.0-hf03c1c4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.7.0-h148e6f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-h85f69ea_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py312hbb81ca0_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.6-hfd05255_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py312he06e257_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cfitsio-4.4.1-hc2ea260_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py312he06e257_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.0.2-hf4da5c8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.12.2-h5a68840_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.3-h232476a_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.14.2-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.3.2-h1da161f_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.7.1-h88938e3_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.11.0-h667fc28_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.27.3-hbdc738f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.16.0-hd7b40c3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.12.8-hd7ee1a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.2.7-h1da161f_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.2.10-h1da161f_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.40.1-he7c1723_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.16.3-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.13.3-hf9fdf8e_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.18.0-h4fb3251_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.3-nompi_h2b43c12_105.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kealib-1.5.3-h6c43f9b_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py312h78d62e6_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20240116.2-cxx17_he0c23c2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.5-haf901d7_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.7.7-h88ece9c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-17.0.0-h29daf90_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-17.0.0-he0c23c2_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-17.0.0-he0c23c2_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-17.0.0-h1f0e801_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h04e5de1_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-3.9.2-h57928b3_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.9.2-h6b59ad6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-fits-3.9.2-h0a0b71e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-grib-3.9.2-hd2a089b_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf4-3.9.2-h430f241_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf5-3.9.2-had131a1_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-jp2openjpeg-3.9.2-hed4c6cb_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-kea-3.9.2-h95b1a77_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-netcdf-3.9.2-h55e78d3_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-pdf-3.9.2-ha1c78db_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-pg-3.9.2-hfaa227e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-postgisraster-3.9.2-hfaa227e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-tiledb-3.9.2-hb8b5d01_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-xls-3.9.2-hd0e23a6_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.0-h7025463_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.28.0-h5e7cea3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-2.28.0-he5eb982_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.62.2-h5273850_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-devel-5.8.3-hfd05255_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_h92078aa_114.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-17.0.0-ha915800_13_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpq-17.2-h7ec079e_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-4.25.3-h47a098d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2023.09.01-hf8d8778_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h6c42fcb_16.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-hab0cb6d_9.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.3-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.20.0-hbe90ef8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.8.0-hb602f4b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.9-h741aa76_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.3.3-py312h0608a1d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py312h78d62e6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/poppler-24.08.0-h9415970_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/postgresql-17.2-heca7946_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.4.1-hd9569ee_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-17.0.0-py312h6a9c419_2_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.6.1-py312h6f27134_9.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.3.11-py312he4a2ebf_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2023.09.01-hd3b24a8_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.0.6-py312h3a88d77_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py312he06e257_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.14.1-h9f2357e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tiledb-2.26.0-h98a567f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py312he06e257_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36231-h84cd919_39.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.2.5-he0c23c2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xz-5.8.3-hb6c8415_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xz-tools-5.8.3-hfd05255_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda test: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ - - url: https://conda.anaconda.org/ddahawkins/label/dev/ packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda @@ -1725,217 +1661,215 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py314h97ea11e_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py314haf003cf_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py314hc02f841_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py314hbcf5174_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py314h97ea11e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_hc2c16d8_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.8-default_h08c5240_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.8-default_hd70ba2e_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-hd345f60_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h3a9caae_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-h174a0a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h2840a7c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-hf4e2dac_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py314hd4c109c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py314h815e797_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py314h9a9c090_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hb71707f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h97ea11e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314h2b28147_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py314h8ec4b1a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py314hdafbbf9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py314h969be7f_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py314hbcf5174_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py314h7ce3bca_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py314h3987850_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-h242f9ac_102_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py314h91cf270_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py314hf07bd8e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py314hbe3edd8_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h04a0ce9_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-h1964d1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-h78fed68_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda @@ -1943,9 +1877,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda @@ -1955,7 +1889,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda @@ -1963,8 +1896,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda @@ -1974,21 +1907,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda @@ -2000,12 +1933,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda @@ -2014,18 +1948,17 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda osx-arm64: - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda @@ -2033,9 +1966,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda @@ -2053,8 +1986,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda @@ -2064,16 +1997,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda @@ -2090,12 +2023,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda @@ -2104,7 +2038,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda @@ -2124,163 +2058,163 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h3daef5d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-hd20048c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py314hf8a3a22_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h2062a1b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h6fbacd7_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-h21e278e_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-h934fa54_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-hb71b141_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h8daa630_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-h1ae2325_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-hdb7fadc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314hdf3942c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314hb79c6fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-ha88f16d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py314h54f3292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314ha82c7e6_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-hf4d206d_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-h77b7338_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda win-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda @@ -2288,9 +2222,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda @@ -2308,8 +2242,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda @@ -2319,16 +2253,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda @@ -2346,12 +2280,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda @@ -2381,143 +2316,139 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314he701e3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/glib-2.88.3-h89924da_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/glib-tools-2.88.3-hf027272_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.3.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py314hf309875_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h0eb2380_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_he24518a_100.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h04e5de1_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-9_h8455456_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-9_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_h2778606_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.1.0-h110b43a_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.1.0-h8ee18e1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.3.0-h03b5201_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-devel-14.3.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-devel-0.22.5-h5728263_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-9_hf9ab0e9_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h0ffbb96_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_234.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314hf309875_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-hd2b5f0e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py314h9f07db2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.1-py314h447aaf0_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h53f6dd8_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.1-pl5321hfcac499_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda @@ -2526,28 +2457,30 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda packages: -- conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda - sha256: 71fd599b42cd1352bb8a7d278eef9b681b8de5efe210f358101ce47de5a4d699 - md5: 9a082a900139f69846e93aa11ab3597d +- conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda + sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 + md5: e9f9d8d88a22521378dace6b7399d59b depends: - black >=26.3.1,<27.0 - click >=8.2.0,<9.0 - go-shfmt >=3.13,<4.0 - - pathspec + - pathspec >=1.0.0 - python >=3.11,<4.0 license: MIT license_family: MIT - size: 47149 - timestamp: 1781674515403 + run_exports: + weak: + - snakefmt >=2.0.3,<2.1.0a0 + size: 47262 + timestamp: 1784295220199 - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda sha256: 1e8393a8ef060c62ef963fa9e0b3cc8f69b1dec4bdb51599c0dfcdeb9b8c7d12 md5: a5ec344abd4b0cdda5a52e870e4d25a3 @@ -2558,8 +2491,6 @@ packages: - python >=3.8 license: MIT license_family: MIT - purls: - - pkg:pypi/snakemake-interface-common?source=hash-mapping run_exports: weak: - snakemake-interface-common >=1.23.0,<2.0a0 @@ -2575,8 +2506,6 @@ packages: - throttler >=1.2.2,<2.0.0 license: MIT license_family: MIT - purls: - - pkg:pypi/snakemake-interface-executor-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-executor-plugins >=9.4.0,<10.0a0 @@ -2589,8 +2518,6 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.17.4,<2.0.0 license: MIT - purls: - - pkg:pypi/snakemake-interface-logger-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-logger-plugins >=2.1.0,<3.0a0 @@ -2603,8 +2530,6 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.16.0,<2.0.0 license: MIT - purls: - - pkg:pypi/snakemake-interface-report-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-report-plugins >=1.3.0,<2.0a0 @@ -2617,8 +2542,6 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.20.1,<2.0.0 license: MIT - purls: - - pkg:pypi/snakemake-interface-scheduler-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-scheduler-plugins >=2.0.2,<3.0a0 @@ -2636,16 +2559,14 @@ packages: - wrapt >=1.15.0,<2.0.0 license: MIT license_family: MIT - purls: - - pkg:pypi/snakemake-interface-storage-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-storage-plugins >=4.4.1,<5.0a0 size: 22783 timestamp: 1773699846635 -- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.23.1-pyhdfd78af_1.conda - sha256: 6d7a1d2e308506e867121edb6d258dbbb359ce3287631e54601e33edb110485d - md5: ff39f8e27137655553f9fbb842ee1aa2 +- conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda + sha256: 19f35e2089e02ade249857d470abb7141368b8eb4cc78d5f31f16926f39dad85 + md5: 356c74158836510a1a7d99a7497c30d7 depends: - conda-inject >=1.3.1,<2.0 - configargparse @@ -2672,7 +2593,7 @@ packages: - snakemake-interface-logger-plugins >=1.1.0,<3.0.0 - snakemake-interface-report-plugins >=1.2.0,<2.0.0 - snakemake-interface-scheduler-plugins >=2.0.0,<3.0.0 - - snakemake-interface-storage-plugins >=4.3.2,<5.0 + - snakemake-interface-storage-plugins >=4.4.1,<5.0 - sqlmodel >=0.0.37,<0.0.38 - tabulate - tenacity >=9.1.4,<10.0 @@ -2681,8 +2602,11 @@ packages: - yte >=1.5.5,<2.0 license: MIT license_family: MIT - size: 888303 - timestamp: 1781821379807 + run_exports: + weak: + - snakemake-minimal >=9.26.1,<10.0a0 + size: 889702 + timestamp: 1787898064588 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 @@ -2694,42 +2618,41 @@ packages: - openmp_impl <0.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: strong: - _openmp_mutex >=4.5 size: 28948 timestamp: 1770939786096 -- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda - sha256: cf93ca0f1f107e95a35969a4622684e08fcb8cf37f8cf4a1e9e424828386c921 - md5: 8904e09bda369377b3dd07e2ac828c5d +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda + sha256: a35bddac04be093769e81814465a537961c6ed0f8d3cc23d6dce6ecdfaf71821 + md5: 7094e0d8d14de0eff6d83f0d2f1f661e depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: LGPL-2.1-or-later license_family: LGPL - purls: [] run_exports: weak: - alsa-lib >=1.2.16.1,<1.3.0a0 - size: 592377 - timestamp: 1781521980743 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.5.0-py310hd8a072f_1.conda + size: 594986 + timestamp: 1787763412911 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda noarch: python - sha256: cf1cf3d0fa59fe0ab6bc3af722d820c1a85a9233c786f614f377c651fec6a7f9 - md5: 6adea4814147f458d6278d053850b0ac + sha256: 25c15e85cb03a1c4d43d02d0ff37c8c9a970c3e84abeaf420fc190bb053bbe3f + md5: e46fc69f093a840e98b9e2c29727862d depends: - python >=3.10 - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=14 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT license_family: MIT - size: 1125371 - timestamp: 1780396651124 + run_exports: {} + size: 1116952 + timestamp: 1786328964477 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -2741,7 +2664,6 @@ packages: - libglib >=2.68.1,<3.0a0 license: LGPL-2.1-or-later license_family: LGPL - purls: [] run_exports: weak: - at-spi2-atk >=2.38.0,<3.0a0 @@ -2759,7 +2681,6 @@ packages: - xorg-libxtst license: LGPL-2.1-or-later license_family: LGPL - purls: [] run_exports: weak: - at-spi2-core >=2.40.3,<2.41.0a0 @@ -2776,7 +2697,6 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - atk-1.0 >=2.38.0 @@ -2795,47 +2715,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 size: 135073 timestamp: 1784086842394 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.30-hec5e740_0.conda - sha256: 5735b8ae76580e81662137c4dafca62cd9da8083b5b7bebe8ea7e9a806f1053f - md5: bc1b9f70ea7fa533aefa6a8b6fbe8da7 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-auth >=0.7.30,<0.7.31.0a0 - size: 107190 - timestamp: 1726208110918 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hfd43aa1_1.conda - sha256: 8c8100499b7fced0c6a5eea156e85994d3bb0702b30eecedd949d555ca11f6a8 - md5: f301eb944d297fc879c441fffe461d8a - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - libgcc >=13 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - size: 47532 - timestamp: 1725829965837 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda sha256: a67c944be1728f576c02fe8f28b0cbb78b5353415075db3da4ef71bc9dd8c00c md5: 9c6072e7b882d35ac956c07e493d6a9e @@ -2846,7 +2730,6 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -2860,41 +2743,11 @@ packages: - libgcc >=14 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 size: 246263 timestamp: 1783637234072 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda - sha256: febe894ae2f5bfc4d65c51bd058433e9061d994ff06b30d5eca18919639c5083 - md5: 1b53af320b24547ce0fb8196d2604542 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-common >=0.9.28,<0.9.29.0a0 - size: 236451 - timestamp: 1725670076853 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.19-h756ea98_1.conda - sha256: 0e7fd40a9f8aa235e78202af75a421a7f6ea589e30c5cbe1787ceaccf36a3ce9 - md5: 5e08c385a1b8a79b52012b74653bbb99 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-compression >=0.2.19,<0.2.20.0a0 - size: 19116 - timestamp: 1725829968483 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda sha256: 8e0a5513cfc42df8bd16adbd8e83a37d3ca89b8e04cb20eb04eb177bbbfea365 md5: c9be3f5854f349ae77a1a174b59e11a8 @@ -2904,30 +2757,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 size: 22301 timestamp: 1784042853709 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h235a6dd_1.conda - sha256: 987b3654e7cbb8ead0227c2442a02b6c379d21bb1509a834c423d492a4862706 - md5: c05358e3a231195f7f0b3f592078bb0c - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - - libgcc >=13 - - libstdcxx >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - size: 53989 - timestamp: 1725856758424 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda sha256: b1f35f7b7a5e745e2d56cf93212770bb56f5207fa9f0e38f98b0c05a1b898a90 md5: 204d1e8d60c3ae839bd1898e4c7742c8 @@ -2940,7 +2774,6 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -2958,47 +2791,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 size: 231294 timestamp: 1784075685646 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.9-h5e77a74_0.conda - sha256: 9eac64d76ba4a799689392eb03bbbc93fd169ccbcb7719bcccbe2e9100ff2075 - md5: d7714013c40363f45850a25113e2cb05 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-compression >=0.2.19,<0.2.20.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-http >=0.8.9,<0.8.10.0a0 - size: 197695 - timestamp: 1726016818491 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.18-h2af50b2_12.conda - sha256: ca10865b8e5d16ea9f9ebc14833ef49bc30eed194233539794db887def925390 - md5: 700f1883f5a0a28c30fd98c43d4d946f - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - libgcc >=13 - - s2n >=1.5.5,<1.5.6.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-io >=0.14.18,<0.14.19.0a0 - size: 158169 - timestamp: 1728562824182 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda sha256: 94c32ca672ce290e250aea1cfb92582cca4658bdc3ec7cb1ceef254f34451595 md5: bd19b685b88557830f1a617a6d404eb2 @@ -3010,29 +2807,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 size: 183100 timestamp: 1784054829913 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.5-h0009854_0.conda - sha256: 2345d19078aa7c40aaa6b95a7d0e019ddbf3518ff47dc09fadd69f4e1a7073a5 - md5: d393d0a6c9b993771fbc67a998fccf6c - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - size: 194112 - timestamp: 1726205708559 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda sha256: 0252f072e2f493f8348575938c69b48b2799f29f0489c4ad2c5a919ab7e3d02e md5: 9728195c2f600ef427a1964ee193e707 @@ -3044,7 +2823,6 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -3065,48 +2843,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 size: 156174 timestamp: 1784100985258 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.5-hbaf354b_4.conda - sha256: 03d8bd937ed49c857255483fa5d252064121adfbccab46aa0b3716de75022747 - md5: 2cefeb144de7712995d1b52cc6a3864c - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - - libgcc >=13 - - openssl >=3.3.2,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - size: 112846 - timestamp: 1726237070601 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.19-h756ea98_3.conda - sha256: 4e6f79f3fee5ebb4fb12b6258d91315ed0f7a2ac16c75611cffdbaa0d54badb2 - md5: bfe6623096906d2502c78ccdbfc3bc7a - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - size: 55799 - timestamp: 1725836731034 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda sha256: e419e179e04021fc680d98af23fac4b4c2369cea61171087e57a734e968dd9bd md5: 816f62fa82532118ebb2398382090945 @@ -3116,27 +2857,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 size: 68515 timestamp: 1784044260935 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.18-h756ea98_11.conda - sha256: c343bc670bdb52248fc039cbd1cba20fe1d18af81960ab43153d9b55dfb08bc1 - md5: eadcc12bedac44f13223a2909c0e5bcc - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - libgcc >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-checksums >=0.1.18,<0.1.19.0a0 - size: 49962 - timestamp: 1725836852149 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda sha256: 6cc8617854a43040291dea791fe111ba408e7a5bbd9ee9e5a99375da7a16846b md5: 24ee781effc5779206a80139323c9caf @@ -3146,36 +2871,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 size: 101904 timestamp: 1784044248506 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.28.2-h6c0439f_6.conda - sha256: 02b1494f6be3d8e5f40afea1ff2c24cb7f0b42d5d18761a99b43f0faa7c39d29 - md5: 4e472c316d08af60faeb71f86d7563e1 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - - libgcc >=13 - - libstdcxx >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - size: 350331 - timestamp: 1726483498115 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda sha256: e0c3a119c7035a00e0da325187ee723e07e0e6337e50362349d178ab40961f97 md5: 3315ce09371baf6d70248b8927aa9866 @@ -3194,34 +2894,11 @@ packages: - aws-c-http >=0.11.0,<0.11.1.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 size: 416399 timestamp: 1784113232967 -- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.379-h5a9005d_9.conda - sha256: cc2227d97f5e7aed68aeb274a2bec0236af5c20519bde200c8ea7cba114ec978 - md5: 5dc18b385893b7991a3bbeb135ad7c3e - depends: - - __glibc >=2.17,<3.0.a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgcc >=13 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - size: 2934257 - timestamp: 1725944617781 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda sha256: 0c15c036c3b71471eecc58bbe08a68c66fd6a051b548dca7675c7c924ed3972b md5: c65efa87d532339a090c87093097bf09 @@ -3236,29 +2913,11 @@ packages: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 size: 3394321 timestamp: 1784137257627 -- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.13.0-h935415a_0.conda - sha256: b7e0a22295db2e1955f89c69cefc32810309b3af66df986d9fb75d89f98a80f7 - md5: debd1677c2fea41eb2233a260f48a298 - depends: - - __glibc >=2.17,<3.0.a0 - - libcurl >=8.8.0,<9.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - size: 338134 - timestamp: 1720853194547 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda sha256: fffc66e9be8806a92b314e27129c42b1298ea7065028c9e5d175dacb07829261 md5: 0cabc152dc8896c3a4c4aebf2b308627 @@ -3270,7 +2929,6 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -3287,46 +2945,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 size: 250737 timestamp: 1781268163278 -- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.8.0-hd126650_2.conda - sha256: f85452eca3ae0e156b1d1a321a1a9f4f58d44ff45236c0d8602ab96aaad3c6ba - md5: 36df3cf05459de5d0a41c77c4329634b - depends: - - __glibc >=2.17,<3.0.a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - size: 199516 - timestamp: 1721777604325 -- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.12.0-hd2e3451_0.conda - sha256: 69a0f5c2a08a1a40524b343060debb8d92295e2cc5805c3db56dad7a41246a93 - md5: 61f1c193452f0daa582f39634627ea33 - depends: - - __glibc >=2.17,<3.0.a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - size: 523120 - timestamp: 1721865032339 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda sha256: 04bb27dbf1d426b4447b28c3dc92ec01c807fa784eea86ffa1f8c2bd9b9e8076 md5: 43151a3b0a8e037747d79a82dff9f967 @@ -3338,7 +2961,6 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -3357,48 +2979,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 size: 159394 timestamp: 1781268171097 -- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.7.0-h10ac4d7_1.conda - sha256: 1030fa54497a73eb78c509d451f25701e2e781dc182e7647f55719f1e1f9bee8 - md5: ab6d507ad16dbe2157920451d662e4a1 - depends: - - __glibc >=2.17,<3.0.a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - libxml2 >=2.12.7,<2.14.0a0 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - size: 143039 - timestamp: 1721832724803 -- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.11.0-h325d260_1.conda - sha256: 1726fa324bb402e52d63227d6cb3f849957cd6841f8cb8aed58bb0c81203befb - md5: 11d926d1f4a75a1b03d1c053ca20424b - depends: - - __glibc >=2.17,<3.0.a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 - size: 274492 - timestamp: 1721925100762 - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda sha256: 13e2e6eb942f65bf81e9089bf6b5926534d9245c781288c5270beb3a25c9156b md5: 70972c9cb0893d6499dd4118415a966b @@ -3411,12 +2996,24 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 size: 308699 timestamp: 1781538835962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda + sha256: c10df0467f534472f0aa39013850d6dd9dd38ea5a6fb5c0812afb6f3fc768924 + md5: e7eb25765bdf21397cc6e30828871625 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause AND MIT AND EPL-2.0 + run_exports: {} + size: 240967 + timestamp: 1786861419155 - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d md5: 2c2fae981fd2afd00812c92ac47d023d @@ -3430,132 +3027,74 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 size: 48427 timestamp: 1733513201413 -- conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda - sha256: 6cc260f9c6d32c5e728a2099a52fdd7ee69a782fff7b400d0606fcd32e0f5fd1 - md5: 54fe76ab3d0189acaef95156874db7f9 - depends: - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.0,<1.3.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - blosc >=1.21.6,<2.0a0 - size: 48842 - timestamp: 1719266029046 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda - sha256: 294526a54fa13635341729f250d0b1cf8f82cad1e6b83130304cbf3b6d8b74cc - md5: eaf3fbd2aa97c212336de38a51fe404e - depends: - - __glibc >=2.17,<3.0.a0 - - brotli-bin 1.1.0 hb03c661_4 - - libbrotlidec 1.1.0 hb03c661_4 - - libbrotlienc 1.1.0 hb03c661_4 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 19883 - timestamp: 1756599394934 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda - sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 - md5: 8ccf913aaba749a5496c17629d859ed1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda + sha256: 1f2ccfebe6e0113bfc473b21906372c1ee4eb69bf6faef0ad7f3d4d79af63a98 + md5: 6ff307b78fbfb7dcafb7e25ff8bc9c10 depends: - __glibc >=2.17,<3.0.a0 - - brotli-bin 1.2.0 hb03c661_1 - - libbrotlidec 1.2.0 hb03c661_1 - - libbrotlienc 1.2.0 hb03c661_1 - - libgcc >=14 + - brotli-bin 1.2.0 h9908984_3 + - libbrotlidec 1.2.0 ha411449_3 + - libbrotlienc 1.2.0 h018ffa1_3 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20103 - timestamp: 1764017231353 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda - sha256: 444903c6e5c553175721a16b7c7de590ef754a15c28c99afbc8a963b35269517 - md5: ca4ed8015764937c81b830f7f5b68543 - depends: - - __glibc >=2.17,<3.0.a0 - - libbrotlidec 1.1.0 hb03c661_4 - - libbrotlienc 1.1.0 hb03c661_4 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 19615 - timestamp: 1756599385418 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda - sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 - md5: af39b9a8711d4a8d437b52c1d78eb6a1 + size: 20676 + timestamp: 1786622810792 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda + sha256: 56b2e5e8a49f9887af74cc63e0d55a3654e60b667ad5558da089549a36ae6a0c + md5: 8929291d9efc59715df1cfa7daf5e3ed depends: - __glibc >=2.17,<3.0.a0 - - libbrotlidec 1.2.0 hb03c661_1 - - libbrotlienc 1.2.0 hb03c661_1 - - libgcc >=14 + - libbrotlidec 1.2.0 ha411449_3 + - libbrotlienc 1.2.0 h018ffa1_3 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 21021 - timestamp: 1764017221344 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda - sha256: 52a9ac412512b418ecdb364ba21c0f3dc96f0abbdb356b3cfbb980020b663d9b - md5: fd0e7746ed0676f008daacb706ce69e4 + size: 21598 + timestamp: 1786622801722 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda + sha256: 32ae6e002843704af9f39395f3116815fa66f2b27de1bd9044fb2a2d53fbe3d3 + md5: d176f3ed2824f930b524c45eb8f158bb depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - - libbrotlicommon 1.1.0 hb03c661_4 + - libbrotlicommon 1.2.0 h39a168f_3 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 354149 - timestamp: 1756599553574 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda - sha256: 3ad3500bff54a781c29f16ce1b288b36606e2189d0b0ef2f67036554f47f12b0 - md5: 8910d2c46f7e7b519129f486e0fe927a + size: 367032 + timestamp: 1786622975850 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_3.conda + sha256: e52ff7e1e3c5f4423421fbcd1f1ebf1d6ce123e22890ceb225d6552b7bbc551f + md5: bd1be0851060138e038f6f4e09cc1eb4 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 constrains: - - libbrotlicommon 1.2.0 hb03c661_1 + - libbrotlicommon 1.2.0 h39a168f_3 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 367376 - timestamp: 1764017265553 + size: 367948 + timestamp: 1786622843866 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 md5: e675fabcf81499adc7edf58124fb1e01 @@ -3564,163 +3103,68 @@ packages: - libgcc >=14 license: bzip2-1.0.6 license_family: BSD - purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 size: 257808 timestamp: 1785906269155 -- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 - md5: d2ffd7602c02f2b316fd921d39876885 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: bzip2-1.0.6 - license_family: BSD - purls: [] - run_exports: - weak: - - bzip2 >=1.0.8,<2.0a0 - size: 260182 - timestamp: 1771350215188 -- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - sha256: cc9accf72fa028d31c2a038460787751127317dcfa991f8d1f1babf216bb454e - md5: 920bb03579f15389b9e512095ad995b7 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - c-ares >=1.34.6,<2.0a0 - size: 207882 - timestamp: 1765214722852 -- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda - sha256: 5139b6afbfaca91c47104ba9a6a40f81211e1c9200e96b73ce3a56f0ca1902f4 - md5: 2cef891b791040aab83e218c7d137679 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + sha256: 9c37cc233238adf366ea75b0e845662a5be07ceadf62e458183516369340274b + md5: 3ad2b403be8fc5612b9159e2ce621f69 depends: + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - constrains: - - c-ares-static <0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 - size: 226755 - timestamp: 1786116641939 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda - sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 - md5: 09262e66b19567aff4f592fb53b28760 + size: 228700 + timestamp: 1787169971173 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda + sha256: 8395bb43e188b76be0842b8ecd440e79a8d202d320bfee009e31b6623390b40d + md5: e83331a940393006789fedddc3b492f3 depends: - __glibc >=2.17,<3.0.a0 - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - icu >=75.1,<76.0a0 - - libexpat >=2.6.4,<3.0a0 - - libgcc >=13 - - libglib >=2.82.2,<3.0a0 - - libpng >=1.6.47,<1.7.0a0 - - libstdcxx >=13 - - libxcb >=1.17.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - pixman >=0.44.2,<1.0a0 - - xorg-libice >=1.1.2,<2.0a0 - - xorg-libsm >=1.2.5,<2.0a0 - - xorg-libx11 >=1.8.11,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 - - xorg-libxrender >=0.9.12,<0.10.0a0 - license: LGPL-2.1-only or MPL-1.1 - purls: [] - run_exports: - weak: - - cairo >=1.18.4,<2.0a0 - size: 978114 - timestamp: 1741554591855 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - sha256: 06525fa0c4e4f56e771a3b986d0fdf0f0fc5a3270830ee47e127a5105bde1b9a - md5: bb6c4808bfa69d6f7f6b07e5846ced37 - depends: - - __glibc >=2.17,<3.0.a0 - - fontconfig >=2.15.0,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - - icu >=78.1,<79.0a0 - - libexpat >=2.7.3,<3.0a0 - - libfreetype >=2.14.1 - - libfreetype6 >=2.14.1 - - libgcc >=14 - - libglib >=2.86.3,<3.0a0 - - libpng >=1.6.53,<1.7.0a0 - - libstdcxx >=14 + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=15 - libxcb >=1.17.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 - pixman >=0.46.4,<1.0a0 - xorg-libice >=1.1.2,<2.0a0 - xorg-libsm >=1.2.6,<2.0a0 - - xorg-libx11 >=1.8.12,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.1-only or MPL-1.1 - purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 989514 - timestamp: 1766415934926 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda - sha256: 7dafe8173d5f94e46cf9cd597cc8ff476a8357fbbd4433a8b5697b2864845d9c - md5: 648ee28dcd4e07a1940a17da62eccd40 - depends: - - __glibc >=2.17,<3.0.a0 - - libffi >=3.5.2,<3.6.0a0 - - libgcc >=14 - - pycparser - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cffi?source=hash-mapping - run_exports: {} - size: 295716 - timestamp: 1761202958833 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314h4a8dc5f_1.conda - sha256: c6339858a0aaf5d939e00d345c98b99e4558f285942b27232ac098ad17ac7f8e - md5: cf45f4278afd6f4e6d03eda0f435d527 + size: 989707 + timestamp: 1787926031792 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h8d76f0c_2.conda + sha256: 9d181949eead0d4092ed9ffa3b7ec066a15572d515327939c8a074c224262528 + md5: 314853abf64fc052dea08bd17df17b65 depends: - __glibc >=2.17,<3.0.a0 - - libffi >=3.5.2,<3.6.0a0 - - libgcc >=14 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 - pycparser - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 300271 - timestamp: 1761203085220 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.4.1-hf8ad068_0.conda - sha256: 74ed4d8b327fa775d9c87e476a7221b74fb913aadcef207622596a99683c8faf - md5: 1b7a01fd02d11efe0eb5a676842a7b7d - depends: - - bzip2 >=1.0.8,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libgcc-ng >=12 - - libgfortran-ng - - libgfortran5 >=12.3.0 - - libzlib >=1.3.1,<2.0a0 - license: LicenseRef-fitsio - purls: [] - run_exports: - weak: - - cfitsio >=4.4.1,<4.4.2.0a0 - size: 924198 - timestamp: 1718906379286 + run_exports: {} + size: 306626 + timestamp: 1786775112485 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda sha256: 200da7fefacb1a196dd7b4b6f45106cebe017042b1491e2b27c7cc833beed8ea md5: 5f68e67b2b9463501260e731e2999dec @@ -3744,7 +3188,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -3772,7 +3215,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -3799,7 +3241,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -3825,7 +3266,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -3848,55 +3288,53 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 size: 664399 timestamp: 1778500281099 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.5.3-py314hdafbbf9_0.conda - sha256: 327c64a25ab1719a51b114dbae83318c9943ed5936882736ba83c8107b43c9b8 - md5: 417bbce4160506ed31e8bb294c0ab97c +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.1-py314h9e666f3_0.conda + sha256: 3651193a900fbb7221573de2e80bca0a8608e3fb1f8ba96523d81212dc4cafd0 + md5: 03ef3d814cc36f7feb7ab83e17992aa0 depends: - archspec >=0.2.3 - boltons >=23.0.0 - charset-normalizer - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - conda-package-handling >=2.2.0 - - conda-pypi >=0.9.0 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - distro >=1.5.0 - frozendict >=2.4.2 - - jsonpatch >=1.32 - menuinst >=2 + - msgpack-python >=1.1.1 - packaging >=23.0 - platformdirs >=3.10.0 - pluggy >=1.6.0 - pycosat >=0.6.3 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python - requests >=2.28.0,<3 - ruamel.yaml >=0.11.14,<0.19 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 - - zstandard >=0.19.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python_abi 3.14.* *_cp314 constrains: - - conda-env >=2.6 - - conda-content-trust >=0.3.0 - conda-build >=25.9 + - conda-content-trust >=0.3.0 + - conda-env >=2.6 license: BSD-3-Clause license_family: BSD - size: 1357035 - timestamp: 1781680868886 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.10.1-py314h9e666f3_2.conda - sha256: 989d9ab43bf25e1acff1b7519c96ac156ccdf46a2df605aff8a60ad85dbd6356 - md5: 4689abef13301a7556f0c3f6d3addee7 + run_exports: {} + size: 1465310 + timestamp: 1787306252426 +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda + sha256: f864a806c98c44a0cdc4ede1b42fa6cdcb9d327d8d0013f51af9344f553d0cbf + md5: d8cc5cf325134981f65dd72c8e082433 depends: - python - - conda >=26.1.0 - pip >=23.0.1 - packaging - unearth @@ -3906,10 +3344,13 @@ packages: - conda-index >=0.11.0 - conda-package-streaming >=0.11 - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 license: MIT license_family: MIT - size: 318028 - timestamp: 1781646467890 + run_exports: {} + size: 323834 + timestamp: 1784203010834 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 @@ -3922,38 +3363,20 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 320386 timestamp: 1769155979897 -- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py314h97ea11e_4.conda - sha256: b0314a7f1fb4a294b1a8bcf5481d4a8d9412a9fee23b7e3f93fb10e4d504f2cc - md5: 95bede9cdb7a30a4b611223d52a01aa4 - depends: - - numpy >=1.25 - - python - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping - run_exports: {} - size: 324013 - timestamp: 1769155968691 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_0.conda - sha256: 0d9405d9f2de5d4b15d746609d87807aac10e269072d6408b769159762ed113d - md5: d17488e343e4c5c0bd0db18b3934d517 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda + sha256: 6a905f317b93104444dc74875933a7cdf65fa14ae05dd6485ece6ffd9d0b5e89 + md5: 451367db888b29ad0db6b298669fbe77 depends: - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: CC0-1.0 - size: 24283 - timestamp: 1756734785482 + run_exports: {} + size: 24388 + timestamp: 1785920465106 - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda sha256: e3127e539763a0e16fb7977fac8fc42e5fe6df0517757f56283df1b7567ac6bd md5: 6f283e8bb11a748bc30af46258683ba8 @@ -3968,66 +3391,27 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1817713 timestamp: 1763019879546 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py314haf003cf_2.conda - sha256: c0fa696e7120421450d7b35baeb94b905bf0e48b90a0f6287265ee4eeb5ba18e - md5: 24b0934613e9e0404436ee469629cb74 - depends: - - python - - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - python_abi 3.14.* *_cp314 - constrains: - - __glibc >=2.17 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping - run_exports: {} - size: 1817375 - timestamp: 1763019875032 -- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - sha256: 8f14ff477bf2cdf3cd4ff0ab2486b3e6490350da650e67be6c6a0b8a18994a90 - md5: a91f709770cddb5e7a027ab000c441a5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda + sha256: 0ba202eb4cbf3909d196e919f12e229fbd745e44462e7e3004569d20a26cf02a + md5: 82c2a5fc14064073501f5e1b817f4d80 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 heca4667_4 - - libgcc >=14 - - libpsl >=0.23.0,<0.24.0a0 + - libcurl 8.21.0 ha042cf0_5 + - libgcc >=15 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT - purls: [] - run_exports: {} - size: 195067 - timestamp: 1785500114646 -- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda - sha256: 13d843b27791294f7e0f01a49de3a1fbc873bb8f193f93a7cd86ca012bfce172 - md5: 2e7dedf73dfbfcee662e2a0f6175e4bb - depends: - - __glibc >=2.17,<3.0.a0 - - krb5 >=1.21.3,<1.22.0a0 - - libcurl 8.9.1 hdb1bdb2_0 - - libgcc-ng >=12 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: curl - license_family: MIT - purls: [] run_exports: {} - size: 169240 - timestamp: 1722439933267 + size: 194983 + timestamp: 1787183729323 - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 md5: af491aae930edc096b58466c51c4126c @@ -4041,31 +3425,11 @@ packages: - openssl >=3.5.5,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD - purls: [] run_exports: weak: - cyrus-sasl >=2.1.28,<3.0a0 size: 210103 timestamp: 1771943128249 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda - sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f - md5: cae723309a49399d2949362f4ab5c9e4 - depends: - - __glibc >=2.17,<3.0.a0 - - krb5 >=1.21.3,<1.22.0a0 - - libgcc >=13 - - libntlm >=1.8,<2.0a0 - - libstdcxx >=13 - - libxcrypt >=4.4.36 - - openssl >=3.5.0,<4.0a0 - license: BSD-3-Clause-Attribution - license_family: BSD - purls: [] - run_exports: - weak: - - cyrus-sasl >=2.1.28,<3.0a0 - size: 209774 - timestamp: 1750239039316 - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda sha256: 75b3d3c9497cded41e029b7a0ce4cc157334bbc864d6701221b59bb76af4396d md5: 29fd0bdf551881ab3d2801f7deaba528 @@ -4077,42 +3441,9 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 623770 timestamp: 1771855837505 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py314h5bd0f2a_2.conda - sha256: 5edb3c0fbf5c39b66f71fef0264d4fbab561f6adbdd7ef88188b725a82fcd6da - md5: a6a32cab83d59c7812ddbb03220057e3 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - toolz >=0.10.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping - run_exports: {} - size: 641304 - timestamp: 1771855845410 -- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 - sha256: 8f5f995699a2d9dbdd62c61385bfeeb57c82a681a7c8c5313c395aa0ccab68a5 - md5: ecfff944ba3960ecb334b9a2663d708d - depends: - - expat >=2.4.2,<3.0a0 - - libgcc-ng >=9.4.0 - - libglib >=2.70.2,<3.0a0 - license: GPL-2.0-or-later - license_family: GPL - purls: [] - run_exports: - weak: - - dbus >=1.13.6,<2.0a0 - size: 618596 - timestamp: 1640112124844 - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 md5: ce96f2f470d39bd96ce03945af92e280 @@ -4124,7 +3455,6 @@ packages: - libglib >=2.86.2,<3.0a0 - libexpat >=2.7.3,<3.0a0 license: AFL-2.1 OR GPL-2.0-or-later - purls: [] run_exports: weak: - dbus >=1.16.2,<2.0a0 @@ -4141,26 +3471,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 2842115 timestamp: 1780390153580 -- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda - sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 - md5: bfd56492d8346d669010eccafe0ba058 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - double-conversion >=3.3.1,<3.4.0a0 - size: 69544 - timestamp: 1739569648873 - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc md5: dbe3ec0f120af456b3477743ffd99b74 @@ -4170,7 +3483,6 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - double-conversion >=3.4.0,<3.5.0a0 @@ -4196,27 +3508,11 @@ packages: - xorg-libxxf86vm >=1.1.6,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - epoxy >=1.5.10,<1.6.0a0 size: 411735 timestamp: 1758743520805 -- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda - sha256: bc1e8177a4ebbbfcda98b6f4a1575f3337e69f0d2f1025a84570533ca27b89eb - md5: e211a78613b9d50a096644b97cede8f7 - depends: - - __glibc >=2.17,<3.0.a0 - - libexpat 2.8.1 hecca717_1 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libexpat >=2.8.1,<3.0a0 - size: 147797 - timestamp: 1781203608158 - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda sha256: 319d05cd6d63c0b1fe5d155c42e1f97c2d1c50f35269e321cf627f9055638904 md5: 5d169c5bc70f98251c4802caf26cad53 @@ -4232,59 +3528,12 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 570988 timestamp: 1778864779103 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py314hc02f841_0.conda - sha256: 344f92cb3e2e5e9d4d98219556b2987cbd6cead93e8c80be4fc824ba63e1911d - md5: 7eacd1ab0881bd50ebf2824ae227098d - depends: - - __glibc >=2.17,<3.0.a0 - - cramjam >=2.3 - - fsspec - - libgcc >=14 - - numpy >=1.23,<3 - - packaging - - pandas >=1.5.0 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping - run_exports: {} - size: 581290 - timestamp: 1778864786750 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda - sha256: 36f7f5d852a28c2a18d1cd31c02bb3ec0bfdb6b3ee1c3a0118827110e5457d18 - md5: 4a30f4277a1894928a7057d0e14c1c95 - depends: - - __glibc >=2.17,<3.0.a0 - - attrs >=19.2.0 - - click >=8.0,<9.dev0 - - click-plugins >=1.0 - - cligj >=0.5 - - gdal - - libgcc >=13 - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - libstdcxx >=13 - - pyparsing - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - shapely - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping - run_exports: {} - size: 1171712 - timestamp: 1726664552119 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py314hbcf5174_6.conda - sha256: 6a25e057b4fc912ea56accdfd8b083653861c2b61fb875584385c9c27223334b - md5: 9973d51b9bb91ae8cca8cfff05d9106b +- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda + sha256: 801274da37e28d0cff6d091b679d34b10df718420a97d8fe577a968805f396d3 + md5: a68cae58a81a937a6edcb3e4e6f0bbe7 depends: - __glibc >=2.17,<3.0.a0 - attrs >=19.2.0 @@ -4295,82 +3544,47 @@ packages: - libgdal-core >=3.12.1,<3.13.0a0 - libstdcxx >=14 - pyparsing - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - shapely license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping run_exports: {} - size: 1203455 - timestamp: 1767051530201 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-11.0.2-h07f6e7f_1.conda - sha256: f9eeb49752b250e035ed0ee957fd813f7d2212eccb814006bbf92b89711605b1 - md5: fbbfeaa24a99c3da00c35334935b0d24 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=11.0.2,<11.1.0a0 - size: 197559 - timestamp: 1743030768885 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-hff5e90c_0.conda - sha256: d4e92ba7a7b4965341dc0fca57ec72d01d111b53c12d11396473115585a9ead6 - md5: f7d7a4104082b39e3b3473fbd4a38229 + size: 1203804 + timestamp: 1767051329805 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + sha256: 25d14a197601fdd616f2e501431b52887e69b31e7defbc1679381d718357ceb7 + md5: a70bb6d42ad121aef456e0007e92bed6 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: MIT license_family: MIT - size: 198107 - timestamp: 1767681153946 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda - sha256: 2e50bdcebdf70a865b81f2456bbc586386451ec601c60f2b6cd22b8c40a2d384 - md5: e0e050cfa9fa85fe39632ab11cb7f3e0 - depends: - - __glibc >=2.17,<3.0.a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libgcc >=14 - - libuuid >=2.42.1,<3.0a0 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - fontconfig >=2.18.1,<3.0a0 - - fonts-conda-ecosystem - size: 281880 - timestamp: 1780450077431 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - sha256: 05b57e2f5aaf3adcccc80653c9795d29974f24dcf9daa40c98b254c99b726227 - md5: bc8843ae1a655600014bb7a09d7b8b89 + - fmt >=12.1.0,<12.2.0a0 + size: 199072 + timestamp: 1785915412102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + sha256: 5a3eb10b18a97223ab06b3a7f0d7f56658db2f2800e2f2af95836fe3bf55ba63 + md5: 922776b528a470ab5afa81fd42abfa1d depends: - __glibc >=2.17,<3.0.a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - - libgcc >=14 + - libgcc >=15 - libuuid >=2.42.2,<3.0a0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - size: 296480 - timestamp: 1786381145956 + size: 296288 + timestamp: 1786667377340 - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda sha256: d235ae7075642044ceb3d922ef2a710a82665755ac9bbb7e8dad7daa72bc6d87 md5: 294fb524171e2a2748cb7fe708aba826 @@ -4384,65 +3598,37 @@ packages: - unicodedata2 >=15.1.0 license: MIT license_family: MIT - purls: - - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 3007892 timestamp: 1778770568019 -- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda - sha256: c934c385889c7836f034039b43b05ccfa98f53c900db03d8411189892ced090b - md5: 8462b5322567212beeb025f3519fb3e2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + sha256: 612a8e0c1a6ecae23da54d81ef395f6ebb5c8e4468ec2611593ebce75876a4e0 + md5: 2d0ea23b23603e07ca47f23f949f67b6 depends: - - libfreetype 2.14.3 ha770c72_0 - - libfreetype6 2.14.3 h73754d4_0 - license: GPL-2.0-only OR FTL - purls: [] - run_exports: - weak: - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - size: 173839 - timestamp: 1774298173462 -- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - sha256: d109354d4584aa1ef6e915a8f02cbe13a9708f384aa167c075953b6f8196111c - md5: 8dd74ae1edf2b6490af0e960be773431 - depends: - - libfreetype 2.14.3 ha770c72_1 - - libfreetype6 2.14.3 h73754d4_1 + - libfreetype 2.14.3 ha770c72_2 + - libfreetype6 2.14.3 h5e6c136_2 license: GPL-2.0-only OR FTL - purls: [] run_exports: weak: - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - size: 174748 - timestamp: 1785641176027 -- conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda - sha256: c8960e00a6db69b85c16c693ce05484facf20f1a80430552145f652a880e0d2a - md5: ecb5d11305b8ba1801543002e69d2f2f + size: 175239 + timestamp: 1786641011029 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda + sha256: f154843c5ac16d6cbe6297292c82ed65462aca9749f2f8722b166f47165fbcff + md5: 66ee86d087145e4d91d8a75437692238 depends: - __glibc >=2.17,<3.0.a0 - - libexpat >=2.6.4,<3.0a0 - - libgcc >=13 - - libiconv >=1.17,<2.0a0 - - minizip >=4.0.7,<5.0a0 + - libexpat >=2.8.1,<3.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - minizip >=4.2.2,<5.0a0 license: MPL-1.1 - license_family: MOZILLA - purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 - size: 59299 - timestamp: 1734014884486 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda - sha256: 858283ff33d4c033f4971bf440cebff217d5552a5222ba994c49be990dacd40d - md5: f9f81ea472684d75b9dd8d0b328cf655 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: LGPL-2.1-or-later - size: 61244 - timestamp: 1757438574066 + size: 61858 + timestamp: 1788125490606 - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda sha256: 4846a3ca0402f3fe33ad84ed50ab213c6aafde4a0faef3c5002f6bf753e21671 md5: 1cd10eda5692519d01bb20e086e214c9 @@ -4450,110 +3636,42 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 size: 61782 timestamp: 1785912528684 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gdal-3.9.2-py312h1299960_7.conda - sha256: 656eea557c38f713cb6d94efa798ecae9b70b807976d2e99fd7080462575098d - md5: 9cf27e3f9d97ea13f250db9253a25dc8 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - - libxml2 >=2.12.7,<2.14.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/gdal?source=hash-mapping - run_exports: {} - size: 1698682 - timestamp: 1728293498040 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.6-h2b0a6b4_0.conda - sha256: c5594497f0646e9079705b3199dbb2d5b13c48173cf110000fa1c8818e2b3e0c - md5: 7892f39a39ed39591a89a28eba03e987 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_0.conda + sha256: 4345423572cb80f13acbe52987a880576046a0b42c4e22e3a85e0198ee02aab0 + md5: 10dab6a745f32ceeac6c9e00d9979797 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libglib >=2.86.4,<3.0a0 - - libjpeg-turbo >=3.1.2,<4.0a0 - - liblzma >=5.8.2,<6.0a0 - - libpng >=1.6.56,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libgcc >=15 + - libglib >=2.88.3,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 license: LGPL-2.1-or-later license_family: LGPL - size: 577414 - timestamp: 1774985848058 -- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda - sha256: bc3860e6689be6968ca5bae3660f43dd3e22f4dd61c0bfc99ffd0d0daf4f7a73 - md5: aab9195bc018b82dc77a84584b36cce9 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: LGPL-2.1-only - purls: [] run_exports: weak: - - geos >=3.12.2,<3.12.3.0a0 - size: 1737633 - timestamp: 1721746525671 -- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda - sha256: 08896dcd94e14a83f247e91748444e610f344ab42d80cbf2b6082b481c3f8f4b - md5: 4d4efd0645cd556fab54617c4ad477ef + - gdk-pixbuf >=2.44.8,<3.0a0 + size: 579757 + timestamp: 1786715266831 +- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda + sha256: dc9f03bef913c8b3c94e93a61318b7437bd5e4f7136ef841e68df8de082405d5 + md5: 5f856b72ca9cd5b5b4a1c43551804622 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 license: LGPL-2.1-only - purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 - size: 1974942 - timestamp: 1761593471198 -- conda: https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.3-hf7fa9e8_2.conda - sha256: 3ecd04a14cb3d64f0641828aa9e918895b508809aedf7b5b0ec712c6957b5815 - md5: 1d6bdc6b2c62c8cc90c67b50142d7b7f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libstdcxx-ng >=12 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - zlib - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - geotiff >=1.7.3,<1.8.0a0 - size: 131714 - timestamp: 1722335412421 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda - sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a - md5: d411fc29e338efb48c5fd4576d71d881 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - gflags >=2.2.2,<2.3.0a0 - size: 119654 - timestamp: 1726600001928 + size: 2002850 + timestamp: 1787893677347 - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda sha256: f7026323ac769ef432c747a6c22abd39c583d0d4f6985a7100fb371e2167f214 md5: 04be12e7dd050aaf364ece2393988e85 @@ -4563,50 +3681,36 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - gflags >=2.3.1,<2.4.0a0 size: 130991 timestamp: 1785044715896 -- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda - sha256: 3611c5d605cd56017704e6c01c7bd849e74a656a546d4c49d655fde49ddfeecc - md5: bd7c3a8586ed0101f094962100d30a63 +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda + sha256: 734cd8518063b707aa098932cb109f779b8cf5a3c31633f227d22557a6bfaf53 + md5: 6d9a90cfee2c7343d6c05a1b37ef75fb depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=14 + - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT - purls: [] - run_exports: - weak: - - giflib >=5.2.2,<5.3.0a0 - size: 77403 - timestamp: 1784694210187 -- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff - md5: 3bf7b9fd5a7136126e0234db4b87c8b6 - depends: - - libgcc-ng >=12 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - giflib >=5.2.2,<5.3.0a0 - size: 77248 - timestamp: 1712692454246 -- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.1-hee1de02_2.conda - sha256: ae41fd5c867bc4e713a8cc1dc06f5b418026fec116cc222abe33e94235c6b241 - md5: e5a459d2bb98edb88de5a44bfad66b9d + - giflib >=6.1.3,<6.2.0a0 + size: 87196 + timestamp: 1785907555387 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h67ed8a3_2.conda + sha256: 966f78753d7f044df52a0b32876580ee15f006e8ec960afa5360c50e66be0fc5 + md5: 5c35ace78b7d630e77efcb37dacb62ae depends: - - libglib ==2.88.1 h0d30a3d_2 + - libglib ==2.88.3 he503a2a_2 - libffi - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 license: LGPL-2.1-or-later - size: 236955 - timestamp: 1778508800134 + run_exports: {} + size: 236792 + timestamp: 1787884091247 - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda sha256: f24feeda3aeec3a2cbb9fe2e0fabc4785372e70b6cc838c96023e08e17f4bfa0 md5: 6941f96b8a8056677d79b4f5a2c4aaca @@ -4617,36 +3721,18 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 size: 149031 timestamp: 1784094370091 -- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda - sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 - md5: ff862eebdfeb2fd048ae9dc92510baca - depends: - - gflags >=2.2.2,<2.3.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - glog >=0.7.1,<0.8.0a0 - size: 143452 - timestamp: 1718284177264 -- conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.13.1-hfc2019e_0.conda - sha256: 5ee7116bbc0ad0c46234a168d2e17db0bc7c1a3f9aefbb7c4fb082fddb31deef - md5: 1e152fa4fe2a7b77d4b86c76a6a85b50 +- conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.0-hfc2019e_0.conda + sha256: fc9ce48c6e7c9a33ba67a35e7816209600d783bc6d84329487e36b78c0c7100d + md5: 4fef13b9643fae40e21f8392ab82669a license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: {} - size: 2008937 - timestamp: 1775514521503 + size: 2061866 + timestamp: 1787948975693 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda sha256: 7fa3b6a9c081fa3e545573152a788d061a0a0ba57df7251cc0f4f75225fc93e7 md5: f9fe2984587fa8235a6af6004760cd18 @@ -4656,23 +3742,11 @@ packages: - libgcc >=14 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 size: 102835 timestamp: 1786118485753 -- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda - sha256: 885fa7d1d7e2ad9ed0a700ee0d81ceb49de278253082d517959b22d6336eecce - md5: cf09e9fc938518e91d0706572cadf17a - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: LGPL-2.0-or-later - license_family: LGPL - size: 100054 - timestamp: 1780454302233 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda sha256: 48d4aae8d2f7dd038b8c2b6a1b68b7bca13fa6b374b78c09fcc0757fa21234a1 md5: 341fc61cfe8efa5c72d24db56c776f44 @@ -4695,25 +3769,25 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other - purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 size: 2426455 timestamp: 1769427102743 -- conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.2-py314h42812f9_0.conda - sha256: 4aa1f8147701b5ab71a71aa208ec1a4d0c8cfcaa68f1fff9ad5d2015abb8ddde - md5: 5fef7ee7545d98388c129de1a47537bb +- conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda + sha256: 8077b6042be044d2c571d5eb340a1ad7a1949534113fb8d9cafb213982b7c60b + md5: a0423baf08abf2f98136e8cc103e46dd depends: - python - - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 267632 - timestamp: 1781762129332 + run_exports: {} + size: 278368 + timestamp: 1786384047792 - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda sha256: c6bb4f06331bcb0a566d84e0f0fad7af4b9035a03b13e2d5ecfaf13be57e6e10 md5: bcaea22d85999a4f17918acfab877e61 @@ -4754,7 +3828,6 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - gtk3 >=3.24.52,<4.0a0 @@ -4770,137 +3843,31 @@ packages: - libstdcxx-ng >=12 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - gts >=0.7.6,<0.8.0a0 size: 318312 timestamp: 1686545244763 -- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda - sha256: 9d33201d3e12a61d4ea4b1252a3468afb18b11a418f095dceffdf09bc6792f59 - md5: 347cb348bfc8d77062daee11c326e518 - depends: - - __glibc >=2.17,<3.0.a0 - - cairo >=1.18.4,<2.0a0 - - freetype >=2.13.3,<3.0a0 - - graphite2 - - icu >=75.1,<76.0a0 - - libexpat >=2.6.4,<3.0a0 - - libgcc >=13 - - libglib >=2.84.0,<3.0a0 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - harfbuzz >=11.0.0,<12.0a0 - size: 1720702 - timestamp: 1743082646624 -- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda - sha256: da9901aa1e20cbc2369fda212039b294dd02bce95f005539bab840b7310bf7d0 - md5: 21ee4640b7c2d94e584349fa12b29b9a - depends: - - __glibc >=2.17,<3.0.a0 - - cairo >=1.18.4,<2.0a0 - - graphite2 >=1.3.14,<2.0a0 - - icu >=78.3,<79.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libgcc >=14 - - libglib >=2.88.1,<3.0a0 - - libstdcxx >=14 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - size: 2362258 - timestamp: 1780450503234 -- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda - sha256: 511813aaad42ed2494efc77452738fed7734985e069efc08c279a701b1708ba0 - md5: 7f42f814e1306f83e4cac267baa9acab +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_0.conda + sha256: 78cf9cedd013ba49455b2c75e95d2cdc4aafd384be8f9ece0def6ff1b2a86c61 + md5: c2d4a4fe3216b26ef30e91d917c1b718 depends: - - libharfbuzz-devel 14.3.0 h17a8019_0 + - libharfbuzz-devel 14.4.0 h23af247_0 license: MIT license_family: MIT - purls: [] - run_exports: - weak: - - libharfbuzz >=14.3.0 - size: 11045 - timestamp: 1785770087614 -- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda - sha256: 0d09b6dc1ce5c4005ae1c6a19dc10767932ef9a5e9c755cfdbb5189ac8fb0684 - md5: bd77f8da987968ec3927990495dc22e4 - depends: - - libgcc-ng >=12 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libstdcxx-ng >=12 - - libzlib >=1.2.13,<2.0.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - hdf4 >=4.2.15,<4.2.16.0a0 - size: 756742 - timestamp: 1695661547874 -- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_hdf9ad27_105.conda - sha256: 2278fa07da6f96e807d402cd55480624d67d2dee202191aaaf278ce5ab23605a - md5: 7e1729554e209627636a0f6fabcdd115 - depends: - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libgcc-ng >=12 - - libgfortran-ng - - libgfortran5 >=12.3.0 - - libstdcxx-ng >=12 - - libzlib >=1.2.13,<2.0a0 - - openssl >=3.3.1,<4.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - hdf5 >=1.14.3,<1.14.4.0a0 - size: 3911675 - timestamp: 1717587866574 + - libharfbuzz >=14.4.0 + size: 11141 + timestamp: 1787795205932 - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda sha256: 6d7e6e1286cb521059fe69696705100a03b006efb914ffe82a2ae97ecbae66b7 md5: 129e404c5b001f3ef5581316971e3ea0 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: {} size: 17625 timestamp: 1771539597968 -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda - sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e - md5: 8b189310083baabfb622af68fd9d3ae3 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - icu >=75.1,<76.0a0 - size: 12129203 - timestamp: 1720853576813 -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - sha256: fbf86c4a59c2ed05bbffb2ba25c7ed94f6185ec30ecb691615d42342baa1a16a - md5: c80d8a3b84358cb967fa81e7075fbc8a - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - size: 12723451 - timestamp: 1773822285671 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 md5: 72a381cbad04f24b1c2a43ef707f45b4 @@ -4909,7 +3876,7 @@ packages: - libstdcxx >=14 - libgcc >=14 license: MIT - purls: [] + license_family: MIT run_exports: weak: - icu >=78.3,<79.0a0 @@ -4925,25 +3892,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 55407 timestamp: 1757685419776 -- conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h1220068_1.conda - sha256: 0caf06ccfbd6f9a7b3a1e09fa83e318c9e84f2d1c1003a9e486f2600f4096720 - md5: f8f0f0c4338bad5c34a4e9e11460481d - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - json-c >=0.17,<0.18.0a0 - size: 83682 - timestamp: 1720812978049 - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda sha256: 09e706cb388d3ea977fabcee8e28384bdaad8ce1fc49340df5f868a2bd95a7da md5: 38f5dbc9ac808e31c00650f7be1db93f @@ -4952,140 +3903,69 @@ packages: - libgcc >=13 license: MIT license_family: MIT - purls: [] run_exports: weak: - json-c >=0.18,<0.19.0a0 size: 82709 timestamp: 1726487116178 -- conda: https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.3-hf8d3e68_2.conda - sha256: a45cb038fce2b6fa154cf0c71485a75b59cb1d8d6b0465bdcb23736aca6bf2ac - md5: ffe68c611ae0ccfda4e7a605195e22b3 - depends: - - __glibc >=2.17,<3.0.a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libgcc >=13 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - kealib >=1.5.3,<1.6.0a0 - size: 180005 - timestamp: 1725399272056 -- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 - md5: b38117a3c920364aff79f870c984b4a3 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + sha256: dd053c96dcb0dcfd59422aefea9d2fe937a190167f34fba7893ec1e10a7e8963 + md5: ba55d1b89fd7775e67de8291029b4059 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - libgcc >=15 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - keyutils >=1.6.3,<2.0a0 - size: 134088 - timestamp: 1754905959823 -- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda - sha256: eec7654c2d68f06590862c6e845cc70987b6d6559222b6f0e619dea4268f5dd5 - md5: cd74a9525dc74bbbf93cf8aa2fa9eb5b + size: 135295 + timestamp: 1786739238128 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda + sha256: 09f3578b5cf4484d236e02f5b9fe455e45448419b86ef16aee46d45e9fd3b715 + md5: 6ec45525bac89c279b6e5f4c47eec6bf depends: - python - - libstdcxx >=14 - - libgcc >=14 + - libstdcxx >=15 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} - size: 77120 - timestamp: 1773067050308 -- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py314h97ea11e_0.conda - sha256: e3488ea4a336f29e57de8f282bf40c0505cfc482e03004615e694b48e7d9c79f - md5: 7397e418cab519b8d789936cf2dde6f6 - depends: - - python - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping - run_exports: {} - size: 77363 - timestamp: 1773067048780 -- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 - md5: 3f43953b7d3fb3aaa1d0d0723d91e368 - depends: - - keyutils >=1.6.1,<2.0a0 - - libedit >=3.1.20191231,<3.2.0a0 - - libedit >=3.1.20191231,<4.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - krb5 >=1.21.3,<1.22.0a0 - size: 1370023 - timestamp: 1719463201255 -- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - sha256: 9b07046870772f28740e3f6149f09ff222843733087a33c5540b169c6289652d - md5: 54157a1c8c0bb70f62dd0b17fba7e7f2 + size: 75073 + timestamp: 1787938396652 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + sha256: 2a5c38c85e63df84c4e69ee71439841ce570d259ae3060627bb9a49a938d66f4 + md5: 53318d715316929a574f83591308b1f8 depends: - __glibc >=2.17,<3.0.a0 - keyutils >=1.6.3,<2.0a0 - libedit >=3.1.20250104,<3.2.0a0 - libedit >=3.1.20250104,<4.0a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 - size: 1388990 - timestamp: 1781859420533 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - sha256: 112b5b9462572d970f4abd2912f76a25ee7db158b1e7260163d91dd8a630db84 - md5: 8b3ce45e929cd8e8e5f4d18586b56d8b + size: 1394333 + timestamp: 1786762112514 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda + sha256: 8cdec1ff3f276cd2069dca0dae4f45f3dc5c224e2d72daef78227832938467a6 + md5: b68c7304d2edb0f1a5bdfb875094d603 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libgcc >=15 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 - size: 251971 - timestamp: 1780211695895 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c - md5: 18335a698559cdbcd86150a48bf54ba6 - depends: - - __glibc >=2.17,<3.0.a0 - - zstd >=1.5.7,<1.6.0a0 - constrains: - - binutils_impl_linux-64 2.45.1 - license: GPL-3.0-only - license_family: GPL - purls: [] - run_exports: {} - size: 728002 - timestamp: 1774197446916 + size: 253830 + timestamp: 1788155917881 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda sha256: 27d83f1188cd19bcb7754a078b3fa7f4cfb8527f8eb2fde54dd01fc529d1adec md5: 449500f2c089da11c40f5c21312e3e07 @@ -5096,62 +3976,26 @@ packages: - binutils_impl_linux-64 2.46.1 license: GPL-3.0-only license_family: GPL - purls: [] run_exports: {} size: 745303 timestamp: 1784214507189 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda - sha256: f84cb54782f7e9cea95e810ea8fef186e0652d0fa73d3009914fa2c1262594e1 - md5: a752488c68f2e7c456bcbd8f16eec275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + sha256: bf9fdebf55d8bc99d83531cdffda00703f4dc5f93a1a956768c147362c72feda + md5: fb9d356b1a57d6d54768be7ebd5fce09 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: Apache-2.0 license_family: Apache - purls: [] - run_exports: - weak: - - lerc >=4.1.0,<5.0a0 - size: 261513 - timestamp: 1773113328888 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - sha256: bf9fdebf55d8bc99d83531cdffda00703f4dc5f93a1a956768c147362c72feda - md5: fb9d356b1a57d6d54768be7ebd5fce09 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: Apache-2.0 - license_family: Apache - purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 size: 271158 timestamp: 1785036167977 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda - sha256: 945396726cadae174a661ce006e3f74d71dbd719219faf7cc74696b267f7b0b5 - md5: c48fc56ec03229f294176923c3265c05 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - constrains: - - abseil-cpp =20240116.2 - - libabseil-static =20240116.2=cxx17* - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libabseil >=20240116.2,<20240117.0a0 - - libabseil =*=cxx17* - size: 1264712 - timestamp: 1720857377573 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda - sha256: 32933de2d4fa6e6ffd949052815b49cb65a0649ad70007155c533ab97ea8cefd - md5: c4393db381bffa0a83a8d9e47b238106 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda + sha256: 7bb3d495411b7059e85225aae500d84e7846a4bb02ebd77e4450200b20c180f0 + md5: 6983bfe8e09992014b9cf393769c7a84 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -5161,79 +4005,22 @@ packages: - libabseil-static =20260526.0=cxx17* license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 - libabseil =*=cxx17* - size: 1437712 - timestamp: 1780524559298 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 - md5: 86f7414544ae606282352fa1e116b41f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libaec >=1.1.5,<2.0a0 - size: 36544 - timestamp: 1769221884824 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.7.7-hadbb8c3_0.conda - sha256: 68afcb4519d08cebf71845aff6038e7273f021efc04ef48246f8d41e4e462a61 - md5: 4a099677417658748239616b6ca96bb6 - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=13 - - libxml2 >=2.13.5,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.4.0,<4.0a0 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libarchive >=3.7.7,<3.8.0a0 - size: 874221 - timestamp: 1732614239458 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.8-gpl_hc2c16d8_100.conda - sha256: f916b51f55f51a9bb2d902e0a5f029490f7b745aee549c349751c1787ddc26b8 - md5: 44652e646cb623f486ea72e7e7479222 - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=14 - - liblzma >=5.8.3,<6.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - libzlib >=1.3.2,<2.0a0 - - lz4-c >=1.10.0,<1.11.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - size: 867280 - timestamp: 1782289011634 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_hc2c16d8_100.conda - sha256: 000b4baa9ce849b5fb259af9256331d0c7872361ac63a2bba0d0c48eb35663d0 - md5: 3988040b14a8083b1a5382d99f584e5f + size: 1436888 + timestamp: 1787217011773 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda + sha256: bfae027a67d02325cd6b75edd60beb67cf24483af9df0ae9ef74d4438e41c806 + md5: 680520cff78a974d564cd99cdf5ec0b5 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - - libgcc >=14 + - libgcc >=15 - liblzma >=5.8.3,<6.0a0 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 - libzlib >=1.3.2,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - lzo >=2.10,<3.0a0 @@ -5241,56 +4028,11 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 - size: 876197 - timestamp: 1785250447640 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-h8d2e343_13_cpu.conda - build_number: 13 - sha256: 91e639761f29ee1ca144e92110d47c8e68038f26201eef25585a48826e037fb2 - md5: dc379f362829d5df5ce6722565110029 - depends: - - __glibc >=2.17,<3.0.a0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 - - bzip2 >=1.0.8,<2.0a0 - - gflags >=2.2.2,<2.3.0a0 - - glog >=0.7.1,<0.8.0a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libgcc >=13 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libre2-11 >=2023.9.1 - - libstdcxx >=13 - - libutf8proc >=2.8.0,<3.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - orc >=2.0.2,<2.0.3.0a0 - - re2 - - snappy >=1.2.1,<1.3.0a0 - - zstd >=1.5.6,<1.6.0a0 - - libutf8proc <2.9 - constrains: - - arrow-cpp <0.0a0 - - parquet-cpp <0.0a0 - - apache-arrow-proc =*=cpu - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow >=17.0.0,<17.1.0a0 - size: 8512685 - timestamp: 1725214716301 + size: 875346 + timestamp: 1787292369121 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda build_number: 4 sha256: b3b92d94fa20d1d195de702c4b61cd0c3438d26672cbaf6ebbf021114a840a77 @@ -5326,29 +4068,11 @@ packages: - parquet-cpp <0.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 size: 6598422 timestamp: 1786315080658 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_13_cpu.conda - build_number: 13 - sha256: cda9e38ad7af7ba72416031b089de5048f8526ae586149ff9f6506366689d699 - md5: b654d072b8d5da807495e49b28a0b884 - depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 17.0.0 h8d2e343_13_cpu - - libgcc >=13 - - libstdcxx >=13 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-acero >=17.0.0,<17.1.0a0 - size: 609649 - timestamp: 1725214754397 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda build_number: 4 sha256: 886631b6e32f62e04cc314462c0fbbf9d461cfb6625d2384ae0f36b5a8a72890 @@ -5361,7 +4085,6 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -5381,31 +4104,11 @@ packages: - re2 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 size: 2974934 timestamp: 1786315141259 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_13_cpu.conda - build_number: 13 - sha256: b3fac9bc9a399670d6993738d018324d6e1b0a85755b484204405bb72efabc4e - md5: cd2c36e8865b158b82f61c6aac28b7e1 - depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 17.0.0 h8d2e343_13_cpu - - libarrow-acero 17.0.0 h5888daf_13_cpu - - libgcc >=13 - - libparquet 17.0.0 h39682fd_13_cpu - - libstdcxx >=13 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-dataset >=17.0.0,<17.1.0a0 - size: 582848 - timestamp: 1725214820464 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda build_number: 4 sha256: 6715d36e6aaf00986876e67e97ae8e9b60afb2a7f52ab79b89543c37cd914e28 @@ -5420,34 +4123,11 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 size: 591131 timestamp: 1786315326056 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf54134d_13_cpu.conda - build_number: 13 - sha256: 01ff52d5b866f3174018c81dee808fbef1101f2cff05cc5f29c80ff68cc8796c - md5: 46f41533959eee8826c09e55976b8c06 - depends: - - __glibc >=2.17,<3.0.a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libarrow 17.0.0 h8d2e343_13_cpu - - libarrow-acero 17.0.0 h5888daf_13_cpu - - libarrow-dataset 17.0.0 h5888daf_13_cpu - - libgcc >=13 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libstdcxx >=13 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-substrait >=17.0.0,<17.1.0a0 - size: 550883 - timestamp: 1725214851656 - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda build_number: 4 sha256: 7478e785ad4b7aee72e685921d849b3fb11b0c477dfea76d9e861ee5056b35a6 @@ -5464,249 +4144,124 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 size: 500255 timestamp: 1786315353425 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - build_number: 8 - sha256: b2da6bfd72a1c9cb143ccf64bf5b28790cb4eb58bd1cb978f6537b2322f7d48b - md5: 00fc660ab1b2f5ca07e92b4900d10c79 - depends: - - libopenblas >=0.3.33,<0.3.34.0a0 - - libopenblas >=0.3.33,<1.0a0 - constrains: - - blas 2.308 openblas - - mkl <2027 - - libcblas 3.11.0 8*_openblas - - liblapack 3.11.0 8*_openblas - - liblapacke 3.11.0 8*_openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libblas >=3.11.0,<4.0a0 - size: 18804 - timestamp: 1779859100675 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda - build_number: 9 - sha256: 39c7b3c5427b435c9c059ede9da61d46d42574e5b846ad37fdc3af4a5eab1e48 - md5: f5c4b041925dea221dc4bad2e50569d9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda + build_number: 10 + sha256: 3b0600d79b16cc868421adbba03364ba59d2a3c088c79eef678f4a0b43fc7c07 + md5: e2ca3eadd889d39b4e4b6b1ecc671816 depends: - libopenblas >=0.3.34,<0.3.35.0a0 - libopenblas >=0.3.34,<1.0a0 constrains: - - blas 2.309 openblas - - libcblas 3.11.0 9*_openblas - - liblapack 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - libcblas 3.11.0 10*_openblas + - liblapack 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas - mkl <2027 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 - size: 18033 - timestamp: 1786059035239 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda - sha256: 2338a92d1de71f10c8cf70f7bb9775b0144a306d75c4812276749f54925612b6 - md5: 1d29d2e33fe59954af82ef54a8af3fe1 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - size: 69333 - timestamp: 1756599354727 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e - md5: 72c8fd1af66bd67bf580645b426513ed + size: 17975 + timestamp: 1788076997926 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda + sha256: e5864f257f839ffc27d681659bac95901f524f602b9121e5dcc5e2df18437f2d + md5: 7a2499a177753582fb7ae7e9dc4a908a depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 79965 - timestamp: 1764017188531 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda - sha256: fcec0d26f67741b122f0d5eff32f0393d7ebd3ee6bb866ae2f17f3425a850936 - md5: 5cb5a1c9a94a78f5b23684bcb845338d - depends: - - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.1.0 hb03c661_4 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 33406 - timestamp: 1756599364386 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b - md5: 366b40a69f0ad6072561c1d09301c886 + size: 80265 + timestamp: 1786622773969 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda + sha256: dad31b6d104973deb89710929a35651033aad692d4e7793cdb5b786a9bd54678 + md5: 6ab3315dc56618d652c1da42a648a129 depends: - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.2.0 hb03c661_1 - - libgcc >=14 + - libbrotlicommon 1.2.0 h39a168f_3 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 34632 - timestamp: 1764017199083 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda - sha256: d42c7f0afce21d5279a0d54ee9e64a2279d35a07a90e0c9545caae57d6d7dc57 - md5: 2e55011fa483edb8bfe3fd92e860cd79 - depends: - - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.1.0 hb03c661_4 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlienc >=1.1.0,<1.2.0a0 - size: 289680 - timestamp: 1756599375485 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d - md5: 4ffbb341c8b616aa2494b6afb26a0c5f + size: 34828 + timestamp: 1786622783405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda + sha256: d37124d0f51816e7d5e3a94bfc9ed3d6174d077f9b4f832d20c5a08b52bebf1f + md5: 2ac965638d4c6b2b38383bb1aebaf543 depends: - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.2.0 hb03c661_1 - - libgcc >=14 + - libbrotlicommon 1.2.0 h39a168f_3 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 298378 - timestamp: 1764017210931 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - build_number: 8 - sha256: 1a2bc77bb26520255904a3d9b1f40e6bf0bf9d8d3405c7709dd162282820915a - md5: 33a413f1095f8325e5c30fde3b0d2445 + size: 298639 + timestamp: 1786622792145 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda + build_number: 10 + sha256: 90fd992748d501f6efe2b8310aa81f5ee4a63629450dd88e851ba4ab757f3c3a + md5: a275bd95aa4e22c24120bf6ece6eb056 depends: - - libblas 3.11.0 8_h4a7cf45_openblas - constrains: - - blas 2.308 openblas - - liblapacke 3.11.0 8*_openblas - - liblapack 3.11.0 8*_openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libcblas >=3.11.0,<4.0a0 - size: 18778 - timestamp: 1779859107964 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda - build_number: 9 - sha256: 4c532a70ea9aeff2fa1aabaa4828ebc00c2ed12b22aa8ba19da5302b882fc82b - md5: 092c5649f3727af436ab0f67f48c3811 - depends: - - libblas 3.11.0 9_h4a7cf45_openblas + - libblas 3.11.0 10_h4a7cf45_openblas constrains: - - blas 2.309 openblas - - liblapack 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - liblapack 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 - size: 17998 - timestamp: 1786059041397 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda - sha256: 6b256303e6a1db40e4ce61a5bc69d0e78bf8946070309b665555248667aa23f3 - md5: feec6e0f138982e3f7b1ac372fb396cf - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libllvm19 >=19.1.7,<19.2.0a0 - - libstdcxx >=14 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - purls: [] - run_exports: - weak: - - libclang-cpp19.1 >=19.1.7,<19.2.0a0 - size: 20824232 - timestamp: 1776984079535 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp22.1-22.1.8-default_h08c5240_6.conda - sha256: fc2981a8e45dd232ae88cf2580f7e75c258f4dccdc96f879b68ef0a9b45c9a2b - md5: 4fd9026a57bb45cd15bd08ff8bd0578e + size: 17959 + timestamp: 1788077003985 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda + sha256: ff0507777b9d5ff5678466516d2f1793361ecef541114fb6f3cabb91fc7d5b3c + md5: d3ba2947f7d7cdd7c4eb73b206de330b depends: - - libstdcxx >=14 - - libgcc >=14 - __glibc >=2.17,<3.0.a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 + - libstdcxx >=15 + - libgcc >=15 - libxml2 - - libxml2-16 >=2.14.6 - - libllvm22 >=22.1.8,<22.2.0a0 - license: Apache-2.0 WITH LLVM-exception - purls: [] - run_exports: - weak: - - libclang-cpp22.1 >=22.1.8,<22.2.0a0 - size: 24171067 - timestamp: 1786527767794 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda - sha256: e6c0123b888d6abf03c66c52ed89f9de1798dde930c5fd558774f26e994afbc6 - md5: 327c78a8ce710782425a89df851392f7 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libllvm21 >=21.1.0,<21.2.0a0 - - libstdcxx >=14 + - libxml2-16 >=2.15.3 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.2,<2.0a0 + - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception - license_family: Apache - purls: [] run_exports: weak: - - libclang13 >=21.1.0 - size: 12358102 - timestamp: 1757383373129 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.8-default_hd70ba2e_6.conda - sha256: 8475136d3be6c2d16bdb801dcaf8769cb2ba372cad239c89ebd6a58912e14465 - md5: a8e147873eca89dc1c5d319b027a6cd3 + - libclang-cpp23.1 >=23.1.0,<23.2.0a0 + size: 25353391 + timestamp: 1788037800581 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda + sha256: 1c7589a69833294ab79e5b239d4d70c8c6af68b745a5c17344319d088d884bb0 + md5: 6afb92887568acc8f18281c57c92c28b depends: - - libclang-cpp22.1 ==22.1.8 default_h08c5240_6 - - libstdcxx >=14 - - libgcc >=14 + - libclang-cpp23.1 ==23.1.0 default_h0acdd01_1 - __glibc >=2.17,<3.0.a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 + - libstdcxx >=15 + - libgcc >=15 - libxml2 - - libxml2-16 >=2.14.6 - - libllvm22 >=22.1.8,<22.2.0a0 + - libxml2-16 >=2.15.3 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.2,<2.0a0 + - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception - purls: [] run_exports: weak: - - libclang13 >=22.1.8 - size: 14275196 - timestamp: 1786527767794 + - libclang13 >=23.1.0 + size: 15442245 + timestamp: 1788037800581 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 md5: c965a5aa0d5c1c37ffc62dff36e28400 @@ -5715,7 +4270,6 @@ packages: - libstdcxx-ng >=9.4.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 @@ -5732,99 +4286,31 @@ packages: - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libcups >=2.3.3,<2.4.0a0 size: 4518030 timestamp: 1770902209173 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda - sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 - md5: d4a250da4737ee127fb1fa6452a9002e - depends: - - __glibc >=2.17,<3.0.a0 - - krb5 >=1.21.3,<1.22.0a0 - - libgcc >=13 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libcups >=2.3.3,<2.4.0a0 - size: 4523621 - timestamp: 1749905341688 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_0.conda - sha256: 93ab25f02666eb8696fa70d9c920f2e3b370742ee17e2758705038a72e11147f - md5: dcd79cabd5d435f48d75db3d81cb5874 - depends: - - __glibc >=2.17,<3.0.a0 - - krb5 >=1.22.2,<1.23.0a0 - - libgcc >=14 - - libnghttp2 >=1.68.1,<2.0a0 - - libssh2 >=1.11.1,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: curl - license_family: MIT - size: 479582 - timestamp: 1782296544301 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda - sha256: aff8ef75636d0825ce34911e0bef2f26816e7afd090a9dcb4b9bacab75cbf584 - md5: 3f2fd5617cfacac49c85f6dc63842ea1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + sha256: 0b6cc13e36cf19ae9b764740112fc591682e189c99353be9f72f3d96ccf29d79 + md5: 0ed167049513943d078a6c997df2b4e0 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - - libgcc >=14 + - libgcc >=15 - libnghttp2 >=1.68.1,<2.0a0 - - libpsl >=0.23.0,<0.24.0a0 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT - purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 - size: 480565 - timestamp: 1785500108494 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda - sha256: 0ba60f83709068e9ec1ab543af998cb5a201c8379c871205447684a34b5abfd8 - md5: 7da1d242ca3591e174a3c7d82230d3c0 - depends: - - krb5 >=1.21.3,<1.22.0a0 - - libgcc-ng >=12 - - libnghttp2 >=1.58.0,<2.0a0 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: curl - license_family: MIT - purls: [] - run_exports: - weak: - - libcurl >=8.9.1,<9.0a0 - size: 416057 - timestamp: 1722439924963 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 - md5: 6c77a605a7a689d17d4819c0f8ac9a00 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libdeflate >=1.25,<1.26.0a0 - size: 73490 - timestamp: 1761979956660 + size: 484517 + timestamp: 1787183722915 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda sha256: 82e134c8a08b1eed9a2ed8ab578b89aa1730dcde3dea8dd87645ed0637878e54 md5: 40f9b31aa9cf007789867df0decd0492 @@ -5833,80 +4319,64 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 size: 73710 timestamp: 1785908694612 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - sha256: 7d3187c11b7ae66c5595a8afd5a7ce352a490527fdf6614cab129bc7f2c16ba3 - md5: d8d16b9b32a3c5df7e5b3350e2cbe058 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libpciaccess >=0.19,<0.20.0a0 - license: MIT - license_family: MIT - size: 311505 - timestamp: 1778975798004 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda - sha256: b4e74db8ed1d5f8b6c9edf9582953c063650d677b299e90585d00f127644eb87 - md5: 65514a7ba857e9dbffbffd641f293e33 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + sha256: ea46b0ca0fa16af1f8b329b740e6cd8b4577c5378fa8717b28a885f70668633d + md5: 64cc91512b6278c315349dc13c53f680 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - libpciaccess >=0.19,<0.20.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - libdrm >=2.4.127,<2.5.0a0 - size: 311002 - timestamp: 1785984394604 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 - md5: c277e0a4d549b03ac1e9d6cbbe3d017b + - libdrm >=2.4.129,<2.5.0a0 + size: 313461 + timestamp: 1786684701973 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + sha256: 6473eb8caf2aae830f37caa93db9b26dddf7ac84b63229e8bf7fc0e5c3ab95b0 + md5: 50708d3b951d0f8e2d7f2df5b5edc040 depends: - ncurses + - libgcc >=14 - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - ncurses >=6.5,<7.0a0 + - ncurses >=6.6,<7.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 - size: 134676 - timestamp: 1738479519902 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - sha256: 9a25ea93e8272785405a21d30f84e620befb1d545f6dfaae18f06103b5df0443 - md5: 75e9f795be506c96dd43cb09c7c8d557 + size: 135098 + timestamp: 1786616658086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + sha256: 13d3fde9c1dcf254968cc70652222a43f25d04e69555ccf855553110e753288e + md5: 3a1b41c4591a0a1734382af3e2b8bc08 depends: - __glibc >=2.17,<3.0.a0 - - libglvnd 1.7.0 ha4b6fd6_3 + - libglvnd 1.7.0 ha4b6fd6_5 license: LicenseRef-libglvnd - purls: [] run_exports: {} - size: 46500 - timestamp: 1779728188901 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda - sha256: e4b46919c9bb65930bce238bd2736110ed7b8c30e5cd5394e4e1edb48de54843 - md5: 5bc6d55503483aabe8a90c5e7f49a2a4 + size: 46694 + timestamp: 1787310030923 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + sha256: fd2794bbcff7e692fb476c17886702702893d074071d429217bad7cfb072abfd + md5: 577800fc8c9d8b7d9727246bbfde704e depends: - __glibc >=2.17,<3.0.a0 - - libegl 1.7.0 ha4b6fd6_3 - - libgl-devel 1.7.0 ha4b6fd6_3 + - libegl 1.7.0 ha4b6fd6_5 + - libgl-devel 1.7.0 ha4b6fd6_5 - xorg-libx11 license: LicenseRef-libglvnd - purls: [] run_exports: weak: - libegl >=1.7.0,<2.0a0 - size: 31718 - timestamp: 1779728222280 + size: 31242 + timestamp: 1787310065866 - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda sha256: e4418f68d01a6307c4431b585c71b584f40189877364e542ee87deb777f5e85b md5: 7bc31538d6e3fb349e897353969237ec @@ -5915,25 +4385,11 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 size: 43220 timestamp: 1785917328200 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 - md5: 172bf1cd1ff8629f2b1179945ed45055 - depends: - - libgcc-ng >=12 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libev >=4.33,<4.34.0a0 - size: 112766 - timestamp: 1702146165126 - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 md5: a1cfcc585f0c42bf8d5546bb1dfb668d @@ -5942,7 +4398,6 @@ packages: - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -5958,144 +4413,71 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT - purls: [] run_exports: {} size: 77856 timestamp: 1781203599810 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 - md5: a360c33a5abe61c07959e449fa1453eb - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libffi >=3.5.2,<3.6.0a0 - size: 58592 - timestamp: 1769456073053 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda - sha256: ac38603008bf1e99b8ed379b1a656a67a70e2841f2b6a069c630cdf6316012d2 - md5: 0abe40a9880086ca4d2e5daf09dceff9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + sha256: c8c7583ef063bc3c430f1d48298e5ad24f796a35c22ed5b14183325825a61106 + md5: 6525a0b06aa4fd390795f0740636a9dd depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 - size: 67576 - timestamp: 1783520858222 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - sha256: 38f014a7129e644636e46064ecd6b1945e729c2140e21d75bb476af39e692db2 - md5: e289f3d17880e44b633ba911d57a321b + size: 68004 + timestamp: 1787753412298 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + sha256: fb12ecb46c30d18d928c0e8fc346ab13b5f6fc8a9cacae4f2f4bb188782586f5 + md5: f8054e759d0ddddaf34a5c8fedc900a1 depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL - purls: [] run_exports: {} - size: 8049 - timestamp: 1774298163029 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda - sha256: 9f09d889d1021fa0d99968e5f209a7a7b316dee48c97ed0d79e996e1272a6280 - md5: 12a05d10f2e4eadfd7b8f754a17f5e1a - depends: - - libfreetype6 >=2.14.3 - license: GPL-2.0-only OR FTL - purls: [] - run_exports: {} - size: 8419 - timestamp: 1785641173212 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda - sha256: 16f020f96da79db1863fcdd8f2b8f4f7d52f177dd4c58601e38e9182e91adf1d - md5: fb16b4b69e3f1dcfe79d80db8fd0c55d - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libpng >=1.6.55,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - constrains: - - freetype >=2.14.3 - license: GPL-2.0-only OR FTL - purls: [] - run_exports: {} - size: 384575 - timestamp: 1774298162622 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda - sha256: 162f1736f9ec7b19915658cbb25a685748932f697418ab85657332de5da5f496 - md5: e63acf9b3849fd9fc2dba64b69849716 + size: 8407 + timestamp: 1786641007099 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + sha256: ec607dd5445dd17ff6bf8b7fe6832e5504c226dd91d3aaea7ba83569808b0ce4 + md5: b72a266a9317036fd0464cb98b027cd0 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL - purls: [] run_exports: {} - size: 386042 - timestamp: 1785641172605 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - sha256: 8e0a3b5e41272e5678499b5dfc4cddb673f9e935de01eb0767ce857001229f46 - md5: 57736f29cc2b0ec0b6c2952d3f101b6a + size: 387671 + timestamp: 1786641006460 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + sha256: 24090e675d34403b4ee1cd4372d8f6c0937da7ecfd66a19a57cac2ed0f4ea793 + md5: cba14d01083fc62ffd32c24d7d390633 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 constrains: - - libgcc-ng ==15.2.0=*_19 - - libgomp 15.2.0 he0feb66_19 + - libgcc-ng ==16.2.0=*_4 + - libgomp 16.2.0 he0feb66_4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 1041084 - timestamp: 1778269013026 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda - sha256: d5cb8475131c31680f8fd30512c418f373064e272e452063276a8fb14c9fa42f - md5: 5a7d954665c707c93311657cd779c705 + size: 1058083 + timestamp: 1787618680111 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda + sha256: d8e66c14e23f2b3c70410cff5979d9d357e6edfb990b28d8e630852f4d395629 + md5: b3e52878163a841f6fb951989cc0b217 depends: - - __glibc >=2.17,<3.0.a0 - - _openmp_mutex >=4.5 - constrains: - - libgomp 16.1.0 he0feb66_1 - - libgcc-ng ==16.1.0=*_1 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 1057877 - timestamp: 1785375436766 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - sha256: 9dcf54adfaa5e861123c2da4f2f0451a685464ea7e5a41ad91cf67b31d658d98 - md5: 331ee9b72b9dff570d56b1302c5ab37d - depends: - - libgcc 15.2.0 he0feb66_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: - strong: - - libgcc - size: 27694 - timestamp: 1778269016987 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda - sha256: 225275c562337a1cd61705da0ee4235dde7bba7504de1c34b74c894adb2b0eee - md5: 7ed870c014a6f23c7dfafda53d2763a9 - depends: - - libgcc 16.1.0 ha9f2e26_1 + - libgcc 16.2.0 ha9f2e26_4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: strong: - libgcc - size: 28210 - timestamp: 1785375440733 + size: 28403 + timestamp: 1787618684957 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda sha256: 245be793e831170504f36213134f4c24eedaf39e634679809fd5391ad214480b md5: 88c1c66987cd52a712eea89c27104be6 @@ -6115,462 +4497,110 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD - purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 size: 177306 timestamp: 1766331805898 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.9.2-ha770c72_7.conda - sha256: 33ae5aed64c19e3e7e50f0d1bbbd7abfe814687b2a350444c4b2867f81fca9b4 - md5: 63779711c7afd4fcf9cea67538baa67a - depends: - - libgdal-core 3.9.2.* - - libgdal-fits 3.9.2.* - - libgdal-grib 3.9.2.* - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libgdal-jp2openjpeg 3.9.2.* - - libgdal-kea 3.9.2.* - - libgdal-netcdf 3.9.2.* - - libgdal-pdf 3.9.2.* - - libgdal-pg 3.9.2.* - - libgdal-postgisraster 3.9.2.* - - libgdal-tiledb 3.9.2.* - - libgdal-xls 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - - libgdal >=3.9.2,<3.10.0a0 - size: 422887 - timestamp: 1728295073003 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-hd345f60_4.conda - sha256: 710302e174e8aa65d808158dbd5ca89dd171d5ea9db9ca1b469a95199f020990 - md5: e219de8ffa44fdf2eaa036f3b614bf84 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda + sha256: 386644b941e727549a88af3da791764478d6aecf42630012f6dcf0e8006fa93c + md5: 13e691ba803adfed23773eb0a46e78fc depends: - - libstdcxx >=14 - - libgcc >=14 + - libgcc >=15 + - libstdcxx >=15 - __glibc >=2.17,<3.0.a0 - - lerc >=4.2.0,<5.0a0 - - libcurl >=8.21.0,<9.0a0 - - proj >=9.8.1,<9.9.0a0 - - zstd >=1.5.7,<1.6.0a0 - - libjxl >=0.12.0,<0.13.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libzlib >=1.3.2,<2.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libdeflate >=1.25,<1.26.0a0 - - liblzma >=5.8.3,<6.0a0 - - giflib >=5.2.2,<5.3.0a0 - - json-c >=0.18,<0.19.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - lz4-c >=1.10.0,<1.11.0a0 - libexpat >=2.8.1,<3.0a0 - - libjpeg-turbo >=3.2.0,<4.0a0 - - libsqlite >=3.53.4,<4.0a0 - - geos >=3.14.1,<3.14.2.0a0 - - openssl >=3.5.7,<4.0a0 + - libpng >=1.6.58,<1.7.0a0 + - liblzma >=5.8.3,<6.0a0 - libiconv >=1.18,<2.0a0 + - json-c >=0.18,<0.19.0a0 - muparser >=2.3.5,<2.4.0a0 + - openssl >=3.5.7,<4.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libsqlite >=3.53.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + - lerc >=4.2.0,<5.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 - libarchive >=3.8.9,<3.9.0a0 - - libpng >=1.6.58,<1.7.0a0 - blosc >=1.21.6,<2.0a0 + - giflib >=6.1.3,<6.2.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libzlib >=1.3.2,<2.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - libcurl >=8.21.0,<9.0a0 - xerces-c >=3.3.0,<3.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - proj >=9.8.1,<9.9.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libspatialite >=5.1.0,<5.2.0a0 constrains: - libgdal 3.12.4.* license: MIT license_family: MIT - purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 - size: 14098092 - timestamp: 1786090857358 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.9.2-h353785f_1.conda - sha256: ddb6756cdf027e2b8d4886eb248ca0113af25d752a94844a13655121a9658fa6 - md5: c363d0b330b4b21b4c1b10e0981d3a99 - depends: - - __glibc >=2.17,<3.0.a0 - - blosc >=1.21.6,<2.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - geotiff >=1.7.3,<1.8.0a0 - - giflib >=5.2.2,<5.3.0a0 - - json-c >=0.17,<0.18.0a0 - - lerc >=4.0.0,<5.0a0 - - libarchive >=3.7.4,<3.8.0a0 - - libcurl >=8.9.1,<9.0a0 - - libdeflate >=1.21,<1.26.0a0 - - libexpat >=2.6.2,<3.0a0 - - libgcc >=13 - - libiconv >=1.17,<2.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - libsqlite >=3.46.1,<4.0a0 - - libstdcxx >=13 - - libtiff >=4.6.0,<4.8.0a0 - - libuuid >=2.38.1,<3.0a0 - - libwebp-base >=1.4.0,<2.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - pcre2 >=10.44,<10.45.0a0 - - proj >=9.4.1,<9.5.0a0 - - xerces-c >=3.2.5,<3.3.0a0 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 - constrains: - - libgdal 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - size: 10485906 - timestamp: 1725465814087 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-fits-3.9.2-h2db6552_7.conda - sha256: 156ae6b968301cc0ce51c96b60df594569a6df0caab0ac936d2532d09619e2fc - md5: 524e64f1aa0ebc87230109e684f392f4 - depends: - - __glibc >=2.17,<3.0.a0 - - cfitsio >=4.4.1,<4.4.2.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 478024 - timestamp: 1728294286914 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-grib-3.9.2-hc3b29a1_7.conda - sha256: 54937f8f0b85b941321324f350a9e1895b772153b70be64539689466899dd9b1 - md5: 56a7436a66a1a4636001ce4b621a3a33 - depends: - - __glibc >=2.17,<3.0.a0 - - libaec >=1.1.3,<2.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 722657 - timestamp: 1728294356817 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf4-3.9.2-hd5ecb85_7.conda - sha256: 0b8b77e609b72a51e9548e63c4423222515cd833ab5321eb7f283cf250bb00b5 - md5: 9c8431dc0b83d5fe9c12a2c0b6861a72 - depends: - - __glibc >=2.17,<3.0.a0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - libaec >=1.1.3,<2.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 579075 - timestamp: 1728294420920 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf5-3.9.2-h6283f77_7.conda - sha256: 0998f51e51086a56871538c803eb4e87eb404862a38ab0109e1dc78705491db2 - md5: c8c82df3aece4e23804d178a8a8b308a - depends: - - __glibc >=2.17,<3.0.a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 643208 - timestamp: 1728294499558 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-jp2openjpeg-3.9.2-h1b2c38e_7.conda - sha256: 27068921e22565c71cca415211f0185154db3f1d070680790f5c3cc59bb376c7 - md5: f0f86f8cb8835bb91acb8c7fa2c350b0 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - - openjpeg >=2.5.2,<3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 469287 - timestamp: 1728294554655 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-kea-3.9.2-h1df15e4_7.conda - sha256: 252d6f9cc3bb2fa2788e73ce5c8a4587f653f9b1f1dc34ecc022ef4aa1b53bf0 - md5: c693e703649051ee9db0fabd4fcd0483 - depends: - - __glibc >=2.17,<3.0.a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - kealib >=1.5.3,<1.6.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 480532 - timestamp: 1728294987957 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-netcdf-3.9.2-hf2d2f32_7.conda - sha256: 58155b0df43b090ed55341c9b24e07047db9b4bd8889309a02180e99c4e69558 - md5: 4015ef020928219acc0b5c9edbce8d30 - depends: - - __glibc >=2.17,<3.0.a0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libnetcdf >=4.9.2,<4.9.3.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 738251 - timestamp: 1728295070799 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-pdf-3.9.2-h600f43f_7.conda - sha256: 10ebe0047d4300152185c095a74a3159fcc3b3d2b0e0bb111381dc7d018cbf65 - md5: 567066db0820f4983a6741e429c651d1 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - - poppler - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 668085 - timestamp: 1728294642230 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-pg-3.9.2-h5e77dd0_7.conda - sha256: 24bebc7b479dc2373739655a4e8e4142d47d64b37dd5529fdf87dfc2e7586cc4 - md5: e86b26f53ae868565e95fde5b10753d3 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - libstdcxx >=13 - - postgresql - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 527072 - timestamp: 1728294709895 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-postgisraster-3.9.2-h5e77dd0_7.conda - sha256: cfa1968d15e1e4ab94c74a426c55795bd2b702bd9e99767cb74633dfba77afbc - md5: 3392965ffc4e8b7c66a532750ce0e91f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - libstdcxx >=13 - - postgresql - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 480524 - timestamp: 1728294772712 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-tiledb-3.9.2-h4a3bace_2.conda - sha256: f3f2ecc68a847ab644c6ba7d9a38dcdd4996393f851ddaef401fe4d90f0ba8e9 - md5: c3fac34ecba2fcf9d5d31a03b975d5a1 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - - tiledb >=2.26.0,<2.27.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 681765 - timestamp: 1726093490312 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-xls-3.9.2-h03c987c_7.conda - sha256: 363f00ff7b5295a65e918c5f96bcd8fd3daba09fd8d6563de7b7d266144f86e5 - md5: 165f12373452e8d17889e9c877431acf - depends: - - __glibc >=2.17,<3.0.a0 - - freexl >=2.0.0,<3.0a0 - - libgcc >=13 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libstdcxx >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 434813 - timestamp: 1728294922029 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - sha256: 561a42758ef25b9ce308c4e2cf56daee4f06138385a17e29a492cd928e00be6f - md5: 42bf7eca1a951735fa06c0e3c0d5c8e6 - depends: - - libgfortran5 15.2.0 h68bc16d_19 - constrains: - - libgfortran-ng ==15.2.0=*_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 27655 - timestamp: 1778269042954 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda - sha256: 9e82d410a50bd4e5e47cbbb026454c3eb543954baca4db23929575b571bf56a3 - md5: 2fbed65cc90cf0724e1ec4de13696737 - depends: - - libgfortran5 16.1.0 h79bb938_1 - constrains: - - libgfortran-ng ==16.1.0=*_1 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 28134 - timestamp: 1785375470055 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda - sha256: 9ca1d254a3e44e608abec6186b18d372cec21e5253e6da9750f4a8f4780ea0bb - md5: 35d07243abf828674d273aecd1dd537e + size: 14240911 + timestamp: 1786825194007 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda + sha256: 7653be4d88a4d74676c38dd892914d027dcecc0c65c406be772d31cb641f8cfd + md5: 5e92b8413fd1c8f8f3006f4661b12def depends: - - libgfortran 15.2.0 h69a702a_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: - strong: - - libgfortran - size: 27727 - timestamp: 1778269220455 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - sha256: 057978bb69fea29ed715a9b98adf71015c31baecc4aeb2bfc20d4fd5d83579d4 - md5: 85072b0ad177c966294f129b7c04a2d5 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=15.2.0 + - libgfortran5 16.2.0 h6b99dfc_4 constrains: - - libgfortran 15.2.0 + - libgfortran-ng ==16.2.0=*_4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 2483673 - timestamp: 1778269025089 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda - sha256: 05078ab464d506dff971860cb1a553b35bc27c0b5ce8ec29b8bfaca5f2359652 - md5: dd51ed33e8c70995f8e33cc9dc537297 + size: 28377 + timestamp: 1787618711732 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda + sha256: a5510cbcea3b9e9ab24b336429de997fa727af1dba323031500a962a20e38600 + md5: c22348a769bb072b6184eb6f7f05e4f2 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=16.1.0 + - libgcc >=16.2.0 constrains: - - libgfortran 16.1.0 + - libgfortran 16.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 2538696 - timestamp: 1785375448623 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - sha256: ec353b3076ed8e357ed961d0e9ff6997491cade0e603de5bd18a2e301ac78ebd - md5: f25206d7322c0e9648e8b83694d143ab + size: 2526008 + timestamp: 1787618692926 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + sha256: 7b2e7e31e8a4f5a01c45ecadcd8aa68c8f733b035240bc7ba9881835ef4bf7b4 + md5: 81141db127a106eb5a91df69a29c9918 depends: - __glibc >=2.17,<3.0.a0 - - libglvnd 1.7.0 ha4b6fd6_3 - - libglx 1.7.0 ha4b6fd6_3 + - libglvnd 1.7.0 ha4b6fd6_5 + - libglx 1.7.0 ha4b6fd6_5 license: LicenseRef-libglvnd - purls: [] run_exports: {} - size: 133469 - timestamp: 1779728207669 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda - sha256: 41d7d864ad1f199bdb06ff6cc3931455c8af62f1d2071a08c6fa08affbcb678f - md5: 63e43d278ee5084813fe3c2edf4834ce + size: 131988 + timestamp: 1787310049847 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + sha256: 3bebdbeb3ce451287794dddd5cc4d7f4970aac200b2fb797f0d17ac727a71cb7 + md5: f5f7fc038c611a25b22758c0a40d25c9 depends: - __glibc >=2.17,<3.0.a0 - - libgl 1.7.0 ha4b6fd6_3 - - libglx-devel 1.7.0 ha4b6fd6_3 + - libgl 1.7.0 ha4b6fd6_5 + - libglx-devel 1.7.0 ha4b6fd6_5 license: LicenseRef-libglvnd - purls: [] run_exports: weak: - libgl >=1.7.0,<2.0a0 - size: 115664 - timestamp: 1779728218325 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda - sha256: 8e8737ca776d897d81a97e3de28c4bb33c45b5877bbe202b9b0ad2f61ca39397 - md5: 40cdeafb789a5513415f7bdbef053cf5 - depends: - - __glibc >=2.17,<3.0.a0 - - libffi >=3.4,<4.0a0 - - libgcc >=13 - - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - pcre2 >=10.44,<10.45.0a0 - constrains: - - glib 2.84.0 *_0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libglib >=2.84.0,<3.0a0 - size: 3998765 - timestamp: 1743038881905 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda - sha256: 33eb5d5310a5c2c0a4707a0afa644801c2e08c8f70c45e1f62f354116dfe0970 - md5: 17d484ab9c8179c6a6e5b7dbb5065afc - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libffi >=3.5.2,<3.6.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libzlib >=1.3.2,<2.0a0 - - libiconv >=1.18,<2.0a0 - constrains: - - glib >2.66 - license: LGPL-2.1-or-later - size: 4754097 - timestamp: 1778508800134 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda - sha256: 4499458124bcf0bd45e8bd17576f9f007cb1373cb3b01659105443f522bab45c - md5: 533cb021c1bfeba9f720e669cd863fdf + size: 116212 + timestamp: 1787310061570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + sha256: af6c14f387f810c5df491b328ae955329596d3bc73b1e2e091ab5adb46a10759 + md5: 533d342dedadf134c67760ce6fc5b748 depends: - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 - pcre2 >=10.47,<10.48.0a0 - libzlib >=1.3.2,<2.0a0 - libiconv >=1.18,<2.0a0 @@ -6578,98 +4608,57 @@ packages: constrains: - glib >2.66 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4755172 - timestamp: 1786457663614 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - sha256: e019ebe4e3f5cdf23e2f5e58ddf7ade27988c53820115b17b98f218ebcc87748 - md5: eb83f3f8cecc3e9bff9e250817fc69b6 + size: 4758097 + timestamp: 1787884091247 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + sha256: 6eb77601a44b78c4631d886d54ef54f321c2bdc69fdefc4065a1e0491f030c76 + md5: cfcf11edcfd4acf0094771a9c3b8587f depends: - __glibc >=2.17,<3.0.a0 license: LicenseRef-libglvnd - purls: [] run_exports: {} - size: 133586 - timestamp: 1779728183422 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - sha256: 2f74713c9ca408ea84e88a30a9028153e7b553e8bb42e06139eac9a753c27da9 - md5: ec3c4350aa0261bf7f87b8ca15c8e80e + size: 133827 + timestamp: 1787310026653 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + sha256: d1f0d51aea6bcc5d915969cbed32e72a8ed6a95931b87357ef8d0866573688c4 + md5: 614e10aae180f87b84f0d1ca8ceb7d9e depends: - __glibc >=2.17,<3.0.a0 - - libglvnd 1.7.0 ha4b6fd6_3 + - libglvnd 1.7.0 ha4b6fd6_5 - xorg-libx11 >=1.8.13,<2.0a0 license: LicenseRef-libglvnd - purls: [] run_exports: {} - size: 76586 - timestamp: 1779728199059 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda - sha256: a17ae2d4cb2de04a20882ae14ec3cc1958e868a4dec81e3d7eca30115ee50e94 - md5: 16b6330783ce0d1ae8d22782173b32c9 + size: 79834 + timestamp: 1787310042550 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + sha256: aff3841e3404d78e1887fef988ca4842930c577399d566fa0980808756b1df51 + md5: fb00b4fb800f2d431303a5c1625ef9d0 depends: - __glibc >=2.17,<3.0.a0 - - libglx 1.7.0 ha4b6fd6_3 + - libglx 1.7.0 ha4b6fd6_5 - xorg-libx11 >=1.8.13,<2.0a0 - xorg-xorgproto license: LicenseRef-libglvnd - purls: [] run_exports: weak: - libglx >=1.7.0,<2.0a0 - size: 27363 - timestamp: 1779728211402 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - sha256: 5abe4ab9d93f6c9757d654f1969ae2267d4505315c1f2f8fe705fd60af084f1b - md5: faac990cb7aedc7f3a2224f2c9b0c26c - depends: - - __glibc >=2.17,<3.0.a0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: - strong: - - _openmp_mutex >=4.5 - size: 603817 - timestamp: 1778268942614 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda - sha256: 62cb599ad0539d99386515326d9d5e8f51f75a60c69c2131b21df76edf35bd89 - md5: 88f2d91cb1533194c323534253094d23 + size: 27698 + timestamp: 1787310053582 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda + sha256: 0fe5cb8e0752241ab55e11656ed1b9726248b522d23b929fe7c95b83eb55b9bb + md5: 89d2c1231f47bd818f5d624b9411459d depends: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: strong: - _openmp_mutex >=4.5 - size: 640415 - timestamp: 1785375373755 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.28.0-h26d7fe4_0.conda - sha256: d87b83d91a9fed749b80dea915452320598035949804db3be616b8c3d694c743 - md5: 2c51703b4d775f8943c08a361788131b - depends: - - __glibc >=2.17,<3.0.a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgcc-ng >=12 - - libgrpc >=1.62.2,<1.63.0a0 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libstdcxx-ng >=12 - - openssl >=3.3.1,<4.0a0 - constrains: - - libgoogle-cloud 2.28.0 *_0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud >=2.28.0,<2.29.0a0 - size: 1226849 - timestamp: 1723370075980 + size: 639968 + timestamp: 1787618616266 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda sha256: 46126b858d173d6808262e39e3a8aa39946d39aebb5ffb720984dc44f74feafe md5: 60ff8935e16bf2fd302289ec4f129df8 @@ -6688,33 +4677,11 @@ packages: - libgoogle-cloud 3.8.0 *_0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud >=3.8.0,<3.9.0a0 size: 2663465 timestamp: 1786226759530 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.28.0-ha262f82_0.conda - sha256: 3237bc1ee88dab8d8fea0a1886e12a0262ff5e471944a234c314aa1da411588e - md5: 9e7960f0b9ab3895ef73d92477c47dae - depends: - - __glibc >=2.17,<3.0.a0 - - libabseil - - libcrc32c >=1.1.2,<1.2.0a0 - - libcurl - - libgcc-ng >=12 - - libgoogle-cloud 2.28.0 h26d7fe4_0 - - libstdcxx-ng >=12 - - libzlib >=1.3.1,<2.0a0 - - openssl - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - size: 769298 - timestamp: 1723370220027 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda sha256: 03437ce50129f9ca82a0400b7722cd2dd3ee3bfa50dcb557657f55a7de76ab6f md5: 84a8d5a661792da2127ab908b5ae83ef @@ -6730,36 +4697,11 @@ packages: - openssl license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 size: 810573 timestamp: 1786226867433 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda - sha256: 28241ed89335871db33cb6010e9ccb2d9e9b6bb444ddf6884f02f0857363c06a - md5: 8dabe607748cb3d7002ad73cd06f1325 - depends: - - c-ares >=1.28.1,<2.0a0 - - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - libgcc-ng >=12 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libre2-11 >=2023.9.1 - - libstdcxx-ng >=12 - - libzlib >=1.2.13,<2.0.0a0 - - openssl >=3.2.1,<4.0a0 - - re2 - constrains: - - grpc-cpp =1.62.2 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libgrpc >=1.62.2,<1.63.0a0 - size: 7316832 - timestamp: 1713390645548 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda sha256: 1e657c1b802c111178bc1845fe06488db5f69a85e34e970ce0989810aa562d45 md5: aa4571fc3bcf18ad12370429aa991223 @@ -6779,15 +4721,14 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 size: 6957755 timestamp: 1783588701960 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda - sha256: fb72d6f7e90d4927cb53a4e13592559ce74b65052323d5d6dd12499b026c680e - md5: f33637ebded146eafa6b2197c8f597a6 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + sha256: a064695a1c12133d6a9dcf6b3b42423b8614fa45667606201af1b5f72628df0d + md5: 4463210c2a16ff5d3bf7f502af23f1f4 depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 @@ -6795,20 +4736,19 @@ packages: - icu >=78.3,<79.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - - libgcc >=14 + - libgcc >=15 - libglib >=2.88.3,<3.0a0 - libpng >=1.6.58,<1.7.0a0 - - libstdcxx >=14 + - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 1333436 - timestamp: 1785770053613 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda - sha256: 7ce9326c2520ae758f523ae2d72a0762f7494d77fe8cc9a96065df541e1db8e2 - md5: 15634b3c7e5a68ca7c6e0bbf84cb2383 + size: 1380045 + timestamp: 1787795176798 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_0.conda + sha256: bdecc73c22cb0a8d44ce066116562e35322fd6c1c04584a4754ae2befcab4cb3 + md5: 26e37e05324d7bb723350d50b33057fc depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 @@ -6817,95 +4757,75 @@ packages: - icu >=78.3,<79.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - - libgcc >=14 + - libgcc >=15 - libglib >=2.88.3,<3.0a0 - - libharfbuzz 14.3.0 h17a8019_0 + - libharfbuzz 14.4.0 h23af247_0 - libpng >=1.6.58,<1.7.0a0 - - libstdcxx >=14 + - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - libharfbuzz >=14.3.0 - size: 2081226 - timestamp: 1785770079548 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h3a9caae_0.conda - sha256: 02ab5e50c3921e88ad4dd0bc8f3fe282d2d7d03a20203d3281954b94641deb3d - md5: 9544a7225c8366ea2c397aad5fb53470 + - libharfbuzz >=14.4.0 + size: 2142051 + timestamp: 1787795196934 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda + sha256: 23c7cf726b883f5e7c1bfa6bf24bceafa8be87245a7690e0b5c974eb320a9e83 + md5: d58bfbaaf51ed85771eae92c2e7f5816 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 license: Apache-2.0 OR BSD-3-Clause - purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 - size: 1431901 - timestamp: 1784325535334 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f - md5: 915f5995e94f60e9a4826e0b0920ee88 + size: 1454025 + timestamp: 1787282422734 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + sha256: f943117edb9cd4d9c61cc972eee5a34291dc55ea7a6e9e38da104995841cbcb6 + md5: f92233bf33e24a25668bb2119e2c51f9 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: LGPL-2.1-only - purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 - size: 790176 - timestamp: 1754908768807 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - sha256: 10056646c28115b174de81a44e23e3a0a3b95b5347d2e6c45cc6d49d35294256 - md5: 6178c6f2fb254558238ef4e6c56fb782 + size: 789471 + timestamp: 1787033836207 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + sha256: bba8538e6538ed58a8479b332337b96986561f975d06cfa2039a016c2d246ee4 + md5: 898d1c9793eaa52efc4727bd84d2e39a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib - purls: [] - run_exports: - weak: - - libjpeg-turbo >=3.1.4.1,<4.0a0 - size: 633831 - timestamp: 1775962768273 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - sha256: bba8538e6538ed58a8479b332337b96986561f975d06cfa2039a016c2d246ee4 - md5: 898d1c9793eaa52efc4727bd84d2e39a - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - constrains: - - jpeg <0.0.0a - license: IJG AND BSD-3-Clause AND Zlib - purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 size: 650434 timestamp: 1785896381946 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-h174a0a3_1.conda - sha256: 1811d6c6558fbfe89326616c207cc7584032b60bc6f4329d2a76b961e2936a15 - md5: 1fff55640e1f12d7606965915be37bbb +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda + sha256: 965e59ea776344e93a416f7e0ba309810470a61c0e0c65f1eb90be0e808d7e9c + md5: 485788d339785bac87fe86315b4a0627 depends: + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libstdcxx >=15 - libhwy >=1.4.0,<1.5.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 - size: 1849836 - timestamp: 1783146019356 + size: 1886013 + timestamp: 1786691380980 - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda sha256: f6348ac9cebbc03f765ea6d2da88d57d19531585c8f3a963b98635b208e8bef1 md5: 953b7cca897e21215302dbfe2af5cd0c @@ -6918,130 +4838,59 @@ packages: - uriparser >=0.9.8,<1.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 412514 timestamp: 1777026799177 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - build_number: 8 - sha256: 168e327d737059553e15cc6ec36d76b9bbb3931c2a7721555fd68b4c9348b247 - md5: 809be8ba8712c77bc7d44c2d99390dc4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda + build_number: 10 + sha256: 166da1ef513efd2f9ea9e132246766306e5d1a18b4e83b8b8425b5adffb6345e + md5: 7b918d4958f359c889c662aa361cf992 depends: - - libblas 3.11.0 8_h4a7cf45_openblas - constrains: - - blas 2.308 openblas - - libcblas 3.11.0 8*_openblas - - liblapacke 3.11.0 8*_openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - liblapack >=3.11.0,<3.12.0a0 - size: 18790 - timestamp: 1779859115086 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.conda - build_number: 9 - sha256: ea989e2dabd21d296a5a4ec515e695645aefcdf778ffdb5eeea515421d243ab5 - md5: e51473c2b7e1f9cb61daafccfd912abf - depends: - - libblas 3.11.0 9_h4a7cf45_openblas + - libblas 3.11.0 10_h4a7cf45_openblas constrains: - - blas 2.309 openblas - - libcblas 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - libcblas 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 - size: 18021 - timestamp: 1786059046733 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-8_h6ae95b6_openblas.conda - build_number: 8 - sha256: 0b982865888a73fe7c2e7d8e8ee3738ac378b83012a7da1a12264a473cb2382b - md5: da17457f689df5c0f246d2f0b3798fd2 + size: 17978 + timestamp: 1788077010883 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-10_h6ae95b6_openblas.conda + build_number: 10 + sha256: 96532b53b1743c9abfe25c2c2ea3d267737a0c5af9595e98a1c1cdb697812ef4 + md5: 52caccfdceb79e427db4137ac4e13d68 depends: - - libblas 3.11.0 8_h4a7cf45_openblas - - libcblas 3.11.0 8_h0358290_openblas - - liblapack 3.11.0 8_h47877c9_openblas + - libblas 3.11.0 10_h4a7cf45_openblas + - libcblas 3.11.0 10_h0358290_openblas + - liblapack 3.11.0 10_h47877c9_openblas constrains: - - blas 2.308 openblas + - blas 2.310 openblas license: BSD-3-Clause - license_family: BSD - size: 18824 - timestamp: 1779859122364 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda - sha256: 22909d64038bdc87de61311c4ae615dc574a548a7340b963bb7c9eb61b191669 - md5: 6d2362046dce932eefbdeb0540de0c38 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - - libxml2 >=2.13.5,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - purls: [] run_exports: weak: - - libllvm19 >=19.1.7,<19.2.0a0 - size: 40143643 - timestamp: 1737789465087 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda - sha256: d190f1bf322149321890908a534441ca2213a9a96c59819da6cabf2c5b474115 - md5: 9ad637a7ac380c442be142dfb0b1b955 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - libxml2 >=2.13.8,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - purls: [] - run_exports: - weak: - - libllvm21 >=21.1.0,<21.2.0a0 - size: 44363060 - timestamp: 1756291822911 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.8-hf7376ad_1.conda - sha256: e9b5f301d6b001a9b8ce782157f56b75c92c4fbc9eba95dc6345c1139251d13b - md5: 298bb2483fc7d15396147cf1c1465359 + - liblapacke >=3.11.0,<3.12.0a0 + size: 17998 + timestamp: 1788077016927 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda + sha256: 50b31f6c51afe7df0639acf5dde8acf3f4f0dc9f644ab9842b717ae798507d68 + md5: 3b8c8325547a4fd8c51649ef7dfab688 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache - purls: [] - run_exports: - weak: - - libllvm22 >=22.1.8,<22.2.0a0 - size: 44320272 - timestamp: 1781788728739 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d - md5: b88d90cad08e6bc8ad540cb310a761fb - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - constrains: - - xz 5.8.3.* - license: 0BSD - purls: [] run_exports: weak: - - liblzma >=5.8.3,<6.0a0 - size: 113478 - timestamp: 1775825492909 + - libllvm23 >=23.1.0,<23.2.0a0 + size: 45046595 + timestamp: 1787716721647 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 @@ -7051,156 +4900,123 @@ packages: constrains: - xz 5.8.3.* license: 0BSD - purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 size: 112995 timestamp: 1786348617826 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda - sha256: 7858f6a173206bc8a5bdc8e75690483bb66c0dcc3809ac1cb43c561a4723623a - md5: 55c20edec8e90c4703787acaade60808 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - liblzma 5.8.3 hb03c661_0 - license: 0BSD - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 491429 - timestamp: 1775825511214 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.8.1-hd28c85e_0.conda - sha256: 24fe39602b6fc637dc79b973bc49cd884226d7b02aea7e7b3e831ee8cde7135b - md5: aeed88a1185f15ae486744897a8020cf +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda + sha256: 1b9ca94f36f6072ff71550dfaa8dd4106a118bf01f352ed639aef134b462bab6 + md5: 444e55c79d67040779af3a0167963bea depends: - cpp-expected >=1.3.1,<1.3.2.0a0 + - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - nlohmann_json-abi ==3.12.0 + - simdjson >=4.6.6,<4.7.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libcurl >=8.21.0,<9.0a0 - libmsgpack-c >=6.1.0,<7.0a0 - - simdjson >=4.6.4,<4.7.0a0 - - reproc >=14.2,<15.0a0 - - libarchive >=3.8.7,<3.9.0a0 - - openssl >=3.5.6,<4.0a0 - - reproc-cpp >=14.2,<15.0a0 - - spdlog >=1.17.0,<1.18.0a0 + - reproc >=14.2.7.post0,<14.3.0a0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + - openssl >=3.5.7,<4.0a0 - fmt >=12.1.0,<12.2.0a0 - - libcurl >=8.20.0,<9.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - nlohmann_json-abi ==3.12.0 - zstd >=1.5.7,<1.6.0a0 - libsolv >=0.7.39,<0.8.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 license: BSD-3-Clause license_family: BSD - size: 2808228 - timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.8.1-h9b73f26_0.conda - sha256: ef3d79e638dc1e294df239bf83a268dbea835c2c1f66001383473c22a8dc0ffd - md5: 410217d65f86c0a60c9b11ecf1bf842e + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 2850628 + timestamp: 1786125663902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda + sha256: ddae16548197cf30cf72d700733427cb314ce6fa2921fea7e2922566288de42e + md5: 1066c66681f6a1d6e36d13dc3eecbd50 depends: - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - - libmamba >=2.8.1,<2.9.0a0 + - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD - size: 20307 - timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.8.1-py314hd8b5600_0.conda - sha256: c35bf3900a533b2e8a2ad9f63ab645240208ad28c3532d2d46dd84cbddfc2d7e - md5: 828f1c59aa72aa02de5b559c0428ebb8 + run_exports: {} + size: 20529 + timestamp: 1786125663902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda + sha256: 0c2f07dfcb61e14a357df8816abb236e1705b92998ba068784f87fd74070aa85 + md5: 231869ede4419e814ca7d3de6fabdf95 depends: - python - - libmamba ==2.8.1 hd28c85e_0 - - libmamba-spdlog ==2.8.1 h9b73f26_0 + - libmamba ==2.9.0 py314h3b59866_0 + - libmamba-spdlog ==2.9.0 hf1e253f_0 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 + - openssl >=3.5.7,<4.0a0 - fmt >=12.1.0,<12.2.0a0 - - openssl >=3.5.6,<4.0a0 + - spdlog >=1.17.0,<1.18.0a0 - python_abi 3.14.* *_cp314 - - libmamba >=2.8.1,<2.9.0a0 - - zstd >=1.5.7,<1.6.0a0 - yaml-cpp >=0.8.0,<0.9.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - pybind11-abi ==11 - nlohmann_json-abi ==3.12.0 + - zstd >=1.5.7,<1.6.0a0 + - libmamba >=2.9.0,<2.10.0a0 + - pybind11-abi ==11 license: BSD-3-Clause license_family: BSD - size: 967437 - timestamp: 1780948903590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843 - md5: 2c21e66f50753a083cbe6b80f38268fa + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 967399 + timestamp: 1786125663902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + sha256: 46820b4a835e175940ae20bec00fdddaff804cda27a1408ab1b95e77a2196437 + md5: fcfed1dc5053eb1901b66e7b1fc32588 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: {} - size: 92400 - timestamp: 1769482286018 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_6.conda - sha256: 79a75422873cb4107fb731d3794402eda063fcc9a809f4bb0b5738a6b3d46dee - md5: fde05e07c2a9c96ad09216f0d5fda9a1 + size: 92759 + timestamp: 1786650399772 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda + sha256: c23ac21547d9fac6be14b6b3463dd29fb0fd4aba5c64c4a9876dfa6d2e3158a9 + md5: 9ab61308317dbf6609e2142b16b792a5 depends: + - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - - __glibc >=2.17,<3.0.a0 constrains: - - msgpack-c ==6.1.0 *_6 + - msgpack-c ==6.1.0 *_7 license: BSL-1.0 - size: 40340 - timestamp: 1770807484162 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda - sha256: 055572a4c8a1c3f9ac60071ee678f5ea49cfd7ac60a636d817988a6f9d6de6ae - md5: a908e463c710bd6b10a9eaa89fdf003c - depends: - - blosc >=1.21.5,<2.0a0 - - bzip2 >=1.0.8,<2.0a0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - - libxml2 >=2.12.7,<2.14.0a0 - - libzip >=1.10.1,<2.0a0 - - libzlib >=1.2.13,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zlib - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - libnetcdf >=4.9.2,<4.9.3.0a0 - size: 849172 - timestamp: 1717671645362 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - sha256: 663444d77a42f2265f54fb8b48c5450bfff4388d9c0f8253dd7855f0d993153f - md5: 2a45e7f8af083626f009645a6481f12d + - libmsgpack-c >=6.1.0,<7.0a0 + size: 40166 + timestamp: 1786189331007 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda + sha256: 11a2f54a6f391e25738bce0023e6ef499600147bbcf21c1fa69d2e251b03cc6a + md5: 75d211f04ac87506f1f43420712a69fe depends: - __glibc >=2.17,<3.0.a0 - - c-ares >=1.34.6,<2.0a0 + - c-ares >=1.34.8,<2.0a0 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - - libgcc >=14 - - libstdcxx >=14 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.5,<4.0a0 + - libgcc >=15 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 - size: 663344 - timestamp: 1773854035739 + size: 649974 + timestamp: 1787177490105 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 md5: d864d34357c3b65a4b731f78c0801dc4 @@ -7209,7 +5025,6 @@ packages: - libgcc >=13 license: LGPL-2.1-only license_family: GPL - purls: [] run_exports: weak: - libnsl >=2.0.1,<2.1.0a0 @@ -7222,59 +5037,37 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libntlm >=1.8,<2.0a0 size: 33418 timestamp: 1734670021371 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - sha256: 3d9aa85648e5e18a6d66db98b8c4317cc426721ad7a220aa86330d1ccedc8903 - md5: 2d3278b721e40468295ca755c3b84070 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libgfortran - - libgfortran5 >=14.3.0 - constrains: - - openblas >=0.3.33,<0.3.34.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libopenblas >=0.3.33,<1.0a0 - size: 5931919 - timestamp: 1776993658641 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda - sha256: 23392fc4f4e5ba230fcd1ef825878ba5ca7ee4f6259fac0cbb13299134b7bf7a - md5: c282d68f272927612462b5d626838ef1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda + sha256: b2846ca0b00cc08d248589d289ba891856d4b42da4a3c823be6b2d660bd80a83 + md5: 25994250f54292352a82eb05ab4499ba depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - libgfortran - - libgfortran5 >=14.3.0 + - libgfortran5 >=15.3.0 constrains: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libopenblas >=0.3.34,<1.0a0 - size: 5952629 - timestamp: 1784287497473 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda - sha256: 90777039b48529283df5f16383fc399866024257a8bd93de583f4730db1ab30a - md5: c2bd8055a2e2dce7a7f32cfd02101fb6 + size: 5994112 + timestamp: 1788056652715 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda + sha256: 7bdca717c840e17d7dd050f925dd964da61086c2612b8579a4ff4147105f291f + md5: d207a2e9bae9da476bc0a603215d5167 depends: - __glibc >=2.17,<3.0.a0 - - libglvnd 1.7.0 ha4b6fd6_3 + - libglvnd 1.7.0 ha4b6fd6_5 license: LicenseRef-libglvnd - purls: [] run_exports: {} - size: 51767 - timestamp: 1779728204026 + size: 50627 + timestamp: 1787310045795 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda sha256: 8f9281133322e9eb0bd4a5b11330db1c14f3b40c409639dd3805251151ca52e2 md5: f749f223bede1bac7724e49d686ac598 @@ -7292,7 +5085,6 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -7303,29 +5095,9 @@ packages: md5: 6cc68cbbe88746be8beaf027d6c64ad3 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 394726 timestamp: 1783133038109 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda - build_number: 13 - sha256: 3c63b7391275cf6cf2a18d2dba3c30c16dd9d210373d206675e342b084cccdf4 - md5: 49c60a8dc089d8127b9368e9eb6c1a77 - depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 17.0.0 h8d2e343_13_cpu - - libgcc >=13 - - libstdcxx >=13 - - libthrift >=0.20.0,<0.20.1.0a0 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libparquet >=17.0.0,<17.1.0a0 - size: 1189824 - timestamp: 1725214804075 - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda build_number: 4 sha256: dba948e05d62de3833caa065c1b7de960f921bb0a8a7687b8593df95d2d925a9 @@ -7339,22 +5111,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 size: 1412634 timestamp: 1786315223543 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - sha256: f41721636a7c2e51bc2c642e1127955ab9c81145470714fdaac44d4d09e4af41 - md5: 33082e13b4769b48cfeb648e15bfe3fc - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - size: 29147 - timestamp: 1773533027610 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda sha256: addc80c69d362a9e6c40305c493139a8e9ee504b2f45a6687dbdaa9da3c6183c md5: 35fa2b34bbced424e6976d30f5fde576 @@ -7363,112 +5124,84 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - libpciaccess >=0.19,<0.20.0a0 size: 30070 timestamp: 1785971678815 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 - md5: eba48a68a1a2b9d3c0d9511548db85db +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda + sha256: c19eefb87d70d9b4b0629fa48414a155a47a1be4f04583ae71ed8c5a9a32fdcb + md5: fcf71c8d979148873f6f8ad4cfc73d86 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement - purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 - size: 317729 - timestamp: 1776315175087 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda - sha256: d8ed60436b8f1484d74f68b01f98301d6c8174df1d77a3e89ba42f033dcb43c5 - md5: 52dd46162c6fb2765b49e6fd06adf8d5 - depends: - - __glibc >=2.17,<3.0.a0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libgcc >=13 - - openldap >=2.6.8,<2.7.0a0 - - openssl >=3.4.0,<4.0a0 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 2588868 - timestamp: 1732204566030 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda - sha256: f7232cb79a31f5a5bcd499aea930b469cde8b96d26db9541022493fd274d2a6e - md5: 6c9103e7ea739a3bb3505da49a4708c1 + size: 316643 + timestamp: 1786616563127 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + sha256: b1eb210c180fda9c445b11cba1286ec250ce2c2a85ccb0551a4ed5423df51e68 + md5: 72e019c04ed02a179da38c56965802d1 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - krb5 >=1.22.2,<1.23.0a0 - - libgcc >=14 + - libgcc >=15 - openldap >=2.6.13,<2.7.0a0 - openssl >=3.5.7,<4.0a0 license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=18.4,<19.0a0 - size: 2709008 - timestamp: 1782580447454 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda - sha256: 8b5e4e31ed93bf36fd14e9cf10cd3af78bb9184d0f1f87878b8d28c0374aa4dc - md5: 06def97690ef90781a91b786cb48a0a9 - depends: - - __glibc >=2.17,<3.0.a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libgcc >=13 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - libprotobuf >=4.25.3,<4.25.4.0a0 - size: 2883090 - timestamp: 1727161327039 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h2840a7c_2.conda - sha256: b5ac3938186516c1091b96414c34f90255da2a3bbd736a0712b22bbf4b5ebf02 - md5: 729db8acaadb9a5e5bb2dc3d9ac84ae4 + - libpq >=18.6,<19.0a0 + size: 2719680 + timestamp: 1786641133110 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + sha256: 37c51fbc936a6bb8bf5f67c8f056edacaaa41e8603738bc8a4dee186292bb114 + md5: fd307b61cceb36fdca38e1718bdb2893 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260526.0,<20260527.0a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 - size: 3774596 - timestamp: 1783168720126 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda - sha256: 2e6405feb59e3f40a5a4641dfa73afda158046893aa73d478e23415e18997ece - md5: c8217f5bdd5b018087bdea081912cda5 + size: 3808338 + timestamp: 1787657986197 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + sha256: 09e8effdc89bc5d021349318d32b85594f5b8d8e3ab8f90a85b81f8fe695a566 + md5: 77be60417aa572afcb48cbe0f967401d depends: - - libgcc >=14 - - libstdcxx >=14 + - libstdcxx >=15 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 - size: 72505 - timestamp: 1786443494718 + size: 72519 + timestamp: 1786970753847 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_104_cp314.conda + build_number: 104 + sha256: ea102c446220e9b8ad2125e38e0f4a0d9b04a7fc10193eca8d130bee507ce9e2 + md5: eb63e79ef1ac24c034d5b6ab3bcbd00c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + license: Python-2.0 + run_exports: {} + size: 10507939 + timestamp: 1788161778972 - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda sha256: ab3ccb9fd5816f76b18ee8974d359cd2605ca87d8ddef55369dc461b9986f95c md5: 3ac89a48d224409739dbf6200e524373 @@ -7481,30 +5214,11 @@ packages: - libharfbuzz >=14.2.1 license: MIT license_family: MIT - purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 size: 33983 timestamp: 1784850267262 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda - sha256: 3f3c65fe0e9e328b4c1ebc2b622727cef3e5b81b18228cfa6cf0955bc1ed8eff - md5: 41c69fba59d495e8cf5ffda48a607e35 - depends: - - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - constrains: - - re2 2023.09.01.* - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - size: 232603 - timestamp: 1708946763521 - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda sha256: 43132eb45a569b1f63199b0e7d5874d94227e9be950b327a323e7dcc1f8cd58c md5: ee5c400d37b79db5d32a69ed1a79fc0b @@ -7518,7 +5232,6 @@ packages: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -7541,7 +5254,6 @@ packages: constrains: - __glibc >=2.17 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - librsvg >=2.62.3,<3.0a0 @@ -7557,57 +5269,38 @@ packages: - libstdcxx >=14 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 size: 231670 timestamp: 1761670395043 -- conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hc670b87_16.conda - sha256: 65bfd9f8915b1fc2523c58bf556dc2b9ed6127b7c6877ed2841c67b717f6f924 - md5: 3d9f3a2e5d7213c34997e4464d2f938c - depends: - - __glibc >=2.17,<3.0.a0 - - geos >=3.12.2,<3.12.3.0a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: GPL-2.0-or-later - license_family: GPL - purls: [] - run_exports: - weak: - - librttopo >=1.1.0,<1.2.0a0 - size: 231637 - timestamp: 1720347750456 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda - sha256: b677bbf1c339d894757c3dcfbb2f88649e499e4991d70ae09a1466da9a6c92d6 - md5: 965e4d531b588b2e42f66fd8e48b056c +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-hebe6cf0_2.conda + sha256: 7454c6a7cf27033757f2895bc5e3941fe27604506edd62cf6bc00e50ab015a43 + md5: 42c585f153c17b790cd01f01553d24a1 depends: + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 license: ISC - purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 - size: 269272 - timestamp: 1779163468406 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h9463b59_0.conda - sha256: 65e49d51607307ca47542de815ef9d1698cfd067350e03d3d82fde61ba5ecf6f - md5: 3a1136dda53febf7693145db51db3dcb + size: 269985 + timestamp: 1787225747011 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h72ddc62_1.conda + sha256: c55a7242db95609b6f0b56b49ef2f5c1796060c0a221bbca3d61fbe19bf05add + md5: f5ac3845eb3e5a6c74100e3a7730c110 depends: - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 + - libgcc >=15 + - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 - size: 521190 - timestamp: 1780057179710 + size: 528489 + timestamp: 1786955817362 - conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda sha256: 1226948565ea9fa4fbaefc8c5ff6bb4c66e3cb96a9db71d236dcc0be0bc319cb md5: 5947bdda89ced07ab0d8a5d6503082c3 @@ -7629,164 +5322,65 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA - purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 size: 4094822 timestamp: 1772594360111 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h15fa968_9.conda - sha256: 541eadcc9f2e3f5c7f801265563412930c9c65e22c21634df96a8cd6465a385e - md5: 4957a903bd6a68cc2e53e47476f9c6f4 - depends: - - __glibc >=2.17,<3.0.a0 - - freexl >=2 - - freexl >=2.0.0,<3.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - libgcc-ng >=12 - - librttopo >=1.1.0,<1.2.0a0 - - libsqlite >=3.46.0,<4.0a0 - - libstdcxx-ng >=12 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - sqlite - - zlib - license: MPL-1.1 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - libspatialite >=5.1.0,<5.2.0a0 - size: 3495758 - timestamp: 1722337893853 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - sha256: 1ab603b6ec93933e76027e1f23b21b22b858ba1b56f1e1695ef6fe5e80cb7358 - md5: 062b0ac602fb0adf250e3dfa86f221c4 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libzlib >=1.3.2,<2.0a0 - license: blessing - size: 957849 - timestamp: 1780574429573 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.3-h0c1763c_0.conda - sha256: 365376f4815e5e80def2b3462a2419708b7c292da0da85278386c2618621fff4 - md5: 4aed8e657e9ff156bdbe849b4df44389 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libzlib >=1.3.2,<2.0a0 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.3,<4.0a0 - size: 962119 - timestamp: 1782519076616 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-hf4e2dac_0.conda - sha256: 72023efc207fe681e26b65fc9d668062cf0b4f0eacf3431e6eb099b95c1f2efd - md5: df088a279cd5e6fd2790b4c196434da1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + sha256: f20d70da54e5b31dd4a51fb1efeaafafd5ab6dd8d7ac9d1438eaa2526ac4ed3d + md5: e72bbec309c2b0f37823ee7d4fabfcd3 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - - libgcc >=14 + - libgcc >=15 - libzlib >=1.3.2,<2.0a0 license: blessing - purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 - size: 964200 - timestamp: 1785016112246 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 - md5: eecce068c7e4eddeb169591baac20ac4 + size: 974348 + timestamp: 1787051145557 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + sha256: 7e463000fa1cba3f3a6597b776f6ab301d425a96e3bc20d0d9d76a3b9f237aa3 + md5: 5c526561e1d2a2e071941174eae84557 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.0,<4.0a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 - size: 304790 - timestamp: 1745608545575 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - sha256: dff1058c76ec6b8759e41cefa2508162d00e4a5e6721aa68ec3fd10094e702dc - md5: 5794b3bdc38177caf969dabd3af08549 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc 15.2.0 he0feb66_19 - constrains: - - libstdcxx-ng ==15.2.0=*_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 5852044 - timestamp: 1778269036376 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda - sha256: 79721dd08aeb0ab9e773f1f9ef41cf4e6c17477e3d72319147619045bce05a09 - md5: aed6cf89adc1e9b846e4367ac538e434 + size: 306045 + timestamp: 1786713107897 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + sha256: 40b792b0186c1e8859280a1f6f19a54fc50a11b32724fc7b637009c1a9bd302b + md5: 2f2ef0d96de5bdd8c1270ff22fdf9352 depends: - __glibc >=2.17,<3.0.a0 - - libgcc 16.1.0 ha9f2e26_1 + - libgcc 16.2.0 ha9f2e26_4 constrains: - - libstdcxx-ng ==16.1.0=*_1 + - libstdcxx-ng ==16.2.0=*_4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 6631744 - timestamp: 1785375462643 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - sha256: 0672b6b6e1791c92e8eccad58081a99d614fcf82bca5841f9dfa3c3e658f83b9 - md5: e5ce228e579726c07255dbf90dc62101 - depends: - - libstdcxx 15.2.0 h934c35e_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: - strong: - - libstdcxx - size: 27776 - timestamp: 1778269074600 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda - sha256: 2876ca4463d1b394eb969ce4a84d1620aa63fb8202a6397837d1e45ec76c1208 - md5: c94f06123272d8e129d4acf3a25ffb35 + size: 6613148 + timestamp: 1787618704262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + sha256: 4cdebd87b76cf53a58a08ebd6d15336daa79c5f0aa34d83a895ac503c0203632 + md5: de0dceacf3e33c5fc88e167885dc8274 depends: - - libstdcxx 16.1.0 h934c35e_1 + - libstdcxx 16.2.0 h934c35e_4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: strong: - libstdcxx - size: 28253 - timestamp: 1785375500257 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.20.0-h0e7cc3e_1.conda - sha256: 3e70dfda31a3ce28310c86cc0001f20abb78c917502e12c94285a1337fe5b9f0 - md5: d0ed81c4591775b70384f4cc78e05cd1 - depends: - - __glibc >=2.17,<3.0.a0 - - libevent >=2.1.12,<2.1.13.0a0 - - libgcc-ng >=13 - - libstdcxx-ng >=13 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libthrift >=0.20.0,<0.20.1.0a0 - size: 417404 - timestamp: 1724652349098 + size: 28459 + timestamp: 1787618737021 - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda sha256: af6025aa4a4fc3f4e71334000d2739d927e2f678607b109ec630cc17d716918a md5: b6e326fbe1e3948da50ec29cee0380db @@ -7799,82 +5393,44 @@ packages: - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 size: 423861 timestamp: 1777018957474 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda - sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 - md5: cd5a90476766d53e901500df9215e927 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + sha256: 10e125da82ca93e09191e66e5a94d566b2cf8d4024e2a0db3a9114297447e0d9 + md5: 0907d876f460ebc941ae590338b6102f depends: - __glibc >=2.17,<3.0.a0 - - lerc >=4.0.0,<5.0a0 + - lerc >=4.2.0,<5.0a0 - libdeflate >=1.25,<1.26.0a0 - - libgcc >=14 - - libjpeg-turbo >=3.1.0,<4.0a0 - - liblzma >=5.8.1,<6.0a0 - - libstdcxx >=14 + - libgcc >=15 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libstdcxx >=15 - libwebp-base >=1.6.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND - purls: [] run_exports: weak: - - libtiff >=4.7.1,<4.8.0a0 - size: 435273 - timestamp: 1762022005702 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda - sha256: b31346e1c01ab40a170e91147092ee8fd92b1dee3c66ee47ef025571c879b159 - md5: c1fcb4a88bc15a9f77ad8d27d7af1df9 - depends: - - __glibc >=2.17,<3.0.a0 - - lerc >=4.1.0,<5.0a0 - - libdeflate >=1.25,<1.26.0a0 - - libgcc >=14 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblzma >=5.8.3,<6.0a0 - - libstdcxx >=14 - - libwebp-base >=1.6.0,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: HPND - purls: [] - run_exports: - weak: - - libtiff >=4.7.2,<4.8.0a0 - size: 452337 - timestamp: 1783084902636 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda - sha256: ecbf4b7520296ed580498dc66a72508b8a79da5126e1d6dc650a7087171288f9 - md5: 1247168fe4a0b8912e3336bccdbf98a5 + - libtiff >=4.7.2,<4.8.0a0 + size: 459753 + timestamp: 1787755584172 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + sha256: ecbf4b7520296ed580498dc66a72508b8a79da5126e1d6dc650a7087171288f9 + md5: 1247168fe4a0b8912e3336bccdbf98a5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 size: 85969 timestamp: 1768735071295 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-hf23e847_1.conda - sha256: 104cf5b427fc914fec63e55f685a39480abeb4beb34bdbc77dea084c8f5a55cb - md5: b1aa0faa95017bca11369bd080487ec4 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libutf8proc >=2.8.0,<2.9.0a0 - size: 80852 - timestamp: 1732829699583 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda sha256: 9b1bdce27a7e31f7d241aeecff67a1f3101d52a2b1e33ccc2cdf2613072bf81f md5: 01bb81d12c957de066ea7362007df642 @@ -7883,47 +5439,29 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libuuid >=2.42.2,<3.0a0 size: 40017 timestamp: 1781625522462 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h5279c79_0.conda - sha256: 1e30138cff1e6ba5739ce3ec787b24ef22ac6e2008d8a2073df334e9e5f8690b - md5: 9d8c72f797f6f2d1c32897603d70824c +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda + sha256: a70c25b66321ee77f9892b205e3247263c400803f543dc8aca53d569a59c5a77 + md5: c5f5f159b66332152197893427071695 depends: - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - xorg-libx11 >=1.8.13,<2.0a0 + - libstdcxx >=15 + - libgcc >=15 - xorg-libxrandr >=1.5.5,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 constrains: - libvulkan-headers 1.4.357.0.* license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libvulkan-loader >=1.4.357.0,<2.0a0 - size: 203456 - timestamp: 1785311377294 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b - md5: aea31d2e5b1091feca96fcfe945c3cf9 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - constrains: - - libwebp 1.6.0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libwebp-base >=1.6.0,<2.0a0 - size: 429011 - timestamp: 1752159441324 + size: 206957 + timestamp: 1787491938583 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda sha256: 8415001414f488c85b72b9d8cc2071dfb3981a47bc3c8eb56ef91a57d12eae7f md5: 9332b53d0ea93c5d39e33be03a0c611a @@ -7934,41 +5472,27 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 size: 428430 timestamp: 1785954557217 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa - md5: 92ed62436b625154323d40d5f2f11dd7 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + sha256: 7b49e6fd2a584b7f072943e6adf4149677af5121246975278804782d37752837 + md5: 61f0ffba9161cbb3a77722869b21e4bb depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 + - libgcc >=15 - pthread-stubs - - xorg-libxau >=1.0.11,<2.0a0 - - xorg-libxdmcp + - xorg-libxau >=1.0.12,<2.0a0 + - xorg-libxdmcp >=1.1.5,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 - size: 395888 - timestamp: 1727278577118 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c - md5: 5aa797f8787fe7a17d1b0821485b5adc - depends: - - libgcc-ng >=12 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libxcrypt >=4.4.36 - size: 100393 - timestamp: 1702724383534 + size: 395120 + timestamp: 1787885788278 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda sha256: f7e9292dd219a6435bbb1223da9586c3e70d66d169c5a92f08db3f2127df04e9 md5: f7a7ff5a6ab331e037abd34f379a631d @@ -7976,54 +5500,32 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libxcrypt >=4.4.38 size: 101957 timestamp: 1785887123445 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda - sha256: 23f47e86cc1386e7f815fa9662ccedae151471862e971ea511c5c886aa723a54 - md5: 74e91c36d0eef3557915c68b6c2bef96 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + sha256: d44878d396713ccf3b355df617f61c40a1442fffcf134392bc6ce0e6c2219369 + md5: f888787e0eab7a8076a67d197e155ffc depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - libxcb >=1.17.0,<2.0a0 - - libxml2 >=2.13.8,<2.14.0a0 - xkeyboard-config - - xorg-libxau >=1.0.12,<2.0a0 - license: MIT/X11 Derivative - license_family: MIT - purls: [] - run_exports: - weak: - - libxkbcommon >=1.11.0,<2.0a0 - size: 791328 - timestamp: 1754703902365 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda - sha256: 046f2ff4acebd8729fac03e99c8c307dfb48b6a32894ba8c11576e78f6e76e43 - md5: dc8b067e22b414172bedd8e3f03f3c95 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 - libxcb >=1.17.0,<2.0a0 + - xorg-libxau >=1.0.12,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - - xkeyboard-config - - xorg-libxau >=1.0.12,<2.0a0 - license: MIT/X11 Derivative - license_family: MIT - purls: [] + license: MIT AND MIT-open-group AND HPND AND HPND-sell-variant AND ISC run_exports: weak: - libxkbcommon >=1.13.2,<2.0a0 - size: 851166 - timestamp: 1780213397575 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda - sha256: 3d44f737c5ae52d5af32682cc1530df433f401f8e58a7533926536244127572a - md5: e79d2c2f24b027aa8d5ab1b1ba3061e7 + size: 942571 + timestamp: 1787178780572 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda + sha256: 087d4de023f22d6a28b9e1b818961dd41e9bda6e9793590600ff16ca150bf9cb + md5: 20e4d67ec908f0405124f11612c48305 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 @@ -8035,69 +5537,48 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 559775 - timestamp: 1776376739004 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda - sha256: 5d12e993894cb8e9f209e2e6bef9c90fa2b7a339a1f2ab133014b71db81f5d88 - md5: 35eeb0a2add53b1e50218ed230fa6a02 - depends: - - __glibc >=2.17,<3.0.a0 - - icu >=75.1,<76.0a0 - - libgcc >=14 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.1,<6.0a0 - - libzlib >=1.3.1,<2.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxml2 >=2.13.9,<2.14.0a0 - size: 697033 - timestamp: 1761766011241 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda - sha256: 3bc5551720c58591f6ea1146f7d1539c734ed1c40e7b9f5cb8cb7e900c509aba - md5: 995d8c8bad2a3cc8db14675a153dec2b + size: 559721 + timestamp: 1787237579170 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + sha256: a16a576a5844a3a0e1cdfc5e162b9fe9c64dccf6a93f44c293bb42851aebe2b4 + md5: 2d34cdb31014cbc8f1e9384c89ede0a5 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 hca6bf5a_0 + - libxml2-16 2.15.3 hca6bf5a_1 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 46810 - timestamp: 1776376751152 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda - sha256: adada9c781ea51faea3e3adcd6883dc294ff2fa044c7f4e199430a11862dfa40 - md5: be70cb50dd0ecc1531c58034d38f72e0 + size: 46203 + timestamp: 1787237584107 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda + sha256: 443ff3b29df8eab5b075abed87e4edbf59ca5602f627a79bffe5b0855ea40ce9 + md5: f986d9c9de80813d076d235f753889ff depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h49c6c72_0 - - libxml2-16 2.15.3 hca6bf5a_0 + - libxml2 2.15.3 h49c6c72_1 + - libxml2-16 2.15.3 hca6bf5a_1 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 80529 - timestamp: 1776376762779 + size: 80644 + timestamp: 1787237588477 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 md5: 87e6096ec6d542d1c1f8b33245fe8300 @@ -8108,59 +5589,11 @@ packages: - libxml2-16 >=2.14.6 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 size: 245434 timestamp: 1757963724977 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda - sha256: 35ddfc0335a18677dd70995fa99b8f594da3beb05c11289c87b6de5b930b47a3 - md5: 31059dc620fa57d787e3899ed0421e6d - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libxml2 >=2.13.8,<2.14.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxslt >=1.1.43,<2.0a0 - size: 244399 - timestamp: 1753273455036 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda - sha256: 991e7348b0f650d495fb6d8aa9f8c727bdf52dabf5853c0cc671439b160dce48 - md5: a7b27c075c9b7f459f1c022090697cba - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=13 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libzip >=1.11.2,<2.0a0 - size: 109043 - timestamp: 1730442108429 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 - md5: d87ff7921124eccd67248aa483c23fec - depends: - - __glibc >=2.17,<3.0.a0 - constrains: - - zlib 1.3.2 *_2 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 63629 - timestamp: 1774072609062 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 md5: 0de0122d9570a8ab637c6b73db268389 @@ -8170,88 +5603,53 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 size: 63713 timestamp: 1785362952714 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py312hb3f7f12_1.conda - sha256: ea8151b4f55fa1f9b8d2220c7193c91f545aa15d44415cddbac9ea1f8782c117 - md5: b99d90ef4e77acdab74828f79705a919 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - lz4-c >=1.9.3,<1.10.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping - run_exports: {} - size: 39432 - timestamp: 1725089587134 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py314hd4c109c_1.conda - sha256: 7f3083018be486b73c82e5e2421ab882d5231fcd424843c96058b01ce5f3cbaf - md5: 2f6295571ea5e9278046efc3ef377a98 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + sha256: e8ae9141c7afcc95555fca7ff5f91d7a84f094536715211e750569fd4bb2caa4 + md5: a669145a2c834895bdf3fcba1f1e5b9c depends: - python - lz4-c - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python_abi 3.14.* *_cp314 + - python_abi 3.12.* *_cp312 - lz4-c >=1.10.0,<1.11.0a0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping run_exports: {} - size: 45224 - timestamp: 1765026391393 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda - sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 - md5: 9de5350a85c4a20c685259b889aa6393 + size: 44154 + timestamp: 1765026394687 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + sha256: b8c0e930183e6b7f83cd35ace102f2b8c2f0faae41dc05255dc72f247dfc556a + md5: e38a3253d72ab07d471483f24947e2a2 depends: + - libgcc >=15 + - libstdcxx >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 - size: 167055 - timestamp: 1733741040117 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda - sha256: 1b4c105a887f9b2041219d57036f72c4739ab9e9fe5a1486f094e58c76b31f5f - md5: 318b08df404f9c9be5712aaa5a6f0bb0 - depends: - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - lz4-c >=1.9.4,<1.10.0a0 - size: 143402 - timestamp: 1674727076728 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda - sha256: 5c6bbeec116e29f08e3dad3d0524e9bc5527098e12fc432c0e5ca53ea16337d4 - md5: 45161d96307e3a447cc3eb5896cf6f8c + size: 181812 + timestamp: 1786717122815 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda + sha256: a0a1400198e5302d8367f2ed53437ee3cc41dbc463cb481921ce765578780b7a + md5: 10e0aa58ed390ba598b728badf9eb1b7 depends: - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 - size: 191060 - timestamp: 1753889274283 + size: 191573 + timestamp: 1787049167898 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda sha256: 5f3aad1f3a685ed0b591faad335957dbdb1b73abfd6fc731a0d42718e0653b33 md5: 93a4752d42b12943a355b682ee43285b @@ -8264,8 +5662,6 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 26057 timestamp: 1772445297924 @@ -8281,75 +5677,26 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 27424 timestamp: 1772445227915 -- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda - sha256: cdd59bb1a52b09979f11c68909d53120b2e749edd1992853a74e1604db19c8b0 - md5: 579c6a324b197594fabc9240bddf2d8b - depends: - - matplotlib-base >=3.10.9,<3.10.10.0a0 - - pyside6 >=6.7.2 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - tornado >=5 - license: PSF-2.0 - license_family: PSF - purls: [] - run_exports: {} - size: 17831 - timestamp: 1777000588302 -- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py314h815e797_2.conda - sha256: 5dc6c469ed94cba223272ba70be250b5820e88640c8fdfe4ae85b2966b8d4b67 - md5: 19db05d03d18c1746c2212bc83e9c4c2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda + sha256: 72daeb18e9f82509cf6eec25975324c06681127fb94b9b20b53a620a76457e62 + md5: 26c8cd6cbd98ec961a2f8969e14a6e4d depends: - matplotlib-base >=3.11.1,<3.11.2.0a0 - pyside6 >=6.7.2 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - tornado >=5 - license: PSF-2.0 - license_family: PSF - purls: [] - run_exports: {} - size: 15007 - timestamp: 1785211130117 -- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda - sha256: c7e133837376e53e6a52719c205a3067c42f05769bc3e8307417f8d817dfc63e - md5: 7d499b5b6d150f133800dc3a582771c7 - depends: - - __glibc >=2.17,<3.0.a0 - - contourpy >=1.0.1 - - cycler >=0.10 - - fonttools >=4.22.0 - - freetype - - kiwisolver >=1.3.1 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libgcc >=14 - - libstdcxx >=14 - - numpy >=1.23 - - numpy >=1.23,<3 - - packaging >=20.0 - - pillow >=8 - - pyparsing >=2.3.1 - python >=3.12,<3.13.0a0 - - python-dateutil >=2.7 - python_abi 3.12.* *_cp312 - - qhull >=2020.2,<2020.3.0a0 - - tk >=8.6.13,<8.7.0a0 + - tornado >=5 license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} - size: 8336056 - timestamp: 1777000573501 -- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py314h9a9c090_2.conda - sha256: d76657ece6fef30e44fca988a0a884cf5744f2fb1611814c5c993700148fbefa - md5: 57f668cbc4b70c11ea9d19ebed67295e + size: 14938 + timestamp: 1785211151683 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda + sha256: 8596841a7adf2ff135a7d3a5266727d7a562046f998e8edf9c568a0b20de7ddd + md5: 97eb87f2704fc5212a05b5fb6202ace0 depends: - __glibc >=2.17,<3.0.a0 - contourpy >=1.0.1 @@ -8367,192 +5714,105 @@ packages: - packaging >=20.0 - pillow >=9 - pyparsing >=3 - - python >=3.14,<3.15.0a0 + - python >=3.12,<3.13.0a0 - python-dateutil >=2.7 - - python_abi 3.14.* *_cp314 + - python_abi 3.12.* *_cp312 - qhull >=2020.2,<2020.3.0a0 - tk >=8.6.13,<8.7.0a0 license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} - size: 9068707 - timestamp: 1785211110030 -- conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.0-py314h9e666f3_2.conda - sha256: 6e20ef7149f06bcfc6342b90c617d074141d827128c27b2ccdcb5a79a4bc5dc4 - md5: a9edd3780999970d02f3e0440cf688c5 + size: 8931979 + timestamp: 1785211134185 +- conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_2.conda + sha256: 2c1c0a0ea8804f69dd4361b1f0dd23cdcb951600017de3dfa6ba0510951e3801 + md5: 1a71185007f2ee6fa2ecd5b40f033e0c depends: - python - tomli-w - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT - size: 207080 - timestamp: 1782377704416 -- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda - sha256: 41558b95a387ce2374260aa034a99c3a88cbb98e1a56510f4fa6839063de867b - md5: fe7b4ff5792fee512aad843294ea809a - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=14 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libstdcxx >=14 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.6,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - minizip >=4.2.1,<5.0a0 - size: 520570 - timestamp: 1778002506337 -- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hb71707f_0.conda - sha256: 80398daac34dfe10d88c92ef64f6ff85f52950cab8b2bdfc2d00cc176ed7edff - md5: 88450ba743694055e218d03a2fdd561e + run_exports: {} + size: 207738 + timestamp: 1788033918440 +- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda + sha256: 83837ffc5db1abfc14de88f5c9f07d1a666f149cd95249777f0ed09073ae7c22 + md5: ade84857af069283ff1e3b7aacae1232 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - - libgcc >=14 + - libgcc >=15 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libstdcxx >=14 + - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other - purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 521106 - timestamp: 1782851456704 -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py312h0a2e395_1.conda - sha256: c9764c77dd7f9c581c1d8a24c3024edf777cacfb5a4180de5badc6638dc8590f - md5: a142257c1b69e37cfefc66dc228a4d93 + size: 526042 + timestamp: 1788051267376 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda + sha256: ecc6920ef31af5a646ee162dd5ee0b0096a5b9ec07a6377aaf6715a0b9dd502d + md5: 881c695332c119ffd74b0a856f2c8fba depends: - python + - libstdcxx >=15 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - python_abi 3.12.* *_cp312 license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping run_exports: {} - size: 112800 - timestamp: 1782460774570 -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h97ea11e_1.conda - sha256: f33032ef3dcb8759309e8a55b0cb989e377ce315122bb86b00c0adbcf6665abf - md5: 1c8698b2012012806ec17a062bb9f339 + size: 114265 + timestamp: 1787850881233 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_0.conda + sha256: ab2c6627819bdafc603763f599e65383879a52d4f17073baa0ea9a5920dd15a1 + md5: 66e5d65a13ac23cd29ca87eb858c7d27 depends: - python - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 + - libstdcxx >=15 + - libgcc >=15 - python_abi 3.14.* *_cp314 license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping run_exports: {} - size: 113343 - timestamp: 1782460776477 -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py314h9891dd4_0.conda - sha256: bbbb210e90dbd8c9ddba24730aca81aefe23f5ecaf79a81b6740e0dbd25f7b21 - md5: 9b67bf9c63607097b6e010a7b555a388 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: Apache - size: 103513 - timestamp: 1782070374726 -- conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda - sha256: 320dfc59a94cb9e3635bda71b9e62278b34aa2fdaea0caa6832ddb9b37e9ccd5 - md5: ab3e3db511033340e75e7002e80ce8c0 + size: 115256 + timestamp: 1787850879389 +- conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda + sha256: c9322797c769560e96025d294f0daacbcf7ed95ae68903dd2e398ffb87fa2c73 + md5: 7de84fd76ec2bdb7ac617507de3d3052 depends: + - libstdcxx >=15 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 license: MIT - license_family: MIT - purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 - size: 203174 - timestamp: 1747116762269 -- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.1.0-py314h518bba1_0.conda - sha256: 9fcf9e7343c30fdeab75e8440caa2814798eeff22a6dc5f7a556df8372e1b0ca - md5: 5625fce7423d7579e60f9cbcc5738d2b + size: 233133 + timestamp: 1788122511702 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hc824af9_0.conda + sha256: fc92573d64890aa2b33f2ce52d35a8816569aa1423e7ec467271e608154328ce + md5: 98c8f0218a5ba4e6d9c7b8bbd9e80078 depends: - - ast-serialize >=0.3.0,<1.0.0 + - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 - pathspec >=1.0.0 - python - - python-librt >=0.11.0 + - python-librt >=0.13.0 - typing_extensions >=4.6.0 - psutil >=4.0 - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 22860519 - timestamp: 1780315602311 -- conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda - sha256: 9c2e3f9e9883e4b8d7e9e6abf7b235dc00bdcd5ef66640a360464a9f5756294d - md5: 94116b69829e90b72d566e64421e1bff - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - - openssl >=3.4.1,<4.0a0 - license: GPL-2.0-or-later - license_family: GPL - purls: [] - run_exports: {} - size: 616215 - timestamp: 1744124836761 -- conda: https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda - sha256: 274467a602944d12722f757f660ad034de6f5f5d7d2ea1b913ef6fd836c1b8ce - md5: 9802ae6d20982f42c0f5d69008988763 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - - mysql-common 9.0.1 h266115a_6 - - openssl >=3.4.1,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: GPL-2.0-or-later - license_family: GPL - purls: [] run_exports: {} - size: 1369369 - timestamp: 1744124916632 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - sha256: fc89f74bbe362fb29fa3c037697a89bec140b346a2469a90f7936d1d7ea4d8a3 - md5: fc21868a1a5aacc937e7a18747acb8a5 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: X11 AND BSD-3-Clause - purls: [] - run_exports: - weak: - - ncurses >=6.6,<7.0a0 - size: 918956 - timestamp: 1777422145199 + size: 23721948 + timestamp: 1786887255055 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 md5: ee6c0cd80a60961a1f48aa3e0b91f986 @@ -8560,7 +5820,6 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: X11 AND BSD-3-Clause - purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -8573,109 +5832,52 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 136372 timestamp: 1785920438981 -- conda: https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda - sha256: e3664264bd936c357523b55c71ed5a30263c6ba278d726a75b1eb112e6fb0b64 - md5: e235d5566c9cc8970eb2798dd4ecf62f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - license: MPL-2.0 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - nspr >=4.38,<5.0a0 - size: 228588 - timestamp: 1762348634537 -- conda: https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda - sha256: 44dd98ffeac859d84a6dcba79a2096193a42fc10b29b28a5115687a680dd6aea - md5: 567fbeed956c200c1db5782a424e58ee +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda + sha256: be778735f693d6c1e9b877d7a28fd7d9e8c0d695c5b93a4dd5b8ec4a4317ca13 + md5: e70abe6ab4c60ecb6a51e67903bcec07 depends: + - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libsqlite >=3.51.0,<4.0a0 - - libstdcxx >=14 - - libzlib >=1.3.1,<2.0a0 - - nspr >=4.38,<5.0a0 - license: MPL-2.0 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - nss >=3.118,<4.0a0 - size: 2057773 - timestamp: 1763485556350 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda - sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 - md5: d8285bea2a350f63fab23bf460221f3f - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - libgcc-ng >=12 + - libgcc >=15 + - libstdcxx >=15 - liblapack >=3.9.0,<4.0a0 - - libstdcxx-ng >=12 - - python >=3.12,<3.13.0a0 + - libcblas >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 + - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - - numpy >=1.26.4,<2.0a0 - size: 7484186 - timestamp: 1707225809722 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py314h2b28147_0.conda - sha256: bbc665584886c90daf3f33cfbf665f279cf91d4bd5323f0432c16d2bf4d525e7 - md5: bdb21d2b990f9d3aee10fd43aca851fe + - numpy >=1.25,<3 + size: 9212503 + timestamp: 1788129260074 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314hd3a7d6b_1.conda + sha256: d1d5c1e8025bb2389ade51a717f73d34f72b3fb4c6858f54ca637465b3941502 + md5: 8b80e8395626350a957f3b7ad1348cbe depends: - python - - libgcc >=14 - - libstdcxx >=14 - __glibc >=2.17,<3.0.a0 - - libcblas >=3.9.0,<4.0a0 + - libgcc >=15 + - libstdcxx >=15 - libblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 + - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause - license_family: BSD - size: 9075918 - timestamp: 1782112541752 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314h2b28147_0.conda - sha256: 124b753583ea9c157301fe78de3e88aa5fa8806bd2da8abaa8808065d1b93d51 - md5: d77631addad93399a90157d2c597e7f3 - depends: - - python - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - - libblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping - run_exports: - weak: - - numpy >=1.25,<3 - size: 9119694 - timestamp: 1786330625923 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d - md5: 11b3379b191f63139e29c0d19dee24cd + run_exports: + weak: + - numpy >=1.25,<3 + size: 9372636 + timestamp: 1788129259187 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -8685,30 +5887,11 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - openjpeg >=2.5.4,<3.0a0 size: 355400 timestamp: 1758489294972 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda - sha256: cb0b07db15e303e6f0a19646807715d28f1264c6350309a559702f4f34f37892 - md5: 2e5bf4f1da39c0b32778561c3c4e5878 - depends: - - __glibc >=2.17,<3.0.a0 - - cyrus-sasl >=2.1.27,<3.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libgcc >=13 - - libstdcxx >=13 - - openssl >=3.5.0,<4.0a0 - license: OLDAP-2.8 - license_family: BSD - purls: [] - run_exports: - weak: - - openldap >=2.6.10,<2.7.0a0 - size: 780253 - timestamp: 1748010165522 - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda sha256: 21c4f6c7f41dc9bec2ea2f9c80440d9a4d45a6f2ac13243e658f10dcf1044146 md5: 680608784722880fbfe1745067570b00 @@ -8721,63 +5904,25 @@ packages: - openssl >=3.5.6,<4.0a0 license: OLDAP-2.8 license_family: BSD - purls: [] run_exports: weak: - openldap >=2.6.13,<2.7.0a0 size: 786149 timestamp: 1775741359582 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_0.conda - sha256: d48f5c22b9897c01e4dff3680f1f57ceb02711ab9c62f74339b080419dfad34b - md5: 79dd2074b5cd5c5c6b2930514a11e22d - depends: - - __glibc >=2.17,<3.0.a0 - - ca-certificates - - libgcc >=14 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 3159683 - timestamp: 1781069855778 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda - sha256: 012096056b97abf1f68c46b7146bd2cbd68c1be762340b4f5dad4fbbe99177bc - md5: c5955c27917ff2234def47f075e71e02 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + sha256: 4747b2d6a8336f52343bceb8a1ebf41a9e6e665b9d2e3f989972de53a310599e + md5: 16e3034a330cc625f2c685edec60cf4e depends: - __glibc >=2.17,<3.0.a0 - ca-certificates - - libgcc >=14 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 3182423 - timestamp: 1785913583650 -- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h669347b_0.conda - sha256: 8a126e0be7f87c499f0a9b5229efa4321e60fc4ae46abdec9b13240631cb1746 - md5: 1e6c10f7d749a490612404efeb179eb8 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libstdcxx-ng >=12 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.1,<1.3.0a0 - - tzdata - - zstd >=1.5.6,<1.6.0a0 + - libgcc >=15 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - - orc >=2.0.2,<2.0.3.0a0 - size: 1066349 - timestamp: 1723760593232 + - openssl >=3.6.4,<4.0a0 + size: 3202955 + timestamp: 1787698780103 - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda sha256: ae96bc0895a2279f2ea296879e0475333e838cc88754b0c1a4903dd92a7e1b21 md5: 1280a5f8270f30b89cc8373ecfe8899d @@ -8795,24 +5940,23 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 size: 1458252 timestamp: 1784301236872 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda - sha256: 009408dcfdc789b8a1748d6a63fd2134ea2edc8474231ea7beba0ac3ad772a37 - md5: 15c437bfa4cbddd379b95357c9aa4150 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda + sha256: 393e529c0574c020a9b790275af10ff35e21cbb4e12740888bd3f214f2f07034 + md5: 85eb29ade84d1bd97be5ec55607efb00 depends: - python - numpy >=1.26.0 - python-dateutil >=2.8.2 + - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.12.* *_cp312 - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 constrains: - adbc-driver-postgresql >=1.2.0 - adbc-driver-sqlite >=1.2.0 @@ -8854,66 +5998,9 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping run_exports: {} - size: 14872605 - timestamp: 1778602625175 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py314hb4ffadd_0.conda - sha256: 8e4b161f3f7fbdf17f842b518ff3794b6af9378a90d095719d7153360d126dc1 - md5: bc2e1390314b1269e66fb1966fbcae5d - depends: - - python - - numpy >=1.26.0 - - python-dateutil >=2.8.2 - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - numpy >=1.23,<3 - - python_abi 3.14.* *_cp314 - constrains: - - adbc-driver-postgresql >=1.2.0 - - adbc-driver-sqlite >=1.2.0 - - beautifulsoup4 >=4.12.3 - - blosc >=1.21.3 - - bottleneck >=1.4.2 - - fastparquet >=2024.11.0 - - fsspec >=2024.10.0 - - gcsfs >=2024.10.0 - - html5lib >=1.1 - - hypothesis >=6.116.0 - - jinja2 >=3.1.5 - - lxml >=5.3.0 - - matplotlib >=3.9.3 - - numba >=0.60.0 - - numexpr >=2.10.2 - - odfpy >=1.4.1 - - openpyxl >=3.1.5 - - psycopg2 >=2.9.10 - - pyarrow >=13.0.0 - - pyiceberg >=0.8.1 - - pymysql >=1.1.1 - - pyqt5 >=5.15.9 - - pyreadstat >=1.2.8 - - pytables >=3.10.1 - - pytest >=8.3.4 - - pytest-xdist >=3.6.1 - - python-calamine >=0.3.0 - - pytz >=2024.2 - - pyxlsb >=1.0.10 - - qtpy >=2.4.2 - - scipy >=1.14.1 - - s3fs >=2024.10.0 - - sqlalchemy >=2.0.36 - - tabulate >=0.9.0 - - xarray >=2024.10.0 - - xlrd >=2.0.1 - - xlsxwriter >=3.2.0 - - zstandard >=0.23.0 - license: BSD-3-Clause - license_family: BSD - size: 15303815 - timestamp: 1778602611222 + size: 14936796 + timestamp: 1785276227779 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda sha256: b8197d816dfef2e237c45e875d7e37d125c1e12af819a911dcfd95c2cb98a010 md5: 15cd0f375b79b47c4048e44396e6e6f1 @@ -8967,127 +6054,69 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 15360863 timestamp: 1785276230218 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hda50119_1.conda - sha256: 315b52bfa6d1a820f4806f6490d472581438a28e21df175290477caec18972b0 - md5: d53ffc0edc8eabf4253508008493c5bc +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + sha256: 48f27a6c3e4062bc09dafe9c7f6b288c5de5655e81095ab7f1aad920b2163b7b + md5: 6a2822aaf9a34ac3708904a47ff3dd7e depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.17.1,<3.0a0 + - fontconfig >=2.18.2,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - - harfbuzz >=13.2.1 - - libexpat >=2.7.4,<3.0a0 - - libfreetype >=2.14.2 - - libfreetype6 >=2.14.2 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 - libgcc >=14 - - libglib >=2.86.4,<3.0a0 - - libpng >=1.6.55,<1.7.0a0 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later - size: 458036 - timestamp: 1774281947855 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda - sha256: 09717569649d89caafbf32f6cda1e65aef86e5a86c053d30e4ce77fca8d27b68 - md5: 31614c73d7b103ef76faa4d83d261d34 - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=13 - - libzlib >=1.3.1,<2.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - pcre2 >=10.44,<10.45.0a0 - size: 956207 - timestamp: 1745931215744 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff - md5: 7a3bff861a6583f1889021facefc08b1 + - pango >=1.58.2,<2.0a0 + size: 469916 + timestamp: 1786107384537 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + sha256: 9ebb10a4eb3be51ae67d85c679f5a7a50cb040ed25c783e6331a225ea09991d4 + md5: 06f6113cf1ff4a54b65f87ead132a5f1 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - - libgcc >=14 - - libzlib >=1.3.1,<2.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 - size: 1222481 - timestamp: 1763655398280 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda - sha256: fa291f8915114733dc1df9f1627b8c63c517217c1eee1a6ede2ceb5e368cf27a - md5: 9e5609720e31213d4f39afe377f6217e + size: 1218833 + timestamp: 1787294571916 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda + sha256: 76b2e56c7a903a06be1210d35f35c2a8dcdc57912fda5c96b2e68acfd2b281fe + md5: d749d04e1965315078f29320565c595a depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - - lcms2 >=2.18,<3.0a0 - - libxcb >=1.17.0,<2.0a0 - - libjpeg-turbo >=3.1.2,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - openjpeg >=2.5.4,<3.0a0 - - python_abi 3.12.* *_cp312 + - libxcb >=1.17.0,<2.0a0 - tk >=8.6.13,<8.7.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - zlib-ng >=2.3.3,<2.4.0a0 - license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping - run_exports: {} - size: 1039561 - timestamp: 1775060059882 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py314h8ec4b1a_0.conda - sha256: bcab128df8980514061a78b9c67f5004954048f584da20df8c5a78de9e3f5abb - md5: 233e62a8eb894b79b5c93f4f8dec4dcd - depends: - - python - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libtiff >=4.7.1,<4.8.0a0 - openjpeg >=2.5.4,<3.0a0 - - libxcb >=1.17.0,<2.0a0 - zlib-ng >=2.3.3,<2.4.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - python_abi 3.14.* *_cp314 + - python_abi 3.12.* *_cp312 - libjpeg-turbo >=3.1.4.1,<4.0a0 - lcms2 >=2.19.1,<3.0a0 - - tk >=8.6.13,<8.7.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libwebp-base >=1.6.0,<2.0a0 license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping run_exports: {} - size: 1108174 + size: 1064129 timestamp: 1782912080163 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda - sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a - md5: c01af13bdc553d1a8fbfff6e8db075f0 - depends: - - libgcc >=14 - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - pixman >=0.46.4,<1.0a0 - size: 450960 - timestamp: 1754665235234 - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda sha256: 829d8288764282de5a9f7b9169acb75cc7dc0b6c3fe2535cfe87dea3436bbc5d md5: 0ee5bb30034b081a1386c1e2c98ab0a7 @@ -9097,88 +6126,11 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 size: 376704 timestamp: 1786106621354 -- conda: https://conda.anaconda.org/conda-forge/linux-64/poppler-24.08.0-h47131b8_1.conda - sha256: b32fe787525236908e21885fef8d77e8ebdbbe6694b2fb89ed799444ebda3178 - md5: 0854b9ff0cc10a1f6f67b0f352b8e75a - depends: - - __glibc >=2.17,<3.0.a0 - - cairo >=1.18.0,<2.0a0 - - fontconfig >=2.14.2,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - lcms2 >=2.16,<3.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgcc-ng >=13 - - libglib >=2.80.3,<3.0a0 - - libiconv >=1.17,<2.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libstdcxx-ng >=13 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - nspr >=4.35,<5.0a0 - - nss >=3.103,<4.0a0 - - openjpeg >=2.5.2,<3.0a0 - - poppler-data - license: GPL-2.0-only - license_family: GPL - purls: [] - run_exports: {} - size: 1907007 - timestamp: 1724659640508 -- conda: https://conda.anaconda.org/conda-forge/linux-64/postgresql-17.2-h1122569_0.conda - sha256: ccfd1577944940f1f643cfbe71046ac0c18f3262496bed71699b24856debe1dd - md5: 848402b976b31bfecb3e476ea85cb285 - depends: - - __glibc >=2.17,<3.0.a0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libgcc >=13 - - libpq 17.2 h04577a9_0 - - libxml2 >=2.13.5,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openldap >=2.6.8,<2.7.0a0 - - openssl >=3.4.0,<4.0a0 - - readline >=8.2,<9.0a0 - - tzcode - - tzdata - - zstd >=1.5.6,<1.6.0a0 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 5578100 - timestamp: 1732204587837 -- conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.4.1-h54d7996_1.conda - sha256: 7e5aa324f89eece539001daa8df802d1b5851caee4be41b99ffe3b6e168993a9 - md5: e479d1991c725e1a355f33c0e40dbc66 - depends: - - __glibc >=2.17,<3.0.a0 - - libcurl >=8.9.0,<9.0a0 - - libgcc-ng >=12 - - libsqlite >=3.46.0,<4.0a0 - - libstdcxx-ng >=12 - - libtiff >=4.6.0,<4.8.0a0 - - sqlite - constrains: - - proj4 ==999999999999 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - proj >=9.4.1,<9.5.0a0 - size: 3050689 - timestamp: 1722327846022 - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda sha256: dff6f355025b9a510d9093e29fd970fa1091e758b848c9dec814d96ae63a09ba md5: b23619e5e9009eaa070ead0342034027 @@ -9196,7 +6148,6 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT - purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -9214,42 +6165,37 @@ packages: - zlib license: MIT license_family: MIT - purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 199544 timestamp: 1730769112346 -- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda - sha256: d834fd656133c9e4eaf63ffe9a117c7d0917d86d89f7d64073f4e3a0020bd8a7 - md5: dd94c506b119130aef5a9382aed648e7 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda + sha256: 2c6e88ac7aa5384fb843cb96023deb4382d6d5afbc81f5db1c7a1ef09c9e3090 + md5: 1653ce584b7859810f116e592eba9c0c depends: - python - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping run_exports: {} - size: 225545 - timestamp: 1769678155334 -- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda - sha256: f15574ed6c8c8ed8c15a0c5a00102b1efe8b867c0bd286b498cd98d95bd69ae5 - md5: 4f225a966cfee267a79c5cb6382bd121 + size: 225418 + timestamp: 1787417371835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_1.conda + sha256: d4c4c9e1bcaeb38e4c4b6a17e8e0b25f8083e03d11813b872427bd47c9bfe1ca + md5: 1dadeb3cb86afd90e106395730cd87aa depends: - python - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping run_exports: {} - size: 231303 - timestamp: 1769678156552 + size: 231304 + timestamp: 1787417370367 - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda sha256: afc3b27b2cbb0487c1d0e963f96e71181ecfb623a24fb393bb19ff974a6382a1 md5: df2c27f36bdb0dde779f55b5df76a352 @@ -9258,22 +6204,9 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: {} size: 9115 timestamp: 1786067714761 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 - md5: b3c17d95b5a10c6e64a21fa17573e70e - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 8252 - timestamp: 1726802366959 - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda sha256: 32354767a80f6650ad043356a975e7562c7286f7b2cdbb2acfe15bd057508e2f md5: 057e330e50d141ee706f1d4c46594979 @@ -9284,88 +6217,45 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 230470 timestamp: 1757853295064 -- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310h70157a2_1.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_3.conda noarch: python - sha256: 94b274e855ddf2ee50442424a1f6a9d6ac5077d1eaf2490ee36e5dd447ae9ada - md5: acec063784813869c885cc81a31bc2ae + sha256: 673052cec285da1db0198313277b077103b8f235b12b9564cce8af5222b4af70 + md5: 2a75e016bb35e33e2acb65ee2aa4f0af depends: - python - - libgcc >=14 - __glibc >=2.17,<3.0.a0 - - openssl >=3.5.6,<4.0a0 + - libgcc >=15 - _python_abi3_support 1.* - cpython >=3.10 + - openssl >=3.5.8,<4.0a0 constrains: - __glibc >=2.17 license: BSD-3-Clause - license_family: BSD - size: 12191860 - timestamp: 1781020717312 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda - sha256: 9e1baddd1199e244f4f8be10c7250691088948583ab3955c510b90eb71c91a2c - md5: 5f7d505626cb057e1320bbd46dd02ef2 - depends: - - libarrow-acero 17.0.0.* - - libarrow-dataset 17.0.0.* - - libarrow-substrait 17.0.0.* - - libparquet 17.0.0.* - - numpy >=1.19,<3 - - pyarrow-core 17.0.0 *_2_* - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - purls: [] run_exports: {} - size: 25538 - timestamp: 1730169714708 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py314hdafbbf9_0.conda - sha256: fe0e85d85a9609c62a981ca4beb02a83c363fc86774fb49cbec6d854c46af664 - md5: 2cd4fd80bec78a128b26fe81c4d07ead + size: 12066610 + timestamp: 1787913197340 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + sha256: 9b6c4aed5da2a3b8f649e1b73596e0867a5506bed9c3aa45dbaedfb674822191 + md5: 6f1918b7565c3cc5ed047a97ea510e55 depends: - libarrow-acero 25.0.0.* - libarrow-dataset 25.0.0.* - libarrow-substrait 25.0.0.* - libparquet 25.0.0.* - pyarrow-core 25.0.0 *_0_* - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: {} - size: 26040 - timestamp: 1784258391200 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h01725c0_2_cpu.conda - build_number: 2 - sha256: e5ed32ee7664a6322263e446d3504a35ff6f5452b17f1161bce7922d03f3cbd4 - md5: add603bfa43d9bf3f06783f780e1a817 - depends: - - __glibc >=2.17,<3.0.a0 - - libarrow 17.0.0.* *cpu - - libgcc >=13 - - libstdcxx >=13 - - libzlib >=1.3.1,<2.0a0 - - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - constrains: - - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} - size: 4607408 - timestamp: 1730169265797 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py314h969be7f_0_cpu.conda - sha256: 910e3c1920477e6d13589e106086f6fd65e40d6e846f88814da413d369c5fad3 - md5: 4ab67ab85baaa448bae03e6d9de3b4a7 + size: 26079 + timestamp: 1784258397971 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + sha256: 8b54b732de45d85198e8e9ce5d5b5642c847f4379f917c64a4de48643a3305ee + md5: aff56abfcef5a031603250b7dc23e9d5 depends: - __glibc >=2.17,<3.0.a0 - libarrow 25.0.0.* *cpu @@ -9373,88 +6263,62 @@ packages: - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.2,<2.0a0 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 constrains: - numpy >=1.23,<3 - apache-arrow-proc * cpu license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} - size: 5466581 - timestamp: 1784258329989 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_3.conda - sha256: ccef6174b89815de1ede61b3b20ebccfe301865f2954d789e0b5c1e52dd45f91 - md5: be49bb746ad2cd2ba6737b4afd6cf32a + size: 5395858 + timestamp: 1784258391353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda + sha256: 0d3cbeb41afef769fe2d8ab7e0e6a2140c8577018ae5e237839b2eae9d571747 + md5: 0fa51561c73d1f362997f8aceab87146 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 88644 - timestamp: 1757744868118 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda - sha256: b8260660d064fb947f4b573ec4a782696bc8b19042452eaa4e9bb1152b540555 - md5: dfb9a57535eb8c35c6744da7043063f0 + run_exports: {} + size: 88896 + timestamp: 1784146233775 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda + sha256: a805a64e0e7e5ed22e8f49f0038a5d8cab4b96cf094c8822da75d6823f2377e8 + md5: 2854d36e093d53d0cf81ec433ffd9c4c depends: - python - typing-extensions >=4.6.0,!=4.7.0 - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 constrains: - __glibc >=2.17 license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} - size: 1895409 - timestamp: 1778084226169 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda - sha256: 802e216c39f1359aed60823b6e11d8ccd812b0ae1c81ae5ac1c81f99446409ab - md5: 0c96993dbeadf3a277cf757b9f1c9412 + size: 1877711 + timestamp: 1787928982283 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py314h7e8cd81_1.conda + sha256: 85154e3129bb93421f9307d45bc37f775a492978dfec5a260623b52cb5ed983c + md5: 2b163c163407eaaa862002a9ceb8ff11 depends: - python - typing-extensions >=4.6.0,!=4.7.0 - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 constrains: - __glibc >=2.17 license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping - run_exports: {} - size: 1895020 - timestamp: 1778084229247 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.conda - sha256: cb0647597ae54d0007e698149f6ca519d80c389daee0f4fef149fe9902a0b31b - md5: 309f7524c82d168cc055e7b136713693 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libgdal-core >=3.9.2,<3.10.0a0 - - libstdcxx >=13 - - numpy - - packaging - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} - size: 639343 - timestamp: 1727771812389 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py314hbcf5174_0.conda - sha256: 53b72845bc9051b1b94c83ed06047788cdb07af529b9368393ac1a9eb720ac21 - md5: b6696a3d5c567d3b2015bf77f454f247 + size: 1876160 + timestamp: 1787928980353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda + sha256: 2b0a366d9066e3d9f495369b95cdb1b9d3dba2f59577e4560b7d1086e1fe3d70 + md5: f8e7e5ddfbdca16b65335b0b6615eb4c depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -9462,197 +6326,119 @@ packages: - libstdcxx >=14 - numpy - packaging - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping - run_exports: {} - size: 667940 - timestamp: 1764402531595 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py312h9211aeb_9.conda - sha256: 9525e8363d4b5509c7520f8b47a7011c0d1e8f66b294f17679093a3d15af086c - md5: 173afeb0d112c854fd1a9fcac4b5cce3 - depends: - - __glibc >=2.17,<3.0.a0 - - certifi - - libgcc >=13 - - proj >=9.4.1,<9.5.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: MIT license_family: MIT - purls: - - pkg:pypi/pyproj?source=hash-mapping run_exports: {} - size: 547934 - timestamp: 1725436149519 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py314h7ce3bca_5.conda - sha256: 63c864c34287c09518e6d3bb78443226939dce29308e152793ba864cef5c7b04 - md5: ebb3897b4acfe36f029e8f7c4a3e861c + size: 661347 + timestamp: 1764402531050 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda + sha256: 8416187ef4766eb2de7bad6e751f0aced2f773c3853057c0eb848b345fedc1aa + md5: 42e2d36478a1fcc0dbc6358ffdb46786 depends: - python - proj - certifi - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 - proj >=9.8.1,<9.9.0a0 - - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT - purls: - - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} - size: 573847 - timestamp: 1786351881422 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py314h3987850_1.conda - sha256: e410d0d4151f418dc75ea2dc38dfb0e7a136090b6874e5ca1c699fa840b4994d - md5: 5d2051f0630a568926943fc53c0aaa4c + size: 572689 + timestamp: 1787945032861 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda + sha256: 4e25d9259683126c377b219a741e081f7b8daa63aa256435d037cdaa3f453589 + md5: bfcdbf7dfa025bf855ffbf44181c143e depends: - python - - qt6-main 6.11.1.* - - libstdcxx >=14 - - libgcc >=14 + - qt6-main 6.11.2.* - __glibc >=2.17,<3.0.a0 - - libvulkan-loader >=1.4.341.0,<2.0a0 - - libgl >=1.7.0,<2.0a0 + - libgcc >=15 + - libstdcxx >=15 + - python_abi 3.12.* *_cp312 + - libclang13 >=22.1.8 - libopengl >=1.7.0,<2.0a0 - - libxslt >=1.1.43,<2.0a0 + - libgl >=1.7.0,<2.0a0 - libegl >=1.7.0,<2.0a0 - - python_abi 3.14.* *_cp314 - - qt6-main >=6.11.1,<6.12.0a0 - - libclang13 >=22.1.5 - libxml2 - libxml2-16 >=2.14.6 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - qt6-main >=6.11.2,<7.0a0 + - libxslt >=1.1.43,<2.0a0 license: LGPL-3.0-only license_family: LGPL - purls: - - pkg:pypi/pyside6?source=hash-mapping - - pkg:pypi/shiboken6?source=hash-mapping - run_exports: {} - size: 13821776 - timestamp: 1778933872780 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda - sha256: 6569b51ed41e46957e6587a705a930681375accd737c677acae883089f50a2a8 - md5: 8baf6a8672bf235afede64de7a7da1c2 - depends: - - __glibc >=2.17,<3.0.a0 - - libclang13 >=19.1.7 - - libegl >=1.7.0,<2.0a0 - - libgcc >=13 - - libgl >=1.7.0,<2.0a0 - - libopengl >=1.7.0,<2.0a0 - - libstdcxx >=13 - - libxml2 >=2.13.6,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - qt6-main 6.8.2.* - - qt6-main >=6.8.2,<6.9.0a0 - license: LGPL-3.0-only - license_family: LGPL - purls: - - pkg:pypi/pyside6?source=hash-mapping - - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} - size: 10898802 - timestamp: 1740757230072 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e - md5: 7eccb41177e15cc672e1babe9056018e + size: 13943954 + timestamp: 1787219437874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda + sha256: ceb9c724de53ee3560f121dbfcb00fe8acb22c08691158fce7b6c32425855626 + md5: e9dcdd23a1c68738eb3257d23d5f7285 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - - libexpat >=2.7.4,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 - libgcc >=14 - - liblzma >=5.8.2,<6.0a0 + - liblzma >=5.8.3,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - - libsqlite >=3.51.2,<4.0a0 - - libuuid >=2.41.3,<3.0a0 - - libxcrypt >=4.4.36 - - libzlib >=1.3.1,<2.0a0 - - ncurses >=6.5,<7.0a0 - - openssl >=3.5.5,<4.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.2,<3.0a0 + - libxcrypt >=4.4.38 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.7,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 - purls: [] run_exports: weak: - python_abi 3.12.* *_cp312 noarch: - python - size: 31608571 - timestamp: 1772730708989 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-h242f9ac_102_cp314.conda - build_number: 102 - sha256: f5ff5c1fac471dfed4fc4288856a63eb9d771e6042d9f70420d75b9f488400d8 - md5: 9b6c336ef7195fbee1c10c09bfcf901b + size: 31590137 + timestamp: 1787353586056 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_104_cp314.conda + build_number: 104 + sha256: 5aaf3af8d4f99541fef4e746ae58677acda6ce02cfa5751abc3d1cc29ea8f732 + md5: 663015cba592a7375ca3c465af84186d depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.8.1,<3.0a0 - libffi >=3.7.0,<3.8.0a0 - - libgcc >=14 + - libgcc >=15 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.7 hdc7f604_104_cp314 - libsqlite >=3.53.4,<4.0a0 - libuuid >=2.42.2,<3.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 - purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 noarch: - python - size: 36866750 - timestamp: 1786444737142 - python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.6-habeac84_100_cp314.conda - build_number: 100 - sha256: 6d28ac2b061179deb434d3d57afa98ffd20ec3c5d44ab8048a1ca33424b22d38 - md5: 0b9b2f83b5b600e1ac38becde8d0dd44 - depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - ld_impl_linux-64 >=2.36.1 - - libexpat >=2.8.1,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - libgcc >=14 - - liblzma >=5.8.3,<6.0a0 - - libmpdec >=4.0.0,<5.0a0 - - libsqlite >=3.53.2,<4.0a0 - - libuuid >=2.42.1,<3.0a0 - - libzlib >=1.3.2,<2.0a0 - - ncurses >=6.6,<7.0a0 - - openssl >=3.5.7,<4.0a0 - - python_abi 3.14.* *_cp314 - - readline >=8.3,<9.0a0 - - tk >=8.6.13,<8.7.0a0 - - tzdata - - zstd >=1.5.7,<1.6.0a0 - license: Python-2.0 - size: 36717183 - timestamp: 1781255094700 + size: 26435588 + timestamp: 1788161824322 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.11.0-py314h0f05182_0.conda - sha256: 4529fdc71dcaa13ad25c4708751029c56f507f7d26f1748f20875cea85a158fa - md5: 1c6a332e01cd8f81f350434fbf7bcaad +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda + sha256: 2655bda3e0f53863c9ba1239aa9212b399141582074d0c043df6f449ff21b0f1 + md5: 102786d7a63373e0441273b7d863d4ea depends: - python - libgcc >=14 @@ -9660,8 +6446,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 156252 - timestamp: 1778511622812 + run_exports: {} + size: 162846 + timestamp: 1786328851615 - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda sha256: 17cd445cbdf0a33a247934497c867195edc2bf51faad8011c74332460d47634b md5: 711ddd4f498e10b56f687683ae76e059 @@ -9672,8 +6459,6 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 291078 timestamp: 1778688806850 @@ -9688,8 +6473,6 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 198293 timestamp: 1770223620706 @@ -9704,30 +6487,26 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 202391 timestamp: 1770223462836 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_3.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.2.0-py312h8a5ba0d_0.conda noarch: python - sha256: 970b2a1d12983d8d1cc05d914ad88a0b6ef1fa14038c9649aa834dd6ebee65d7 - md5: acd216255e1370e9aeab5351b831f07c + sha256: 9e8b94a2c9b4479cac80def117178a1658b089b9e5d4ee64408d2e4ff89fdaba + md5: ceffbc006d87e8f81e750cebd63fd8c4 depends: - python - - libgcc >=14 - - libstdcxx >=14 + - libstdcxx >=15 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 + - zeromq >=4.3.5,<4.4.0a0 - _python_abi3_support 1.* - cpython >=3.12 - - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} - size: 210896 - timestamp: 1779483879367 + size: 214050 + timestamp: 1787300896477 - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc md5: 353823361b1d27eb3960efb076dfcaf6 @@ -9736,15 +6515,14 @@ packages: - libgcc-ng >=12 - libstdcxx-ng >=12 license: LicenseRef-Qhull - purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 size: 552937 timestamp: 1720813982144 -- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda - sha256: aefbc43bde188ff4027d480da99c7fa9e8e6341e9762e065190239cb9b99bb1c - md5: 331d660aef48fec733a878dd1f8f4206 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda + sha256: 00d9bc98d04aeeab6f698ca7fe9d666b157ab39b52cad1af0fa346522705668d + md5: 2ff98660281d1c6bfebda5bffa52cc89 depends: - libxcb - xcb-util @@ -9755,165 +6533,71 @@ packages: - xcb-util-cursor - libgl-devel - libegl-devel - - libgcc >=14 + - libgcc >=15 + - libstdcxx >=15 - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - xcb-util >=0.4.1,<0.5.0a0 - - xorg-libx11 >=1.8.13,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 + - libglib >=2.88.3,<3.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - - fontconfig >=2.18.1,<3.0a0 - - fonts-conda-ecosystem - - xorg-libxxf86vm >=1.1.7,<2.0a0 - - xorg-libxrandr >=1.5.5,<2.0a0 - - libsqlite >=3.53.2,<4.0a0 - - libpq >=18.4,<19.0a0 - - xorg-libice >=1.1.2,<2.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - wayland >=1.25.0,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - libvulkan-loader >=1.4.341.0,<2.0a0 - - xorg-libxext >=1.3.7,<2.0a0 - - xcb-util-keysyms >=0.4.1,<0.5.0a0 - - libpng >=1.6.58,<1.7.0a0 - - harfbuzz >=14.2.1 - - xcb-util-cursor >=0.1.6,<0.2.0a0 - - xorg-libxcursor >=1.2.3,<2.0a0 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - wayland >=1.26.0,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 - libcups >=2.3.3,<2.4.0a0 - - libxcb >=1.17.0,<2.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - libdrm >=2.4.127,<2.5.0a0 - - xorg-libxcomposite >=0.4.7,<1.0a0 - - xcb-util-image >=0.4.0,<0.5.0a0 - - xcb-util-wm >=0.4.2,<0.5.0a0 + - alsa-lib >=1.2.16.1,<1.3.0a0 - zstd >=1.5.7,<1.6.0a0 - - krb5 >=1.22.2,<1.23.0a0 - - xcb-util-renderutil >=0.3.10,<0.4.0a0 - - icu >=78.3,<79.0a0 - - xorg-libxdamage >=1.1.6,<2.0a0 - - xorg-libsm >=1.2.6,<2.0a0 - - alsa-lib >=1.2.16,<1.3.0a0 - - openssl >=3.5.6,<4.0a0 - - libglib >=2.88.1,<3.0a0 - - libgl >=1.7.0,<2.0a0 - - libxkbcommon >=1.13.2,<2.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - double-conversion >=3.4.0,<3.5.0a0 - - dbus >=1.16.2,<2.0a0 - - xorg-libxtst >=1.2.5,<2.0a0 - - libegl >=1.7.0,<2.0a0 + - libtiff >=4.7.2,<4.8.0a0 - libxml2 - libxml2-16 >=2.14.6 - constrains: - - qt ==6.11.1 - license: LGPL-3.0-only - license_family: LGPL - purls: [] - run_exports: - weak: - - qt6-main >=6.11.1,<7.0a0 - size: 60185421 - timestamp: 1780593127053 -- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda - sha256: 8776405bb5b256456fa1749bd26946944577f2d73b8c749f9d7c1a5c187bdca2 - md5: 4d483b12b9fc7169d112d4f7a250c05c - depends: - - __glibc >=2.17,<3.0.a0 - - alsa-lib >=1.2.13,<1.3.0a0 - - dbus >=1.13.6,<2.0a0 - - double-conversion >=3.3.0,<3.4.0a0 - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - harfbuzz >=10.2.0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libclang-cpp19.1 >=19.1.7,<19.2.0a0 - - libclang13 >=19.1.7 - - libcups >=2.3.3,<2.4.0a0 - - libdrm >=2.4.124,<2.5.0a0 + - dbus >=1.16.2,<2.0a0 - libegl >=1.7.0,<2.0a0 - - libgcc >=13 - - libgl >=1.7.0,<2.0a0 - - libglib >=2.82.2,<3.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libllvm19 >=19.1.7,<19.2.0a0 - - libpng >=1.6.46,<1.7.0a0 - - libpq >=17.2,<18.0a0 - - libsqlite >=3.48.0,<4.0a0 - - libstdcxx >=13 - - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.5.0,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libxxf86vm >=1.1.7,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - icu >=78.3,<79.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - openssl >=3.5.7,<4.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libpq >=18.6,<19.0a0 - libxcb >=1.17.0,<2.0a0 - - libxkbcommon >=1.7.0,<2.0a0 - - libxml2 >=2.13.5,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - mysql-libs >=9.0.1,<9.1.0a0 - - openssl >=3.4.0,<4.0a0 - - pcre2 >=10.44,<10.45.0a0 - - wayland >=1.23.1,<2.0a0 - xcb-util >=0.4.1,<0.5.0a0 - - xcb-util-cursor >=0.1.5,<0.2.0a0 - - xcb-util-image >=0.4.0,<0.5.0a0 - xcb-util-keysyms >=0.4.1,<0.5.0a0 - xcb-util-renderutil >=0.3.10,<0.4.0a0 - - xcb-util-wm >=0.4.2,<0.5.0a0 - - xorg-libice >=1.1.2,<2.0a0 - - xorg-libsm >=1.2.5,<2.0a0 - - xorg-libx11 >=1.8.10,<2.0a0 - - xorg-libxcomposite >=0.4.6,<1.0a0 - - xorg-libxcursor >=1.2.3,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - libharfbuzz >=14.3.1 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem - xorg-libxdamage >=1.1.6,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 - - xorg-libxrandr >=1.5.4,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libgl >=1.7.0,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - libsqlite >=3.53.4,<4.0a0 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - libxkbcommon >=1.13.2,<2.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libdrm >=2.4.129,<2.5.0a0 + - xorg-libxext >=1.3.7,<2.0a0 - xorg-libxtst >=1.2.5,<2.0a0 - - xorg-libxxf86vm >=1.1.6,<2.0a0 - - zstd >=1.5.6,<1.6.0a0 constrains: - - qt 6.8.2 + - qt ==6.11.2 license: LGPL-3.0-only license_family: LGPL - purls: [] run_exports: weak: - - qt6-main >=6.8.2,<6.9.0a0 - size: 51501033 - timestamp: 1738333763120 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.11-py312hd177ed6_1.conda - sha256: a2dbadde579d105c4484ae351aa792d691091b3c639e6bcdc3471629972fe7c8 - md5: 246c5f31c607ecfe1ece1e8cc6ecc9c5 - depends: - - __glibc >=2.17,<3.0.a0 - - affine - - attrs - - certifi - - click >=4 - - click-plugins - - cligj >=0.5 - - libgcc >=13 - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - libstdcxx >=13 - - numpy >=1.19,<3 - - proj >=9.4.1,<9.5.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - setuptools >=0.9.8 - - snuggs >=1.4.1 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping - run_exports: {} - size: 7430252 - timestamp: 1726176608886 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py314h91cf270_1.conda - sha256: 6fe69aa2c0c213adf737f91036a50fe3ac0baeedb385916e4d116c787887fd1b - md5: 46740345de0ebe1dbba6ef6ed26282c0 + - qt6-main >=6.11.2,<7.0a0 + size: 60888449 + timestamp: 1787048975419 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda + sha256: 273497d08e9742690399a6e738d227ccd6b7ca62f0fe9faa6cb3a91d5e4ba991 + md5: 38f624a32029e9fced9ed40fa8c644db depends: - __glibc >=2.17,<3.0.a0 - affine @@ -9927,31 +6611,15 @@ packages: - libstdcxx >=14 - numpy >=1.25,<3 - proj >=9.8.1,<9.9.0a0 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - setuptools >=0.9.8 - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping run_exports: {} - size: 7777425 - timestamp: 1785964760305 -- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda - sha256: f0f520f57e6b58313e8c41abc7dfa48742a05f1681f05654558127b667c769a8 - md5: 8f70e36268dea8eb666ef14c29bd3cda - depends: - - libre2-11 2023.09.01 h5a48ba9_2 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - - re2 - size: 26617 - timestamp: 1708946796423 + size: 8118849 + timestamp: 1785964755746 - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda sha256: 7279908454f0c87b84ebc4aec6924f02f41a105d88420fb35dfaf5ecd976e0f5 md5: d83f2ee212d217a6d745a00e5be84671 @@ -9959,64 +6627,68 @@ packages: - libre2-11 2025.11.05 h60473fc_2 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 - re2 size: 27397 timestamp: 1781312576962 -- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 - md5: d7d95fc8287ea7bf33e0e7116d2b95ec +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + sha256: 01b3fe073a66e321970d09e04b388708f8cbdca5cdfbfcb7c9eeb470ad10383d + md5: 69c01c781e7c8190bbdaf79e3848c5ca depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - ncurses >=6.5,<7.0a0 + - libgcc >=15 + - ncurses >=6.6,<7.0a0 license: GPL-3.0-only license_family: GPL - purls: [] run_exports: weak: - readline >=8.3,<9.0a0 - size: 345073 - timestamp: 1765813471974 -- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.7.post0-hb03c661_1.conda - sha256: b47cbe954b1522f944c1c17fae964d362dfcf7ef7516fda1c368971426a8e933 - md5: 2e1edbf819157ca726ec65afb4eb2d5d + size: 348899 + timestamp: 1787033801506 +- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.8.post0-hee9eb32_0.conda + sha256: fc23277984a1bfff6edfaf4956484f3daa2f6271035050480eddb3c4c9076d46 + md5: 3248db26fec71163db19d13fbabecd42 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT - license_family: MIT - size: 35494 - timestamp: 1779092421531 -- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.7.post0-hecca717_1.conda - sha256: f29ba41cc71a01f1dd7eeba573b1535f0feec72e9ab6b4a390b4e71230d61ade - md5: a0ef2594b3ab25a933ee8e7a1ac1e21b + run_exports: + weak: + - reproc >=14.2.8.post0,<14.3.0a0 + size: 37774 + timestamp: 1788160449779 +- conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.8.post0-h1c70be6_0.conda + sha256: 86baa58c2aa797fc9c60d8dac7c9b084651464fcad2c6a2bea8036fcc1d3dd7d + md5: 04afaf7b13f8ac3c947230a24d835c94 depends: + - reproc ==14.2.8.post0 hee9eb32_0 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - reproc 14.2.7.post0 hb03c661_1 + - libstdcxx >=15 + - libgcc >=15 + - reproc >=14.2.8.post0,<14.3.0a0 license: MIT - license_family: MIT - size: 27069 - timestamp: 1779092443763 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.5.1-py314h1bee95f_0.conda - sha256: 79d753848377c415578f42285f5f87be711503b887c99e8890fd51d3fae1d6a5 - md5: fb5b4fc759b225d4f32730cc8380ec8e + run_exports: + weak: + - reproc-cpp >=14.2.8.post0,<14.3.0a0 + size: 29221 + timestamp: 1788160449779 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_0.conda + sha256: b189c45a480760097c0e8ad376be0c2467f436539097f7a12f8bb132930ad4ed + md5: 4c5bc78879aabba9ec582ef330109996 depends: - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python_abi 3.14.* *_cp314 + - libgcc >=15 + - python_abi 3.12.* *_cp312 constrains: - __glibc >=2.17 license: MIT license_family: MIT - size: 309696 - timestamp: 1779976994059 + run_exports: {} + size: 300808 + timestamp: 1787344306791 - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda sha256: 3425007379dc5b709b02699fa4329f1b82880a42f8dec40003cad7e7b6e2adac md5: aefc39100f5d6c043d02a38c3b90ff47 @@ -10029,8 +6701,6 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT - purls: - - pkg:pypi/rpds-py?source=compressed-mapping run_exports: {} size: 300155 timestamp: 1787344359780 @@ -10045,55 +6715,36 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 308487 timestamp: 1766175778417 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314h0f05182_1.conda - sha256: 3bd8db7556e87c98933a47ff9f962af7b8e0dc3757a72180b27cbfcb1f98d2d9 - md5: 4f35ae1228a6c5d9df593367ffe8dda1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_2.conda + sha256: be8703d9d347586b1609d93bb5ec05a0e54ccedafeee5f5d03cb31fe349e80ad + md5: 12909f4b50ec672c63dc4767566189fc depends: - python - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} - size: 150041 - timestamp: 1766159514023 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.15.19-h6a952e8_0.conda + size: 152916 + timestamp: 1787264326043 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.5-hdfdb5ae_0.conda noarch: python - sha256: 6791056748e4c4d06a286d1b591b2543764988b85d91ae12284635ae7f81de80 - md5: a7fb4598acb68a071b45d53820ff8812 + sha256: 6e056514a68826e940616a26fb0351d3d15d89ba47f6c2cbeb1cadd0fe60277a + md5: 7ddc6f2699c79c53286b478a6b4c9e92 depends: - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 constrains: - __glibc >=2.17 license: MIT - license_family: MIT - size: 9506544 - timestamp: 1782288610637 -- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda - sha256: a6fa0afa836f8f26dea0abc180ca2549bb517932d9a88a121e707135d4bcb715 - md5: 334dba9982ab9f5d62033c61698a8683 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - openssl >=3.3.2,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - s2n >=1.5.5,<1.5.6.0a0 - size: 353081 - timestamp: 1728534228471 + run_exports: {} + size: 8955205 + timestamp: 1787865273472 - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda sha256: 7369ac21f3561e2203f5b1600ef0671270057380783ca4b9e15f679ba25db575 md5: fa1c00d999e83ec20173c9775f71a1f1 @@ -10103,7 +6754,6 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - s2n >=1.7.5,<1.7.6.0a0 @@ -10127,61 +6777,12 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 10038167 timestamp: 1780401052981 -- conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - sha256: 6a01f4403db746acd676e34e80e3a14d041f2261d658402ca13dae6407c35d44 - md5: 30883954413aad9e3ac42134bef91ffe - depends: - - python - - numpy >=1.24.1 - - scipy >=1.10.0 - - joblib >=1.4.0 - - threadpoolctl >=3.5.0 - - narwhals >=2.0.1 - - libstdcxx >=14 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - _openmp_mutex >=4.5 - - numpy >=1.23,<3 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping - run_exports: {} - size: 10311253 - timestamp: 1780401051520 -- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda - sha256: d5ac05ad45c0d48731eb189c2cbb2bb99f0e3cb7e1acaad373cb2f1f2597fc75 - md5: 15995ecb2ef890778ba9a3750190f09d - depends: - - __glibc >=2.17,<3.0.a0 - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - libgcc >=14 - - libgfortran - - libgfortran5 >=14.3.0 - - liblapack >=3.9.0,<4.0a0 - - libstdcxx >=14 - - numpy <2.7 - - numpy >=1.23,<3 - - numpy >=1.25.2 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping - run_exports: {} - size: 16828243 - timestamp: 1779874781187 -- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py314hf07bd8e_0.conda - sha256: 85503102237f8515ab92319fc14609e894ac9e95e3a1398b0c49db1f9ee50877 - md5: 62c390c1f8f51240f1ebc7ba782669ad +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda + sha256: c304dfb0bbf0d824d3706623f6437237d7bfc83941bb7b18f80e05abb10ec79e + md5: f8d242c552b0f7f682451ce95879af5e depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 @@ -10194,90 +6795,55 @@ packages: - numpy <2.7 - numpy >=1.23,<3 - numpy >=2.0.0 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping - run_exports: {} - size: 17260022 - timestamp: 1781912924009 -- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.6-py312h6cab151_1.conda - sha256: b7818c7264926401b78b0afc9a7d2c98ff0fc0ed637ad9e5c126da38a40382f7 - md5: 5be02e05e1adaa42826cc6800ce399bc - depends: - - __glibc >=2.17,<3.0.a0 - - geos >=3.12.2,<3.12.3.0a0 - - libgcc >=13 - - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping run_exports: {} - size: 571255 - timestamp: 1725394110104 -- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py314hbe3edd8_2.conda - sha256: 17cb5cec9283f993072e8b6f5e1417d8d892cc5efa27029eae954ab06b33c7e2 - md5: 5963e6ee81772d450a35e6bc95522761 + size: 17104066 + timestamp: 1781912972195 +- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda + sha256: da100ac0210f52399faf814f701165058fa2e2f65f5c036cdf2bf99a40223373 + md5: 69e400d3deca12ee7afd4b73a5596905 depends: - __glibc >=2.17,<3.0.a0 - geos >=3.14.1,<3.14.2.0a0 - libgcc >=14 - numpy >=1.23,<3 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping run_exports: {} - size: 652785 - timestamp: 1762523657698 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.4-hb700be7_0.conda - sha256: 7a1f81823ec7d5ad659024de878ec34ea90205743cd685790621d7c013cc0a75 - md5: a0a159ba93ca0018f3d3e333470b045f + size: 631649 + timestamp: 1762523699384 +- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.9-h4dbf13b_0.conda + sha256: 58d3bd2face0c19f8680c9da0cd7df4bcbc5e32439f00f133986238783d0e26a + md5: 53a8ff86f8bd6f5d2ea089df06836fdc depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libstdcxx >=15 + - libgcc >=15 license: Apache-2.0 license_family: APACHE - size: 317505 - timestamp: 1778109124630 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py312h4c3975b_0.conda - sha256: 70a0ab8787576d55f23d114cc1d7569fd83f5b148c71a7f277180299e86ea8f1 - md5: bb3b931588a7dc83cc7b82836b0017df + run_exports: + weak: + - simdjson >=4.6.9,<4.7.0a0 + size: 357796 + timestamp: 1787803683470 +- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda + sha256: da0443224cb579bd3d68fba20ff1422a2774b6e3be4e068129722bc4ddadd1a9 + md5: 75095e959e733b32c2ae343b1bcd0073 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: MIT license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping - run_exports: {} - size: 160254 - timestamp: 1777112097179 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py314h5bd0f2a_0.conda - sha256: d4e5dff4086b8efda9e1f12d456a24a5d62a355b01a681eaa97a2ef95b91a246 - md5: 3f114d76f35141da9f4be18af7cba051 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping run_exports: {} - size: 163682 - timestamp: 1777112100084 + size: 167762 + timestamp: 1787824152736 - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 md5: 98b6c9dc80eb87b2519b97bcf7e578dd @@ -10288,31 +6854,14 @@ packages: - libgcc >=14 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 size: 45829 timestamp: 1762948049098 -- conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.14.1-hed91bc2_1.conda - sha256: 0c604fe3f78ddb2b612841722bd9b5db24d0484e30ced89fac78c0a3f524dfd6 - md5: 909188c8979846bac8e586908cf1ca6a - depends: - - __glibc >=2.17,<3.0.a0 - - fmt >=11.0.1,<11.1a0 - - libgcc-ng >=12 - - libstdcxx-ng >=12 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.14.1,<1.15.0a0 - size: 195665 - timestamp: 1722238295031 -- conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hab81395_1.conda - sha256: c650f3df027afde77a5fbf58600ec4ed81a9edddf81f323cfb3e260f6dc19f56 - md5: a3b0e874fa56f72bc54e5c595712a333 +- conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda + sha256: 5fca627690d56c6f87a9dabafceee54cb75d7da392309bb080333948c048365a + md5: 5e32ceeb7b9514ce4730565052829dbd depends: - __glibc >=2.17,<3.0.a0 - fmt >=12.1.0,<12.2.0a0 @@ -10320,128 +6869,62 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT - size: 196681 - timestamp: 1767781665629 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.51-py314h0f05182_0.conda - sha256: 3c46e9af535a376c7269b61563f84c601235b00d50fc97f87ffbf3b68a51fe17 - md5: aeb7447f3ea37b2bb32167267dcf0bfe + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 197438 + timestamp: 1785924346496 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda + sha256: 0a3d82fb1650d364f8c2427d870a6c6684e02c9e5c4b0cccb6def83957f007fb + md5: c54cb6754823fe6710369511aab5c29c depends: - python - greenlet !=0.4.17 - typing-extensions >=4.6.0 - - __glibc >=2.17,<3.0.a0 - libgcc >=14 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 4034632 - timestamp: 1781547880247 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda - sha256: ef17725138fa72aa5a0f8ccace9727831c4b333e39575f78fb5ad1755826b687 - md5: b345ea7f13e4cae9809c69b1d1af2c99 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libsqlite 3.53.3 h0c1763c_0 - - libzlib >=1.3.2,<2.0a0 - - ncurses >=6.6,<7.0a0 - - readline >=8.3,<9.0a0 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.3,<4.0a0 - size: 206481 - timestamp: 1782519082269 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h04a0ce9_0.conda - sha256: 1f6c9638d64c51a35769219cc895e49af5cd0615aef896604c5472bc79b980f7 - md5: b6db30b9cfd0cb089910ccb47a2516f9 + run_exports: {} + size: 4047549 + timestamp: 1786535232010 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda + sha256: b01812eff4e950a73933cb3dc14647a97ee377c7fab4858495b80a749ebfbf8b + md5: d42b24574925bfa3167efb3571c905ad depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - - libgcc >=14 - - libsqlite 3.53.4 hf4e2dac_0 + - libgcc >=15 + - libsqlite 3.53.4 h13e7031_1 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing - purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 - size: 206694 - timestamp: 1785016118388 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.26.0-h86fa3b2_0.conda - sha256: 3e92cec15daed5e03d7fc676a021500fc92ac80716495504537d6e4bdb80138f - md5: 061175d9d4c046a1cf8bffe95a359fab - depends: - - __glibc >=2.17,<3.0.a0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - bzip2 >=1.0.8,<2.0a0 - - fmt >=11.0.2,<11.1a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgcc >=13 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libstdcxx >=13 - - libwebp-base >=1.4.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - spdlog >=1.14.1,<1.15.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - tiledb >=2.26.0,<2.27.0a0 - size: 4537477 - timestamp: 1726059097900 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac - md5: cffd3bdd58090148f4cfcd831f4b26ab - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libzlib >=1.3.1,<2.0a0 - constrains: - - xorg-libx11 >=1.8.12,<2.0a0 - license: TCL - license_family: BSD - purls: [] - run_exports: - weak: - - tk >=8.6.13,<8.7.0a0 - size: 3301196 - timestamp: 1769460227866 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - build_number: 103 - sha256: 43624eab22f5f29df7d6ffe914cf442f28fd559b55b290906255492826e636e8 - md5: 48a1049e710857572fc2a832aa394d9f + size: 205686 + timestamp: 1787051150973 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + build_number: 104 + sha256: a1a241d172c1ccab067ba245206dd048bc3c2d1b84504b53c9468e99adfc16a1 + md5: 676a2f9b1c4fcf57b70aa5c65f6c60d0 depends: + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - libzlib >=1.3.2,<2.0a0 constrains: - xorg-libx11 >=1.8.13,<2.0a0 license: TCL - purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 - size: 3550916 - timestamp: 1784229071544 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda - sha256: f54504d6eeef133ddc2b964b6a021f3faf085bb08bd70debc07f56d6b9b726f1 - md5: 55f526c3fb5302a1ce922612348442e1 + size: 3566806 + timestamp: 1787272857910 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda + sha256: 2ea8f8b10e869b675d2fba157b387eaf4eed1696bfc924bb7b28e67d7aa0a947 + md5: 5423c2b8f82d60320e65b9ff30babbdf depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -10449,23 +6932,9 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/tornado?source=hash-mapping run_exports: {} - size: 864705 - timestamp: 1781006801632 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py314h5bd0f2a_0.conda - sha256: bbb7056f7c5fd606df16ed73ee68687050de2c02fd69a3f69a1cb533a7ed2ae8 - md5: 4a8e5889712641aabdf6695e292857fe - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: Apache - size: 918368 - timestamp: 1781006801436 + size: 869730 + timestamp: 1786226754702 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda sha256: ebec0d90f99fa7bbe91eabab41ee77d3f36dbd58ec2f08aa4220fdd713610b6d md5: faea84d2f2ccadf70cf9e55381d75b3e @@ -10476,23 +6945,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/tornado?source=compressed-mapping run_exports: {} size: 923237 timestamp: 1786226770518 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda - sha256: 35da5b89561ed93629f751f111a092abcaaa9fdc516e09e9b4ab9880756a1871 - md5: 5c5f9b9bad2a0852ffa38833ba2a1c7d - depends: - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 71525 - timestamp: 1776960300375 - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda sha256: 895bbfe9ee25c98c922799de901387d842d7c01cae45c346879865c6a907f229 md5: 0b6c506ec1f272b685240e70a29261b8 @@ -10503,26 +6958,9 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 410641 timestamp: 1770909099497 -- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py314h5bd0f2a_0.conda - sha256: ff1c1d7c23b91c9b0eb93a3e1380f4e2ac6c37ea2bba4f932a5484e9a55bba30 - md5: 494fdf358c152f9fdd0673c128c2f3dd - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping - run_exports: {} - size: 409562 - timestamp: 1770909102180 - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda sha256: 2aad2aeff7c69a2d7eecd7b662eef756b27d6a6b96f3e2c2a7071340ce14543e md5: d71d3a66528853c0a1ac2c02d79a0284 @@ -10531,59 +6969,27 @@ packages: - libstdcxx-ng >=12 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 size: 48270 timestamp: 1715010035325 -- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - sha256: ea374d57a8fcda281a0a89af0ee49a2c2e99cc4ac97cf2e2db7064e74e764bdb - md5: 996583ea9c796e5b915f7d7580b51ea6 - depends: - - __glibc >=2.17,<3.0.a0 - - libexpat >=2.7.4,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - size: 334139 - timestamp: 1773959575393 -- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-h1964d1d_1.conda - sha256: a8f1333274382d23721d6f72483ece364631231f8ac3f74389951b1ff2820148 - md5: 47e3c5d0b1e968ee49efc7c3fa8ebc0c +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda + sha256: df65b987979e6d7f88e756494da5a230d4f2d08d17437d04eecd35559c3a04e4 + md5: 88a47e22b53e50865b1cabe2eb648352 depends: - __glibc >=2.17,<3.0.a0 - libexpat >=2.8.1,<3.0a0 - libffi >=3.7.0,<3.8.0a0 - - libgcc >=14 - - libstdcxx >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - wayland >=1.26.0,<2.0a0 - size: 339462 - timestamp: 1786151675802 -- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda - sha256: 6b9e182021ef3a64ab3bf788ebdab6de6775035612237c639ccafc941639eb13 - md5: b34c5559f45d8996e3bc0b6250a6cc84 - depends: - - __glibc >=2.17,<3.0.a0 - - libexpat >=2.8.1,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 license: MIT license_family: MIT - purls: [] run_exports: weak: - wayland >=1.26.0,<2.0a0 - size: 340543 - timestamp: 1784249169392 + size: 340058 + timestamp: 1787793849093 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda sha256: e2b6545651aed5e7dead39b7ba3bf8c2669f194c71e89621343bd0bb321a87f1 md5: 82da729c870ada2f675689a39b4f697f @@ -10594,8 +7000,6 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 64997 timestamp: 1756851739706 @@ -10608,7 +7012,6 @@ packages: - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util >=0.4.1,<0.5.0a0 @@ -10626,7 +7029,6 @@ packages: - xcb-util-renderutil >=0.3.10,<0.4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util-cursor >=0.1.6,<0.2.0a0 @@ -10641,7 +7043,6 @@ packages: - xcb-util >=0.4.1,<0.5.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util-image >=0.4.0,<0.5.0a0 @@ -10655,7 +7056,6 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util-keysyms >=0.4.1,<0.5.0a0 @@ -10669,7 +7069,6 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util-renderutil >=0.3.10,<0.4.0a0 @@ -10683,46 +7082,27 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xcb-util-wm >=0.4.2,<0.5.0a0 size: 51689 timestamp: 1718844051451 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda - sha256: 339ab0ff05170a295e59133cd0fa9a9c4ba32b6941c8a2a73484cc13f81e248a - md5: 9dda9667feba914e0e80b95b82f7402b - depends: - - __glibc >=2.17,<3.0.a0 - - icu >=75.1,<76.0a0 - - libgcc >=13 - - libnsl >=2.0.1,<2.1.0a0 - - libstdcxx >=13 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - xerces-c >=3.2.5,<3.3.0a0 - size: 1648243 - timestamp: 1727733890754 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda - sha256: 605980121ad3ee9393a9b53fb0996929c9732f8fc6b9f796d25244ca6fa23032 - md5: 66a1db55ecdb7377d2b91f54cd56eafa +- conda: https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-h78fed68_2.conda + sha256: 1acbd62b33b6efc985f70e0b9f0013ca18fcef2df5a18edb1c81a6b63ad20dc0 + md5: f2ff0de6c209e90c53b3a195c2708f5f depends: - __glibc >=2.17,<3.0.a0 - - icu >=78.1,<79.0a0 - - libgcc >=14 + - icu >=78.3,<79.0a0 + - libgcc >=15 - libnsl >=2.0.1,<2.1.0a0 - - libstdcxx >=14 + - libstdcxx >=15 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 - size: 1660075 - timestamp: 1766327494699 + size: 1675829 + timestamp: 1788144975107 - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda sha256: 3b04afd5d1a65d2d27ac2d49a63b01ab8bcd875776779ec63e337370ed38afdc md5: b233b41be0bf210989d57160ed39b394 @@ -10732,7 +7112,6 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 441670 timestamp: 1782027360439 @@ -10743,26 +7122,12 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: MIT - purls: [] + license_family: MIT run_exports: weak: - xorg-libice >=1.1.2,<2.0a0 size: 62517 timestamp: 1786474410404 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b - md5: fb901ff28063514abb6046c9ec2c4a45 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libice >=1.1.2,<2.0a0 - size: 58628 - timestamp: 1734227592886 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda sha256: ef907caee0665cf3b0f775602cc50e388e3bade8ce22ecade0873ff26604b2fd md5: aa7459ed9ad086ba11dda843d764b33d @@ -10772,57 +7137,26 @@ packages: - xorg-libice >=1.1.2,<2.0a0 - libuuid >=2.42.2,<3.0a0 license: MIT - purls: [] + license_family: MIT run_exports: weak: - xorg-libsm >=1.2.6,<2.0a0 size: 30739 timestamp: 1786545374265 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 - md5: 1c74ff8c35dcadf952a16f752ca5aa49 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - libuuid >=2.38.1,<3.0a0 - - xorg-libice >=1.1.2,<2.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libsm >=1.2.6,<2.0a0 - size: 27590 - timestamp: 1741896361728 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - sha256: 516d4060139dbb4de49a4dcdc6317a9353fb39ebd47789c14e6fe52de0deee42 - md5: 861fb6ccbc677bb9a9fb2468430b9c6a +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + sha256: 68053eebfa9f0d91666786c8fb5839d989aa9b869add92cb8815228bb2d7302c + md5: 8c282bbe4808a3cc80a5c98e9aec1cfc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libx11 >=1.8.13,<2.0a0 - size: 839652 - timestamp: 1770819209719 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda - sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b - md5: b2895afaf55bf96a8c8282a2e47a5de0 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libxau >=1.0.12,<2.0a0 - size: 15321 - timestamp: 1762976464266 + size: 839578 + timestamp: 1787087012372 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda sha256: cbf891f6cc1a859347680af1c8562bc6b033bf182a2a3bb536016932be3206de md5: f06ef439c280a5f90b8bf62355008dbc @@ -10831,7 +7165,6 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -10847,7 +7180,6 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxcomposite >=0.4.7,<1.0a0 @@ -10864,7 +7196,6 @@ packages: - xorg-libxrender >=0.9.11,<0.10.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxcursor >=1.2.3,<2.0a0 @@ -10881,26 +7212,11 @@ packages: - xorg-libxfixes >=6.0.1,<7.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxdamage >=1.1.6,<2.0a0 size: 13217 timestamp: 1727891438799 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 - md5: 1dafce8548e38671bea82e3f5c6ce22f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libxdmcp >=1.1.5,<2.0a0 - size: 20591 - timestamp: 1762976546182 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda sha256: c50a16c05ccd7fe7dd6d6cfb539f4e9a491d50f9ed7a5c902fec638f7d0d27be md5: 2e66c929f3d879708335b6ea4557c838 @@ -10909,59 +7225,55 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 size: 21120 timestamp: 1786381006369 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f - md5: 34e54f03dfea3e7a2dcf1453a85f1085 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + sha256: aa9bbe8b278aacc194e280ff5037f9f9a1f2c5b33ed97de8e7f01cfbe90dda43 + md5: e5b6b28536b81b3f4cb20db4668a4642 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - xorg-libx11 >=1.8.12,<2.0a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxext >=1.3.7,<2.0a0 - size: 50326 - timestamp: 1769445253162 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 - md5: ba231da7fccf9ea1e768caf5c7099b84 + size: 53124 + timestamp: 1787100841900 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + sha256: aec84f554fc897bf4085c7bc8b8f0740e4c224e13bca3cc51c93e7699d56f83a + md5: 09132e874fe0e1f5e4492b0b6b904b6e depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - xorg-libx11 >=1.8.12,<2.0a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxfixes >=6.0.2,<7.0a0 - size: 20071 - timestamp: 1759282564045 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - sha256: 495f99c8eacfa4ae2d8fed2a7f2105777af89acdc204df145d2bbbc380ac631b - md5: adba2e334082bb218db806d4c12277c9 + size: 21440 + timestamp: 1787248059682 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + sha256: a523d71344a2f640efe6fc42b88fc261d9cd389d4f9838a5d42dcdb120c2b0c8 + md5: a1412b2b1184dacda45f8476fef2cc25 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - xorg-libx11 >=1.8.13,<2.0a0 - xorg-libxext >=1.3.7,<2.0a0 - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxi >=1.8.3,<2.0a0 - size: 47717 - timestamp: 1779111857071 + size: 49165 + timestamp: 1787257060460 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda sha256: 3a9da41aac6dca9d3ff1b53ee18b9d314de88add76bafad9ca2287a494abcd86 md5: 93f5d4b5c17c8540479ad65f206fea51 @@ -10973,61 +7285,57 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxinerama >=1.1.6,<1.2.0a0 size: 14818 timestamp: 1769432261050 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - sha256: 80ed047a5cb30632c3dc5804c7716131d767089f65877813d4ae855ee5c9d343 - md5: e192019153591938acf7322b6459d36e +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + sha256: 05a7f25d7f7f5cd32b27a019233ece97167fd8ade255bc13a0e49e53387f4c30 + md5: 798a8c9d171859a022e1cb89e7d0eb10 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - xorg-libx11 >=1.8.12,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxrandr >=1.5.5,<2.0a0 - size: 30456 - timestamp: 1769445263457 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 - md5: 96d57aba173e878a2089d5638016dc5e + size: 31106 + timestamp: 1787246614086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + sha256: 6901f91d398811e4ec89d7e20a69abac02a7bfebfaf073338b7ea3d1a99685b7 + md5: e470d224a7a5be1b1d021bded7abb536 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - xorg-libx11 >=1.8.10,<2.0a0 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxrender >=0.9.12,<0.10.0a0 - size: 33005 - timestamp: 1734229037766 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a - md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f + size: 34645 + timestamp: 1787100191192 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda + sha256: d66525e7b1c492aa179c6c55610fc8dfb0a9a62ea7d4fb9f904fa6af62127f61 + md5: 752a5ac9322e0fbbc24146c1ce3ae44e depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - - xorg-libx11 >=1.8.10,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 - - xorg-libxi >=1.7.10,<2.0a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxi >=1.8.3,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxtst >=1.2.5,<2.0a0 - size: 32808 - timestamp: 1727964811275 + size: 35052 + timestamp: 1787360025506 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda sha256: 64db17baaf36fa03ed8fae105e2e671a7383e22df4077486646f7dbf12842c9f md5: 665d152b9c6e78da404086088077c844 @@ -11038,7 +7346,6 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxxf86vm >=1.1.7,<2.0a0 @@ -11052,93 +7359,36 @@ packages: - libgcc >=14 license: MIT license_family: MIT - purls: [] run_exports: {} size: 594844 timestamp: 1786114408394 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda - sha256: 7a8c64938428c2bfd016359f9cb3c44f94acc256c6167dbdade9f2a1f5ca7a36 - md5: aa8d21be4b461ce612d8f5fb791decae +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + sha256: d164dfa75ecd538f6fd68765defcc06aa875bc697b9b215362d79a2a73125dd0 + md5: e741576fb8f89821ac7c1c537322a33d depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT - size: 570010 - timestamp: 1766154256151 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.3-ha02ee65_0.conda - sha256: 2553fd3ec0a1020b2ca05ca10b0036a596cb0d4bf3645922fcf69dacce0e6679 - md5: 6a1b6af49a334e4e06b9f103367762bf - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - liblzma 5.8.3 hb03c661_0 - - liblzma-devel 5.8.3 hb03c661_0 - - xz-gpl-tools 5.8.3 ha02ee65_0 - - xz-tools 5.8.3 hb03c661_0 - license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 24360 - timestamp: 1775825568523 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.3-ha02ee65_0.conda - sha256: 8f139666ea18dc8340a44a54056627dd4e89e242e8cd136ab2467d6dc2c192ba - md5: 8f5e2c6726c1339287a3c76a2c138ac7 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - liblzma 5.8.3 hb03c661_0 - constrains: - - xz 5.8.3.* - license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later - purls: [] run_exports: weak: - - liblzma >=5.8.3,<6.0a0 - size: 34213 - timestamp: 1775825548743 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.3-hb03c661_0.conda - sha256: 162ebd76803464b8c8ebc7d45df32edf0ec717b3bf369a437ae3b0254f22dc2e - md5: b62b615caa60812640f24db3a8d0fc87 + - yaml >=0.2.5,<0.3.0a0 + size: 84936 + timestamp: 1787228426393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda + sha256: 425808ebd2a20bfc460995293995e1fa0510e93c300535e2ec3622bd3d84288c + md5: 18bf4b05b458fb8135987e47364f3dcb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - liblzma 5.8.3 hb03c661_0 - constrains: - - xz 5.8.3.* - license: 0BSD AND LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 95955 - timestamp: 1775825530484 -- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad - md5: a77f85f77be52ff59391544bfe73390a - depends: - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 license: MIT license_family: MIT - purls: [] run_exports: weak: - - yaml >=0.2.5,<0.3.0a0 - size: 85189 - timestamp: 1753484064210 -- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h3f2d84a_0.conda - sha256: 4b0b713a4308864a59d5f0b66ac61b7960151c8022511cdc914c0c0458375eca - md5: 92b90f5f7a322e74468bb4909c7354b5 - depends: - - libstdcxx >=13 - - libgcc >=13 - - __glibc >=2.17,<3.0.a0 - - libgcc >=13 - license: MIT - license_family: MIT - size: 223526 - timestamp: 1745307989800 + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 242654 + timestamp: 1785923983284 - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda sha256: dc9f28dedcb5f35a127fad2d847674d2833369dd616d294e423b8997df31d8a8 md5: 96b08867e21d4694fa5c2c226e6581b0 @@ -11150,26 +7400,11 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA - purls: [] run_exports: weak: - zeromq >=4.3.5,<4.4.0a0 size: 311184 timestamp: 1779123989774 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda - sha256: 245c9ee8d688e23661b95e3c6dd7272ca936fabc03d423cdb3cdee1bbcf9f2f2 - md5: c2a01a08fc991620a74b32420e97868a - depends: - - __glibc >=2.17,<3.0.a0 - - libzlib 1.3.2 h25fd6f3_2 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 95931 - timestamp: 1774072620848 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda sha256: 16080a1c7724f7d25727cdc23c7658e0cec2db52448c1dc0c33467ee2c6e1c62 md5: 6acb86426229f96f93e5468d1df3a5e8 @@ -11178,87 +7413,64 @@ packages: - libzlib 1.3.2 h25fd6f3_3 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 size: 96132 timestamp: 1785362957588 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f - md5: 2aadb0d17215603a82a2a6b0afd9a4cb +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda + sha256: 8b786bb4380fa69a718b08a400040b865bc9207eda337e0a1955717c3c3a9403 + md5: 1726acec19beeed1c764385cd8622405 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 + - libgcc >=15 + - libstdcxx >=15 license: Zlib license_family: Other - purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 - size: 122618 - timestamp: 1770167931827 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - sha256: c2bcb8aa930d6ea3c9c7a64fc4fab58ad7bcac483a9a45de294f67d2f447f413 - md5: 02738ff9855946075cbd1b5274399a41 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping - run_exports: {} - size: 467133 - timestamp: 1762512686069 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda - sha256: e589f694b44084f2e04928cabd5dda46f20544a512be2bdb0d067d498e4ac8d0 - md5: 2930a6e1c7b3bc5f66172e324a8f5fc3 + size: 123959 + timestamp: 1786736890973 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314hfe1a184_3.conda + sha256: 8c73ee662ea1446a322b1f0e8ddbb3cd631b9e6abf9b2dbe70662fe73b0c196b + md5: 7fe9ffe3e04a39dc12669cc0e4f23f05 depends: - python - cffi >=1.11 - zstd >=1.5.7,<1.5.8.0a0 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - zstd >=1.5.7,<1.6.0a0 + - libgcc >=15 - python_abi 3.14.* *_cp314 + - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping run_exports: {} - size: 473605 - timestamp: 1762512687493 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - sha256: 68f0206ca6e98fea941e5717cec780ed2873ffabc0e1ed34428c061e2c6268c7 - md5: 4a13eeac0b5c8e5b8ab496e6c4ddd829 + size: 472944 + timestamp: 1787896524818 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 + md5: aa459086047c0e5e27023ab19f8cb86a depends: - __glibc >=2.17,<3.0.a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 - size: 601375 - timestamp: 1764777111296 -- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 - md5: aaa2a381ccc56eac91d63b6c1240312f + size: 601301 + timestamp: 1786599621503 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + sha256: 2a7204314663eeda5dec482a956f0e2eaf289bd5b9953eaaaad0e81aa64638f2 + md5: 3845f3d75991bae0fb90884662f4327c depends: - cpython - python-gil license: MIT license_family: MIT - size: 8191 - timestamp: 1744137672556 + run_exports: {} + size: 8144 + timestamp: 1784221492234 - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda sha256: a362b4f5c96a0bf4def96be1a77317e2730af38915eb9bec85e2a92836501ed7 md5: b3f0179590f3c0637b7eb5309898f79e @@ -11268,35 +7480,20 @@ packages: - librsvg license: LGPL-3.0-or-later OR CC-BY-SA-3.0 license_family: LGPL - purls: [] run_exports: {} size: 631452 timestamp: 1758743294412 -- conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - sha256: 0deeaf0c001d5543719db9b2686bc1920c86c7e142f9bec74f35e1ce611b1fc2 - md5: 8c4061f499edec6b8ac7000f6d586829 +- conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda + sha256: 8801cd5b4e0f0203b7a18e8def15929f3413e93e2da88d5b918b735454106f85 + md5: aeb404cab9e719a49264ab40928f80c8 depends: - - python >=3.9 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/affine?source=hash-mapping - run_exports: {} - size: 19164 - timestamp: 1733762153202 -- conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.0-pyhd8ed1ab_0.conda - sha256: 5a72f7fbb1adb86817fde49acf972148638cc613edae9c15ac575d176da2746a - md5: 71dd867a02739013ae65894ca32d67b7 - depends: - - attrs + - attrs >=21.3.0 - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/affine?source=compressed-mapping run_exports: {} - size: 16762 - timestamp: 1786226684740 + size: 22619 + timestamp: 1788134268940 - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda sha256: a21c69c91d735faaf7b74dc2b3d013b14016951b66354461fc1d796caab9b1c4 md5: 69a01a9927733014f3955861e2969a7b @@ -11305,24 +7502,9 @@ packages: - pyparsing - python >=3.10 license: EPL-2.0 - purls: - - pkg:pypi/amply?source=hash-mapping run_exports: {} size: 26885 timestamp: 1782349642228 -- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 - md5: 2934f256a8acfe48f6ebb4fce6cde29c - depends: - - python >=3.9 - - typing-extensions >=4.0.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/annotated-types?source=hash-mapping - run_exports: {} - size: 18074 - timestamp: 1733247158254 - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda sha256: b8fcb994134d3918d1c64a9d62f4168ff85d5bfb89e698102fe4ed679fe0df24 md5: 108c928d2a8551832dbc762b535e90bb @@ -11331,14 +7513,12 @@ packages: - typing-extensions >=4.0.0 license: MIT license_family: MIT - purls: - - pkg:pypi/annotated-types?source=hash-mapping run_exports: {} size: 19461 timestamp: 1784935220549 -- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.1-pyhcf101f3_0.conda - sha256: 6119355a0b2a33e7b0dd31a740dbe01df66ffee0a643f80968afb1d53e7dbdb3 - md5: 7498bb2648f852c6a3cd5724ca80bdeb +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + sha256: e36998c5e860e26b22e5dbcd5726dd0c4eabad949c84d383cad8512757bbf6a1 + md5: fb568fbae6908ba86a090a85a089d11f depends: - exceptiongroup >=1.0.2 - idna >=2.8 @@ -11350,28 +7530,27 @@ packages: - uvloop >=0.22.1 - winloop >=0.2.3 license: MIT - size: 161308 - timestamp: 1782357087265 -- conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - sha256: 8f032b140ea4159806e4969a68b4a3c0a7cab1ad936eb958a2b5ffe5335e19bf - md5: 54898d0f524c9dee622d44bbb081a8ab + license_family: MIT + run_exports: {} + size: 164465 + timestamp: 1783889660383 +- conda: https://conda.anaconda.org/conda-forge/noarch/appnope-1.0.0-pyhcf101f3_0.conda + sha256: 52bc705ba773214cb2f8f884ee0128d007fa7dfdcf19218c910465381b91cd6e + md5: 56d53a5e9740367f5f8cf83f995263c2 depends: - - python >=3.9 + - python >=3.10 + - python license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/appnope?source=hash-mapping run_exports: {} - size: 10076 - timestamp: 1733332433806 + size: 12232 + timestamp: 1787337124448 - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda sha256: eb68e1ce9e9a148168a4b1e257a8feebffdb0664b557bb526a1e4853f2d2fc00 md5: 845b38297fca2f2d18a29748e2ece7fa depends: - python >=3.9 license: MIT OR Apache-2.0 - purls: - - pkg:pypi/archspec?source=hash-mapping run_exports: {} size: 50894 timestamp: 1737352715041 @@ -11382,22 +7561,21 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/argparse-dataclass?source=hash-mapping run_exports: {} size: 12806 timestamp: 1764079623900 -- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - sha256: ee4da0f3fe9d59439798ee399ef3e482791e48784873d546e706d0935f9ff010 - md5: 9673a61a297b00016442e022d689faa6 +- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + sha256: fbbd8ce60cbd5c16f3fe559eb644551f94285caff30985ea961ff851c1cf25ac + md5: 89d495168582cb00428dad699d149624 depends: - python >=3.10 constrains: - astroid >=2,<5 license: Apache-2.0 license_family: Apache - size: 28797 - timestamp: 1763410017955 + run_exports: {} + size: 34639 + timestamp: 1783975742052 - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda sha256: 1b6124230bb4e571b1b9401537ecff575b7b109cc3a21ee019f65e083b8399ab md5: c6b0543676ecb1fb2d7643941fe375f2 @@ -11406,22 +7584,19 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/attrs?source=hash-mapping run_exports: {} size: 64927 timestamp: 1773935801332 -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.6.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda noarch: generic - sha256: 709cac7434d1c5a8828105036212a2a36022a07d807e89e2e99cac939c2d2526 - md5: 40d89d8546ad6e139e73ec8f6d56068b + sha256: 19514d89d1e725e44b0650d31a4d43da8e070e4e93e4131e3b16bd139404f2b2 + md5: 92adf685875ab717f68ad4172ef6de27 depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 - purls: [] run_exports: {} - size: 7526 - timestamp: 1781450817767 + size: 7541 + timestamp: 1786861415851 - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda sha256: aed4b9dcf68ec2a75e5645fed14d77fd884d38d2e52bfa6ef4b278d90cd88781 md5: 3b261da3fe9b4168738712832410b022 @@ -11431,8 +7606,6 @@ packages: - typing-extensions license: MIT license_family: MIT - purls: - - pkg:pypi/beautifulsoup4?source=hash-mapping run_exports: {} size: 92704 timestamp: 1780853175566 @@ -11449,14 +7622,12 @@ packages: - pytokens >=0.4 license: MIT license_family: MIT - purls: - - pkg:pypi/black?source=hash-mapping run_exports: {} size: 176838 timestamp: 1779460936776 -- conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda - sha256: b3d3b93a17fa678e81cd5ff5309fe64c7feb65c71cb134f14e4fe2113dc0c8d5 - md5: 123fcfe0df4b6fc21804538e59cdaafe +- conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda + sha256: 34bb5cc4f6ccff1e410dea2d39880190f88a8be7ad5cfe9ff0da2cf3d2ad7ea1 + md5: 67d0ed593951a4933024453b76ef1355 depends: - contourpy >=1.2 - jinja2 >=2.9 @@ -11464,7 +7635,7 @@ packages: - numpy >=1.16 - packaging >=16.8 - pillow >=7.1.0 - - python >=3.10 + - python >=3.12 - pyyaml >=3.10 - tornado >=6.2 - xyzservices >=2021.09.1 @@ -11472,43 +7643,19 @@ packages: - panel >=1.8.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/bokeh?source=hash-mapping run_exports: {} - size: 4526315 - timestamp: 1781002115296 -- conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.2-pyhd8ed1ab_0.conda - sha256: 577c2614f3a59512697c1ffe2021c5194818b55f734006fcdeb6c6921b514c44 - md5: b2781afb860ae9ed84cb92bdebbc83ea + size: 4454345 + timestamp: 1787144786949 +- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + sha256: ca44f906756aaa908645c9bcef31a132fc82e7934777aa6bb45c12edada6a58c + md5: 92c88d22e52af7b672605b12a31bf0bb depends: - - contourpy >=1.2 - - jinja2 >=2.9 - - narwhals >=1.13 - - numpy >=1.16 - - packaging >=16.8 - - pillow >=7.1.0 - python >=3.10 - - pyyaml >=3.10 - - tornado >=6.2 - - xyzservices >=2021.09.1 - constrains: - - panel >=1.8.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/bokeh?source=hash-mapping run_exports: {} - size: 4292921 - timestamp: 1785249803504 -- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.0.0-pyhd8ed1ab_0.conda - sha256: d3c8be0524a5223bfed89c4a5be90598bfca28e0c702014f7799b978027eb5e1 - md5: 06c2cbdcfd20af2f72a95e09f8747c52 - depends: - - python >=3.10 - license: BSD-3-Clause - license_family: BSD - size: 307488 - timestamp: 1781879511321 + size: 309453 + timestamp: 1784706150046 - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda sha256: 1acf87c77d920edd098ddc91fa785efc10de871465dee0f463815b176e019e8b md5: 1fcdf88e7a8c296d3df8409bf0690db4 @@ -11517,38 +7664,15 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/branca?source=hash-mapping run_exports: {} size: 30176 timestamp: 1759755695447 -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-h4c7d964_0.conda - sha256: 7f458e4a82514d7bebbfef23d92817794a16aaf1c748a15f04870d4fb49aeab2 - md5: b9696b2cf00dfeec138c70cee38ed192 - depends: - - __win - license: ISC - purls: [] - run_exports: {} - size: 129352 - timestamp: 1781709016515 -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda - sha256: f8e3c730fa14ee3f170493779f06522c4acf89169f43db4f039727709b6419cf - md5: a9965dd99f683c5f444428f896635716 - depends: - - __unix - license: ISC - purls: [] - run_exports: {} - size: 128866 - timestamp: 1781708962055 - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda sha256: 95e8e74062a5fe5f870ac8c90302b6e89945165fdaed7810606e84ddee6aac12 md5: e27d2ac27b096dc51fedfcf775a53f9b depends: - __win license: ISC - purls: [] run_exports: {} size: 132136 timestamp: 1784754918886 @@ -11558,104 +7682,49 @@ packages: depends: - __unix license: ISC - purls: [] run_exports: {} size: 131780 timestamp: 1784754889428 -- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - noarch: python - sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 - md5: 9b347a7ec10940d3f7941ff6c460b551 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + sha256: 39358005c3cc44d8315ed79789b18a960d69365be979970cc6bae8f7a6177703 + md5: 70e764e549c6c25de5e429d6e4a28b39 depends: - - cached_property >=1.5.2,<1.5.3.0a0 + - cached_property >=2.0.1,<2.0.2.0a0 license: BSD-3-Clause license_family: BSD - size: 4134 - timestamp: 1615209571450 -- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 - md5: 576d629e47797577ab0f1b351297ef4a + run_exports: {} + size: 3720 + timestamp: 1787678456483 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + sha256: 4bd96dab5a434e4ee59fb6910cae704d4c5b453907d4886272a9943d37e42b6f + md5: 256d863ded0f79464391a075944d24a6 depends: - - python >=3.6 + - python >=3.10 + - python license: BSD-3-Clause license_family: BSD - size: 11065 - timestamp: 1615209567874 -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda - sha256: 6c13620e458ba43278379d0cdacc30c497336bddfda81681fd50d114a65c702f - md5: c13824fedced67005d3832c152fe9c2f - depends: - - python >=3.10 - license: ISC - purls: - - pkg:pypi/certifi?source=hash-mapping run_exports: {} - size: 133877 - timestamp: 1781719949728 + size: 16597 + timestamp: 1787678456483 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda sha256: fb167de4388e64e52aa3907ed099afab944c1fa6e5f74b281a312dae1bcf7f3b md5: 37e13edbe3b48f1095a9d085ef9cd83b depends: - python >=3.10 license: ISC - purls: - - pkg:pypi/certifi?source=hash-mapping run_exports: {} size: 137015 timestamp: 1784717699092 -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 - md5: a9167b9571f3baa9d448faa2139d1089 - depends: - - python >=3.10 - license: MIT - license_family: MIT - purls: - - pkg:pypi/charset-normalizer?source=hash-mapping - run_exports: {} - size: 58872 - timestamp: 1775127203018 -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.9-pyhd8ed1ab_0.conda - sha256: 8d8813ef655b4e75e4fb897abd83ad548882efad7b4e836b021b797f42780799 - md5: d154b40b109e503430979e8a8d099eaf +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + sha256: cb60ef3e0631c8bacb4f7057196dee4496091a22baa3bb4b9bccb12c7e1c921b + md5: e0ac3accc64e23e40969d660e5f58ac8 depends: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/charset-normalizer?source=hash-mapping run_exports: {} - size: 61418 - timestamp: 1783505332569 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda - sha256: 5b8e8d8876ace41735f51ca43c43cdc9e1b4fbbae0f415d6b8441fec826d8c47 - md5: f73f35eedcd8e89d6c4407df15101233 - depends: - - __win - - colorama - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/click?source=hash-mapping - run_exports: {} - size: 104080 - timestamp: 1779900586237 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - sha256: c253a41cdf898b651a0786cbb76c6d5fc101d0dbbe719f93a124bc4fde5cdd6a - md5: 554304a07e581a85891b15e39ea9f268 - depends: - - __unix - - python - - python >=3.10 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/click?source=hash-mapping - run_exports: {} - size: 104999 - timestamp: 1779900548735 + size: 64487 + timestamp: 1786835648298 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda sha256: 5b5c96afdd801dd9c3b78ebc2cd9a9f3ce34186257415d394dde1aa8468aa3c0 md5: 8a0d65027e25e367f9f1754f0604e8de @@ -11666,8 +7735,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/click?source=hash-mapping run_exports: {} size: 106227 timestamp: 1783085395110 @@ -11680,8 +7747,6 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/click?source=hash-mapping run_exports: {} size: 107155 timestamp: 1783085363526 @@ -11693,8 +7758,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/click-plugins?source=hash-mapping run_exports: {} size: 12683 timestamp: 1750848314962 @@ -11706,8 +7769,6 @@ packages: - python >=3.9,<4.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cligj?source=hash-mapping run_exports: {} size: 12521 timestamp: 1733750069604 @@ -11725,8 +7786,6 @@ packages: - pyyaml >=6.0.2 license: MIT license_family: MIT - purls: - - pkg:pypi/clio-tools?source=hash-mapping run_exports: {} size: 23713 timestamp: 1782229072527 @@ -11738,8 +7797,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cloudpickle?source=hash-mapping run_exports: {} size: 27353 timestamp: 1765303462831 @@ -11751,8 +7808,6 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cmap?source=hash-mapping run_exports: {} size: 678920 timestamp: 1775317331599 @@ -11763,8 +7818,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/colorama?source=hash-mapping run_exports: {} size: 27011 timestamp: 1733218222191 @@ -11776,8 +7829,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/comm?source=hash-mapping run_exports: {} size: 14690 timestamp: 1753453984907 @@ -11795,8 +7846,6 @@ packages: - zstandard license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/conda-index?source=hash-mapping run_exports: {} size: 210538 timestamp: 1782239750810 @@ -11808,39 +7857,37 @@ packages: - pyyaml >=6.0.0,<7.0.0 license: MIT license_family: MIT - purls: - - pkg:pypi/conda-inject?source=hash-mapping run_exports: {} size: 10327 timestamp: 1717043667069 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.4.2-pyhd8ed1ab_0.conda - sha256: 14545088118baa0d7597f574f2e5b2244817a8def79c8c8c065986ff22acd99d - md5: b8d70ceaeb03c3fb4fcfa0ace49272dc +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda + sha256: b7cc67470a361330edd8d9e331cafd4807dd77bc766ca197a3783ed3db66dfeb + md5: 49592004caf4336815fd5282bbb72172 depends: - - boltons >=23.0.0 - - libmambapy >=2.0.0 - - msgpack-python >=1.1.1 - python >=3.10 - - requests >=2.28.0,<3 - - zstandard >=0.15 + - libmambapy >=2.0.0,!=2.6.2 + - boltons >=23.0.0 + - python constrains: - - conda >=25.9 + - conda >=26.5 license: BSD-3-Clause license_family: BSD - size: 59960 - timestamp: 1778768196921 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.0-pyhd8ed1ab_0.conda - sha256: 3a9934ecf483d46b56a59547c63bd40bef4c398bfc671acb2ba0f98a045ccb8b - md5: 6248ea5c3194d00fb1db6d68fcdcfd3e - depends: - - conda >=25.9.0 - - pydantic >=2,<3 + run_exports: {} + size: 46828 + timestamp: 1784200590190 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda + sha256: eb20fc63ce6438e10d7c64890bd23d0c68f62a479f2bae8b3c892e13949daf03 + md5: 7f1b127b170f9f0d01eca4b54a046ee5 + depends: + - conda >=26.3.0 + - pydantic >=2.12.5,<3 - python >=3.10 - ruamel.yaml license: BSD-3-Clause license_family: BSD - size: 20396 - timestamp: 1778023975685 + run_exports: {} + size: 20633 + timestamp: 1783438353470 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda sha256: 63d5b510644a01b4c93738304cb0dc8a336542fc335b0b77f3b355ba7d119260 md5: 3f1854f1ec2090538fb49b7078dbb9de @@ -11852,8 +7899,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/conda-package-handling?source=hash-mapping run_exports: {} size: 256182 timestamp: 1781015592350 @@ -11865,8 +7910,6 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/conda-package-streaming?source=hash-mapping run_exports: {} size: 23954 timestamp: 1781293054998 @@ -11880,21 +7923,20 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/conda-rattler-solver?source=hash-mapping run_exports: {} size: 37334 timestamp: 1781212602267 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.0-pyhd8ed1ab_0.conda - sha256: ef5313a54fa530cc95b3026a9b03557a836749a986a57e737f684ca41bf92cb9 - md5: 76e0899bd902fd2779307e76748d3d8a +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda + sha256: efd3b7b79a0e3993a174ab0140794479e2479b59fd053605acaaa25718f0f013 + md5: ac705504abf28bb924a021a3d5f4b55f depends: - conda >=26.1.1 - python >=3.10 license: BSD-3-Clause license_family: BSD - size: 22216 - timestamp: 1778152874928 + run_exports: {} + size: 22510 + timestamp: 1784724984642 - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda sha256: 7f8ea42a8411b433ec7244dfd30637d90564256c13a114dbe42455fe032ae89c md5: 12389a21e7f69704b0ae77f44355e30b @@ -11904,8 +7946,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/configargparse?source=hash-mapping run_exports: {} size: 45817 timestamp: 1773233306055 @@ -11916,21 +7956,20 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/connection-pool?source=hash-mapping run_exports: {} size: 8331 timestamp: 1608581999360 -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.6-py314hd8ed1ab_100.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda noarch: generic - sha256: 7a548856ef5307890a8cadfc196655117658f8c24589ce175caa4c1c2ded9d13 - md5: b28fe35fd43d5f425c0dccbe5b5039fd + sha256: 9d8f07af6f8a205932d010cf1f3993e16a81b6e9321e6b2670f4e8be36109ed5 + md5: b710ef6c508928496586b4496e9ab52e depends: - python >=3.14,<3.15.0a0 - python_abi * *_cp314 license: Python-2.0 - size: 49333 - timestamp: 1781254618863 + run_exports: {} + size: 51095 + timestamp: 1788160819721 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 md5: 4c2a8fef270f6c69591889b93f9f55c1 @@ -11939,41 +7978,16 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cycler?source=hash-mapping run_exports: {} size: 14778 timestamp: 1764466758386 -- conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda - sha256: 2ada45db64509bc5c119cbd7d68953b5ec7a9341cc9fad7cbe6a4f029e8af0fb - md5: a2d2a05abf6076c3d041ec91b2235823 - depends: - - python >=3.10 - - dask-core >=2026.6.0,<2026.6.1.0a0 - - distributed >=2026.6.0,<2026.6.1.0a0 - - cytoolz >=0.11.2 - - lz4 >=4.3.2 - - numpy >=1.26 - - pandas >=2.0 - - bokeh >=3.1.0 - - jinja2 >=2.10.3 - - pyarrow >=16.0 - - python - constrains: - - openssl !=1.1.1e - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 11216 - timestamp: 1781272553165 -- conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.7.1-pyhc364b38_0.conda - sha256: 6fe2135c130f5832841d5fa5cd6dbadf82d84f61858ae8e2dfadf71ab1c8fb88 - md5: 07d4f6b76ccd0a3721c0964dac50db99 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda + sha256: 757750c674117059fde8114f305591f1b4ee8f446b11274278151e88fdcf61c6 + md5: e79afa78ebd31bb1e76de5fe117edb29 depends: - python >=3.10 - - dask-core >=2026.7.1,<2026.7.2.0a0 - - distributed >=2026.7.1,<2026.7.2.0a0 + - dask-core >=2026.8.0,<2026.8.1.0a0 + - distributed >=2026.8.0,<2026.8.1.0a0 - cytoolz >=0.11.2 - lz4 >=4.3.2 - numpy >=1.26 @@ -11985,35 +7999,12 @@ packages: constrains: - openssl !=1.1.1e license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 10399 - timestamp: 1784056179014 -- conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda - sha256: ce64f55e95912bff84119f05a296c90f8df57ecf9c3100bd8a6beca1cedabbb7 - md5: 56a3cae23de84509452da890fb2bfd85 - depends: - - python >=3.10 - - click >=8.1 - - cloudpickle >=3.0.0 - - fsspec >=2021.9.0 - - packaging >=20.0 - - partd >=1.4.0 - - pyyaml >=5.4.1 - - toolz >=0.12.0 - - importlib-metadata >=4.13.0 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/dask?source=hash-mapping run_exports: {} - size: 1069053 - timestamp: 1781221472758 -- conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.7.1-pyhc364b38_0.conda - sha256: feef96841d636fc35c3fef7c7276fa9c175baf4444896073bee65f08abe6ac91 - md5: 7e9fcc61c119f585f2b7b491147df821 + size: 10415 + timestamp: 1787917206356 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda + sha256: 1126e38c6763e5c9ab3885f859c0111bd5e7abcf346a9037655177ef7ae06909 + md5: 2ccdf45d2372e15c7edcb7a72d573e76 depends: - python >=3.10 - click >=8.1 @@ -12027,11 +8018,9 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/dask?source=hash-mapping run_exports: {} - size: 1074755 - timestamp: 1784031128995 + size: 1071072 + timestamp: 1787845029550 - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda sha256: 430bd9d731b265f0bedb3183ac3ecfaa1656390c092b6e864ff8cc1229843c8c md5: 61dcf784d59ef0bd62c57d982b154ace @@ -12039,50 +8028,18 @@ packages: - python >=3.10 license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/decorator?source=hash-mapping run_exports: {} size: 16102 timestamp: 1779115228886 -- conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda - sha256: 2386b3b1dabe713b46d46c21758e03bc2c080a784fdd225f46c20b3b7e45c854 - md5: 2a6851a6c69ce7c0b03a89d5d4209257 - depends: - - python >=3.10 - - click >=8.0 - - cloudpickle >=3.0.0 - - cytoolz >=0.12.0 - - dask-core >=2026.6.0,<2026.6.1.0a0 - - jinja2 >=2.10.3 - - locket >=1.0.0 - - msgpack-python >=1.0.2 - - packaging >=20.0 - - psutil >=5.8.0 - - pyyaml >=5.4.1 - - sortedcontainers >=2.0.5 - - tblib >=1.6.0 - - toolz >=0.12.0 - - tornado >=6.2.0 - - zict >=3.0.0 - - python - constrains: - - openssl !=1.1.1e - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/distributed?source=hash-mapping - run_exports: {} - size: 838296 - timestamp: 1781563082788 -- conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.7.1-pyhc364b38_0.conda - sha256: bae0d85df264228cbd485b444bd1f5135e32093e32503aa72e4b4b61636d12b2 - md5: f06a1abc946ca9b6107851cf148621f3 +- conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda + sha256: 3dd953eba3c59ecfac0e8cdaa47d5017f48a1843c8b0ad07cb743563e9d64bc6 + md5: f382c4eed5376ee372a6e67a0f6cd4d9 depends: - python >=3.10 - click >=8.0 - cloudpickle >=3.0.0 - cytoolz >=0.12.0 - - dask-core >=2026.7.1,<2026.7.2.0a0 + - dask-core >=2026.8.0,<2026.8.1.0a0 - jinja2 >=2.10.3 - locket >=1.0.0 - msgpack-python >=1.0.2 @@ -12098,12 +8055,9 @@ packages: constrains: - openssl !=1.1.1e license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/distributed?source=hash-mapping run_exports: {} - size: 843078 - timestamp: 1784043304773 + size: 840592 + timestamp: 1787863556779 - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda sha256: 5603c7d0321963bb9b4030eadabc3fd7ca6103a38475b4e0ed13ed6d97c86f4e md5: 0a2014fd9860f8b1eaa0b1f3d3771a08 @@ -12111,8 +8065,6 @@ packages: - python >=3.9 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/distro?source=hash-mapping run_exports: {} size: 41773 timestamp: 1734729953882 @@ -12122,8 +8074,6 @@ packages: depends: - python >=3.10 license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 - purls: - - pkg:pypi/docutils?source=hash-mapping run_exports: {} size: 438002 timestamp: 1766092633160 @@ -12134,8 +8084,6 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/dpath?source=hash-mapping run_exports: {} size: 21853 timestamp: 1762165431693 @@ -12150,8 +8098,6 @@ packages: - requests license: MIT license_family: MIT - purls: - - pkg:pypi/entsoe-py?source=hash-mapping run_exports: {} size: 952643 timestamp: 1776204877247 @@ -12162,8 +8108,6 @@ packages: - python >=3.10 - typing_extensions >=4.6.0 license: MIT and PSF-2.0 - purls: - - pkg:pypi/exceptiongroup?source=hash-mapping run_exports: {} size: 21333 timestamp: 1763918099466 @@ -12174,19 +8118,18 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/executing?source=hash-mapping run_exports: {} size: 30753 timestamp: 1756729456476 -- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.4-pyhd8ed1ab_0.conda - sha256: feb5c13cc8f256212a979783a7645abd7e27925c51ee5431babbc0efc661cdfd - md5: 66f138d7a6dffb5c959cc4bf6dc2b797 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda + sha256: c2c2527101fea8d2fbae3883d3328a4cd225e8fc3f133b49504acb7a8cecf6fe + md5: 0171dc5d54fdbb3f6e55f285f805e0fe depends: - python >=3.10 license: Unlicense - size: 36989 - timestamp: 1781381078337 + run_exports: {} + size: 78622 + timestamp: 1787521663311 - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda sha256: 782fa186d7677fd3bc1ff7adb4cc3585f7d2c7177c30bcbce21f8c177135c520 md5: a6997a7dcd6673c0692c61dfeaea14ab @@ -12199,8 +8142,6 @@ packages: - xyzservices license: MIT license_family: MIT - purls: - - pkg:pypi/folium?source=hash-mapping run_exports: {} size: 82665 timestamp: 1750113928159 @@ -12209,7 +8150,6 @@ packages: md5: 0c96522c6bdaed4b1566d11387caaf45 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 397370 timestamp: 1566932522327 @@ -12218,7 +8158,6 @@ packages: md5: 34893075a5c9e55cdafac56607368fc6 license: OFL-1.1 license_family: Other - purls: [] run_exports: {} size: 96530 timestamp: 1620479909603 @@ -12227,7 +8166,6 @@ packages: md5: 4d59c254e01d9cde7957100457e2d5fb license: OFL-1.1 license_family: Other - purls: [] run_exports: {} size: 700814 timestamp: 1620479612257 @@ -12236,7 +8174,6 @@ packages: md5: 49023d73832ef61042f6a237cb2687e7 license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other - purls: [] run_exports: {} size: 1620504 timestamp: 1727511233259 @@ -12247,7 +8184,6 @@ packages: - fonts-conda-forge license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 3667 timestamp: 1566974674465 @@ -12261,7 +8197,6 @@ packages: - font-ttf-source-code-pro license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 4059 timestamp: 1762351264405 @@ -12277,8 +8212,6 @@ packages: - fonttools_no_compile license: MIT license_family: MIT - purls: - - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 846038 timestamp: 1778770337113 @@ -12290,23 +8223,9 @@ packages: - python license: LGPL-3.0-only license_family: LGPL - purls: - - pkg:pypi/frozendict?source=hash-mapping run_exports: {} size: 34300 timestamp: 1781203600051 -- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda - sha256: fe0156e6d658be3531aad2a99e42e8ad1ee23c69837d469c44c1b6010373913d - md5: 7d7e6c826ba0743fc491ebee0e7b899c - depends: - - python >=3.10 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fsspec?source=hash-mapping - run_exports: {} - size: 149709 - timestamp: 1781615868173 - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda sha256: 3cd1c985695d8114bdba2a4a38c87e86d633fadd7cfe7a6733ebb3fe807fdc86 md5: b9176565976c773a0739bd83deaf06cc @@ -12314,8 +8233,6 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fsspec?source=hash-mapping run_exports: {} size: 151868 timestamp: 1785325238671 @@ -12333,7 +8250,6 @@ packages: - xyzservices license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 8267 timestamp: 1782507446937 @@ -12348,8 +8264,6 @@ packages: - shapely >=2.0.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/geopandas?source=hash-mapping run_exports: {} size: 255909 timestamp: 1782507445933 @@ -12361,22 +8275,21 @@ packages: - smmap >=3.0.1,<6 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/gitdb?source=hash-mapping run_exports: {} size: 53136 timestamp: 1735887290843 -- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.50-pyhd8ed1ab_0.conda - sha256: 718c9d0cc287ffda978996c4105ddd2863dd7bad7fb5794cdd365bd809ef2fa5 - md5: 98958318a01373a615f119b1040b3027 +- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda + sha256: 51cb1c112e47ccda9a0c7850fa5ff7c4eaa8bd65a3f7a81861623b8acff00d9e + md5: a4d052a35ae133a5fef5bc34fd337cd8 depends: - - gitdb >=4.0.1,<5 - python >=3.10 + - gitdb >=4.0.1,<5 - typing_extensions >=3.10.0.2 + - python license: BSD-3-Clause - license_family: BSD - size: 162193 - timestamp: 1778065820061 + run_exports: {} + size: 178174 + timestamp: 1787936103205 - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda sha256: 4d3f8a3a42a88493cdae21f54efc57154c23403901d5d54d05b9fa6991b9bfc7 md5: 36a2d7837350f7e8da83bcaad552d0b5 @@ -12395,8 +8308,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/gregor?source=hash-mapping run_exports: {} size: 16487 timestamp: 1782753092500 @@ -12409,26 +8320,9 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/h11?source=hash-mapping run_exports: {} size: 39069 timestamp: 1767729720872 -- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 - md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 - depends: - - python >=3.10 - - hyperframe >=6.1,<7 - - hpack >=4.1,<5 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/h2?source=hash-mapping - run_exports: {} - size: 95967 - timestamp: 1756364871835 - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda sha256: 307dd6ec90140c3cf4171071b0e5e870abec314f4565c1edd5bc433e942cdcc0 md5: e652ac7756069c456d0da2a922cd7df5 @@ -12439,8 +8333,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/h2?source=hash-mapping run_exports: {} size: 100789 timestamp: 1785796355216 @@ -12451,8 +8343,6 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/hpack?source=hash-mapping run_exports: {} size: 32884 timestamp: 1782283986153 @@ -12469,8 +8359,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/httpcore?source=hash-mapping run_exports: {} size: 49483 timestamp: 1745602916758 @@ -12485,8 +8373,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/httpx?source=hash-mapping run_exports: {} size: 63082 timestamp: 1733663449209 @@ -12498,8 +8384,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/humanfriendly?source=hash-mapping run_exports: {} size: 73563 timestamp: 1733928021866 @@ -12512,8 +8396,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/humanfriendly?source=hash-mapping run_exports: {} size: 74084 timestamp: 1733928364561 @@ -12524,38 +8406,31 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/hyperframe?source=hash-mapping run_exports: {} size: 17397 timestamp: 1737618427549 -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda - sha256: c75632ea624aa450a394f570749420c5a2e0997d0216bc29d5d45b0f39df0426 - md5: 577b04680ae422adb86fc60d7b940659 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda + sha256: 1c35a59c1545ad0fdaddf1fbde7bcfa6ef41a8d68c3a2b0b4a291be00676163e + md5: a39ae05027e9b707742e41b30d296b75 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/idna?source=hash-mapping run_exports: {} - size: 163869 - timestamp: 1781620148226 -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - sha256: 43e2a5497cad1598ff88a3e69f69bc88b7b8f141fa63c60eab5db296317318b8 - md5: ffc17e785d64e12fc311af9184221839 + size: 177433 + timestamp: 1787059857580 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda + sha256: 95a074a7d3f58b3494c068d4789c364dcb591c56b70ca6b12f149a540f9aeea4 + md5: 77ec68e0aa61c3fff9ecbf840f93d64e depends: - python >=3.10 - zipp >=3.20 - python license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/importlib-metadata?source=hash-mapping run_exports: {} - size: 34766 - timestamp: 1779714582554 + size: 34920 + timestamp: 1787943971257 - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda sha256: e1a9e3b1c8fe62dc3932a616c284b5d8cbe3124bbfbedcf4ce5c828cb166ee19 md5: 9614359868482abba1bd15ce465e3c42 @@ -12563,8 +8438,6 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/iniconfig?source=hash-mapping run_exports: {} size: 13387 timestamp: 1760831448842 @@ -12578,8 +8451,6 @@ packages: - toml >=0.10.2 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/ipdb?source=hash-mapping run_exports: {} size: 18713 timestamp: 1734884952029 @@ -12607,8 +8478,6 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 137725 timestamp: 1781101860049 @@ -12635,8 +8504,6 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 138046 timestamp: 1781101760172 @@ -12663,17 +8530,14 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 138635 timestamp: 1781101665847 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda - sha256: a3f76e06c31bcf1bda0f633d5c9f1c834286b4f6decc6626067a6cffee283318 - md5: fbd58549b374103c1a80577f09a328ef +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda + sha256: 8165024016181bf7d32a027180239cf15c4447ec02080b31dcae731809c0b3e2 + md5: 6ebd34681d8bc56e474fac76999de45a depends: - __unix - - decorator >=5.1.0 - ipython_pygments_lexers >=1.0.0 - jedi >=0.18.2 - matplotlib-inline >=0.1.6 @@ -12687,15 +8551,14 @@ packages: - pexpect >4.6 - python license: BSD-3-Clause - license_family: BSD - size: 652893 - timestamp: 1780654403616 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyhe2676ad_0.conda - sha256: 3c5f2269e357118abfa49d21fdca3a35420ee5b251c2f5cb705310b38843db40 - md5: bf12187c2d1ef0bb63df01ace31ff26b + run_exports: {} + size: 730292 + timestamp: 1787921913409 +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyhe2676ad_0.conda + sha256: de5052db34cd414d5993dc7330c7f9ce422fadd6e5f3c1422d48040e0319c633 + md5: c2cf78253e434f1283cfd276ec9abd2f depends: - __win - - decorator >=5.1.0 - ipython_pygments_lexers >=1.0.0 - jedi >=0.18.2 - matplotlib-inline >=0.1.6 @@ -12709,9 +8572,9 @@ packages: - colorama >=0.4.4 - python license: BSD-3-Clause - license_family: BSD - size: 652076 - timestamp: 1780654438137 + run_exports: {} + size: 729408 + timestamp: 1787921907454 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda sha256: 894682a42a7d659ae12878dbcb274516a7031bbea9104e92f8e88c1f2765a104 md5: bd80ba060603cc228d9d81c257093119 @@ -12720,8 +8583,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/ipython-pygments-lexers?source=hash-mapping run_exports: {} size: 13993 timestamp: 1737123723464 @@ -12733,8 +8594,6 @@ packages: - parso >=0.8.6,<0.9.0 - python license: Apache-2.0 AND MIT - purls: - - pkg:pypi/jedi?source=hash-mapping run_exports: {} size: 2715215 timestamp: 1782251948616 @@ -12747,8 +8606,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/jinja2?source=hash-mapping run_exports: {} size: 120685 timestamp: 1764517220861 @@ -12760,54 +8617,9 @@ packages: - setuptools license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/joblib?source=hash-mapping run_exports: {} size: 226448 timestamp: 1765794135253 -- conda: https://conda.anaconda.org/conda-forge/noarch/jsonpatch-1.33-pyhd8ed1ab_1.conda - sha256: 304955757d1fedbe344af43b12b5467cca072f83cce6109361ba942e186b3993 - md5: cb60ae9cf02b9fcb8004dec4089e5691 - depends: - - jsonpointer >=1.9 - - python >=3.9 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/jsonpatch?source=hash-mapping - run_exports: {} - size: 17311 - timestamp: 1733814664790 -- conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - sha256: a3d10301b6ff399ba1f3d39e443664804a3d28315a4fb81e745b6817845f70ae - md5: 89bf346df77603055d3c8fe5811691e6 - depends: - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/jsonpointer?source=hash-mapping - run_exports: {} - size: 14190 - timestamp: 1774311356147 -- conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - sha256: db973a37d75db8e19b5f44bbbdaead0c68dde745407f281e2a7fe4db74ec51d7 - md5: ada41c863af263cc4c5fcbaff7c3e4dc - depends: - - attrs >=22.2.0 - - jsonschema-specifications >=2023.3.6 - - python >=3.10 - - referencing >=0.28.4 - - rpds-py >=0.25.0 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/jsonschema?source=hash-mapping - run_exports: {} - size: 82356 - timestamp: 1767839954256 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda sha256: 328756a4555941d57af4a5f553e5d8598e9f3b37db028f557e30f9f0b3086db6 md5: 204b5ca91eba7738790ecc333facbbbe @@ -12819,8 +8631,7 @@ packages: - rpds-py >=0.25.0 - python license: MIT - purls: - - pkg:pypi/jsonschema?source=compressed-mapping + license_family: MIT run_exports: {} size: 82084 timestamp: 1787579299493 @@ -12833,14 +8644,12 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/jsonschema-specifications?source=hash-mapping run_exports: {} size: 19236 timestamp: 1757335715225 -- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.1-pyhcf101f3_0.conda - sha256: 48b18974cc93b2c0d2681563237034e521f51d1878f0bbc6a5a67ca31b1608a6 - md5: 49440e66df843bee2273937e8032ec43 +- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.10.0-pyhcf101f3_0.conda + sha256: 94f11bbdbac51a68415fb0a8bb441fb3475c0138e778664a3fb48f5698688ebf + md5: 85130259a26c023f53108dc9ab1cfb44 depends: - jupyter_core >=5.1 - python >=3.10 @@ -12851,12 +8660,9 @@ packages: - typing_extensions >=4.13.0 - python license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/jupyter-client?source=hash-mapping run_exports: {} - size: 117954 - timestamp: 1781019994076 + size: 118962 + timestamp: 1787929615607 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyh6dadd2b_0.conda sha256: ed709a6c25b731e01563521ef338b93986cd14b5bc17f35e9382000864872ccc md5: a8db462b01221e9f5135be466faeb3e0 @@ -12871,8 +8677,6 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/jupyter-core?source=hash-mapping run_exports: {} size: 64679 timestamp: 1760643889625 @@ -12890,8 +8694,6 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/jupyter-core?source=hash-mapping run_exports: {} size: 65503 timestamp: 1760643864586 @@ -12902,28 +8704,9 @@ packages: - python >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/locket?source=hash-mapping run_exports: {} size: 8250 timestamp: 1650660473123 -- conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda - sha256: 967841d300598b17f76ba812e7dae642176692ed2a6735467b93c2b2debe35c1 - md5: cc293b4cad9909bf66ca117ea90d4631 - depends: - - networkx >=3.2 - - numpy >=1.26 - - pandas >=2.1 - - python >=3.11 - - scikit-learn >=1.4 - - scipy >=1.12 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/mapclassify?source=hash-mapping - run_exports: {} - size: 810830 - timestamp: 1752271625200 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda sha256: 791137f3e107c73315c74bb0c7be34e5f7c36da1828efbc4584544311121484c md5: 0323c95182d828bb1bcf708c4077db3b @@ -12936,8 +8719,6 @@ packages: - scipy >=1.14 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/mapclassify?source=hash-mapping run_exports: {} size: 448635 timestamp: 1786480374013 @@ -12949,8 +8730,6 @@ packages: - traitlets license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/matplotlib-inline?source=hash-mapping run_exports: {} size: 15725 timestamp: 1778264403247 @@ -12961,8 +8740,6 @@ packages: - python >=3.9 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/munkres?source=hash-mapping run_exports: {} size: 15851 timestamp: 1749895533014 @@ -12973,50 +8750,35 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/mypy-extensions?source=hash-mapping run_exports: {} size: 11766 timestamp: 1745776666688 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - sha256: dd2744a501f2db0aef084566bf3d0c2b312661dc91beb5a4cc97d27cdda0a959 - md5: 9450fb40fb1e147d0bcbdf07cd02ca96 - depends: - - python >=3.10 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/narwhals?source=hash-mapping - run_exports: {} - size: 285532 - timestamp: 1780672242196 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.24.0-pyhcf101f3_0.conda - sha256: 57f525a1c55b08c3204fab05d2c74a3e9c2172d7f08f1ecaa07e19865cc1d7cf - md5: 42ef6cbb3e1d0e6689b9dd160f560eae +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + sha256: e48d972fae8e32ca43eefd1a9649afa3a82b4dda5fe12caa441f0bc9e8d1938d + md5: 0117bf65e45c951a9b0004172cfaeef6 depends: - python >=3.10 - python license: MIT license_family: MIT - purls: - - pkg:pypi/narwhals?source=hash-mapping run_exports: {} - size: 289946 - timestamp: 1783943716915 -- conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - sha256: 7a5bd30a2e7ddd7b85031a5e2e14f290898098dc85bea5b3a5bf147c25122838 - md5: bbe1963f1e47f594070ffe87cdf612ea + size: 293989 + timestamp: 1787261063562 +- conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.1-pyhcf101f3_0.conda + sha256: d85d76827ff732e1639f50f45e4d7d3387a27abd857b194dcbb5bb4166c2a7c2 + md5: 2fbbf92e1173ae024f0b12759c1e64a1 depends: - jsonschema >=2.6 - jupyter_core >=4.12,!=5.0.* - - python >=3.9 + - python >=3.10 - python-fastjsonschema >=2.15 - traitlets >=5.1 + - python license: BSD-3-Clause license_family: BSD - size: 100945 - timestamp: 1733402844974 + run_exports: {} + size: 107750 + timestamp: 1787133512599 - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda sha256: e6768ceef038f4d7e083de7e393f5dd7d672b937e2bda570b740f6399b686689 md5: fcd832bfd4749e9b246112b6894f97fc @@ -13025,8 +8787,6 @@ packages: - python license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/nest-asyncio2?source=hash-mapping run_exports: {} size: 15903 timestamp: 1770973502283 @@ -13043,18 +8803,19 @@ packages: - pandas >=2.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/networkx?source=hash-mapping run_exports: {} size: 1587439 timestamp: 1765215107045 -- conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_1.conda - sha256: 2a909594ca78843258e4bda36e43d165cda844743329838a29402823c8f20dec - md5: 59659d0213082bc13be8500bab80c002 +- conda: https://conda.anaconda.org/conda-forge/noarch/nlohmann_json-abi-3.12.0-h0f90c79_2.conda + sha256: 30d271dd63391e053aa3c393c5d93fb622dec5d50a64107171ee28242660608e + md5: 849b8852621992c3f3a1901c939a0399 license: MIT license_family: MIT - size: 4335 - timestamp: 1758194464430 + run_exports: + weak: + - nlohmann_json-abi ==3.12.0 + size: 4365 + timestamp: 1785920576349 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 md5: 58335b26c38bf4a20f399384c33cbcf9 @@ -13063,35 +8824,20 @@ packages: - python license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/packaging?source=hash-mapping run_exports: {} size: 62477 timestamp: 1745345660407 -- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 - md5: 4c06a92e74452cfa53623a81592e8934 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + sha256: c432626b16768b8dab228bfb706f7060c2d462a21c516d240f68f2f902b5a044 + md5: 936687ed80f295a1f5dbcf8bd34c252c depends: - - python >=3.8 + - python >=3.9 - python license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/packaging?source=hash-mapping run_exports: {} - size: 91574 - timestamp: 1777103621679 -- conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.31.1-h27fc3a9_0.conda - sha256: 6f38ed60c3ba34c729708701f5a9798dfa396f62c5db704ce2e16aae78b86ed0 - md5: 33197d0b44a95fa6ea53c04db06a4f94 - depends: - - pandera-base ==0.31.1 pyhcf101f3_0 - - pandas >=2.1.1 - - numpy >=1.24.4 - license: MIT - license_family: MIT - size: 4431 - timestamp: 1777000436203 + size: 116363 + timestamp: 1785888127370 - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda sha256: c686bea5de04fd6a93de31c9be18bfd24b9efe672a5e2e8692d34a35b282cbb8 md5: f6b96a51cda3cf9a068fd748c8155ced @@ -13101,25 +8847,9 @@ packages: - numpy >=1.24.4 license: MIT license_family: MIT - purls: [] run_exports: {} size: 4464 timestamp: 1782916642380 -- conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.31.1-pyhcf101f3_0.conda - sha256: d197f0ad887b9896f118e6bf9604f4ca955de49561b776e9c69b8e7cd4382cca - md5: 531ff805a1551c6caaf976d577ea233c - depends: - - python >=3.10 - - packaging >=20.0 - - pydantic - - typeguard - - typing_extensions - - typing_inspect >=0.6.0 - - python - license: MIT - license_family: MIT - size: 230747 - timestamp: 1777000436203 - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda sha256: 81eb4d5f49a2f98b7dbeb60b134ecef4b90cdf21b2af8a024d5a0fe2858e914b md5: c78daae943cb94e6e6b2eab16e16a18f @@ -13133,8 +8863,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/pandera?source=hash-mapping run_exports: {} size: 268608 timestamp: 1782916642380 @@ -13146,8 +8874,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/parso?source=hash-mapping run_exports: {} size: 82472 timestamp: 1777722955579 @@ -13160,8 +8886,6 @@ packages: - toolz license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/partd?source=hash-mapping run_exports: {} size: 20884 timestamp: 1715026639309 @@ -13172,8 +8896,6 @@ packages: - python >=3.10 license: MPL-2.0 license_family: MOZILLA - purls: - - pkg:pypi/pathspec?source=hash-mapping run_exports: {} size: 56559 timestamp: 1777271601895 @@ -13184,30 +8906,29 @@ packages: - ptyprocess >=0.5 - python >=3.9 license: ISC - purls: - - pkg:pypi/pexpect?source=hash-mapping run_exports: {} size: 53561 timestamp: 1733302019362 -- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh145f28c_0.conda - sha256: 9e673d3c6003f416df11670a14b026a04e3a45ebec55357987e15b860f138f2a - md5: 733cc07ed34162ac50b936464b163366 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda + sha256: 0f7021cfb3ff454c91a5e28e1f5060906a52c6d1cde3f879a7d03ac33c28b1c3 + md5: 573cb3ed111004230ed3de650653cd4d depends: - python >=3.13.0a0 license: MIT license_family: MIT - size: 1202377 - timestamp: 1780262809860 -- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - sha256: 9e5e1fd3506ccfc4d444fc4d2d39b0ed097d5d0e3bd3d4bdf6bcc81aaf66860d - md5: 2c5ef45db85d34799771629bd5860fd7 + run_exports: {} + size: 1198163 + timestamp: 1785914482439 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda + sha256: 4496a7e0b584a388b3580ab594a4f384696ab7d702282588fd33a7ee38af983c + md5: 152ec36401f710145a7db03475594410 depends: - python >=3.10 - python license: MIT - license_family: MIT - size: 26308 - timestamp: 1779972894916 + run_exports: {} + size: 27461 + timestamp: 1787881635603 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -13216,40 +8937,28 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/pluggy?source=hash-mapping run_exports: {} size: 25877 timestamp: 1764896838868 -- conda: https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda - sha256: 2f227e17b3c0346112815faa605502b66c1c4511a856127f2899abf15a98a2cf - md5: d8d7293c5b37f39b2ac32940621c6592 - license: BSD-3-Clause AND (GPL-2.0-only OR GPL-3.0-only) - license_family: OTHER - purls: [] - run_exports: {} - size: 2348171 - timestamp: 1675353652214 -- conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - sha256: 4817651a276016f3838957bfdf963386438c70761e9faec7749d411635979bae - md5: edb16f14d920fb3faf17f5ce582942d6 +- conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + sha256: efe8def2c93aa34cd8d3c9af1dc4c7d312791cf769d8b2b615e32733e6df6051 + md5: 39c92a39517316e5001d645ae63d9ab9 depends: - python >=3.10 - wcwidth constrains: - - prompt_toolkit 3.0.52 + - prompt_toolkit 3.0.53 license: BSD-3-Clause license_family: BSD - size: 273927 - timestamp: 1756321848365 + run_exports: {} + size: 276081 + timestamp: 1785160613307 - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda sha256: a7713dfe30faf17508ec359e0bc7e0983f5d94682492469bd462cdaae9c64d83 md5: 7d9daffbb8d8e0af0f769dbbcd173a54 depends: - python >=3.9 license: ISC - purls: - - pkg:pypi/ptyprocess?source=hash-mapping run_exports: {} size: 19457 timestamp: 1733302371990 @@ -13260,8 +8969,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/pure-eval?source=hash-mapping run_exports: {} size: 16668 timestamp: 1733569518868 @@ -13270,7 +8977,6 @@ packages: md5: f0599959a2447c1e544e216bddf393fa license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - pybind11-abi ==11 @@ -13284,8 +8990,6 @@ packages: - setuptools license: LGPL-2.1-or-later license_family: LGPL - purls: - - pkg:pypi/pycountry?source=hash-mapping run_exports: {} size: 3105570 timestamp: 1718094617616 @@ -13297,28 +9001,23 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pycparser?source=hash-mapping run_exports: {} size: 55886 timestamp: 1779293633166 -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda - sha256: 69700e31165df070e9716315e042196aa92525dae5deb5107785847ab9f4189f - md5: 729843edafc0899b3348bd3f19525b9d +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + sha256: 701ed8122021f62e1c9a46bdf4932ada911c10afa56ba0459f56da940bcbe5a7 + md5: 2d0f9304fc1cb1e3e94e7b37c14cd70a depends: - typing-inspection >=0.4.2 - typing_extensions >=4.14.1 - python >=3.10 - annotated-types >=0.6.0 - - pydantic-core ==2.46.4 + - pydantic-core ==2.46.5 - python license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic?source=hash-mapping run_exports: {} - size: 346511 - timestamp: 1778103405862 + size: 346633 + timestamp: 1787941273167 - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda sha256: af7213a8ca077895e7e10c8f33d5de3436b8a26828422e8a113cc59c9277a3e2 md5: 15f6d0866b0997c5302fc230a566bc72 @@ -13329,23 +9028,20 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/pydot?source=hash-mapping run_exports: {} size: 150656 timestamp: 1766345630713 -- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 - md5: 16c18772b340887160c79a6acc022db0 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + sha256: f5f015ff1bc3e1b7fc08eee096b1865234189ae0b82bebdb86a5369116e42fa0 + md5: 2882dee445dfa45b0c5afce3ffb7730a depends: - python >=3.10 + - python license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/pygments?source=hash-mapping run_exports: {} - size: 893031 - timestamp: 1774796815820 + size: 959376 + timestamp: 1786995678795 - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda sha256: 417fba4783e528ee732afa82999300859b065dc59927344b4859c64aae7182de md5: 3687cc0b82a8b4c17e1f0eb7e47163d5 @@ -13354,8 +9050,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/pyparsing?source=hash-mapping run_exports: {} size: 110893 timestamp: 1769003998136 @@ -13367,8 +9061,6 @@ packages: - tomli >=1.1.0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyproject-hooks?source=hash-mapping run_exports: {} size: 15528 timestamp: 1733710122949 @@ -13381,8 +9073,6 @@ packages: - win_inet_pton license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pysocks?source=hash-mapping run_exports: {} size: 21784 timestamp: 1733217448189 @@ -13394,8 +9084,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pysocks?source=hash-mapping run_exports: {} size: 21085 timestamp: 1733217331982 @@ -13416,14 +9104,12 @@ packages: - pytest-faulthandler >=2 license: MIT license_family: MIT - purls: - - pkg:pypi/pytest?source=hash-mapping run_exports: {} size: 306724 timestamp: 1782127176429 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.5.0-pyhc364b38_0.conda - sha256: eb8d5e44fddee9033eb7cfdd5ea584b7594b50e31c7602bef553af0fd4ee9266 - md5: fa587158c0d768127faa1a3b4df01e5d +- conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda + sha256: d5bad775346fbd21cce7922b29cab2587fe599517654de65c3d2fe50a7126279 + md5: 1e696c762d003f8f4af971b6dccb8c1e depends: - python >=3.10 - colorama @@ -13435,12 +9121,9 @@ packages: constrains: - build <0 license: MIT - license_family: MIT - purls: - - pkg:pypi/build?source=hash-mapping run_exports: {} - size: 28946 - timestamp: 1778048948266 + size: 33461 + timestamp: 1787910624806 - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 md5: 5b8d21249ff20967101ffa321cab24e8 @@ -13450,30 +9133,30 @@ packages: - python license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/python-dateutil?source=hash-mapping run_exports: {} size: 233310 timestamp: 1751104122689 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - sha256: df9aa74e9e28e8d1309274648aac08ec447a92512c33f61a8de0afa9ce32ebe8 - md5: 23029aae904a2ba587daba708208012f +- conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda + sha256: fc4a704822df22defce49d0fb811fdc036a1fd3b579aeaa601228e9cfd198b3d + md5: aa75b7f096d17621bc307b3025b29461 depends: - - python >=3.9 + - python >=3.10 - python license: BSD-3-Clause license_family: BSD - size: 244628 - timestamp: 1755304154927 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.6-h4df99d1_100.conda - sha256: 84c129bdd6abcecac42a948f2670d17fe735d02d3a5a483a9b1f1bc33ba38c28 - md5: 224f69f177eb5aae6c9a6052846bf609 + run_exports: {} + size: 254446 + timestamp: 1786892280524 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda + sha256: 11fb369ceec6ac6cc3b3552854c399b97fa547e44d19ef699e48309310949e96 + md5: 397bdd86501d63b918ec7acfec8d5872 depends: - - cpython 3.14.6.* + - cpython 3.14.7.* - python_abi * *_cp314 license: Python-2.0 - size: 49315 - timestamp: 1781254664376 + run_exports: {} + size: 51092 + timestamp: 1788160828119 - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda sha256: 9c18fa1ebd0c839b6ea12e99d510a0968c2288cc423185cb34bf7332b97684e9 md5: 5cc7e2e78962dc902cae9e29c2aa4b2f @@ -13481,23 +9164,9 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/installer?source=hash-mapping run_exports: {} size: 239330 timestamp: 1778554210561 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - sha256: e943f9c15a6bdba2e1b9f423ab913b3f6b02197b0ef9f8e6b7464d78b59965b9 - md5: f6ad7450fc21e00ecc23812baed6d2e4 - depends: - - python >=3.10 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/tzdata?source=hash-mapping - run_exports: {} - size: 146639 - timestamp: 1777068997932 - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda sha256: 3f05db78cf8be33cf6dbc469664b8e3a01f3980d61d6d6bef48669b171896d8a md5: eefc8d916bd2e708d76d40398ef9a1ee @@ -13505,8 +9174,6 @@ packages: - python >=3.10 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/tzdata?source=hash-mapping run_exports: {} size: 146862 timestamp: 1783704822814 @@ -13518,7 +9185,6 @@ packages: - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 6958 timestamp: 1752805918820 @@ -13530,23 +9196,9 @@ packages: - python 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 6989 timestamp: 1752805904792 -- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - sha256: 5020863d629f584b5c057333a67a7aed43e3ed013ba15dd70f353501ccb5aff6 - md5: 03cb60f505ad3ada0a95277af5faeb1a - depends: - - python >=3.10 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/pytz?source=hash-mapping - run_exports: {} - size: 201747 - timestamp: 1777892201250 - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda sha256: ad774abda71c759baef515983bfedbba34d56d6227974c23715c04612f812a90 md5: eb2fb9070c0232e96ae5515d8b28e4f9 @@ -13555,8 +9207,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/pytz?source=hash-mapping run_exports: {} size: 201126 timestamp: 1785077087428 @@ -13577,8 +9227,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/rasterstats?source=hash-mapping run_exports: {} size: 24082 timestamp: 1779718549710 @@ -13593,8 +9241,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/referencing?source=hash-mapping run_exports: {} size: 51788 timestamp: 1760379115194 @@ -13612,29 +9258,9 @@ packages: - chardet >=3.0.2,<8 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/requests?source=hash-mapping run_exports: {} size: 68709 timestamp: 1778851103479 -- conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda - sha256: fa2fa8745ff8dce18d3243370b31726435b7f9448cdd214896f730291dc6aa55 - md5: 27583d44e139c520d9fdc1fd7aedd58d - depends: - - numpy >=1.23 - - packaging - - pyproj >=3.3 - - python >=3.12 - - rasterio >=1.3.7 - - scipy - - xarray >=2024.7.0 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/rioxarray?source=hash-mapping - run_exports: {} - size: 53405 - timestamp: 1761337252983 - conda: https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda sha256: 26884204704cb0399339c50b12ba715a2ef426fb90afecd39d68d6617890f722 md5: 3e549fac4b34fd5c8a89b9c0cc5bbd8e @@ -13648,23 +9274,9 @@ packages: - python license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/rioxarray?source=hash-mapping run_exports: {} size: 65705 timestamp: 1785242371731 -- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - sha256: 82088a6e4daa33329a30bc26dc19a98c7c1d3f05c0f73ce9845d4eab4924e9e1 - md5: 8e194e7b992f99a5015edbd4ebd38efd - depends: - - python >=3.10 - license: MIT - license_family: MIT - purls: - - pkg:pypi/setuptools?source=hash-mapping - run_exports: {} - size: 639697 - timestamp: 1773074868565 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda sha256: 9e200ee5f9ff19a4d94e4b51c4856d53dec849f91032f345cf0c6bc3d51a7183 md5: 62ac906f1cd582c6c264c95625cb9d6f @@ -13672,8 +9284,6 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/setuptools?source=compressed-mapping run_exports: {} size: 524488 timestamp: 1786282924579 @@ -13685,22 +9295,21 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/six?source=hash-mapping run_exports: {} size: 18455 timestamp: 1753199211006 -- conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.6.1-pyhcf101f3_0.conda - sha256: 58185ed2290aa6ef0a5de7012c2a581d75a3577b7e93de455c19fa10d344570a - md5: 8e4971d25d53f168dfb64d6f5ee1316d +- conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda + sha256: d0abe2d97b67d42aca30fcd34b3db6cd5de57b4a9ffcf4e04091dd9f4a0cf1e6 + md5: fd2e276dbbd1a2ae15ffdbdbabd908f1 depends: - python >=3.10 - wrapt - python license: MIT license_family: MIT - size: 57112 - timestamp: 1778331246761 + run_exports: {} + size: 57158 + timestamp: 1782573421136 - conda: https://conda.anaconda.org/conda-forge/noarch/smmap-5.0.3-pyhcf101f3_1.conda sha256: bc86861086db65c9b56dd6a1605755f41a9875b94fc3b69e137f686700d91aa1 md5: b899f1195be56d8215ed1973a8f409c3 @@ -13709,8 +9318,6 @@ packages: - python license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/smmap?source=hash-mapping run_exports: {} size: 27262 timestamp: 1781021238494 @@ -13721,8 +9328,6 @@ packages: - python >=3.10 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/sniffio?source=hash-mapping run_exports: {} size: 15698 timestamp: 1762941572482 @@ -13735,8 +9340,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/snuggs?source=hash-mapping run_exports: {} size: 11313 timestamp: 1733818738919 @@ -13747,23 +9350,9 @@ packages: - python >=3.9 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/sortedcontainers?source=hash-mapping run_exports: {} size: 28657 timestamp: 1738440459037 -- conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - sha256: 2afa5fe9331c09b4c4689ddf6ace8fc16c837eae547c57dab325b844072fdd77 - md5: 9e21f087f087f805debe877d88e00a14 - depends: - - python >=3.10 - license: MIT - license_family: MIT - purls: - - pkg:pypi/soupsieve?source=hash-mapping - run_exports: {} - size: 38802 - timestamp: 1779635534390 - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda sha256: 056d7a2e91e303a9ee37e580f6dde0511fc3fb72476581cc337aacf2cc747613 md5: ba33e6c8a46ee373fdf6dd8665212778 @@ -13771,8 +9360,6 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/soupsieve?source=compressed-mapping run_exports: {} size: 39439 timestamp: 1786202135509 @@ -13786,8 +9373,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/sqlmodel?source=hash-mapping run_exports: {} size: 30854 timestamp: 1771872849343 @@ -13801,8 +9386,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/stack-data?source=hash-mapping run_exports: {} size: 26988 timestamp: 1733569565672 @@ -13814,8 +9397,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/tabulate?source=hash-mapping run_exports: {} size: 43964 timestamp: 1772732795746 @@ -13827,11 +9408,21 @@ packages: - python license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/tblib?source=hash-mapping run_exports: {} size: 19397 timestamp: 1762956379123 +- conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda + sha256: 6a6329139f72dac12a8d30234f0191af03dc3bbeab1010b89b314f651180e9df + md5: 9715ba984ea99ba23d0d0399311937c1 + depends: + - python >=3.12 + - pandas >=3.0.3 + - pandera >=0.31.1 + - python + license: MIT + run_exports: {} + size: 46423 + timestamp: 1788163064651 - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda sha256: 32e75900d6a094ffe4290a8c9f1fa15744d9da8ff617aba4acaa0f057a065c34 md5: 043f0599dc8aa023369deacdb5ac24eb @@ -13840,8 +9431,6 @@ packages: - python license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/tenacity?source=hash-mapping run_exports: {} size: 31404 timestamp: 1770510172846 @@ -13852,8 +9441,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/threadpoolctl?source=hash-mapping run_exports: {} size: 23869 timestamp: 1741878358548 @@ -13864,8 +9451,6 @@ packages: - python >=3.6 license: MIT license_family: MIT - purls: - - pkg:pypi/throttler?source=hash-mapping run_exports: {} size: 12341 timestamp: 1691135604942 @@ -13877,8 +9462,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/toml?source=hash-mapping run_exports: {} size: 24017 timestamp: 1764486833072 @@ -13890,8 +9473,6 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/tomli?source=hash-mapping run_exports: {} size: 21561 timestamp: 1774492402955 @@ -13902,8 +9483,6 @@ packages: - python >=3.9 license: MIT license_family: MIT - purls: - - pkg:pypi/tomli-w?source=hash-mapping run_exports: {} size: 12680 timestamp: 1736962345843 @@ -13914,14 +9493,12 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/toolz?source=hash-mapping run_exports: {} size: 53978 timestamp: 1760707830681 -- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.68.3-pyh8f84b5b_0.conda - sha256: 613d5cc47571c0b66e31265ff1e0fa5bef0e7b7670f33c67f5a2c587faf6e5d1 - md5: 65094960cb7ed216b6049aab57205902 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyh8f84b5b_0.conda + sha256: a4d9e9da15b13f1ab047e7db412e2ed564bc615832e80213cf129b973c3abf4a + md5: 00953c3b729e03b0ae21f49fdf3441bb depends: - python >=3.10 - __unix @@ -13930,11 +9507,12 @@ packages: - envwrap >=0.2 - ipywidgets >=6.0 license: MPL-2.0 and MIT - size: 94965 - timestamp: 1781741268338 -- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.68.3-pyha7b4d00_0.conda - sha256: 41768105c1b4d2cfa3877b902f653275de26bb57d8877904791113329acc1fd4 - md5: 0b814124391b7e176bcc0cce445a3a36 + run_exports: {} + size: 98184 + timestamp: 1785172528905 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.70.0-pyha7b4d00_0.conda + sha256: 669cd06a4c7c1139f9c6b9850d16d71c1b4ce42375806976a03dba824283fef0 + md5: 7eac270516c8221cedc7f40a96d7fb8f depends: - python >=3.10 - colorama @@ -13944,18 +9522,20 @@ packages: - envwrap >=0.2 - ipywidgets >=6.0 license: MPL-2.0 and MIT - size: 94630 - timestamp: 1781741323148 -- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda - sha256: b89a823edf524956b94a2a4db974866e4501f05c68976eff458c5dcf07f88431 - md5: 37e3be7b6e2977d37b8fa5da229f5dc0 + run_exports: {} + size: 97602 + timestamp: 1785172567718 +- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + sha256: 03dba5917f944c6684ab44c81daacac1624cd148e4b2cae215dcec594a210c48 + md5: a79bf97561232a31447b6246c2153ab5 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD - size: 115158 - timestamp: 1780507822178 + run_exports: {} + size: 116935 + timestamp: 1785761789772 - conda: https://conda.anaconda.org/conda-forge/noarch/truststore-0.10.4-pyhcf101f3_0.conda sha256: eece5be81588c39a855a0b70da84e0febb878a6d91dd27d6d21370ce9e5c5a46 md5: c2db35b004913ec69bcac64fb0783de0 @@ -13964,25 +9544,9 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/truststore?source=hash-mapping run_exports: {} size: 24279 timestamp: 1766494826559 -- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - sha256: 59d7851d32fddb5b510272e6557aa982edeb927d349648dac27f5bf01d18bb26 - md5: 4460f039b7dedf15f7df086446ca75ae - depends: - - typing_extensions >=4.14.0 - - python >=3.10 - - importlib-metadata >=3.6 - - python - constrains: - - pytest >=7 - license: MIT - license_family: MIT - size: 38297 - timestamp: 1778779291237 - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda sha256: 82cb76b4dd554d1da90a9e1ea5f99e24aaa4997bcd19ceef8233fc17d9da432e md5: a61716f1640d9986d5d6de1c4d140b42 @@ -13994,74 +9558,31 @@ packages: - pytest >=7 license: MIT license_family: MIT - purls: - - pkg:pypi/typeguard?source=hash-mapping run_exports: {} size: 38525 timestamp: 1785075519409 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c - md5: edd329d7d3a4ab45dcf905899a7a6115 - depends: - - typing_extensions ==4.15.0 pyhcf101f3_0 - license: PSF-2.0 - license_family: PSF - purls: [] - run_exports: {} - size: 91383 - timestamp: 1756220668932 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - sha256: b141933ece3518f6d7b75dfb59451e2f26b405a44c18e2518a83e9a02e09315c - md5: c680b5747e8c4c8f23dca0bb7042a8fc +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + sha256: b141933ece3518f6d7b75dfb59451e2f26b405a44c18e2518a83e9a02e09315c + md5: c680b5747e8c4c8f23dca0bb7042a8fc depends: - typing_extensions ==4.16.0 pyhcf101f3_0 license: PSF-2.0 license_family: PSF - purls: [] run_exports: {} size: 94080 timestamp: 1783002732887 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - sha256: 8b90d2f19f9458b8c58a55e1fcdc1d90c1603a847a47654d8a454549413ba60a - md5: 53f5409c5cfd6c5a66417d68e3f0a864 - depends: - - python >=3.10 - - typing_extensions >=4.12.0 - - python - license: MIT - license_family: MIT - purls: - - pkg:pypi/typing-inspection?source=hash-mapping - run_exports: {} - size: 20935 - timestamp: 1777105465795 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.3-pyhcf101f3_0.conda - sha256: f9d821cb64f2bd33cff02d4969d6b8f8f299b6209abae946ac8aa92927dc217a - md5: 4934c9f5695514b925be8a5503bcd171 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda + sha256: 293c66b208468d186a94ff486c2ffbf67859a2bde8c88e965fc9d06c517191b2 + md5: 880e91eac5d926568ef278e20554148e depends: - python >=3.10 - typing_extensions >=4.15.0 - python license: MIT license_family: MIT - purls: - - pkg:pypi/typing-inspection?source=compressed-mapping - run_exports: {} - size: 20940 - timestamp: 1786372593154 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 - md5: 0caa1af407ecff61170c9437a808404d - depends: - - python >=3.10 - - python - license: PSF-2.0 - license_family: PSF - purls: - - pkg:pypi/typing-extensions?source=hash-mapping run_exports: {} - size: 51692 - timestamp: 1756220668932 + size: 21070 + timestamp: 1786818918217 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda sha256: 2d888f90af0686044882c74193ec80a90ec1943145d94a7b1b048958acda1848 md5: c70ad746c22219b9700931707482992c @@ -14070,8 +9591,6 @@ packages: - python license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/typing-extensions?source=hash-mapping run_exports: {} size: 52631 timestamp: 1783002732887 @@ -14084,30 +9603,19 @@ packages: - typing_extensions >=3.7.4 license: MIT license_family: MIT - purls: - - pkg:pypi/typing-inspect?source=hash-mapping run_exports: {} size: 14919 timestamp: 1733845966415 -- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c - md5: ad659d0a2b3e47e38d829aa8cad2d610 - license: LicenseRef-Public-Domain - purls: [] - run_exports: {} - size: 119135 - timestamp: 1767016325805 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 md5: fcb489df604d100968b737f2cb6076c6 license: LicenseRef-Public-Domain - purls: [] run_exports: {} size: 118849 timestamp: 1784250406640 -- conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.2-pyhd8ed1ab_0.conda - sha256: 2fe118c7c33571613cd12f7e9771049e19a46abaa6abfa4adc92d0defc54c61e - md5: a85b53859c40d115022948c8f225d8d6 +- conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda + sha256: 00a56cdd16bf3a398f6e87d7696c92a3012d823b8e42ba7d3bda2ec2fa5d8947 + md5: 2f0f2fb0f8a61529464d95a87b10eda0 depends: - cached-property >=1.5.2 - httpx @@ -14116,27 +9624,9 @@ packages: - requests >=2.25 license: MIT license_family: MIT - purls: - - pkg:pypi/unearth?source=hash-mapping - run_exports: {} - size: 286235 - timestamp: 1766474788879 -- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda - sha256: 4fb9789154bd666ca74e428d973df81087a697dbb987775bc3198d2215f240f8 - md5: 436c165519e140cb08d246a4472a9d6a - depends: - - brotli-python >=1.0.9 - - h2 >=4,<5 - - pysocks >=1.5.6,<2.0,!=1.5.7 - - python >=3.9 - - zstandard >=0.18.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/urllib3?source=hash-mapping run_exports: {} - size: 101735 - timestamp: 1750271478254 + size: 288616 + timestamp: 1786845009822 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda sha256: feff959a816f7988a0893201aa9727bbb7ee1e9cec2c4f0428269b489eb93fb4 md5: cbb88288f74dbe6ada1c6c7d0a97223e @@ -14148,20 +9638,19 @@ packages: - python >=3.10 license: MIT license_family: MIT - purls: - - pkg:pypi/urllib3?source=hash-mapping run_exports: {} size: 103560 timestamp: 1778188657149 -- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.1-pyhd8ed1ab_0.conda - sha256: 5ddde23d65aecde7e8dac0b9d9c7821ead2b87a320d787f9e4288c0ee00fa332 - md5: 19c961dd9cab6c3e13cd195f0176dbfa +- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + sha256: 4acf845da404e84cef1acccc66cc0156af1b83a5b5d7077b2ca19b705c561e57 + md5: 99f7755ec8648a042b0dbe906234f888 depends: - python >=3.10 license: MIT license_family: MIT - size: 133769 - timestamp: 1780932915297 + run_exports: {} + size: 132415 + timestamp: 1782771807703 - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda sha256: 93807369ab91f230cf9e6e2a237eaa812492fe00face5b38068735858fba954f md5: 46e441ba871f524e2b067929da3051c2 @@ -14169,50 +9658,9 @@ packages: - __win - python >=3.9 license: LicenseRef-Public-Domain - purls: - - pkg:pypi/win-inet-pton?source=hash-mapping run_exports: {} size: 9555 timestamp: 1733130678956 -- conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda - sha256: 9f5f5905afea6e0188aa6887aed0809033143343a7af06983f4b390c2286d2d7 - md5: 099794df685f800c3f319ff4742dc1bb - depends: - - python >=3.11 - - numpy >=1.26 - - packaging >=24.2 - - pandas >=2.2 - - python - constrains: - - bottleneck >=1.4 - - cartopy >=0.23 - - cftime >=1.6 - - dask-core >=2024.6 - - distributed >=2024.6 - - flox >=0.9 - - h5netcdf >=1.3 - - h5py >=3.11 - - hdf5 >=1.14 - - iris >=3.9 - - matplotlib-base >=3.8 - - nc-time-axis >=1.4 - - netcdf4 >=1.6.0 - - numba >=0.60 - - numbagg >=0.8 - - pint >=0.24 - - pydap >=3.5.0 - - scipy >=1.13 - - seaborn-base >=0.13 - - sparse >=0.15 - - toolz >=0.12 - - zarr >=2.18 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/xarray?source=hash-mapping - run_exports: {} - size: 1017999 - timestamp: 1776122774298 - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda sha256: 47244c233aeb55de993fe79eebbbb2adc14b388bb0bf2f9dfbaa8aa6610b6345 md5: f72531e6cf99c98d47306ec917f5e6b9 @@ -14247,8 +9695,6 @@ packages: - zarr >=2.18 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/xarray?source=hash-mapping run_exports: {} size: 1027069 timestamp: 1783633473025 @@ -14259,8 +9705,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/xyzservices?source=hash-mapping run_exports: {} size: 51732 timestamp: 1774900074457 @@ -14274,8 +9718,6 @@ packages: - pyyaml >=6.0,<7.0 license: MIT license_family: MIT - purls: - - pkg:pypi/yte?source=hash-mapping run_exports: {} size: 16215 timestamp: 1764250734338 @@ -14286,8 +9728,6 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/zict?source=hash-mapping run_exports: {} size: 36341 timestamp: 1733261642963 @@ -14299,29 +9739,25 @@ packages: - python license: MIT license_family: MIT - purls: - - pkg:pypi/zipp?source=hash-mapping run_exports: {} size: 24190 timestamp: 1779159948016 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - build_number: 7 - sha256: 7acaa2e0782cad032bdaf756b536874346ac1375745fb250e9bdd6a48a7ab3cd - md5: a44032f282e7d2acdeb1c240308052dd +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda + build_number: 8 + sha256: c7c98ce74f6a7ff25aaa4706d06fa41d63a333add4d697b73aa4d8d2d7ad7651 + md5: a2d706b4a7d603524133037c34f7fb76 depends: - llvm-openmp >=9.0.1 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - _openmp_mutex >=4.5 - size: 8325 - timestamp: 1764092507920 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.5.0-py310h3b8a9b8_1.conda + size: 8016 + timestamp: 1788046437162 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda noarch: python - sha256: 6c6e47ef2a85e7ade5c94d2388f43bd8197129e6b6305f9e8a49380b7dfbc427 - md5: 480f5277fd3ed13ea6ea8b5d74563815 + sha256: 51c8a631b36bf2eb2f27d49c0a0df9e269a76b91a447da7e75f9d0854cabb4a7 + md5: 5ce8b03801df5eff0088bdc85a19d09a depends: - python >=3.10 - __osx >=11.0 @@ -14331,8 +9767,9 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - size: 1086020 - timestamp: 1780396657560 + run_exports: {} + size: 1085485 + timestamp: 1786328966627 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda sha256: b0747f9b1bc03d1932b4d8c586f39a35ac97e7e72fe6e63f2b2a2472d466f3c1 md5: 57301986d02d30d6805fdce6c99074ee @@ -14345,7 +9782,6 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - atk-1.0 >=2.38.0 @@ -14363,45 +9799,11 @@ packages: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 size: 117350 timestamp: 1784086893887 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.7.30-h338687b_0.conda - sha256: 2d650815e881ac24237a98ab33d6f6d7431ad52edafc18c748399e1d85ca8641 - md5: 385fc8994159e570d0d45223f6b48aa9 - depends: - - __osx >=11.0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-auth >=0.7.30,<0.7.31.0a0 - size: 92830 - timestamp: 1726208174150 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.7.4-h41dd001_1.conda - sha256: 2167b44bc879fb9cb7aaf2ca8418c2f8764c82c8732a41c08616e3f70fc92224 - md5: 3f2c1743ed973b58fd187b0c31861dd8 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - size: 39881 - timestamp: 1725829996108 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda sha256: 9760cd6c5cc16ecd989866e86fea1fbe23bff3279831ce6f13b83afb6f6f2075 md5: 5f5ba0cd72e15095ede1ad38e8907f5f @@ -14410,7 +9812,6 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -14423,39 +9824,11 @@ packages: - __osx >=11.0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 size: 228084 timestamp: 1783637822478 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.9.28-hd74edd7_0.conda - sha256: 4081ada22148dc500765aac106ed224829810fd5e5d6f942a842b0a40f53783e - md5: 8dc8711c903ab57ead8ce99b65625a95 - depends: - - __osx >=11.0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-common >=0.9.28,<0.9.29.0a0 - size: 220787 - timestamp: 1725670124570 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.2.19-h41dd001_1.conda - sha256: d0a4362beb22aa4da126aab5ddadcb4bbde5032f407d7e4b03969a3d7e5f9cb2 - md5: 98e9d9c62300fd87bee44d2a63792ee5 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-compression >=0.2.19,<0.2.20.0a0 - size: 17974 - timestamp: 1725830013702 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda sha256: 8363308db46a84c5d7e9a309aaca8a18541f46f3b6e7b7027479a95ab910e19a md5: 6e0883cca4143f456e019f751d0b9f3b @@ -14464,29 +9837,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 size: 21774 timestamp: 1784042939907 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.4.3-hb2a355e_1.conda - sha256: 848473eecd5a438f93072457614f8dabbc09e4f7f12e0512dd4ba51cb0b2a9f3 - md5: b84f719ac7c5223ecd2471d86def6bf1 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - - libcxx >=17 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - size: 46990 - timestamp: 1725856827197 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda sha256: d1742afa3329b3ea892b5f0a75b36b4217a7c3eb43250e827c56f122343e9317 md5: 4ce3e032bfa6d72114e481c2dc99e2e2 @@ -14498,7 +9853,6 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -14515,44 +9869,11 @@ packages: - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 size: 177761 timestamp: 1784075780838 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.8.9-hf5a2c8c_0.conda - sha256: c2781a06bdd8a695296429c3bbe8dc528a93f426e45ebeb83f3a41de43213dd6 - md5: 59efa3b4dc632c4fef6911be61ed1779 - depends: - - __osx >=11.0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-compression >=0.2.19,<0.2.20.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-http >=0.8.9,<0.8.10.0a0 - size: 152487 - timestamp: 1726017067618 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.14.18-hc3cb426_12.conda - sha256: 59c510b61aad4da05f17756d84e3b138c51a5f27a8466021587504368818f159 - md5: efdd67503fa663c31d51b399c8f4cc2e - depends: - - __osx >=11.0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-io >=0.14.18,<0.14.19.0a0 - size: 137133 - timestamp: 1728562901503 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda sha256: 2b127f1ccd9f022c945363e1e8eb6fd4a5fbc89677ac678abde6dc022ba26765 md5: c4d73dc09e972a895d5a3dc7ce158be9 @@ -14563,28 +9884,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 size: 189938 timestamp: 1784054954272 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.10.5-h9658b26_0.conda - sha256: a43c24179ddeb980a9cd920354f866d10d1f6a70c00ca7fe18840cb79678ecb1 - md5: 3736531fdfe90d4513b633d907aab907 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - size: 133346 - timestamp: 1726205300093 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda sha256: b352abb6f7cd42bdbbdef647409cf0d94f6edc841151dc3405bf3023b761e65b md5: 742911742b564828dbaaffaa0f1a21eb @@ -14595,7 +9899,6 @@ packages: - aws-c-http >=0.11.0,<0.11.1.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -14614,45 +9917,11 @@ packages: - aws-checksums >=0.2.10,<0.2.11.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 size: 133754 timestamp: 1784101134287 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.6.5-h663ac5c_4.conda - sha256: 693463e8dbc86441f9a612c0556f200089aaa72e6e83479205072444d7e1fe34 - md5: 1714f0dbabb1431a351c8babe6b75bd9 - depends: - - __osx >=11.0 - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - size: 96372 - timestamp: 1726237156052 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.1.19-h41dd001_3.conda - sha256: b320a08973f22468fd816bb957947369381913ae045d33bd872d03ebabaa355f - md5: 53bd7f3e6723288f531387a892d01635 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - size: 49674 - timestamp: 1725836815498 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda sha256: 3ee2dcefcd45283508a3049bd4f5b508a46c16af906a0c3d351d0f9d81738464 md5: de81e53b45b103470d3be61984dcc292 @@ -14661,26 +9930,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 size: 60771 timestamp: 1784044312402 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.1.18-h41dd001_11.conda - sha256: 246c91ee57fd417685f838958f8532e6ef2a610753f89a5b714f8ebe5d727318 - md5: c7cd8fb206915662718006953228dbf7 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-checksums >=0.1.18,<0.1.19.0a0 - size: 49078 - timestamp: 1725836952728 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda sha256: 13313afb01bf4f1c328695724d5b16263810f84d7572f6cb0b4d12f877d7f1d6 md5: 99cd08b2a8261083e4ad9414cbe737df @@ -14689,35 +9943,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 size: 92225 timestamp: 1784044297006 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.28.2-h8f7a527_6.conda - sha256: 47c3a71486f5889b46706e45158e1bc3c375aec9eca99b1edf8d719cdda5cf46 - md5: 119a410e9b4fe4e31f91a32d7cb4a764 - depends: - - __osx >=11.0 - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - - libcxx >=17 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - size: 229739 - timestamp: 1726483567299 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda sha256: aa4d21bdc3c891d2dca32121ef543aa7f2d8d83868b347207f04ebf65dd5d65d md5: e850d4d6e349471c50fbed1f55fd3069 @@ -14735,33 +9965,11 @@ packages: - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 size: 275833 timestamp: 1784113326407 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.379-h67f4a54_9.conda - sha256: fe06825bfcc9fcbeb4b589e1d62b3621b1565a566ce7d231c198f89bbbfe2a41 - md5: 6e081189763244df6240298be3f29823 - depends: - - __osx >=11.0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-checksums >=0.1.18,<0.1.19.0a0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - libcurl >=8.9.1,<9.0a0 - - libcxx >=17 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - size: 2664996 - timestamp: 1725944742080 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda sha256: 215efb2e64b92094c3229c7d43177873068d0a088d7e94ff2c1447360f17a6c8 md5: 1748ae5f4015e2fec7009ff36cbfafa0 @@ -14775,28 +9983,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 size: 2834544 timestamp: 1784137336612 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.13.0-hd01fc5c_0.conda - sha256: aff4af38416cf7a81c79e5a3b071ce5aa13ec48da28db0312bc1ebe62cf7273d - md5: 2083f6313e623079db6ee67af00e6b27 - depends: - - __osx >=11.0 - - libcurl >=8.8.0,<9.0a0 - - libcxx >=16 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - size: 287922 - timestamp: 1720853302106 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda sha256: 9645073c4682ad3a334a2b06c5fc0ddc57fc9f0f537cc3123053634c92c28625 md5: 4f42d997f42a8d34ff74cb677cf0c828 @@ -14807,7 +9998,6 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -14823,44 +10013,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 size: 168032 timestamp: 1781268747743 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.8.0-h13ea094_2.conda - sha256: 11b01715cae19390890f29ebb56d36d895feafd787ba929aa10b6ce712f3f4b9 - md5: 383b72f2ee009992b21f4db08a708510 - depends: - - __osx >=11.0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - libcxx >=16 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - size: 142135 - timestamp: 1721777696118 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.12.0-hfde595f_0.conda - sha256: f733f4acedd8bf1705c780e0828f0b83242ae7e72963aef60d12a7c5b3a8640d - md5: f2c935764fdacd0fafc05f975fd347e0 - depends: - - __osx >=11.0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - libcxx >=16 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - size: 419976 - timestamp: 1721865180569 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda sha256: 3f096d7cd6fd7788d57887c3d3e48be1ebc5c3a971666ddc0dd8a3493a5b7cb5 md5: 0948246cb8e514e4ae1cfe7dbb4046c7 @@ -14871,7 +10028,6 @@ packages: - libcxx >=19 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -14889,46 +10045,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 size: 128710 timestamp: 1781268693348 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.7.0-hcf3b6fd_1.conda - sha256: 3fdf9c0337c48706cffe2e4c761cdea4132fb6dbd1f144d969c28afd903cf256 - md5: df7e01bcf8f3a9bfb0ab06778f915f29 - depends: - - __osx >=11.0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - libcxx >=16 - - libxml2 >=2.12.7,<2.14.0a0 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - size: 119821 - timestamp: 1721832870493 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.11.0-h082e32e_1.conda - sha256: 3c288dc1ae6bff9a1e21ab5196d13ab486850f61ec649a743a87bf9726901abf - md5: 16b05d31f626717668f01c01a970115f - depends: - - __osx >=11.0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - libcxx >=16 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 - size: 189065 - timestamp: 1721925275724 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda sha256: fb210d8d068df72a68c5fa655cf1f816792e278da3041c485478e7924d6e80eb md5: 06ba52b8a66fd041f4910b547e3f3da1 @@ -14940,30 +10061,11 @@ packages: - libcxx >=19 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 size: 206698 timestamp: 1781541197750 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h5499902_0.conda - sha256: 5a1e635a371449a750b776cab64ad83f5218b58b3f137ebd33ad3ec17f1ce92e - md5: e94ca7aec8544f700d45b24aff2dd4d7 - depends: - - __osx >=11.0 - - libcxx >=16 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.0,<1.3.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - blosc >=1.21.6,<2.0a0 - size: 33201 - timestamp: 1719266149627 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda sha256: c3fe902114b9a3ac837e1a32408cc2142c147ec054c1038d37aec6814343f48a md5: 925acfb50a750aa178f7a0aced77f351 @@ -14976,110 +10078,55 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 size: 33602 timestamp: 1733513285902 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda - sha256: 8aa8ee52b95fdc3ef09d476cbfa30df722809b16e6dca4a4f80e581012035b7b - md5: ce8659623cea44cc812bc0bfae4041c5 - depends: - - __osx >=11.0 - - brotli-bin 1.1.0 h6caf38d_4 - - libbrotlidec 1.1.0 h6caf38d_4 - - libbrotlienc 1.1.0 h6caf38d_4 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 20003 - timestamp: 1756599758165 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda - sha256: 422ac5c91f8ef07017c594d9135b7ae068157393d2a119b1908c7e350938579d - md5: 48ece20aa479be6ac9a284772827d00c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda + sha256: 7050e1b11876219bd0df375ef3d83fd00f94fb4e13c1ffb21dede0c506893079 + md5: aa7df8174ee3b34a5ad8a3160429db68 depends: - __osx >=11.0 - - brotli-bin 1.2.0 hc919400_1 - - libbrotlidec 1.2.0 hc919400_1 - - libbrotlienc 1.2.0 hc919400_1 + - brotli-bin 1.2.0 he4a93e4_3 + - libbrotlidec 1.2.0 h5295a6a_3 + - libbrotlienc 1.2.0 h2ddc9cb_3 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20237 - timestamp: 1764018058424 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda - sha256: e57d402b02c9287b7c02d9947d7b7b55a4f7d73341c210c233f6b388d4641e08 - md5: ab57f389f304c4d2eb86d8ae46d219c3 - depends: - - __osx >=11.0 - - libbrotlidec 1.1.0 h6caf38d_4 - - libbrotlienc 1.1.0 h6caf38d_4 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 17373 - timestamp: 1756599741779 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda - sha256: e2d142052a83ff2e8eab3fe68b9079cad80d109696dc063a3f92275802341640 - md5: 377d015c103ad7f3371be1777f8b584c - depends: - - __osx >=11.0 - - libbrotlidec 1.2.0 hc919400_1 - - libbrotlienc 1.2.0 hc919400_1 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 18628 - timestamp: 1764018033635 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312h6b01ec3_4.conda - sha256: e45f24660a89c734c3d54f185ecdc359e52a5604d7e0b371e35dce042fa3cf3a - md5: 0d50ab05d6d8fa7a38213c809637ba6d + size: 20767 + timestamp: 1786622887445 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda + sha256: 1e92cea587c2eaab169e45dae01c149b9889a8249eda296b8b6db39aee708531 + md5: d9de6c0ad7e008afdf62fb2ffd450b4a depends: - __osx >=11.0 - - libcxx >=19 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - constrains: - - libbrotlicommon 1.1.0 h6caf38d_4 + - libbrotlidec 1.2.0 h5295a6a_3 + - libbrotlienc 1.2.0 h2ddc9cb_3 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 341750 - timestamp: 1756600036931 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h3daef5d_1.conda - sha256: 5c2e471fd262fcc3c5a9d5ea4dae5917b885e0e9b02763dbd0f0d9635ed4cb99 - md5: f9501812fe7c66b6548c7fcaa1c1f252 + size: 18962 + timestamp: 1786622878369 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + sha256: 3cdfc0a96de4717fc309e39133e2a192f4e1df96680577e1d48804021d1726eb + md5: d3a28add84f2412a562355f89ef5e0f5 depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 constrains: - - libbrotlicommon 1.2.0 hc919400_1 + - libbrotlicommon 1.2.0 h1dcdb26_3 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 359854 - timestamp: 1764018178608 + size: 365272 + timestamp: 1786623009364 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 md5: b50612e7d190b8061ab4e7dc119cf4d5 @@ -15087,147 +10134,59 @@ packages: - __osx >=11.0 license: bzip2-1.0.6 license_family: BSD - purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 size: 124965 timestamp: 1785906749812 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df - md5: 620b85a3f45526a8bc4d23fd78fc22f0 - depends: - - __osx >=11.0 - license: bzip2-1.0.6 - license_family: BSD - purls: [] - run_exports: - weak: - - bzip2 >=1.0.8,<2.0a0 - size: 124834 - timestamp: 1771350416561 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - sha256: 2995f2aed4e53725e5efbc28199b46bf311c3cab2648fc4f10c2227d6d5fa196 - md5: bcb3cba70cf1eec964a03b4ba7775f01 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - c-ares >=1.34.6,<2.0a0 - size: 180327 - timestamp: 1765215064054 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda - sha256: 104b41473845649101ba8ebf8221c7431256465d34ce380b10e9a90558ed33ac - md5: 7d9390a4d4b43f91652823d870f68065 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda + sha256: 82bdd5a3fcada6afef6255a9bf41172f07b362f36e04a354dc2abc0a29bfb756 + md5: 4cc01a374215de38387d45e19c8c5c9c depends: - __osx >=11.0 - constrains: - - c-ares-static <0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 - size: 197274 - timestamp: 1786116660078 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h6a3b0d2_0.conda - sha256: 00439d69bdd94eaf51656fdf479e0c853278439d22ae151cabf40eb17399d95f - md5: 38f6df8bc8c668417b904369a01ba2e2 - depends: - - __osx >=11.0 - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - icu >=75.1,<76.0a0 - - libcxx >=18 - - libexpat >=2.6.4,<3.0a0 - - libglib >=2.82.2,<3.0a0 - - libpng >=1.6.47,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 - - pixman >=0.44.2,<1.0a0 - license: LGPL-2.1-only or MPL-1.1 - purls: [] - run_exports: - weak: - - cairo >=1.18.4,<2.0a0 - size: 896173 - timestamp: 1741554795915 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - sha256: cde9b79ee206fe3ba6ca2dc5906593fb7a1350515f85b2a1135a4ce8ec1539e3 - md5: 36200ecfbbfbcb82063c87725434161f + size: 197819 + timestamp: 1787169996553 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda + sha256: 3d368a359c3b526082eb8b8c4a07371f0c1e40e6dadfeef326dbec7956c149d0 + md5: ae0e0e8ea304c6ff1608d7918703ba29 depends: - __osx >=11.0 - - fontconfig >=2.15.0,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - - icu >=78.1,<79.0a0 - - libcxx >=19 - - libexpat >=2.7.3,<3.0a0 - - libfreetype >=2.14.1 - - libfreetype6 >=2.14.1 - - libglib >=2.86.3,<3.0a0 - - libpng >=1.6.53,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=21 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 - pixman >=0.46.4,<1.0a0 license: LGPL-2.1-only or MPL-1.1 - purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 900035 - timestamp: 1766416416791 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda - sha256: 597e986ac1a1bd1c9b29d6850e1cdea4a075ce8292af55568952ec670e7dd358 - md5: 503ac138ad3cfc09459738c0f5750705 + size: 841724 + timestamp: 1787926737310 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314h618e29d_2.conda + sha256: 77b0bb0f3fd2d28b6bbd216d370d98320835b8e1df4c86a478a1d0391d1913fe + md5: 1572fc59fc2b1461f47b0d6907acef30 depends: - __osx >=11.0 - - libffi >=3.5.2,<3.6.0a0 - - pycparser - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cffi?source=hash-mapping - run_exports: {} - size: 288080 - timestamp: 1761203317419 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py314h44086f9_1.conda - sha256: 5b5ee5de01eb4e4fd2576add5ec9edfc654fbaf9293e7b7ad2f893a67780aa98 - md5: 10dd19e4c797b8f8bdb1ec1fbb6821d7 - depends: - - __osx >=11.0 - - libffi >=3.5.2,<3.6.0a0 + - libffi >=3.7.0,<3.8.0a0 - pycparser - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 292983 - timestamp: 1761203354051 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.4.1-h793ed5c_0.conda - sha256: cad6c9f86f98f1ac980e8229ef76a9bb8f62d167a52d29770e0548c7f9a80eb1 - md5: c2a9a79b58d2de021ad9295f53e1f40a - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libgfortran >=5 - - libgfortran5 >=12.3.0 - - libgfortran5 >=13.2.0 - - libzlib >=1.3.1,<2.0a0 - license: LicenseRef-fitsio - purls: [] - run_exports: - weak: - - cfitsio >=4.4.1,<4.4.2.0a0 - size: 802060 - timestamp: 1718906517515 + run_exports: {} + size: 292307 + timestamp: 1786775143512 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda sha256: 238328ef13c15fa87ff718ef6bfc32ee83c255f47af7d4ba07190ff865c652fa md5: f76979642f978340ec48d809c548732f @@ -15250,7 +10209,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -15277,7 +10235,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -15303,7 +10260,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -15328,7 +10284,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -15350,56 +10305,54 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 size: 553075 timestamp: 1778500827484 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.5.3-py314h4dc9dd8_0.conda - sha256: 91aa3f41bf0d7c3d53c17fe44e8ea9a3502231aea4c059fe513449459132b94f - md5: 90646b8cc1b322840689a4a890bd704e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.1-py314hcec6336_0.conda + sha256: 2e7212eacb92dd19de4074c6427b34e2fdc6c9aa02d27ccc29d65160a8ab9c28 + md5: ce8660590c4e5537b767f0ac9b17ab2d depends: - archspec >=0.2.3 - boltons >=23.0.0 - charset-normalizer - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - conda-package-handling >=2.2.0 - - conda-pypi >=0.9.0 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - distro >=1.5.0 - frozendict >=2.4.2 - - jsonpatch >=1.32 - menuinst >=2 + - msgpack-python >=1.1.1 - packaging >=23.0 - platformdirs >=3.10.0 - pluggy >=1.6.0 - pycosat >=0.6.3 - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 + - python - requests >=2.28.0,<3 - ruamel.yaml >=0.11.14,<0.19 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 - - zstandard >=0.19.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 constrains: + - conda-build >=25.9 - conda-content-trust >=0.3.0 - conda-env >=2.6 - - conda-build >=25.9 license: BSD-3-Clause license_family: BSD - size: 1359398 - timestamp: 1781681460540 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.10.1-py314hcec6336_2.conda - sha256: 48c5bba381b7a9b6db4b70f39962fdeb12a53a0935d70d513437cb589b4a473d - md5: 652a02b8973cd22717c108ffa6873744 + run_exports: {} + size: 1469487 + timestamp: 1787306459722 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda + sha256: 4da4678a03488c985aed27956dd1a35281b515240ff33a3fe9238f3756fa6059 + md5: a123df8bb2f1dc4f85ba6bbddb744968 depends: - python - - conda >=26.1.0 - pip >=23.0.1 - packaging - unearth @@ -15410,27 +10363,13 @@ packages: - conda-package-streaming >=0.11 - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 license: MIT license_family: MIT - size: 322322 - timestamp: 1781646689091 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda - sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c - md5: f9cce0bc86b46533489a994a47d3c7d2 - depends: - - numpy >=1.25 - - python - - python 3.12.* *_cpython - - __osx >=11.0 - - libcxx >=19 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping run_exports: {} - size: 286084 - timestamp: 1769156157865 + size: 328032 + timestamp: 1784203150047 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda sha256: 754ab72f1c1ae99ef7c57995f59224dc9632cbd6731fe7e6277437fd01d43156 md5: cddc851000ce131d757678c2f329eaad @@ -15443,38 +10382,19 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 290405 timestamp: 1769156069514 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h4f10f1e_0.conda - sha256: a7380056125a29ddc4c4efc4e39670bc8002609c70f143d92df801b42e0b486f - md5: 5cb4f9b93055faf7b6ae76da6123f927 - depends: - - __osx >=11.0 - - libcxx >=19 - license: CC0-1.0 - size: 24960 - timestamp: 1756734870487 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py312h8eba7c0_2.conda - sha256: 157b325152e5417900e719da2e742bb2dc26c1b6f51f29ce72f389c60282d8dc - md5: 7d10262860832f3664fb503636a799d5 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda + sha256: 83819551a243ac95a96867977a0c7e2661d9e06ca268026c93d2c9f93969424f + md5: 93748a43e1a27821795af6701b7c06be depends: - - python - - python 3.12.* *_cpython - - __osx >=11.0 - libcxx >=19 - - python_abi 3.12.* *_cp312 - constrains: - __osx >=11.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping + license: CC0-1.0 run_exports: {} - size: 1638054 - timestamp: 1763019931024 + size: 25909 + timestamp: 1785920661291 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda sha256: 5fdd6299bc379ccd89cd7732d8e4ae5cebd8e05babe6d7fe4660e16511091634 md5: da101197ce8bfbdad65fc7bed572ab38 @@ -15488,79 +10408,26 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1639272 timestamp: 1763019908165 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda - sha256: ec606fd8e789f6a13c3d7c8c14b3ce616191ebd10a7d216d7149e8840aadfbb9 - md5: b6c94f8b09905110a958584e2436e497 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda + sha256: b9e7e07f4487109f9bcafc581f778784a19b6fb8e2ca4b5c86be6088e642821d + md5: a78919380afa9842a9c46f35fc7a518c depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 hf618e03_4 - - libpsl >=0.23.0,<0.24.0a0 + - libcurl 8.21.0 h6651222_5 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT - purls: [] - run_exports: {} - size: 181023 - timestamp: 1785500142429 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.9.1-hbf5303f_0.conda - sha256: 8dd4d08ca377f11a60fbf33d8350179d83dbf8bcbb3bb01b0c95779fd459419c - md5: 93440b8d934e90496e1939ecd72cbf0c - depends: - - __osx >=11.0 - - krb5 >=1.21.3,<1.22.0a0 - - libcurl 8.9.1 hfd8ffcc_0 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: curl - license_family: MIT - purls: [] - run_exports: {} - size: 153833 - timestamp: 1722440571650 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cyrus-sasl-2.1.28-ha1cbb27_0.conda - sha256: 7de03254fa5421e7ec2347c830a59530fb5356022ee0dc26ec1cef0be1de0911 - md5: 2867ea6551e97e53a81787fd967162b1 - depends: - - __osx >=11.0 - - krb5 >=1.21.3,<1.22.0a0 - - libcxx >=18 - - libntlm >=1.8,<2.0a0 - - openssl >=3.5.0,<4.0a0 - license: BSD-3-Clause-Attribution - license_family: BSD - purls: [] - run_exports: - weak: - - cyrus-sasl >=2.1.28,<3.0a0 - size: 193732 - timestamp: 1750239236574 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda - sha256: be8d2bf477d0bf8d19a7916c2ceccef33cbfecf918508c18b89098aa7b20017e - md5: 49389c14c49a416f458ce91491f62c67 - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - - toolz >=0.10.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} - size: 591797 - timestamp: 1771856474133 + size: 180586 + timestamp: 1787183693425 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda sha256: 8deb1f8ef80525cccc68c73cdcd690cb8614035f6e1d0eb6880c0c9b377d29ac md5: aae872d4d6847677924de8e023e00457 @@ -15572,8 +10439,6 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 615096 timestamp: 1771856205834 @@ -15588,8 +10453,6 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 2776045 timestamp: 1780390212997 @@ -15600,32 +10463,11 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - epoxy >=1.5.10,<1.6.0a0 size: 296347 timestamp: 1758743805063 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py312hf57c059_0.conda - sha256: 94b8677b25ef847fd89fd174cfd65afdb35621fc35185bcb81c758388cbef7b6 - md5: 99266b4d62fc3d663b084a18b0bb82d5 - depends: - - __osx >=11.0 - - cramjam >=2.3 - - fsspec - - numpy >=1.23,<3 - - packaging - - pandas >=1.5.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping - run_exports: {} - size: 524384 - timestamp: 1778865060800 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda sha256: 91992021fe8f3416314198388729ef4cc9d364503ce0cfbd934f14a00ff75896 md5: 9bcd9bf5df8950f1f661bdd6c9299c8e @@ -15641,36 +10483,9 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 535738 timestamp: 1778865753466 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py312hf3c922e_1.conda - sha256: 2dd68c3881e0dd33c2c8c2e09f7af92ed8ce60ea863c1910dc2524bb7d4c4dbc - md5: 268287f41c9d4ba5b426d5206986fbca - depends: - - __osx >=11.0 - - attrs >=19.2.0 - - click >=8.0,<9.dev0 - - click-plugins >=1.0 - - cligj >=0.5 - - gdal - - libcxx >=17 - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - pyparsing - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - - shapely - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping - run_exports: {} - size: 1025443 - timestamp: 1726664909376 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda sha256: ba8da8eb94e2762116a6c8877814826d638f046210f23b9da1ef444f9a55046a md5: d30d4527aacdf33b1e80ef858f33608c @@ -15689,57 +10504,25 @@ packages: - shapely license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 1059210 timestamp: 1767051558277 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-11.0.2-h440487c_1.conda - sha256: afc3d556845e42112953279d3703c603c00bf6e139658778ea6b904c4fc1f50a - md5: f90a1a21cef4c1e11f421ba29538d704 - depends: - - __osx >=11.0 - - libcxx >=18 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=11.0.2,<11.1.0a0 - size: 180080 - timestamp: 1743030842101 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h403dcb5_0.conda - sha256: dba5d4a93dc62f20e4c2de813ccf7beefed1fb54313faff9c4f2383e4744c8e5 - md5: ae2f556fbb43e5a75cc80a47ac942a8e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + sha256: ca42427b99ff92228e907bed5f10a1df868180997571d29c230faad2c38e35fd + md5: 891b1202d7b835f215e26a4db685ec7c depends: - __osx >=11.0 - libcxx >=19 license: MIT license_family: MIT - size: 180970 - timestamp: 1767681372955 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda - sha256: 8607d8d0b32f9f6fc61ea8c06b537486b78428a04516658222fa4d1d521af765 - md5: 9d928e6a62192141fb6540a3125b1345 - depends: - - __osx >=11.0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libintl >=0.25.1,<1.0a0 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - fontconfig >=2.18.1,<3.0a0 - - fonts-conda-ecosystem - size: 248677 - timestamp: 1780450500773 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - sha256: 6ec1937b910b2ac468459c2f7844b7c98238ada39ede05461dbd4dc764e519a5 - md5: d718960dea5abf5b582fe8ecf363c11d + - fmt >=12.1.0,<12.2.0a0 + size: 181008 + timestamp: 1785915450316 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + sha256: 004570d35fb0eff73ce3ae49b209a47622d0c2e580dd0dc4c61d59626b386a09 + md5: 8ac8cc3fe744b484de4387703134d8b2 depends: - __osx >=11.0 - libexpat >=2.8.1,<3.0a0 @@ -15749,175 +10532,80 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - size: 267950 - timestamp: 1786381516923 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda - sha256: b78cc8df0d93ac0f0d59cb91cf54cc91effa6c124a78c8d2e8afc8011d9fb320 - md5: 77e64a600d8426c3863942f67491f5fb - depends: - - __osx >=11.0 - - brotli - - munkres - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - - unicodedata2 >=15.1.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/fonttools?source=hash-mapping - run_exports: {} - size: 2904377 - timestamp: 1778771054844 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda - sha256: 96b33f1e2a32c602b167f43719e3acf89ec742b4a1e25e99ffd0e6f99b38d277 - md5: 7bd06ab4ed807154c2d9031eb5ebf025 - depends: - - libfreetype 2.14.3 hce30654_1 - - libfreetype6 2.14.3 hdfa99f5_1 + size: 265659 + timestamp: 1786667932256 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + sha256: 90681f1d0658cb2fd49a074f644eb4774272499290487a4bebb1c4df01d6997b + md5: 2f4cc7dc2e6622591735c49dda1b92aa + depends: + - libfreetype 2.14.3 hce30654_2 + - libfreetype6 2.14.3 h2ed5691_2 license: GPL-2.0-only OR FTL - purls: [] run_exports: weak: - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - size: 173518 - timestamp: 1780933616544 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda - sha256: b4146ac9ba1676494e3d812ca39664dd7dd454e4d0984f3665fd6feec318c71c - md5: dd655a29b40fe0d1bf95c64cf3cb348d + size: 175388 + timestamp: 1786641016583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda + sha256: b16d44eb53028e82eebbfacdc37f9895179dc18da3eca8e5e7acec9485e71119 + md5: fc08d48da7f739ebf2110c3491ae9eff depends: - __osx >=11.0 - - libexpat >=2.6.4,<3.0a0 - - libiconv >=1.17,<2.0a0 - - minizip >=4.0.7,<5.0a0 + - libexpat >=2.8.1,<3.0a0 + - libiconv >=1.18,<2.0a0 + - minizip >=4.2.2,<5.0a0 license: MPL-1.1 - license_family: MOZILLA - purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 - size: 53378 - timestamp: 1734014980768 + size: 54221 + timestamp: 1788125835063 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda sha256: 6dd18694340b84290bb1906cc1359502b974aada6a8476cfe6dec3ce0e860af8 md5: 2bb7d7dd91116b8c85e805b0e08cc67b depends: - __osx >=11.0 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 size: 60230 timestamp: 1785912572097 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda - sha256: d856dc6744ecfba78c5f7df3378f03a75c911aadac803fa2b41a583667b4b600 - md5: 04bdce8d93a4ed181d1d726163c2d447 - depends: - - __osx >=11.0 - license: LGPL-2.1-or-later - size: 59391 - timestamp: 1757438897523 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdal-3.9.2-py312h936c49d_7.conda - sha256: ffd8d88c9e7d3b8617df4cbe9ad4755995ef5f180bfb6afc50e1f8a4f68446a3 - md5: 305595575712364e99c69bf43d7078f0 - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/gdal?source=hash-mapping - run_exports: {} - size: 1662506 - timestamp: 1728293877630 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.6-h4e57454_0.conda - sha256: 07cbba4e12430de35ea608eb3006cf1f7f63832c4f89a081cd6f3872944c1aa6 - md5: e67ebd2f639f46e52af8531622fa6051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_0.conda + sha256: 0e91dc3531a3a7c4938abc115d7a49be61284a9a8650b404a37b2123fe5cf7dd + md5: aa0e0c67d3e89789959e0d77c8361b67 depends: - __osx >=11.0 - - libglib >=2.86.4,<3.0a0 + - libglib >=2.88.3,<3.0a0 - libintl >=0.25.1,<1.0a0 - - libjpeg-turbo >=3.1.2,<4.0a0 - - liblzma >=5.8.2,<6.0a0 - - libpng >=1.6.56,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 license: LGPL-2.1-or-later license_family: LGPL - size: 548309 - timestamp: 1774986047281 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.12.2-h00cdb27_1.conda - sha256: 1090c9b8e9a628eb2f9718d19e3cc6372c39cf763295c446a3da9288d0dbba11 - md5: e07a7fa032741a056a72f46b43064ea2 - depends: - - __osx >=11.0 - - libcxx >=16 - license: LGPL-2.1-only - purls: [] run_exports: weak: - - geos >=3.12.2,<3.12.3.0a0 - size: 1400339 - timestamp: 1721747764174 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda - sha256: 1ac5f5a3a35f2e4778025043c87993208d336e30539406e380e0952bb7ffd188 - md5: 4238412c29eff0bb2bb5c60a720c035a + - gdk-pixbuf >=2.44.8,<3.0a0 + size: 551879 + timestamp: 1786715345711 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda + sha256: 6bb801cb23b7e884c2e6ce96d933d141d6d87ad07f5bf8d13d6a59a3403952fa + md5: e7480ad364d7a5bf03812ba272c39707 depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 license: LGPL-2.1-only - purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 - size: 1530844 - timestamp: 1761594597236 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.3-h7e5fb84_2.conda - sha256: 2cd4db594cb9580c92ec40063a0160237d31fa633abf8b4c81730a0f2626ba7b - md5: 5a9ebf4312dffc9b44f41af5d2a2332e - depends: - - __osx >=11.0 - - libcxx >=16 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - zlib - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - geotiff >=1.7.3,<1.8.0a0 - size: 114324 - timestamp: 1722335738912 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda - sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 - md5: 57a511a5905caa37540eb914dfcbf1fb - depends: - - __osx >=11.0 - - libcxx >=17 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - gflags >=2.2.2,<2.3.0a0 - size: 82090 - timestamp: 1726600145480 + size: 1545653 + timestamp: 1787894628839 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda sha256: 3028f8f857d438444d3fb56c9c74d46699cb04e676c6575747cf3ad8d9a308b1 md5: 96aea99abfaf8d1e7731c03e3f70fb02 @@ -15926,47 +10614,35 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - gflags >=2.3.1,<2.4.0a0 size: 93701 timestamp: 1785044718935 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - sha256: 843b3f364ff844137e37d5c0a181f11f6d51adcedd216f019d074e5aa5d7e09c - md5: 95fa1486c77505330c20f7202492b913 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - giflib >=5.2.2,<5.3.0a0 - size: 71613 - timestamp: 1712692611426 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-hd20048c_1.conda - sha256: be9b4cf253c7d3bf20ccc9ff278ea7ec6f144ebbe8fdc9a131d18c35ff49692f - md5: 93963a54079e27fdb1bf8d289ad592d4 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda + sha256: ebfdf94fe2fb8e8d39702e26c747a9a692c0fcf78db737b56deeba4ed16dcb82 + md5: 39725a84a4b2b6b3ae6177f58be48b91 depends: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - giflib >=5.2.2,<5.3.0a0 - size: 73137 - timestamp: 1784694527697 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.1-h37541a8_2.conda - sha256: 414bdf86a8096d5706293d163359def2e61b8ffd3fe106bbf2028d79e58e6a97 - md5: 8d4580a91948a6c3383a7c2fbfe5311c + - giflib >=6.1.3,<6.2.0a0 + size: 82339 + timestamp: 1785907599925 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h026c20a_2.conda + sha256: 20b9cb0c314a02e0940d2695ed5ed206e271b202f3935bc93b8c5a8529853cc4 + md5: 085d8718f37acd1e2c35ef6ec9b330f7 depends: - - libglib ==2.88.1 ha08bb59_2 + - libglib ==2.88.3 h89cd0c0_2 - libffi - __osx >=11.0 - libintl >=0.25.1,<1.0a0 license: LGPL-2.1-or-later - size: 204902 - timestamp: 1778508895255 + run_exports: {} + size: 202400 + timestamp: 1787884141804 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda sha256: 1ac5991fd354de81baf3892fee0ad09438623698fcad73758521dfe90711ed44 md5: 68937f90f7930d49e106b94e95d4536c @@ -15976,36 +10652,18 @@ packages: - libcxx >=19 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 size: 112670 timestamp: 1784094909098 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda - sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 - md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 - depends: - - __osx >=11.0 - - gflags >=2.2.2,<2.3.0a0 - - libcxx >=16 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - glog >=0.7.1,<0.8.0a0 - size: 112215 - timestamp: 1718284365403 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.13.1-hf76c51c_0.conda - sha256: a190edc92d5c9d7b65e624596cf6a45c5f6f372409f660610bb847bda2329ed9 - md5: 55beafb01e44f9527026dbffcea260ee +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.0-hf76c51c_0.conda + sha256: ce190c611e2114c6da7251e1f82f84f64786d09186734d4d44deeaec2b267881 + md5: 67acc9c808bdd91b1c5c91fcfa59dcd2 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: {} - size: 1829675 - timestamp: 1775514584688 + size: 1879157 + timestamp: 1787949037965 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda sha256: 471f34a187fdb4f2df33e26f2e471b16c239a5b277903f643d9c3a8c9a9f44ec md5: 0c7b78d9ffff4f5a6ca28d346f734d8f @@ -16014,22 +10672,11 @@ packages: - __osx >=11.0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 size: 86493 timestamp: 1786118637573 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-hf6b4638_0.conda - sha256: c0a060d7b7a05669043ef3f68c7a1025c8594e1ab73735afb64c35e8baa41da5 - md5: 0d576cff278a2e60456d5b2c0a1ffda3 - depends: - - __osx >=11.0 - - libcxx >=19 - license: LGPL-2.0-or-later - license_family: LGPL - size: 82245 - timestamp: 1780454628763 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda sha256: 755c72d469330265f80a615912a3b522aef6f26cbc52763862b6a3c492fbf97c md5: 1f3d859de3ca2bcaa845e92e87d73660 @@ -16051,25 +10698,25 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other - purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 size: 2218284 timestamp: 1769427599940 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.2-py314he609de1_0.conda - sha256: 0538d74bc47b2ac3cff2a6c1cfca23abf609909bc9395062a1130f399fb8ae77 - md5: 98ee4e107dd538b2fbd183b0ecad5a84 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda + sha256: d960aefe961553b75450b7584f2fad7693ed1fac423da0fc5a9d649c48d2a019 + md5: ae860c6208235503f82ef3019461d410 depends: - python - - python 3.14.* *_cp314 - __osx >=11.0 - libcxx >=19 + - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 263816 - timestamp: 1781762277927 + run_exports: {} + size: 274201 + timestamp: 1786384265137 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda sha256: 26862a9898054b8552e55e609e5ce73c7ef1eb28bbe6fb87f0b9109d73cd09df md5: 5557a2433b1339b8e536c264afea41ef @@ -16093,7 +10740,6 @@ packages: - pango >=1.56.4,<2.0a0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - gtk3 >=3.24.52,<4.0a0 @@ -16108,104 +10754,38 @@ packages: - libglib >=2.76.3,<3.0a0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - gts >=0.7.6,<0.8.0a0 size: 304331 timestamp: 1686545503242 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.2.1-h3103d1b_0.conda - sha256: 5593f4aad6580707eb268e8dbb4c562a736d87bea03f5e1551becaebfe1a6620 - md5: 389b1c7cb4738fa74f8a142336807a13 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_0.conda + sha256: 1cb2c173e80c2c166589e2e43d822db4ce84385d54800a108de39f240e0c24c6 + md5: f71b69fa28637827ec618fe783b7ec11 depends: - - __osx >=11.0 - - cairo >=1.18.4,<2.0a0 - - graphite2 >=1.3.14,<2.0a0 - - icu >=78.3,<79.0a0 - - libcxx >=19 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.1,<3.0a0 - - libzlib >=1.3.2,<2.0a0 + - libharfbuzz-devel 14.4.0 hcda0f7c_0 license: MIT license_family: MIT - size: 1721040 - timestamp: 1780451752518 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda - sha256: c3b01e3c3fe4ca1c4d28c287eaa5168a4f2fd3ffd76690082ac919244c22fa90 - md5: ff5d749fd711dc7759e127db38005924 - depends: - - libcxx >=15.0.7 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libzlib >=1.2.13,<2.0.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - hdf4 >=4.2.15,<4.2.16.0a0 - size: 762257 - timestamp: 1695661864625 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_hec07895_105.conda - sha256: 5d87a1b63862e7da78c7bd9c17dea3526c0462c11df9004943cfa4569cc25dd3 - md5: f9c8c7304d52c8846eab5d6c34219812 - depends: - - __osx >=11.0 - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libcxx >=16 - - libgfortran >=5 - - libgfortran5 >=12.3.0 - - libgfortran5 >=13.2.0 - - libzlib >=1.2.13,<2.0a0 - - openssl >=3.3.1,<4.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - hdf5 >=1.14.3,<1.14.4.0a0 - size: 3445248 - timestamp: 1717587775787 + - libharfbuzz >=14.4.0 + size: 11020 + timestamp: 1787795107813 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda sha256: 46a4958f2f916c5938f2a6dc0709f78b175ece42f601d79a04e0276d55d25d07 md5: cfb39109ac5fa8601eb595d66d5bf156 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: {} size: 17616 timestamp: 1771539622983 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 - md5: 5eb22c1d7b3fc4abb50d92d621583137 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - icu >=75.1,<76.0a0 - size: 11857802 - timestamp: 1720853997952 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - sha256: 3a7907a17e9937d3a46dfd41cffaf815abad59a569440d1e25177c15fd0684e5 - md5: f1182c91c0de31a7abd40cedf6a5ebef - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - size: 12361647 - timestamp: 1773822915649 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda sha256: 6cdb5dee54c72e56ab189fb3ad33cb28533553d42590e7e831160248f4416a43 md5: a5efc0b42bb8b42e97d0a29ae3e3c187 depends: - __osx >=11.0 license: MIT - purls: [] + license_family: MIT run_exports: weak: - icu >=78.3,<79.0a0 @@ -16221,24 +10801,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 52567 timestamp: 1757685575891 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.17-he54c16a_1.conda - sha256: b12b280c0179628b2aa355286331d48b136104cf96179c355971f2e7c5b226ac - md5: 4831302cd6badbdb87c0334163fb7d68 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - json-c >=0.17,<0.18.0a0 - size: 74409 - timestamp: 1720813255190 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda sha256: 73179a1cd0b45c09d4f631cb359d9e755e6e573c5d908df42006728e0bf8297c md5: 94f14ef6157687c30feb44e1abecd577 @@ -16246,122 +10811,54 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - json-c >=0.18,<0.19.0a0 size: 73715 timestamp: 1726487214495 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kealib-1.5.3-h8edbb62_2.conda - sha256: 29fef9ff99514a34d8026da4be5289bc4d2526974df459b63e92445fca7fd55e - md5: d5c581103f5433dd862acbf24facdf9b - depends: - - __osx >=11.0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libcxx >=17 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - kealib >=1.5.3,<1.6.0a0 - size: 142261 - timestamp: 1725399546359 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda - sha256: 8de440f0e33ab6895e81f2c47c51e59d177349a832087a0367e8e259c97f4833 - md5: 58261af35f0d33fd28e2257b208a1be0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda + sha256: cff0bfcd5685c8bb630cd2e8e99ea9a58857e6ccf943cbf96df7b1d2b70eb451 + md5: 3e24b0e86143022ba7eba8ae276fab3d depends: - python + - libcxx >=21 - __osx >=11.0 - - libcxx >=19 - - python 3.12.* *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping - run_exports: {} - size: 68490 - timestamp: 1773067215781 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py314hf8a3a22_0.conda - sha256: 840de1b0ba2fa646475bc53ba0f723c8a13e66139633a070831b8279deaa7c64 - md5: eb1465d8a644ef290d18fb86af6e9bc4 - depends: - - python - python 3.14.* *_cp314 - - libcxx >=19 - - __osx >=11.0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} - size: 69284 - timestamp: 1773067285911 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b - md5: c6dc8a0fdec13a0565936655c33069a1 + size: 66681 + timestamp: 1787938517076 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda + sha256: aaea1d42b07769920db2af7471ece9399d2613448863da64ad8272998db5db34 + md5: 15235dd10450d67bc25ccafd5b46d2bc depends: - __osx >=11.0 - - libcxx >=16 - - libedit >=3.1.20191231,<3.2.0a0 - - libedit >=3.1.20191231,<4.0a0 - - openssl >=3.3.1,<4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - krb5 >=1.21.3,<1.22.0a0 - size: 1155530 - timestamp: 1719463474401 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - sha256: c740e4a2e7247776a9883158fdab50ae0732c8f67f96d8f1db8ad9da5e0b5222 - md5: 8780f41b013d19219faef9c82260744b - depends: - - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 - libedit >=3.1.20250104,<3.2.0a0 - libedit >=3.1.20250104,<4.0a0 - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 - size: 1159780 - timestamp: 1781859501654 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda - sha256: ccb5598fad3694e79bf54f0eb812e3b3c3dd63d1497e631f5978800eadb9bcc4 - md5: d2f2c7c10e2957647d45589b7701a453 + size: 1165740 + timestamp: 1786762145768 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda + sha256: ee92e3ee789881b08a95d254d4dbb54186d31089743eac9bb34df45b33e5b3a2 + md5: 39f3afa677c4f1cd499aec3b1f5c76a9 depends: - __osx >=11.0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 - size: 213747 - timestamp: 1780212240694 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda - sha256: 66e5ffd301a44da696f3efc2f25d6d94f42a9adc0db06c44ad753ab844148c51 - md5: 095e5749868adab9cae42d4b460e5443 - depends: - - __osx >=11.0 - - libcxx >=19 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - lerc >=4.1.0,<5.0a0 - size: 164222 - timestamp: 1773114244984 + size: 212111 + timestamp: 1788156381412 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda sha256: c97aa17d16d2ac332ba9f184e82ce8f72dcb10e9a10c5f299030be2d44e191b9 md5: e429aec4037d5cb8fd34ded9f5dadd39 @@ -16370,113 +10867,38 @@ packages: - libcxx >=19 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 size: 166477 timestamp: 1785036480092 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda - sha256: a9517c8683924f4b3b9380cdaa50fdd2009cd8d5f3918c92f64394238189d3cb - md5: f16963d88aed907af8b90878b8d8a05c - depends: - - __osx >=11.0 - - libcxx >=16 - constrains: - - abseil-cpp =20240116.2 - - libabseil-static =20240116.2=cxx17* - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libabseil >=20240116.2,<20240117.0a0 - - libabseil =*=cxx17* - size: 1136123 - timestamp: 1720857649214 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h2062a1b_1.conda - sha256: 450026eb01a52acd0ff122e331ec9b8546c93790143214b73e1c14bc2b075b22 - md5: 8adfdc0215e979a0ce31be676883e0b3 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda + sha256: d9c62dae9d8f5bebadf72fcf369c00cc353183cb2521f9fffbf4c2f70b58bef9 + md5: 2aa5e7dc7b5d218908effd2a8c70a8a8 depends: - __osx >=11.0 - libcxx >=19 constrains: - - libabseil-static =20260526.0=cxx17* - abseil-cpp =20260526.0 + - libabseil-static =20260526.0=cxx17* license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 - libabseil =*=cxx17* - size: 1273408 - timestamp: 1780524599788 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 - md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 - depends: - - __osx >=11.0 - - libcxx >=19 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libaec >=1.1.5,<2.0a0 - size: 30390 - timestamp: 1769222133373 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.7.7-h7c07d2a_0.conda - sha256: 10d5c755761c6823d20c6ddbd42292ef91f34e271b6ba3e78d0c5fa81c22b3ed - md5: 49b28e291693b70cf8a7e70f290834d8 - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libiconv >=1.17,<2.0a0 - - libxml2 >=2.13.5,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.4.0,<4.0a0 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libarchive >=3.7.7,<3.8.0a0 - size: 777074 - timestamp: 1732614642044 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.8-gpl_h6fbacd7_100.conda - sha256: 05c370fae4f2a5fd7baf59c15d75caec718d785ec47e813dbf7bff68355e4bb7 - md5: cfa10f3c4b14c13f676dc08e2ea29023 - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - libzlib >=1.3.2,<2.0a0 - - lz4-c >=1.10.0,<1.11.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - size: 796153 - timestamp: 1782289667690 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h6fbacd7_100.conda - sha256: b58cfffd0774142d308a25f1bdc610c46d71cb67489f1b1eec0891e78ede5b11 - md5: 4963589c8bed67dd0540d890ba690f53 + size: 1277537 + timestamp: 1787217005681 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda + sha256: cee5639cdfc5121339516e2cdf2f8338e376ca6ac42a45bba77b8b7e199b8305 + md5: bdcec8546f15ded1b3718309e027e603 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 - libzlib >=1.3.2,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - lzo >=2.10,<3.0a0 @@ -16484,54 +10906,11 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 - size: 800281 - timestamp: 1785251408018 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-17.0.0-h20538ec_13_cpu.conda - build_number: 13 - sha256: fb77709a184e934b8662388f91bf9bd51a96eb3b11c53d0453e9bc43b01b4c27 - md5: c6813f605a0fd1947e768b5f6138e0a7 - depends: - - __osx >=11.0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 - - bzip2 >=1.0.8,<2.0a0 - - glog >=0.7.1,<0.8.0a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libcxx >=17 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libre2-11 >=2023.9.1 - - libutf8proc >=2.8.0,<3.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - orc >=2.0.2,<2.0.3.0a0 - - re2 - - snappy >=1.2.1,<1.3.0a0 - - zstd >=1.5.6,<1.6.0a0 - - libutf8proc <2.9 - constrains: - - apache-arrow-proc =*=cpu - - parquet-cpp <0.0a0 - - arrow-cpp <0.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow >=17.0.0,<17.1.0a0 - size: 5253411 - timestamp: 1725214512114 + size: 798787 + timestamp: 1787292999432 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda build_number: 4 sha256: 3c289bab2a8c4be0ef86363c97c5532dbcc289708b8523d6a3cfef35a2dd0b9b @@ -16566,28 +10945,11 @@ packages: - parquet-cpp <0.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 size: 4294348 timestamp: 1786315141730 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-17.0.0-hf9b8971_13_cpu.conda - build_number: 13 - sha256: ea958d1947670dc913f1a0ee631c70d8ac8d6db5f08039002b233bf896815cb2 - md5: 5d52b70e8174f52934d646bbaf0a928b - depends: - - __osx >=11.0 - - libarrow 17.0.0 h20538ec_13_cpu - - libcxx >=17 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-acero >=17.0.0,<17.1.0a0 - size: 477391 - timestamp: 1725214612356 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda build_number: 4 sha256: 154feae16991d0e50d4b4d45291904cfbf1c8c97955237f71a84975b363cdd04 @@ -16603,7 +10965,6 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -16626,30 +10987,11 @@ packages: - re2 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 size: 2228808 timestamp: 1786315247988 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-17.0.0-hf9b8971_13_cpu.conda - build_number: 13 - sha256: 3dac99549e4f9307bb4d35e94003f6e7c9052a957de122ec78c5d9750fd46096 - md5: 35df832aa0371c7bbe9fec5ea1c3139c - depends: - - __osx >=11.0 - - libarrow 17.0.0 h20538ec_13_cpu - - libarrow-acero 17.0.0 hf9b8971_13_cpu - - libcxx >=17 - - libparquet 17.0.0 hf0ba9ef_13_cpu - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-dataset >=17.0.0,<17.1.0a0 - size: 482335 - timestamp: 1725215502662 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda build_number: 4 sha256: 580382aae415669e2ff4af670db9381462d5d966ecde7d35f041356344b77dd1 @@ -16667,33 +11009,11 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 size: 519748 timestamp: 1786315616734 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-17.0.0-hbf8b706_13_cpu.conda - build_number: 13 - sha256: 5fd22a610e14669f0f392aaf7b61511d9a7a5f99a23a3ce4bdf5b2880ddbd244 - md5: e079f9b14e75bb3f571b1345ce8dad78 - depends: - - __osx >=11.0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libarrow 17.0.0 h20538ec_13_cpu - - libarrow-acero 17.0.0 hf9b8971_13_cpu - - libarrow-dataset 17.0.0 hf9b8971_13_cpu - - libcxx >=17 - - libprotobuf >=4.25.3,<4.25.4.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-substrait >=17.0.0,<17.1.0a0 - size: 466501 - timestamp: 1725215667169 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda build_number: 4 sha256: 4f5d1e1e1ecead58d80472e2965143a4d4daf37d401a527334116caf5b5cff9b @@ -16709,172 +11029,84 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 size: 455505 timestamp: 1786315676110 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - build_number: 8 - sha256: 8f5ec18ead0619a9cf0f38b49796c22f6fc0f44850c0df2baea0f5277db16e75 - md5: dbfe729181a32741ae63ecb41eefbac6 - depends: - - libopenblas >=0.3.33,<0.3.34.0a0 - - libopenblas >=0.3.33,<1.0a0 - constrains: - - blas 2.308 openblas - - liblapack 3.11.0 8*_openblas - - liblapacke 3.11.0 8*_openblas - - libcblas 3.11.0 8*_openblas - - mkl <2027 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libblas >=3.11.0,<4.0a0 - size: 18949 - timestamp: 1779859141315 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda - build_number: 9 - sha256: 0437866fe43b4c911470d3e7ddea18d78390bd7062d9563ce6d38c8ba5798405 - md5: cb1f85be9d88af453fcda3dfba995099 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda + build_number: 10 + sha256: e5a6dd4210286ae61f868cf70f297e851856b8512776f4efac4e50dba0d57457 + md5: 37ec75f777eac2ee1322e19d64df82e7 depends: - libopenblas >=0.3.34,<0.3.35.0a0 - libopenblas >=0.3.34,<1.0a0 constrains: - - blas 2.309 openblas - - libcblas 3.11.0 9*_openblas - - liblapack 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - libcblas 3.11.0 10*_openblas + - liblapack 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas - mkl <2027 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 - size: 18162 - timestamp: 1786058887392 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda - sha256: 023b609ecc35bfee7935d65fcc5aba1a3ba6807cbba144a0730198c0914f7c79 - md5: 231cffe69d41716afe4525c5c1cc5ddd + size: 18171 + timestamp: 1788076987757 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda + sha256: 5e62b856b2e77ce98db44133bacab1281b42c1040dcbd69dbacfb80890cff5b0 + md5: b457450ba3f27c4749783c0204bd17b0 depends: - __osx >=11.0 license: MIT license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - size: 68938 - timestamp: 1756599687687 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - sha256: a7cb9e660531cf6fbd4148cff608c85738d0b76f0975c5fc3e7d5e92840b7229 - md5: 006e7ddd8a110771134fcc4e1e3a6ffa - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 79443 - timestamp: 1764017945924 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda - sha256: 7f1cf83a00a494185fc087b00c355674a0f12e924b1b500d2c20519e98fdc064 - md5: cb7e7fe96c9eee23a464afd57648d2cd - depends: - - __osx >=11.0 - - libbrotlicommon 1.1.0 h6caf38d_4 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 29015 - timestamp: 1756599708339 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - sha256: 2eae444039826db0454b19b52a3390f63bfe24f6b3e63089778dd5a5bf48b6bf - md5: 079e88933963f3f149054eec2c487bc2 + size: 80027 + timestamp: 1786622846050 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda + sha256: 510c9fce0d9ffcf41741dd96fc05db381672109d5665ef002c2e58f0d4ca0118 + md5: e07a99c6fdd984f4d588060f2f936bf4 depends: - __osx >=11.0 - - libbrotlicommon 1.2.0 hc919400_1 + - libbrotlicommon 1.2.0 h1dcdb26_3 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 29452 - timestamp: 1764017979099 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda - sha256: a2f2c1c2369360147c46f48124a3a17f5122e78543275ff9788dc91a1d5819dc - md5: 4ce5651ae5cd6eebc5899f9bfe0eac3c - depends: - - __osx >=11.0 - - libbrotlicommon 1.1.0 h6caf38d_4 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlienc >=1.1.0,<1.2.0a0 - size: 275791 - timestamp: 1756599724058 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - sha256: 01436c32bb41f9cb4bcf07dda647ce4e5deb8307abfc3abdc8da5317db8189d1 - md5: b2b7c8288ca1a2d71ff97a8e6a1e8883 + size: 29935 + timestamp: 1786622857695 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda + sha256: eac412417eee2e93c62e9d53559f1f9c14f40b6c41e2c432af8b517596898dd1 + md5: 954c78a9f591bfb12c79beaff7338ec8 depends: - __osx >=11.0 - - libbrotlicommon 1.2.0 hc919400_1 + - libbrotlicommon 1.2.0 h1dcdb26_3 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 290754 - timestamp: 1764018009077 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - build_number: 8 - sha256: f93efcd44bc24f97c2478c7474d3baa6801a057974f330e1d06bedc33e4c778f - md5: 03a2ef3491da9e5b4d18c03e9f4b3109 + size: 295650 + timestamp: 1786622868044 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda + build_number: 10 + sha256: d4d786f18344c6e4a3fddd1ea19e014fa5f7bd33d11f6255b147a79dd00f9327 + md5: 953e30e380c03f058cacffe19bce9dc9 depends: - - libblas 3.11.0 8_h51639a9_openblas - constrains: - - blas 2.308 openblas - - liblapack 3.11.0 8*_openblas - - liblapacke 3.11.0 8*_openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libcblas >=3.11.0,<4.0a0 - size: 18911 - timestamp: 1779859147634 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda - build_number: 9 - sha256: c4c71f20fdb20c86bf6f61c8c31bb349e4055bb4d19928e8580bb5615039cb4b - md5: a7b6ba94e3ca58bc7d4e1ae4ff95c215 - depends: - - libblas 3.11.0 9_h51639a9_openblas + - libblas 3.11.0 10_h51639a9_openblas constrains: - - blas 2.309 openblas - - liblapack 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - liblapack 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 - size: 18110 - timestamp: 1786058893756 + size: 18146 + timestamp: 1788076992075 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 md5: 32bd82a6a625ea6ce090a81c3d34edeb @@ -16882,89 +11114,40 @@ packages: - libcxx >=11.1.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 size: 18765 timestamp: 1633683992603 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hd5a2499_0.conda - sha256: 5f73f41b3504c9d41a60aa161f6c87c36f19f97c92b3510441db618e361dc946 - md5: 862eb5c81e1295f492fa261a68a4233f - depends: - - __osx >=11.0 - - krb5 >=1.22.2,<1.23.0a0 - - libnghttp2 >=1.68.1,<2.0a0 - - libssh2 >=1.11.1,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: curl - license_family: MIT - size: 410874 - timestamp: 1782297872451 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - sha256: d25be36712d7f854a5f93513b9fbf1b0ee3219975b7fef4a3ee071b021b10167 - md5: 66cf9c5003ee81ecdb0dc9f9df17bbe5 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda + sha256: 59dd850def9473e7f63ed72afc750bba9670316f279c0bd409572cd47569ed5f + md5: 5ae67c128838b686894b4a3aef015c29 depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - libnghttp2 >=1.68.1,<2.0a0 - - libpsl >=0.23.0,<0.24.0a0 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT - purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 - size: 411491 - timestamp: 1785500129743 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.9.1-hfd8ffcc_0.conda - sha256: 4d6006c866844a39fb835436a48407f54f2310111a6f1d3e89efb16cf5c4d81b - md5: be0f46c6362775504d8894bd788a45b2 - depends: - - krb5 >=1.21.3,<1.22.0a0 - - libnghttp2 >=1.58.0,<2.0a0 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: curl - license_family: MIT - purls: [] - run_exports: - weak: - - libcurl >=8.9.1,<9.0a0 - size: 374937 - timestamp: 1722440523552 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - sha256: a2e7abab5add9750fab064c024394de48e49f97631c605ad5db5c8ac3fc769ef - md5: 89f76a2a21a3ec3ec983b5eb237c4113 + size: 409852 + timestamp: 1787183686192 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + sha256: d3e5f0b767964af25ef81edffc002f8e822b1a5c55330da1ae3a857f70c4e4ee + md5: 5303ba06fab927399ed8dfb3227b0af8 depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache - purls: [] run_exports: {} - size: 569349 - timestamp: 1781670209146 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda - sha256: 5e0b6961be3304a5f027a8c00bd0967fc46ae162cffb7553ff45c70f51b8314c - md5: a6130c709305cd9828b4e1bd9ba0000c - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libdeflate >=1.25,<1.26.0a0 - size: 55420 - timestamp: 1761980066242 + size: 575940 + timestamp: 1787698697875 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda sha256: d896f4aa4ce4c590c2838678cb1917356fdb461d2a189991c0280c818c362172 md5: 78650d671cb56909bb3e5c13bce310f9 @@ -16972,27 +11155,25 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 size: 55727 timestamp: 1785909153744 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 - md5: 44083d2d2c2025afca315c7a172eab2b +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda + sha256: 257c926f19e32bdb981fc674c76966049b6fc73f706bae58e9fed8757ad1da70 + md5: 843ef89082f368cb889305084d3b483c depends: - ncurses - __osx >=11.0 - - ncurses >=6.5,<7.0a0 + - ncurses >=6.6,<7.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 - size: 107691 - timestamp: 1738479560845 + size: 107742 + timestamp: 1786616721640 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda sha256: c0100064506ae8abb432c5a506d474f10af2cf48c33d62bc221fb28b6d6ff6ac md5: 19e86c8a6a47e92bb2e70ca12e758c5c @@ -17000,23 +11181,11 @@ packages: - __osx >=11.0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 size: 39991 timestamp: 1785917376956 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f - md5: 36d33e440c31857372a72137f78bacf5 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libev >=4.33,<4.34.0a0 - size: 107458 - timestamp: 1702146414478 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 md5: 1a109764bff3bdc7bdd84088347d71dc @@ -17024,7 +11193,6 @@ packages: - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -17039,49 +11207,33 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT - purls: [] run_exports: {} size: 69362 timestamp: 1781203631990 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - sha256: 6686a26466a527585e6a75cc2a242bf4a3d97d6d6c86424a441677917f28bec7 - md5: 43c04d9cb46ef176bb2a4c77e324d599 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libffi >=3.5.2,<3.6.0a0 - size: 40979 - timestamp: 1769456747661 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda - sha256: 2c6ac9a6cd65af89b2bd448518bb1e13b44a2e48c0d469398e37bcfc0092e832 - md5: 92e8690d170d46d768c32553458c0105 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + sha256: 2783389a7b9dda04c62cc54c6bbeb03dd3cef24b3fc642715d02a3fa19464a9a + md5: 216bbcc23c11e9695b3db4a8771d76eb depends: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 - size: 43734 - timestamp: 1783521647536 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - sha256: d5637b01941c0fc8f5cbb1f170c238f4ee153b3c1708b9d50f4f1305438ff051 - md5: 0582e67cd14cfed773be2f3b1aba08e0 + size: 43637 + timestamp: 1787753403688 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + sha256: 19ce8bd320414cb6b9889ecbf48a3ded848b0d1068b76ff6bea099bd7387d6f3 + md5: ee1fc5bba400ff0cae27fbf141a1ae0c depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL - purls: [] run_exports: {} - size: 8365 - timestamp: 1780933612390 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - sha256: abbfffd8a8c776bb8b59a10c8247fc3aa6b17ba0051e9f6d199dca38479f214f - md5: a0bb0678f67c464938d3693fa96f6884 + size: 8367 + timestamp: 1786641013393 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + sha256: d9b203d6484ad491b5b5f33d49a9d7a91189d776a9adae53d2b63af18ec11e6a + md5: 881cfb44ea02cc9849f612725a959660 depends: - __osx >=11.0 - libpng >=1.6.58,<1.7.0a0 @@ -17089,38 +11241,22 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL - purls: [] - run_exports: {} - size: 338442 - timestamp: 1780933611662 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - sha256: 06644fa4d34d57c9e48f4d84b1256f9e5f654fdb37f43acc8a58a396952d42b7 - md5: 644058123986582db33aebd4ae2ca184 - depends: - - _openmp_mutex - constrains: - - libgcc-ng ==15.2.0=*_19 - - libgomp 15.2.0 19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] run_exports: {} - size: 404080 - timestamp: 1778273064154 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - sha256: fdd1502babb50b802d090496586231f56236d7a6fc042a4e1ac2dee48da8366b - md5: 0124dc2e6f70f3e8ebea643b42b18abb + size: 340923 + timestamp: 1786641012794 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda + sha256: 00b8d07ea98344c72c6e332a09b8060f4176849d5fd0eb78dd5b30176ad97ca4 + md5: 408af8d9d5226ff45ff04756ff1aa91e depends: - _openmp_mutex constrains: - - libgomp 16.1.0 1 - - libgcc-ng ==16.1.0=*_1 + - libgcc-ng ==16.2.0=*_4 + - libgomp 16.2.0 4 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 364162 - timestamp: 1785374452947 + size: 365758 + timestamp: 1787617459249 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda sha256: 269edce527e204a80d3d05673301e0207efcd0dbeebc036a118ceb52690d6341 md5: fa4a92cfaae9570d89700a292a9ca714 @@ -17140,441 +11276,96 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD - purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 size: 159247 timestamp: 1766331953491 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-3.9.2-hce30654_7.conda - sha256: a8344237821a6a71c5f0b415df44fea61faed86afc09dd18d2a311cb3a2593b9 - md5: b9ff370534f04743fea9a532bb1cb967 - depends: - - libgdal-core 3.9.2.* - - libgdal-fits 3.9.2.* - - libgdal-grib 3.9.2.* - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libgdal-jp2openjpeg 3.9.2.* - - libgdal-kea 3.9.2.* - - libgdal-netcdf 3.9.2.* - - libgdal-pdf 3.9.2.* - - libgdal-pg 3.9.2.* - - libgdal-postgisraster 3.9.2.* - - libgdal-tiledb 3.9.2.* - - libgdal-xls 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - - libgdal >=3.9.2,<3.10.0a0 - size: 422965 - timestamp: 1728296452637 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-h21e278e_4.conda - sha256: 83b437c6c6b3b5ac9d5570d7121f0ef7dc832446879ba6ad17cdfb5e02eefb41 - md5: c73f4453e35b1ca58ffcb9550821d15a +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda + sha256: 0d2695cc7bc4e862f430d3b07e89d6e7b64a0020b9bf0293cda64ddc90b10c85 + md5: de41c6d2ce7d0138acd66d6b4122549d depends: - - libcxx >=19 - __osx >=11.0 - - libdeflate >=1.25,<1.26.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - xerces-c >=3.3.0,<3.4.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - lz4-c >=1.10.0,<1.11.0a0 - - liblzma >=5.8.3,<6.0a0 - - openssl >=3.5.7,<4.0a0 + - libcxx >=21 + - libexpat >=2.8.1,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - giflib >=6.1.3,<6.2.0a0 - libsqlite >=3.53.4,<4.0a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - - libjxl >=0.12.0,<0.13.0a0 - - json-c >=0.18,<0.19.0a0 + - libkml >=1.3.0,<1.4.0a0 - libiconv >=1.18,<2.0a0 - - libjpeg-turbo >=3.2.0,<4.0a0 - - blosc >=1.21.6,<2.0a0 - - giflib >=5.2.2,<5.3.0a0 - - libarchive >=3.8.9,<3.9.0a0 - muparser >=2.3.5,<2.4.0a0 - - libcurl >=8.21.0,<9.0a0 - proj >=9.8.1,<9.9.0a0 - - libexpat >=2.8.1,<3.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libxml2 - - libxml2-16 >=2.14.6 + - json-c >=0.18,<0.19.0a0 + - geos >=3.14.1,<3.14.2.0a0 - lerc >=4.2.0,<5.0a0 + - blosc >=1.21.6,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - pcre2 >=10.47,<10.48.0a0 + - liblzma >=5.8.3,<6.0a0 + - libcurl >=8.21.0,<9.0a0 - libpng >=1.6.58,<1.7.0a0 - - geos >=3.14.1,<3.14.2.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 constrains: - libgdal 3.12.4.* license: MIT license_family: MIT - purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 - size: 10819037 - timestamp: 1786090987756 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.9.2-hcf5b7dd_1.conda - sha256: 3938fe06332eb7b79cdd452a344fc352b2528793156d8fcdd763f3a3ac99aa93 - md5: 98dc71fff9381ffed515c519443b4111 + size: 10894319 + timestamp: 1786825501570 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda + sha256: 7582efbbffc6964093afd80e01c2ac60d89aa4e404cac664544675b9d4d1c5e7 + md5: aa6c627acd0f5709d9c79bf708a7b81b depends: - - __osx >=11.0 - - blosc >=1.21.6,<2.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - geotiff >=1.7.3,<1.8.0a0 - - giflib >=5.2.2,<5.3.0a0 - - json-c >=0.17,<0.18.0a0 - - lerc >=4.0.0,<5.0a0 - - libarchive >=3.7.4,<3.8.0a0 - - libcurl >=8.9.1,<9.0a0 - - libcxx >=17 - - libdeflate >=1.21,<1.26.0a0 - - libexpat >=2.6.2,<3.0a0 - - libiconv >=1.17,<2.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - libsqlite >=3.46.1,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libwebp-base >=1.4.0,<2.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - pcre2 >=10.44,<10.45.0a0 - - proj >=9.4.1,<9.5.0a0 - - xerces-c >=3.2.5,<3.3.0a0 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 + - libgfortran5 16.2.0 hdb7a957_4 constrains: - - libgdal 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - size: 8210225 - timestamp: 1725465971834 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-fits-3.9.2-h248c7bc_7.conda - sha256: 2795e2d484722cbc3381920982da0250d3dcc3f3556b8bcdf1ed1c134a7d2f1b - md5: f6fddae38163fff25a99adef1765496c - depends: - - __osx >=11.0 - - cfitsio >=4.4.1,<4.4.2.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 466138 - timestamp: 1728294964843 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-grib-3.9.2-h6d3d72d_7.conda - sha256: d9eb5d2a428da6d057c84c0902692e73ce77993b5dbced725dc0b814d382d23d - md5: f8794c6cd7aaa4cd18ebde3fe10fba07 - depends: - - __osx >=11.0 - - libaec >=1.1.3,<2.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 652171 - timestamp: 1728295096895 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf4-3.9.2-h3847bb8_7.conda - sha256: 2431fbe2e19007c61093052ce021963313446472a5bfd148da546c388c9409be - md5: 0ff2c29987702b8f7b61c865d951cd90 - depends: - - __osx >=11.0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - libaec >=1.1.3,<2.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] + - libgfortran-ng ==16.2.0=*_4 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL run_exports: {} - size: 578331 - timestamp: 1728295222220 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf5-3.9.2-h2def128_7.conda - sha256: 3c298f5da6f445637deba5bd3bd48389e84740060f565fcc889912de7eeccd12 - md5: 6bbc7e8df9ef22139bc1bab39ba3dd56 + size: 99624 + timestamp: 1787617544212 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda + sha256: 902719c38c7a390d305435a362dc83893754c3f933569a38347c434cd1c12540 + md5: d54390644d893d50e739b19145aae45f depends: - - __osx >=11.0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] + - libgcc >=16.2.0 + constrains: + - libgfortran 16.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL run_exports: {} - size: 591844 - timestamp: 1728295364313 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-jp2openjpeg-3.9.2-hd61e619_7.conda - sha256: abcbbe2d98a6eb471ac620aef4d687ad6acdcc61188063dc42e9e598a90d7868 - md5: 3114191129246e6571d739289bb8083f - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - openjpeg >=2.5.2,<3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 462891 - timestamp: 1728295482655 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-kea-3.9.2-h7b2de0b_7.conda - sha256: 8ba32b0e3654b221f3dc902ddfb3ad1e74777220c5b4ea30331e80fe801c5bef - md5: 47c89ca8baab301fb54f3b1faa166e4d - depends: - - __osx >=11.0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - kealib >=1.5.3,<1.6.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 469587 - timestamp: 1728296301261 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-netcdf-3.9.2-h5e0d008_7.conda - sha256: eb093b7e72a9374c421fa92128282a676a54bb37ca5960a8132dd6326306a1a8 - md5: 438cf785fe8b4d9acabbae8ce6e39cb6 - depends: - - __osx >=11.0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libnetcdf >=4.9.2,<4.9.3.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 668071 - timestamp: 1728296444763 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pdf-3.9.2-h587d690_7.conda - sha256: 68c1a57552963982a1a703b85a42bbd8a15bb253d9acce13332d1ff911078de4 - md5: 4323634089f1156bd69a77ad48f53d0d - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - poppler - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 598629 - timestamp: 1728295629149 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pg-3.9.2-h6a0b679_7.conda - sha256: f0b0d93eb7e4d99c5581978adab99b4b930be40b610e858d642af36c9ef00793 - md5: 596b2a38085a9352856af7ab3bdefe41 - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - postgresql - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 503494 - timestamp: 1728295758903 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-postgisraster-3.9.2-h6a0b679_7.conda - sha256: d9b9dfece530a470e957c188c8452082d387a6b5666bafa640aed6694e4b4265 - md5: f044c31cdd36806e627e23329c6089b0 - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - postgresql - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 468214 - timestamp: 1728295893350 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-tiledb-3.9.2-h27a95ea_2.conda - sha256: 69c2a06f5d19525d8bc796be8dfe04a95d2e8dc06e5736f3f7152c6de64271c3 - md5: 3c57516fbe39e1ac39eaa496e3f10204 - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - tiledb >=2.26.0,<2.27.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 616363 - timestamp: 1726096931827 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-xls-3.9.2-habc1c91_7.conda - sha256: ad62f074cd24ebf915b2e715e2d2a1e315795672444b7be1be0c6ddd7f51b0e4 - md5: 09290c8b53af1b977967ad9a4734a0e2 - depends: - - __osx >=11.0 - - freexl >=2.0.0,<3.0a0 - - libcxx >=17 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 432445 - timestamp: 1728296164752 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - sha256: d4837b3b9b30af3132d260225e91ab9dde83be04c59513f500cc81050fb37486 - md5: 1ea03f87cdb1078fbc0e2b2deb63752c - depends: - - libgfortran5 15.2.0 hdae7583_19 - constrains: - - libgfortran-ng ==15.2.0=*_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 139675 - timestamp: 1778273280875 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - sha256: d7c6dd601dbbde495ab213a76d690b2fec19455d26c595ec0257cfde3e80166b - md5: d6f10dbb9c5830f540904c91ea5b252a - depends: - - libgfortran5 16.1.0 h32cdfcc_1 - constrains: - - libgfortran-ng ==16.1.0=*_1 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 98528 - timestamp: 1785374566402 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - sha256: d0a68b7a121d115b80c169e24d1265dcc25a3fe58d107df1bbc430797e226d88 - md5: ba36d8c606a6a53fe0b8c12d47267b3d - depends: - - libgcc >=15.2.0 - constrains: - - libgfortran 15.2.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 599691 - timestamp: 1778273075448 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda - sha256: 3e8cb79a421e0c350566717febdba9079574cbbe204591b4fd4b4081ea89cb92 - md5: c1c10ea48f95054aa9b93c2315a0a74a - depends: - - libgcc >=16.1.0 - constrains: - - libgfortran 16.1.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: {} - size: 556657 - timestamp: 1785374459225 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.0-hdff4504_0.conda - sha256: 70a414faef075e11e7a51861e9e9c953d8373b0089070f98136a7578d8cda67e - md5: 86bdf23c648be3498294c4ab861e7090 - depends: - - __osx >=11.0 - - libffi >=3.4,<4.0a0 - - libiconv >=1.18,<2.0a0 - - libintl >=0.23.1,<1.0a0 - - libzlib >=1.3.1,<2.0a0 - - pcre2 >=10.44,<10.45.0a0 - constrains: - - glib 2.84.0 *_0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libglib >=2.84.0,<3.0a0 - size: 3698518 - timestamp: 1743039055882 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.1-ha08bb59_2.conda - sha256: 3b32a7a710132d509f2ea38b2f0384414c863533e0fc7ac71b6a0763e4c67424 - md5: 62d6f3b832d7d79ae0c0aa1bb3c325fa + size: 554806 + timestamp: 1787617465010 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + sha256: 9c25def7c7f9ab98b265690c80012e917d080538875f9679b46ccd0c042e6201 + md5: 14ef48eb322e0bfb1e5fe31e047359e3 depends: - __osx >=11.0 - libintl >=0.25.1,<1.0a0 - - libffi >=3.5.2,<3.6.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libiconv >=1.18,<2.0a0 - libzlib >=1.3.2,<2.0a0 - constrains: - - glib >2.66 - license: LGPL-2.1-or-later - size: 4439458 - timestamp: 1778508895255 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda - sha256: c9f7273bbe06d1693ff3a2b6f8b49b74e2ef6cbac2cead7aae767f2f5191b7aa - md5: 70a6bcf13dc0dd00fe3fe91190d38771 - depends: - - __osx >=11.0 - pcre2 >=10.47,<10.48.0a0 - - libffi >=3.7.0,<3.8.0a0 - - libintl >=0.25.1,<1.0a0 - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.2,<2.0a0 + - libffi >=3.7.0,<3.8.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4447961 - timestamp: 1786457780347 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.28.0-hfe08963_0.conda - sha256: 8ac585e360937aaf9f323e7414c710bf00eec6cf742c15b521fd502e6e3abf2b - md5: 68fb9b247b79e8ac3be37c2923a0cf8a - depends: - - __osx >=11.0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcurl >=8.9.1,<9.0a0 - - libcxx >=16 - - libgrpc >=1.62.2,<1.63.0a0 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - openssl >=3.3.1,<4.0a0 - constrains: - - libgoogle-cloud 2.28.0 *_0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud >=2.28.0,<2.29.0a0 - size: 848880 - timestamp: 1723369224404 + size: 4443052 + timestamp: 1787884141804 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda sha256: a48050bcf3827e0fc10de5a7d251760b8b84011f3aeea23ae18b5e0ce25aff19 md5: 4f34ca73f16b37fae583ce16c48b5492 @@ -17592,32 +11383,11 @@ packages: - libgoogle-cloud 3.8.0 *_0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud >=3.8.0,<3.9.0a0 size: 1804429 timestamp: 1786225612587 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.28.0-h1466eeb_0.conda - sha256: c62d08339e98fd56d65390df1184d8c2929de2713d431a910c3bb59750daccac - md5: 16874ac519f64d2199fab04fd9bd821d - depends: - - __osx >=11.0 - - libabseil - - libcrc32c >=1.1.2,<1.2.0a0 - - libcurl - - libcxx >=16 - - libgoogle-cloud 2.28.0 hfe08963_0 - - libzlib >=1.3.1,<2.0a0 - - openssl - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - size: 522700 - timestamp: 1723370053755 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda sha256: 13251d6a72028a13a9d142815b23818a56f6f88a67f9dd5f19a881eb2fcf5636 md5: 36ba29b4cf02ed97ce269939b750961a @@ -17632,35 +11402,11 @@ packages: - openssl license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 size: 541527 timestamp: 1786225749476 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda - sha256: d2c5b5a828f6f1242c11e8c91968f48f64446f7dd5cbfa1197545e465eb7d47a - md5: e624fc11026dbb84c549435eccd08623 - depends: - - c-ares >=1.28.1,<2.0a0 - - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - libcxx >=16 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libre2-11 >=2023.9.1 - - libzlib >=1.2.13,<2.0.0a0 - - openssl >=3.2.1,<4.0a0 - - re2 - constrains: - - grpc-cpp =1.62.2 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libgrpc >=1.62.2,<1.63.0a0 - size: 5016525 - timestamp: 1713392846329 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda sha256: b9d0f510488074e889ca3cecd2b7490e8e830f7a60e2e91809a90355bf4d1a57 md5: 5f7f645a15eedc24da7f271dc2086bcf @@ -17679,21 +11425,20 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 size: 4898235 timestamp: 1783587327477 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda - sha256: 5803da482e914fc5d7ea82b31789471973b03ca58e9caffedd86e175c3aee447 - md5: 19248fa96b4c573e46bfec7fa3c875fa +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + sha256: d152fe1ade504acaa7d4167f74565ea1dfd85d88cc9085e076e0e7010068e894 + md5: c931e6d5af7f8f824fab6c474f3a1e94 depends: - __osx >=11.0 - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.15,<2.0a0 - icu >=78.3,<79.0a0 - - libcxx >=19 + - libcxx >=21 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libglib >=2.88.3,<3.0a0 @@ -17701,35 +11446,55 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 942277 - timestamp: 1785770026085 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-h493e8d7_0.conda - sha256: e2b0108325d360961750bd35d975ca59694f9c1efff6ec241470c68ca09cacc0 - md5: 5b102fdc40afa0b6030c7867d939c00e + size: 972888 + timestamp: 1787795084686 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_0.conda + sha256: 1c98121d5e12ffbb86a77b1477d3bd7eca6f5f22dab3eb50ba57bf2fc98234e8 + md5: 6f23309d5939e5c7edcf57095daaa99a depends: - __osx >=11.0 - - libcxx >=19 + - cairo >=1.18.4,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=21 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz 14.4.0 hcda0f7c_0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + run_exports: + weak: + - libharfbuzz >=14.4.0 + size: 1508756 + timestamp: 1787795102730 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda + sha256: d965f815878a176fb75329d02718f449230e687e100a081762688f763990abf2 + md5: 0fd99c6f562545ace82194d79e697ad3 + depends: + - __osx >=11.0 + - libcxx >=21 license: Apache-2.0 OR BSD-3-Clause - purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 - size: 610044 - timestamp: 1784325884330 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda - sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03 - md5: 4d5a7445f0b25b6a3ddbb56e790f5251 + size: 612619 + timestamp: 1787282995626 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda + sha256: 689a14968267f2f97c07112fca5636e7d756036b9aee969911267c20bd3eea1f + md5: 17f4744c0873e9f77ac9b5cd0a27c187 depends: - __osx >=11.0 license: LGPL-2.1-only - purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 - size: 750379 - timestamp: 1754909073836 + size: 750816 + timestamp: 1787033961086 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda sha256: 99d2cebcd8f84961b86784451b010f5f0a795ed1c08f1e7c76fbb3c22abf021a md5: 5103f6a6b210a3912faf8d7db516918c @@ -17737,26 +11502,11 @@ packages: - __osx >=11.0 - libiconv >=1.18,<2.0a0 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libintl >=0.25.1,<1.0a0 size: 90957 timestamp: 1751558394144 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - sha256: 17e035ae6a520ff6a6bb5dd93a4a7c3895891f4f9743bcb8c6ef607445a31cd0 - md5: b8a7544c83a67258b0e8592ec6a5d322 - depends: - - __osx >=11.0 - constrains: - - jpeg <0.0.0a - license: IJG AND BSD-3-Clause AND Zlib - purls: [] - run_exports: - weak: - - libjpeg-turbo >=3.1.4.1,<4.0a0 - size: 555681 - timestamp: 1775962975624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda sha256: 05006418f9392c9b723e8428808db106de0350a497832f95f921b85ff1072310 md5: b2f8c8e5a7651a1d7c05404c3a159517 @@ -17765,29 +11515,27 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib - purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 size: 558459 timestamp: 1785896382474 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-h934fa54_1.conda - sha256: 83b69f759845ddc12444f5a812bdf08782ab2b0156ae08b706b26777918416c3 - md5: a7040219e41397bf619b7062aaa88de1 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-hb71b141_2.conda + sha256: 1f02661543be1722b619548b5207d6eaceab5bf95983fd6c9487f52bd8246a7a + md5: f62de5ae42f3c1f54f87ae2b303f6e90 depends: + - libcxx >=21 - __osx >=11.0 - - libcxx >=19 - libhwy >=1.4.0,<1.5.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 - size: 1032881 - timestamp: 1783146206980 + size: 1054154 + timestamp: 1786691545529 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda sha256: bfc6c0b1f30de524e3270eed2171d87e6b96552ff90cf2a5eb34c00d6bcb3dfa md5: 3d8eeedb8e541d1cb593e8204fcc397d @@ -17799,74 +11547,41 @@ packages: - uriparser >=0.9.8,<1.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 283804 timestamp: 1777027670481 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - build_number: 8 - sha256: 8a076fe82142a00fe85f5a5a5351e286e8064f0100fe13608d19182cd0018c25 - md5: 85adeb3d469d082dbd9c8c39e36dec57 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda + build_number: 10 + sha256: 09551a3022c0f8151dab85f8b89dc151a635f672c177364139c7bd019459a2b8 + md5: 35ee607ea2e9f75c699158c5f77ef9fd depends: - - libblas 3.11.0 8_h51639a9_openblas - constrains: - - libcblas 3.11.0 8*_openblas - - blas 2.308 openblas - - liblapacke 3.11.0 8*_openblas - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - liblapack >=3.11.0,<3.12.0a0 - size: 18925 - timestamp: 1779859153970 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda - build_number: 9 - sha256: db09e8e6a58415da1d866221cf518e53e84c6766a4500faae716cfa204f696ff - md5: ecc87ca1e25bd94a08c82904eb4e6846 - depends: - - libblas 3.11.0 9_h51639a9_openblas + - libblas 3.11.0 10_h51639a9_openblas constrains: - - blas 2.309 openblas - - libcblas 3.11.0 9*_openblas - - liblapacke 3.11.0 9*_openblas + - blas 2.310 openblas + - libcblas 3.11.0 10*_openblas + - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 - size: 18144 - timestamp: 1786058899889 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-8_h1b118fd_openblas.conda - build_number: 8 - sha256: 589d3218009b4002d486a7d3f88d3313e81c1e7720ddb3ee4ee2eff07fa34f9b - md5: bd1b597f41e950e2058978497bf35c2e + size: 18183 + timestamp: 1788076996690 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-10_h1b118fd_openblas.conda + build_number: 10 + sha256: 4e0c5e615a343d9b21801f026f2faa855dd8696e894e9bd6773c1b5b604ff769 + md5: db6da38ebe5cf31883500a1393522223 depends: - - libblas 3.11.0 8_h51639a9_openblas - - libcblas 3.11.0 8_hb0561ab_openblas - - liblapack 3.11.0 8_hd9741b5_openblas + - libblas 3.11.0 10_h51639a9_openblas + - libcblas 3.11.0 10_hb0561ab_openblas + - liblapack 3.11.0 10_hd9741b5_openblas constrains: - - blas 2.308 openblas + - blas 2.310 openblas license: BSD-3-Clause - license_family: BSD - size: 18968 - timestamp: 1779859160325 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - sha256: 34878d87275c298f1a732c6806349125cebbf340d24c6c23727268184bba051e - md5: b1fd823b5ae54fbec272cea0811bd8a9 - depends: - - __osx >=11.0 - constrains: - - xz 5.8.3.* - license: 0BSD - purls: [] run_exports: weak: - - liblzma >=5.8.3,<6.0a0 - size: 92472 - timestamp: 1775825802659 + - liblapacke >=3.11.0,<3.12.0a0 + size: 18199 + timestamp: 1788077001021 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda sha256: 23d0630046a3e8b164d8f80f2b74ed2605af2e7050ab9913018056402fae4311 md5: 8ab10323068b107661a4b9a4af84f3b5 @@ -17875,198 +11590,134 @@ packages: constrains: - xz 5.8.3.* license: 0BSD - purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 size: 91720 timestamp: 1786348695846 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.3-h8088a28_0.conda - sha256: 3002be39c0e98ec6cd103b0dc2963dc9e0d7cab127fb2fe9a8de9707a76ed1f0 - md5: ebe1f5418d6e2d4bbc26b2c906a0a470 - depends: - - __osx >=11.0 - - liblzma 5.8.3 h8088a28_0 - license: 0BSD - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 118482 - timestamp: 1775825828010 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.8.1-h7950639_0.conda - sha256: 329842b641d98232297d02be78933e921e2a83fe4162a73bb5808eaa4ce8f18a - md5: 14e6371e08fd05a5b186a6cd5dd28141 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda + sha256: 5291da681b85d4a12483b0f9a57bf18e191d0a52e749f18266922457b5bc200c + md5: 1646d42b756ad5a554903866fc2871a1 depends: - cpp-expected >=1.3.1,<1.3.2.0a0 - libcxx >=19 - __osx >=11.0 - - reproc-cpp >=14.2,<15.0a0 - - nlohmann_json-abi ==3.12.0 - libmsgpack-c >=6.1.0,<7.0a0 - - libcurl >=8.20.0,<9.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - - reproc >=14.2,<15.0a0 - - libarchive >=3.8.7,<3.9.0a0 - - simdjson >=4.6.4,<4.7.0a0 + - simdjson >=4.6.6,<4.7.0a0 + - reproc >=14.2.7.post0,<14.3.0a0 + - libcurl >=8.21.0,<9.0a0 - fmt >=12.1.0,<12.2.0a0 - libsolv >=0.7.39,<0.8.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 - spdlog >=1.17.0,<1.18.0a0 - - openssl >=3.5.6,<4.0a0 - zstd >=1.5.7,<1.6.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - openssl >=3.5.7,<4.0a0 + - nlohmann_json-abi ==3.12.0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 license: BSD-3-Clause license_family: BSD - size: 1819419 - timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.8.1-hb995d7d_0.conda - sha256: b18cbec4a4c33ab0e47ecf5a6973c045e14c6e0e60d4c92ee7f7bafc0f0872e1 - md5: 09a9c695f497c6f39a99ae6d0335837d + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 1846328 + timestamp: 1786125740624 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda + sha256: 7e379c5b7527d878e2617237b73d7866dbdf3d9dbaf36352ad39ab0a85802831 + md5: c0894144cb6c8d04f7699a5f5ead0c64 depends: - libcxx >=19 - __osx >=11.0 - - libmamba >=2.8.1,<2.9.0a0 + - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD - size: 23141 - timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.8.1-py314h96ff89e_0.conda - sha256: 87894c7d410ef8e96763aa0dd7774e5daeb411099da884ca29c1630a4f2ef7ab - md5: b9dcc4e28de586704989de517ad499de + run_exports: {} + size: 23751 + timestamp: 1786125740624 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda + sha256: 785d75a82bf43c123222a3933f84a7c2d1f9d83eb5a07d2552c75725f17a6309 + md5: c57c6b1c9fe4b161997ce6a6a705faf6 depends: - python - - libmamba ==2.8.1 h7950639_0 - - libmamba-spdlog ==2.8.1 hb995d7d_0 - - libcxx >=19 + - libmamba ==2.9.0 py314hf3ae1d6_0 + - libmamba-spdlog ==2.9.0 heb3ad76_0 - __osx >=11.0 + - libcxx >=19 - python 3.14.* *_cp314 - - pybind11-abi ==11 - - yaml-cpp >=0.8.0,<0.9.0a0 - - nlohmann_json-abi ==3.12.0 + - zstd >=1.5.7,<1.6.0a0 + - libmamba >=2.9.0,<2.10.0a0 - spdlog >=1.17.0,<1.18.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 + - openssl >=3.5.7,<4.0a0 - fmt >=12.1.0,<12.2.0a0 - python_abi 3.14.* *_cp314 - - zstd >=1.5.7,<1.6.0a0 - - openssl >=3.5.6,<4.0a0 - - libmamba >=2.8.1,<2.9.0a0 + - pybind11-abi ==11 + - nlohmann_json-abi ==3.12.0 license: BSD-3-Clause license_family: BSD - size: 778735 - timestamp: 1780948977563 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - sha256: 1089c7f15d5b62c622625ec6700732ece83be8b705da8c6607f4dabb0c4bd6d2 - md5: 57c4be259f5e0b99a5983799a228ae55 + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 779694 + timestamp: 1786125740624 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + sha256: 04cc136c5a956a73aa14e0a160b5822b0b29714e67b299fa1b1fd16dbcba5366 + md5: ff33a4dbd93abc8a798cc4e0e7c8136d depends: - __osx >=11.0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: {} - size: 73690 - timestamp: 1769482560514 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_6.conda - sha256: 75d11ecc6e819f35fc683928263b14d2b865a623897e194172b3542bcbf7452c - md5: 4d3876da4f34c265f62bc4b4890ecd82 + size: 73289 + timestamp: 1786651074391 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda + sha256: 7eea97b70943a054f90405b1c17da0c8b25348c77bc815978fac05d99e574334 + md5: bfb208af98c6c65859211b972e45cafd depends: - libcxx >=19 - __osx >=11.0 constrains: - - msgpack-c ==6.1.0 *_6 + - msgpack-c ==6.1.0 *_7 license: BSL-1.0 - size: 39063 - timestamp: 1770807536463 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_he469be0_114.conda - sha256: aeac591ba859f9cf775993e8b7f21e50803405d41ef363dc4981d114e8df88a8 - md5: 8fd3ce6d910ed831c130c391c4364d3f - depends: - - __osx >=11.0 - - blosc >=1.21.5,<2.0a0 - - bzip2 >=1.0.8,<2.0a0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libcxx >=16 - - libxml2 >=2.12.7,<2.14.0a0 - - libzip >=1.10.1,<2.0a0 - - libzlib >=1.2.13,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - zlib - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - libnetcdf >=4.9.2,<4.9.3.0a0 - size: 681051 - timestamp: 1717671966211 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - sha256: 2bc7bc3978066f2c274ebcbf711850cc9ab92e023e433b9631958a098d11e10a - md5: 6ea18834adbc3b33df9bd9fb45eaf95b + - libmsgpack-c >=6.1.0,<7.0a0 + size: 39302 + timestamp: 1786189396450 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda + sha256: 445440d8cccccb6585f71d1eb7c949d64a2af2ced5481d924621424ff3b6a398 + md5: ac9902dcbe0417f50cf95ab2bde062fa depends: - __osx >=11.0 - - c-ares >=1.34.6,<2.0a0 - - libcxx >=19 + - c-ares >=1.34.8,<2.0a0 + - libcxx >=21 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.5,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 - size: 576526 - timestamp: 1773854624224 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libntlm-1.8-h5505292_0.conda - sha256: ea8c680924d957e12270dca549620327d5e986f23c4bd5f45627167ca6ef7a3b - md5: c90c1d3bd778f5ec0d4bb4ef36cbd5b6 - depends: - - __osx >=11.0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libntlm >=1.8,<2.0a0 - size: 31099 - timestamp: 1734670168822 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - sha256: 9dd455b2d172aeedfa2058d324b5b5822b0bc1b7c1f32cd183d7078540d2f6eb - md5: 909e41855c29f0d52ae630198cd57135 - depends: - - __osx >=11.0 - - libgfortran - - libgfortran5 >=14.3.0 - - llvm-openmp >=19.1.7 - constrains: - - openblas >=0.3.33,<0.3.34.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libopenblas >=0.3.33,<1.0a0 - size: 4304965 - timestamp: 1776995497368 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda - sha256: bcf1967f12f1b1cc769dcc77b255fb1b27aaceb2f185450e9596d137bc6ede76 - md5: 89d28fb841cf16211318524fa985e384 + size: 567024 + timestamp: 1787177422467 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda + sha256: 45bda47a2fd2e3936cd6da2756f57a5b22af445a6da6cc37a753bd96949721e7 + md5: 0b5dfcfae89beafb81234b90d5d07729 depends: - __osx >=11.0 - libgfortran - - libgfortran5 >=14.3.0 + - libgfortran5 >=14.4.0 - llvm-openmp >=19.1.7 constrains: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libopenblas >=0.3.34,<1.0a0 - size: 4318474 - timestamp: 1784288246205 + size: 4315889 + timestamp: 1788055739634 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda sha256: 9c1840af12f31643723a7ea7f6bde6a3a394783ced6aa65b75a9fc3ecb84f39f md5: a6c2fc0c5ddf105b3d3e353c1383a492 @@ -18084,7 +11735,6 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -18095,28 +11745,9 @@ packages: md5: 7f61001d82fd50385d4f9fb57f936e95 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 394664 timestamp: 1783133038717 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-17.0.0-hf0ba9ef_13_cpu.conda - build_number: 13 - sha256: fb7ee16e8f9bf60a1f136170231615c1a6f200087f12de4220e0c73f45edcdc6 - md5: 8d415217e6cc74179b5d00a61238b6a9 - depends: - - __osx >=11.0 - - libarrow 17.0.0 h20538ec_13_cpu - - libcxx >=17 - - libthrift >=0.20.0,<0.20.1.0a0 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libparquet >=17.0.0,<17.1.0a0 - size: 859328 - timestamp: 1725215440648 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda build_number: 4 sha256: 318412a665f7637598aef088a9dbc30c2a52614eebe1bde6e75be76708875271 @@ -18133,90 +11764,64 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 size: 1096341 timestamp: 1786315419506 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - sha256: 66eae34546df1f098a67064970c92aa14ae7a7505091889e00468294d2882c36 - md5: 2259ae0949dbe20c0665850365109b27 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda + sha256: f88da60b348ee1f3e1a9c20fe02142fdafe889f08da0b1cc33c1f3f14460239d + md5: e0466c58fd07db190b9a81b5e58539f2 depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement - purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 - size: 289546 - timestamp: 1776315246750 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpq-17.2-h9b1ab17_0.conda - sha256: 3df7defd514d989ead1280cabb13618e03f7cdace828e6b7a307f2e47845123d - md5: 0ba6b6772a08c40de13427c957ceaf67 - depends: - - __osx >=11.0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - openldap >=2.6.8,<2.7.0a0 - - openssl >=3.4.0,<4.0a0 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 2603320 - timestamp: 1732204754944 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-4.25.3-hc39d83c_1.conda - sha256: f51bde2dfe73968ab3090c1098f520b65a8d8f11e945cb13bf74d19e30966b61 - md5: fa77986d9170450c014586ab87e144f8 - depends: - - __osx >=11.0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcxx >=17 - - libzlib >=1.3.1,<2.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libprotobuf >=4.25.3,<4.25.4.0a0 - size: 2177164 - timestamp: 1727160770879 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h8daa630_2.conda - sha256: 782d30325e61249e3240979e0f76d9dbf67867b1f38424cfdb1abb3a0f2cfa95 - md5: 7f77d9a99dd9e79e1f5be50d6632f675 + size: 290219 + timestamp: 1786616561185 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda + sha256: 4129c13739e30dfbab8c20594acd8c182de2008f4dfc94d54f52b556eb39050c + md5: 7666c238f885d0c7927607ebfc163f97 depends: - __osx >=12.0 - libabseil * cxx17* - libabseil >=20260526.0,<20260527.0a0 - - libcxx >=19 + - libcxx >=21 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 - size: 2900719 - timestamp: 1783168180812 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda - sha256: 0140cb0d059ac531e476b85a543668b69b7dffec16f0d26269ab6c9b70546921 - md5: 75a4cdf128d016141db61732ea462276 + size: 2877232 + timestamp: 1787657252942 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda + sha256: c8c3153df39abedf3413483f672151ad70f1adb0c06c192bdf7ec432c3616b07 + md5: 5d270f716a2b4bc13df9af8612071b17 depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 - icu >=78.3,<79.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 - size: 72950 - timestamp: 1786443660105 + size: 72673 + timestamp: 1786970928205 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda + build_number: 104 + sha256: 551c2160312cbba2e8b4573c827a756d8ab7824a05f0eb64d954f0a18b2e5c21 + md5: 9ef5086783f252da744fc0f7a336e4d8 + depends: + - __osx >=11.0 + - libcxx >=20 + license: Python-2.0 + run_exports: {} + size: 1883387 + timestamp: 1788160791643 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda sha256: b110f7f9b2cec61ea793c521950414e00cd64826e18aeb4ab40369acf05c0039 md5: 46ea0eb4e71bffa35ab7070678bcb053 @@ -18228,42 +11833,23 @@ packages: - fribidi >=1.0.16,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 size: 31333 timestamp: 1784850348342 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda - sha256: c8a0a6e7a627dc9c66ffb8858f8f6d499f67fd269b6636b25dc5169760610f05 - md5: 0b7b2ced046d6b5fe6e9d46b1ee0324c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + sha256: 06f49291605c379467987989ff08d44b470a23afeb690d7e078517871969bff7 + md5: b750c4f83563c7528634bdcfd28622ce depends: + - __osx >=11.0 - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - libcxx >=16 - constrains: - - re2 2023.09.01.* - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - size: 171443 - timestamp: 1708947163461 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda - sha256: 06f49291605c379467987989ff08d44b470a23afeb690d7e078517871969bff7 - md5: b750c4f83563c7528634bdcfd28622ce - depends: - - __osx >=11.0 - - libabseil * cxx17* - - libabseil >=20260526.0,<20260527.0a0 - - libcxx >=19 + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=19 constrains: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -18285,27 +11871,11 @@ packages: constrains: - __osx >=11.0 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - librsvg >=2.62.3,<3.0a0 size: 2397567 timestamp: 1780452232118 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-h31fb324_16.conda - sha256: bf022fa3a85bc38c200c5f97d2e19ac5aa4e97908a6a542e8c13b7d3ff869224 - md5: 1a8e3f8e886499916b8942628e6b6880 - depends: - - __osx >=11.0 - - geos >=3.12.2,<3.12.3.0a0 - - libcxx >=16 - license: GPL-2.0-or-later - license_family: GPL - purls: [] - run_exports: - weak: - - librttopo >=1.1.0,<1.2.0a0 - size: 191683 - timestamp: 1720347975066 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda sha256: 2b28c777889b1b638244f65d5bef4a8ba4624bdb740cecf26c845876653552c2 md5: d07359797436cfc891b38e203cf0caac @@ -18315,39 +11885,36 @@ packages: - libcxx >=19 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 size: 192590 timestamp: 1761670939075 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda - sha256: 202be45db5726757a8ea1f374f85aacc18c504f5ff15b2558496dff4c8779c48 - md5: 9ed5ab909c449bdcae72322e44875a18 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h74c22ad_2.conda + sha256: 1cc97c217b103145e67ae6f928d0ae11ebc56879dfd10d739539624f0de80f86 + md5: ea78b9246e47aade6c94db52b1c9b5a4 depends: - __osx >=11.0 license: ISC - purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 - size: 247352 - timestamp: 1779164136206 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h7d962ec_0.conda - sha256: c5095231ffdc793bc46f191c0e072db5da220802433de548e882ab7d098a0496 - md5: 8d5409cf10e54f495099b2fda7d06306 + size: 249624 + timestamp: 1787225816699 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h626c152_1.conda + sha256: 45e2047bfd53afe77b275ac26c561a63fa0b50f42819bd7d0e73eb83f40593f8 + md5: b1685ac2297c78e075531b2b93cbd53e depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 - size: 430365 - timestamp: 1780057267477 + size: 434198 + timestamp: 1786955859375 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda sha256: 0ae7ae664900f278c89c5f7e7824f8189d03610b91b77fedc93bf6ae9d4da90c md5: 8909f6e5df2f99065ef12a6e6c2db274 @@ -18369,105 +11936,37 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA - purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 size: 2711368 timestamp: 1772594727365 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf7a34df_9.conda - sha256: ee462007733b803a549acc1ebb2ea7b40cfd28efecdd6c17cadb48f0acd2e8ad - md5: a0c631a4cf164b1688484db0da3072b5 - depends: - - __osx >=11.0 - - freexl >=2 - - freexl >=2.0.0,<3.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - libcxx >=16 - - libiconv >=1.17,<2.0a0 - - librttopo >=1.1.0,<1.2.0a0 - - libsqlite >=3.46.0,<4.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - sqlite - - zlib - license: MPL-1.1 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - libspatialite >=5.1.0,<5.2.0a0 - size: 3000596 - timestamp: 1722338001828 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - sha256: 862463917e8ef5ac3ebdaf8f19914634b457609cc27ba678b7197124cefeb1f7 - md5: 1ebde5c677f00765233a17e278571177 - depends: - - __osx >=11.0 - - icu >=78.3,<79.0a0 - - libzlib >=1.3.2,<2.0a0 - license: blessing - size: 927724 - timestamp: 1780575223548 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.3-h1b79a29_0.conda - sha256: a73a8acd97a6599fd6e561514db9f101ca7fd984cdc0cfd91ba74c8aa9dbe067 - md5: 7184d95871a58b8258a8ea124ed5aabc - depends: - - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.3,<4.0a0 - size: 924912 - timestamp: 1782519136322 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-h1ae2325_0.conda - sha256: 745662565e103f290e9dc4263bbd88285082f8cf699854fe2d5f1e35a4a0d326 - md5: 0e3477c0c3e718dcf2eb74ccc8f68570 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda + sha256: 839b31d4830e896b4d315b551d27f2bb08026bc946df04bcc360d8627c3ba2cd + md5: fde96d40ebe9a9cb34e4122380189ddb depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libzlib >=1.3.2,<2.0a0 license: blessing - purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 - size: 929203 - timestamp: 1785016131414 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a - md5: b68e8f66b94b44aaa8de4583d3d4cc40 + size: 942754 + timestamp: 1787051243846 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda + sha256: a056e518c3d2d09fbb1778cea80c0c95338fb24adf1a817bbf90fb484850a304 + md5: d957a8eda98c49b073e8dcfd677c127a depends: - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.0,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 - size: 279193 - timestamp: 1745608793272 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.20.0-h64651cc_1.conda - sha256: b6afcbc934258e0474e0f1059bc7b23865723b902062f2f2910e0370e6495401 - md5: 4cf2e5233320648397184415f380c891 - depends: - - __osx >=11.0 - - libcxx >=17 - - libevent >=2.1.12,<2.1.13.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libthrift >=0.20.0,<0.20.1.0a0 - size: 315041 - timestamp: 1724657608736 + size: 280492 + timestamp: 1786714226814 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda sha256: 568bb23db02b050c3903bec05edbcab84960c8c7e5a1710dac3109df997ac7f1 md5: d006875f9a58a44f92aec9a7ebeb7150 @@ -18479,52 +11978,30 @@ packages: - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 size: 323017 timestamp: 1777019893083 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda - sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f - md5: e2a72ab2fa54ecb6abab2b26cde93500 - depends: - - __osx >=11.0 - - lerc >=4.0.0,<5.0a0 - - libcxx >=19 - - libdeflate >=1.25,<1.26.0a0 - - libjpeg-turbo >=3.1.0,<4.0a0 - - liblzma >=5.8.1,<6.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: HPND - purls: [] - run_exports: - weak: - - libtiff >=4.7.1,<4.8.0a0 - size: 373892 - timestamp: 1762022345545 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda - sha256: 253153cabf9469170e02b21a6bc9aa598048e7c34966b1103af52d27d2a708a4 - md5: 6fa87106b3c041ad6d965a9411e79b94 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + sha256: 847d22a86ae28f66d3888702698254d25bb4eac3a0f64c1fabfb1842ac00be4c + md5: b6b191267455bf202af79f973690ed5d depends: - __osx >=11.0 - - lerc >=4.1.0,<5.0a0 - - libcxx >=19 + - lerc >=4.2.0,<5.0a0 + - libcxx >=21 - libdeflate >=1.25,<1.26.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 - liblzma >=5.8.3,<6.0a0 - libwebp-base >=1.6.0,<2.0a0 - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND - purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 - size: 387825 - timestamp: 1783085754081 + size: 380645 + timestamp: 1787756067271 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda sha256: ae1a82e62cd4e3c18e005ae7ff4358ed72b2bfbfe990d5a6a5587f81e9a100dc md5: 2255add2f6ae77d0a96624a5cbde6d45 @@ -18532,40 +12009,11 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 size: 87916 timestamp: 1768735311947 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.8.0-hc098a78_1.conda - sha256: 7807a98522477a8bf12460402845224f607ab6e1e73ac316b667169f5143cfe5 - md5: ed89b8bf0d74d23ce47bcf566dd36608 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libutf8proc >=2.8.0,<2.9.0a0 - size: 82462 - timestamp: 1732829832932 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda - sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd - md5: e5e7d467f80da752be17796b87fe6385 - depends: - - __osx >=11.0 - constrains: - - libwebp 1.6.0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libwebp-base >=1.6.0,<2.0a0 - size: 294974 - timestamp: 1752159906788 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda sha256: 0ff54650d470c7e54cbeffdd53a8c063e055a7bcf784388c0385ce5c4741b0f4 md5: 168a13e329259710b28277abc1395b8e @@ -18575,31 +12023,29 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 size: 294522 timestamp: 1785955350410 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 - md5: af523aae2eca6dfa1c8eec693f5b9a79 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda + sha256: f55c15b66c8ae27d8e30445193acc049b280eb51a47d6f1753bed5ac2937a217 + md5: 786c0680e77665d1938ce9cdd7cf82a7 depends: - __osx >=11.0 - pthread-stubs - - xorg-libxau >=1.0.11,<2.0a0 - - xorg-libxdmcp + - xorg-libxau >=1.0.12,<2.0a0 + - xorg-libxdmcp >=1.1.5,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 - size: 323658 - timestamp: 1727278733917 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - sha256: ff75b84cdb9e8d123db2fa694a8ac2c2059516b6cbc98ac21fb68e235d0fd354 - md5: 19edaa53885fc8205614b03da2482282 + size: 324234 + timestamp: 1787885764666 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda + sha256: 2ca61d8287339c726b151fa612e83d2afa1418b89fa17694c873dc231bc9b077 + md5: f86c0b8ac5c866049717b55df1049c2c depends: - __osx >=11.0 - icu >=78.3,<79.0a0 @@ -18610,111 +12056,46 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 466360 - timestamp: 1776377102261 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.9-h4a9ca0c_0.conda - sha256: 7ab9b3033f29ac262cd3c846887e5b512f5916c3074d10f298627d67b7a32334 - md5: 763c7e76295bf142145d5821f251b884 - depends: - - __osx >=11.0 - - icu >=75.1,<76.0a0 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.1,<6.0a0 - - libzlib >=1.3.1,<2.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxml2 >=2.13.9,<2.14.0a0 - size: 581379 - timestamp: 1761766437117 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda - sha256: 2fe1d8de0854342ae9cabe408b476935f82f5636e153b3b497456264dc8ff3a1 - md5: 8e037d73747d6fe34e12d7bcac10cf21 + size: 466188 + timestamp: 1787237580766 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda + sha256: ff1b70683ccb82f8aa7f3eda53405a2036d849daba4e4e622f330dcd49d37700 + md5: 06a3f8eb5cdde56b2d10b49ae3337954 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 h5ef1a60_0 + - libxml2-16 2.15.3 h5ef1a60_1 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 41102 - timestamp: 1776377119495 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda - sha256: 541980a15bf0acb7794754f1cde9cfeb74ca27c139d065c1c6541c73b4e07105 - md5: 469f4af737803bf7deda25d41c557593 + size: 41264 + timestamp: 1787237585903 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda + sha256: 5628776073d5e736e3a87747d4f91c31478fbbc980c01c4455c892edf6d308d0 + md5: fff82f66f5775661123c2963930f318b depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h5654f7c_0 - - libxml2-16 2.15.3 h5ef1a60_0 + - libxml2 2.15.3 h5654f7c_1 + - libxml2-16 2.15.3 h5ef1a60_1 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 80217 - timestamp: 1776377134719 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxslt-1.1.43-h429d6fd_0.conda - sha256: 3491de18eb09c9d6298a7753bcc23b49a58886bd097d2653accaa1290f92c2c6 - md5: f25eb0a9e2c2ecfd33a4b97bb1a84fb6 - depends: - - __osx >=11.0 - - libxml2 >=2.13.8,<2.14.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxslt >=1.1.43,<2.0a0 - size: 219752 - timestamp: 1753273652440 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzip-1.11.2-h1336266_0.conda - sha256: 507599a77c1ce823c2d3acaefaae4ead0686f183f3980467a4c4b8ba209eff40 - md5: 7177414f275db66735a17d316b0a81d6 - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libzip >=1.11.2,<2.0a0 - size: 125507 - timestamp: 1730442214849 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - sha256: 361415a698514b19a852f5d1123c5da746d4642139904156ddfca7c922d23a05 - md5: bc5a5721b6439f2f62a84f2548136082 - depends: - - __osx >=11.0 - constrains: - - zlib 1.3.2 *_2 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 47759 - timestamp: 1774072956767 + size: 80342 + timestamp: 1787237590812 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 md5: f39288f0ea63ae962e1a2e4f355a0d75 @@ -18724,44 +12105,26 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 size: 47822 timestamp: 1785277049190 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda - sha256: ccbaad6bbc88f135ab849bc36af5fa6eda36a9ed18ce6f58e3dde3d11784c156 - md5: a9c118f6343fb6301b6f3b4e94c4c562 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + sha256: 996d3a9de61c8ccc3fcb265938f9845f11868251f38e6db4e7190de3f9a971a2 + md5: 0affff5130a421d11d4d77ffbb00dad7 depends: - __osx >=11.0 constrains: - intel-openmp <0.0a0 - - openmp 22.1.8|22.1.8.* + - openmp 23.1.0|23.1.0.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE - purls: [] run_exports: strong: - - llvm-openmp >=22.1.8 - size: 286313 - timestamp: 1781736516782 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.3.3-py312h11d1bbd_1.conda - sha256: 592e903be664d4daca335bc8b95f7e092aef0276830a2a7b5962ca7bbca60ee6 - md5: 4a9eb3dc0ba76484cae3084b99d008e5 - depends: - - __osx >=11.0 - - lz4-c >=1.9.3,<1.10.0a0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping - run_exports: {} - size: 108178 - timestamp: 1725089672204 + - llvm-openmp >=23.1.0 + size: 287808 + timestamp: 1787723323710 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda sha256: fb105138b325e81f8dabc859cc47e9e29295b68cd6fd4dd333ed30e527e7c08b md5: aea17e1b366b814eff15fc3c8c4c1e3c @@ -18774,68 +12137,34 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 127471 timestamp: 1765026415723 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda - sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 - md5: 01511afc6cc1909c5303cf31be17b44f +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + sha256: 1887867b393eb3c2b336deaffcf228574821ea1e0dcb9ef7bf6c9f59cec40f03 + md5: d47f7f3481c6f2fcc4ed22802f61c6dd depends: - __osx >=11.0 - - libcxx >=18 + - libcxx >=21 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 - size: 148824 - timestamp: 1733741047892 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.9.4-hb7217d7_0.conda - sha256: fc343b8c82efe40819b986e29ba748366514e5ab94a1e1138df195af5f45fa24 - md5: 45505bec548634f7d05e02fb25262cb9 - depends: - - libcxx >=14.0.6 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - lz4-c >=1.9.4,<1.10.0a0 - size: 141188 - timestamp: 1674727268278 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda - sha256: db40fd25c6306bfda469f84cddd8b5ebb9aa08d509cecb49dfd0bb8228466d0c - md5: e56eaa1beab0e7fed559ae9c0264dd88 + size: 171838 + timestamp: 1786717209785 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda + sha256: 95d5f16bfa62ff50980a5e0329920bb2b5ec66d0e0d6c87954603713c3519ece + md5: 7fad2bcb66997fc0a53442db98031a39 depends: - __osx >=11.0 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 - size: 152755 - timestamp: 1753889267953 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda - sha256: 330394fb9140995b29ae215a19fad46fcc6691bdd1b7654513d55a19aaa091c1 - md5: 11d95ab83ef0a82cc2de12c1e0b47fe4 - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - constrains: - - jinja2 >=3.0.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping - run_exports: {} - size: 25564 - timestamp: 1772445846939 + size: 154521 + timestamp: 1787049250312 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda sha256: 411153d14ee0d98be6e3751cf5cc0502db17bce2deebebb8779e33d29d0e525f md5: d33c0a15882b70255abdd54711b06a45 @@ -18848,25 +12177,9 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 27256 timestamp: 1772445397216 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda - sha256: 79d518d9556ce81ff4e55e3e947a5a8cb6b1c4a101536e86ab90c34d2c80efcf - md5: 94174d301ce2f5962197fb9b880ea8ff - depends: - - matplotlib-base >=3.10.9,<3.10.10.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - tornado >=5 - license: PSF-2.0 - license_family: PSF - purls: [] - run_exports: {} - size: 17770 - timestamp: 1777001080046 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda sha256: ea350f06df2581e68b9f5e1d029ad603c4044d9afff1294ca9315ac986b5d05f md5: 710b3448473bb1bff68d2ba19cbc6c58 @@ -18877,40 +12190,9 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF - purls: [] run_exports: {} size: 14958 timestamp: 1785211126869 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda - sha256: c2dc997012fb8a901163cdad333ba5ba27733aa26d67a28a6501f4b4d69fcbce - md5: e5d83f3ae6ada32d4e64e366a41f9ff6 - depends: - - __osx >=11.0 - - contourpy >=1.0.1 - - cycler >=0.10 - - fonttools >=4.22.0 - - freetype - - kiwisolver >=1.3.1 - - libcxx >=19 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - numpy >=1.23 - - numpy >=1.23,<3 - - packaging >=20.0 - - pillow >=8 - - pyparsing >=2.3.1 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python-dateutil >=2.7 - - python_abi 3.12.* *_cp312 - - qhull >=2020.2,<2020.3.0a0 - license: PSF-2.0 - license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping - run_exports: {} - size: 8191891 - timestamp: 1777001043842 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda sha256: 363b319e21cf4782fe264b96494c6be949db79d0d0ba126be1a1a94a5e96aae6 md5: 41fb78906bf657af4fbdcf8c67f41c11 @@ -18936,157 +12218,89 @@ packages: - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8776291 timestamp: 1785211103855 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.0-py314hcec6336_1.conda - sha256: 0c6499e1ff5294fb33615e5d93990b7e29c8caa742d627016072b15f51becf1d - md5: 5a2e8415a1cddef0de8461327bd96bf5 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_2.conda + sha256: 2cd4b2522ccfb110b9256194c0c6754228cd4242276600dc0c93bfab2234abff + md5: 83de909f056ea0ba1613832b657229a8 depends: - python - tomli-w - - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT - size: 212016 - timestamp: 1782062817762 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda - sha256: 4b1d4e4b17d27bbec50b1ad144e000c3b077cc4e0ef487ef11011c52ef8c86ab - md5: fcd860469a8fa003e25d472b40b0ff81 - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libcxx >=19 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.6,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - minizip >=4.2.1,<5.0a0 - size: 459816 - timestamp: 1778003052237 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-hdb7fadc_0.conda - sha256: 9a59199ab5812dd7a237ab9cbd1091a29a9532a70035929fb7f255fd2ee5da0d - md5: 6bf8d76a71b4eb31138fc5c217a77af0 + run_exports: {} + size: 207305 + timestamp: 1788033960061 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda + sha256: 0a737bebd276d48f0436842ca47b38b823b4f8e3c3065848c9d1f421f334a4aa + md5: b7819f4118a456301e220e995afd53ac depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - - libcxx >=19 + - libcxx >=21 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other - purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 462323 - timestamp: 1782852375098 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py312h3093aea_1.conda - sha256: 1ace9b344734fda6d8c84d02c3efbf106fe68a90b6dfdb49cd46009f58e831ee - md5: bcab4615e2f53affe6b34fc8887b3f12 - depends: - - python - - __osx >=11.0 - - python 3.12.* *_cpython - - libcxx >=19 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping - run_exports: {} - size: 104400 - timestamp: 1782460860576 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314h6cfcd04_0.conda - sha256: 09ea486f9248fdd987430a3519c31a0331e903edecc9740586a3e0abd590f840 - md5: 7f80c3267c4b89767d7bbdf36d69fe80 - depends: - - __osx >=11.0 - - libcxx >=19 - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: Apache - size: 92074 - timestamp: 1782070774684 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py314hdf3942c_1.conda - sha256: 81391bab68e075ddbcf2bfe035c069734ba12832a813b39897155008f431899b - md5: 709964d5659d3a2192395a7ac8768b15 + size: 462531 + timestamp: 1788051612824 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + sha256: 1376d243d5f9a1145ba03950679932a70834d5db4ef9de8fdd1b030a64a7d619 + md5: 0b9070858d6aaf7f767a520063f68a3c depends: - python - - libcxx >=19 + - libcxx >=21 - __osx >=11.0 - python_abi 3.14.* *_cp314 license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping run_exports: {} - size: 103113 - timestamp: 1786217418606 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda - sha256: 5533e7e3d4b0819b4426f8a1b3f680e6b9c922cdae2b7fabcd0e8c59df22772a - md5: 1cdbe54881794ee356d3cba7e3ed6668 + size: 101639 + timestamp: 1787850896574 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda + sha256: cc40c57dbfa4890873835f22415760488319b03d4edfc11386b2e90b89f011da + md5: 408009d803402dd1ad4ffae452f17353 depends: + - llvm-openmp + - libcxx >=21 - __osx >=11.0 - - libcxx >=18 - - llvm-openmp >=18.1.8 + - llvm-openmp >=21.1.8 license: MIT - license_family: MIT - purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 - size: 154087 - timestamp: 1747117056226 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.1.0-py314h2fbedac_0.conda - sha256: d205dc89fa338ca312ed54864ca377e9a52e4273509370c3678503115c95eb05 - md5: 54f38ddefd5fbd675bc9ff69d7a31fd7 + size: 171420 + timestamp: 1788122548109 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hf2636f2_0.conda + sha256: 934a50e83c879f3665cc84f03f0d83f58b319376cb3e94275b2aa67d328a309d + md5: 0819586a0fc1927c72a757d872e452cc depends: - - ast-serialize >=0.3.0,<1.0.0 + - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 - pathspec >=1.0.0 - python - - python-librt >=0.11.0 + - python-librt >=0.13.0 - typing_extensions >=4.6.0 - psutil >=4.0 - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 13520137 - timestamp: 1780315680928 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - sha256: 4ea6c620b87bd1d42bb2ccc2c87cd2483fa2d7f9e905b14c223f11ff3f4c455d - md5: 343d10ed5b44030a2f67193905aea159 - depends: - - __osx >=11.0 - license: X11 AND BSD-3-Clause - purls: [] - run_exports: - weak: - - ncurses >=6.6,<7.0a0 - size: 805509 - timestamp: 1777423252320 + run_exports: {} + size: 13265109 + timestamp: 1786887244960 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 md5: 3dfa0d0316dc246cd44937a557de4501 depends: - __osx >=11.0 license: X11 AND BSD-3-Clause - purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -19099,184 +12313,57 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 137984 timestamp: 1785920576349 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.38-heaf21c2_0.conda - sha256: f8373ff02098179b9f9b2ce7b309e7ca4021c26180d07d67e1726e3d02e68e26 - md5: 0b528ead848386c8a53ee0b76fbf2ac1 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda + sha256: 372dfde32c064cfcc9e2bd73956d8229053794697c3e875a9bcb916e5163d66e + md5: 07f5745d2ec9349b1401e3891240c64f depends: + - python + - libcxx >=21 - __osx >=11.0 - - libcxx >=19 - license: MPL-2.0 - license_family: MOZILLA - purls: [] + - liblapack >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause run_exports: weak: - - nspr >=4.38,<5.0a0 - size: 201977 - timestamp: 1762349100072 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nss-3.118-h1c710a3_0.conda - sha256: d57f7b2cf2860a2a848e3dd43cc4f5488e60050a7d62af1834da3ee43911d9c4 - md5: ae07409126ced4f0d982dfeab0681016 + - numpy >=1.25,<3 + size: 7257323 + timestamp: 1788129265176 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 + md5: 4b5d3a91320976eec71678fad1e3569b depends: - __osx >=11.0 - libcxx >=19 - - libsqlite >=3.51.0,<4.0a0 + - libpng >=1.6.55,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 - libzlib >=1.3.1,<2.0a0 - - nspr >=4.38,<5.0a0 - license: MPL-2.0 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - nss >=3.118,<4.0a0 - size: 1839904 - timestamp: 1763486575227 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda - sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 - md5: d83fc83d589e2625a3451c9a7e21047c - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - libcxx >=16 - - liblapack >=3.9.0,<4.0a0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause + license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - - numpy >=1.26.4,<2.0a0 - size: 6073136 - timestamp: 1707226249608 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py314hb79c6fa_0.conda - sha256: dfd260fce05ff833ac121a1b1e4aa22d9c346a781c1e883dd871338cc1d9f8b0 - md5: 5283d56d01517fa1780c72f2544e8603 - depends: - - python - - __osx >=11.0 - - libcxx >=19 - - libblas >=3.9.0,<4.0a0 - - python_abi 3.14.* *_cp314 - - liblapack >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause - license_family: BSD - size: 7123654 - timestamp: 1782112549976 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314hb79c6fa_0.conda - sha256: e70532da227635af2338676036c4a6b6acf8863e4dc9fc2cc55d68bd74e2f1f5 - md5: d050d2d7aac4d90c0dccf2b5829e2a7a - depends: - - python - - __osx >=11.0 - - libcxx >=19 - - python_abi 3.14.* *_cp314 - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping - run_exports: - weak: - - numpy >=1.25,<3 - size: 7154942 - timestamp: 1786330664173 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 - md5: 4b5d3a91320976eec71678fad1e3569b - depends: - - __osx >=11.0 - - libcxx >=19 - - libpng >=1.6.55,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - openjpeg >=2.5.4,<3.0a0 - size: 319697 - timestamp: 1772625397692 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.10-hbe55e7a_0.conda - sha256: 08d859836b81296c16f74336c3a9a455b23d57ce1d7c2b0b3e1b7a07f984c677 - md5: 6fd5d73c63b5d37d9196efb4f044af76 - depends: - - __osx >=11.0 - - cyrus-sasl >=2.1.27,<3.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libcxx >=18 - - openssl >=3.5.0,<4.0a0 - license: OLDAP-2.8 - license_family: BSD - purls: [] - run_exports: - weak: - - openldap >=2.6.10,<2.7.0a0 - size: 843597 - timestamp: 1748010484231 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_0.conda - sha256: b3e3ca895c336d4eb91c5d2f244a312bdb59a0de8cfa0cc4c179225ab2f6bbfb - md5: 8187a86242741725bfa74785fe812979 + - openjpeg >=2.5.4,<3.0a0 + size: 319697 + timestamp: 1772625397692 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda + sha256: f23239eacd75c4c50705e68fae1aa3292da473e6a3a4abe2330f1e6afa680704 + md5: ae71ab40048c19a389a7dcccb86c2481 depends: - __osx >=11.0 - ca-certificates license: Apache-2.0 license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 3102584 - timestamp: 1781069820667 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - sha256: 66be2283b5b37dcda1332b5e74c1782a8cb14fd2e62e0d38017c2d35bf73c119 - md5: 65d1906712b85d1679263c518d011b5b - depends: - - __osx >=11.0 - - ca-certificates - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 3109132 - timestamp: 1785913735357 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.2-h75dedd0_0.conda - sha256: a23f3a88a6b16363bd13f964b4abd12be1576abac460126f3269cbed12d04840 - md5: 9c89e09cede143716b479c5eacc924fb - depends: - - __osx >=11.0 - - libcxx >=16 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.1,<1.3.0a0 - - tzdata - - zstd >=1.5.6,<1.6.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] run_exports: weak: - - orc >=2.0.2,<2.0.3.0a0 - size: 436164 - timestamp: 1723760750932 + - openssl >=3.6.4,<4.0a0 + size: 3110142 + timestamp: 1787698648639 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda sha256: 11b8e71ecdf9d50c9c5249e009b9f7d13d6df68091bd70fe202f3c3c16d0e3d8 md5: d5626f645bea2fb646e12910c181fc79 @@ -19293,125 +12380,11 @@ packages: - libabseil * cxx17* license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 size: 513679 timestamp: 1784301269750 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda - sha256: 7202013525593f57a452dac7e5fee9f26478822be3ba5c893643517b8627406d - md5: 4581a32b837950217327fcab93214313 - depends: - - python - - numpy >=1.26.0 - - python-dateutil >=2.8.2 - - __osx >=11.0 - - python 3.12.* *_cpython - - libcxx >=19 - - python_abi 3.12.* *_cp312 - - numpy >=1.23,<3 - constrains: - - adbc-driver-postgresql >=1.2.0 - - adbc-driver-sqlite >=1.2.0 - - beautifulsoup4 >=4.12.3 - - blosc >=1.21.3 - - bottleneck >=1.4.2 - - fastparquet >=2024.11.0 - - fsspec >=2024.10.0 - - gcsfs >=2024.10.0 - - html5lib >=1.1 - - hypothesis >=6.116.0 - - jinja2 >=3.1.5 - - lxml >=5.3.0 - - matplotlib >=3.9.3 - - numba >=0.60.0 - - numexpr >=2.10.2 - - odfpy >=1.4.1 - - openpyxl >=3.1.5 - - psycopg2 >=2.9.10 - - pyarrow >=13.0.0 - - pyiceberg >=0.8.1 - - pymysql >=1.1.1 - - pyqt5 >=5.15.9 - - pyreadstat >=1.2.8 - - pytables >=3.10.1 - - pytest >=8.3.4 - - pytest-xdist >=3.6.1 - - python-calamine >=0.3.0 - - pytz >=2024.2 - - pyxlsb >=1.0.10 - - qtpy >=2.4.2 - - scipy >=1.14.1 - - s3fs >=2024.10.0 - - sqlalchemy >=2.0.36 - - tabulate >=0.9.0 - - xarray >=2024.10.0 - - xlrd >=2.0.1 - - xlsxwriter >=3.2.0 - - zstandard >=0.23.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping - run_exports: {} - size: 13926263 - timestamp: 1778602825408 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py314he609de1_0.conda - sha256: 90d84a2a6e7e9826f28f71ff34c7daacd0819c96eb3951f1ab59ef460a75fb58 - md5: 703276fc0e3693ff6a7566f1ac6865ab - depends: - - python - - numpy >=1.26.0 - - python-dateutil >=2.8.2 - - libcxx >=19 - - python 3.14.* *_cp314 - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - - numpy >=1.23,<3 - constrains: - - adbc-driver-postgresql >=1.2.0 - - adbc-driver-sqlite >=1.2.0 - - beautifulsoup4 >=4.12.3 - - blosc >=1.21.3 - - bottleneck >=1.4.2 - - fastparquet >=2024.11.0 - - fsspec >=2024.10.0 - - gcsfs >=2024.10.0 - - html5lib >=1.1 - - hypothesis >=6.116.0 - - jinja2 >=3.1.5 - - lxml >=5.3.0 - - matplotlib >=3.9.3 - - numba >=0.60.0 - - numexpr >=2.10.2 - - odfpy >=1.4.1 - - openpyxl >=3.1.5 - - psycopg2 >=2.9.10 - - pyarrow >=13.0.0 - - pyiceberg >=0.8.1 - - pymysql >=1.1.1 - - pyqt5 >=5.15.9 - - pyreadstat >=1.2.8 - - pytables >=3.10.1 - - pytest >=8.3.4 - - pytest-xdist >=3.6.1 - - python-calamine >=0.3.0 - - pytz >=2024.2 - - pyxlsb >=1.0.10 - - qtpy >=2.4.2 - - scipy >=1.14.1 - - s3fs >=2024.10.0 - - sqlalchemy >=2.0.36 - - tabulate >=0.9.0 - - xarray >=2024.10.0 - - xlrd >=2.0.1 - - xlsxwriter >=3.2.0 - - zstandard >=0.23.0 - license: BSD-3-Clause - license_family: BSD - size: 14368928 - timestamp: 1778602917992 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda sha256: 9db72f83b107fab7393e02c4005003c97c7a7aaffc33417e78aadb5a82056cd5 md5: b1dde16791eca59c8316d0ad1487bff0 @@ -19465,84 +12438,45 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 14402418 timestamp: 1784821943837 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.56.4-hf80efc4_1.conda - sha256: b57c59cf5abb06d407b3a79017b990ca5bfb10c15a10c62fc29e113f2b12d9a9 - md5: 4b433508ebb295c05dd3d03daf27f7bb +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda + sha256: c300fba11c4cd7cdd7609b6f165981af24d2181d40280ca6af420710c4c7d42e + md5: 83c3d3d895dd96f87b0a1916e2c41f70 depends: - __osx >=11.0 - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.17.1,<3.0a0 + - fontconfig >=2.18.2,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - - harfbuzz >=13.2.1 - - libexpat >=2.7.4,<3.0a0 - - libfreetype >=2.14.2 - - libfreetype6 >=2.14.2 - - libglib >=2.86.4,<3.0a0 - - libpng >=1.6.55,<1.7.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later - size: 425743 - timestamp: 1774282709773 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-ha881caa_2.conda - sha256: 797411a2d748c11374b84329002f3c65db032cbf012b20d9b14dba9b6ac52d06 - md5: 1a3f7708de0b393e6665c9f7494b055e - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - pcre2 >=10.44,<10.45.0a0 - size: 621564 - timestamp: 1745931340774 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - sha256: 5e2e443f796f2fd92adf7978286a525fb768c34e12b1ee9ded4000a41b2894ba - md5: 9b4190c4055435ca3502070186eba53a + - pango >=1.58.2,<2.0a0 + size: 444011 + timestamp: 1786108209790 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + sha256: f8c415329b542e1fca1ff2917b80e687c18302dfa0353530668b90cb225b494f + md5: 5ab90033816cbe4097e8a297e5179f67 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 - size: 850231 - timestamp: 1763655726735 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda - sha256: f7ee5d45bf16184d2b53f0d35c98c06e4e82e21688ce93e52b55c02ec7153bf3 - md5: 0634560e556adb3e7924668e49ad53fc - depends: - - python - - __osx >=11.0 - - python 3.12.* *_cpython - - libxcb >=1.17.0,<2.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libjpeg-turbo >=3.1.2,<4.0a0 - - zlib-ng >=2.3.3,<2.4.0a0 - - tk >=8.6.13,<8.7.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - lcms2 >=2.18,<3.0a0 - - python_abi 3.12.* *_cp312 - - openjpeg >=2.5.4,<3.0a0 - - libwebp-base >=1.6.0,<2.0a0 - license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping - run_exports: {} - size: 965082 - timestamp: 1775060469004 + size: 851704 + timestamp: 1787294559157 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda sha256: dd7303ea135116cc1182c109825459a9eeb77d244d3899f03dd8d83a8db956f9 md5: 4f25498ea1962ab24097fed8700e91ae @@ -19562,8 +12496,6 @@ packages: - tk >=8.6.13,<8.7.0a0 - lcms2 >=2.19.1,<3.0a0 license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 1019767 timestamp: 1782912213683 @@ -19575,122 +12507,31 @@ packages: - libcxx >=19 license: MIT license_family: MIT - purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 size: 198717 timestamp: 1786106922508 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda - sha256: 29c9b08a9b8b7810f9d4f159aecfd205fce051633169040005c0b7efad4bc718 - md5: 17c3d745db6ea72ae2fce17e7338547f - depends: - - __osx >=11.0 - - libcxx >=19 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - pixman >=0.46.4,<1.0a0 - size: 248045 - timestamp: 1754665282033 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/poppler-24.08.0-h37b219d_1.conda - sha256: a6b5abfcb9b44049f80e85d91fd1de2cfb2c18c9831c8f9efef9923bcac6051d - md5: 7926153cd183b32ba82966ab548611ab - depends: - - __osx >=11.0 - - cairo >=1.18.0,<2.0a0 - - fontconfig >=2.14.2,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - lcms2 >=2.16,<3.0a0 - - libcurl >=8.9.1,<9.0a0 - - libcxx >=17 - - libglib >=2.80.3,<3.0a0 - - libiconv >=1.17,<2.0a0 - - libintl >=0.22.5,<1.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - nspr >=4.35,<5.0a0 - - nss >=3.103,<4.0a0 - - openjpeg >=2.5.2,<3.0a0 - - poppler-data - license: GPL-2.0-only - license_family: GPL - purls: [] - run_exports: {} - size: 1514138 - timestamp: 1724660307740 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/postgresql-17.2-h393ceee_0.conda - sha256: 0ec1eda2b5611887d00a819bab94559004182a6bd16f42369db1b23fff1e85ee - md5: 75be3b77c1bc7ef0bedaee64cb60bbcb - depends: - - __osx >=11.0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libpq 17.2 h9b1ab17_0 - - libxml2 >=2.13.5,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openldap >=2.6.8,<2.7.0a0 - - openssl >=3.4.0,<4.0a0 - - readline >=8.2,<9.0a0 - - tzcode - - tzdata - - zstd >=1.5.6,<1.6.0a0 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 4583025 - timestamp: 1732205016578 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.4.1-hfb94cee_1.conda - sha256: 41da87faa84f90861637542cd237f3b430c87af79520866ebbfc95cf75cde92a - md5: 786c3dc1fbc9ca08b82002ab69353c53 - depends: - - __osx >=11.0 - - libcurl >=8.9.0,<9.0a0 - - libcxx >=16 - - libsqlite >=3.46.0,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - sqlite - constrains: - - proj4 ==999999999999 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - proj >=9.4.1,<9.5.0a0 - size: 2708661 - timestamp: 1722327880161 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-ha88f16d_0.conda - sha256: f5818ac1741aa91b43185989b35319e6dd819c7beab271fc2bb7fda4be089128 - md5: 1d135fa1ea825987507d1e14e4187b1e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda + sha256: 3e4f4d51d9efcf97f7bed432e8fa0009a96c13b320fafb1b0113a19e7aec05a9 + md5: 816e590c5cd37b19c1e5932d8052acc9 depends: - sqlite - libtiff - libcurl - __osx >=11.0 - - libcxx >=19 - - libsqlite >=3.53.0,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libcurl >=8.19.0,<9.0a0 + - libcxx >=21 + - libsqlite >=3.53.4,<4.0a0 + - libcurl >=8.21.0,<9.0a0 + - libtiff >=4.7.2,<4.8.0a0 constrains: - proj4 ==999999999999 license: MIT - license_family: MIT - purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 - size: 3146690 - timestamp: 1775840276015 + size: 3150271 + timestamp: 1787904976247 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda sha256: 851a77ae1a8e90db9b9f3c4466abea7afb52713c3d98ceb0d37ba6ff27df2eff md5: 7172339b49c94275ba42fec3eaeda34f @@ -19702,42 +12543,23 @@ packages: - zlib license: MIT license_family: MIT - purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 173220 timestamp: 1730769371051 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda - sha256: 6d0e21c76436374635c074208cfeee62a94d3c37d0527ad67fd8a7615e546a05 - md5: fd856899666759403b3c16dcba2f56ff +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda + sha256: 1cb01e783a9bcc350ecc1d80cbd207c0b0e1f60dc0db6ded8650e318796ba4bf + md5: bea6a028c9205ea3789bb38409cbfe43 depends: - python - __osx >=11.0 - - python 3.12.* *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping - run_exports: {} - size: 239031 - timestamp: 1769678393511 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda - sha256: e0f31c053eb11803d63860c213b2b1b57db36734f5f84a3833606f7c91fedff9 - md5: fc4c7ab223873eee32080d51600ce7e7 - depends: - - python - - __osx >=11.0 - - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping run_exports: {} - size: 245502 - timestamp: 1769678303655 + size: 242631 + timestamp: 1787417424394 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda sha256: 1c2338b9b0486af86883b9593d2f3e2845bbf216ea453dfe5d9a228e8dbe4057 md5: d4852e6054b74645cf49ca10f6349191 @@ -19745,21 +12567,9 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 9238 timestamp: 1786068031100 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 - md5: 415816daf82e0b23a736a069a75e9da7 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 8381 - timestamp: 1726802424786 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda sha256: fad9646989fb16b3f5f858ffd81dbac5d9f5e51e98b829fa19cfb5897fbc66db md5: 568c38aba1768777e2e92bfd1a3e94f2 @@ -19771,45 +12581,25 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 229818 timestamp: 1757853438761 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hbaa5893_1.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hd89b7e0_3.conda noarch: python - sha256: 29086f8a5a51a03f347a67372257f2095e9cd341c38187646eeca897d7262714 - md5: ae2a040d54c01f2469945011243f6d13 + sha256: a0fc7ff4902bf882b14c6c56c4d58365a6872e330fc34704da7fa6cc992985c8 + md5: c32ebe551aa8bac30b41c3717a15e1d6 depends: - python - __osx >=11.0 + - openssl >=3.5.8,<4.0a0 - _python_abi3_support 1.* - cpython >=3.10 - - openssl >=3.5.6,<4.0a0 constrains: - __osx >=11.0 license: BSD-3-Clause - license_family: BSD - size: 10707077 - timestamp: 1781020721263 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda - sha256: d6433c343120e723cad92b52ea05c16e05096845489275a697201ce0a50fc568 - md5: 04a90c4ce691f2e289658dd475f69631 - depends: - - libarrow-acero 17.0.0.* - - libarrow-dataset 17.0.0.* - - libarrow-substrait 17.0.0.* - - libparquet 17.0.0.* - - numpy >=1.19,<3 - - pyarrow-core 17.0.0 *_2_* - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - purls: [] run_exports: {} - size: 25811 - timestamp: 1730169125041 + size: 10601531 + timestamp: 1787913213404 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda sha256: cd50941d2b035acef8f54088108d464bfa1845c45b8ba63270f7cee70801157b md5: 1a1b2c10602cd3dd7241e485e71d02a2 @@ -19823,32 +12613,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 25915 timestamp: 1784258318884 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-17.0.0-py312hc40f475_2_cpu.conda - build_number: 2 - sha256: 708488e602a159fa38a7fd5fa4466e9f093761dc4a8538661f06be3df42f30a5 - md5: bc617fed2854d65a16760d2bf02a475c - depends: - - __osx >=11.0 - - libarrow 17.0.0.* *cpu - - libcxx >=18 - - libzlib >=1.3.1,<2.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - constrains: - - apache-arrow-proc =*=cpu - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping - run_exports: {} - size: 3990396 - timestamp: 1730169098217 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda sha256: d205d9efa4ee35e7cfe5605c32354c010b1f3f1956cb85442f659bdcc32da8a5 md5: d617d0524dd584ed2b4dba355cc3fc52 @@ -19865,186 +12632,70 @@ packages: - numpy >=1.23,<3 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 4030783 timestamp: 1784258302127 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hb84d1df_3.conda - sha256: ec919254bca5ba7ef89ac1284582aa91297b89c3b640feda05a3cab0a0402db2 - md5: b23019984b3399dada68581c20e6ef85 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda + sha256: 640d2d29a18e3a8dda02427616bd55af5670565fd780f7b5df4c045548471909 + md5: 99e06db67584235df895feda84a4790e depends: - __osx >=11.0 - - python >=3.14.0rc2,<3.15.0a0 - - python >=3.14.0rc2,<3.15.0a0 *_cp314 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 92986 - timestamp: 1757744788529 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda - sha256: 480b10f3197270a98c2b564670ca6e201bc57b5005e9d58fd0d232338fd33ad7 - md5: e38d54c5e4318faa79f71b713b059566 - depends: - - python - - typing-extensions >=4.6.0,!=4.7.0 - - python 3.12.* *_cpython - - __osx >=11.0 - - python_abi 3.12.* *_cp312 - constrains: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} - size: 1720774 - timestamp: 1778084314791 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py314h54f3292_0.conda - sha256: c034a7cc16f279c260d3456fcfc625838495a7f9f55aa79408a629a427769bb2 - md5: 16314d88257820013e698381af19153f + size: 93498 + timestamp: 1784146274338 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda + sha256: 60a639a7bd3cde997574e0f41af74c76fe37056c42b94fe1bf61a4e1b376942b + md5: 586050b385a407a449fd7e5e4a5c8fef depends: - python - typing-extensions >=4.6.0,!=4.7.0 - __osx >=11.0 - - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 constrains: - __osx >=11.0 license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping - run_exports: {} - size: 1722694 - timestamp: 1778084354332 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.10.0-py312hf9e36c7_0.conda - sha256: 632019ab944cc1d9989fea02386c92132c3b9e7b0786ffc4516e3140ca3898ca - md5: 3955ea435842be121db2da0762accb76 - depends: - - __osx >=11.0 - - libcxx >=17 - - libgdal-core >=3.9.2,<3.10.0a0 - - numpy - - packaging - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping - run_exports: {} - size: 562964 - timestamp: 1727771822945 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - sha256: dcaaab4d8b539f7c4ee740e0242ae09c48f68e75949476ca36d9c67e61aafc3b - md5: 9b33fa020bd4da86a2dddfd0f63a43ba - depends: - - __osx >=11.0 - - libcxx >=19 - - libgdal-core >=3.12.0,<3.13.0a0 - - numpy - - packaging - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping - run_exports: {} - size: 599877 - timestamp: 1764402722213 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.6.1-py312hc2894cb_9.conda - sha256: 642f46f2b3682b2327e1f05bdcdf0f25a57069d1d43525f62c62cbf83996faf5 - md5: 1b6c7a6ae3d2ed8fc284c0015d2679aa - depends: - - __osx >=11.0 - - certifi - - proj >=9.4.1,<9.5.0a0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyproj?source=hash-mapping - run_exports: {} - size: 485985 - timestamp: 1725436209323 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314ha82c7e6_5.conda - sha256: a05c48253c8f1fcf377be85859f411d0a9d811b9b08da093c92952e95f92fd33 - md5: 8ad655a1c8ac08ae0465ccf217745f76 - depends: - - python - - proj - - certifi - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - - proj >=9.8.1,<9.9.0a0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} - size: 533238 - timestamp: 1786351900726 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a - md5: 8e7608172fa4d1b90de9a745c2fd2b81 - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.7.4,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - liblzma >=5.8.2,<6.0a0 - - libsqlite >=3.51.2,<4.0a0 - - libzlib >=1.3.1,<2.0a0 - - ncurses >=6.5,<7.0a0 - - openssl >=3.5.5,<4.0a0 - - readline >=8.3,<9.0a0 - - tk >=8.6.13,<8.7.0a0 - - tzdata - constrains: - - python_abi 3.12.* *_cp312 - license: Python-2.0 - purls: [] - run_exports: - weak: - - python_abi 3.12.* *_cp312 - noarch: - - python - size: 12127424 - timestamp: 1772730755512 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-h156bc91_100_cp314.conda - build_number: 100 - sha256: 984081c9fae3a3944c6f2707bbbbc70e8b961f02cdb7c640d9745e2636235632 - md5: 4841be3d0cf616a860efc6e60af66f8b + size: 1691220 + timestamp: 1787928985509 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda + sha256: dcaaab4d8b539f7c4ee740e0242ae09c48f68e75949476ca36d9c67e61aafc3b + md5: 9b33fa020bd4da86a2dddfd0f63a43ba depends: - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - liblzma >=5.8.3,<6.0a0 - - libmpdec >=4.0.0,<5.0a0 - - libsqlite >=3.53.2,<4.0a0 - - libzlib >=1.3.2,<2.0a0 - - ncurses >=6.6,<7.0a0 - - openssl >=3.5.7,<4.0a0 + - libcxx >=19 + - libgdal-core >=3.12.0,<3.13.0a0 + - numpy + - packaging + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 - - readline >=8.3,<9.0a0 - - tk >=8.6.13,<8.7.0a0 - - tzdata - - zstd >=1.5.7,<1.6.0a0 - license: Python-2.0 - size: 14059371 - timestamp: 1781254578985 - python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.6-hf4d206d_102_cp314.conda - build_number: 102 - sha256: 9767b5eee5cef50716708787bb3b4225d2a974bcd50653e856f9db5910a4b17e - md5: 8da4ea285021110e2617f7538c386afc + license: MIT + license_family: MIT + run_exports: {} + size: 599877 + timestamp: 1764402722213 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda + sha256: 98ac80a1357dd67d9e3ba4b9957020e4c30643dd5e8dcfeccdd9a77a1efbc9e8 + md5: 3bdcaa1d81e1cc32648f6a22517f92e3 + depends: + - python + - proj + - certifi + - __osx >=11.0 + - proj >=9.8.1,<9.9.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + run_exports: {} + size: 518157 + timestamp: 1787944739017 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda + build_number: 104 + sha256: 90df7fc0a043b2888cce45361c55473ce37cb57ec5f8e1bc301d981b5ba8087a + md5: 86c5773c0f675287d7852068554806c9 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 @@ -20052,37 +12703,38 @@ packages: - libffi >=3.7.0,<3.8.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.7 h4311e70_104_cp314 - libsqlite >=3.53.4,<4.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 - purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 noarch: - python - size: 13960847 - timestamp: 1786444540722 + size: 12357045 + timestamp: 1788160826259 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.11.0-py314ha14b1ff_0.conda - sha256: 483c34d3224b1d8209206d5eba7b0f36f33a891908eec46d12a7a621d0a39001 - md5: 68be11fb4ea06efd0e13db21921afe56 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda + sha256: 07e71e940c1268e224c9a6a1f96480c10789ca521928cda30c20aa07f19aca21 + md5: e82ce608933c6feb7fc9ef479d2a2227 depends: - python - - __osx >=11.0 - python 3.14.* *_cp314 + - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 129714 - timestamp: 1778511905677 + run_exports: {} + size: 134968 + timestamp: 1786328898613 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda sha256: 37d76254955eaa47df534ae8f3890c45f28aa4a8479597ff6fbacbd5a23810e6 md5: 45bfaa1961db6ace6fc35561ba893fae @@ -20093,27 +12745,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 175381 timestamp: 1778689065100 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda - sha256: 737959262d03c9c305618f2d48c7f1691fb996f14ae420bfd05932635c99f873 - md5: 95a5f0831b5e0b1075bbd80fcffc52ac - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - - yaml >=0.2.5,<0.3.0a0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping - run_exports: {} - size: 187278 - timestamp: 1770223990452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda sha256: 95f385f9606e30137cf0b5295f63855fd22223a4cf024d306cf9098ea1c4a252 md5: dcf51e564317816cb8d546891019b3ab @@ -20125,29 +12759,25 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 189475 timestamp: 1770223788648 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_3.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.2.0-py312hcedbef1_0.conda noarch: python - sha256: 086cc67ec57afb7c9c09b5e09e7356b536b5b1af6c2e97117dc022cd22f0d472 - md5: 73f22bde4991f30ae2bfac3811577c15 + sha256: de17d16785e44ea075ba0912ad4618db3438a3cf79c13249b1646ac4e616ea35 + md5: 52d1e1fe1463e21e34be9b8c2bf6c96c depends: - python - - libcxx >=19 - __osx >=11.0 - - zeromq >=4.3.5,<4.4.0a0 + - libcxx >=21 - _python_abi3_support 1.* - cpython >=3.12 + - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} - size: 191432 - timestamp: 1779484184540 + size: 195379 + timestamp: 1787301084171 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda sha256: 873ac689484262a51fd79bc6103c1a1bedbf524924d7f0088fb80703042805e4 md5: 6483b1f59526e05d7d894e466b5b6924 @@ -20155,40 +12785,11 @@ packages: - __osx >=11.0 - libcxx >=16 license: LicenseRef-Qhull - purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 size: 516376 timestamp: 1720814307311 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.3.11-py312h9302994_1.conda - sha256: 0e38a096d50297f12993b0f95d9e0d590e738820438ce563a651c5bc01ab16f1 - md5: 3fb5410bcce1f85eb8de9d9198c739bd - depends: - - __osx >=11.0 - - affine - - attrs - - certifi - - click >=4 - - click-plugins - - cligj >=0.5 - - libcxx >=17 - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - numpy >=1.19,<3 - - proj >=9.4.1,<9.5.0a0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - - setuptools >=0.9.8 - - snuggs >=1.4.1 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping - run_exports: {} - size: 7233883 - timestamp: 1726176805073 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda sha256: 10737f408812344475097991cf313492be966f477ff6553bfc81e7d182f3f705 md5: d2ca5fbd28cd1fd50b2dabf9ffc96df0 @@ -20211,25 +12812,9 @@ packages: - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7369572 timestamp: 1785965949045 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda - sha256: 0e0d44414381c39a7e6f3da442cb41c637df0dcb383a07425f19c19ccffa0118 - md5: 0342882197116478a42fa4ea35af79c1 - depends: - - libre2-11 2023.09.01 h7b2c953_2 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - - re2 - size: 26770 - timestamp: 1708947220914 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda sha256: ba1154085d70e8e8f94cfc38a018a13d1a734ff1bcc2ab384e4fc3e532b23066 md5: a2578dc92c16ab5b6b183cfcdc8e8b79 @@ -20237,60 +12822,50 @@ packages: - libre2-11 2025.11.05 h5d15848_2 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 - re2 size: 27372 timestamp: 1781312662146 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 - md5: f8381319127120ce51e081dce4865cf4 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + sha256: d6782b430e6dce4fe8c7ac118cd988d5a193eb4a7f60741edfaede58016b6110 + md5: 9347fd56a002e6b58946831d48fe62f6 depends: - __osx >=11.0 - - ncurses >=6.5,<7.0a0 + - ncurses >=6.6,<7.0a0 license: GPL-3.0-only license_family: GPL - purls: [] run_exports: weak: - readline >=8.3,<9.0a0 - size: 313930 - timestamp: 1765813902568 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.7.post0-h84a0fba_1.conda - sha256: f832ce7f2b3f6067ac2e086bff011d487fc2b18c3d2e2f9c5c2525f3ff21bff4 - md5: 5a2e62653a06ab1b810e50bb02dae288 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - size: 33488 - timestamp: 1779092919587 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.7.post0-hf6b4638_1.conda - sha256: f9710955247cd5890797a27db53474d682cd815f8d0c02795e60881d8f9791be - md5: 9cbb9e0c4f09302ddd975887da013015 + size: 314395 + timestamp: 1787033878899 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.8.post0-hdca3b7d_0.conda + sha256: f5198a70187913b2d801092f844b27ed58f1c519f293442e006a03a4d59d8feb + md5: 85d3473e23745290824a52cad54edfed depends: - __osx >=11.0 - - libcxx >=19 - - reproc 14.2.7.post0 h84a0fba_1 license: MIT - license_family: MIT - size: 26223 - timestamp: 1779092975268 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.5.1-py314he1d1ac0_0.conda - sha256: 764b78892f8ba2a7d5cb24c2911bd5718753ba5b130bf4e0d9a0bb0001c139b1 - md5: 58fe40c3bba570ac2cbfac4f4ea983b7 + run_exports: + weak: + - reproc >=14.2.8.post0,<14.3.0a0 + size: 37211 + timestamp: 1788160507406 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.8.post0-h2fd3d4c_0.conda + sha256: f70bf33f32023195071a1bc3450bd0b1621d90a4096970fab20c7874c806c633 + md5: 7b59ae73b965a752c487ae12812d27eb depends: - - python - - __osx >=11.0 - - python_abi 3.14.* *_cp314 - constrains: + - reproc ==14.2.8.post0 hdca3b7d_0 - __osx >=11.0 + - libcxx >=21 + - reproc >=14.2.8.post0,<14.3.0a0 license: MIT - license_family: MIT - size: 292087 - timestamp: 1779977082395 + run_exports: + weak: + - reproc-cpp >=14.2.8.post0,<14.3.0a0 + size: 29958 + timestamp: 1788160507406 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda sha256: 7d4b49a7542735c63f0ef3379f83dfed1870a5d62ae4c3e357f859014fb1fcaf md5: aa2a7e9c0ab5322b9bd6e75a384b48a1 @@ -20302,8 +12877,6 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: - - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} size: 284937 timestamp: 1787344325551 @@ -20318,39 +12891,34 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 311922 timestamp: 1766175797575 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314ha14b1ff_1.conda - sha256: ad575cae7f662b2dafca88c4ce05120e322f825c0610e54b0a116550c817bbbe - md5: 5836fbf79e5f279ffbe4ba06066c51a3 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_2.conda + sha256: 8ab180c1bbe93b952c9948270e25c3f38ad9229e6a7f751084cea0f6ecf150f8 + md5: 6ed337ed07a1c97fac976e54f506e3c4 depends: - python - - python 3.14.* *_cp314 - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} - size: 133016 - timestamp: 1766159585543 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.15.19-h80928e0_0.conda + size: 121129 + timestamp: 1787264332077 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.5-h4e39fff_0.conda noarch: python - sha256: 7916eb37206fe9726db28f722f14fe2da5e64b68b3747ae5bf92a9df6245c79f - md5: 2639d46decb4c0ef9eca4f8fd2f5803e + sha256: 7770d275e7bab61c9f33dd646551975e546c8df5fa215691133e13207814f535 + md5: c736cce00267b24375ea10abbe2fedc7 depends: - python - __osx >=11.0 constrains: - __osx >=11.0 license: MIT - license_family: MIT - size: 8644257 - timestamp: 1782288982578 + run_exports: {} + size: 8177581 + timestamp: 1787865392183 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda sha256: 47ec36bdd05117c974853a8dbaa9d89605a5f70b592d47c7d198b20252ddf1f4 md5: 94b3b302cd6c07c5ec68648b25c8c525 @@ -20359,35 +12927,11 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - s2n >=1.7.5,<1.7.6.0a0 size: 276705 timestamp: 1783165153651 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda - sha256: e140dce2c42d0a121a113e29fbde4fd95a69cdf528ea195558ba25b809b439b7 - md5: b2b777cdad320a08da92bb1f58040bfd - depends: - - python - - numpy >=1.24.1 - - scipy >=1.10.0 - - joblib >=1.4.0 - - threadpoolctl >=3.5.0 - - narwhals >=2.0.1 - - python 3.12.* *_cpython - - llvm-openmp >=19.1.7 - - libcxx >=19 - - __osx >=11.0 - - numpy >=1.23,<3 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping - run_exports: {} - size: 9400478 - timestamp: 1780401354221 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda sha256: c5dc417c26c46eecf7e8931c53a4c18bcd2c274c994ee80bae4767baeed4807c md5: 72cd17b6f8016221faaa96123711f8c9 @@ -20406,34 +12950,9 @@ packages: - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9667030 timestamp: 1780401292916 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda - sha256: c0ed2cbfa3485bac8570e52b577475778c603ee92d078a8b164d1ddec992e577 - md5: 173d5eeba324363d9171946e86a81687 - depends: - - __osx >=11.0 - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - libcxx >=19 - - libgfortran - - libgfortran5 >=14.3.0 - - liblapack >=3.9.0,<4.0a0 - - numpy <2.7 - - numpy >=1.23,<3 - - numpy >=1.25.2 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping - run_exports: {} - size: 13936510 - timestamp: 1779874824714 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda sha256: 7ce218a4e1c55775547835d21a7ead0d50e5ac348fd638ce6ba316c48c8547b7 md5: e55fe08bb5d43e7120672338dd129030 @@ -20452,28 +12971,9 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 14122215 timestamp: 1781912992503 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.0.6-py312ha6b2466_1.conda - sha256: ecba27a5a5fa5c708bb33516c580db609978550235668226c4096224db769c9d - md5: 946eaf02209d9b1047d9cffb3167ce78 - depends: - - __osx >=11.0 - - geos >=3.12.2,<3.12.3.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping - run_exports: {} - size: 531604 - timestamp: 1725394221848 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda sha256: 3d6f64391563dbe47f2e795ce99b2389c84c695df12170e0a1743b10963ebce7 md5: 947d1f4e3160c83140a9c8bcb046fdac @@ -20486,39 +12986,25 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 618928 timestamp: 1762524178138 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.4-h4ddebb9_0.conda - sha256: 3437bdc98b7a506bbf8207df8ed5f6f991c93e74b4eba635e8dfb681249e2225 - md5: 14ac38259fb96102bc9d17ac9a94e887 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.9-hfbe1efa_0.conda + sha256: 3f2e7a3d3fe479c4216a588202effbcc5496a41f6ae42ed294fb21513254cfc8 + md5: 1df792a337dc7d1a940bb32083d63210 depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 license: Apache-2.0 license_family: APACHE - size: 275639 - timestamp: 1778109594368 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py312h2bbb03f_0.conda - sha256: 24e5aeb84a6c58f4fb5a0c9b6966106f49af85d62c1b5789d94be00c24b16d6b - md5: 85801137e6f7634e0fa9cd7a8070c2d5 - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping - run_exports: {} - size: 160192 - timestamp: 1777112397342 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py314h6c2aa35_0.conda - sha256: 972c363d7df721f529e2a353f4acb7b3ed1498886700a3e1378a60fec38ee951 - md5: f96c48a75942b72af1246d789fa8ba29 + run_exports: + weak: + - simdjson >=4.6.9,<4.7.0a0 + size: 311606 + timestamp: 1787803743194 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda + sha256: cc5a4251cb21aa940082f85d4359f591e2e0cc854106ac3d63002130eb7e58f8 + md5: c2ca364702b1313c4f42aa39125724e4 depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 @@ -20526,11 +13012,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping run_exports: {} - size: 163732 - timestamp: 1777112456003 + size: 167699 + timestamp: 1787824816800 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d md5: fca4a2222994acd7f691e57f94b750c5 @@ -20539,172 +13023,67 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 size: 38883 timestamp: 1762948066818 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.14.1-h6d8af72_1.conda - sha256: f981d4f3555125cb913be49397892f43c6b747705c0d72cba3676f7d98709f92 - md5: 4af518b01539da8e4af17aee5fb92639 - depends: - - __osx >=11.0 - - fmt >=11.0.1,<11.1a0 - - libcxx >=16 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.14.1,<1.15.0a0 - size: 164011 - timestamp: 1722238482313 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-ha0f8610_1.conda - sha256: 465e81abc0e662937046a2c6318d1a9e74baee0addd51234d36e08bae6811296 - md5: 1885f7cface8cd627774407eeacb2caf +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda + sha256: 791d91a18647346c4318a4177add14abc546632fc2bf12a049cc328025368054 + md5: e4df5c4de3a06437b69ff6255c258ca2 depends: - __osx >=11.0 - fmt >=12.1.0,<12.2.0a0 - libcxx >=19 license: MIT license_family: MIT - size: 166603 - timestamp: 1767781942683 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.51-py314ha14b1ff_0.conda - sha256: c355708c29086c3ce0916f5d5367cfea733f73d9c194b4fa5d21a40317374d32 - md5: 540fe3cb235eb16dcf0ff5e13eb96411 + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 167030 + timestamp: 1785925098650 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda + sha256: 9c86803397cf137bf92d517b6a4395d059cfb3dbfc1def708ed5963c5848db48 + md5: 61db956083da31d178a6940043d176d2 depends: - python - greenlet !=0.4.17 - typing-extensions >=4.6.0 - __osx >=11.0 - - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 4036851 - timestamp: 1781547978515 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda - sha256: 95482d44bef095da3c63be6e0c66a2e5b66a2a551e11d4c40949c075df13ec2c - md5: b9df2fcb7e9eba945c1c946c438c4586 - depends: - - __osx >=11.0 - - libsqlite 3.53.3 h1b79a29_0 - - libzlib >=1.3.2,<2.0a0 - - ncurses >=6.6,<7.0a0 - - readline >=8.3,<9.0a0 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.3,<4.0a0 - size: 183088 - timestamp: 1782519149990 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-h77b7338_0.conda - sha256: fffebc6bbe45dd4462d3a701a0426c224ad75f8d8bcd174860d7f1b277d4ef45 - md5: 5b4d5422b8e681ce2619d0cdfe6ed161 + run_exports: {} + size: 4044830 + timestamp: 1786535242452 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda + sha256: 4e0c8704a29b6c265e7bdb6508e1d4180570adcc6e031daf182578d3777fb8b1 + md5: c144cc02c82671616e0fb4caf731bc88 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - - libsqlite 3.53.4 h1ae2325_0 + - libsqlite 3.53.4 hca69786_1 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing - purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 - size: 183124 - timestamp: 1785016142653 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tiledb-2.26.0-h3c94177_0.conda - sha256: 02c930cfec6a9b4a9a3837a1e5e723d1dc163cb9c60f186f6817c87ee028ad62 - md5: ad1a0bc095a4e0d1d15eafd888c119b8 - depends: - - __osx >=11.0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - bzip2 >=1.0.8,<2.0a0 - - fmt >=11.0.2,<11.1a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcurl >=8.9.1,<9.0a0 - - libcxx >=17 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libwebp-base >=1.4.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - spdlog >=1.14.1,<1.15.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - tiledb >=2.26.0,<2.27.0a0 - size: 3588995 - timestamp: 1726059138173 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - sha256: 799cab4b6cde62f91f750149995d149bc9db525ec12595e8a1d91b9317f038b3 - md5: a9d86bc62f39b94c4661716624eb21b0 - depends: - - __osx >=11.0 - - libzlib >=1.3.1,<2.0a0 - license: TCL - license_family: BSD - purls: [] - run_exports: - weak: - - tk >=8.6.13,<8.7.0a0 - size: 3127137 - timestamp: 1769460817696 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda - sha256: 47186bc7ab8d7e8bee86bbd1a917196f8c21cf63f081fc33cd6d1221af087580 - md5: 8e3cf0e455e6b54519f0b1c72c61780a + size: 179903 + timestamp: 1787051253247 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + sha256: 857d89087ae7f2c328cf256728affcb7343031a148b4af847334d248a9cf564c + md5: 90a01bf394d1c594e0eb9258f967d828 depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: TCL - purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 - size: 3338712 - timestamp: 1784229090530 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda - sha256: 483350b0e4a3ebec90f6418e454d22b453f54d1bac803c2aaa7a9035cfcd7493 - md5: d037e9adb0365ab53445f357bd9a035f - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/tornado?source=hash-mapping - run_exports: {} - size: 863360 - timestamp: 1781007464047 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py314h6c2aa35_0.conda - sha256: 1a4f3d940cf239acde2fc61674b7cf80219a7f22a369e92b95b245be2d47caf1 - md5: cc1d84777343f00f01c08a2d9550f639 - depends: - - __osx >=11.0 - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: Apache - size: 915857 - timestamp: 1781007345425 + size: 3342183 + timestamp: 1787272852357 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda sha256: 4af8e4394d249619b4f1641c82fd6c0a8b97d51991ca2986095bed400c2e5eb9 md5: ab1bd70ec9c95d199f8ce456823004ab @@ -20715,37 +13094,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 922067 timestamp: 1786227125229 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tzcode-2026b-h1a92334_0.conda - sha256: 9b81a80ffad060a6120ae7acf85c6d36e5bd988c286a4cd7ca523767d735e94e - md5: 0b3d2910b2d5ac37f2c465fc59313b5d - depends: - - __osx >=11.0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 66995 - timestamp: 1776960443135 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda - sha256: e935d0c11581e31e89ce4899a28b16f924d1a3c1af89f18f8a2c5f5728b3107f - md5: 45b836f333fd3e282c16fff7dc82994e - depends: - - __osx >=11.0 - - python >=3.12,<3.13.0a0 - - python >=3.12,<3.13.0a0 *_cpython - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping - run_exports: {} - size: 415828 - timestamp: 1770909782683 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda sha256: 09bfbee5a2bcf4df06f21a2aa9eb40a7af97864a569beb5ea85fd6baf6e03ce7 md5: 4fffb3ba871bb05f34ffb705534dfef5 @@ -20756,8 +13107,6 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 416130 timestamp: 1770909728445 @@ -20769,7 +13118,6 @@ packages: - libcxx >=16 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 @@ -20785,41 +13133,23 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 61800 timestamp: 1756851815321 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.2.5-h92fc2f4_2.conda - sha256: 863a7c2a991a4399d362d42c285ebc20748a4ea417647ebd3a171e2220c7457d - md5: 50b7325437ef0901fe25dc5c9e743b88 - depends: - - __osx >=11.0 - - icu >=75.1,<76.0a0 - - libcxx >=17 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - xerces-c >=3.2.5,<3.3.0a0 - size: 1277884 - timestamp: 1727733870250 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda - sha256: 89152175f45b5e84e0f1575848f607e305ffc122ab59d9704ea77ce699b1bd2b - md5: 0b886d06130b774f086d3b2ce0b7277a +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda + sha256: 17f4b089b64cb3cc194cf7d12e7381fcb24906c96301ce632d3d0409d0607a24 + md5: 1f38144f935e874fe7e296658484e719 depends: - __osx >=11.0 - - icu >=78.1,<79.0a0 - - libcxx >=19 + - icu >=78.3,<79.0a0 + - libcxx >=21 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 - size: 1283088 - timestamp: 1766327630028 + size: 1285603 + timestamp: 1788145085997 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda sha256: 5c5ea38ceec008e4ff40111fc166b04086fca310b0aa9e5bd46f65e6b0dcb71d md5: 31d71ce1b056f69b6ec67ee0712bdf97 @@ -20827,25 +13157,11 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 size: 15124 timestamp: 1786381585975 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda - sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f - md5: 78b548eed8227a689f93775d5d23ae09 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libxau >=1.0.12,<2.0a0 - size: 14105 - timestamp: 1762976976084 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda sha256: 2f6915f0897ace4a1b5d66d0463079f7bc9819b0048c03677e47764f0a743499 md5: f51e9414bf1b7bb556aac1a0b74a75fe @@ -20853,92 +13169,36 @@ packages: - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 size: 19486 timestamp: 1786381546642 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda - sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 - md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda + sha256: 3e78c43207502418fc5fb2317bbbefe5df8b6fc1051d90bdcd1c881c76bb4193 + md5: 31cf6dcc138abe673c9440cd6fe6c6fe depends: - __osx >=11.0 license: MIT license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libxdmcp >=1.1.5,<2.0a0 - size: 19156 - timestamp: 1762977035194 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.8.3-hd0f0c4f_0.conda - sha256: d5bb880876336f625523c022aa9bdc6be76a85df721d9e7b33c352f528619185 - md5: 4df12be699991b97b66a85cc46ea75b5 - depends: - - __osx >=11.0 - - liblzma 5.8.3 h8088a28_0 - - liblzma-devel 5.8.3 h8088a28_0 - - xz-gpl-tools 5.8.3 hd0f0c4f_0 - - xz-tools 5.8.3 h8088a28_0 - license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 24348 - timestamp: 1775825911109 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.8.3-hd0f0c4f_0.conda - sha256: 0864d53202f618b4c8a5f2c38b7029f14b900b852e99b6248813303f69ccfdee - md5: 43d168a8c95a8fcdcc44c2a0a7887653 - depends: - - __osx >=11.0 - - liblzma 5.8.3 h8088a28_0 - constrains: - - xz 5.8.3.* - license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later - purls: [] run_exports: weak: - - liblzma >=5.8.3,<6.0a0 - size: 34224 - timestamp: 1775825884830 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.8.3-h8088a28_0.conda - sha256: beb7fb34d5517cce06f5f89710de7108a8ca65ed9c0324269fc0446807bcbd91 - md5: b8c47eab4e58fe7015a12ceb9d5b114c - depends: - - __osx >=11.0 - - liblzma 5.8.3 h8088a28_0 - constrains: - - xz 5.8.3.* - license: 0BSD AND LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 85931 - timestamp: 1775825857949 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac - md5: 78a0fe9e9c50d2c381e8ee47e3ea437d + - yaml >=0.2.5,<0.3.0a0 + size: 77530 + timestamp: 1787228556857 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda + sha256: 450d4c6ad26bc2c18eb9420261568b86918b118c96ab4a7f2176c352b94101a2 + md5: 04b9b2b8cc9e14758965758d9c423b34 depends: + - libcxx >=19 - __osx >=11.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - yaml >=0.2.5,<0.3.0a0 - size: 83386 - timestamp: 1753484079473 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-ha1acc90_0.conda - sha256: 66ba31cfb8014fdd3456f2b3b394df123bbd05d95b75328b7c4131639e299749 - md5: 30475b3d0406587cf90386a283bb3cd0 - depends: - - libcxx >=18 - - __osx >=11.0 - license: MIT - license_family: MIT - size: 136222 - timestamp: 1745308075886 + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 136591 + timestamp: 1785924101870 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda sha256: 01fd50d2801b23b59fafea6bf704a6c5faf0f5969104400eae0e6572cb2e5304 md5: d31c0e54c4f9c51100ec8c812ee925d1 @@ -20949,26 +13209,11 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA - purls: [] - run_exports: - weak: - - zeromq >=4.3.5,<4.4.0a0 - size: 245404 - timestamp: 1779124076307 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda - sha256: 8dd2ac25f0ba714263aac5832d46985648f4bfb9b305b5021d702079badc08d2 - md5: f1c0bce276210bed45a04949cfe8dc20 - depends: - - __osx >=11.0 - - libzlib 1.3.2 h8088a28_2 - license: Zlib - license_family: Other - purls: [] run_exports: weak: - - libzlib >=1.3.2,<2.0a0 - size: 81123 - timestamp: 1774072974535 + - zeromq >=4.3.5,<4.4.0a0 + size: 245404 + timestamp: 1779124076307 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda sha256: ab46d85e4fcffff1b1c5cac517afa40b7ae784cf76fc9da1f55f6a6934291eb6 md5: 2c966485853aa27985fbec7e3fcc4e77 @@ -20977,76 +13222,51 @@ packages: - libzlib 1.3.2 h8088a28_3 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 size: 81744 timestamp: 1785277062753 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - sha256: a339606a6b224bb230ff3d711e801934f3b3844271df9720165e0353716580d4 - md5: d99c2a23a31b0172e90f456f580b695e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda + sha256: 489a29915a3e1b425cff4df10a448f55bbaaf29d777a0f8a9e381444e6a284f1 + md5: 4621ded2fd5b36f51454d5f81bff4ec1 depends: - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 license: Zlib license_family: Other - purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 - size: 94375 - timestamp: 1770168363685 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - sha256: af843b0fe62d128a70f91dc954b2cb692f349a237b461788bd25dd928d0d1ef8 - md5: 9300889791d4decceea3728ad3b423ec + size: 95857 + timestamp: 1786737243497 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314hd98292b_3.conda + sha256: e790bd7c15afa1d59d2d72b86825932338147cd7c832e7e54b8dc29adfb61be5 + md5: a8e20554ed5fbd1f81f1b098bfe03eb5 depends: - python - cffi >=1.11 - zstd >=1.5.7,<1.5.8.0a0 - - python 3.12.* *_cpython - __osx >=11.0 - - python_abi 3.12.* *_cp312 - zstd >=1.5.7,<1.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping - run_exports: {} - size: 390920 - timestamp: 1762512713481 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda - sha256: cdeb350914094e15ec6310f4699fa81120700ca7ab7162a6b3421f9ea9c690b4 - md5: 8a92a736ab23b4633ac49dcbfcc81e14 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - python 3.14.* *_cp314 - - __osx >=11.0 - python_abi 3.14.* *_cp314 - - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping run_exports: {} - size: 397786 - timestamp: 1762512730914 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - sha256: 9485ba49e8f47d2b597dd399e88f4802e100851b27c21d7525625b0b4025a5d9 - md5: ab136e4c34e97f34fb621d2592a393d8 + size: 383104 + timestamp: 1787896531255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + sha256: da867f5092eb0cb746d353694f0098031fd9817a4ce7d5743121209ae0f406ca + md5: 4ec2684c73812cc2c3d78379384a39cc depends: - __osx >=11.0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 - size: 433413 - timestamp: 1764777166076 + size: 433687 + timestamp: 1786599629846 - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 8a1cee28bd0ee7451ada1cd50b64720e57e17ff994fc62dd8329bef570d382e4 @@ -21059,16 +13279,15 @@ packages: - msys2-conda-epoch <0.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: strong: - _openmp_mutex >=4.5 size: 52252 timestamp: 1770943776666 -- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.5.0-py310ha413424_1.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda noarch: python - sha256: 6045fef43d0e5c9ce898dc89da2d4d5f81f5da5c10ca25fcc3592f663dbbec5d - md5: 3a08e9b5d3bbdb909799bb061cab5e67 + sha256: c52d4750dce807f24a4698fb3c8894b8c6e7cfca8000f5e4050a8e43d24eaf1f + md5: ecde03792069f1f16ff12c2395635035 depends: - python >=3.10 - vc >=14.3,<15 @@ -21078,8 +13297,9 @@ packages: - cpython >=3.10 license: MIT license_family: MIT - size: 1080282 - timestamp: 1780396676915 + run_exports: {} + size: 1071375 + timestamp: 1786328955132 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda sha256: cf7ab8abaac6b462463310412cc37d087a767a95920809cbe6dc0a08fe4bcfbd md5: f7069cdc81f7a5e781f1faf6aab6c171 @@ -21094,49 +13314,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 size: 127842 timestamp: 1784086873207 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.7.30-h4ab18a5_0.conda - sha256: 7f80bf6b3a276cbef67c8ad2f7d1868fd20c789ca02bffb5aa1eb07e7c5e3351 - md5: d48ee1d9eec32116fe1a0a5c92a4cdf7 - depends: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-auth >=0.7.30,<0.7.31.0a0 - size: 102448 - timestamp: 1726208446187 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.7.4-hf1fc857_1.conda - sha256: f7ea9d52f759775dde2a39e1a2325e4659bfb2859f7a45798323c7cb00ed2770 - md5: 7c01760e07f867666662a4d91e998308 - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - openssl >=3.3.1,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - size: 46848 - timestamp: 1725830274457 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda sha256: cda33360c71ab9a4b4e269004a5672d712fa1ae581ecca0404181805ce6762ce md5: fda8fa85ef5d8570d5a0aadea688bb82 @@ -21147,7 +13329,6 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -21162,43 +13343,11 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 size: 241190 timestamp: 1783637351552 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.9.28-h2466b09_0.conda - sha256: 102e955695d4b996753773552820b18b6d0c1f8d77ac0412041341bece100815 - md5: 3ffb0664a913a557bf89ed1834d0c12c - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-common >=0.9.28,<0.9.29.0a0 - size: 233724 - timestamp: 1725670503118 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.2.19-hf1fc857_1.conda - sha256: 0e5913b72e730644a9ea8b5ed8d8fbc32d288d202882a9ec089b64a18612dc31 - md5: 289e8943be0dce6b1abf60652bc1492e - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-compression >=0.2.19,<0.2.20.0a0 - size: 22447 - timestamp: 1725830398597 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.3.2-h1da161f_4.conda sha256: 97eaf03572b61d118616e8ee8459823c7b0f5dc26295bbab3ac3f6d671f1547a md5: 996e5c7da8f9e255eac094d2413b40c3 @@ -21209,30 +13358,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 size: 23355 timestamp: 1784042894276 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.4.3-hd0ca3c1_2.conda - sha256: be7815f98f210acc1e6cbac1d9a0cb05d6f91fe53c2dd62cab585c4da66359e3 - md5: 93704218ce07e4d961299e170ed430b6 - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.20,<0.1.21.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - size: 54331 - timestamp: 1726327493766 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.7.1-h88938e3_4.conda sha256: 9efd649371d7341a7a02107e7a22af304b9d9f2c16ccc534a7f93adbedc62e66 md5: 6f00186d3ab808e5b04c6063f1e2b48c @@ -21245,7 +13375,6 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -21264,48 +13393,11 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 size: 213342 timestamp: 1784075730337 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.8.9-heca9ddf_0.conda - sha256: b347d4d86850eb7f5089bb99cff00071997697d451835c4b976f2a245a93986c - md5: c66174f469df56f4e2d6dbdcac7033e0 - depends: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-compression >=0.2.19,<0.2.20.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-http >=0.8.9,<0.8.10.0a0 - size: 182415 - timestamp: 1726017296936 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.14.18-h3831a8d_12.conda - sha256: 26778c5dd0f4bd4e2cfafbfc7920a1289461ba98608ca4b400f87a05e77e2de8 - md5: 360c63cdf64d7bbd99196eb23d753908 - depends: - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-io >=0.14.18,<0.14.19.0a0 - size: 161275 - timestamp: 1728563406574 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.27.3-hbdc738f_1.conda sha256: 36816ff394ee736fcf705db5b1c1491c98f6f77ef84791d73b71e78154df402d md5: 579600368b15fb523d69e2a3f8a9bc55 @@ -21317,30 +13409,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 size: 183828 timestamp: 1784054860660 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.10.5-h8fec231_0.conda - sha256: 2f851d2895d77b41c7ef0f8ce956fb14e85f307e1dfaa227d49de67af283e82a - md5: ac525109aeb0571fe434e4e4d6ecbfef - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - size: 185093 - timestamp: 1726206008091 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.16.0-hd7b40c3_2.conda sha256: b48cdc8aea115b589377d9e3045005eae706119afd94de78dd9d47c39ff709e8 md5: 87bbe253aafe6d570a176b6aa2ef9c68 @@ -21353,7 +13426,6 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -21374,49 +13446,11 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 size: 146292 timestamp: 1784101024241 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.6.5-he24745f_5.conda - sha256: 53a93bc75e50410a8eca4235a360489cc7182cee2b236454bf18484cc1caa4b4 - md5: f6571e7933236c29bcb992338d0f3090 - depends: - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-checksums >=0.1.20,<0.1.21.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - size: 107798 - timestamp: 1726328091287 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.1.19-hf1fc857_3.conda - sha256: 5e42bba0f1ffd1a1cc5b80f5abae03c7118809f4545c688e56c2bb5a0ee3740e - md5: b00e5b1b3985d9dfadde29e8b00f85e4 - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - size: 55242 - timestamp: 1725837225397 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.2.7-h1da161f_2.conda sha256: d5da00123d9ceb082e61fd5bf82fff9cd5bec0b8e1ae91454f29155a59499bf5 md5: 882d834f12e0abf4c7a0e9bb0c72e281 @@ -21427,28 +13461,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 size: 64723 timestamp: 1784044301184 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.1.20-hf1fc857_0.conda - sha256: 446710cc7d12beddfe11bfd50a5d2a8f2418b66fb3a0a92a1a9031e041b101e9 - md5: 1b66a8719c94d85fa6658d8f46600f21 - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-checksums >=0.1.20,<0.1.21.0a0 - size: 75478 - timestamp: 1726282558694 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.2.10-h1da161f_4.conda sha256: 2227fa77f395f1b4699533709e7ef140a8292fccb31376ec5498565c7ad33225 md5: a3c81085892f2983979836f2937291ce @@ -21459,36 +13476,11 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 size: 117110 timestamp: 1784044282001 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.28.2-h2ae5ca2_6.conda - sha256: 8246682d553c794efcce331609f99515fd7c739b852a3c73cb7b482304f8daba - md5: 61f5d43cfbabdbace9203a6fd0bd2267 - depends: - - aws-c-auth >=0.7.30,<0.7.31.0a0 - - aws-c-cal >=0.7.4,<0.7.5.0a0 - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-c-http >=0.8.9,<0.8.10.0a0 - - aws-c-io >=0.14.18,<0.14.19.0a0 - - aws-c-mqtt >=0.10.5,<0.10.6.0a0 - - aws-c-s3 >=0.6.5,<0.6.6.0a0 - - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - size: 254015 - timestamp: 1726483967569 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.40.1-he7c1723_3.conda sha256: de1ddb38c8b7455f10d81affe16a8baa88fdd16c4c938ce0cfeeb63c68f1169c md5: 2b25be5d69a7ed4a63cde762e82cd1e0 @@ -21507,32 +13499,11 @@ packages: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 size: 311706 timestamp: 1784113271301 -- conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.379-h9ac9443_10.conda - sha256: 39bc5a9941e5a76eebc5ae52819f1212550fe18689dc7e31841aa15412cc047a - md5: 65e55af6d94f870f7a1af70921d91296 - depends: - - aws-c-common >=0.9.28,<0.9.29.0a0 - - aws-c-event-stream >=0.4.3,<0.4.4.0a0 - - aws-checksums >=0.1.20,<0.1.21.0a0 - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - size: 2781287 - timestamp: 1726515944980 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.conda sha256: a58fdb26e7268b00bf6a1c1ca34865e82a7fe41acc630791f835c413c06981e2 md5: 0d6b7d24ad2b8454b061404b76382e0d @@ -21546,28 +13517,11 @@ packages: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 size: 21901375 timestamp: 1784137301858 -- conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.13.0-haf5610f_0.conda - sha256: e3d828f79368057258140e46404892b0ed8983797c05c04eac3bd24dea71da41 - md5: 14ed34c3091f89784d926cc7cf4b773b - depends: - - libcurl >=8.8.0,<9.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - size: 487099 - timestamp: 1720853456727 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.16.3-h49e36cd_0.conda sha256: cfcbd49faf954343220ed5e5feb8be1f65070f9d077ad223c9b1958f3a73b075 md5: 1e8ee0cbdb1822de68e488e98cd8a31e @@ -21577,7 +13531,6 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -21593,45 +13546,11 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 size: 425897 timestamp: 1781268289981 -- conda: https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.8.0-h148e6f0_2.conda - sha256: 1d5c52c0619d4ab1be47cd7958c5c9ecc327b0f5854ae0354b7c9cc60c73afe4 - md5: 83ec332c6f07f9e48c8d5706cceab962 - depends: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - size: 383395 - timestamp: 1721777916149 -- conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.12.0-hf03c1c4_0.conda - sha256: 27a8b5df83d650129fb7ed4f73272f08bd92f72c2622e96c5145048ee442a39f - md5: 093769d5e96a6940cf10086af031dbca - depends: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - size: 967558 - timestamp: 1721865277797 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.18.0-h4fb3251_1.conda sha256: 51533ea83750b76c89dca67e2967f2ef20be5b26406f76ecb0e85537237b7a2e md5: 7d38f326139cefa0a9c2ec505c0177b1 @@ -21643,7 +13562,6 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -21659,28 +13577,11 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 size: 256108 timestamp: 1781268295342 -- conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.7.0-h148e6f0_1.conda - sha256: e65871ff5c3f6e19d21f9e98318de93fbed2ead70f1e6f379246c5e696bd87a7 - md5: 9802dfd947dba7777ffcb25078c59c2d - depends: - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - size: 239921 - timestamp: 1721833165139 - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda sha256: 51d41dd74824cfc3dd0312378bb6d7bd6a9672aaf6f105834ee63cfbfd2fecbb md5: 59a1891edca316111ad277dc3535f2f0 @@ -21693,31 +13594,11 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 size: 450656 timestamp: 1781540588533 -- conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-h85f69ea_0.conda - sha256: 1289853b41df5355f45664f1cb015c868df1f570cf743e9e4a5bda8efe8c42fa - md5: 2390269374fded230fcbca8332a4adc0 - depends: - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.0,<1.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - blosc >=1.21.6,<2.0a0 - size: 50135 - timestamp: 1719266616208 - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda sha256: 9303a7a0e03cf118eab3691013f6d6cbd1cbac66efbc70d89b20f5d0145257c0 md5: 357d7be4146d5fec543bfaa96a8a40de @@ -21731,103 +13612,47 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 size: 49840 timestamp: 1733513605730 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda - sha256: df2a43cc4a99bd184cb249e62106dfa9f55b3d06df9b5fc67072b0336852ff65 - md5: 441706c019985cf109ced06458e6f742 - depends: - - brotli-bin 1.1.0 hfd05255_4 - - libbrotlidec 1.1.0 hfd05255_4 - - libbrotlienc 1.1.0 hfd05255_4 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 20233 - timestamp: 1756599828380 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda - sha256: a4fffdf1c9b9d3d0d787e20c724cff3a284dfa3773f9ce609c93b1cfd0ce8933 - md5: bc58fdbced45bb096364de0fba1637af +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda + sha256: 85e12348d058791aabd6eeb6abc1d7ab44b8f6308f91c8475f44f778f2a158af + md5: 9eba56a007c44dc32d0b9d0a820871ea depends: - - brotli-bin 1.2.0 hfd05255_1 - - libbrotlidec 1.2.0 hfd05255_1 - - libbrotlienc 1.2.0 hfd05255_1 + - brotli-bin 1.2.0 hd477307_3 + - libbrotlidec 1.2.0 h84f9c24_3 + - libbrotlienc 1.2.0 he2a975b_3 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20342 - timestamp: 1764017988883 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda - sha256: e92c783502d95743b49b650c9276e9c56c7264da55429a5e45655150a6d1b0cf - md5: ef022c8941d7dcc420c8533b0e419733 - depends: - - libbrotlidec 1.1.0 hfd05255_4 - - libbrotlienc 1.1.0 hfd05255_4 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 21425 - timestamp: 1756599802301 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda - sha256: e76966232ef9612de33c2087e3c92c2dc42ea5f300050735a3c646f33bce0429 - md5: 6abd7089eb3f0c790235fe469558d190 - depends: - - libbrotlidec 1.2.0 hfd05255_1 - - libbrotlienc 1.2.0 hfd05255_1 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 22714 - timestamp: 1764017952449 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py312hbb81ca0_4.conda - sha256: f3c7c9b0a41c0ec0c231b92fe944e1ab9e64cf0b4ae9d82e25994d3233baa20c - md5: 3bb5cbb24258cc7ab83126976d36e711 + size: 20954 + timestamp: 1786622876247 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda + sha256: 5328e0576c729813d28efc15613f4be94d029d77c4fe5afda16cab0b8d0c9d05 + md5: dc04f7b3d82c6fb3fdf4d93e45b6ea2a depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 + - libbrotlidec 1.2.0 h84f9c24_3 + - libbrotlienc 1.2.0 he2a975b_3 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - constrains: - - libbrotlicommon 1.1.0 hfd05255_4 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 323090 - timestamp: 1756599941278 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314he701e3d_1.conda - sha256: 6854ee7675135c57c73a04849c29cbebc2fb6a3a3bfee1f308e64bf23074719b - md5: 1302b74b93c44791403cbeee6a0f62a3 + size: 23119 + timestamp: 1786622866096 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + sha256: f96c411313beb92a6a9066b823f7e8ea085f3e3889219b44306c44d59f99e611 + md5: b1ff58c1f0deedd3f25c103e37049cee depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -21835,14 +13660,12 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - libbrotlicommon 1.2.0 hfd05255_1 + - libbrotlicommon 1.2.0 hf02afa3_3 license: MIT license_family: MIT - purls: - - pkg:pypi/brotli?source=hash-mapping run_exports: {} - size: 335782 - timestamp: 1764018443683 + size: 336902 + timestamp: 1786623039339 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda sha256: 04767466ee9227c9c57ab2c6503e0149177d34111c7418d2f420297acb1eb229 md5: c3301c058362f340100d91cd8be0393f @@ -21852,126 +13675,51 @@ packages: - vc14_runtime >=14.44.35208 license: bzip2-1.0.6 license_family: BSD - purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 size: 55919 timestamp: 1785906343696 -- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - sha256: 76dfb71df5e8d1c4eded2dbb5ba15bb8fb2e2b0fe42d94145d5eed4c75c35902 - md5: 4cb8e6b48f67de0b018719cdf1136306 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: bzip2-1.0.6 - license_family: BSD - purls: [] - run_exports: - weak: - - bzip2 >=1.0.8,<2.0a0 - size: 56115 - timestamp: 1771350256444 -- conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.6-hfd05255_0.conda - sha256: 5e1e2e24ce279f77e421fcc0e5846c944a8a75f7cf6158427c7302b02984291a - md5: 7c6da34e5b6e60b414592c74582e28bf - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - c-ares >=1.34.6,<2.0a0 - size: 193550 - timestamp: 1765215100218 -- conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_1.conda - sha256: 38920d53a152dabc4d932c3e015edac32a6a74547e4a7ffa2e4f471c3d6af046 - md5: c6cfed2bd3db1ebac70293aa99e7196a +- conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda + sha256: b474e4b7f3136576c321533e340b34368a5ce671949bc1a77ef446eb94870136 + md5: b5a0be28d048d8bbe81df51e4b750500 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - constrains: - - c-ares-static <0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 - size: 210677 - timestamp: 1786116676848 -- conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_1.conda - sha256: 9ee4ad706c5d3e1c6c469785d60e3c2b263eec569be0eac7be33fbaef978bccc - md5: 52ea1beba35b69852d210242dd20f97d + size: 210803 + timestamp: 1787169972884 +- conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda + sha256: 9128dfc6a864e369ee7b1c9bd4b3875de9d06890925760aec932cf3cc67bcb24 + md5: 601e2ec8ae0ed934d8788000f984ff18 depends: - - fontconfig >=2.15.0,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - - icu >=78.1,<79.0a0 - - libexpat >=2.7.3,<3.0a0 - - libfreetype >=2.14.1 - - libfreetype6 >=2.14.1 - - libglib >=2.86.3,<3.0a0 - - libpng >=1.6.53,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 - pixman >=0.46.4,<1.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only or MPL-1.1 - purls: [] - run_exports: - weak: - - cairo >=1.18.4,<2.0a0 - size: 1537783 - timestamp: 1766416059188 -- conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda - sha256: b9f577bddb033dba4533e851853924bfe7b7c1623d0697df382eef177308a917 - md5: 20e32ced54300292aff690a69c5e7b97 - depends: - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - freetype >=2.12.1,<3.0a0 - - icu >=75.1,<76.0a0 - - libexpat >=2.6.4,<3.0a0 - - libglib >=2.82.2,<3.0a0 - - libpng >=1.6.47,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 - - pixman >=0.44.2,<1.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: LGPL-2.1-only or MPL-1.1 - purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 1524254 - timestamp: 1741555212198 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py312he06e257_1.conda - sha256: 3e3bdcb85a2e79fe47d9c8ce64903c76f663b39cb63b8e761f6f884e76127f82 - md5: 46f7dccfee37a52a97c0ed6f33fcf0a3 - depends: - - pycparser - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cffi?source=hash-mapping - run_exports: {} - size: 291324 - timestamp: 1761203195397 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py314h5a2d7ad_1.conda - sha256: 924f2f01fa7a62401145ef35ab6fc95f323b7418b2644a87fea0ea68048880ed - md5: c360170be1c9183654a240aadbedad94 + size: 1540051 + timestamp: 1787926241225 +- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_2.conda + sha256: e0e10d676eb67a8a4c8991cec89fb6f150f919eb1266a1b39253a857b3dc0454 + md5: 672d6ff72c6265b25eeef94ce21e71ac depends: - pycparser - python >=3.14,<3.15.0a0 @@ -21981,24 +13729,9 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - size: 294731 - timestamp: 1761203441365 -- conda: https://conda.anaconda.org/conda-forge/win-64/cfitsio-4.4.1-hc2ea260_0.conda - sha256: 97249ec67f115c05a2a452e62f6aed2e3f3a244ba1f33b0e9395a05f9d7f6fee - md5: b3263858e6a924d05dc2e9ce335593ba - depends: - - libcurl >=8.8.0,<9.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: LicenseRef-fitsio - purls: [] - run_exports: - weak: - - cfitsio >=4.4.1,<4.4.2.0a0 - size: 601046 - timestamp: 1718906922426 + run_exports: {} + size: 346657 + timestamp: 1786775160153 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda sha256: 39891566d1620cd759e8e2f7b3d2bee66f3cfceca577f23cf12bbaa3f7f0c066 md5: 779a28ebe99dde22af1b281ee2a09ecf @@ -22017,7 +13750,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -22040,7 +13772,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -22062,7 +13793,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -22083,7 +13813,6 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -22103,55 +13832,53 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER - purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 size: 1091975 timestamp: 1778500610309 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.5.3-py314h86ab7b2_0.conda - sha256: de88bb2a650244c3d56ed42672139f48178fd6584492aa9c8032db949133b6b3 - md5: 490a594d0a8c209bd6e23651c6537f2e +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.1-py314hb821551_0.conda + sha256: d265867efa08fbec3dad842b9da4e015d50b7c43cafa67921b0f38f96bb67e17 + md5: de36e896fdc6c9ed9950a93fc3022e27 depends: - archspec >=0.2.3 - boltons >=23.0.0 - charset-normalizer - - conda-libmamba-solver >=26.4.1 - - conda-lockfiles >=0.2.0 - conda-package-handling >=2.2.0 - - conda-pypi >=0.9.0 - - conda-rattler-solver >=0.1.1 - - conda-self >=0.2.0 - distro >=1.5.0 - frozendict >=2.4.2 - - jsonpatch >=1.32 - menuinst >=2 + - msgpack-python >=1.1.1 - packaging >=23.0 - platformdirs >=3.10.0 - pluggy >=1.6.0 - pycosat >=0.6.3 - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 + - python - requests >=2.28.0,<3 - ruamel.yaml >=0.11.14,<0.19 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 - - zstandard >=0.19.0 + - conda-libmamba-solver >=26.4.1 + - conda-lockfiles >=0.2.0 + - conda-pypi >=0.10.1 + - conda-rattler-solver >=0.1.1 + - conda-self >=0.2.0 + - python_abi 3.14.* *_cp314 constrains: - conda-build >=25.9 - - conda-env >=2.6 - conda-content-trust >=0.3.0 + - conda-env >=2.6 license: BSD-3-Clause license_family: BSD - size: 1362284 - timestamp: 1781681191655 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.10.1-py314hb821551_2.conda - sha256: f1440b3dea081d6fe76ddffba6e1f01a4d8b97f4a7b3f8fef889ffe3b0ee0e1c - md5: 0c4992e5a5d69efdaa97ab733bc7f530 + run_exports: {} + size: 1473305 + timestamp: 1787306256970 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda + sha256: af3766a095bd7b2f5f0a8958474d29c139e51628230b98b7e45ff4af1702c8ae + md5: bab6a2a990c12195bb6553216ab660ec depends: - python - - conda >=26.1.0 - pip >=23.0.1 - packaging - unearth @@ -22161,27 +13888,13 @@ packages: - conda-index >=0.11.0 - conda-package-streaming >=0.11 - python_abi 3.14.* *_cp314 + constrains: + - conda >=26.1.0 license: MIT license_family: MIT - size: 317787 - timestamp: 1781646513612 -- conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda - sha256: 5f0dd3a4243e8293acc40abf3b11bcb23401268a1ef2ed3bce4d5a060383c1da - md5: 475bd41a63e613f2f2a2764cd1cd3b25 - depends: - - numpy >=1.25 - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping run_exports: {} - size: 244035 - timestamp: 1769155978578 + size: 323375 + timestamp: 1784203061870 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda sha256: f141bcbf8e490b49b2f53f517173d13a64d75e43cfae170e0d931cb0b66f4bce md5: c26934035616f7d578f9da0491aed3d8 @@ -22194,43 +13907,20 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 247437 timestamp: 1769155978556 -- conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_0.conda - sha256: cd24ac6768812d53c3b14c29b468cc9a5516b71e1880d67f58d98d9787f4cc3a - md5: 444e9f7d9b3f69006c3af5db59e11364 +- conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda + sha256: ca7f86a8f5d91dffcbc0a720bb4c3be8b35ccfb8e1e44cfa43c95dd13b6c83fc + md5: 8f83a0d68492c0d19528ce750d6566b9 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 license: CC0-1.0 - size: 21733 - timestamp: 1756734797622 -- conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda - sha256: ec5cce1b3a66fa8f154e9597d628988db79fb015d375351562376f80a7e965d1 - md5: 7a20e585f458d44b5fd1e59bb7d95e32 - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping run_exports: {} - size: 1645185 - timestamp: 1763019902333 + size: 22027 + timestamp: 1785920525392 - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda sha256: 6c87f747dac77a3a6db80d229d42d90f2864b984f612a722ceedc75d0dc09cee md5: 3d84e97e6606a7e6bb8096afb53e9bfc @@ -22245,18 +13935,16 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1644554 timestamp: 1763019901149 -- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_4.conda - sha256: 07f77d576f2044860cca9490f1397d8d05f54adeb57eb2c4af8785639ba7f1f5 - md5: 8f6cf02d07b029c5f4716cb1bab3fcce +- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda + sha256: 61185005c73fe2ebdf1656a7b730a47eaf1802409d5a76ac6a54d1f70a78ac1c + md5: b0dcd6182bfd56a219563a153d550d20 depends: - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 hdb0ef4a_4 - - libpsl >=0.23.0,<0.24.0a0 + - libcurl 8.21.0 hdb0ef4a_5 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 @@ -22264,44 +13952,9 @@ packages: - vc14_runtime >=14.44.35208 license: curl license_family: MIT - purls: [] - run_exports: {} - size: 189174 - timestamp: 1785500250348 -- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda - sha256: e66a02405681ec973b96ea13a23c07a778c5d3a7fbf818779bda9ed088ea287e - md5: 45d3504d24c6a853f22bb93d721d9d20 - depends: - - krb5 >=1.21.3,<1.22.0a0 - - libcurl 8.9.1 h18fefc2_0 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: curl - license_family: MIT - purls: [] - run_exports: {} - size: 157453 - timestamp: 1722440281830 -- conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py312he06e257_2.conda - sha256: e817c9154c917f562e378cf2898a1ff82f20c87ef465b75b2bcba94235604814 - md5: 978c009bc3f0add939e44aff97bfaee1 - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - toolz >=0.10.0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} - size: 563651 - timestamp: 1771855915942 + size: 189161 + timestamp: 1787183770186 - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda sha256: 04dca71339134390f2d76467070f02356efb7bf21408cdd45a868a94009e93b9 md5: 8309b55a55dac8050260de935d31724a @@ -22314,8 +13967,6 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 582082 timestamp: 1771855937157 @@ -22330,26 +13981,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 4022782 timestamp: 1780390190830 -- conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda - sha256: b1fee32ef36a98159f0a2a96c4e734dfc9adff73acd444940831b22c1fb6d5c0 - md5: e9a1402439c18a4e3c7a52e4246e9e1c - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - double-conversion >=3.3.1,<3.4.0a0 - size: 71355 - timestamp: 1739570178995 - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda sha256: 09e30a170e0da3e9847d449b594b5e55e6ae2852edd3a3680e05753a5e015605 md5: 3d3caf4ccc6415023640af4b1b33060a @@ -22359,33 +13993,11 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - double-conversion >=3.4.0,<3.5.0a0 size: 70943 timestamp: 1765193243911 -- conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda - sha256: 5fb4696e76cc9fce16223009915c14eff36cad190eca809fbb77f5a000a80cde - md5: caa98e34aa51b84fc70effb79404a89d - depends: - - cramjam >=2.3 - - fsspec - - numpy >=1.23,<3 - - packaging - - pandas >=1.5.0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping - run_exports: {} - size: 511190 - timestamp: 1778864902674 - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda sha256: ebd6e87d44c3ced24a8f84e4a016ca31f691c6f2b5c58f84ed09787f1fc084f4 md5: bfc434b47d15ca2f2afa329394fa138c @@ -22402,36 +14014,9 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 523153 timestamp: 1778864931353 -- conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda - sha256: 0f91c93cff199371f665fe0807d4c871dd6e6534c8c9b5ede6897c867b055c23 - md5: 1b03775deb65180f956a7c1b0e6edc5f - depends: - - attrs >=19.2.0 - - click >=8.0,<9.dev0 - - click-plugins >=1.0 - - cligj >=0.5 - - gdal - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - pyparsing - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - shapely - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping - run_exports: {} - size: 976102 - timestamp: 1726664869512 - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda sha256: 96a3ce5f2868a47e08d00a8fbcf68e1b8dbf9ca30a018120554ca26db3255c5e md5: c1bae1515ff287d5bf6e287dddbc6646 @@ -22450,62 +14035,26 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 985443 timestamp: 1767051440390 -- conda: https://conda.anaconda.org/conda-forge/win-64/fmt-11.0.2-hf4da5c8_1.conda - sha256: ecadffd95f09614aff9d520db9f7ccd677c90c3bfb4d3b2981c36ce61562f81b - md5: bde3a8f593f24929b8a6e4be0ff009cf - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - fmt >=11.0.2,<11.1.0a0 - size: 190055 - timestamp: 1743031131636 -- conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h7f4e812_0.conda - sha256: cce96406ec353692ab46cd9d992eddb6923979c1a342cbdba33521a7c234176f - md5: 6e226b58e18411571aaa57a16ad10831 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 186390 - timestamp: 1767681264793 -- conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda - sha256: 9217184c4a8e82101b0e512b059ae3ff67e3913133b9031edad89ab5341284e4 - md5: abd79bad98c99c1a116154d6de74ea89 +- conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda + sha256: 1d6d6f1bf284e50cf617ce9a6250e0bf70bd0d30b068568364d6b1852aad9466 + md5: ca8fa0e01ed34bb161f4e8e86dfda22e depends: - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libiconv >=1.18,<2.0a0 - - libintl >=0.22.5,<1.0a0 - - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - - fontconfig >=2.18.1,<3.0a0 - - fonts-conda-ecosystem - size: 202630 - timestamp: 1780450217840 -- conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_0.conda - sha256: 47263b05990c7ef4b52982a7ea8d2cfb74cd4bee0d72b363e20caf651d846fc8 - md5: 3bede9169c3a1a0a8fb1eee60aec27b7 + - fmt >=12.1.0,<12.2.0a0 + size: 187511 + timestamp: 1785915492086 +- conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda + sha256: f26139e3c774a6434c8a73381c08e3d2a4c2f9d9ef9587c66aab8836211ee2ec + md5: ed95670fed91b091fe34ba6cae6677d7 depends: - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 @@ -22518,75 +14067,42 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - size: 218758 - timestamp: 1786381247448 -- conda: https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda - sha256: 4e31266b0ddacb2e2f48f00d999aa7d5005c62a5cc51a32f9ce0be5b11e9897e - md5: 2944f5f8ea7e2db9cea01ed951e09194 - depends: - - brotli - - munkres - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - unicodedata2 >=15.1.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: - - pkg:pypi/fonttools?source=hash-mapping - run_exports: {} - size: 2533681 - timestamp: 1778770493020 -- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda - sha256: a0e419e96146159f12344c870dca608d11bca36841f228092b986ffc2e1e0f02 - md5: e77293b32225b136a8be300f93d0e89f - depends: - - libfreetype 2.14.3 h57928b3_1 - - libfreetype6 2.14.3 hdbac1cb_1 + size: 217988 + timestamp: 1786667461139 +- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda + sha256: 32f7dd8ffe62fd485e9e6570e871f5be45af74c84fde62241a2417046a373efd + md5: 6fc6c09c05a099d58efd9b2e96598e41 + depends: + - libfreetype 2.14.3 h57928b3_2 + - libfreetype6 2.14.3 hdbac1cb_2 - zlib license: GPL-2.0-only OR FTL - purls: [] run_exports: weak: - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - size: 185584 - timestamp: 1780934817461 -- conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda - sha256: 1e62cbc6daa74656034dc4a6e58faa2d50291719c1cba53cc0b1946f0d2b9404 - md5: d6a8059de245e53478b581742b53f71d - depends: - - libexpat >=2.6.4,<3.0a0 - - libiconv >=1.17,<2.0a0 - - minizip >=4.0.7,<5.0a0 + size: 186945 + timestamp: 1786641050416 +- conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda + sha256: 857183489abc5a088b20b221bd78c7ea91aa23a065291149bb4c8a32981e6bc9 + md5: 9f0212e4c4bdf5f8f52a1f9793d86311 + depends: + - libexpat >=2.8.1,<3.0a0 + - libiconv >=1.18,<2.0a0 + - minizip >=4.2.2,<5.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: MPL-1.1 - license_family: MOZILLA - purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 - size: 77528 - timestamp: 1734015193826 -- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda - sha256: 15011071ee56c216ffe276c8d734427f1f893f275ef733f728d13f610ed89e6e - md5: c27bd87e70f970010c1c6db104b88b18 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LGPL-2.1-or-later - size: 64394 - timestamp: 1757438741305 + size: 79171 + timestamp: 1788125519179 - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda sha256: 274b3e4ae5dff527062039d1dcb5cfdd9f91fa7d8eaf61358c09450b361385de md5: 66f5ce9d0d618332023619a899ceb26f @@ -22595,80 +14111,24 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 size: 65318 timestamp: 1785912637725 -- conda: https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda - sha256: 4247071c2216b9813f6732edc9c4728bb11f6877a1c87000b6695eebef741641 - md5: 4a093f053f2827cfa102304bf945e845 - depends: - - libgdal-core 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: - - pkg:pypi/gdal?source=hash-mapping - run_exports: {} - size: 1634719 - timestamp: 1728294392653 -- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.12.2-h5a68840_1.conda - sha256: e8606bbf3ebbaf2817d65d4b48180cc1d828a030061e0a5ef55281f9cc7f1e28 - md5: 019e3460f99eb7c2198c532c50d08791 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: LGPL-2.1-only - purls: [] - run_exports: - weak: - - geos >=3.12.2,<3.12.3.0a0 - size: 1561663 - timestamp: 1721747131206 -- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-hdade9fe_0.conda - sha256: 032a16d78e69a20ffae6216191a66977bc50f6c21cb75f9853b37298b95308c4 - md5: 8c75d7e401a4d799ce8d4bb922320967 +- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda + sha256: 70c51f6e7d2e2d68dd8fafe3a2c821764b7e33f2e66379a6b5691e942c73b7d3 + md5: f3405ff0366fe7ee04580acf8e422d16 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only - purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 - size: 1772787 - timestamp: 1761593910217 -- conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.3-h232476a_2.conda - sha256: cf512663c8681e5e5a3d30046860ad06a8a4700b217d34c348f974ea481a0b18 - md5: 8968032e8f14d84b40a20437707f8ec7 - depends: - - libjpeg-turbo >=3.0.0,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zlib - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - geotiff >=1.7.3,<1.8.0a0 - size: 123406 - timestamp: 1722335928788 + size: 1723817 + timestamp: 1787894635500 - conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda sha256: d04c4a6c11daa72c4a0242602e1d00c03291ef66ca2d7cd0e171088411d57710 md5: 49c36fcad2e9af6b91e91f2ce5be8ebd @@ -22681,56 +14141,18 @@ packages: - ucrt >=10.0.20348.0 license: LGPL-3.0-only license_family: LGPL - purls: [] run_exports: weak: - getopt-win32 >=0.1,<0.1.1.0a0 size: 26238 timestamp: 1750744808182 -- conda: https://conda.anaconda.org/conda-forge/win-64/glib-2.88.3-h89924da_1.conda - sha256: 4aadcd822cee664e41f413fc73d58ee44e9f9609dfa8bae7fedd01d569b1447c - md5: ac5df4663035b908c8bdccfd7fe45e0e - depends: - - python * - - packaging - - libglib ==2.88.3 he810d59_1 - - glib-tools ==2.88.3 hf027272_1 - - libintl-devel - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - libintl >=0.22.5,<1.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libglib >=2.88.3,<3.0a0 - size: 76039 - timestamp: 1786457672418 -- conda: https://conda.anaconda.org/conda-forge/win-64/glib-tools-2.88.3-hf027272_1.conda - sha256: b4b637f6e3b408e21af8e98d635745146f7434ecf1b7ca73b8e510c1d3544ef1 - md5: a22de7dd4d070f48158307df1749c9b1 - depends: - - libglib ==2.88.3 he810d59_1 - - libffi - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - libintl >=0.22.5,<1.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 251656 - timestamp: 1786457672418 -- conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.13.1-h11686cb_0.conda - sha256: f698800c168ba3185bbda4fbfb4a10bb625129f3ca2a6194616e7a6867234be4 - md5: 354dc95ae9872158dc0f56a9b696d841 +- conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.0-h11686cb_0.conda + sha256: 44320b78b03e503fb19e7897b94dda469702c16fd67ba8a90f6526211bc1b2d5 + md5: 4415fc5872344fa14b0e49e1830e1ca6 license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: {} - size: 2017538 - timestamp: 1775514603704 + size: 2062286 + timestamp: 1787948982916 - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda sha256: 93a59bdf944fb6f947bd7ad2f293d5d80db3a90f8ff8dfabc591e3752141e46f md5: 79538e7a7bc024084eda5989f73fde35 @@ -22740,23 +14162,11 @@ packages: - ucrt >=10.0.20348.0 license: LGPL-2.0-or-later license_family: LGPL - purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 size: 98064 timestamp: 1786118492029 -- conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-hac47afa_0.conda - sha256: 88b6601f8edae59834b59b521e293ff3b58361dc1603240f5a8328c24e6936ad - md5: ff9a9bfe791f56b0227597a7651a6af0 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LGPL-2.0-or-later - license_family: LGPL - size: 97308 - timestamp: 1780454389458 - conda: https://conda.anaconda.org/conda-forge/win-64/graphviz-14.1.2-h4c50273_0.conda sha256: 58f83755509a19501a9efe40c484727ffa61fcfaf6a237870678a79638fa6982 md5: afabed4c46b197b89eb974aa038d12db @@ -22775,146 +14185,40 @@ packages: - vc14_runtime >=14.44.35208 license: EPL-1.0 license_family: Other - purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 size: 1223547 timestamp: 1769427507016 -- conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.2-py314hb98de8c_0.conda - sha256: 37f7f2be58798f79f6cc9a8951a4617941ef44a3d383e8318f4e71c112a23e78 - md5: bd08b57adf4fa0886867123c7df63e75 - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - size: 248927 - timestamp: 1781762180310 -- conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda - sha256: b79755d2f9fc2113b6949bfc170c067902bc776e2c20da26e746e780f4f5a2d4 - md5: a41f14768d5e377426ad60c613f2923b - depends: - - libglib >=2.76.3,<3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: LGPL-2.0-or-later - license_family: LGPL - purls: [] - run_exports: - weak: - - gts >=0.7.6,<0.8.0a0 - size: 188688 - timestamp: 1686545648050 -- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda - sha256: f1ab5960a52a11186f528249bec5ce5e43bb4c44c87ffa24334255f07c3fd4b8 - md5: b7648427f5b6797ae3904ad76e4c7f19 - depends: - - cairo >=1.18.4,<2.0a0 - - freetype >=2.13.3,<3.0a0 - - graphite2 - - icu >=75.1,<76.0a0 - - libexpat >=2.6.4,<3.0a0 - - libglib >=2.84.0,<3.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - harfbuzz >=11.0.0,<12.0a0 - size: 1125019 - timestamp: 1743083466989 -- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.2.1-h5a1b470_0.conda - sha256: 55d6d483e089afe68bdbb38a003d7b76002e65341665b80f38e6ce4b494beef6 - md5: 0bcbb7f911590beec914555c6b82050d - depends: - - cairo >=1.18.4,<2.0a0 - - graphite2 >=1.3.14,<2.0a0 - - icu >=78.3,<79.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.1,<3.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - size: 1304897 - timestamp: 1780450940279 -- conda: https://conda.anaconda.org/conda-forge/win-64/harfbuzz-14.3.0-h57928b3_0.conda - sha256: bcefb437e994108aaf768e52e5ee63b6eddc6dc4b91a83b2ad59363e49486709 - md5: a2696e807c11de8867ecfacc6fe467aa - depends: - - libharfbuzz-devel 14.3.0 h03b5201_0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libharfbuzz >=14.3.0 - size: 11509 - timestamp: 1785770514661 -- conda: https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda - sha256: 52fa5dde69758c19c69ab68a3d7ebfb2c9042e3a55d405c29a59d3b0584fd790 - md5: 84344a916a73727c1326841007b52ca8 - depends: - - libjpeg-turbo >=3.0.0,<4.0a0 - - libzlib >=1.2.13,<2.0.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - hdf4 >=4.2.15,<4.2.16.0a0 - size: 779637 - timestamp: 1695662145568 -- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.3-nompi_h2b43c12_105.conda - sha256: 56c803607a64b5117a8b4bcfdde722e4fa40970ddc4c61224b0981cbb70fb005 - md5: 5788de34381caf624b78c4981618dc0a +- conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda + sha256: c1f9d4b92bf255fc5cc92ea10448d812417fd26132856cefe9f41f2d5ccc2f8a + md5: 212518796fa1c60ca92e4d1c8ef80ce3 depends: - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libzlib >=1.2.13,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - hdf5 >=1.14.3,<1.14.4.0a0 - size: 2039111 - timestamp: 1717587493910 -- conda: https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda - sha256: 1d04369a1860a1e9e371b9fc82dd0092b616adcf057d6c88371856669280e920 - md5: 8579b6bb8d18be7c0b27fb08adeeeb40 + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + run_exports: {} + size: 259251 + timestamp: 1786384106710 +- conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda + sha256: b79755d2f9fc2113b6949bfc170c067902bc776e2c20da26e746e780f4f5a2d4 + md5: a41f14768d5e377426ad60c613f2923b depends: + - libglib >=2.76.3,<3.0a0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] + license: LGPL-2.0-or-later + license_family: LGPL run_exports: weak: - - icu >=75.1,<76.0a0 - size: 14544252 - timestamp: 1720853966338 + - gts >=0.7.6,<0.8.0a0 + size: 188688 + timestamp: 1686545648050 - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda sha256: 75c549b55b673e15de8785a8e5dd85bca7eb612eee0ff4dc8d7bdaa15eacbdbb md5: e596942e8ee6ee17fdcf1e6a77757a66 @@ -22924,23 +14228,11 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - icu >=78.3,<79.0a0 size: 16835644 timestamp: 1784916416303 -- conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h637d24d_0.conda - sha256: 1bda728d70a619731b278c859eda364146cb5b4b8c739a64da8128353d81d1c4 - md5: 0097b24800cb696915c3dbd1f5335d3f - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 14954024 - timestamp: 1773822508646 - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda sha256: 0e9f91b6e48a3d2a52b2fbbf21caafe6018ec71838f4f501d0b88e3e9f4e7cb6 md5: 523af9223804c4eeebef3156d1bfd688 @@ -22952,46 +14244,12 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 56350 timestamp: 1757685649329 -- conda: https://conda.anaconda.org/conda-forge/win-64/kealib-1.5.3-h6c43f9b_2.conda - sha256: 19c981a049651439cfd851bbf785144d0f10db1f605ce19001a8eb27da6def94 - md5: 873b3deabbefe46d00cc81ce7d9547a7 - depends: - - hdf5 >=1.14.3,<1.14.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - kealib >=1.5.3,<1.6.0a0 - size: 133242 - timestamp: 1725399840908 -- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py312h78d62e6_0.conda - sha256: 5942bd7ae7b1d68906a00681e733b41ac8577ca7ac8da7523eb514d698b6e1f2 - md5: 4ff6f76c2c16c85806ee7533768f5e64 - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping - run_exports: {} - size: 73357 - timestamp: 1773067062006 -- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py314hf309875_0.conda - sha256: 37cbc49fd7255532d09fb3bc9cc699554693e632fa90678a9b3d0ed12557d0d7 - md5: 0508c8dabeab91311e5c59b5e3f6d278 +- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda + sha256: b34910eacb872a9b9b55318fd12332954e64b42dbc710f6a384e0500a58a2d7a + md5: 842f2582140b5567bcad1defbbd9b948 depends: - python - vc >=14.3,<15 @@ -22999,31 +14257,12 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} - size: 73330 - timestamp: 1773067062280 -- conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda - sha256: 18e8b3430d7d232dad132f574268f56b3eb1a19431d6d5de8c53c29e6c18fa81 - md5: 31aec030344e962fbd7dbbbbd68e60a9 - depends: - - openssl >=3.3.1,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - krb5 >=1.21.3,<1.22.0a0 - size: 712034 - timestamp: 1719463874284 -- conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_1.conda - sha256: c55745796e762ba9e817ab1fc0f21f1a049e202f90fa762df39578f37923f6c2 - md5: 00335c2c4a98656554771aaf6f1a7400 + size: 73212 + timestamp: 1787938458524 +- conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda + sha256: 63ff03324e903eb01a715ccf357df56d66224e61952fd6615d86490ebefb3285 + md5: 93f5a01dec294a2228f757fe2f3432d4 depends: - openssl >=3.5.7,<4.0a0 - ucrt >=10.0.20348.0 @@ -23031,44 +14270,27 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 - size: 750320 - timestamp: 1781859644591 -- conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda - sha256: 5ed63a32639a130564a870becb679fd52dfb816666a61ed3c023917389010480 - md5: 1df4012c8a2478699d07bc26af66d41e + size: 753425 + timestamp: 1786762169034 +- conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda + sha256: df1c1a4dd44c54f238dc0a230d31eab9e73ada472dc4fa357552333b35641804 + md5: 2bd9052d2e496ea54f47d9db8ae4f881 depends: - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 - size: 523194 - timestamp: 1780211799997 -- conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda - sha256: 45df58fca800b552b17c3914cc9ab0d55a82c5172d72b5c44a59c710c06c5473 - md5: 54b231d595bc1ff9bff668dd443ee012 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - lerc >=4.1.0,<5.0a0 - size: 172395 - timestamp: 1773113455582 + size: 523808 + timestamp: 1788155971355 - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda sha256: 93d666f63f284ef77b87b0b1f77b70f7d36d315a132f9afa64bc0012d937ba39 md5: add59e2b60ac9d4299d17c938185c75a @@ -23078,116 +14300,37 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 size: 175297 timestamp: 1785036247761 -- conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20240116.2-cxx17_he0c23c2_1.conda - sha256: aafa7993698420ef786c145f660e6822139c02cf9230fbad43efff6d4828defc - md5: 19725e54b7f996e0a5748ec5e9e37ae9 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - libabseil-static =20240116.2=cxx17* - - abseil-cpp =20240116.2 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libabseil >=20240116.2,<20240117.0a0 - - libabseil =*=cxx17* - size: 1802886 - timestamp: 1720857653184 -- conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h0eb2380_1.conda - sha256: 103c3c49368204d26c1c4ac82cd29b6c39adf0492501b0145f6a853f0c600b3f - md5: 50a46018a50bb1feeb4496dc98deae5b +- conda: https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h04e5de1_2.conda + sha256: 0ba14fd35791873ef167d826f05907421455bd42c1a82a1ff84ad9e8ddddf73b + md5: 7cd4be232c8e6388fcf5fadc7f429dd7 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - libabseil-static =20260526.0=cxx17* - abseil-cpp =20260526.0 + - libabseil-static =20260526.0=cxx17* license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 - libabseil =*=cxx17* - size: 2007071 - timestamp: 1780524734178 -- conda: https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.5-haf901d7_0.conda - sha256: e54c08964262c73671d9e80e400333e59c617e0b454476ad68933c0c458156c8 - md5: 43b6385cfad52a7083f2c41984eb4e91 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libaec >=1.1.5,<2.0a0 - size: 34463 - timestamp: 1769221960556 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.7.7-h88ece9c_0.conda - sha256: afc496c826ec21cb898018695a7785baced3ebb66a90e39a1c2604dfaa1546be - md5: 37c6e11d9f2e69789198ef2bfc661392 - depends: - - bzip2 >=1.0.8,<2.0a0 - - libxml2 >=2.13.5,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.4.0,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libarchive >=3.7.7,<3.8.0a0 - size: 977329 - timestamp: 1732614920501 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.8-gpl_he24518a_100.conda - sha256: abeec1902a9933f71a70bb54a436897d54ea08b290f084c0ce511e4d7eb24548 - md5: e61cb5e50e73c4563c2427de40435171 - depends: - - bzip2 >=1.0.8,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - libzlib >=1.3.2,<2.0a0 - - lz4-c >=1.10.0,<1.11.0a0 - - lzo >=2.10,<3.0a0 - - openssl >=3.5.7,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - zstd >=1.5.7,<1.6.0a0 - license: BSD-2-Clause - license_family: BSD - size: 1117061 - timestamp: 1782289351052 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_he24518a_100.conda - sha256: d27338b30444c82b7958c833ecd6db905d45ee4d88df6e29faa7c65140b1a601 - md5: 3267f5cd28472841f357865668e17c10 + size: 1978839 + timestamp: 1787217043767 +- conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda + sha256: 71cb6e49282d2aee1eb0249a290dc2333b0063ac2ecec69116ad569d822a0aaf + md5: 74c32a74e1b188a4e14fc359c721e4b9 depends: - bzip2 >=1.0.8,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 - libzlib >=1.3.2,<2.0a0 - lz4-c >=1.10.0,<1.11.0a0 - lzo >=2.10,<3.0a0 @@ -23198,52 +14341,11 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 - size: 1149153 - timestamp: 1785250768915 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-17.0.0-h29daf90_13_cpu.conda - build_number: 13 - sha256: 1a0f66e822f4cde398b15fe7ac94cb4197635798da9feebcb88c900637e05f77 - md5: d0ea8c4474c45aae86eff71a0f293013 - depends: - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - bzip2 >=1.0.8,<2.0a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libbrotlidec >=1.1.0,<1.2.0a0 - - libbrotlienc >=1.1.0,<1.2.0a0 - - libcrc32c >=1.1.2,<1.2.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libre2-11 >=2023.9.1 - - libutf8proc >=2.8.0,<3.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - orc >=2.0.2,<2.0.3.0a0 - - re2 - - snappy >=1.2.1,<1.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zstd >=1.5.6,<1.6.0a0 - - libutf8proc <2.9 - constrains: - - arrow-cpp <0.0a0 - - parquet-cpp <0.0a0 - - apache-arrow-proc =*=cpu - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow >=17.0.0,<17.1.0a0 - size: 5128979 - timestamp: 1725215183038 + size: 1151402 + timestamp: 1787292629824 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda build_number: 3 sha256: 84de044b7639d9b5a344015cf064830be75ec4acfc08b7a07ca776f534ea9579 @@ -23279,29 +14381,11 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 size: 4444092 timestamp: 1784543154690 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-17.0.0-he0c23c2_13_cpu.conda - build_number: 13 - sha256: 850b28abba3e40302cb5425ffb96f085d2089decafb2e80d85b4f8b44c2c777d - md5: 1a38e993ef119557596ae20cd68a1207 - depends: - - libarrow 17.0.0 h29daf90_13_cpu - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-acero >=17.0.0,<17.1.0a0 - size: 445286 - timestamp: 1725215254997 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda build_number: 3 sha256: 22dc95af3d3122cd3b40c26e4fb8f8fdfac9758d9b81f15acd7e549e2f6870a2 @@ -23314,7 +14398,6 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -23334,31 +14417,11 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 size: 1750569 timestamp: 1784543230425 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-17.0.0-he0c23c2_13_cpu.conda - build_number: 13 - sha256: 12b0395dc22a2c3fb03e8b8ab32bcf4ff08947b8611b2a1e9c49644d8391893c - md5: dd78096e1335abc3c7bf6915d0ac7c34 - depends: - - libarrow 17.0.0 h29daf90_13_cpu - - libarrow-acero 17.0.0 he0c23c2_13_cpu - - libparquet 17.0.0 ha915800_13_cpu - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-dataset >=17.0.0,<17.1.0a0 - size: 427535 - timestamp: 1725215469376 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda build_number: 3 sha256: d3c84cd4ce691c0d2018b1422fd7b176a814a8b3d54803aea32c3e92f4748a54 @@ -23373,34 +14436,11 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 size: 428449 timestamp: 1784543502973 -- conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-17.0.0-h1f0e801_13_cpu.conda - build_number: 13 - sha256: 637c2652cfe676d6949f7953de7d51e90bc35863c3a114c29795b5b0e119699c - md5: b618c36e7eff7a28a53bde4d9aa017e0 - depends: - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libarrow 17.0.0 h29daf90_13_cpu - - libarrow-acero 17.0.0 he0c23c2_13_cpu - - libarrow-dataset 17.0.0 he0c23c2_13_cpu - - libprotobuf >=4.25.3,<4.25.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libarrow-substrait >=17.0.0,<17.1.0a0 - size: 382757 - timestamp: 1725215569161 - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda build_number: 3 sha256: 7a2895e041ae4f22829b6ba6d65e7d28198e0fe56d336d671456901d8a34c66a @@ -23417,245 +14457,106 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 size: 366569 timestamp: 1784543539794 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - build_number: 6 - sha256: 10c8054f007adca8c780cd8bb9335fa5d990f0494b825158d3157983a25b1ea2 - md5: 95543eec964b4a4a7ca3c4c9be481aa1 - depends: - - mkl >=2025.3.1,<2026.0a0 - constrains: - - blas 2.306 mkl - - liblapacke 3.11.0 6*_mkl - - liblapack 3.11.0 6*_mkl - - libcblas 3.11.0 6*_mkl - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libblas >=3.11.0,<4.0a0 - size: 68082 - timestamp: 1774503684284 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - build_number: 8 - sha256: 43a87b59e6d4c68d80b2e4de487b1b54d66fe1f9a06636909b5a5ab9eae27269 - md5: 4a0ce24b1a946ff77ae9eaa7ef015a33 - depends: - - mkl >=2026.0.0,<2027.0a0 - constrains: - - libcblas 3.11.0 8*_mkl - - liblapacke 3.11.0 8*_mkl - - blas 2.308 mkl - - liblapack 3.11.0 8*_mkl - license: BSD-3-Clause - license_family: BSD - size: 68103 - timestamp: 1779859688049 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-9_h8455456_mkl.conda - build_number: 9 - sha256: a99c640f10a3f77efe5c6605676ddc8d365d020eec4fdf1c803d34bdaf49b357 - md5: 17b26a4ad064259983bec1eaa36f40d3 +- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda + build_number: 10 + sha256: 75eda322affc30fcdbd68e5aba7f8dbec91ff53c7d3f281828b5473af3e95bd4 + md5: 0d13dde4466d62a488e58f12aedacc1c depends: - mkl >=2026.1.0,<2027.0a0 constrains: - - blas 2.309 mkl - - libcblas 3.11.0 9*_mkl - - liblapack 3.11.0 9*_mkl - - liblapacke 3.11.0 9*_mkl + - blas 2.310 mkl + - libcblas 3.11.0 10*_mkl + - liblapack 3.11.0 10*_mkl + - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 - size: 67235 - timestamp: 1786059219098 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda - sha256: 65d0aaf1176761291987f37c8481be132060cc3dbe44b1550797bc27d1a0c920 - md5: 58aec7a295039d8614175eae3a4f8778 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlicommon >=1.1.0,<1.2.0a0 - size: 71243 - timestamp: 1756599708777 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda - sha256: 5097303c2fc8ebf9f9ea9731520aa5ce4847d0be41764edd7f6dee2100b82986 - md5: 444b0a45bbd1cb24f82eedb56721b9c4 + size: 67014 + timestamp: 1788077238349 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda + sha256: c739589318a1f8a88cd1b66d385176fd1ec2c4609e9f90d23d748be94b547e4e + md5: 8ef4beb3cb18e1a876b9af9a757cc1a5 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 82042 - timestamp: 1764017799966 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda - sha256: aa03aff197ed503e38145d0d0f17c30382ac1c6d697535db24c98c272ef57194 - md5: bf0ced5177fec8c18a7b51d568590b7c - depends: - - libbrotlicommon 1.1.0 hfd05255_4 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlidec >=1.1.0,<1.2.0a0 - size: 33430 - timestamp: 1756599740173 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda - sha256: 3239ce545cf1c32af6fffb7fc7c75cb1ef5b6ea8221c66c85416bb2d46f5cccb - md5: 450e3ae947fc46b60f1d8f8f318b40d4 + size: 82655 + timestamp: 1786622832371 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda + sha256: f4bdb7ec97c3122e531fc867efae5ec928b649d047aa70d42893f0d8eb110fd9 + md5: 10c479888ee4e960587967b2d0c8143a depends: - - libbrotlicommon 1.2.0 hfd05255_1 + - libbrotlicommon 1.2.0 hf02afa3_3 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 34449 - timestamp: 1764017851337 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda - sha256: a593cde3e728a1e0486a19537846380e3ce90ae9d6c22c1412466a49474eeeed - md5: 37f4669f8ac2f04d826440a8f3f42300 - depends: - - libbrotlicommon 1.1.0 hfd05255_4 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libbrotlienc >=1.1.0,<1.2.0a0 - size: 245418 - timestamp: 1756599770744 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda - sha256: 3226df6b7df98734440739f75527d585d42ca2bfe912fbe8d1954c512f75341a - md5: ccd93cfa8e54fd9df4e83dbe55ff6e8c + size: 34788 + timestamp: 1786622843818 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda + sha256: c5e638ea9704c94b316238b425a3439ba38d4d8fba81682842e6d7d464472848 + md5: cb5d08dc81f52e87c27914b5636cd995 depends: - - libbrotlicommon 1.2.0 hfd05255_1 + - libbrotlicommon 1.2.0 hf02afa3_3 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 252903 - timestamp: 1764017901735 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - build_number: 6 - sha256: 02b2a2225f4899c6aaa1dc723e06b3f7a4903d2129988f91fc1527409b07b0a5 - md5: 9e4bf521c07f4d423cba9296b7927e3c - depends: - - libblas 3.11.0 6_hf2e6a31_mkl - constrains: - - blas 2.306 mkl - - liblapacke 3.11.0 6*_mkl - - liblapack 3.11.0 6*_mkl - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libcblas >=3.11.0,<4.0a0 - size: 68221 - timestamp: 1774503722413 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - build_number: 8 - sha256: 2a5b6555b481df4603e44cba49a6ef727584fd2f3c5235dd4bcb3028fffbdfb5 - md5: 09f1d8e4d2675d34ad2acb115211d10c + size: 253894 + timestamp: 1786622854214 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda + build_number: 10 + sha256: 683c47e93178836bbbf0213241f4beb5454b60f75622040cbc73d1ef61df311f + md5: 7f19a81a13d7e167c42c096ff311a341 depends: - - libblas 3.11.0 8_h8455456_mkl - constrains: - - liblapacke 3.11.0 8*_mkl - - blas 2.308 mkl - - liblapack 3.11.0 8*_mkl - license: BSD-3-Clause - license_family: BSD - size: 68443 - timestamp: 1779859701498 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-9_h2a3cdd5_mkl.conda - build_number: 9 - sha256: 8c20714bbb85c8a109e9002e76c32be64a82d9867beb1a35a20c85258cc2b535 - md5: 9d1d0c22e9ed8c31ec2efc0d063d6f48 - depends: - - libblas 3.11.0 9_h8455456_mkl + - libblas 3.11.0 10_h8455456_mkl constrains: - - blas 2.309 mkl - - liblapack 3.11.0 9*_mkl - - liblapacke 3.11.0 9*_mkl + - blas 2.310 mkl + - liblapack 3.11.0 10*_mkl + - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 - size: 67587 - timestamp: 1786059232952 -- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_h2778606_6.conda - sha256: ddab96250fe45da60cf3d94df81d9d0988db157e5feffa29f8e15aea53b50b16 - md5: a49a33080bc3aa65784b563934b324a0 + size: 67515 + timestamp: 1788077250451 +- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda + sha256: f62f41ee933c27f89a0b5dc506b7fcba59675006c6ca742b40037f2e8df1e348 + md5: f177ba8f56631ec8d8cc1f799642e05d depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - llvm-openmp >=22.1.8 + - llvm-openmp >=23.1.0 - libxml2 - - libxml2-16 >=2.14.6 - - libzlib >=1.3.2,<2.0a0 + - libxml2-16 >=2.15.3 - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - purls: [] - run_exports: - weak: - - libclang13 >=22.1.8 - size: 34918557 - timestamp: 1786527644245 -- conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda - sha256: 1a2ead8255567347e6f9b2e941e07a1bcf3ce061d94d51f1b7d88089cd1a9552 - md5: 45a2834578a5f167258aa109af48b036 - depends: - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception - license_family: Apache - purls: [] run_exports: weak: - - libclang13 >=22.1.8 - size: 30487117 - timestamp: 1781848941933 + - libclang13 >=23.1.0 + size: 37510978 + timestamp: 1788037656780 - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 sha256: 75e60fbe436ba8a11c170c89af5213e8bec0418f88b7771ab7e3d9710b70c54e md5: cd4cc2d0c610c8cb5419ccc979f2d6ce @@ -23664,32 +14565,17 @@ packages: - vs2015_runtime >=14.16.27012 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 size: 25694 timestamp: 1633684287072 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-h8206538_0.conda - sha256: 384082b6f79454a89423f3e42ebba482719dac2221af33715970791d970c79b4 - md5: ed32b5c68abfae7702a700b636c84a91 - depends: - - krb5 >=1.22.2,<1.23.0a0 - - libssh2 >=1.11.1,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: curl - license_family: MIT - size: 403550 - timestamp: 1782296631338 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_4.conda - sha256: af17c990ec52b6aba29e052c438d7ac61bfd541d6d183a8f09aa4f2c532c9bab - md5: 5e9c5b83929cf7ab2c04121af771e7b5 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + sha256: bf5445ab8acfaccaf511d774f131cf0d99c5eef6b8def270e832ad26970d5011 + md5: 50861643cbe90dc7267feb7854b8efdf depends: - krb5 >=1.22.2,<1.23.0a0 - - libpsl >=0.23.0,<0.24.0a0 + - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 @@ -23697,30 +14583,11 @@ packages: - vc14_runtime >=14.44.35208 license: curl license_family: MIT - purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 - size: 404586 - timestamp: 1785500241182 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda - sha256: 024be133aed5f100c0b222761e747cc27a2bdf94af51947ad5f70e88cf824988 - md5: 099a1016d23baa4f41148a985351a7a8 - depends: - - krb5 >=1.21.3,<1.22.0a0 - - libssh2 >=1.11.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: curl - license_family: MIT - purls: [] - run_exports: - weak: - - libcurl >=8.9.1,<9.0a0 - size: 339298 - timestamp: 1722440239161 + size: 405126 + timestamp: 1787183759927 - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda sha256: af1cda21d4653f594fbef20aa4e1ff158a546902b3307ef8af9a9b44b43862d2 md5: e4e122e124676a49eebf241399ad8393 @@ -23730,27 +14597,11 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 size: 157828 timestamp: 1785908793271 -- conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda - sha256: 834e4881a18b690d5ec36f44852facd38e13afe599e369be62d29bd675f107ee - md5: e77030e67343e28b084fabd7db0ce43e - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libdeflate >=1.25,<1.26.0a0 - size: 156818 - timestamp: 1761979842440 - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda sha256: af03882afb7a7135288becf340c2f0cf8aa8221138a9a7b108aaeb308a486da1 md5: 25efbd786caceef438be46da78a7b5ef @@ -23761,7 +14612,6 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -23778,53 +14628,35 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT - purls: [] run_exports: {} size: 71631 timestamp: 1781203724164 -- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - sha256: 59d01f2dfa8b77491b5888a5ab88ff4e1574c9359f7e229da254cdfe27ddc190 - md5: 720b39f5ec0610457b725eb3f396219a - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libffi >=3.5.2,<3.6.0a0 - size: 45831 - timestamp: 1769456418774 -- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_0.conda - sha256: 2ea8d2fe7b84ca37653777e15ac1e7abd35f0c90d3efbe7f6c4de9b489606369 - md5: 92bdfc0e5012660892b0e0eaf3069a5c +- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda + sha256: 613e8077c5cca5df7fe84ade7d5050301bee3c6c4c450ffe61d34a349ab4f11c + md5: ceb38b0ba900847d8da4289c3c8e5708 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 - size: 50247 - timestamp: 1783521107166 -- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda - sha256: 035d0c67bf9f7a16f4a1764f420c120f1a995d071bb265fcc66ef688ef709d7b - md5: e45b52fb9a81c9e2708465a706e05952 + size: 49992 + timestamp: 1787753517346 +- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda + sha256: d794d7fddb6eea50e18a62fa27ab3819f40d51bad00b90cf4ca591fb0d4006c2 + md5: 740f99c3c91079c03874b809fedace1b depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL - purls: [] run_exports: {} - size: 8711 - timestamp: 1780934891782 -- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda - sha256: 0bbd19c9f7c4d0232b31892e6a4d1f82b8d19d1b84d89725f1f491b336447758 - md5: 4e4d54f9f98383d977ba56ef39ebf46d + size: 8742 + timestamp: 1786641045882 +- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda + sha256: cbc650854003e434d4ff6c7b1a2667e38a4242ad8a391a1c5ff89721624065ce + md5: 8157483eb7ed3fcba71aad3b50a97131 depends: - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 @@ -23833,43 +14665,25 @@ packages: - vc14_runtime >=14.44.35208 constrains: - freetype >=2.14.3 - license: GPL-2.0-only OR FTL - purls: [] - run_exports: {} - size: 340411 - timestamp: 1780934813224 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda - sha256: 80e80ef5e31b00b12539db3c5aaecde60dab91381abfc1060e323d5c3b016dce - md5: cc5d690fc1c629038f13c68e88e65f44 - depends: - - _openmp_mutex >=4.5 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - constrains: - - msys2-conda-epoch <0.0a0 - - libgcc-ng ==15.2.0=*_19 - - libgomp 15.2.0 h8ee18e1_19 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] + license: GPL-2.0-only OR FTL run_exports: {} - size: 821854 - timestamp: 1778273037795 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.1.0-h110b43a_1.conda - sha256: 9d12bae84edf872eefcddef28677b80d3550e1ef42b319122659aaf732c4ee53 - md5: 0b5cc3373b5f925934421259463397a4 + size: 340385 + timestamp: 1786641044865 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda + sha256: 32c0d1adcc96835d5f681c7e72b7f5e2e30088149239e002b8a6f581a072e1d2 + md5: 02793cf48b68687fee158a48c92850a6 depends: - _openmp_mutex >=4.5 - libwinpthread >=12.0.0.r4.gg4f2fc60ca constrains: - - libgomp 16.1.0 h8ee18e1_1 - - libgcc-ng ==16.1.0=*_1 + - libgcc-ng ==16.2.0=*_4 + - libgomp 16.2.0 h8ee18e1_4 - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: {} - size: 819817 - timestamp: 1785377219855 + size: 826694 + timestamp: 1787625780171 - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda sha256: 9ab562c718bd3fcef5f6189c8e2730c3d9321e05f13749a611630475d41207fc md5: 3a5b40267fcd31f1ba3a24014fe92044 @@ -23891,437 +14705,88 @@ packages: - xorg-libxpm >=3.5.17,<4.0a0 license: GD license_family: BSD - purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 size: 166711 timestamp: 1766331770351 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-3.9.2-h57928b3_7.conda - sha256: 84c190f70ff07a75cb9dd42e8a71e5f44846ce423cf68846f7000ead89ab19aa - md5: 08b7dfed6465467d50389c323e7b2a15 - depends: - - libgdal-core 3.9.2.* - - libgdal-fits 3.9.2.* - - libgdal-grib 3.9.2.* - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libgdal-jp2openjpeg 3.9.2.* - - libgdal-kea 3.9.2.* - - libgdal-netcdf 3.9.2.* - - libgdal-pdf 3.9.2.* - - libgdal-pg 3.9.2.* - - libgdal-postgisraster 3.9.2.* - - libgdal-tiledb 3.9.2.* - - libgdal-xls 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - - libgdal >=3.9.2,<3.10.0a0 - size: 423224 - timestamp: 1728298647854 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_4.conda - sha256: a80c324901a7f6f3c587aad6ee7cc6416a88887fdc55df3393bb3cec38851ff0 - md5: 633568a818a3b8a2cc81ed9adfb8fe16 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda + sha256: 9ab0d75d60952f422e59ead3b24d59871c8718b9179b0a708b86057d37d6a7ef + md5: 6f86ca570061dd7f251ab5554eb610a5 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libdeflate >=1.25,<1.26.0a0 - - libjxl >=0.12.0,<0.13.0a0 - - geos >=3.14.1,<3.14.2.0a0 - - libexpat >=2.8.1,<3.0a0 - - pcre2 >=10.47,<10.48.0a0 - - muparser >=2.3.5,<2.4.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - proj >=9.8.1,<9.9.0a0 - - xerces-c >=3.3.0,<3.4.0a0 - - libarchive >=3.8.9,<3.9.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - blosc >=1.21.6,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 + - libjxl >=0.12.0,<0.13.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 - libpng >=1.6.58,<1.7.0a0 - - lerc >=4.2.0,<5.0a0 - - libsqlite >=3.53.4,<4.0a0 - zstd >=1.5.7,<1.6.0a0 + - lerc >=4.2.0,<5.0a0 - libiconv >=1.18,<2.0a0 - - libcurl >=8.21.0,<9.0a0 + - libdeflate >=1.25,<1.26.0a0 - lz4-c >=1.10.0,<1.11.0a0 + - libcurl >=8.21.0,<9.0a0 + - libexpat >=2.8.1,<3.0a0 - liblzma >=5.8.3,<6.0a0 - - libjpeg-turbo >=3.2.0,<4.0a0 - - libkml >=1.3.0,<1.4.0a0 + - libsqlite >=3.53.4,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 - libxml2 - libxml2-16 >=2.14.6 + - xerces-c >=3.3.0,<3.4.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - muparser >=2.3.5,<2.4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - geos >=3.14.1,<3.14.2.0a0 + - proj >=9.8.1,<9.9.0a0 + - libarchive >=3.8.9,<3.9.0a0 constrains: - libgdal 3.12.4.* license: MIT license_family: MIT - purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 - size: 10741934 - timestamp: 1786090940755 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.9.2-h6b59ad6_1.conda - sha256: 1da89a5daf80c6b30a407c9c84e7d92f235abf79b594b3ca682ac29a6e5fe278 - md5: 79a9d077a3adaede1031b0b09368577a - depends: - - blosc >=1.21.6,<2.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - geotiff >=1.7.3,<1.8.0a0 - - lerc >=4.0.0,<5.0a0 - - libarchive >=3.7.4,<3.8.0a0 - - libcurl >=8.9.1,<9.0a0 - - libdeflate >=1.21,<1.26.0a0 - - libexpat >=2.6.2,<3.0a0 - - libiconv >=1.17,<2.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - libsqlite >=3.46.1,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libwebp-base >=1.4.0,<2.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - pcre2 >=10.44,<10.45.0a0 - - proj >=9.4.1,<9.5.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - xerces-c >=3.2.5,<3.3.0a0 - - xz >=5.2.6,<6.0a0 - - zstd >=1.5.6,<1.6.0a0 - constrains: - - libgdal 3.9.2.* - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libgdal-core >=3.9.2,<3.10.0a0 - size: 8023137 - timestamp: 1725466607341 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-fits-3.9.2-h0a0b71e_7.conda - sha256: 2c5b68e63880919d8f7c17b9928db2be118153506c99a70b74b19b5e163a0bbb - md5: d24f52a1eeec99436f3e7b548ae4d048 - depends: - - cfitsio >=4.4.1,<4.4.2.0a0 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 497868 - timestamp: 1728296632048 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-grib-3.9.2-hd2a089b_7.conda - sha256: 695af1fb1f52beeb2b0d3f0ea922c00c5373fbbdc300a0b227ccfc258c40c086 - md5: dd3572ebb1832021bb0bd1dc06b56043 - depends: - - libaec >=1.1.3,<2.0a0 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 678215 - timestamp: 1728296811437 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf4-3.9.2-h430f241_7.conda - sha256: 8a0f9532574fd7cdb26a3b79c505c26dd04317bd742fd9587ee508e773c527ab - md5: 720ff75366c6e31259ccd53b73b27abf - depends: - - hdf4 >=4.2.15,<4.2.16.0a0 - - libaec >=1.1.3,<2.0a0 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 562915 - timestamp: 1728296979645 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf5-3.9.2-had131a1_7.conda - sha256: f64a98ee37ed7eb3fbdd6de402190403b4b28d893d50b08bd92262e847effae4 - md5: 7e62763a83b3730f5058daefb4962053 - depends: - - hdf5 >=1.14.3,<1.14.4.0a0 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 613800 - timestamp: 1728297167598 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-jp2openjpeg-3.9.2-hed4c6cb_7.conda - sha256: 88f40b96af8df6d164ead2a138fc3ead313821f127cfcaadf60059d07f54c62a - md5: 4135def2658a84e8739677a39eadd200 - depends: - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - openjpeg >=2.5.2,<3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 498132 - timestamp: 1728297319442 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-kea-3.9.2-h95b1a77_7.conda - sha256: eb0c503103eaf5821564ab47d0dfcc23481037115cb988d7af3c7963fadbb9c4 - md5: a20cfdcc279d52808b1a53296bfd308c - depends: - - hdf5 >=1.14.3,<1.14.4.0a0 - - kealib >=1.5.3,<1.6.0a0 - - libgdal-core >=3.9 - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 518891 - timestamp: 1728298457098 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-netcdf-3.9.2-h55e78d3_7.conda - sha256: 0e74029976a25c4088d7aa5967f0cd81e7963aae4633fd01bae9a63df5ad799e - md5: 55406e979f268a570952c49fe941eb04 - depends: - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libgdal-core >=3.9 - - libgdal-hdf4 3.9.2.* - - libgdal-hdf5 3.9.2.* - - libkml >=1.3.0,<1.4.0a0 - - libnetcdf >=4.9.2,<4.9.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 667045 - timestamp: 1728298642403 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-pdf-3.9.2-ha1c78db_7.conda - sha256: 0ae547978fb81ce421257dd2cff84e83cc78e921f0bd5c99ceeef167164188fa - md5: 27fb85b4f988b64aa9ab0e926ddd6b33 - depends: - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - poppler - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 626278 - timestamp: 1728297526707 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-pg-3.9.2-hfaa227e_7.conda - sha256: 73a4bfb87b58d74527142887dbc941e4abffd9a6a377115d47ea5be79303da8c - md5: c37be85d5b9a3b1fc94a99d6f8327ab1 - depends: - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - postgresql - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 532532 - timestamp: 1728297702019 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-postgisraster-3.9.2-hfaa227e_7.conda - sha256: eb60e16b2727fa20288c8c5d01b168df38476904dd0c3ea3d92ae96b3579b7d9 - md5: b24ac84c98145ebf555950076c0417da - depends: - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - libpq >=17.0,<18.0a0 - - postgresql - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 505346 - timestamp: 1728297893312 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-tiledb-3.9.2-hb8b5d01_2.conda - sha256: 3d9b00940e17134d0c16c6406b68d7b9afa33e49a8c695f11eb0da7d467f4718 - md5: acc3612fa26fab59d18d4075d12c15b0 - depends: - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - tiledb >=2.26.0,<2.27.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 628075 - timestamp: 1726098183036 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-xls-3.9.2-hd0e23a6_7.conda - sha256: 44c5c2fd75a5b34fbd71316bef26018023376adaaf2145a8277f906899184eb2 - md5: c7366f3ec5ce24208987333a077b42f6 - depends: - - freexl >=2.0.0,<3.0a0 - - libgdal-core >=3.9 - - libkml >=1.3.0,<1.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 466632 - timestamp: 1728298290371 -- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.0-h7025463_0.conda - sha256: 0b4f9581e2dba58bc38cb00453e145140cf6230a56887ff1195e63e2b1e3f1c2 - md5: ea8df8a5c5c7adf4c03bf9e3db1637c3 - depends: - - libffi >=3.4,<4.0a0 - - libiconv >=1.18,<2.0a0 - - libintl >=0.22.5,<1.0a0 - - libzlib >=1.3.1,<2.0a0 - - pcre2 >=10.44,<10.45.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - glib 2.84.0 *_0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libglib >=2.84.0,<3.0a0 - size: 3842095 - timestamp: 1743039211561 -- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.1-h7ce1215_2.conda - sha256: f61277e224e9889c221bb2eac0f57d5aeeb82fc45d3dc326957d251c97444f7c - md5: 5fb838786a8317ebb38056bbe236d3ff + size: 10741883 + timestamp: 1786825244360 +- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + sha256: 001620bac27dbf6daa25e5cd52970749fe2bc02a26649b0ad516d26609f6bcae + md5: e40bcfee79dbfda3eff751ed098b6ad3 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - pcre2 >=10.47,<10.48.0a0 - libiconv >=1.18,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libintl >=0.22.5,<1.0a0 - - libffi >=3.5.2,<3.6.0a0 - constrains: - - glib >2.66 - license: LGPL-2.1-or-later - size: 4522891 - timestamp: 1778508851933 -- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_1.conda - sha256: fd94edec4f945c82ba6b983aae2bec21dd08209a5b387fb7a713534bb3db51af - md5: d8b3236ab45b58a0c4f4aa0a286cee13 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - pcre2 >=10.47,<10.48.0a0 - libffi >=3.7.0,<3.8.0a0 - libintl >=0.22.5,<1.0a0 - - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.2,<2.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later - purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4518977 - timestamp: 1786457672418 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda - sha256: 4dc958ced2fc7f42bc675b07e2c9abe3e150875ffdf62ca551d94fc6facf1fd7 - md5: f1147651e3fdd585e2f442c0c2fc8f2d - depends: - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - constrains: - - msys2-conda-epoch <0.0a0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - run_exports: - strong: - - _openmp_mutex >=4.5 - - libgomp >=15.2.0 - size: 664640 - timestamp: 1778272979661 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.1.0-h8ee18e1_1.conda - sha256: ab1b70f492c859e2e76bb2c2a0725880f5ed5aa0e142a7650b0b78d6e64487de - md5: baa80f69f3a43e4ec7e1cfb3addeae56 + size: 4519014 + timestamp: 1787884093942 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda + sha256: 9ef1a22fb50b31fbf9c01cf709e8f55b60fd3c02401d150be4a55943f9e3832f + md5: 91189f0bc9ff21f385fcda6232346612 depends: - libwinpthread >=12.0.0.r4.gg4f2fc60ca constrains: - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL - purls: [] run_exports: strong: - _openmp_mutex >=4.5 - - libgomp >=16.1.0 - size: 682053 - timestamp: 1785377156260 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.28.0-h5e7cea3_0.conda - sha256: 30c5eb3509d0a4b5418e58da7cda7cfee7d06b8759efaec1f544f7fcb54bcac0 - md5: 78a31d951ca2e524c6c223d865edd7ae - depends: - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgrpc >=1.62.2,<1.63.0a0 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - libgoogle-cloud 2.28.0 *_0 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud >=2.28.0,<2.29.0a0 - size: 14358 - timestamp: 1723371187491 + - libgomp >=16.2.0 + size: 688168 + timestamp: 1787625720312 - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda sha256: 7caeceff8cc78b6ccdf416c3950ee7c86390be9c43969e6f0df11ffe46db8d32 md5: 4957e887b8ff6e7c1108e217b879ebc3 @@ -24339,32 +14804,11 @@ packages: - libgoogle-cloud 3.7.0 *_0 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud >=3.7.0,<3.8.0a0 size: 17240 timestamp: 1784213003080 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-2.28.0-he5eb982_0.conda - sha256: 6318a81a6ef2a72b70c2ddfdadaa5ac79fce431ffa1125e7ca0f9286fa9d9342 - md5: c60153238c7fcdda236b51248220c4bb - depends: - - libabseil - - libcrc32c >=1.1.2,<1.2.0a0 - - libcurl - - libgoogle-cloud 2.28.0 h5e7cea3_0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - size: 14259 - timestamp: 1723371607596 - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda sha256: 43f9ab1e9055f7fa4981a5da7b359901cda8fc5607e1c489b69152ce23adc3c4 md5: 58728b379859ceb2d41642f49411e440 @@ -24379,37 +14823,11 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.7.0,<3.8.0a0 size: 17200 timestamp: 1784213272078 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.62.2-h5273850_0.conda - sha256: 08794bf5ea0e19ac23ed47d0f8699b5c05c46f14334b41f075e53bac9bbf97d8 - md5: 2939e4b5baecfeac1e8dee5c4f579f1a - depends: - - c-ares >=1.28.1,<2.0a0 - - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libre2-11 >=2023.9.1 - - libzlib >=1.2.13,<2.0.0a0 - - openssl >=3.2.1,<4.0a0 - - re2 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - grpc-cpp =1.62.2 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libgrpc >=1.62.2,<1.63.0a0 - size: 16097674 - timestamp: 1713392821679 - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda sha256: 56db0effd12badb6075a2b5877ed72c06e61211b8c9a42843711c7b67a3324e1 md5: 343241db44cbda08b4b41689608a07af @@ -24429,15 +14847,14 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 size: 11674380 timestamp: 1783586253244 -- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.3.0-h03b5201_0.conda - sha256: 43e09008ce97186b41cf8b8a5ac455005b6c5e7586b36368cfe98e1f4353f6af - md5: 3d81338de690051645d0aabeb2631441 +- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda + sha256: 08713208fd7e2e6f2ccde1fc09765f2585d5cc809d187865fd9190ddad271f3d + md5: 4618efa4303fdda115da50b8090d73c6 depends: - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.15,<2.0a0 @@ -24453,54 +14870,9 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 1041066 - timestamp: 1785770469568 -- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-devel-14.3.0-h03b5201_0.conda - sha256: 54fea2e201eeb6940ff050ff4847c1021b69ad984881369470f1b2f0446c6340 - md5: caa9cfa4e495ec225e5ccde3cab32a88 - depends: - - cairo >=1.18.4,<2.0a0 - - freetype - - glib - - graphite2 >=1.3.15,<2.0a0 - - icu >=78.3,<79.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz 14.3.0 h03b5201_0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libharfbuzz >=14.3.0 - size: 324303 - timestamp: 1785770503649 -- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda - sha256: 2fb437b82912c74b4869b66c601d52c77bb3ee8cb4812eab346d379f1c823225 - md5: e6298294e7612eccf57376a0683ddc80 - depends: - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - libxml2 >=2.13.8,<2.14.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libhwloc >=2.12.1,<2.12.2.0a0 - size: 2412139 - timestamp: 1752762145331 + size: 1053822 + timestamp: 1787795353946 - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda sha256: 2ee12e37223dfcd0acd050c80a91150c482b6e2899198521e1800dce66662467 md5: 6a01c986e30292c715038d2788aa1385 @@ -24513,81 +14885,48 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libhwloc >=2.13.0,<2.13.1.0a0 size: 2396128 timestamp: 1770954127918 -- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_0.conda - sha256: ade3e4e8e09051bad9c75ea9e96b09e3c635216c409c87c369e6f8566a528cb1 - md5: 430378e206cf5a148fc8da7603b2466e +- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda + sha256: a6dc4360c00eca941d31ad94e8e4a102f38ce9de1668fac83118f1ad457577d2 + md5: 972a4e44d5a8c3447769414fd3518d5d depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 OR BSD-3-Clause - purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 - size: 564121 - timestamp: 1784325614001 -- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 - md5: 64571d1dd6cdcfa25d0664a5950fdaa2 + size: 563336 + timestamp: 1787282448113 +- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda + sha256: 35e04e3ddac7720fc7c550a1c9f998604299d6a7bd1f3ec9b2825d825061daa2 + md5: a8a2abdf0f901bc4779d9b7be0845921 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only - purls: [] - run_exports: - weak: - - libiconv >=1.18,<2.0a0 - size: 696926 - timestamp: 1754909290005 -- conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda - sha256: c7e4600f28bcada8ea81456a6530c2329312519efcf0c886030ada38976b0511 - md5: 2cf0cf76cc15d360dfa2f17fd6cf9772 - depends: - - libiconv >=1.17,<2.0a0 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libintl >=0.22.5,<1.0a0 - size: 95568 - timestamp: 1723629479451 -- conda: https://conda.anaconda.org/conda-forge/win-64/libintl-devel-0.22.5-h5728263_3.conda - sha256: be1f3c48bc750bca7e68955d57180dfd826d6f9fa7eb32994f6cb61b813f9a6a - md5: 7537784e9e35399234d4007f45cdb744 - depends: - - libiconv >=1.17,<2.0a0 - - libintl 0.22.5 h5728263_3 - license: LGPL-2.1-or-later - purls: [] - run_exports: - weak: - - libintl >=0.22.5,<1.0a0 - size: 40746 - timestamp: 1723629745649 -- conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda - sha256: 698d57b5b90120270eaa401298319fcb25ea186ae95b340c2f4813ed9171083d - md5: 25a127bad5470852b30b239f030ec95b - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - jpeg <0.0.0a - license: IJG AND BSD-3-Clause AND Zlib - purls: [] run_exports: weak: - - libjpeg-turbo >=3.1.4.1,<4.0a0 - size: 842806 - timestamp: 1775962811457 + - libiconv >=1.18,<2.0a0 + size: 694899 + timestamp: 1787033851701 +- conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda + sha256: 85b50be2209f3b8a873830ec9894477d260f4c7ba9540e65959f5812bf7219f8 + md5: 36d6e0045173974521fabd42bd5033d6 + depends: + - libiconv >=1.18,<2.0a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - libintl >=0.22.5,<1.0a0 + size: 96669 + timestamp: 1787841116808 - conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda sha256: df78ab4c0eecb3dd9331898f96baeed8e5ca1c363346332517abeb0b614b9a53 md5: fc2c23bacefd1e733f1e1d6cd3b2aaeb @@ -24598,15 +14937,14 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib - purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 size: 990125 timestamp: 1785896494014 -- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_1.conda - sha256: 141310ec747808be57ba9e7501fcfab75b260803233dd9ba818fb85f6080aad9 - md5: adb20d060513fe9655d28a780bafafaa +- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_2.conda + sha256: c0381a39fd601430ebe7e2686543f5b1685d2374d8bb7be0aabf4d4705327efa + md5: cf415a2296a1d862e93559645c91fd30 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -24616,12 +14954,11 @@ packages: - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 - size: 1199695 - timestamp: 1783146089449 + size: 1199700 + timestamp: 1786691396735 - conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda sha256: 6b2c6506dc7d7bb3bc4128d575e4ae6c2cf18d3f9828a4be331e0b5e49edf108 md5: b44e0d9eb84c6c086d809787aab0a1da @@ -24634,76 +14971,25 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 1668156 timestamp: 1777026660593 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda - build_number: 6 - sha256: 2e6ac39e456ba13ec8f02fc0787b8a22c89780e24bd5556eaf642177463ffb36 - md5: 7e9cdaf6f302142bc363bbab3b5e7074 - depends: - - libblas 3.11.0 6_hf2e6a31_mkl - constrains: - - blas 2.306 mkl - - liblapacke 3.11.0 6*_mkl - - libcblas 3.11.0 6*_mkl - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - liblapack >=3.11.0,<3.12.0a0 - size: 80571 - timestamp: 1774503757128 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - build_number: 8 - sha256: 44999ed04bc0a56de44ee0ac8bd5b3702efd411a8b29491c0e3d3deb8619c94e - md5: d584799b920ecae9b75a2b70743a3de7 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda + build_number: 10 + sha256: d7074faabf4ecace4584801d5f41c71be1e5cf52a659eab2604a96c5a8f437a9 + md5: 3a442b27a0ca4153d07b3ceb12fd486f depends: - - libblas 3.11.0 8_h8455456_mkl - constrains: - - libcblas 3.11.0 8*_mkl - - liblapacke 3.11.0 8*_mkl - - blas 2.308 mkl - license: BSD-3-Clause - license_family: BSD - size: 81027 - timestamp: 1779859714698 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-9_hf9ab0e9_mkl.conda - build_number: 9 - sha256: 62d0a7a70ee13c554d5d7ff94a2ba758c4111439822dcc81324dd4cfaf576071 - md5: bee6f13ab945c4034bdd0f722ad14dd5 - depends: - - libblas 3.11.0 9_h8455456_mkl + - libblas 3.11.0 10_h8455456_mkl constrains: - - blas 2.309 mkl - - libcblas 3.11.0 9*_mkl - - liblapacke 3.11.0 9*_mkl + - blas 2.310 mkl + - libcblas 3.11.0 10*_mkl + - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 - size: 79963 - timestamp: 1786059243440 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - sha256: d636d1a25234063642f9c531a7bb58d84c1c496411280a36ea000bd122f078f1 - md5: 8f83619ab1588b98dd99c90b0bfc5c6d - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - xz 5.8.3.* - license: 0BSD - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 106486 - timestamp: 1775825663227 + size: 79490 + timestamp: 1788077261256 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda sha256: d36c4a1e1f80fd08e18a407e03622ff2f34dfdd022da6488ad19603dea19e6d5 md5: 880a0c8549479b198af21ba5dc49b109 @@ -24714,138 +15000,105 @@ packages: constrains: - xz 5.8.3.* license: 0BSD - purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 size: 105809 timestamp: 1786348717883 -- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-devel-5.8.3-hfd05255_0.conda - sha256: 875f0535e135b9949720b80057508e14a9cb9351d4117760dacc6296f5b5704a - md5: 7845201435d0c7c0a02269c2742da1cc - depends: - - liblzma 5.8.3 hfd05255_0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: 0BSD - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 130960 - timestamp: 1775825690054 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.8.1-h06825f5_0.conda - sha256: 302b4099ad91b06be78a841b1d0eaeae59ac6baeceadc6479d19529e039d87bb - md5: 112762877fd3a9e761c9bd4feee68928 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-2.9.0-py314h08176f6_0.conda + sha256: a95ff44e76be486507973dda39600f8309194f61a7b80d9ba65967aa90077de5 + md5: ab365478ee65fbdb478e51c855fa481b depends: - cpp-expected >=1.3.1,<1.3.2.0a0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - reproc >=14.2.7.post0,<14.3.0a0 + - fmt >=12.1.0,<12.2.0a0 - spdlog >=1.17.0,<1.18.0a0 - - libcurl >=8.20.0,<9.0a0 - - libsolv >=0.7.39,<0.8.0a0 - - libmsgpack-c >=6.1.0,<7.0a0 - - openssl >=3.5.6,<4.0a0 - - reproc-cpp >=14.2,<15.0a0 - - simdjson >=4.6.4,<4.7.0a0 - nlohmann_json-abi ==3.12.0 - - fmt >=12.1.0,<12.2.0a0 - - reproc >=14.2,<15.0a0 - - libarchive >=3.8.7,<3.9.0a0 - - zstd >=1.5.7,<1.6.0a0 + - simdjson >=4.6.6,<4.7.0a0 - yaml-cpp >=0.8.0,<0.9.0a0 + - zstd >=1.5.7,<1.6.0a0 + - reproc-cpp >=14.2.7.post0,<14.3.0a0 + - libmsgpack-c >=6.1.0,<7.0a0 + - libsolv >=0.7.39,<0.8.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - libcurl >=8.21.0,<9.0a0 + - openssl >=3.5.7,<4.0a0 license: BSD-3-Clause license_family: BSD - size: 5316504 - timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.8.1-h7dcf005_0.conda - sha256: 64e869c54e328ca5b184932c43fad9a4172975fdf8a088da89f8182f9a7b6c9a - md5: c3424255d9c261f649e6d1851102b458 + run_exports: + weak: + - libmamba >=2.9.0,<2.10.0a0 + size: 5416175 + timestamp: 1786125710299 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmamba-spdlog-2.9.0-h9ac7702_0.conda + sha256: 9b43a18a720163f746b26c1178a198cb5a8a829ecdc3ac8d66ad5b4116b0e5ab + md5: 42e4a919086813b2c14aaed13ec73aec depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libmamba >=2.8.1,<2.9.0a0 + - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD - size: 18083 - timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.8.1-py314h40f010b_0.conda - sha256: 5106c6952325c512715aadca45dd93febac92008cf907207c97ff4bbdb36f913 - md5: 23d746a541606c9142fc7518fadcda2f + run_exports: {} + size: 18480 + timestamp: 1786125710299 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmambapy-2.9.0-py314h5301c5d_0.conda + sha256: cc735d3f77c5cf2f8b2263c41f60fd2365015a7eddecdab95aa913f7e32fc3a4 + md5: 41fe4e717b819c816fe498fb37317ab7 depends: - python - - libmamba ==2.8.1 h06825f5_0 - - libmamba-spdlog ==2.8.1 h7dcf005_0 + - libmamba ==2.9.0 py314h08176f6_0 + - libmamba-spdlog ==2.9.0 h9ac7702_0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - nlohmann_json-abi ==3.12.0 - - zstd >=1.5.7,<1.6.0a0 - - spdlog >=1.17.0,<1.18.0a0 - - openssl >=3.5.6,<4.0a0 - - libmamba >=2.8.1,<2.9.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 - pybind11-abi ==11 + - openssl >=3.5.7,<4.0a0 + - spdlog >=1.17.0,<1.18.0a0 - python_abi 3.14.* *_cp314 + - yaml-cpp >=0.8.0,<0.9.0a0 + - libmamba >=2.9.0,<2.10.0a0 - fmt >=12.1.0,<12.2.0a0 + - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD - size: 594639 - timestamp: 1780948946071 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - sha256: 40dcd0b9522a6e0af72a9db0ced619176e7cfdb114855c7a64f278e73f8a7514 - md5: e4a9fc2bba3b022dad998c78856afe47 + run_exports: + weak: + - libmambapy >=2.9.0,<2.10.0a0 + size: 596336 + timestamp: 1786125710299 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda + sha256: f07e451de3db1836b87f7aedf95c8e65cdb06c0e6105329ba24bb5f7b5c75e2a + md5: 5ae92fd6614edd024576e14069d7ad4c depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: {} - size: 89411 - timestamp: 1769482314283 -- conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_6.conda - sha256: 30fba0b5c6c6a82a7e683e14b39f34659dd23552d24a0911707e9a1ab16e7e0e - md5: 44307023d8a49869094cb15ddef99715 + size: 89109 + timestamp: 1786650384519 +- conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda + sha256: 9b81aaa83ae15710a3466053bdf9090b1a4ba9ec40c8231b90f0efd5e3d02855 + md5: 7928697b653d56d99504b533b02f3c64 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 constrains: - - msgpack-c ==6.1.0 *_6 + - msgpack-c ==6.1.0 *_7 license: BSL-1.0 - size: 40694 - timestamp: 1770807535968 -- conda: https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_h92078aa_114.conda - sha256: 111fb98bf02e717c69eb78388a5b03dc7af05bfa840ac51c2b31beb70bf42318 - md5: 819507db3802d9a179de4d161285c22f - depends: - - blosc >=1.21.5,<2.0a0 - - bzip2 >=1.0.8,<2.0a0 - - hdf4 >=4.2.15,<4.2.16.0a0 - - hdf5 >=1.14.3,<1.14.4.0a0 - - libaec >=1.1.3,<2.0a0 - - libcurl >=8.8.0,<9.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzip >=1.10.1,<2.0a0 - - libzlib >=1.2.13,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zlib - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] run_exports: weak: - - libnetcdf >=4.9.2,<4.9.3.0a0 - size: 624793 - timestamp: 1717672198533 + - libmsgpack-c >=6.1.0,<7.0a0 + size: 40862 + timestamp: 1786189315611 - conda: https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda sha256: 453e42fcdc1770ef1e2f3f159f5b333c8b836aeaa5e141642fb2e34d80c7e5c4 md5: 300e6113eae2004627a2c2233bbd5a55 @@ -24863,7 +15116,6 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -24874,29 +15126,9 @@ packages: md5: 85e4342d7e798c99a828d59303909d09 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 436532 timestamp: 1783133530257 -- conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-17.0.0-ha915800_13_cpu.conda - build_number: 13 - sha256: 8cf6d193600b4dd6cb1a8fbdea168ef6bddbf8ca1ee57d08ce6992df71a62670 - md5: 30b08e672c5dcd827ce7b44f01f4821e - depends: - - libarrow 17.0.0 h29daf90_13_cpu - - libthrift >=0.20.0,<0.20.1.0a0 - - openssl >=3.3.1,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libparquet >=17.0.0,<17.1.0a0 - size: 805417 - timestamp: 1725215420059 - conda: https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda build_number: 3 sha256: 83398d0ef3b752f94fc99e766a8febeab0547993e55803a267fe618d697eba92 @@ -24910,65 +15142,28 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 size: 968008 timestamp: 1784543362314 -- conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda - sha256: 218913aeee391460bd0e341b834dbd9c6fa6ae0a4276c0c300266cc99a816a28 - md5: 52f1280563f3b48b5f75414cd2d15dd1 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda + sha256: 8c49c32adf3ba2c59783630b82377f54ab72204ea99e2bafc90a47a8e25c1032 + md5: 5aa7348e73691187c81d51616f17e48a depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement - purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 - size: 385227 - timestamp: 1776315248638 -- conda: https://conda.anaconda.org/conda-forge/win-64/libpq-17.2-h7ec079e_0.conda - sha256: 96d52202fabd7f98ec04a868699b50691d5cf2593c94f9df6eb5934909f33742 - md5: cf036705ab68a8652529b944de7f86fe - depends: - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - openssl >=3.4.0,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 3882352 - timestamp: 1732205050714 -- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-4.25.3-h47a098d_1.conda - sha256: 6412e1b25d14187a4a9ccd62c27fb163621aa4c4dd5f8e97e2aaabed5e61598e - md5: 2ab67bf04b060ed5af5bc6999f1d6b31 - depends: - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libprotobuf >=4.25.3,<4.25.4.0a0 - size: 5487058 - timestamp: 1727162016965 -- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_2.conda - sha256: 0901c04727c76556a0afb1bf3f32e4ba9f4f8b50e8a6f96d02085f47875b70e0 - md5: 86341febfb82a8beb84058167f41c3d5 + size: 385462 + timestamp: 1786616543374 +- conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda + sha256: ad39d17df4384bacc106e18a3446ac18a4eab57f4f2da15fa58cdc63cb61957b + md5: 4d0c07f8d96e268fc4221c19e51fa165 depends: - libabseil * cxx17* - libabseil >=20260526.0,<20260527.0a0 @@ -24978,15 +15173,14 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 - size: 7373575 - timestamp: 1783170105520 -- conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_0.conda - sha256: 1f92f663cb283c4fd90db8a8a3851a4f17f1e9778746adcbfb7736fde8e5455f - md5: 16ccdcddc776dbb14cd866ab480561ff + size: 7511658 + timestamp: 1787658008053 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda + sha256: e53fe3b09f82b59b644264133d6d148ac31bb5451b7583cff55690bf038f2f62 + md5: 39cdf8c4f59e4d253cfa8091c8b3d7de depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -24994,12 +15188,23 @@ packages: - icu >=78.3,<79.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 - size: 73424 - timestamp: 1786443553911 + size: 73511 + timestamp: 1786970749988 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + build_number: 104 + sha256: 4ffd32fd57d21d4aef34ce82dd7b5ab0248260d0481a26fff64a411f7ea9858b + md5: 83748d09d722103f7aa891ecfa13d672 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Python-2.0 + run_exports: {} + size: 51176 + timestamp: 1788162060329 - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda sha256: 1bc52f781355e233411a7aaebbb155d9fd53efa2a1fc6c621250833d80cc5ab1 md5: df92be5685f712989830bdb7ee39383a @@ -25013,31 +15218,11 @@ packages: - fribidi >=1.0.16,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 size: 33471 timestamp: 1784850324004 -- conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2023.09.01-hf8d8778_2.conda - sha256: 04331dad30a076ebb24c683197a5feabf4fd9be0fa0e06f416767096f287f900 - md5: cf54cb5077a60797d53a132d37af25fc - depends: - - libabseil * cxx17* - - libabseil >=20240116.1,<20240117.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - re2 2023.09.01.* - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - size: 256561 - timestamp: 1708947458481 - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda sha256: 255d32d01d72d7d89ab5c60c20b124216e0731428af350e40ed0e001d249bcb5 md5: 52d096d535c65d26ffb4b84a4bba2aa1 @@ -25051,28 +15236,11 @@ packages: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 size: 266747 timestamp: 1781312690043 -- conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h6c42fcb_16.conda - sha256: 417d468a42860bee6d487a39603740c3650fb7eae03b694a9bddada9ef5d1017 - md5: 4476d717f460b45f5033206bbb84f3f5 - depends: - - geos >=3.12.2,<3.12.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: GPL-2.0-or-later - license_family: GPL - purls: [] - run_exports: - weak: - - librttopo >=1.1.0,<1.2.0a0 - size: 407420 - timestamp: 1720347953921 - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda sha256: d8d091afa0e5009b71e9a931da862a60104b75ca13c3021edf036620eebaf2d0 md5: 7eeb5aed49853f8b3e1ca0463ef55a8e @@ -25083,29 +15251,27 @@ packages: - vc14_runtime >=14.44.35208 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 size: 403088 timestamp: 1761671197546 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_1.conda - sha256: de45b71224da77a1c3a7dd48d8885eb957c9f05455d4f0828463293e7144330f - md5: 7d5abf7ca1bd00b43d273f44d93d05dc +- conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_2.conda + sha256: 7dc8d243cc8bcd12d1666640664288822eda5d30411a0b22ff05090bbac4c32b + md5: 2a001157df8205a7785ea69dd3270a8f depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: ISC - purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 - size: 280234 - timestamp: 1779164124739 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_0.conda - sha256: 4a526d8561cecee9f3969c4a872e6027bf3d50deaaf03a407c0cf61aa201eda5 - md5: c2a7328ec05a3895280f291db51a6e5d + size: 280204 + timestamp: 1787225747847 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_1.conda + sha256: e42da29bf02c95828216e0719de48f4d466779b53c7b0ca233a93c949c51147d + md5: 6d41739b93a9f1870d7031a8e2ca2ee3 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -25113,12 +15279,11 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 - size: 469479 - timestamp: 1780057217183 + size: 469787 + timestamp: 1786955834309 - conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda sha256: 951ae336443a7a568032f928bcd62f549f6a55a6ae9200237c3737e1e00720c3 md5: effdac4d017409986c5861d19775eef1 @@ -25140,110 +15305,40 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA - purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 size: 8132506 timestamp: 1772594244069 -- conda: https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-hab0cb6d_9.conda - sha256: 2b58e62603334b7d3951b93cdee9dd1fe3cd3c18aaafa65ea0f132f780adeb6e - md5: 934f10287da9c46f761abf0ee5f88dd3 - depends: - - freexl >=2 - - freexl >=2.0.0,<3.0a0 - - geos >=3.12.2,<3.12.3.0a0 - - librttopo >=1.1.0,<1.2.0a0 - - libsqlite >=3.46.0,<4.0a0 - - libxml2 >=2.12.7,<2.14.0a0 - - libzlib >=1.3.1,<2.0a0 - - proj >=9.4.1,<9.5.0a0 - - sqlite - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zlib - license: MPL-1.1 - license_family: MOZILLA - purls: [] - run_exports: - weak: - - libspatialite >=5.1.0,<5.2.0a0 - size: 8283487 - timestamp: 1722338203533 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - sha256: 4cd81319dcc58fb758da20a6d5595950c021adc2c18d7cffeadcfb590529629f - md5: df294e7f9f24a6063f0e226f4d028fda - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: blessing - size: 1313306 - timestamp: 1780574491977 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.3-hf5d6505_0.conda - sha256: 692dfb73a22c873656d5e393b8f1e2b019a3c8a6486c97cb6900552e64e38c25 - md5: 051f1b2228e7517a2ef8cca5146c8967 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda + sha256: 0a45d7c0f20146fff787a106f8fa187872e309c70975ff8f0936e188914c26ad + md5: a72d495965b144bb7da033642d389047 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing - purls: [] run_exports: weak: - - libsqlite >=3.53.3,<4.0a0 - size: 1315909 - timestamp: 1782519131898 -- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_0.conda - sha256: 62e1c45ec71ab2e5deeeb0e47e7df6a609991e91d46348f16df50c68fee145c8 - md5: ca0d59f40a02a15e9b5d0ff8db0f85e3 + - libsqlite >=3.53.4,<4.0a0 + size: 1314919 + timestamp: 1787051140039 +- conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda + sha256: 1097b08429b4f53f5751a32c6d99a083d227ca7f7812c0b239eea124cec073a0 + md5: a21dd9ca39e74910bb96989feb916420 depends: + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.4,<4.0a0 - size: 1313790 - timestamp: 1785016158097 -- conda: https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda - sha256: cbdf93898f2e27cefca5f3fe46519335d1fab25c4ea2a11b11502ff63e602c09 - md5: 9dce2f112bfd3400f4f432b3d0ac07b2 - depends: - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.0,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 - size: 292785 - timestamp: 1745608759342 -- conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.20.0-hbe90ef8_1.conda - sha256: 77f92cbacb886f671fdf0bc2fac13f423ba442d0c3171ce3e573ed05f5c8980e - md5: e9f49c00773250da4f622694b7f83f25 - depends: - - libevent >=2.1.12,<2.1.13.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.1,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: - weak: - - libthrift >=0.20.0,<0.20.1.0a0 - size: 612714 - timestamp: 1724653005481 + size: 295149 + timestamp: 1786713186180 - conda: https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda sha256: 7ffb48755c4fc4a7cca454e4afea286e4fb47e50e153df1b006b14691f0f43d0 md5: 42856184560e5cf901551fd414ad25c1 @@ -25256,39 +15351,18 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 size: 634136 timestamp: 1777019194906 -- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda - sha256: f1b8cccaaeea38a28b9cd496694b2e3d372bb5be0e9377c9e3d14b330d1cba8a - md5: 549845d5133100142452812feb9ba2e8 - depends: - - lerc >=4.0.0,<5.0a0 - - libdeflate >=1.25,<1.26.0a0 - - libjpeg-turbo >=3.1.0,<4.0a0 - - liblzma >=5.8.1,<6.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - zstd >=1.5.7,<1.6.0a0 - license: HPND - purls: [] - run_exports: - weak: - - libtiff >=4.7.1,<4.8.0a0 - size: 993166 - timestamp: 1762022118895 -- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_0.conda - sha256: ec6d66308a6d6abaf3225f2f185113e6172e77eb0fa8622af982d7a5d6d47a2c - md5: e83f459471905a04ebe15e21d063c49d +- conda: https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda + sha256: 3575a092e3e52625a1767804a4ebd321becaadf61b63dcd6735ebb88c0b3c359 + md5: 970dbe48f843e7fbd179a9b6c22f865a depends: - - lerc >=4.1.0,<5.0a0 + - lerc >=4.2.0,<5.0a0 - libdeflate >=1.25,<1.26.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 @@ -25296,12 +15370,11 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: HPND - purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 - size: 1014598 - timestamp: 1783085017197 + size: 1014211 + timestamp: 1787755773611 - conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda sha256: 5d82af0779eab283416240da792a0d2fe4f8213c447e9f04aeaab1801468a90c md5: 5f34fcb6578ea9bdbfd53cc2cfb88200 @@ -25311,30 +15384,14 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 size: 89061 timestamp: 1768735187639 -- conda: https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.8.0-hb602f4b_1.conda - sha256: b9e55f0be8ea5bee960565fd18c232a0ef62af7f007d1d102a3b66c496489d68 - md5: 4dce7215af5e642fe84a07321c0628f6 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libutf8proc >=2.8.0,<2.9.0a0 - size: 83847 - timestamp: 1732830082137 -- conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_0.conda - sha256: 432d4796261bc4a4525e8ecf262361c3f69d815af823ebf60242093e47333674 - md5: 63d913ed8ee946e25b1ebf7d9f477b67 +- conda: https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_2.conda + sha256: 4b094443126c5fc38d200eb0ea335b67e64d966d7aa737bb3141217a787b0c73 + md5: aaeed7feddbfd7454f8853a5eae8d902 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -25343,29 +15400,11 @@ packages: - libvulkan-headers 1.4.357.0.* license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - libvulkan-loader >=1.4.357.0,<2.0a0 - size: 286754 - timestamp: 1785311500125 -- conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda - sha256: 7b6316abfea1007e100922760e9b8c820d6fc19df3f42fb5aca684cfacb31843 - md5: f9bbae5e2537e3b06e0f7310ba76c893 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - libwebp 1.6.0 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libwebp-base >=1.6.0,<2.0a0 - size: 279176 - timestamp: 1752159543911 + size: 288787 + timestamp: 1787491929908 - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda sha256: 4470d98d3178b0d45492eed4808afe421d2e7808352b8d06c2dc29166b75d94d md5: 35a9475e4cc999d52921f57c181979fc @@ -25377,7 +15416,6 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -25392,31 +15430,29 @@ packages: - pthreads-win32 <0.0a0 - msys2-conda-epoch <0.0a0 license: MIT AND BSD-3-Clause-Clear - purls: [] run_exports: {} size: 36621 timestamp: 1759768399557 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - sha256: 08dec73df0e161c96765468847298a420933a36bc4f09b50e062df8793290737 - md5: a69bbf778a462da324489976c84cfc8c +- conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda + sha256: f5932cbcadb67bef9374ee864fed72411af29e88690435c6c7fc5633c89dabee + md5: 3658b0201b32da9607ea5e3c1c463c1f depends: - libgcc >=13 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - pthread-stubs - ucrt >=10.0.20348.0 - - xorg-libxau >=1.0.11,<2.0a0 - - xorg-libxdmcp + - xorg-libxau >=1.0.12,<2.0a0 + - xorg-libxdmcp >=1.1.5,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 - size: 1208687 - timestamp: 1727279378819 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_0.conda - sha256: 3b61ee3caba702d2ff432fa3920835db963026e5c99c4e6fdca0c6114f59e7ce - md5: 9e8dd0d90ed830107b2c36801035b7db + size: 1215534 + timestamp: 1787886015478 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda + sha256: 8163de24a7ddb4ebf9587c3a45ba950caf3690b9646b76f007ca15e6e52b5881 + md5: 6e7dadff3f3bde2d29d29a3ec34f2d58 depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 @@ -25429,70 +15465,50 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT - purls: [] run_exports: {} - size: 519871 - timestamp: 1776376969852 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.9-h741aa76_0.conda - sha256: 28ac5bbed11644b9e06241ba1dfdac7e3a99e74b69915d45f646717ad9645ca5 - md5: 333d21ab129d5fa5742225bf1d7557a5 - depends: - - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxml2 >=2.13.9,<2.14.0a0 - size: 1521446 - timestamp: 1761766307746 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_0.conda - sha256: a4599c6bbbbdd7db570896e520c557eec8e66d94e839a59d17dc1f24a3d5f82b - md5: 95591ca5671d2213f5b2d5aa7818420d + size: 519962 + timestamp: 1787237653321 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda + sha256: a83e9adcf7c50f20289dc6890707c8e4e84151b4204b0f7344998138807458d1 + md5: 45a5a1c709a69f14cf176220788d18a9 depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 h3cfd58e_0 + - libxml2-16 2.15.3 h3cfd58e_1 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 43684 - timestamp: 1776376992865 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_0.conda - sha256: 1991b4e5e19b877b4cf01d9980957df1cc00dec94d7b93354a919ddaf8469582 - md5: 97b52e0d228b549e289445ab1238af7e + size: 43610 + timestamp: 1787237661577 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda + sha256: fc3130e5ad77c01309293eac68684907003505adb365027f16136abd4ffe8e3a + md5: daa37dc0bb73bc77322b83f94b72caae depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h8ef44ab_0 - - libxml2-16 2.15.3 h3cfd58e_0 + - libxml2 2.15.3 h8ef44ab_1 + - libxml2-16 2.15.3 h3cfd58e_1 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxml2 - libxml2-16 >=2.15.3 - size: 123614 - timestamp: 1776377012113 + size: 124649 + timestamp: 1787237669397 - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda sha256: 13da38939c2c20e7112d683ab6c9f304bfaf06230a2c6a7cf00359da1a003ec7 md5: 46034d9d983edc21e84c0b36f1b4ba61 @@ -25504,63 +15520,11 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 size: 420223 timestamp: 1757963935611 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda - sha256: 20857f1adb91cc59826e146ee6cb1157c6abf2901a93d359b1106ba87c8e770b - md5: e84f36aa02735c140099d992d491968d - depends: - - libxml2 >=2.13.8,<2.14.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - libxslt >=1.1.43,<2.0a0 - size: 416974 - timestamp: 1753273998632 -- conda: https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda - sha256: 8ed49d8aa0ff908e16c82f92154174027c8906429e8b63d71f0b27ecc987b43e - md5: 09066edc7810e4bd1b41ad01a6cc4706 - depends: - - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.3.2,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libzip >=1.11.2,<2.0a0 - size: 146856 - timestamp: 1730442305774 -- conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - sha256: 88609816e0cc7452bac637aaf65783e5edf4fee8a9f8e22bdc3a75882c536061 - md5: dbabbd6234dea34040e631f87676292f - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - zlib 1.3.2 *_2 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 58347 - timestamp: 1774072851498 - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda sha256: 0629c2cc0404d3bb29d6baa7b4ba62da80797015e86de050db81ea5a07050527 md5: 5d2ff29d465097458cc3ff6569151991 @@ -25572,47 +15536,28 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 size: 58529 timestamp: 1785276664143 -- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda - sha256: 50c02902bb516eeb56680358f052be38b5bf74b40e78ea4b2a675e84957e7307 - md5: de3551bf6508d45ca46b714639e52823 +- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + sha256: f9658ec83a6744f38e2b8188c76e1c2dea2614f5aa0e14d9d274b19f60646b8c + md5: 79055ec79e1b43749763122cead30976 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - openmp 22.1.8|22.1.8.* - intel-openmp <0.0a0 + - openmp 23.1.0|23.1.0.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE - purls: [] run_exports: strong: - - llvm-openmp >=22.1.8 - size: 348002 - timestamp: 1781737042070 -- conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.3.3-py312h0608a1d_1.conda - sha256: 4ebd0ffbe8ce40924459cb3bf1837b5e22bcf3bd0cb807a51795b460878a400a - md5: 90e9d18bcbfe59ac7d6064a58432f365 - depends: - - lz4-c >=1.9.3,<1.10.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping - run_exports: {} - size: 76980 - timestamp: 1725090008004 + - llvm-openmp >=23.1.0 + size: 342468 + timestamp: 1787722783678 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda sha256: 764da65f49c2520ff5e2fdc8aaa57e94ca884dac58da2bdc294cb56950c63cec md5: 8e9b2740fe44700706e8e4ca7e4fb8b6 @@ -25626,77 +15571,37 @@ packages: - lz4-c >=1.10.0,<1.11.0a0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 47054 timestamp: 1765026429237 -- conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda - sha256: 632cf3bdaf7a7aeb846de310b6044d90917728c73c77f138f08aa9438fc4d6b5 - md5: 0b69331897a92fac3d8923549d48d092 +- conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda + sha256: 6824e36be0f95ae516dc36dddd18f69cbad0e4e07906fcb10a5f26a8dc471903 + md5: 3e0691cb20fcaf922df78ccea08b771a depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 - size: 139891 - timestamp: 1733741168264 -- conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda - sha256: a0954b4b1590735ea5f3d0f4579c3883f8ac837387afd5b398b241fda85124ab - md5: e34720eb20a33fc3bfb8451dd837ab7a - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vs2015_runtime >=14.29.30139 - license: BSD-2-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - lz4-c >=1.9.4,<1.10.0a0 - size: 134235 - timestamp: 1674728465431 -- conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda - sha256: 344f4f225c6dfb523fb477995545542224c37a5c86161f053a1a18fe547aa979 - md5: c5cb4159f0eea65663b31dd1e49bbb71 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 + size: 150673 + timestamp: 1786717127870 +- conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda + sha256: e26c855c4b5d797ba5a9e96a6392d58676981660849b99fdb939aee3164e8323 + md5: 3678b093a471615102e97124f37b233f + depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: GPL-2.0-or-later license_family: GPL - purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 - size: 165589 - timestamp: 1753889311940 -- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda - sha256: b744287a780211ac4595126ef96a44309c791f155d4724021ef99092bae4aace - md5: a73298d225c7852f97403ca105d10a13 - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - jinja2 >=3.0.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping - run_exports: {} - size: 28510 - timestamp: 1772445175216 + size: 165289 + timestamp: 1787049159945 - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda sha256: 02805a0f3cd168dbf13afc5e4aed75cc00fe538ce143527a6471485b36f5887c md5: 8de7b40f8b30a8fcaa423c2537fe4199 @@ -25710,26 +15615,9 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 30022 timestamp: 1772445159549 -- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda - sha256: a1864fb26f541716aff7c194d6399b17ec90ee4fded2f74034fe4db7c930c60d - md5: 779fa0e5531694e039385f739ceb88b4 - depends: - - matplotlib-base >=3.10.9,<3.10.10.0a0 - - pyside6 >=6.7.2 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - tornado >=5 - license: PSF-2.0 - license_family: PSF - purls: [] - run_exports: {} - size: 18066 - timestamp: 1777000842638 - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda sha256: 3599b3ae70c4fd210e83d3e04920b70e763a3c3cc6a589f7a46776a339cf79c0 md5: d8f7f4bb221698dda04dd955eb6f7e52 @@ -25741,40 +15629,9 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF - purls: [] run_exports: {} size: 15333 timestamp: 1785211311397 -- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda - sha256: 539ca0eac473a1b7f9da1071ddcd6fe9a3bdd9e51eab0f1c498e6345c1898e8b - md5: 3752482b0df88d7a08a0791f906e87ae - depends: - - contourpy >=1.0.1 - - cycler >=0.10 - - fonttools >=4.22.0 - - freetype - - kiwisolver >=1.3.1 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - numpy >=1.23 - - numpy >=1.23,<3 - - packaging >=20.0 - - pillow >=8 - - pyparsing >=2.3.1 - - python >=3.12,<3.13.0a0 - - python-dateutil >=2.7 - - python_abi 3.12.* *_cp312 - - qhull >=2020.2,<2020.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: PSF-2.0 - license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping - run_exports: {} - size: 8033620 - timestamp: 1777000825433 - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda sha256: b3f19b71ea556c79b67a599929a4c05618fd4ae03d7412e65adc95ec627670c5 md5: 88c095cee1bcd48178328c93920870a2 @@ -25801,14 +15658,12 @@ packages: - vc14_runtime >=14.44.35208 license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8810362 timestamp: 1785211292854 -- conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.0-py314hb98de8c_1.conda - sha256: 60b54e04e997471f697872c64cbab4d52bca06286a74b3ef898ced58bcdaff78 - md5: bc069b5b1bd007235f46cf21989ae558 +- conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_2.conda + sha256: 6d629fbbce2a8490202bfd10d83a6339b1933b35df2f0a6884f193cbcdbbd3a0 + md5: ff333df198dd0aae51c400394ef4ccdd depends: - python - tomli-w @@ -25817,30 +15672,12 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT - size: 198561 - timestamp: 1782062732702 -- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda - sha256: 208b235b20232891d1dc9f468d08b00c53e7ca65a18b9bc0ff4c4867680b9a75 - md5: 5d55038c4d640e5df06de1cfb4e8d741 - depends: - - bzip2 >=1.0.8,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - zstd >=1.5.7,<1.6.0a0 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - minizip >=4.2.1,<5.0a0 - size: 106290 - timestamp: 1778002682910 -- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h0ffbb96_0.conda - sha256: 1c0c9b720b5dc860cc8a127d925c161d234a8a3ca4a10cea5038bfa548315557 - md5: 431ad376b1c4e5306a8ceb6b7278de41 + run_exports: {} + size: 199489 + timestamp: 1788033928311 +- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda + sha256: ee989609be75355a6b9125e28e0ac7e36c29ce77dd65890b5bc469a4a6d14e86 + md5: 124e35907d5fb36b1e0cb3b74c20bc9d depends: - bzip2 >=1.0.8,<2.0a0 - liblzma >=5.8.3,<6.0a0 @@ -25851,45 +15688,14 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other - purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 106476 - timestamp: 1782851531134 -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda - sha256: d0ec759eed591693d4b51d9e31f43e9ec701484a012b14f28f5d6fd75317f481 - md5: c058cdc8796d5eb260a1aef2cbb7fbbf - depends: - - llvm-openmp >=22.1.4 - - onemkl-license 2025.3.1 h57928b3_13 - - tbb >=2022.3.0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LicenseRef-IntelSimplifiedSoftwareOct2022 - license_family: Proprietary - purls: [] - run_exports: {} - size: 99617963 - timestamp: 1778105275590 -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - sha256: f997bfc9bc4d4e14261cdcd1ad195d64a72ee44dca3145d24c1349f8d1311aa5 - md5: 36ea6e1292e9d5e89374201da79646ef - depends: - - llvm-openmp >=22.1.5 - - onemkl-license 2026.0.0 h57928b3_908 - - tbb >=2023.0.0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LicenseRef-IntelSimplifiedSoftwareOct2022 - license_family: Proprietary - size: 114354729 - timestamp: 1779293121860 -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_234.conda - sha256: f7568a5ebf9f4a401a6d9da7be4f82a8794cf305e870e77923a5712ba7f4b964 - md5: f0510f9d5e501462d72a5c0c798dbcc3 + size: 116615 + timestamp: 1788051329472 +- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda + sha256: bb8abe071f73765446df62924031e6599577e8ac9003264dc4569e78c0fea16d + md5: ace316c48c178d7faa403dee51e30c7b depends: - llvm-openmp >=22.1.8 - tbb >=2023.0.0 @@ -25898,79 +15704,44 @@ packages: - vc14_runtime >=14.44.35208 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary - purls: [] run_exports: {} - size: 114429478 - timestamp: 1786086389935 -- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py312h78d62e6_1.conda - sha256: f43cc72ae92759d661187cb4bc4fb4721b97128d150f336b9b0ebf24b8600a27 - md5: 0ac1766b278a8547f22cd0820b8a7e96 + size: 114686732 + timestamp: 1787725739779 +- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + sha256: b4971dcf9b2a8f931512989e97374629a07a243e8e090a3ed365585961e3750f + md5: 921a2c141bc15cf74a238a8fb928554a depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping - run_exports: {} - size: 89054 - timestamp: 1782460807428 -- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314h909e829_0.conda - sha256: 5dc2c51a980e406faf3f06c3674792fb987f20fbcb5620c6860b6982f93f995f - md5: a2bf7491b2d71489532f20db59f470c3 - depends: - - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 license: Apache-2.0 - license_family: Apache - size: 90559 - timestamp: 1782070458677 -- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py314hf309875_1.conda - sha256: 548f4b23229abf8503b1e28438682e79a82ed0f2081a0c5ddfadb49e721b5022 - md5: 2a6b2dce5b76d32c67d11fcdd88ef037 + run_exports: {} + size: 90618 + timestamp: 1787850873390 +- conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda + sha256: 65c4ae59ba5b4b557b4ce79632fb0d01cdcfe0894ed742d9f129442353de7b4a + md5: 3084e08306d71a87b785efae1fa22564 depends: - - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/msgpack?source=hash-mapping - run_exports: {} - size: 89771 - timestamp: 1782460807585 -- conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda - sha256: 57f78d8cd9a282d03cd7a7ffb1f42d570e1bbfb42d606e99de5c16e089067185 - md5: 013aabb169d59009bdf7d70319360e9b - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 license: MIT - license_family: MIT - purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 - size: 148557 - timestamp: 1747117340968 -- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.1.0-py314h13f4da2_0.conda - sha256: 8099ce0e8f971f6a393df52c2d5abca56209d47c1d3da020ca4ee0625539b933 - md5: 26de46b296a732183242a77ac4e6a30f + size: 164026 + timestamp: 1788122502315 +- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314h13f4da2_0.conda + sha256: a94fcfe3d4d01012deca8876beba5e435477a2154edba01c834d16043d7cfb6b + md5: e71d39ca28efc941844d6e6235c39e7a depends: - - ast-serialize >=0.3.0,<1.0.0 + - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 - pathspec >=1.0.0 - python - - python-librt >=0.11.0 + - python-librt >=0.13.0 - typing_extensions >=4.6.0 - psutil >=4.0 - vc >=14.3,<15 @@ -25979,8 +15750,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 10734309 - timestamp: 1780315652597 + run_exports: {} + size: 10916942 + timestamp: 1786887307539 - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda sha256: 460d5a644bd7f195784f7e17d550274986ddffa1d534b18195322de1f81cf42d md5: faf4e3f7981777629dd7cbef578834ad @@ -25988,90 +15760,29 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 135270 timestamp: 1785920493388 -- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda - sha256: 73570817a5109d396b4ebbe5124a89525959269fd33fa33fd413700289fbe0ef - md5: f9ac74c3b07c396014434aca1e58d362 - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping - run_exports: - weak: - - numpy >=1.26.4,<2.0a0 - size: 6495445 - timestamp: 1707226412944 -- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.0-py314h02f10f6_0.conda - sha256: 86c3e926fa1d6f27ebe6b9db11ff12e9a3b6e4b0343bf4a9b489dafd9614da3f - md5: f92585b1624ecdd117b6d13fd4d691ed +- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda + sha256: 34cdcef001ff3630bd6a54d3225959ec6298e549f4d82d2e3abe08af49411191 + md5: e4916c642c9aa7ab2d58ec75591189d3 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - liblapack >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - - python_abi 3.14.* *_cp314 - constrains: - - numpy-base <0a0 - license: BSD-3-Clause - license_family: BSD - size: 7436159 - timestamp: 1782112573833 -- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_0.conda - sha256: 14067f2c7628320708f73531c98c16753ee3991230c6ae32873baed911771b09 - md5: bd342de8875a168535a553191b405821 - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - libcblas >=3.9.0,<4.0a0 - - libblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 constrains: - numpy-base <0a0 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.25,<3 - size: 7462504 - timestamp: 1786330617237 -- conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda - sha256: 4ca711784724992014a2ba1bfc2e4798182f374d1f11a05f33d636e9f10ca6ca - md5: eded4ef8a94852a2e3e4c779b3b6fdda - license: LicenseRef-IntelSimplifiedSoftwareOct2022 - license_family: Proprietary - purls: [] - run_exports: {} - size: 41377 - timestamp: 1778104928629 -- conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - sha256: 42ad15cbb3bf31830efa04d4b86dd2d5c0dd590c86f98adcd3c8c1f75acf5dd5 - md5: 9c9303e08b50e09f5c23e1dac99d0936 - license: LicenseRef-IntelSimplifiedSoftwareOct2022 - license_family: Proprietary - size: 41580 - timestamp: 1779292867015 + size: 7497786 + timestamp: 1788129258926 - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda sha256: 24342dee891a49a9ba92e2018ec0bde56cc07fdaec95275f7a55b96f03ea4252 md5: e723ab7cc2794c954e1b22fde51c16e4 @@ -26084,31 +15795,14 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD - purls: [] run_exports: weak: - openjpeg >=2.5.4,<3.0a0 size: 245594 timestamp: 1772624841727 -- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_0.conda - sha256: cb6e7ba0d010ee0d3249ce9886de3d7613d26d9965d4c95666fa66b9c4c31001 - md5: e99f95734a326c0fd4d02bbd995150d4 - depends: - - ca-certificates - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 9414790 - timestamp: 1781071745579 -- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_1.conda - sha256: 2ebff5a1b5793e82495bf33c91fba040e11ff23333c2385ac66d0c3aee2cc14c - md5: a978392692a910ba1c8920ccb1e784b3 +- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda + sha256: 9dddb559ba49744d5d94092d8d13cb0567f5c3b3f439f3acf28433d1f4256acc + md5: 73cb1783f47c452be4c309430fbef89f depends: - ca-certificates - ucrt >=10.0.20348.0 @@ -26116,33 +15810,11 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] - run_exports: - weak: - - openssl >=3.6.3,<4.0a0 - size: 9427535 - timestamp: 1785915614585 -- conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda - sha256: f083c8f49430ca80b6d8a776c37bc1021075dc5f826527c44a85f90607a5c652 - md5: dbb01d6e4f992ea4f0dcb049ab926cc7 - depends: - - libprotobuf >=4.25.3,<4.25.4.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - snappy >=1.2.1,<1.3.0a0 - - tzdata - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zstd >=1.5.6,<1.6.0a0 - license: Apache-2.0 - license_family: Apache - purls: [] run_exports: weak: - - orc >=2.0.2,<2.0.3.0a0 - size: 999325 - timestamp: 1723761049521 + - openssl >=3.6.4,<4.0a0 + size: 9474879 + timestamp: 1787699876495 - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda sha256: 6097e6facda0184c7be60eb0daaf05a03d6d4b9725ea5901553a1d01ba91271c md5: d0e00e318dceb35f9a66f52225609ed2 @@ -26160,127 +15832,11 @@ packages: - libabseil * cxx17* license: Apache-2.0 license_family: APACHE - purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 size: 1465743 timestamp: 1784301260606 -- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda - sha256: f1f67623997699885c7668b039314f5c9a663eb94dcaf284574fdc6dd248b2b8 - md5: 9da394ea5e0ec5cc5edc1ad14f2a4d4d - depends: - - python - - numpy >=1.26.0 - - python-dateutil >=2.8.2 - - python-tzdata - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - numpy >=1.23,<3 - - python_abi 3.12.* *_cp312 - constrains: - - adbc-driver-postgresql >=1.2.0 - - adbc-driver-sqlite >=1.2.0 - - beautifulsoup4 >=4.12.3 - - blosc >=1.21.3 - - bottleneck >=1.4.2 - - fastparquet >=2024.11.0 - - fsspec >=2024.10.0 - - gcsfs >=2024.10.0 - - html5lib >=1.1 - - hypothesis >=6.116.0 - - jinja2 >=3.1.5 - - lxml >=5.3.0 - - matplotlib >=3.9.3 - - numba >=0.60.0 - - numexpr >=2.10.2 - - odfpy >=1.4.1 - - openpyxl >=3.1.5 - - psycopg2 >=2.9.10 - - pyarrow >=13.0.0 - - pyiceberg >=0.8.1 - - pymysql >=1.1.1 - - pyqt5 >=5.15.9 - - pyreadstat >=1.2.8 - - pytables >=3.10.1 - - pytest >=8.3.4 - - pytest-xdist >=3.6.1 - - python-calamine >=0.3.0 - - pytz >=2024.2 - - pyxlsb >=1.0.10 - - qtpy >=2.4.2 - - scipy >=1.14.1 - - s3fs >=2024.10.0 - - sqlalchemy >=2.0.36 - - tabulate >=0.9.0 - - xarray >=2024.10.0 - - xlrd >=2.0.1 - - xlsxwriter >=3.2.0 - - zstandard >=0.23.0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping - run_exports: {} - size: 13644207 - timestamp: 1778602674307 -- conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py314hf700ef7_0.conda - sha256: 7f9912ba70e53805432f8e3a980fec5d13fe851989f68a70889394a2b4438ac2 - md5: 33451badee17d4162840339efdab40ad - depends: - - python - - numpy >=1.26.0 - - python-dateutil >=2.8.2 - - python-tzdata - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - - numpy >=1.23,<3 - constrains: - - adbc-driver-postgresql >=1.2.0 - - adbc-driver-sqlite >=1.2.0 - - beautifulsoup4 >=4.12.3 - - blosc >=1.21.3 - - bottleneck >=1.4.2 - - fastparquet >=2024.11.0 - - fsspec >=2024.10.0 - - gcsfs >=2024.10.0 - - html5lib >=1.1 - - hypothesis >=6.116.0 - - jinja2 >=3.1.5 - - lxml >=5.3.0 - - matplotlib >=3.9.3 - - numba >=0.60.0 - - numexpr >=2.10.2 - - odfpy >=1.4.1 - - openpyxl >=3.1.5 - - psycopg2 >=2.9.10 - - pyarrow >=13.0.0 - - pyiceberg >=0.8.1 - - pymysql >=1.1.1 - - pyqt5 >=5.15.9 - - pyreadstat >=1.2.8 - - pytables >=3.10.1 - - pytest >=8.3.4 - - pytest-xdist >=3.6.1 - - python-calamine >=0.3.0 - - pytz >=2024.2 - - pyxlsb >=1.0.10 - - qtpy >=2.4.2 - - scipy >=1.14.1 - - s3fs >=2024.10.0 - - sqlalchemy >=2.0.36 - - tabulate >=0.9.0 - - xarray >=2024.10.0 - - xlrd >=2.0.1 - - xlsxwriter >=3.2.0 - - zstandard >=0.23.0 - license: BSD-3-Clause - license_family: BSD - size: 14062915 - timestamp: 1778602665890 - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda sha256: 399c1d7f7918986d9c1a646f4e789ac24749149598d16b529febc3361e9aad23 md5: a519b50147401cf6bad2fd188028ad38 @@ -26335,91 +15891,49 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 14103773 timestamp: 1785276287020 -- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.56.4-h13911b6_1.conda - sha256: 3d4e6e541e633f6fd22fc2c1d79ad5ec39503dea3ba04fc3e01d5be904ec7cea - md5: 1f1cf3772ba7d4eef989e4679ddf97f7 +- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda + sha256: 85fc9c2a3b805d7ad784b6b307f4cf4833d3525ab409a7f86262d476a67d441f + md5: 22d750d2ebf4109bc66c036f1e52b982 depends: - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.17.1,<3.0a0 + - fontconfig >=2.18.2,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - - harfbuzz >=13.2.1 - - libexpat >=2.7.4,<3.0a0 - - libfreetype >=2.14.2 - - libfreetype6 >=2.14.2 - - libglib >=2.86.4,<3.0a0 - - libpng >=1.6.55,<1.7.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.88.3,<3.0a0 + - libharfbuzz >=14.3.0 + - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LGPL-2.1-or-later - size: 454919 - timestamp: 1774282149607 -- conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda - sha256: 15dffc9a2d6bb6b8ccaa7cbd26b229d24f1a0a1c4f5685b308a63929c56b381f - md5: a912b2c4ff0f03101c751aa79a331831 - depends: - - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: [] run_exports: weak: - - pcre2 >=10.44,<10.45.0a0 - size: 816653 - timestamp: 1745931851696 -- conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-hd2b5f0e_0.conda - sha256: 3e9e02174edf02cb4bcdd75668ad7b74b8061791a3bc8bdb8a52ae336761ba3e - md5: 77eaf2336f3ae749e712f63e36b0f0a1 + - pango >=1.58.2,<2.0a0 + size: 466294 + timestamp: 1786107518608 +- conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda + sha256: d8e9ea26b52a09d880d1e5371830c8dd5b4c099a6db64dbdf8236440744b7499 + md5: 009af999dbe2d1db36a37187a4ca4eb4 depends: - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 - size: 995992 - timestamp: 1763655708300 -- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda - sha256: ab7c254e49d0999bbfc3d3b2c76e7d5f9f831692c864c641cf10c557b727ad7e - md5: ba3bcb72a269e7751cadbdd784f84dec - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - libwebp-base >=1.6.0,<2.0a0 - - tk >=8.6.13,<8.7.0a0 - - zlib-ng >=2.3.3,<2.4.0a0 - - libxcb >=1.17.0,<2.0a0 - - python_abi 3.12.* *_cp312 - - libtiff >=4.7.1,<4.8.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - lcms2 >=2.18,<3.0a0 - - openjpeg >=2.5.4,<3.0a0 - - libjpeg-turbo >=3.1.2,<4.0a0 - license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping - run_exports: {} - size: 944998 - timestamp: 1775060119774 + size: 993241 + timestamp: 1787294653206 - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda sha256: c9dc3212ed0021974541072ee3765a5097a0721191c34ee485d7d7e94449648f md5: 09b8e6ac8f4a257b59e5d3025f3c9c1d @@ -26440,29 +15954,9 @@ packages: - tk >=8.6.13,<8.7.0a0 - zlib-ng >=2.3.3,<2.4.0a0 license: HPND - purls: - - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 989058 timestamp: 1782912129930 -- conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda - sha256: 246fce4706b3f8b247a7d6142ba8d732c95263d3c96e212b9d63d6a4ab4aff35 - md5: 08c8fa3b419df480d985e304f7884d35 - depends: - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - pixman >=0.46.4,<1.0a0 - size: 542795 - timestamp: 1754665193489 - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda sha256: cad8b94c2b00264a15d469eed6dc53aac1c59c6d46f27ad1d91bfaf227cf136a md5: 27c5be39d9e4d25fe85b89a069360d1e @@ -26472,85 +15966,14 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 size: 257473 timestamp: 1786106677325 -- conda: https://conda.anaconda.org/conda-forge/win-64/poppler-24.08.0-h9415970_1.conda - sha256: e0066265fefd6954b68f4ab74d133ad49c5ed8c5d4c668e0450ba3e7b9a9501c - md5: 4c7b7a4301314afed48c6544c34399e4 - depends: - - cairo >=1.18.0,<2.0a0 - - freetype >=2.12.1,<3.0a0 - - lcms2 >=2.16,<3.0a0 - - libcurl >=8.9.1,<9.0a0 - - libglib >=2.80.3,<3.0a0 - - libiconv >=1.17,<2.0a0 - - libintl >=0.22.5,<1.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libpng >=1.6.43,<1.7.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - openjpeg >=2.5.2,<3.0a0 - - poppler-data - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: GPL-2.0-only - license_family: GPL - purls: [] - run_exports: {} - size: 2355995 - timestamp: 1724660655649 -- conda: https://conda.anaconda.org/conda-forge/win-64/postgresql-17.2-heca7946_0.conda - sha256: 855e02d8dbd4de3b113661df1cd5e4cfd592f050350572321e06f912e809e529 - md5: e2fa6c7a89d528398dce60b398f05b42 - depends: - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libpq 17.2 h7ec079e_0 - - libxml2 >=2.13.5,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.4.0,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - zstd >=1.5.6,<1.6.0a0 - license: PostgreSQL - purls: [] - run_exports: - weak: - - libpq >=17.2,<18.0a0 - size: 4328521 - timestamp: 1732205108173 -- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.4.1-hd9569ee_1.conda - sha256: cde60f7c07598fd183a90f2725f5b7f3028a382a163f4efcb8b52dcfbb798d03 - md5: 6e15f5054b179959d2410c2e53d5a3e4 - depends: - - libcurl >=8.9.0,<9.0a0 - - libsqlite >=3.46.0,<4.0a0 - - libtiff >=4.6.0,<4.8.0a0 - - sqlite - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - proj4 ==999999999999 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - proj >=9.4.1,<9.5.0a0 - size: 2726576 - timestamp: 1722328352769 -- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_0.conda - sha256: 10ac4a9cdd99d3c2dc4695c5db51d9aa263a3b7551604f57801679b81c785d12 - md5: bbf5692c63b2f280975b3430ed3e37fd +- conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda + sha256: 900c78a977cc0e848c1234064b2469f8b372b9dce1097bb5e9182f56bab1722d + md5: 434616a1662714fe1a75a51ace8c72d6 depends: - sqlite - libtiff @@ -26558,19 +15981,17 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libcurl >=8.19.0,<9.0a0 - - libsqlite >=3.53.0,<4.0a0 - - libtiff >=4.7.1,<4.8.0a0 + - libcurl >=8.21.0,<9.0a0 + - libtiff >=4.7.2,<4.8.0a0 + - libsqlite >=3.53.4,<4.0a0 constrains: - proj4 ==999999999999 license: MIT - license_family: MIT - purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 - size: 3129512 - timestamp: 1775840349774 + size: 3129858 + timestamp: 1787905007013 - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda sha256: ed08acd2ce6c69063693193450df89e8695e8b1251b399d34fb56ab45d900cbc md5: 128297355faf0afcb84e22e43d472101 @@ -26583,31 +16004,14 @@ packages: - zlib license: MIT license_family: MIT - purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 183665 timestamp: 1730769570131 -- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda - sha256: edffc84c001a05b996b5f8607c8164432754e86ec9224e831cd00ebabdec04e7 - md5: a2724c93b745fc7861948eb8b9f6679a - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping - run_exports: {} - size: 242769 - timestamp: 1769678170631 -- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda - sha256: 17c8274ce5a32c9793f73a5a0094bd6188f3a13026a93147655143d4df034214 - md5: fd539ac231820f64066839251aa9fa48 +- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda + sha256: c975e08da7627e95d1f0b70ef9787e9153118e67bb5b31f5530c5c3827d9afe8 + md5: deeef9494cfffa58f5fd20bc6eb57664 depends: - python - vc >=14.3,<15 @@ -26616,24 +16020,9 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/psutil?source=hash-mapping run_exports: {} - size: 249950 - timestamp: 1769678167309 -- conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - sha256: 7e446bafb4d692792310ed022fe284e848c6a868c861655a92435af7368bae7b - md5: 3c8f2573569bb816483e5cf57efbbe29 - depends: - - libgcc >=13 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: [] - run_exports: {} - size: 9389 - timestamp: 1726802555076 + size: 249966 + timestamp: 1787417377436 - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda sha256: 5546927a2e337d2903d6cdb028fb33723cdbcc45bf9abe1770fbde2c4ea8bce6 md5: bc97d497656c9c661ffd3043aca90aa4 @@ -26643,7 +16032,6 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: {} size: 10120 timestamp: 1786067833280 @@ -26657,15 +16045,13 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 14442928 timestamp: 1757853584977 -- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_1.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_3.conda noarch: python - sha256: 7d5006b6ea4af1ca51b3b3df78e6996c624e754445ca194357dc4024efb0282c - md5: 802666e49168e778512a36453cd74a74 + sha256: 2456978696deb80e8291ae38f0d373feb8407c2e70c6f13d6827444658ecbafa + md5: f425c172978e645bc8f823b03ccc533a depends: - python - vc >=14.3,<15 @@ -26674,27 +16060,9 @@ packages: - _python_abi3_support 1.* - cpython >=3.10 license: BSD-3-Clause - license_family: BSD - size: 13201940 - timestamp: 1781020758113 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda - sha256: bc8553befae5d33ddf0d99262de4b7ffbe798741e01b202b7d8d6336d4f2142b - md5: 5601751f674c1ecc1629cdf76f22ab61 - depends: - - libarrow-acero 17.0.0.* - - libarrow-dataset 17.0.0.* - - libarrow-substrait 17.0.0.* - - libparquet 17.0.0.* - - numpy >=1.19,<3 - - pyarrow-core 17.0.0 *_2_* - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - license: Apache-2.0 - license_family: APACHE - purls: [] run_exports: {} - size: 26129 - timestamp: 1730169358866 + size: 13162295 + timestamp: 1787913208737 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda sha256: 050a5a54d49f5877f15566cd4df55233b2c78bb240f97a53c3a5828f68f5c5c9 md5: 7c6d60d53f7b0b09e424b58cd9fdfd37 @@ -26708,32 +16076,9 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 26584 timestamp: 1784258906758 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-17.0.0-py312h6a9c419_2_cpu.conda - build_number: 2 - sha256: 38acc61d435c6c9a30e9c9609a40da356b606c7c3f4ee7e5150ced3416b3f88b - md5: 32ae28e98c1a84532ddb5dc7468edc4f - depends: - - libarrow 17.0.0.* *cpu - - libzlib >=1.3.1,<2.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - constrains: - - apache-arrow-proc =*=cpu - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping - run_exports: {} - size: 3551041 - timestamp: 1730169341038 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda sha256: 2e0422f3b9cc1aa067638cccb0a8e917809550a91eb781cfbf64d5a7e2b8e7ab md5: 54155aba0ef1fe72c52022617e453d52 @@ -26752,44 +16097,26 @@ packages: - libprotobuf >=6.33.5 license: Apache-2.0 license_family: APACHE - purls: - - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 3689897 timestamp: 1784258892556 -- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_3.conda - sha256: e14a8c506e699c968596f1a07e925d63dcc49ee9f3623c4c8a5541ad186e9071 - md5: 4e6be0e1a7a9cd7dde994137b8ebd5cf +- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda + sha256: 38d9062eca5d85ac5de080b3512edc4e0f89c64199afcd2da34cd432b5d2514d + md5: c79e745df2f185732d9cdb5763452440 depends: - - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - size: 79551 - timestamp: 1757744726713 -- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda - sha256: 2b251fb56d07efd36815732477d22bcdd2c7a6e7500012eb024d8c59413d2e24 - md5: edd458c91ab9bc198012c73289b0b0bc - depends: - - python - - typing-extensions >=4.6.0,!=4.7.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.12.* *_cp312 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} - size: 1888918 - timestamp: 1778084258226 -- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py314h9f07db2_0.conda - sha256: 53621d25961c97ef77d7d03b8e0d479ee0bb8135b51d71e99a5ed7713a229f5f - md5: a5a21f85bcee70d505798d56750d69cf + size: 80121 + timestamp: 1784146283080 +- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda + sha256: 5f9b6cebac7129d445193e609e99bd6e66193098bc1fd1d2f784576a67ae5720 + md5: a2e1f7dbbdec163ff40ee75a65bc64dc depends: - python - typing-extensions >=4.6.0,!=4.7.0 @@ -26798,31 +16125,9 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT - purls: - - pkg:pypi/pydantic-core?source=hash-mapping - run_exports: {} - size: 1885001 - timestamp: 1778084256232 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.conda - sha256: 3044e7d1fcdf30159ef7ff3e5f1f648004e3f46bc522aac0190141025d55ee04 - md5: 90249b9c726133a702cddb06c1d9dab9 - depends: - - libgdal-core >=3.9.2,<3.10.0a0 - - numpy - - packaging - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} - size: 806834 - timestamp: 1727772248889 + size: 1861822 + timestamp: 1787928979492 - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda sha256: 3ed86ebf3d68f9ae52b5a74f9db919d367af33acf26d481fcdb00fff7079e350 md5: da6ef5e9a5931e73a7638b60fd82fc63 @@ -26837,29 +16142,9 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: - - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 899343 timestamp: 1764402737921 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.6.1-py312h6f27134_9.conda - sha256: 42c2eaa5d75ad0e184e8b957ae07c9d9438fa611c8b896a9a387aaafe10dc3b2 - md5: a7414c734b08e74d22581a9a07686301 - depends: - - certifi - - proj >=9.4.1,<9.5.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyproj?source=hash-mapping - run_exports: {} - size: 740323 - timestamp: 1725436440016 - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda sha256: 84d335aabc168c716b7989e8b97c07697e5e21083a7e8bfe9be168cf4300c7f7 md5: b7503bde09c3bce5c09d81485855f5a8 @@ -26874,8 +16159,6 @@ packages: - proj >=9.8.1,<9.9.0a0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} size: 893897 timestamp: 1786351867522 @@ -26887,121 +16170,44 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pyreadline3?source=hash-mapping run_exports: {} size: 175241 timestamp: 1778848456926 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.1-py314h447aaf0_1.conda - sha256: 070802d5e1e1c1feb24d481efbd90b300fb0ecc1ce4312a3bbcbaae4393c05f9 - md5: 638be6b8674e7acf7a84132903cf4c8e +- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda + sha256: bf1553eebc31e8ba04689639e8ce235c3df5317414f0c4a11417f238297f1b17 + md5: 7c3ccff96dbc9f8e3c40142c7594ec34 depends: - python - - qt6-main 6.11.1.* + - qt6-main 6.11.2.* - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libxslt >=1.1.43,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - - qt6-main >=6.11.1,<6.12.0a0 + - libclang13 >=22.1.8 + - qt6-main >=6.11.2,<7.0a0 - python_abi 3.14.* *_cp314 - - libvulkan-loader >=1.4.341.0,<2.0a0 - - libclang13 >=22.1.5 - license: LGPL-3.0-only - license_family: LGPL - purls: - - pkg:pypi/pyside6?source=hash-mapping - - pkg:pypi/shiboken6?source=hash-mapping - run_exports: {} - size: 11579652 - timestamp: 1778933912020 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda - sha256: 808204eb911e20f4e58b0b6a90e424410a66668a57c08e2e6466b23137cb4f90 - md5: 52a05ba3f802633cb2234bb3edc45888 - depends: - - libclang13 >=20.1.2 - - libxml2 >=2.13.7,<2.14.0a0 - - libxslt >=1.1.39,<2.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - qt6-main 6.9.0.* - - qt6-main >=6.9.0,<6.10.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.42.34438 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - libxslt >=1.1.43,<2.0a0 license: LGPL-3.0-only license_family: LGPL - purls: - - pkg:pypi/pyside6?source=hash-mapping - - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} - size: 8918147 - timestamp: 1743761403797 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda - sha256: a02b446d8b7b167b61733a3de3be5de1342250403e72a63b18dac89e99e6180e - md5: 2956dff38eb9f8332ad4caeba941cfe7 - depends: - - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.7.4,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - liblzma >=5.8.2,<6.0a0 - - libsqlite >=3.51.2,<4.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.5,<4.0a0 - - tk >=8.6.13,<8.7.0a0 - - tzdata - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - python_abi 3.12.* *_cp312 - license: Python-2.0 - purls: [] - run_exports: - weak: - - python_abi 3.12.* *_cp312 - noarch: - - python - size: 15840187 - timestamp: 1772728877265 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h4b44e0e_100_cp314.conda - build_number: 100 - sha256: f1acb89cb1a6bec9a94ae9f8e7411839de009cd64d3ac6a6aec4f3d8a481099a - md5: 8333e3ca6f8d1ebcd30b678dd53f0a25 - depends: - - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libffi >=3.5.2,<3.6.0a0 - - liblzma >=5.8.3,<6.0a0 - - libmpdec >=4.0.0,<5.0a0 - - libsqlite >=3.53.2,<4.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - python_abi 3.14.* *_cp314 - - tk >=8.6.13,<8.7.0a0 - - tzdata - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - zstd >=1.5.7,<1.6.0a0 - license: Python-2.0 - size: 18481352 - timestamp: 1781256034828 - python_site_packages_path: Lib/site-packages -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.6-h53f6dd8_102_cp314.conda - build_number: 102 - sha256: 9faac11f2b7813585f428310df3768e2a465205d76c3829f8ccbd05e6b98764f - md5: 048ad2c1b1faf11cda7bb8c7ec998b0c + size: 11552286 + timestamp: 1787219450026 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda + build_number: 104 + sha256: 70c04a977ba943691cc9ff9c7cbe4326b3e9bd27a5a040184ca892192d6e3143 + md5: 00cc3f77b4714fd8f297d8f51762bbaf depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.1,<3.0a0 - libffi >=3.7.0,<3.8.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.7 h4f90d01_104_cp314 - libsqlite >=3.53.4,<4.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - python_abi 3.14.* *_cp314 - tk >=8.6.13,<8.7.0a0 - tzdata @@ -27010,18 +16216,17 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 - purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 noarch: - python - size: 17903528 - timestamp: 1786444689733 + size: 18570404 + timestamp: 1788162197665 python_site_packages_path: Lib/site-packages -- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.11.0-py314hc5dbbe4_0.conda - sha256: 3269977dd171fdff6eef27db83dac87847d91501539c317acea354c61cc1cb6d - md5: 7cdfc5fda782933981f50c7a9a746039 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda + sha256: f69e11b4025a7467a1820ce348e39af70d4eb0508e89375d3c2e501c4b8e532f + md5: e43c348d1855554ddd3ccd385940386f depends: - python - vc >=14.3,<15 @@ -27030,8 +16235,9 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 104479 - timestamp: 1778511743226 + run_exports: {} + size: 108739 + timestamp: 1786328843266 - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda sha256: 2c97a59ce98b9d29a30bc49f3001b9206ce789edaee7d2773cde8a8a7f78abc4 md5: 02632a618ced2283cf9a7c4a70a43026 @@ -27043,14 +16249,12 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 120434 timestamp: 1778688861070 -- conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hcaaf0b2_0.conda - sha256: 71f9617186f5e2273bcefc4f3a258df5784886077ad36fe14f1d16b1c2c506eb - md5: 4d49ac91c23c4b1878daf5ea9e7c767a +- conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hf700ef7_1.conda + sha256: 5a6ddd03a8441c2312a09f998dccb0bdeb1b906b45c9b8cd5e9f5a7965b0a604 + md5: 57992821d70e28562192fa29cff08260 depends: - python - vc >=14.3,<15 @@ -27059,28 +16263,9 @@ packages: - python_abi 3.14.* *_cp314 license: PSF-2.0 license_family: PSF - purls: - - pkg:pypi/pywin32?source=hash-mapping - run_exports: {} - size: 4472831 - timestamp: 1781362876741 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda - sha256: 1cab6cbd6042b2a1d8ee4d6b4ec7f36637a41f57d2f5c5cf0c12b7c4ce6a62f6 - md5: 9f6ebef672522cb9d9a6257215ca5743 - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - yaml >=0.2.5,<0.3.0a0 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} - size: 179738 - timestamp: 1770223468771 + size: 4473050 + timestamp: 1787374336870 - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda sha256: a2aff34027aa810ff36a190b75002d2ff6f9fbef71ec66e567616ac3a679d997 md5: 0cd9b88826d0f8db142071eb830bce56 @@ -27093,15 +16278,13 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT - purls: - - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 181257 timestamp: 1770223460931 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_3.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.2.0-py312h343a6d4_0.conda noarch: python - sha256: d7e65c44ea8a92f80cc0e424b4b7dbe63b8a9ec04ea774b7d4f7aed4c34cce4c - md5: ebbda9a4e5161d6e1f98146ad057dc10 + sha256: 56d33fdddf6fb7ffb86120b8e2f1398cc406d0d3e85e5647d3a72cad05c17509 + md5: 58bcf20b110e99aa69ab892a6ced10f5 depends: - python - vc >=14.3,<15 @@ -27112,11 +16295,9 @@ packages: - zeromq >=4.3.5,<4.3.6.0a0 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} - size: 182831 - timestamp: 1779483925948 + size: 187939 + timestamp: 1787300948668 - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda sha256: 887d53486a37bd870da62b8fa2ebe3993f912ad04bd755e7ed7c47ced97cbaa8 md5: 854fbdff64b572b5c0b470f334d34c11 @@ -27125,109 +16306,47 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LicenseRef-Qhull - purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 size: 1377020 timestamp: 1720814433486 -- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.1-pl5321hfcac499_1.conda - sha256: c0f0552a879e18282799431c7d2769b269839ac3b3735082e754df3c6fa0728d - md5: a8d735f3faf356a24acf9eea0a940a0f +- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda + sha256: 042c94a5a7a89b0b28a24f6ae6a2d3d609751edab78454057973ec5cc3e77d7d + md5: d98a163070d05c6ed4421432ee1bafb8 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libharfbuzz >=14.3.1 + - libzlib >=1.3.2,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - libglib >=2.88.3,<3.0a0 - krb5 >=1.22.2,<1.23.0a0 - - libglib >=2.88.1,<3.0a0 + - pcre2 >=10.47,<10.48.0a0 - libpng >=1.6.58,<1.7.0a0 - - double-conversion >=3.4.0,<3.5.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libsqlite >=3.53.4,<4.0a0 + - openssl >=3.5.7,<4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - libwebp-base >=1.6.0,<2.0a0 - - openssl >=3.5.6,<4.0a0 - - icu >=78.3,<79.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - pcre2 >=10.47,<10.48.0a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libvulkan-loader >=1.4.341.0,<2.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libsqlite >=3.53.1,<4.0a0 - - harfbuzz >=14.2.0 constrains: - - qt ==6.11.1 + - qt ==6.11.2 license: LGPL-3.0-only license_family: LGPL - purls: [] - run_exports: - weak: - - qt6-main >=6.11.1,<7.0a0 - size: 89576886 - timestamp: 1780400596481 -- conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda - sha256: 84ff37de3c72a612dfbf9b317d5501231a87582f2edf4959ff706b84b4aa9246 - md5: d92e5a0de3263315551d54d5574f5193 - depends: - - double-conversion >=3.3.1,<3.4.0a0 - - harfbuzz >=11.0.0,<12.0a0 - - icu >=75.1,<76.0a0 - - krb5 >=1.21.3,<1.22.0a0 - - libclang13 >=20.1.1 - - libglib >=2.84.0,<3.0a0 - - libjpeg-turbo >=3.0.0,<4.0a0 - - libpng >=1.6.47,<1.7.0a0 - - libsqlite >=3.49.1,<4.0a0 - - libtiff >=4.7.0,<4.8.0a0 - - libwebp-base >=1.5.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.4.1,<4.0a0 - - pcre2 >=10.44,<10.45.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.42.34438 - - zstd >=1.5.7,<1.6.0a0 - constrains: - - qt 6.9.0 - license: LGPL-3.0-only - license_family: LGPL - purls: [] run_exports: weak: - - qt6-main >=6.9.0,<6.10.0a0 - size: 94992566 - timestamp: 1743635306726 -- conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.3.11-py312he4a2ebf_1.conda - sha256: aba65242801a337fb8d37d8ac71bc78f9c26adb8b02b9369daa64008c4271315 - md5: e301914ba7afd52aef42e85d29a0f3ab - depends: - - affine - - attrs - - certifi - - click >=4 - - click-plugins - - cligj >=0.5 - - libgdal >=3.9.2,<3.10.0a0 - - libgdal-core >=3.9.2,<3.10.0a0 - - numpy >=1.19,<3 - - proj >=9.4.1,<9.5.0a0 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - setuptools >=0.9.8 - - snuggs >=1.4.1 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping - run_exports: {} - size: 7448190 - timestamp: 1726177323954 + - qt6-main >=6.11.2,<7.0a0 + size: 89682645 + timestamp: 1787049046883 - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda sha256: e7236607a5f8abda3c1e22afcdb8ece4a466c4120a83a6162fb623bab5f7ce2d md5: 195d18c86203797871e15b2907579a84 @@ -27250,25 +16369,9 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 8483184 timestamp: 1785964794642 -- conda: https://conda.anaconda.org/conda-forge/win-64/re2-2023.09.01-hd3b24a8_2.conda - sha256: 929744a982215ea19f6f9a9d00c782969cd690bfddeeb650a39df1536af577fe - md5: ffeb985810bc7d103662e1465c758847 - depends: - - libre2-11 2023.09.01 hf8d8778_2 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: - weak: - - libre2-11 >=2023.9.1,<2024.0a0 - - re2 - size: 207315 - timestamp: 1708947529390 - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda sha256: eb11c0e948c2576378a8e7f22768a967044d54bfbdea9ef358d31074b91c21f0 md5: 5d51319ef836277672d665c2fc04bab0 @@ -27276,49 +16379,40 @@ packages: - libre2-11 2025.11.05 h45f70d9_2 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - libre2-11 >=2025.11.5 - re2 size: 222198 timestamp: 1781312702688 -- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.7.post0-hfd05255_1.conda - sha256: a7060320c3f6c9ec7d8222a2a217f02bcf3f41ecf5a5d1a375c6280b604962f2 - md5: 9f428ede23b9a119bd2c0330bfc51851 +- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.8.post0-h5112557_0.conda + sha256: 252b3d912d50e0575f1d64826426ee87076f27abd19fa734ba54dc1a1dadbee3 + md5: 7be4b88c22a3938a80a5107e95f46383 depends: - - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 38712 - timestamp: 1779092487585 -- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.7.post0-hac47afa_1.conda - sha256: 4cbe19c86a5c515724ebfbce34f1bf2a9f08229e704c99d811c00629e27a4671 - md5: 6bf1a32350e40dafb15d85080ad84408 - depends: - - reproc 14.2.7.post0 hfd05255_1 - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 license: MIT - license_family: MIT - size: 31865 - timestamp: 1779092514539 -- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.5.1-py314hc980628_0.conda - sha256: 5ecab2511a0ec8d2ea11ff337d83bf8bad8da1e167ae0b6336ac2e56abae10de - md5: f56d0b6c5af9a03320a5884efa3c5764 + run_exports: + weak: + - reproc >=14.2.8.post0,<14.3.0a0 + size: 38678 + timestamp: 1788160440623 +- conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.8.post0-hf5d2508_0.conda + sha256: a62f567d8de231a7aec332447f0add8593c610426b594da1e5b8e1edf959dec3 + md5: 9f9e7861b48ce4f9d022961e92aef40c depends: - - python + - reproc ==14.2.8.post0 h5112557_0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 + - reproc >=14.2.8.post0,<14.3.0a0 license: MIT - license_family: MIT - size: 229575 - timestamp: 1779977051010 + run_exports: + weak: + - reproc-cpp >=14.2.8.post0,<14.3.0a0 + size: 31779 + timestamp: 1788160440623 - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda sha256: d297e7ef5f062194cf76dcf7a2e0449af24012b895a06aa942805f2933a0256c md5: 07602f253d1ad9436bcac564c9fe183f @@ -27330,8 +16424,6 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/rpds-py?source=compressed-mapping run_exports: {} size: 218916 timestamp: 1787344535083 @@ -27347,14 +16439,12 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 306951 timestamp: 1766175833786 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_1.conda - sha256: b719637ce71e533193cd2bcacbf6ba5c10deaafa1be90d96040ee2314c6b17d1 - md5: 496de351b0f9afe9e245229528304f25 +- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_2.conda + sha256: 530a5bdd43b9c5d4e6e84f831e5eb83c466e66ca23b119ca1236e57a25be3c96 + md5: a4bb148d2119ce0e5e85ca2cffddf5f5 depends: - python - vc >=14.3,<15 @@ -27363,46 +16453,22 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - purls: - - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} - size: 105668 - timestamp: 1766159584330 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.15.19-h45713df_0.conda + size: 115285 + timestamp: 1787264323029 +- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.5-h6bcfbb0_0.conda noarch: python - sha256: bf2eccfa578cff287b9f464b9a312da2df420790783dfd3fd690906731bdf2a5 - md5: 5f1a5e6acee3c4f2a6b3375334b51e81 + sha256: 6bf30b3500b1747b7eb2f47cb1662bba32e194539137ddf1c0b6d6e4cbe9a8ba + md5: 85f44eb5b68a9181cbda89f24f0b68a9 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT - license_family: MIT - size: 9926237 - timestamp: 1782288657493 -- conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda - sha256: c89cfc61bd6c690154ff7a7c9029b9dd83aae1049dc1f84c25c37cafa0b5f69d - md5: 0637562978d4231902754645e4c28640 - depends: - - python - - numpy >=1.24.1 - - scipy >=1.10.0 - - joblib >=1.4.0 - - threadpoolctl >=3.5.0 - - narwhals >=2.0.1 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - numpy >=1.23,<3 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} - size: 9153589 - timestamp: 1780413519467 + size: 9299776 + timestamp: 1787865279792 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda sha256: 3e30cc784bd5af6aa035807e5c2f12a1ecbc298d755561f6ce968b3b598b940a md5: 74bafde39f688cb95c111e74bfad6669 @@ -27420,33 +16486,9 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9411356 timestamp: 1780401101875 -- conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda - sha256: b8a13caeb04f2b943e7c617a4b76805450618716b38688ee8d3f79b60d529e84 - md5: 0b8c96ed1c04732ff8eb5d481b44c43c - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - numpy <2.7 - - numpy >=1.23,<3 - - numpy >=1.25.2 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping - run_exports: {} - size: 15078490 - timestamp: 1779876221509 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda sha256: 952db1642d707d2572b511b1207bfd8e8c114fdc99930c400b547eb8ab92ac19 md5: a925cfb1429da2b1312c86516ae7c418 @@ -27464,29 +16506,9 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 15353018 timestamp: 1781914001107 -- conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.0.6-py312h3a88d77_1.conda - sha256: 1af8c26dc5507f60e4459975d228e7e329119ec9311ebb88c98603838a9871d2 - md5: 4f5c4f3160397a63c7b15c81f6c0e9b3 - depends: - - geos >=3.12.2,<3.12.3.0a0 - - numpy >=1.19,<3 - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping - run_exports: {} - size: 534109 - timestamp: 1725394700590 - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda sha256: ffc9c4b2fae82b4dfb14fe6d4b95382d9e6d88fd01f8eb9fbf6f7d2bef65ea35 md5: c5a64ac49215efe79837adbd7e42b605 @@ -27500,41 +16522,26 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD - purls: - - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 614463 timestamp: 1762523809485 -- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.4-h49e36cd_0.conda - sha256: 1ce4cffa9a3057b734e08bcde34da3afb0205523f9c6721a30727912b0103633 - md5: 89d891f85334c927c5fceb64c4e7ba2e +- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.9-h477610d_0.conda + sha256: d728d2d597d250cd1cc17f92acef80bdfa8b51df1a7ee68bc891bdf7801ee7cd + md5: 03b9f62b6ffc6f22facd8f240e42b331 depends: - - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 license: Apache-2.0 license_family: APACHE - size: 374444 - timestamp: 1778109163936 -- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py312he06e257_0.conda - sha256: 690fd79162c01ef910763c746d46281a128e73a6b42b5bde6a1d6d20c91edcca - md5: ed32da1950de9f6ee034afdeab563b25 - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping - run_exports: {} - size: 158103 - timestamp: 1777112188130 -- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py314h5a2d7ad_0.conda - sha256: 3d32bb1a6d773a2f80693ad2079cce24ec63248a997da872f14ad057da47dde8 - md5: 4f9a825716d3e01b6f3089a68b877e4d + run_exports: + weak: + - simdjson >=4.6.9,<4.7.0a0 + size: 419059 + timestamp: 1787803674143 +- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda + sha256: 6e5e7c358d34e6d0fd3078c822a38dc6d1566616704e07b6d2de0d3435590954 + md5: 2f613befc24a58c3935b1be38db94355 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -27543,11 +16550,9 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - purls: - - pkg:pypi/simplejson?source=hash-mapping run_exports: {} - size: 161975 - timestamp: 1777112198099 + size: 169318 + timestamp: 1787824226409 - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda sha256: d2deda1350abf8c05978b73cf7fe9147dd5c7f2f9b312692d1b98e52efad53c3 md5: 3075846de68f942150069d4289aaad63 @@ -27560,31 +16565,14 @@ packages: - ucrt >=10.0.20348.0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 size: 67417 timestamp: 1762948090450 -- conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.14.1-h9f2357e_1.conda - sha256: 3ed3e9aaeb6255914472109a6d25d5119eb196c8d6cc2ec732cffe79ccc789bf - md5: b9bff07144f2be7ee32f0b83a79ef21d - depends: - - fmt >=11.0.1,<11.1a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - spdlog >=1.14.1,<1.15.0a0 - size: 169120 - timestamp: 1722238639391 -- conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-h9f585f1_1.conda - sha256: 90c9befa5f154463647c8e101bc7a4e05cb84b731e2dea5406bedfea02f8b012 - md5: 5c17c0a063b4d36b15d5f9c0ca5377a0 +- conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda + sha256: bd14e9166389be0c654e724e16290772b65745aee8d8bb8bacc65d3179330387 + md5: 0d1e29b811d1fea9ba0fe8c869fa5e96 depends: - fmt >=12.1.0,<12.2.0a0 - ucrt >=10.0.20348.0 @@ -27592,11 +16580,14 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT - size: 174787 - timestamp: 1767781882230 -- conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.51-py314hc5dbbe4_0.conda - sha256: 55e98a0f4e491151ffad7c274d22dfb2d999d50cafa606ec649eff29f079591b - md5: 920cf560266c77de21a8f55e7d0e0329 + run_exports: + weak: + - spdlog >=1.17.0,<1.18.0a0 + size: 174998 + timestamp: 1785924400857 +- conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda + sha256: d3ab49c60febcc9edd55ec7f65a9e02b631e137fe609643c590e8151becacca7 + md5: caa67c9335b6f97b708186a6474a306b depends: - python - greenlet !=0.4.17 @@ -27607,52 +16598,23 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT - size: 3986755 - timestamp: 1781547909164 -- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda - sha256: 700391bf405117eb5c766c4b506f23d3a31f56191ca6619d1b8e5c715376bb7f - md5: 7a28cb97617f0bd6a650b4b88f1f44cb - depends: - - libsqlite 3.53.3 hf5d6505_0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: blessing - purls: [] - run_exports: - weak: - - libsqlite >=3.53.3,<4.0a0 - size: 426871 - timestamp: 1782519137996 -- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_0.conda - sha256: 1883446b33b220ced706e1a90b3e62159402087d5e720a0734b3a47609b5c21f - md5: dafb42fc12203b56f5574658613d256e + run_exports: {} + size: 3998837 + timestamp: 1786535220662 +- conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda + sha256: 7616fec2d6ded67e111a47d4842738d0b3730c0acf3eaebe928cf6d3e15a6822 + md5: df9d7c5d34602e0f2655a524d31fce87 depends: - - libsqlite 3.53.4 hf5d6505_0 + - libsqlite 3.53.4 hf5d6505_1 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing - purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 - size: 426903 - timestamp: 1785016164714 -- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - sha256: c31cac57913a699745d124cdc016a63e31c5749f16f60b3202414d071fc50573 - md5: 17c38aaf14c640b85c4617ccb59c1146 - depends: - - libhwloc >=2.12.1,<2.12.2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: APACHE - purls: [] - run_exports: {} - size: 155714 - timestamp: 1762510341121 + size: 426888 + timestamp: 1787051146089 - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda sha256: 8a4053839b8e997a5965e2dff7d6cf3c77be62d82c0e48c8a04a5ed2d2e73035 md5: 8ee01a693aecff5432069eaaf1183c45 @@ -27663,103 +16625,22 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE - purls: [] run_exports: {} size: 156515 timestamp: 1778673901757 -- conda: https://conda.anaconda.org/conda-forge/win-64/tiledb-2.26.0-h98a567f_0.conda - sha256: 823d6d5c172cd90b105553d5dd93e07e0860c8e5751deb3cd076b684366797d7 - md5: 451f161732757b5124fc3a320401c587 - depends: - - aws-crt-cpp >=0.28.2,<0.28.3.0a0 - - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 - - azure-core-cpp >=1.13.0,<1.13.1.0a0 - - azure-identity-cpp >=1.8.0,<1.8.1.0a0 - - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 - - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 - - bzip2 >=1.0.8,<2.0a0 - - fmt >=11.0.2,<11.1a0 - - libabseil * cxx17* - - libabseil >=20240116.2,<20240117.0a0 - - libcrc32c >=1.1.2,<1.2.0a0 - - libcurl >=8.9.1,<9.0a0 - - libgoogle-cloud >=2.28.0,<2.29.0a0 - - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 - - libwebp-base >=1.4.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - lz4-c >=1.9.3,<1.10.0a0 - - openssl >=3.3.2,<4.0a0 - - spdlog >=1.14.1,<1.15.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.40.33810 - - zstd >=1.5.6,<1.6.0a0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - tiledb >=2.26.0,<2.27.0a0 - size: 3093646 - timestamp: 1726059615242 -- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - sha256: 0e79810fae28f3b69fe7391b0d43f5474d6bd91d451d5f2bde02f55ae481d5e3 - md5: 0481bfd9814bf525bd4b3ee4b51494c4 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: TCL - license_family: BSD - purls: [] - run_exports: - weak: - - tk >=8.6.13,<8.7.0a0 - size: 3526350 - timestamp: 1769460339384 -- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_3.conda - sha256: 13fa29257d43f8e630a1e591ed77fae9bbbb236b011432f01e2034cf36e6bf03 - md5: aaf79e2af50a151fb5b5a3e3f38b7a69 +- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda + sha256: f19618a3a82cc483dacf1c70a30367ee7b0c7e71b90f1f80e14feff44fbd3688 + md5: dd9eb33c99d352b6356f86964d6040f7 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: TCL - purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 - size: 3782314 - timestamp: 1784229072899 -- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda - sha256: 110cf0c741e181ed929a2acdb488f2095badad973c50dd696af36c4162e063cf - md5: 1045d29f787812d3fac1fd80a1339710 - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/tornado?source=hash-mapping - run_exports: {} - size: 865801 - timestamp: 1781006895319 -- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py314h5a2d7ad_0.conda - sha256: 6b9f5a195ca148f7c6b9a4a0a026631979b3112c43cd7c1064085ff833dfa4f0 - md5: b1b9bf11a82e608c5649d7462de94c5f - depends: - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: Apache - size: 919275 - timestamp: 1781006902968 + size: 3782376 + timestamp: 1787272860855 - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda sha256: 1cac25c190b60a4b6fbe2853c4989a4ce086564ee5f995c17d4b32885993068d md5: 94dfb208db7d79437cfe9b4390706b5c @@ -27771,8 +16652,6 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 925990 timestamp: 1786226866739 @@ -27783,26 +16662,9 @@ packages: - vc14_runtime >=14.29.30037 - vs2015_runtime >=14.29.30037 license: LicenseRef-MicrosoftWindowsSDK10 - purls: [] run_exports: {} size: 694692 timestamp: 1756385147981 -- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py312he06e257_0.conda - sha256: 577fcd97b5b855d341ee511307a0c05f632d184544e31ff9621690770843c995 - md5: 9c46f390eb4b5c6e502df20378e442dd - depends: - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping - run_exports: {} - size: 405896 - timestamp: 1770909182518 - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda sha256: 9041e463044944460f73f9528f2ec491180f0ffe857e3555aa8160b81050b8d9 md5: d6b580a13384df5155c6ca19ee66854e @@ -27814,8 +16676,6 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: - - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 406126 timestamp: 1770909191618 @@ -27828,25 +16688,11 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 size: 49181 timestamp: 1715010467661 -- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_39.conda - sha256: 17693b60cb54f80c60275f003f3bfc1b128af56dbfd65c4fae37c64eeb755ce1 - md5: 2eacea63f545b97342da520df6854276 - depends: - - vc14_runtime >=14.51.36231 - track_features: - - vc14 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 20362 - timestamp: 1781320968457 - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda sha256: 35444c55a92e2f7f7ba26bc70f81e56e52344f7d064c0fd4b40a46a58517b79c md5: aa805b5522c2a98fa286e551a1f48546 @@ -27856,24 +16702,9 @@ packages: - vc14 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 21383 timestamp: 1785359368566 -- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda - sha256: 8153ed849c92e891eacac0f2f8d7ecb79f9b5fd7f7917fbb896f252a60a40390 - md5: 06a5bf5a1ca16cce0df6eaa91fc42bc2 - depends: - - ucrt >=10.0.20348.0 - - vcomp14 14.51.36231 h1b9f54f_39 - constrains: - - vs2015_runtime 14.51.36231.* *_39 - license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime - license_family: Proprietary - purls: [] - run_exports: {} - size: 737434 - timestamp: 1781320964561 - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda sha256: 4e4cb599cdc41bf2109d1464c127b5bcbddf548ce3e322e612afb691338b48f8 md5: ac5333bb3d429361f23adf704cc49a78 @@ -27884,25 +16715,9 @@ packages: - vs2015_runtime 14.51.36247.* *_41 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary - purls: [] run_exports: {} size: 767955 timestamp: 1785359364369 -- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda - sha256: 07fb14713c4bc62e2533a2e23a363abfb0e65650681fba0ae4c840e2219350f3 - md5: 8b53a83fda40ec679e4d63fa32fae989 - depends: - - ucrt >=10.0.20348.0 - constrains: - - vs2015_runtime 14.51.36231.* *_39 - license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime - license_family: Proprietary - purls: [] - run_exports: - strong: - - vcomp14 >=14.51.36231 - size: 120684 - timestamp: 1781320948530 - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda sha256: 731e043390c9457299484d39e427221fc868a9249540a498a5a4f6456c7744d1 md5: 350bb67a5c8e5f1c53347ac544ab6600 @@ -27912,23 +16727,11 @@ packages: - vs2015_runtime 14.51.36247.* *_41 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary - purls: [] run_exports: strong: - vcomp14 >=14.51.36247 size: 155910 timestamp: 1785359349999 -- conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36231-h84cd919_39.conda - sha256: 6de6c2cf008fc2dce61060b583f2d8494c83883106952b201381b6b0505f03d7 - md5: 2ccc63d7b7d066a814ed9f99072832d7 - depends: - - vc14_runtime >=14.51.36231 - license: BSD-3-Clause - license_family: BSD - purls: [] - run_exports: {} - size: 20355 - timestamp: 1781320968804 - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda sha256: ee0ae67c96b80b9fdb50c3f617c6329dbcdf1562d0267604f6c0e24f494431d6 md5: 21504569fa34b5d3a67760219e3ff1cc @@ -27936,7 +16739,6 @@ packages: - vc14_runtime >=14.51.36247 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: {} size: 21386 timestamp: 1785359368990 @@ -27951,96 +16753,68 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD - purls: - - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 63873 timestamp: 1756852097390 -- conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.2.5-he0c23c2_2.conda - sha256: 759ae22a0a221dc1c0ba39684b0dcf696aab4132478e17e56a0366ded519e54e - md5: 82b6eac3c198271e98b48d52d79726d8 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: Apache-2.0 - license_family: Apache - purls: [] - run_exports: - weak: - - xerces-c >=3.2.5,<3.3.0a0 - size: 3574017 - timestamp: 1727734520239 -- conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda - sha256: 9583a8fcf01c59b26a4285bc151b6315fd0bd504e1628f004519dc871eb17073 - md5: d1097e01041cfed41c81f1e3d1f52572 +- conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda + sha256: c0c2fdd02f921388a9f1e455766f65d9b506ade2bba47dc515d467276d1bf621 + md5: e09251a25a8c503eb30375e187de2c93 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache - purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 - size: 3598939 - timestamp: 1766327729418 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-h0e40799_0.conda - sha256: bf1d34142b1bf9b5a4eed96bcc77bc4364c0e191405fd30d2f9b48a04d783fd3 - md5: 105cb93a47df9c548e88048dc9cbdbc9 - depends: - - libgcc >=13 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - ucrt >=10.0.20348.0 - - xorg-libx11 >=1.8.10,<2.0a0 - license: MIT - license_family: MIT - size: 236306 - timestamp: 1734228116846 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-h0e40799_0.conda - sha256: 065d49b0d1e6873ed1238e962f56cb8204c585cdc5c9bd4ae2bf385cadb5bd65 - md5: 570c9a6d9b4909e45d49e9a5daa528de + size: 3602732 + timestamp: 1788145329509 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda + sha256: 85832f409756e78d6d0a92756cd5463fa253219041e40a681d5e8d4c9d21ec8b + md5: a1629a20e5b128c4512b80a93bb1ae55 depends: - - libgcc >=13 - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - libgcc >=14 - ucrt >=10.0.20348.0 - - xorg-libice >=1.1.2,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - size: 97096 - timestamp: 1741896840170 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_0.conda - sha256: eadb12d4597b577cf9bde82a8a2a502a331bd5bfdd60ce508cea93912478e255 - md5: 5a823e21e090f8bc43dbfba00cd2f0e2 + run_exports: + weak: + - xorg-libice >=1.1.2,<2.0a0 + size: 168856 + timestamp: 1786474456144 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda + sha256: bc043a818313abfd836e4e662df1cc9edd6d81994c87a8b35004ca286cebe7c8 + md5: edb94e6121d977d7bc2cc0f56d656c14 depends: - libgcc >=14 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - libxcb >=1.17.0,<2.0a0 - ucrt >=10.0.20348.0 + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - xorg-libice >=1.1.2,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - xorg-libx11 >=1.8.13,<2.0a0 - size: 954604 - timestamp: 1770819901886 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda - sha256: 156a583fa43609507146de1c4926172286d92458c307bb90871579601f6bc568 - md5: 8436cab9a76015dfe7208d3c9f97c156 + - xorg-libsm >=1.2.6,<2.0a0 + size: 116838 + timestamp: 1786545424783 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_1.conda + sha256: 4f86b6f9e8bd76e219440be595bbd7aeba3fe9ad56e4f10c7b41dfd03fb9c3a6 + md5: 369df5999d1a5baf57b53fd892aafc7e depends: - libgcc >=14 - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - libxcb >=1.17.0,<2.0a0 - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - - xorg-libxau >=1.0.12,<2.0a0 - size: 109246 - timestamp: 1762977105140 + - xorg-libx11 >=1.8.13,<2.0a0 + size: 954803 + timestamp: 1787087422301 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda sha256: 892ad69b1a9ecc3903ed5a1b18fa097013eaf462eeed3c6ff31bf5c12e71e84b md5: 87aee04978ab77bf4c0f42b983c216cc @@ -28050,27 +16824,11 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 size: 110446 timestamp: 1786381242485 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda - sha256: 366b8ae202c3b48958f0b8784bbfdc37243d3ee1b1cd4b8e76c10abe41fa258b - md5: a7c03e38aa9c0e84d41881b9236eacfb - depends: - - libgcc >=14 - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - ucrt >=10.0.20348.0 - license: MIT - license_family: MIT - purls: [] - run_exports: - weak: - - xorg-libxdmcp >=1.1.5,<2.0a0 - size: 70691 - timestamp: 1762977015220 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda sha256: 21b11bb98c41ece4ce6069d86d826b50b3d73020fb631b97e59a0521dd9d08a1 md5: 0e21a45f1bb429b6e35e82631e60554a @@ -28080,28 +16838,26 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 size: 71362 timestamp: 1786381239727 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hba3369d_0.conda - sha256: 5966dff3ea3f805e11b5fb466107d64704eb94f00d28818f6891a3ecd075d08e - md5: 74bc8e26c2716e9b1542bef908887b82 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hfa92662_1.conda + sha256: bd39b2950d5144eb6bd5fafc4002fa7bbdcf2f72c32e4709aa080cb4c158ec4a + md5: ff2351642e25ff00ae484a6b533c0c9e depends: - - libgcc >=14 + - libgcc >=15 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 - - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxext >=1.3.7,<2.0a0 - size: 286083 - timestamp: 1769445495320 + size: 288484 + timestamp: 1787101089071 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda sha256: 1d3907533a6e26bb62f109a33107064e2140503a8076de5b28b384ef3e473d27 md5: 39d8a6b9a87047c817e5881fc0706684 @@ -28114,62 +16870,28 @@ packages: - xorg-libxt >=1.3.1,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxpm >=3.5.19,<4.0a0 size: 237565 timestamp: 1776790287445 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-h0e40799_0.conda - sha256: c940a6b71a1e59450b01ebfb3e21f3bbf0a8e611e5fbfc7982145736b0f20133 - md5: 31baf0ce8ef19f5617be73aee0527618 +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxt-1.3.1-hfa92662_1.conda + sha256: 4d1d9b59dc7c2e90f0a1388dd6feb87b54a033bba29f7a7269e35487d332c5be + md5: 41eebca909ba9e18a69eba80152ffb1c depends: - - libgcc >=13 + - libgcc >=15 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 - - xorg-libice >=1.1.1,<2.0a0 - - xorg-libsm >=1.2.4,<2.0a0 - - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT - purls: [] run_exports: weak: - xorg-libxt >=1.3.1,<2.0a0 - size: 918674 - timestamp: 1731861024233 -- conda: https://conda.anaconda.org/conda-forge/win-64/xz-5.8.3-hb6c8415_0.conda - sha256: 2c34f766619921fd543095f26a2688b845fe2cb372b71488d232ad3612630bb7 - md5: 46f70d503d68c55c104a564928a39852 - depends: - - liblzma 5.8.3 hfd05255_0 - - liblzma-devel 5.8.3 hfd05255_0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - xz-tools 5.8.3 hfd05255_0 - license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later - purls: [] - run_exports: - weak: - - liblzma >=5.8.3,<6.0a0 - size: 24697 - timestamp: 1775825746585 -- conda: https://conda.anaconda.org/conda-forge/win-64/xz-tools-5.8.3-hfd05255_0.conda - sha256: 96b8212ff33dd8a1df8c0f69c0ad7b3551496c3068576408c0bedf9260febae2 - md5: 01b20ff45704b888d218f31a3aa3ea2a - depends: - - liblzma 5.8.3 hfd05255_0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - xz 5.8.3.* - license: 0BSD AND LGPL-2.1-or-later - purls: [] - run_exports: {} - size: 68149 - timestamp: 1775825719953 + size: 927081 + timestamp: 1787103397071 - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda sha256: 80ee68c1e7683a35295232ea79bcc87279d31ffeda04a1665efdb43cbd50a309 md5: 433699cba6602098ae8957a323da2664 @@ -28182,26 +16904,25 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT - purls: [] run_exports: weak: - yaml >=0.2.5,<0.3.0a0 size: 63944 timestamp: 1753484092156 -- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-he0c23c2_0.conda - sha256: 031642d753e0ebd666a76cea399497cc7048ff363edf7d76a630ee0a19e341da - md5: 9bb5064a9fca5ca8e7d7f1ae677354b6 +- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda + sha256: 643e9f439d6446c28e85f30b089e0fafbc937e57b7443f9e66d1dc2ef6bce6ff + md5: 04e7e743edaa756d863aaa3bf6c89aad depends: - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT license_family: MIT - size: 148572 - timestamp: 1745308037198 + run_exports: + weak: + - yaml-cpp >=0.8.0,<0.9.0a0 + size: 150033 + timestamp: 1785924043282 - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda sha256: c3e279cb309b153152fcdd6ee6d039ad996d563c849f06be39d85b8e3351df25 md5: f016c0c5f9c01549b259146614786192 @@ -28213,28 +16934,11 @@ packages: - krb5 >=1.22.2,<1.23.0a0 license: MPL-2.0 license_family: MOZILLA - purls: [] run_exports: weak: - zeromq >=4.3.5,<4.3.6.0a0 size: 265717 timestamp: 1779124031378 -- conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda - sha256: ef408f85f664a4b9c9dac3cb2e36154d9baa15a88984ea800e11060e0f2394a1 - md5: 5187ecf958be3c39110fe691cbd6873e - depends: - - libzlib 1.3.2 hfd05255_2 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Zlib - license_family: Other - purls: [] - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 850351 - timestamp: 1774072891049 - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda sha256: 5a0b55df66ff07b4342e04967cef69f8bf348f6a0fb1cc1eca741c7b015dfe77 md5: 945092a9bc1d0f250f7d5ecf51ecd471 @@ -28245,7 +16949,6 @@ packages: - vc14_runtime >=14.44.35208 license: Zlib license_family: Other - purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -28260,37 +16963,14 @@ packages: - vc14_runtime >=14.44.35208 license: Zlib license_family: Other - purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 size: 124542 timestamp: 1770167984883 -- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - sha256: 49241574c373331ae63d9cb4978836db3b2571176a7db81fe48436c84ce38ff4 - md5: e9e25949b682e95535068bae33153ba6 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.12.* *_cp312 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping - run_exports: {} - size: 374949 - timestamp: 1762512770373 -- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_1.conda - sha256: 87bf6ba2dcc59dfbb8d977b9c29d19b6845ad54e092ea8204dcec62d7b461a30 - md5: c1ef46c3666be935fbb7460c24950cff +- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_3.conda + sha256: ba59719687df01c0e846b0308e59d9bbd8215a55532d228d35c14d0ced6bcba4 + md5: 099ca4a359dd7b5cf5140758a433cd58 depends: - python - cffi >=1.11 @@ -28298,42 +16978,24 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/zstandard?source=hash-mapping run_exports: {} - size: 381179 - timestamp: 1762512709971 -- conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - sha256: 368d8628424966fd8f9c8018326a9c779e06913dd39e646cf331226acc90e5b2 - md5: 053b84beec00b71ea8ff7a4f84b55207 + size: 380551 + timestamp: 1787896524598 +- conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda + sha256: ca7daae4f218a11fab82cc2857f0ea518ec3f46acec60490485347a4c22c6b3e + md5: e4ac308c39d6d0e131154976da67cf3b depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - libzlib >=1.3.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD - purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 - size: 388453 - timestamp: 1764777142545 -- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda - sha256: 25fcea0c5103b25ae9535f92ffa631c9b06b5959b09d5911f84947219dc514db - md5: 7baaf81dfa2ac2c3b3ba073bb1b85758 - depends: - - python >=3.12 - - pandas >=3.0.3 - - pandera >=0.31.1 - - python - license: MIT - size: 44569 - timestamp: 1787902306138 + size: 387535 + timestamp: 1786599623274 diff --git a/pixi.toml b/pixi.toml index 2971a4d..6f737ef 100644 --- a/pixi.toml +++ b/pixi.toml @@ -7,7 +7,6 @@ readme = "README.md" channels = [ "conda-forge", "bioconda", - "https://conda.anaconda.org/ddahawkins/label/dev", ] platforms = ["win-64", "linux-64", "osx-arm64"] homepage = "https://www.modelblocks.org/" @@ -43,7 +42,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = {version = "==0.1.1", channel = "https://conda.anaconda.org/ddahawkins/label/dev"} +tclean = "==0.1.1" [feature.test-module.dependencies] pytest = ">=8.3.5" diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 1a8bf92..3a0b090 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -1,323 +1,299 @@ # Generated by `pixi workspace export` # platform: linux-64 @EXPLICIT -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda#ad659d0a2b3e47e38d829aa8cad2d610 -https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda#d87ff7921124eccd67248aa483c23fec -https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda#faac990cb7aedc7f3a2224f2c9b0c26c +https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda#0de0122d9570a8ab637c6b73db268389 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda#89d2c1231f47bd818f5d624b9411459d https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda#a9f577daf3de00bca7c3c76c0ecbd1de -https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda#57736f29cc2b0ec0b6c2952d3f101b6a -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda#cffd3bdd58090148f4cfcd831f4b26ab -https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda#fc21868a1a5aacc937e7a18747acb8a5 -https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda#d7d95fc8287ea7bf33e0e7116d2b95ec -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda#a9965dd99f683c5f444428f896635716 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_0.conda#79dd2074b5cd5c5c6b2930514a11e22d -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda#331ee9b72b9dff570d56b1302c5ab37d -https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc +https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda#cba14d01083fc62ffd32c24d7d390633 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda#676a2f9b1c4fcf57b70aa5c65f6c60d0 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda#ee6c0cd80a60961a1f48aa3e0b91f986 +https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda#69c01c781e7c8190bbdaf79e3848c5ca +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda#0f51e2391ade309db462a55611263e9c +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda#16e3034a330cc625f2c685edec60cf4e +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda#f7a7ff5a6ab331e037abd34f379a631d https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda#01bb81d12c957de066ea7362007df642 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.3-h0c1763c_0.conda#4aed8e657e9ff156bdbe849b4df44389 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda#2f2ef0d96de5bdd8c1270ff22fdf9352 +https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda#72a381cbad04f24b1c2a43ef707f45b4 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda#e72bbec309c2b0f37823ee7d4fabfcd3 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda#b88d90cad08e6bc8ad540cb310a761fb -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda#a360c33a5abe61c07959e449fa1453eb +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda#1390b7c5ac0b1d8e447bc5efa6d3c8c2 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda#6525a0b06aa4fd390795f0740636a9dd https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda#b24d3c612f71e7aa74158d92106318b2 -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13eeac0b5c8e5b8ab496e6c4ddd829 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda#18335a698559cdbcd86150a48bf54ba6 -https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda#d2ffd7602c02f2b316fd921d39876885 -https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda#7eccb41177e15cc672e1babe9056018e -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda#aa459086047c0e5e27023ab19f8cb86a +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda#449500f2c089da11c40f5c21312e3e07 +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda#e675fabcf81499adc7edf58124fb1e01 +https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda#e9dcdd23a1c68738eb3257d23d5f7285 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 -https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 +https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda#880e91eac5d926568ef278e20554148e +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda#c680b5747e8c4c8f23dca0bb7042a8fc https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 -https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda#dfb9a57535eb8c35c6744da7043063f0 -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda#2854d36e093d53d0cf81ec433ffd9c4c +https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda#108c928d2a8551832dbc762b535e90bb +https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda#2d0f9304fc1cb1e3e94e7b37c14cd70a +https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda#936687ed80f295a1f5dbcf8bd34c252c https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda#5794b3bdc38177caf969dabd3af08549 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda#e5ce228e579726c07255dbf90dc62101 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda#85072b0ad177c966294f129b7c04a2d5 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda#42bf7eca1a951735fa06c0e3c0d5c8e6 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda#2d3278b721e40468295ca755c3b84070 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda#00fc660ab1b2f5ca07e92b4900d10c79 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda#809be8ba8712c77bc7d44c2d99390dc4 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda#33a413f1095f8325e5c30fde3b0d2445 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda#d8285bea2a350f63fab23bf460221f3f -https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda#15c437bfa4cbddd379b95357c9aa4150 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda#c22348a769bb072b6184eb6f7f05e4f2 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda#5e92b8413fd1c8f8f3006f4661b12def +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda#25994250f54292352a82eb05ab4499ba +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda#e2ca3eadd889d39b4e4b6b1ecc671816 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda#7b918d4958f359c889c662aa361cf992 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda#a275bd95aa4e22c24120bf6ece6eb056 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda#e70abe6ab4c60ecb6a51e67903bcec07 +https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda#85eb29ade84d1bd97be5ec55607efb00 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.7-py312h4c3975b_0.conda#55f526c3fb5302a1ce922612348442e1 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda#b2895afaf55bf96a8c8282a2e47a5de0 -https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e -https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda#861fb6ccbc677bb9a9fb2468430b9c6a -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda#34e54f03dfea3e7a2dcf1453a85f1085 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda#5423c2b8f82d60320e65b9ff30babbdf +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda#2e66c929f3d879708335b6ea4557c838 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda#f06ef439c280a5f90b8bf62355008dbc +https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda#df2c27f36bdb0dde779f55b5df76a352 +https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda#61f0ffba9161cbb3a77722869b21e4bb +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda#8c282bbe4808a3cc80a5c98e9aec1cfc +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda#e5b6b28536b81b3f4cb20db4668a4642 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda#665d152b9c6e78da404086088077c844 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda#adba2e334082bb218db806d4c12277c9 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda#e192019153591938acf7322b6459d36e +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda#09132e874fe0e1f5e4492b0b6b904b6e +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda#a1412b2b1184dacda45f8476fef2cc25 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda#752a5ac9322e0fbbc24146c1ce3ae44e +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda#e470d224a7a5be1b1d021bded7abb536 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda#798a8c9d171859a022e1cb89e7d0eb10 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda#f2ba4192d38b6cef2bb2c25029071d90 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda#85c9442aec283b4e464fa9ecc484a2f3 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda#aa7459ed9ad086ba11dda843d764b33d +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda#b3e52878163a841f6fb951989cc0b217 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda#4d1fc190b99912ed557a8236e958c559 -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hd6090a7_0.conda#b34c5559f45d8996e3bc0b6250a6cc84 -https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda#94116b69829e90b72d566e64421e1bff -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda#9802ae6d20982f42c0f5d69008988763 -https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 -https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.9-h04c0eec_0.conda#35eeb0a2add53b1e50218ed230fa6a02 +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda#88a47e22b53e50865b1cabe2eb648352 +https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda#06f6113cf1ff4a54b65f87ead132a5f1 +https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda#f92233bf33e24a25668bb2119e2c51f9 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda#20e4d67ec908f0405124f11612c48305 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda#2d34cdb31014cbc8f1e9384c89ede0a5 https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda#b233b41be0bf210989d57160ed39b394 -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda#74e91c36d0eef3557915c68b6c2bef96 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 -https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda#6178c6f2fb254558238ef4e6c56fb782 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda#6c77a605a7a689d17d4819c0f8ac9a00 -https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda#a752488c68f2e7c456bcbd8f16eec275 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927 -https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b -https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 -https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda#f888787e0eab7a8076a67d197e155ffc +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda#9332b53d0ea93c5d39e33be03a0c611a +https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda#c5f5f159b66332152197893427071695 +https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda#898d1c9793eaa52efc4727bd84d2e39a +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda#40f9b31aa9cf007789867df0decd0492 +https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda#fb9d356b1a57d6d54768be7ebd5fce09 +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda#0907d876f460ebc941ae590338b6102f +https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda#50708d3b951d0f8e2d7f2df5b5edc040 +https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda#ba55d1b89fd7775e67de8291029b4059 +https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda#53318d715316929a574f83591308b1f8 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.2-h04577a9_0.conda#52dd46162c6fb2765b49e6fd06adf8d5 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-ha7bfdaf_1.conda#6d2362046dce932eefbdeb0540de0c38 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.0-h2ff4ddf_0.conda#40cdeafb789a5513415f7bdbef053cf5 -https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda#eb83f3f8cecc3e9bff9e250817fc69b6 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda#ec3c4350aa0261bf7f87b8ca15c8e80e -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda#f25206d7322c0e9648e8b83694d143ab -https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda#75e9f795be506c96dd43cb09c7c8d557 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda#35fa2b34bbced424e6976d30f5fde576 -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_1.conda#65514a7ba857e9dbffbffd641f293e33 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e -https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.0-hecd9e04_0.conda#9ad637a7ac380c442be142dfb0b1b955 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.0-default_h746c552_1.conda#327c78a8ce710782425a89df851392f7 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_9.conda#feec6e0f138982e3f7b1ac372fb396cf +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda#af491aae930edc096b58466c51c4126c +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda#680608784722880fbfe1745067570b00 +https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda#72e019c04ed02a179da38c56965802d1 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda#fcf71c8d979148873f6f8ad4cfc73d86 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda#533d342dedadf134c67760ce6fc5b748 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda#b72a266a9317036fd0464cb98b027cd0 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda#f8054e759d0ddddaf34a5c8fedc900a1 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda#f9fe2984587fa8235a6af6004760cd18 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda#fb16b4b69e3f1dcfe79d80db8fd0c55d -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda#e289f3d17880e44b633ba911d57a321b -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda#8462b5322567212beeb025f3519fb3e2 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda#c01af13bdc553d1a8fbfff6e8db075f0 +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda#0ee5bb30034b081a1386c1e2c98ab0a7 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda#e0e050cfa9fa85fe39632ab11cb7f3e0 -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.0.0-h76408a6_0.conda#347cb348bfc8d77062daee11c326e518 -https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda#e211a78613b9d50a096644b97cede8f7 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-hb03c661_0.conda#8904e09bda369377b3dd07e2ac828c5d -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.2-h588cce1_0.conda#4d483b12b9fc7169d112d4f7a250c05c -https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h7a3aeb2_0.conda#31059dc620fa57d787e3899ed0421e6d -https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda#c2bd8055a2e2dce7a7f32cfd02101fb6 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.2-py312h91f0f75_1.conda#8baf6a8672bf235afede64de7a7da1c2 +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda#922776b528a470ab5afa81fd42abfa1d +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda#e83331a940393006789fedddc3b492f3 +https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda#4463210c2a16ff5d3bf7f502af23f1f4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda#3b51576511038b50fdbd05245e22e4b1 +https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda#cfcf11edcfd4acf0094771a9c3b8587f +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda#614e10aae180f87b84f0d1ca8ceb7d9e +https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda#fb00b4fb800f2d431303a5c1625ef9d0 +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda#81141db127a106eb5a91df69a29c9918 +https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda#f5f7fc038c611a25b22758c0a40d25c9 +https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda#3a1b41c4591a0a1734382af3e2b8bc08 +https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda#577800fc8c9d8b7d9727246bbfde704e +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda#35fa2b34bbced424e6976d30f5fde576 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda#64cc91512b6278c315349dc13c53f680 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda#49c553b47ff679a6a1e9fc80b9c5a2d4 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda#7a2499a177753582fb7ae7e9dc4a908a +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda#2ac965638d4c6b2b38383bb1aebaf543 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda#6ab3315dc56618d652c1da42a648a129 +https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda#dbe3ec0f120af456b3477743ffd99b74 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda#ce96f2f470d39bd96ce03945af92e280 +https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda#7094e0d8d14de0eff6d83f0d2f1f661e +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda#2ff98660281d1c6bfebda5bffa52cc89 +https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda#87e6096ec6d542d1c1f8b33245fe8300 +https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda#d207a2e9bae9da476bc0a603215d5167 +https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda#3b8c8325547a4fd8c51649ef7dfab688 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda#d3ba2947f7d7cdd7c4eb73b206de330b +https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda#6afb92887568acc8f18281c57c92c28b +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda#bfcdbf7dfa025bf855ffbf44181c143e +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda#de0dceacf3e33c5fc88e167885dc8274 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 -https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda#2aadb0d17215603a82a2a6b0afd9a4cb +https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda#1726acec19beeed1c764385cd8622405 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda#8b3ce45e929cd8e8e5f4d18586b56d8b -https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda#9e5609720e31213d4f39afe377f6217e -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda#cd74a9525dc74bbbf93cf8aa2fa9eb5b +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda#b68c7304d2edb0f1a5bdfb875094d603 +https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda#d749d04e1965315078f29320565c595a +https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda#1cd10eda5692519d01bb20e086e214c9 +https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda#3ac89a48d224409739dbf6200e524373 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda#6ec45525bac89c279b6e5f4c47eec6bf +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda#2d0ea23b23603e07ca47f23f949f67b6 https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda#0b6c506ec1f272b685240e70a29261b8 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb03c661_4.conda#2e55011fa483edb8bfe3fd92e860cd79 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb03c661_4.conda#5cb5a1c9a94a78f5b23684bcb845338d -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb03c661_4.conda#ca4ed8015764937c81b830f7f5b68543 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda#8929291d9efc59715df1cfa7daf5e3ed +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda#6ff307b78fbfb7dcafb7e25ff8bc9c10 https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda#294fb524171e2a2748cb7fe708aba826 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda#43c2bc96af3ae5ed9e8a10ded942aa50 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda#7d499b5b6d150f133800dc3a582771c7 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda#579c6a324b197594fabc9240bddf2d8b -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda#15995ecb2ef890778ba9a3750190f09d +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda#97eb87f2704fc5212a05b5fb6202ace0 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda#26c8cd6cbd98ec961a2f8969e14a6e4d +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda#f72531e6cf99c98d47306ec917f5e6b9 https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.3-hbc0de68_0.conda#b345ea7f13e4cae9809c69b1d1af2c99 -https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 -https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 -https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda#920bb03579f15389b9e512095ad995b7 -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda#2a45e7f8af083626f009645a6481f12d -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.9.1-hdb1bdb2_0.conda#7da1d242ca3591e174a3c7d82230d3c0 -https://conda.anaconda.org/conda-forge/linux-64/proj-9.4.1-h54d7996_1.conda#e479d1991c725e1a355f33c0e40dbc66 -https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.3-hb03c661_0.conda#b62b615caa60812640f24db3a8d0fc87 -https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.3-ha02ee65_0.conda#8f5e2c6726c1339287a3c76a2c138ac7 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.3-hb03c661_0.conda#55c20edec8e90c4703787acaade60808 -https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.3-ha02ee65_0.conda#6a1b6af49a334e4e06b9f103367762bf -https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda#9dda9667feba914e0e80b95b82f7402b -https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 -https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda#c2a01a08fc991620a74b32420e97868a -https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.2-he02047a_1.conda#aab9195bc018b82dc77a84584b36cce9 -https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hc670b87_16.conda#3d9f3a2e5d7213c34997e4464d2f938c -https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda#fe7b4ff5792fee512aad843294ea809a -https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f -https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h15fa968_9.conda#4957a903bd6a68cc2e53e47476f9c6f4 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda#d42b24574925bfa3167efb3571c905ad +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda#5c526561e1d2a2e071941174eae84557 +https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda#77be60417aa572afcb48cbe0f967401d +https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda#7bc31538d6e3fb349e897353969237ec +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda#3ad2b403be8fc5612b9159e2ce621f69 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda#75d211f04ac87506f1f43420712a69fe +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda#0ed167049513943d078a6c997df2b4e0 +https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda#b23619e5e9009eaa070ead0342034027 +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-h78fed68_2.conda#f2ff0de6c209e90c53b3a195c2708f5f +https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda#7de84fd76ec2bdb7ac617507de3d3052 +https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda#e38a3253d72ab07d471483f24947e2a2 +https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda#6acb86426229f96f93e5468d1df3a5e8 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda#f986d9c9de80813d076d235f753889ff +https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda#5f856b72ca9cd5b5b4a1c43551804622 +https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda#df81fd57eacf341588d728c97920e86d +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda#ade84857af069283ff1e3b7aacae1232 +https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda#66ee86d087145e4d91d8a75437692238 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda#5947bdda89ced07ab0d8a5d6503082c3 https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda#953b7cca897e21215302dbfe2af5cd0c -https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c -https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.7.7-hadbb8c3_0.conda#4a099677417658748239616b6ca96bb6 -https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h1220068_1.conda#f8f0f0c4338bad5c34a4e9e11460481d -https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 -https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.3-hf7fa9e8_2.conda#1d6bdc6b2c62c8cc90c67b50142d7b7f +https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda#d58bfbaaf51ed85771eae92c2e7f5816 +https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda#485788d339785bac87fe86315b4a0627 +https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda#10e0aa58ed390ba598b728badf9eb1b7 +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda#680520cff78a974d564cd99cdf5ec0b5 +https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f +https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda#6d9a90cfee2c7343d6c05a1b37ef75fb https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda#98b6c9dc80eb87b2519b97bcf7e578dd -https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda#54fe76ab3d0189acaef95156874db7f9 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.9.2-h353785f_1.conda#c363d0b330b4b21b4c1b10e0981d3a99 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-xls-3.9.2-h03c987c_7.conda#165f12373452e8d17889e9c877431acf -https://conda.anaconda.org/conda-forge/linux-64/fmt-11.0.2-h07f6e7f_1.conda#fbbfeaa24a99c3da00c35334935b0d24 -https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.14.1-hed91bc2_1.conda#909188c8979846bac8e586908cf1ca6a -https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda#c48fc56ec03229f294176923c3265c05 -https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda#06def97690ef90781a91b786cb48a0a9 -https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda#41c69fba59d495e8cf5ffda48a607e35 -https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda#8f70e36268dea8eb666ef14c29bd3cda -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda#8dabe607748cb3d7002ad73cd06f1325 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.28.0-h26d7fe4_0.conda#2c51703b4d775f8943c08a361788131b -https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.28.0-ha262f82_0.conda#9e7960f0b9ab3895ef73d92477c47dae -https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.13.0-h935415a_0.conda#debd1677c2fea41eb2233a260f48a298 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.7.0-h10ac4d7_1.conda#ab6d507ad16dbe2157920451d662e4a1 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.12.0-hd2e3451_0.conda#61f1c193452f0daa582f39634627ea33 -https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.8.0-hd126650_2.conda#36df3cf05459de5d0a41c77c4329634b -https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda#1b53af320b24547ce0fb8196d2604542 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.19-h756ea98_3.conda#bfe6623096906d2502c78ccdbfc3bc7a -https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.18-h756ea98_11.conda#eadcc12bedac44f13223a2909c0e5bcc -https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda#334dba9982ab9f5d62033c61698a8683 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hfd43aa1_1.conda#f301eb944d297fc879c441fffe461d8a -https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.18-h2af50b2_12.conda#700f1883f5a0a28c30fd98c43d4d946f -https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.19-h756ea98_1.conda#5e08c385a1b8a79b52012b74653bbb99 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.9-h5e77a74_0.conda#d7714013c40363f45850a25113e2cb05 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.30-hec5e740_0.conda#bc1b9f70ea7fa533aefa6a8b6fbe8da7 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.5-hbaf354b_4.conda#2cefeb144de7712995d1b52cc6a3864c -https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.5-h0009854_0.conda#d393d0a6c9b993771fbc67a998fccf6c -https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h235a6dd_1.conda#c05358e3a231195f7f0b3f592078bb0c -https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.28.2-h6c0439f_6.conda#4e472c316d08af60faeb71f86d7563e1 -https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.379-h5a9005d_9.conda#5dc18b385893b7991a3bbeb135ad7c3e -https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.26.0-h86fa3b2_0.conda#061175d9d4c046a1cf8bffe95a359fab -https://conda.anaconda.org/conda-forge/linux-64/libgdal-tiledb-3.9.2-h4a3bace_2.conda#c3fac34ecba2fcf9d5d31a03b975d5a1 -https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026b-h280c20c_0.conda#5c5f9b9bad2a0852ffa38833ba2a1c7d -https://conda.anaconda.org/conda-forge/linux-64/postgresql-17.2-h1122569_0.conda#848402b976b31bfecb3e476ea85cb285 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-postgisraster-3.9.2-h5e77dd0_7.conda#3392965ffc4e8b7c66a532750ce0e91f -https://conda.anaconda.org/conda-forge/linux-64/libgdal-pg-3.9.2-h5e77dd0_7.conda#e86b26f53ae868565e95fde5b10753d3 -https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda#e235d5566c9cc8970eb2798dd4ecf62f -https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda#567fbeed956c200c1db5782a424e58ee -https://conda.anaconda.org/conda-forge/linux-64/poppler-24.08.0-h47131b8_1.conda#0854b9ff0cc10a1f6f67b0f352b8e75a -https://conda.anaconda.org/conda-forge/linux-64/libgdal-pdf-3.9.2-h600f43f_7.conda#567066db0820f4983a6741e429c651d1 -https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda#a7b27c075c9b7f459f1c022090697cba -https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda#86f7414544ae606282352fa1e116b41f -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda#35d07243abf828674d273aecd1dd537e -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_hdf9ad27_105.conda#7e1729554e209627636a0f6fabcdd115 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4 -https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h135f659_114.conda#a908e463c710bd6b10a9eaa89fdf003c -https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf5-3.9.2-h6283f77_7.conda#c8c82df3aece4e23804d178a8a8b308a -https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf4-3.9.2-hd5ecb85_7.conda#9c8431dc0b83d5fe9c12a2c0b6861a72 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-netcdf-3.9.2-hf2d2f32_7.conda#4015ef020928219acc0b5c9edbce8d30 -https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.3-hf8d3e68_2.conda#ffe68c611ae0ccfda4e7a605195e22b3 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-kea-3.9.2-h1df15e4_7.conda#c693e703649051ee9db0fabd4fcd0483 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-jp2openjpeg-3.9.2-h1b2c38e_7.conda#f0f86f8cb8835bb91acb8c7fa2c350b0 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-grib-3.9.2-hc3b29a1_7.conda#56a7436a66a1a4636001ce4b621a3a33 -https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.4.1-hf8ad068_0.conda#1b7a01fd02d11efe0eb5a676842a7b7d -https://conda.anaconda.org/conda-forge/linux-64/libgdal-fits-3.9.2-h2db6552_7.conda#524e64f1aa0ebc87230109e684f392f4 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.9.2-ha770c72_7.conda#63779711c7afd4fcf9cea67538baa67a -https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda#554304a07e581a85891b15e39ea9f268 +https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d +https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda#13e691ba803adfed23773eb0a46e78fc +https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda#2c4bd6aeb90bb157456841c3270a0d92 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 -https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda#c13824fedced67005d3832c152fe9c2f +https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 -https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 -https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.11-py312hd177ed6_1.conda#246c5f31c607ecfe1ece1e8cc6ecc9c5 -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py312h9211aeb_9.conda#173afeb0d112c854fd1a9fcac4b5cce3 -https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda#27583d44e139c520d9fdc1fd7aedd58d -https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.1-py312h4c3975b_0.conda#bb3b931588a7dc83cc7b82836b0017df -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.6-py312h6cab151_1.conda#5be02e05e1adaa42826cc6800ce399bc -https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.10.0-py312he8b4914_0.conda#309f7524c82d168cc055e7b136713693 -https://conda.anaconda.org/conda-forge/linux-64/gdal-3.9.2-py312h1299960_7.conda#9cf27e3f9d97ea13f250db9253a25dc8 -https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h5aa26c2_1.conda#4a30f4277a1894928a7057d0e14c1c95 +https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda#38f624a32029e9fced9ed40fa8c644db +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda#42e2d36478a1fcc0dbc6358ffdb46786 +https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e +https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda#75095e959e733b32c2ae343b1bcd0073 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda#69e400d3deca12ee7afd4b73a5596905 +https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda#f8e7e5ddfbdca16b65335b0b6615eb4c +https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda#a68cae58a81a937a6edcb3e4e6f0bbe7 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda#f8d242c552b0f7f682451ce95879af5e https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda#e6e9b5795bb495325c3b4ebd451519aa https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631 +https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda#6e31007c02f361338281bb78c5b84e7c -https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda#9c5491066224083c41b6d5635ed7107b -https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda#648ee28dcd4e07a1940a17da62eccd40 -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda#02738ff9855946075cbd1b5274399a41 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b -https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda#fd0e7746ed0676f008daacb706ce69e4 -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda#577b04680ae422adb86fc60d7b940659 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089 +https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda#d176f3ed2824f930b524c45eb8f158bb +https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda#e7eb25765bdf21397cc6e30828871625 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e +https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda#4a85203c1d80c1059086ae860836ffb9 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda#93a4752d42b12943a355b682ee43285b https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4 https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda#99d77e0f92f7b0b977f77cfb49c7eaf6 -https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h669347b_0.conda#1e6c10f7d749a490612404efeb179eb8 -https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-hf23e847_1.conda#b1aa0faa95017bca11369bd080487ec4 -https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 -https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda#ff862eebdfeb2fd048ae9dc92510baca -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.11.0-h325d260_1.conda#11d926d1f4a75a1b03d1c053ca20424b -https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-h8d2e343_13_cpu.conda#dc379f362829d5df5ce6722565110029 -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h01725c0_2_cpu.conda#add603bfa43d9bf3f06783f780e1a817 +https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda#6983bfe8e09992014b9cf393769c7a84 +https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda#ee5c400d37b79db5d32a69ed1a79fc0b +https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda#d83f2ee212d217a6d745a00e5be84671 +https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda#1247168fe4a0b8912e3336bccdbf98a5 +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda#fd307b61cceb36fdca38e1718bdb2893 +https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda#1280a5f8270f30b89cc8373ecfe8899d +https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda#a83f6a2fdc079e643237887a37460668 +https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda#fe93be7dd9209e6ae673962e3031ff51 +https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda#6cc68cbbe88746be8beaf027d6c64ad3 +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda#aa4571fc3bcf18ad12370429aa991223 +https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda#f749f223bede1bac7724e49d686ac598 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda#60ff8935e16bf2fd302289ec4f129df8 +https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda#84a8d5a661792da2127ab908b5ae83ef +https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda#04be12e7dd050aaf364ece2393988e85 +https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda#6941f96b8a8056677d79b4f5a2c4aaca +https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda#0cabc152dc8896c3a4c4aebf2b308627 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda#7896f4a6ee78538e2d0261e3b36dfa69 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda#43151a3b0a8e037747d79a82dff9f967 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda#70972c9cb0893d6499dd4118415a966b +https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda#f4fc754ec17176046988c46a54962a8c +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda#f3e0b2e044485ae90d4a59c77e7a0182 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda#816f62fa82532118ebb2398382090945 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda#24ee781effc5779206a80139323c9caf +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda#fa1c00d999e83ec20173c9775f71a1f1 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda#9c6072e7b882d35ac956c07e493d6a9e +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda#bd19b685b88557830f1a617a6d404eb2 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda#c9be3f5854f349ae77a1a174b59e11a8 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda#97f2799ae6ff7b6f52dfa321fef9676b +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda#4347d5ffdf34adf9c5edd10837727d96 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda#706aa99414d18db5b23475b45cc93a0b +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda#9728195c2f600ef427a1964ee193e707 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda#204d1e8d60c3ae839bd1898e4c7742c8 +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda#3315ce09371baf6d70248b8927aa9866 +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda#c65efa87d532339a090c87093097bf09 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda#c344b5608c65a09b341f2e784efb4422 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda#05f6b378ae80d2d06e35dc794412da3a +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda#aff56abfcef5a031603250b7dc23e9d5 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d -https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.20.0-h0e7cc3e_1.conda#d0ed81c4591775b70384f4cc78e05cd1 -https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_13_cpu.conda#49c60a8dc089d8127b9368e9eb6c1a77 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_13_cpu.conda#b654d072b8d5da807495e49b28a0b884 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_13_cpu.conda#cd2c36e8865b158b82f61c6aac28b7e1 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf54134d_13_cpu.conda#46f41533959eee8826c09e55976b8c06 -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_2.conda#5f7d505626cb057e1320bbd46dd02ef2 -https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py312hb3f7f12_1.conda#b99d90ef4e77acdab74828f79705a919 +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda#b6e326fbe1e3948da50ec29cee0380db +https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda#d736a4f89a3b63f9970152923e4c3016 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda#64fad2fca810b34f5bbc9738a6fb298f +https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda#26f7b996f0674217f78d5c6432445b71 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda#287c5915a8b8bf79613840e0df9dc7b7 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda#6f1918b7565c3cc5ed047a97ea510e55 +https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda#a669145a2c834895bdf3fcba1f1e5b9c https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d -https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a +https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda#e741576fb8f89821ac7c1c537322a33d https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda#15878599a87992e44c059731771591cb -https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda#dd94c506b119130aef5a9382aed648e7 -https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.1-py312h0a2e395_1.conda#a142257c1b69e37cfefc66dc228a4d93 +https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda#1653ce584b7859810f116e592eba9c0c +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda#881c695332c119ffd74b0a856f2c8fba https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda#ffc17e785d64e12fc311af9184221839 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda#7d7e6c826ba0743fc491ebee0e7b899c +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e +https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c -https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda#56a3cae23de84509452da890fb2bfd85 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda#29fd0bdf551881ab3d2801f7deaba528 -https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda#2a6851a6c69ce7c0b03a89d5d4209257 -https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda#123fcfe0df4b6fc21804538e59cdaafe -https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda#a2d2a05abf6076c3d041ec91b2235823 +https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda#67d0ed593951a4933024453b76ef1355 +https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda#e79afa78ebd31bb1e76de5fe117edb29 https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda#36a2d7837350f7e8da83bcaad552d0b5 https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda#6f283e8bb11a748bc30af46258683ba8 https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda#5d169c5bc70f98251c4802caf26cad53 -https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a -https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda#9e21f087f087f805debe877d88e00a14 +https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 +https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/linux-64/curl-8.9.1-h18eb788_0.conda#2e7dedf73dfbfcee662e2a0f6175e4bb +https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda#82c2a5fc14064073501f5e1b817f4d80 https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index 602d287..7b923e9 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -1,271 +1,247 @@ # Generated by `pixi workspace export` # platform: osx-arm64 @EXPLICIT -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda#ad659d0a2b3e47e38d829aa8cad2d610 -https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda#bc5a5721b6439f2f62a84f2548136082 -https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda#a9d86bc62f39b94c4661716624eb21b0 -https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda#343d10ed5b44030a2f67193905aea159 -https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda#f8381319127120ce51e081dce4865cf4 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-hbd8a1cb_0.conda#a9965dd99f683c5f444428f896635716 -https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_0.conda#8187a86242741725bfa74785fe812979 -https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.3-h1b79a29_0.conda#7184d95871a58b8258a8ea124ed5aabc -https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda#b1fd823b5ae54fbec272cea0811bd8a9 -https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda#43c04d9cb46ef176bb2a4c77e324d599 +https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda#f39288f0ea63ae962e1a2e4f355a0d75 +https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda#4ec2684c73812cc2c3d78379384a39cc +https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 +https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda#90a01bf394d1c594e0eb9258f967d828 +https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda#3dfa0d0316dc246cd44937a557de4501 +https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda#9347fd56a002e6b58946831d48fe62f6 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda#0f51e2391ade309db462a55611263e9c +https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda#ae71ab40048c19a389a7dcccb86c2481 +https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda#a5efc0b42bb8b42e97d0a29ae3e3c187 +https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda#fde96d40ebe9a9cb34e4122380189ddb +https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda#5303ba06fab927399ed8dfb3227b0af8 +https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda#9ef5086783f252da744fc0f7a336e4d8 +https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda#ff33a4dbd93abc8a798cc4e0e7c8136d +https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda#8ab10323068b107661a4b9a4af84f3b5 +https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda#216bbcc23c11e9695b3db4a8771d76eb https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda#a915151d5d3c5bf039f5ccc8402a436f -https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda#620b85a3f45526a8bc4d23fd78fc22f0 -https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda#8e7608172fa4d1b90de9a745c2fd2b81 -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda#b50612e7d190b8061ab4e7dc119cf4d5 +https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda#86c5773c0f675287d7852068554806c9 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 -https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 -https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py312hb9d4441_0.conda#e38d54c5e4318faa79f71b713b059566 -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda#880e91eac5d926568ef278e20554148e +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda#c680b5747e8c4c8f23dca0bb7042a8fc +https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda#586050b385a407a449fd7e5e4a5c8fef +https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda#108c928d2a8551832dbc762b535e90bb +https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda#2d0f9304fc1cb1e3e94e7b37c14cd70a +https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda#936687ed80f295a1f5dbcf8bd34c252c https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda#a9c118f6343fb6301b6f3b4e94c4c562 -https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda#a44032f282e7d2acdeb1c240308052dd -https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda#644058123986582db33aebd4ae2ca184 -https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda#ba36d8c606a6a53fe0b8c12d47267b3d -https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda#1ea03f87cdb1078fbc0e2b2deb63752c -https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda#909e41855c29f0d52ae630198cd57135 -https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda#dbfe729181a32741ae63ecb41eefbac6 -https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda#85adeb3d469d082dbd9c8c39e36dec57 -https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda#89f76a2a21a3ec3ec983b5eb237c4113 -https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda#03a2ef3491da9e5b4d18c03e9f4b3109 -https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda#d83fc83d589e2625a3451c9a7e21047c -https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda#4581a32b837950217327fcab93214313 +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda#0affff5130a421d11d4d77ffbb00dad7 +https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda#a2d706b4a7d603524133037c34f7fb76 +https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda#408af8d9d5226ff45ff04756ff1aa91e +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda#d54390644d893d50e739b19145aae45f +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda#aa6c627acd0f5709d9c79bf708a7b81b +https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda#0b5dfcfae89beafb81234b90d5d07729 +https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda#37ec75f777eac2ee1322e19d64df82e7 +https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda#35ee607ea2e9f75c699158c5f77ef9fd +https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda#953e30e380c03f058cacffe19bce9dc9 +https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda#07f5745d2ec9349b1401e3891240c64f +https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda#b1dde16791eca59c8316d0ad1487bff0 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.7-py312h2bbb03f_0.conda#d037e9adb0365ab53445f357bd9a035f +https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda#ab1bd70ec9c95d199f8ce456823004ab https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 -https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda#d99c2a23a31b0172e90f456f580b695e -https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda#ab136e4c34e97f34fb621d2592a393d8 -https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda#e5e7d467f80da752be17796b87fe6385 -https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda#b8a7544c83a67258b0e8592ec6a5d322 -https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda#a6130c709305cd9828b4e1bd9ba0000c -https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda#095e5749868adab9cae42d4b460e5443 -https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda#e2a72ab2fa54ecb6abab2b26cde93500 -https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda#2259ae0949dbe20c0665850365109b27 +https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda#4621ded2fd5b36f51454d5f81bff4ec1 +https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda#168a13e329259710b28277abc1395b8e +https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda#b2f8c8e5a7651a1d7c05404c3a159517 +https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda#78650d671cb56909bb3e5c13bce310f9 +https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda#e429aec4037d5cb8fd34ded9f5dadd39 +https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda#b6b191267455bf202af79f973690ed5d +https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda#e0466c58fd07db190b9a81b5e58539f2 https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda#4b5d3a91320976eec71678fad1e3569b -https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda#9d1299ace1924aa8f4e0bc8e71dd0cf7 -https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda#78b548eed8227a689f93775d5d23ae09 -https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda#415816daf82e0b23a736a069a75e9da7 -https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda#af523aae2eca6dfa1c8eec693f5b9a79 -https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda#a0bb0678f67c464938d3693fa96f6884 -https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda#0582e67cd14cfed773be2f3b1aba08e0 -https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda#d2f2c7c10e2957647d45589b7701a453 -https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda#0634560e556adb3e7924668e49ad53fc -https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda#58261af35f0d33fd28e2257b208a1be0 -https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_1.conda#7bd06ab4ed807154c2d9031eb5ebf025 -https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda#45b836f333fd3e282c16fff7dc82994e -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-h6caf38d_4.conda#231cffe69d41716afe4525c5c1cc5ddd -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-h6caf38d_4.conda#4ce5651ae5cd6eebc5899f9bfe0eac3c -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-h6caf38d_4.conda#cb7e7fe96c9eee23a464afd57648d2cd -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-h6caf38d_4.conda#ab57f389f304c4d2eb86d8ae46d219c3 -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-h6caf38d_4.conda#ce8659623cea44cc812bc0bfae4041c5 -https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda#77e64a600d8426c3863942f67491f5fb -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda#f9cce0bc86b46533489a994a47d3c7d2 -https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda#e5d83f3ae6ada32d4e64e366a41f9ff6 -https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda#94174d301ce2f5962197fb9b880ea8ff -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda#173d5eeba324363d9171946e86a81687 -https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.3-h85ec8f2_0.conda#b9df2fcb7e9eba945c1c946c438c4586 -https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda#b68e8f66b94b44aaa8de4583d3d4cc40 -https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda#36d33e440c31857372a72137f78bacf5 -https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda#bcb3cba70cf1eec964a03b4ba7775f01 -https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda#6ea18834adbc3b33df9bd9fb45eaf95b -https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda#44083d2d2c2025afca315c7a172eab2b -https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda#c6dc8a0fdec13a0565936655c33069a1 -https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.9.1-hfd8ffcc_0.conda#be0f46c6362775504d8894bd788a45b2 -https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.4.1-hfb94cee_1.conda#786c3dc1fbc9ca08b82002ab69353c53 -https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.8.3-h8088a28_0.conda#b8c47eab4e58fe7015a12ceb9d5b114c -https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.8.3-hd0f0c4f_0.conda#43d168a8c95a8fcdcc44c2a0a7887653 -https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.3-h8088a28_0.conda#ebe1f5418d6e2d4bbc26b2c906a0a470 -https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.8.3-hd0f0c4f_0.conda#4df12be699991b97b66a85cc46ea75b5 -https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda#5eb22c1d7b3fc4abb50d92d621583137 -https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.2.5-h92fc2f4_2.conda#50b7325437ef0901fe25dc5c9e743b88 -https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-ha881caa_2.conda#1a3f7708de0b393e6665c9f7494b055e -https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.9.4-hb7217d7_0.conda#45505bec548634f7d05e02fb25262cb9 -https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda#4d5a7445f0b25b6a3ddbb56e790f5251 -https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.9-h4a9ca0c_0.conda#763c7e76295bf142145d5821f251b884 -https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda#f1c0bce276210bed45a04949cfe8dc20 -https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.12.2-h00cdb27_1.conda#e07a7fa032741a056a72f46b43064ea2 -https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-h31fb324_16.conda#1a8e3f8e886499916b8942628e6b6880 -https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda#fcd860469a8fa003e25d472b40b0ff81 -https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda#dd655a29b40fe0d1bf95c64cf3cb348d -https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf7a34df_9.conda#a0c631a4cf164b1688484db0da3072b5 -https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802 -https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda#3d8eeedb8e541d1cb593e8204fcc397d -https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda#e56eaa1beab0e7fed559ae9c0264dd88 -https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.7.7-h7c07d2a_0.conda#49b28e291693b70cf8a7e70f290834d8 -https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.17-he54c16a_1.conda#4831302cd6badbdb87c0334163fb7d68 -https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda#95fa1486c77505330c20f7202492b913 -https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.3-h7e5fb84_2.conda#5a9ebf4312dffc9b44f41af5d2a2332e -https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5 -https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h5499902_0.conda#e94ca7aec8544f700d45b24aff2dd4d7 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.9.2-hcf5b7dd_1.conda#98dc71fff9381ffed515c519443b4111 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-xls-3.9.2-habc1c91_7.conda#09290c8b53af1b977967ad9a4734a0e2 -https://conda.anaconda.org/conda-forge/osx-arm64/fmt-11.0.2-h440487c_1.conda#f90a1a21cef4c1e11f421ba29538d704 -https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.14.1-h6d8af72_1.conda#4af518b01539da8e4af17aee5fb92639 -https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda#f16963d88aed907af8b90878b8d8a05c -https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-4.25.3-hc39d83c_1.conda#fa77986d9170450c014586ab87e144f8 -https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda#0b7b2ced046d6b5fe6e9d46b1ee0324c -https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda#0342882197116478a42fa4ea35af79c1 -https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda#e624fc11026dbb84c549435eccd08623 -https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.28.0-hfe08963_0.conda#68fb9b247b79e8ac3be37c2923a0cf8a -https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2#32bd82a6a625ea6ce090a81c3d34edeb -https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.28.0-h1466eeb_0.conda#16874ac519f64d2199fab04fd9bd821d -https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.13.0-hd01fc5c_0.conda#2083f6313e623079db6ee67af00e6b27 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.7.0-hcf3b6fd_1.conda#df7e01bcf8f3a9bfb0ab06778f915f29 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.12.0-hfde595f_0.conda#f2c935764fdacd0fafc05f975fd347e0 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.8.0-h13ea094_2.conda#383b72f2ee009992b21f4db08a708510 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.9.28-hd74edd7_0.conda#8dc8711c903ab57ead8ce99b65625a95 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.1.19-h41dd001_3.conda#53bd7f3e6723288f531387a892d01635 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.1.18-h41dd001_11.conda#c7cd8fb206915662718006953228dbf7 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.7.4-h41dd001_1.conda#3f2c1743ed973b58fd187b0c31861dd8 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.14.18-hc3cb426_12.conda#efdd67503fa663c31d51b399c8f4cc2e -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.2.19-h41dd001_1.conda#98e9d9c62300fd87bee44d2a63792ee5 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.8.9-hf5a2c8c_0.conda#59efa3b4dc632c4fef6911be61ed1779 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.7.30-h338687b_0.conda#385fc8994159e570d0d45223f6b48aa9 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.6.5-h663ac5c_4.conda#1714f0dbabb1431a351c8babe6b75bd9 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.10.5-h9658b26_0.conda#3736531fdfe90d4513b633d907aab907 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.4.3-hb2a355e_1.conda#b84f719ac7c5223ecd2471d86def6bf1 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.28.2-h8f7a527_6.conda#119a410e9b4fe4e31f91a32d7cb4a764 -https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.379-h67f4a54_9.conda#6e081189763244df6240298be3f29823 -https://conda.anaconda.org/conda-forge/osx-arm64/tiledb-2.26.0-h3c94177_0.conda#ad1a0bc095a4e0d1d15eafd888c119b8 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-tiledb-3.9.2-h27a95ea_2.conda#3c57516fbe39e1ac39eaa496e3f10204 -https://conda.anaconda.org/conda-forge/osx-arm64/tzcode-2026b-h1a92334_0.conda#0b3d2910b2d5ac37f2c465fc59313b5d -https://conda.anaconda.org/conda-forge/osx-arm64/libntlm-1.8-h5505292_0.conda#c90c1d3bd778f5ec0d4bb4ef36cbd5b6 -https://conda.anaconda.org/conda-forge/osx-arm64/cyrus-sasl-2.1.28-ha1cbb27_0.conda#2867ea6551e97e53a81787fd967162b1 -https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.10-hbe55e7a_0.conda#6fd5d73c63b5d37d9196efb4f044af76 -https://conda.anaconda.org/conda-forge/osx-arm64/libxslt-1.1.43-h429d6fd_0.conda#f25eb0a9e2c2ecfd33a4b97bb1a84fb6 -https://conda.anaconda.org/conda-forge/osx-arm64/libpq-17.2-h9b1ab17_0.conda#0ba6b6772a08c40de13427c957ceaf67 -https://conda.anaconda.org/conda-forge/osx-arm64/postgresql-17.2-h393ceee_0.conda#75be3b77c1bc7ef0bedaee64cb60bbcb -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-postgisraster-3.9.2-h6a0b679_7.conda#f044c31cdd36806e627e23329c6089b0 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pg-3.9.2-h6a0b679_7.conda#596b2a38085a9352856af7ab3bdefe41 -https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.38-heaf21c2_0.conda#0b528ead848386c8a53ee0b76fbf2ac1 -https://conda.anaconda.org/conda-forge/osx-arm64/nss-3.118-h1c710a3_0.conda#ae07409126ced4f0d982dfeab0681016 +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda#f51e9414bf1b7bb556aac1a0b74a75fe +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda#31d71ce1b056f69b6ec67ee0712bdf97 +https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda#d4852e6054b74645cf49ca10f6349191 +https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda#786c0680e77665d1938ce9cdd7cf82a7 +https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda#881cfb44ea02cc9849f612725a959660 +https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda#ee1fc5bba400ff0cae27fbf141a1ae0c +https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda#39f3afa677c4f1cd499aec3b1f5c76a9 +https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda#4f25498ea1962ab24097fed8700e91ae +https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda#5ab90033816cbe4097e8a297e5179f67 +https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda#17f4744c0873e9f77ac9b5cd0a27c187 https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda#5103f6a6b210a3912faf8d7db516918c -https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.84.0-hdff4504_0.conda#86bdf23c648be3498294c4ab861e7090 +https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda#14ef48eb322e0bfb1e5fe31e047359e3 +https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda#0c7b78d9ffff4f5a6ca28d346f734d8f +https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda#9a99c0b60efe41c194d01c182d000733 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.1-h2b252f5_0.conda#9d928e6a62192141fb6540a3125b1345 -https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda#17c3d745db6ea72ae2fce17e7338547f -https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h6a3b0d2_0.conda#38f6df8bc8c668417b904369a01ba2e2 -https://conda.anaconda.org/conda-forge/osx-arm64/poppler-24.08.0-h37b219d_1.conda#7926153cd183b32ba82966ab548611ab -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pdf-3.9.2-h587d690_7.conda#4323634089f1156bd69a77ad48f53d0d -https://conda.anaconda.org/conda-forge/osx-arm64/libzip-1.11.2-h1336266_0.conda#7177414f275db66735a17d316b0a81d6 -https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda#13e6d9ae0efbc9d2e9a01a91f4372b41 -https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_hec07895_105.conda#f9c8c7304d52c8846eab5d6c34219812 -https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda#ff5d749fd711dc7759e127db38005924 -https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_he469be0_114.conda#8fd3ce6d910ed831c130c391c4364d3f -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf5-3.9.2-h2def128_7.conda#6bbc7e8df9ef22139bc1bab39ba3dd56 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf4-3.9.2-h3847bb8_7.conda#0ff2c29987702b8f7b61c865d951cd90 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-netcdf-3.9.2-h5e0d008_7.conda#438cf785fe8b4d9acabbae8ce6e39cb6 -https://conda.anaconda.org/conda-forge/osx-arm64/kealib-1.5.3-h8edbb62_2.conda#d5c581103f5433dd862acbf24facdf9b -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-kea-3.9.2-h7b2de0b_7.conda#47c89ca8baab301fb54f3b1faa166e4d -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-jp2openjpeg-3.9.2-hd61e619_7.conda#3114191129246e6571d739289bb8083f -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-grib-3.9.2-h6d3d72d_7.conda#f8794c6cd7aaa4cd18ebde3fe10fba07 -https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.4.1-h793ed5c_0.conda#c2a9a79b58d2de021ad9295f53e1f40a -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-fits-3.9.2-h248c7bc_7.conda#f6fddae38163fff25a99adef1765496c -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-3.9.2-hce30654_7.conda#b9ff370534f04743fea9a532bb1cb967 -https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda#554304a07e581a85891b15e39ea9f268 +https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda#8ac8cc3fe744b484de4387703134d8b2 +https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda#ae0e0e8ea304c6ff1608d7918703ba29 +https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda#c931e6d5af7f8f824fab6c474f3a1e94 +https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda#2bb7d7dd91116b8c85e805b0e08cc67b +https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda#46ea0eb4e71bffa35ab7070678bcb053 +https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda#3e24b0e86143022ba7eba8ae276fab3d +https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda#2f4cc7dc2e6622591735c49dda1b92aa +https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda#4fffb3ba871bb05f34ffb705534dfef5 +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda#b457450ba3f27c4749783c0204bd17b0 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda#954c78a9f591bfb12c79beaff7338ec8 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda#e07a99c6fdd984f4d588060f2f936bf4 +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda#d9de6c0ad7e008afdf62fb2ffd450b4a +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda#aa7df8174ee3b34a5ad8a3160429db68 +https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda#0509ee74d95e5b98eb6fe2a47760e399 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 +https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda#cddc851000ce131d757678c2f329eaad +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda#41fb78906bf657af4fbdcf8c67f41c11 +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda#710b3448473bb1bff68d2ba19cbc6c58 +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda#f72531e6cf99c98d47306ec917f5e6b9 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c +https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda#c144cc02c82671616e0fb4caf731bc88 +https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda#d957a8eda98c49b073e8dcfd677c127a +https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda#5d270f716a2b4bc13df9af8612071b17 +https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda#19e86c8a6a47e92bb2e70ca12e758c5c +https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda#4cc01a374215de38387d45e19c8c5c9c +https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda#ac9902dcbe0417f50cf95ab2bde062fa +https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda#843ef89082f368cb889305084d3b483c +https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda#15235dd10450d67bc25ccafd5b46d2bc +https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda#5ae67c128838b686894b4a3aef015c29 +https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda#816e590c5cd37b19c1e5932d8052acc9 +https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda#1f38144f935e874fe7e296658484e719 +https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda#408009d803402dd1ad4ffae452f17353 +https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda#d47f7f3481c6f2fcc4ed22802f61c6dd +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda#f86c0b8ac5c866049717b55df1049c2c +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda#06a3f8eb5cdde56b2d10b49ae3337954 +https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda#2c966485853aa27985fbec7e3fcc4e77 +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda#fff82f66f5775661123c2963930f318b +https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda#e7480ad364d7a5bf03812ba272c39707 +https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda#d07359797436cfc891b38e203cf0caac +https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda#b7819f4118a456301e220e995afd53ac +https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda#fc08d48da7f739ebf2110c3491ae9eff +https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda#8909f6e5df2f99065ef12a6e6c2db274 +https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802 +https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda#3d8eeedb8e541d1cb593e8204fcc397d +https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda#0fd99c6f562545ace82194d79e697ad3 +https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-hb71b141_2.conda#f62de5ae42f3c1f54f87ae2b303f6e90 +https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda#7fad2bcb66997fc0a53442db98031a39 +https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda#bdcec8546f15ded1b3718309e027e603 +https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda#94f14ef6157687c30feb44e1abecd577 +https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda#39725a84a4b2b6b3ae6177f58be48b91 +https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5 +https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda#925acfb50a750aa178f7a0aced77f351 +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda#de41c6d2ce7d0138acd66d6b4122549d +https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda#2c4bd6aeb90bb157456841c3270a0d92 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 -https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda#c13824fedced67005d3832c152fe9c2f +https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 -https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 -https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.3.11-py312h9302994_1.conda#3fb5410bcce1f85eb8de9d9198c739bd -https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.6.1-py312hc2894cb_9.conda#1b6c7a6ae3d2ed8fc284c0015d2679aa -https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda#27583d44e139c520d9fdc1fd7aedd58d -https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.1-py312h2bbb03f_0.conda#85801137e6f7634e0fa9cd7a8070c2d5 -https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.0.6-py312ha6b2466_1.conda#946eaf02209d9b1047d9cffb3167ce78 -https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.10.0-py312hf9e36c7_0.conda#3955ea435842be121db2da0762accb76 -https://conda.anaconda.org/conda-forge/osx-arm64/gdal-3.9.2-py312h936c49d_7.conda#305595575712364e99c69bf43d7078f0 -https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py312hf3c922e_1.conda#268287f41c9d4ba5b426d5206986fbca +https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 +https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda#d2ca5fbd28cd1fd50b2dabf9ffc96df0 +https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda#3bdcaa1d81e1cc32648f6a22517f92e3 +https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e +https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda#c2ca364702b1313c4f42aa39125724e4 +https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda#947d1f4e3160c83140a9c8bcb046fdac +https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda#9b33fa020bd4da86a2dddfd0f63a43ba +https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda#d30d4527aacdf33b1e80ef858f33608c https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc +https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda#e55fe08bb5d43e7120672338dd129030 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 -https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda#b2b777cdad320a08da92bb1f58040bfd +https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda#72cd17b6f8016221faaa96123711f8c9 https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631 +https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda#6e31007c02f361338281bb78c5b84e7c -https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda#9c5491066224083c41b6d5635ed7107b -https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda#503ac138ad3cfc09459738c0f5750705 -https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda#9300889791d4decceea3728ad3b423ec https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b -https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py312h6b01ec3_4.conda#0d50ab05d6d8fa7a38213c809637ba6d -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda#577b04680ae422adb86fc60d7b940659 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089 +https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda#d3a28add84f2412a562355f89ef5e0f5 +https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda#92adf685875ab717f68ad4172ef6de27 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e +https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda#4a85203c1d80c1059086ae860836ffb9 -https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda#11d95ab83ef0a82cc2de12c1e0b47fe4 +https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda#d33c0a15882b70255abdd54711b06a45 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4 https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda#99d77e0f92f7b0b977f77cfb49c7eaf6 -https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.2-h75dedd0_0.conda#9c89e09cede143716b479c5eacc924fb -https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.8.0-hc098a78_1.conda#ed89b8bf0d74d23ce47bcf566dd36608 -https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda#57a511a5905caa37540eb914dfcbf1fb -https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda#fef68d0a95aa5b84b5c1a4f6f3bf40e1 -https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.11.0-h082e32e_1.conda#16b05d31f626717668f01c01a970115f -https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-17.0.0-h20538ec_13_cpu.conda#c6813f605a0fd1947e768b5f6138e0a7 -https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-17.0.0-py312hc40f475_2_cpu.conda#bc617fed2854d65a16760d2bf02a475c +https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda#2aa5e7dc7b5d218908effd2a8c70a8a8 +https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda#b750c4f83563c7528634bdcfd28622ce +https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda#a2578dc92c16ab5b6b183cfcdc8e8b79 +https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda#2255add2f6ae77d0a96624a5cbde6d45 +https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda#7666c238f885d0c7927607ebfc163f97 +https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda#7172339b49c94275ba42fec3eaeda34f +https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda#0debf38c50bb3ea6f712c87310a00289 +https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda#7f61001d82fd50385d4f9fb57f936e95 +https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda#5f7f645a15eedc24da7f271dc2086bcf +https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda#a6c2fc0c5ddf105b3d3e353c1383a492 +https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda#d5626f645bea2fb646e12910c181fc79 +https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda#4f34ca73f16b37fae583ce16c48b5492 +https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2#32bd82a6a625ea6ce090a81c3d34edeb +https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda#36ba29b4cf02ed97ce269939b750961a +https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda#96aea99abfaf8d1e7731c03e3f70fb02 +https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda#68937f90f7930d49e106b94e95d4536c +https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda#4f42d997f42a8d34ff74cb677cf0c828 +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda#2af5d1b5365c4a3de8ed8ec8438fe6ec +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda#0948246cb8e514e4ae1cfe7dbb4046c7 +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda#06ba52b8a66fd041f4910b547e3f3da1 +https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda#e1701f6b2ce6f47aeb6cbfab132403d8 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda#35d52f06a5eaaa66c62dd37a4d82d780 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda#de81e53b45b103470d3be61984dcc292 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda#99cd08b2a8261083e4ad9414cbe737df +https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda#94b3b302cd6c07c5ec68648b25c8c525 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda#5f5ba0cd72e15095ede1ad38e8907f5f +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda#c4d73dc09e972a895d5a3dc7ce158be9 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda#6e0883cca4143f456e019f751d0b9f3b +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda#9e435c44eafb6aba85f0133f4c579d1c +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda#c5d9f5796fb392f0984def10b26f1175 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda#ea0e1e78de6375004530dd9f49d7ae46 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda#742911742b564828dbaaffaa0f1a21eb +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda#4ce3e032bfa6d72114e481c2dc99e2e2 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda#e850d4d6e349471c50fbed1f55fd3069 +https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda#1748ae5f4015e2fec7009ff36cbfafa0 +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda#6c06263450ab8f5f4580effead8b2ca4 +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda#1f47858e8a29295f723b63cd318a31c4 +https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda#d617d0524dd584ed2b4dba355cc3fc52 https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda#1a109764bff3bdc7bdd84088347d71dc -https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.20.0-h64651cc_1.conda#4cf2e5233320648397184415f380c891 -https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-17.0.0-hf0ba9ef_13_cpu.conda#8d415217e6cc74179b5d00a61238b6a9 -https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-17.0.0-hf9b8971_13_cpu.conda#5d52b70e8174f52934d646bbaf0a928b -https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-17.0.0-hf9b8971_13_cpu.conda#35df832aa0371c7bbe9fec5ea1c3139c -https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-17.0.0-hbf8b706_13_cpu.conda#e079f9b14e75bb3f571b1345ce8dad78 -https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-17.0.0-py312ha814d7c_2.conda#04a90c4ce691f2e289658dd475f69631 -https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.3.3-py312h11d1bbd_1.conda#4a9eb3dc0ba76484cae3084b99d008e5 +https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda#d006875f9a58a44f92aec9a7ebeb7150 +https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda#ebf0d32466e8875bf91749744b97165a +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda#5810871245167355126d623aa5ae1096 +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda#5d2b0865a12a8a380c4ad11eae754f30 +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda#e8627e23ad2343bdd0765b49ad7795df +https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda#1a1b2c10602cd3dd7241e485e71d02a2 +https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda#aea17e1b366b814eff15fc3c8c4c1e3c https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d -https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda#78a0fe9e9c50d2c381e8ee47e3ea437d -https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda#95a5f0831b5e0b1075bbd80fcffc52ac -https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda#fd856899666759403b3c16dcba2f56ff -https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.1-py312h3093aea_1.conda#bcab4615e2f53affe6b34fc8887b3f12 +https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda#31cf6dcc138abe673c9440cd6fe6c6fe +https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda#dcf51e564317816cb8d546891019b3ab +https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda#bea6a028c9205ea3789bb38409cbfe43 +https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda#0b9070858d6aaf7f767a520063f68a3c https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda#ffc17e785d64e12fc311af9184221839 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda#7d7e6c826ba0743fc491ebee0e7b899c +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e +https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c -https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda#56a3cae23de84509452da890fb2bfd85 -https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda#49389c14c49a416f458ce91491f62c67 -https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda#2a6851a6c69ce7c0b03a89d5d4209257 -https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda#123fcfe0df4b6fc21804538e59cdaafe -https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda#a2d2a05abf6076c3d041ec91b2235823 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 +https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda#aae872d4d6847677924de8e023e00457 +https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda#67d0ed593951a4933024453b76ef1355 +https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda#e79afa78ebd31bb1e76de5fe117edb29 https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda#36a2d7837350f7e8da83bcaad552d0b5 -https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py312h8eba7c0_2.conda#7d10262860832f3664fb503636a799d5 -https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py312hf57c059_0.conda#99266b4d62fc3d663b084a18b0bb82d5 -https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a -https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda#9e21f087f087f805debe877d88e00a14 +https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda#da101197ce8bfbdad65fc7bed572ab38 +https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda#9bcd9bf5df8950f1f661bdd6c9299c8e +https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 +https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.9.1-hbf5303f_0.conda#93440b8d934e90496e1939ecd72cbf0c +https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda#a78919380afa9842a9c46f35fc7a518c https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index 97688ac..6193b60 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -2,269 +2,251 @@ # platform: win-64 @EXPLICIT https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda#71b24316859acd00bdb8b38f5e2ce328 -https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_39.conda#8b53a83fda40ec679e4d63fa32fae989 -https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_39.conda#06a5bf5a1ca16cce0df6eaa91fc42bc2 -https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_39.conda#2eacea63f545b97342da520df6854276 -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda#ad659d0a2b3e47e38d829aa8cad2d610 -https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda#0481bfd9814bf525bd4b3ee4b51494c4 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.6.17-h4c7d964_0.conda#b9696b2cf00dfeec138c70cee38ed192 -https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.3-hf411b9b_0.conda#e99f95734a326c0fd4d02bbd995150d4 -https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda#dbabbd6234dea34040e631f87676292f -https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.3-hf5d6505_0.conda#051f1b2228e7517a2ef8cca5146c8967 -https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda#8f83619ab1588b98dd99c90b0bfc5c6d -https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda#720b39f5ec0610457b725eb3f396219a +https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda#350bb67a5c8e5f1c53347ac544ab6600 +https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda#ac5333bb3d429361f23adf704cc49a78 +https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda#aa805b5522c2a98fa286e551a1f48546 +https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda#5d2ff29d465097458cc3ff6569151991 +https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda#e4ac308c39d6d0e131154976da67cf3b +https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 +https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda#dd9eb33c99d352b6356f86964d6040f7 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda#e27d2ac27b096dc51fedfcf775a53f9b +https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda#73cb1783f47c452be4c309430fbef89f +https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda#a72d495965b144bb7da033642d389047 +https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda#83748d09d722103f7aa891ecfa13d672 +https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda#5ae92fd6614edd024576e14069d7ad4c +https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda#880a0c8549479b198af21ba5dc49b109 +https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda#ceb38b0ba900847d8da4289c3c8e5708 https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda#ccc490c81ffe14181861beac0e8f3169 -https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda#4cb8e6b48f67de0b018719cdf1136306 -https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda#2956dff38eb9f8332ad4caeba941cfe7 -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d +https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda#c3301c058362f340100d91cd8be0393f +https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda#00cc3f77b4714fd8f297d8f51762bbaf +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 -https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda#53f5409c5cfd6c5a66417d68e3f0a864 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 -https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py312hdabe01f_0.conda#edd458c91ab9bc198012c73289b0b0bc -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda#729843edafc0899b3348bd3f19525b9d -https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#4c06a92e74452cfa53623a81592e8934 +https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda#880e91eac5d926568ef278e20554148e +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda#c680b5747e8c4c8f23dca0bb7042a8fc +https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda#a2e1f7dbbdec163ff40ee75a65bc64dc +https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda#108c928d2a8551832dbc762b535e90bb +https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda#2d0f9304fc1cb1e3e94e7b37c14cd70a +https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda#936687ed80f295a1f5dbcf8bd34c252c https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f -https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda#f6ad7450fc21e00ecc23812baed6d2e4 +https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda#eefc8d916bd2e708d76d40398ef9a1ee https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda#64571d1dd6cdcfa25d0664a5950fdaa2 -https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.9-h741aa76_0.conda#333d21ab129d5fa5742225bf1d7557a5 +https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda#a8a2abdf0f901bc4779d9b7be0845921 +https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda#e596942e8ee6ee17fdcf1e6a77757a66 +https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda#6e7dadff3f3bde2d29d29a3ec34f2d58 +https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda#45a5a1c709a69f14cf176220788d18a9 https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5 -https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h88281d1_1000.conda#e6298294e7612eccf57376a0683ddc80 -https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda#17c38aaf14c640b85c4617ccb59c1146 -https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda#eded4ef8a94852a2e3e4c779b3b6fdda -https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.8-h4fa8253_0.conda#de3551bf6508d45ca46b714639e52823 -https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda#c058cdc8796d5eb260a1aef2cbb7fbbf -https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda#95543eec964b4a4a7ca3c4c9be481aa1 -https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda#7e9cdaf6f302142bc363bbab3b5e7074 -https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda#9e4bf521c07f4d423cba9296b7927e3c -https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda#f9ac74c3b07c396014434aca1e58d362 -https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.3-py312h95189c4_0.conda#9da394ea5e0ec5cc5edc1ad14f2a4d4d +https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda#6a01c986e30292c715038d2788aa1385 +https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda#8ee01a693aecff5432069eaaf1183c45 +https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda#79055ec79e1b43749763122cead30976 +https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda#ace316c48c178d7faa403dee51e30c7b +https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda#0d13dde4466d62a488e58f12aedacc1c +https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda#3a442b27a0ca4153d07b3ceb12fd486f +https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda#7f19a81a13d7e167c42c096ff311a341 +https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda#e4916c642c9aa7ab2d58ec75591189d3 +https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda#a519b50147401cf6bad2fd188028ad38 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.1.1-pyh129be3f_0.conda#7baaf81dfa2ac2c3b3ba073bb1b85758 -https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd +https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.7-py312he06e257_0.conda#1045d29f787812d3fac1fd80a1339710 -https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda#053b84beec00b71ea8ff7a4f84b55207 -https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda#a912b2c4ff0f03101c751aa79a331831 -https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893 -https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda#25a127bad5470852b30b239f030ec95b -https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda#e77030e67343e28b084fabd7db0ce43e -https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda#54b231d595bc1ff9bff668dd443ee012 -https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda#549845d5133100142452812feb9ba2e8 -https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda#52f1280563f3b48b5f75414cd2d15dd1 -https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda#2cf0cf76cc15d360dfa2f17fd6cf9772 -https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.0-h7025463_0.conda#ea8df8a5c5c7adf4c03bf9e3db1637c3 -https://conda.anaconda.org/conda-forge/win-64/libclang13-22.1.8-default_ha2db4b5_2.conda#45a2834578a5f167258aa109af48b036 -https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda#31aec030344e962fbd7dbbbbd68e60a9 -https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda#8579b6bb8d18be7c0b27fb08adeeeb40 +https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda#94dfb208db7d79437cfe9b4390706b5c +https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda#009af999dbe2d1db36a37187a4ca4eb4 +https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda#35a9475e4cc999d52921f57c181979fc +https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_2.conda#aaeed7feddbfd7454f8853a5eae8d902 +https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.2.0-hfd05255_1.conda#fc2c23bacefd1e733f1e1d6cd3b2aaeb +https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda#e4e122e124676a49eebf241399ad8393 +https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda#add59e2b60ac9d4299d17c938185c75a +https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda#970dbe48f843e7fbd179a9b6c22f865a +https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda#5aa7348e73691187c81d51616f17e48a +https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda#36d6e0045173974521fabd42bd5033d6 +https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda#e40bcfee79dbfda3eff751ed098b6ad3 +https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda#8157483eb7ed3fcba71aad3b50a97131 +https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda#740f99c3c91079c03874b809fedace1b https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda#79538e7a7bc024084eda5989f73fde35 -https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda#5187ecf958be3c39110fe691cbd6873e -https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_1.conda#4e4d54f9f98383d977ba56ef39ebf46d -https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_1.conda#e45b52fb9a81c9e2708465a706e05952 -https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_1.conda#e77293b32225b136a8be300f93d0e89f -https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_1.conda#08c8fa3b419df480d985e304f7884d35 +https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda#27c5be39d9e4d25fe85b89a069360d1e https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.1-hd47e2ca_0.conda#abd79bad98c99c1a116154d6de74ea89 -https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda#20e32ced54300292aff690a69c5e7b97 -https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.0.0-h9e37d49_0.conda#b7648427f5b6797ae3904ad76e4c7f19 -https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda#e9a1402439c18a4e3c7a52e4246e9e1c -https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_0.conda#d92e5a0de3263315551d54d5574f5193 -https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h25c3957_0.conda#e84f36aa02735c140099d992d491968d -https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py312h520aab8_0.conda#52a05ba3f802633cb2234bb3edc45888 +https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda#ed95670fed91b091fe34ba6cae6677d7 +https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda#601e2ec8ae0ed934d8788000f984ff18 +https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda#4618efa4303fdda115da50b8090d73c6 +https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda#8ef4beb3cb18e1a876b9af9a757cc1a5 +https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda#cb5d08dc81f52e87c27914b5636cd995 +https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda#10c479888ee4e960587967b2d0c8143a +https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda#93f5a01dec294a2228f757fe2f3432d4 +https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda#3d3caf4ccc6415023640af4b1b33060a +https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda#d98a163070d05c6ed4421432ee1bafb8 +https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda#46034d9d983edc21e84c0b36f1b4ba61 +https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda#f177ba8f56631ec8d8cc1f799642e05d +https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda#7c3ccff96dbc9f8e3c40142c7594ec34 https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda#46a21c0a4e65f1a135251fc7c8663f83 https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda#e723ab7cc2794c954e1b22fde51c16e4 -https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda#f1147651e3fdd585e2f442c0c2fc8f2d +https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda#91189f0bc9ff21f385fcda6232346612 https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda#1626967b574d1784b578b52eaeb071e7 -https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda#cc5d690fc1c629038f13c68e88e65f44 -https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_1.conda#a7c03e38aa9c0e84d41881b9236eacfb -https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_1.conda#8436cab9a76015dfe7208d3c9f97c156 -https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda#3c8f2573569bb816483e5cf57efbbe29 -https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c -https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_1.conda#1df4012c8a2478699d07bc26af66d41e -https://conda.anaconda.org/conda-forge/win-64/pillow-12.2.0-py312h31f0997_0.conda#ba3bcb72a269e7751cadbdd784f84dec -https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py312h78d62e6_0.conda#4ff6f76c2c16c85806ee7533768f5e64 -https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py312he06e257_0.conda#9c46f390eb4b5c6e502df20378e442dd +https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda#02793cf48b68687fee158a48c92850a6 +https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda#0e21a45f1bb429b6e35e82631e60554a +https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda#87aee04978ab77bf4c0f42b983c216cc +https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda#bc97d497656c9c661ffd3043aca90aa4 +https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda#3658b0201b32da9607ea5e3c1c463c1f +https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda#2bd9052d2e496ea54f47d9db8ae4f881 +https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda#09b8e6ac8f4a257b59e5d3025f3c9c1d +https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda#66f5ce9d0d618332023619a899ceb26f +https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda#df92be5685f712989830bdb7ee39383a +https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda#842f2582140b5567bcad1defbbd9b948 +https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda#945092a9bc1d0f250f7d5ecf51ecd471 +https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda#6fc6c09c05a099d58efd9b2e96598e41 +https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda#d6b580a13384df5155c6ca19ee66854e https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-hfd05255_4.conda#58aec7a295039d8614175eae3a4f8778 -https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-hfd05255_4.conda#37f4669f8ac2f04d826440a8f3f42300 -https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-hfd05255_4.conda#bf0ced5177fec8c18a7b51d568590b7c -https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-hfd05255_4.conda#ef022c8941d7dcc420c8533b0e419733 -https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-hfd05255_4.conda#441706c019985cf109ced06458e6f742 -https://conda.anaconda.org/conda-forge/win-64/fonttools-4.63.0-py312h05f76fc_0.conda#2944f5f8ea7e2db9cea01ed951e09194 +https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda#dc04f7b3d82c6fb3fdf4d93e45b6ea2a +https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda#9eba56a007c44dc32d0b9d0a820871ea +https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda#0509ee74d95e5b98eb6fe2a47760e399 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 -https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py312h78d62e6_4.conda#475bd41a63e613f2f2a2764cd1cd3b25 -https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py312h0ebf65c_0.conda#3752482b0df88d7a08a0791f906e87ae -https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.9-py312h2e8e312_0.conda#779fa0e5531694e039385f739ceb88b4 -https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda#099794df685f800c3f319ff4742dc1bb -https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py312h9b3c559_1.conda#0b8c96ed1c04732ff8eb5d481b44c43c +https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda#c26934035616f7d578f9da0491aed3d8 +https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda#88c095cee1bcd48178328c93920870a2 +https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda#d8f7f4bb221698dda04dd955eb6f7e52 +https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda#f72531e6cf99c98d47306ec917f5e6b9 https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c -https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.3-hdb435a2_0.conda#7a28cb97617f0bd6a650b4b88f1f44cb -https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda#9dce2f112bfd3400f4f432b3d0ac07b2 -https://conda.anaconda.org/conda-forge/win-64/libcurl-8.9.1-h18fefc2_0.conda#099a1016d23baa4f41148a985351a7a8 -https://conda.anaconda.org/conda-forge/win-64/proj-9.4.1-hd9569ee_1.conda#6e15f5054b179959d2410c2e53d5a3e4 -https://conda.anaconda.org/conda-forge/win-64/xz-tools-5.8.3-hfd05255_0.conda#01b20ff45704b888d218f31a3aa3ea2a -https://conda.anaconda.org/conda-forge/win-64/liblzma-devel-5.8.3-hfd05255_0.conda#7845201435d0c7c0a02269c2742da1cc -https://conda.anaconda.org/conda-forge/win-64/xz-5.8.3-hb6c8415_0.conda#46f70d503d68c55c104a564928a39852 -https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.2.5-he0c23c2_2.conda#82b6eac3c198271e98b48d52d79726d8 -https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36231-h84cd919_39.conda#2ccc63d7b7d066a814ed9f99072832d7 -https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.4-hcfcfb64_0.conda#e34720eb20a33fc3bfb8451dd837ab7a -https://conda.anaconda.org/conda-forge/win-64/geos-3.12.2-h5a68840_1.conda#019e3460f99eb7c2198c532c50d08791 -https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h6c42fcb_16.conda#4476d717f460b45f5033206bbb84f3f5 -https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda#5d55038c4d640e5df06de1cfb4e8d741 -https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda#d6a8059de245e53478b581742b53f71d -https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-hab0cb6d_9.conda#934f10287da9c46f761abf0ee5f88dd3 +https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda#df9d7c5d34602e0f2655a524d31fce87 +https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda#a21dd9ca39e74910bb96989feb916420 +https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda#39cdf8c4f59e4d253cfa8091c8b3d7de +https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda#50861643cbe90dc7267feb7854b8efdf +https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda#434616a1662714fe1a75a51ace8c72d6 +https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda#e09251a25a8c503eb30375e187de2c93 +https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda#3084e08306d71a87b785efae1fa22564 +https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda#3e0691cb20fcaf922df78ccea08b771a +https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda#daa37dc0bb73bc77322b83f94b72caae +https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda#f3405ff0366fe7ee04580acf8e422d16 +https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda#7eeb5aed49853f8b3e1ca0463ef55a8e +https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda#124e35907d5fb36b1e0cb3b74c20bc9d +https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda#9f0212e4c4bdf5f8f52a1f9793d86311 +https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda#effdac4d017409986c5861d19775eef1 https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda#28b4cf9065681f43cc567410edf8243d https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda#b44e0d9eb84c6c086d809787aab0a1da -https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda#c5cb4159f0eea65663b31dd1e49bbb71 -https://conda.anaconda.org/conda-forge/win-64/libarchive-3.7.7-h88ece9c_0.conda#37c6e11d9f2e69789198ef2bfc661392 -https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.3-h232476a_2.conda#8968032e8f14d84b40a20437707f8ec7 +https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda#972a4e44d5a8c3447769414fd3518d5d +https://conda.anaconda.org/conda-forge/win-64/libjxl-0.12.0-h932607e_2.conda#cf415a2296a1d862e93559645c91fd30 +https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda#3678b093a471615102e97124f37b233f +https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda#74c32a74e1b188a4e14fc359c721e4b9 https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda#3075846de68f942150069d4289aaad63 -https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-h85f69ea_0.conda#2390269374fded230fcbca8332a4adc0 -https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.9.2-h6b59ad6_1.conda#79a9d077a3adaede1031b0b09368577a -https://conda.anaconda.org/conda-forge/win-64/libgdal-xls-3.9.2-hd0e23a6_7.conda#c7366f3ec5ce24208987333a077b42f6 -https://conda.anaconda.org/conda-forge/win-64/fmt-11.0.2-hf4da5c8_1.conda#bde3a8f593f24929b8a6e4be0ff009cf -https://conda.anaconda.org/conda-forge/win-64/spdlog-1.14.1-h9f2357e_1.conda#b9bff07144f2be7ee32f0b83a79ef21d -https://conda.anaconda.org/conda-forge/win-64/libabseil-20240116.2-cxx17_he0c23c2_1.conda#19725e54b7f996e0a5748ec5e9e37ae9 -https://conda.anaconda.org/conda-forge/win-64/libprotobuf-4.25.3-h47a098d_1.conda#2ab67bf04b060ed5af5bc6999f1d6b31 -https://conda.anaconda.org/conda-forge/win-64/libre2-11-2023.09.01-hf8d8778_2.conda#cf54cb5077a60797d53a132d37af25fc -https://conda.anaconda.org/conda-forge/win-64/re2-2023.09.01-hd3b24a8_2.conda#ffeb985810bc7d103662e1465c758847 -https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.6-hfd05255_0.conda#7c6da34e5b6e60b414592c74582e28bf -https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.62.2-h5273850_0.conda#2939e4b5baecfeac1e8dee5c4f579f1a -https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.28.0-h5e7cea3_0.conda#78a31d951ca2e524c6c223d865edd7ae -https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2#cd4cc2d0c610c8cb5419ccc979f2d6ce -https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-2.28.0-he5eb982_0.conda#c60153238c7fcdda236b51248220c4bb -https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.13.0-haf5610f_0.conda#14ed34c3091f89784d926cc7cf4b773b -https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.7.0-h148e6f0_1.conda#9802dfd947dba7777ffcb25078c59c2d -https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.12.0-hf03c1c4_0.conda#093769d5e96a6940cf10086af031dbca -https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.8.0-h148e6f0_2.conda#83ec332c6f07f9e48c8d5706cceab962 -https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.9.28-h2466b09_0.conda#3ffb0664a913a557bf89ed1834d0c12c -https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.1.19-hf1fc857_3.conda#b00e5b1b3985d9dfadde29e8b00f85e4 -https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.1.20-hf1fc857_0.conda#1b66a8719c94d85fa6658d8f46600f21 -https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.7.4-hf1fc857_1.conda#7c01760e07f867666662a4d91e998308 -https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.14.18-h3831a8d_12.conda#360c63cdf64d7bbd99196eb23d753908 -https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.2.19-hf1fc857_1.conda#289e8943be0dce6b1abf60652bc1492e -https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.8.9-heca9ddf_0.conda#c66174f469df56f4e2d6dbdcac7033e0 -https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.7.30-h4ab18a5_0.conda#d48ee1d9eec32116fe1a0a5c92a4cdf7 -https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.6.5-he24745f_5.conda#f6571e7933236c29bcb992338d0f3090 -https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.10.5-h8fec231_0.conda#ac525109aeb0571fe434e4e4d6ecbfef -https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.4.3-hd0ca3c1_2.conda#93704218ce07e4d961299e170ed430b6 -https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.28.2-h2ae5ca2_6.conda#61f5d43cfbabdbace9203a6fd0bd2267 -https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.379-h9ac9443_10.conda#65e55af6d94f870f7a1af70921d91296 -https://conda.anaconda.org/conda-forge/win-64/tiledb-2.26.0-h98a567f_0.conda#451f161732757b5124fc3a320401c587 -https://conda.anaconda.org/conda-forge/win-64/libgdal-tiledb-3.9.2-hb8b5d01_2.conda#acc3612fa26fab59d18d4075d12c15b0 -https://conda.anaconda.org/conda-forge/win-64/libpq-17.2-h7ec079e_0.conda#cf036705ab68a8652529b944de7f86fe -https://conda.anaconda.org/conda-forge/win-64/postgresql-17.2-heca7946_0.conda#e2fa6c7a89d528398dce60b398f05b42 -https://conda.anaconda.org/conda-forge/win-64/libgdal-postgisraster-3.9.2-hfaa227e_7.conda#b24ac84c98145ebf555950076c0417da -https://conda.anaconda.org/conda-forge/win-64/libgdal-pg-3.9.2-hfaa227e_7.conda#c37be85d5b9a3b1fc94a99d6f8327ab1 -https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/win-64/poppler-24.08.0-h9415970_1.conda#4c7b7a4301314afed48c6544c34399e4 -https://conda.anaconda.org/conda-forge/win-64/libgdal-pdf-3.9.2-ha1c78db_7.conda#27fb85b4f988b64aa9ab0e926ddd6b33 -https://conda.anaconda.org/conda-forge/win-64/libzip-1.11.2-h3135430_0.conda#09066edc7810e4bd1b41ad01a6cc4706 -https://conda.anaconda.org/conda-forge/win-64/libaec-1.1.5-haf901d7_0.conda#43b6385cfad52a7083f2c41984eb4e91 -https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.3-nompi_h2b43c12_105.conda#5788de34381caf624b78c4981618dc0a -https://conda.anaconda.org/conda-forge/win-64/hdf4-4.2.15-h5557f11_7.conda#84344a916a73727c1326841007b52ca8 -https://conda.anaconda.org/conda-forge/win-64/libnetcdf-4.9.2-nompi_h92078aa_114.conda#819507db3802d9a179de4d161285c22f -https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf5-3.9.2-had131a1_7.conda#7e62763a83b3730f5058daefb4962053 -https://conda.anaconda.org/conda-forge/win-64/libgdal-hdf4-3.9.2-h430f241_7.conda#720ff75366c6e31259ccd53b73b27abf -https://conda.anaconda.org/conda-forge/win-64/libgdal-netcdf-3.9.2-h55e78d3_7.conda#55406e979f268a570952c49fe941eb04 -https://conda.anaconda.org/conda-forge/win-64/kealib-1.5.3-h6c43f9b_2.conda#873b3deabbefe46d00cc81ce7d9547a7 -https://conda.anaconda.org/conda-forge/win-64/libgdal-kea-3.9.2-h95b1a77_7.conda#a20cfdcc279d52808b1a53296bfd308c -https://conda.anaconda.org/conda-forge/win-64/libgdal-jp2openjpeg-3.9.2-hed4c6cb_7.conda#4135def2658a84e8739677a39eadd200 -https://conda.anaconda.org/conda-forge/win-64/libgdal-grib-3.9.2-hd2a089b_7.conda#dd3572ebb1832021bb0bd1dc06b56043 -https://conda.anaconda.org/conda-forge/win-64/cfitsio-4.4.1-hc2ea260_0.conda#b3263858e6a924d05dc2e9ce335593ba -https://conda.anaconda.org/conda-forge/win-64/libgdal-fits-3.9.2-h0a0b71e_7.conda#d24f52a1eeec99436f3e7b548ae4d048 -https://conda.anaconda.org/conda-forge/win-64/libgdal-3.9.2-h57928b3_7.conda#08b7dfed6465467d50389c323e7b2a15 +https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda#357d7be4146d5fec543bfaa96a8a40de +https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda#6f86ca570061dd7f251ab5554eb610a5 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda#f73f35eedcd8e89d6c4407df15101233 +https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda#8a0d65027e25e367f9f1754f0604e8de https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 -https://conda.anaconda.org/conda-forge/noarch/certifi-2026.6.17-pyhd8ed1ab_0.conda#c13824fedced67005d3832c152fe9c2f +https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 -https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 -https://conda.anaconda.org/conda-forge/win-64/rasterio-1.3.11-py312he4a2ebf_1.conda#e301914ba7afd52aef42e85d29a0f3ab -https://conda.anaconda.org/conda-forge/win-64/pyproj-3.6.1-py312h6f27134_9.conda#a7414c734b08e74d22581a9a07686301 -https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.20.0-pyhd8ed1ab_0.conda#27583d44e139c520d9fdc1fd7aedd58d -https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.1-py312he06e257_0.conda#ed32da1950de9f6ee034afdeab563b25 -https://conda.anaconda.org/conda-forge/win-64/shapely-2.0.6-py312h3a88d77_1.conda#4f5c4f3160397a63c7b15c81f6c0e9b3 -https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.10.0-py312h8705084_0.conda#90249b9c726133a702cddb06c1d9dab9 -https://conda.anaconda.org/conda-forge/win-64/gdal-3.9.2-py312h16ac12d_7.conda#4a093f053f2827cfa102304bf945e845 -https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py312hd215820_1.conda#1b03775deb65180f956a7c1b0e6edc5f +https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 +https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda#195d18c86203797871e15b2907579a84 +https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda#b7503bde09c3bce5c09d81485855f5a8 +https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e +https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda#2f613befc24a58c3935b1be38db94355 +https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda#c5a64ac49215efe79837adbd7e42b605 +https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda#da6ef5e9a5931e73a7638b60fd82fc63 +https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda#c1bae1515ff287d5bf6e287dddbc6646 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc +https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda#a925cfb1429da2b1312c86516ae7c418 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda#9450fb40fb1e147d0bcbdf07cd02ca96 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 -https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py312hea30aaf_0.conda#0637562978d4231902754645e4c28640 +https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda#74bafde39f688cb95c111e74bfad6669 https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631 +https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.4-pyha770c72_0.conda#6e31007c02f361338281bb78c5b84e7c -https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda#9c5491066224083c41b6d5635ed7107b -https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py312he06e257_1.conda#46f7dccfee37a52a97c0ed6f33fcf0a3 -https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda#e9e25949b682e95535068bae33153ba6 https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda#46e441ba871f524e2b067929da3051c2 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda#e2fd202833c4a981ce8a65974fe4abd1 https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b -https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py312hbb81ca0_4.conda#3bb5cbb24258cc7ab83126976d36e711 -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/idna-3.18-pyhcf101f3_0.conda#577b04680ae422adb86fc60d7b940659 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089 +https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 +https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda#b1ff58c1f0deedd3f25c103e37049cee +https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda#92adf685875ab717f68ad4172ef6de27 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e +https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda#4a85203c1d80c1059086ae860836ffb9 -https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda#a73298d225c7852f97403ca105d10a13 +https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda#8de7b40f8b30a8fcaa423c2537fe4199 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4 https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.4-pyhd8ed1ab_0.conda#99d77e0f92f7b0b977f77cfb49c7eaf6 -https://conda.anaconda.org/conda-forge/win-64/orc-2.0.2-h784c2ca_0.conda#dbb01d6e4f992ea4f0dcb049ab926cc7 -https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.8.0-hb602f4b_1.conda#4dce7215af5e642fe84a07321c0628f6 -https://conda.anaconda.org/conda-forge/win-64/libarrow-17.0.0-h29daf90_13_cpu.conda#d0ea8c4474c45aae86eff71a0f293013 -https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-17.0.0-py312h6a9c419_2_cpu.conda#32ae28e98c1a84532ddb5dc7468edc4f +https://conda.anaconda.org/conda-forge/win-64/libabseil-20260526.0-cxx17_h04e5de1_2.conda#7cd4be232c8e6388fcf5fadc7f429dd7 +https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda#52d096d535c65d26ffb4b84a4bba2aa1 +https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda#5d51319ef836277672d665c2fc04bab0 +https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda#5f34fcb6578ea9bdbfd53cc2cfb88200 +https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda#4d0c07f8d96e268fc4221c19e51fa165 +https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda#d0e00e318dceb35f9a66f52225609ed2 +https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda#128297355faf0afcb84e22e43d472101 +https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda#faf4e3f7981777629dd7cbef578834ad +https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-headers-1.27.0-h57928b3_1.conda#85e4342d7e798c99a828d59303909d09 +https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda#b5a0be28d048d8bbe81df51e4b750500 +https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda#343241db44cbda08b4b41689608a07af +https://conda.anaconda.org/conda-forge/win-64/libopentelemetry-cpp-1.27.0-h4cd8edf_1.conda#300e6113eae2004627a2c2233bbd5a55 +https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda#4957e887b8ff6e7c1108e217b879ebc3 +https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda#21504569fa34b5d3a67760219e3ff1cc +https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2#cd4cc2d0c610c8cb5419ccc979f2d6ce +https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda#58728b379859ceb2d41642f49411e440 +https://conda.anaconda.org/conda-forge/win-64/azure-core-cpp-1.16.3-h49e36cd_0.conda#1e8ee0cbdb1822de68e488e98cd8a31e +https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda#dbaa5d09d06d06a5febe0984667aa6d2 +https://conda.anaconda.org/conda-forge/win-64/azure-storage-blobs-cpp-12.18.0-h4fb3251_1.conda#7d38f326139cefa0a9c2ec505c0177b1 +https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda#59a1891edca316111ad277dc3535f2f0 +https://conda.anaconda.org/conda-forge/win-64/azure-identity-cpp-1.13.3-hf9fdf8e_2.conda#d1842ddabf2087c50e5305fc4f613cec +https://conda.anaconda.org/conda-forge/win-64/aws-c-common-0.14.2-hfd05255_0.conda#75b4445fec6477b271920f87830d22a3 +https://conda.anaconda.org/conda-forge/win-64/aws-c-sdkutils-0.2.7-h1da161f_2.conda#882d834f12e0abf4c7a0e9bb0c72e281 +https://conda.anaconda.org/conda-forge/win-64/aws-checksums-0.2.10-h1da161f_4.conda#a3c81085892f2983979836f2937291ce +https://conda.anaconda.org/conda-forge/win-64/aws-c-cal-0.9.14-h032d170_4.conda#fda8fa85ef5d8570d5a0aadea688bb82 +https://conda.anaconda.org/conda-forge/win-64/aws-c-io-0.27.3-hbdc738f_1.conda#579600368b15fb523d69e2a3f8a9bc55 +https://conda.anaconda.org/conda-forge/win-64/aws-c-compression-0.3.2-h1da161f_4.conda#996e5c7da8f9e255eac094d2413b40c3 +https://conda.anaconda.org/conda-forge/win-64/aws-c-http-0.11.0-h667fc28_4.conda#c17a284b159959a8639298ed7da8ad0b +https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda#f7069cdc81f7a5e781f1faf6aab6c171 +https://conda.anaconda.org/conda-forge/win-64/aws-c-s3-0.12.8-hd7ee1a1_1.conda#4ac02fa15bf3809101e2eeb340b6078c +https://conda.anaconda.org/conda-forge/win-64/aws-c-mqtt-0.16.0-hd7b40c3_2.conda#87bbe253aafe6d570a176b6aa2ef9c68 +https://conda.anaconda.org/conda-forge/win-64/aws-c-event-stream-0.7.1-h88938e3_4.conda#6f00186d3ab808e5b04c6063f1e2b48c +https://conda.anaconda.org/conda-forge/win-64/aws-crt-cpp-0.40.1-he7c1723_3.conda#2b25be5d69a7ed4a63cde762e82cd1e0 +https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.conda#0d6b7d24ad2b8454b061404b76382e0d +https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda#082ecab7d62fcf79ce26236b80e76578 +https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda#5231ee28028c0e342550873d25d21d07 +https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda#54155aba0ef1fe72c52022617e453d52 https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda#25efbd786caceef438be46da78a7b5ef -https://conda.anaconda.org/conda-forge/win-64/libthrift-0.20.0-hbe90ef8_1.conda#e9f49c00773250da4f622694b7f83f25 -https://conda.anaconda.org/conda-forge/win-64/libparquet-17.0.0-ha915800_13_cpu.conda#30b08e672c5dcd827ce7b44f01f4821e -https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-17.0.0-he0c23c2_13_cpu.conda#1a38e993ef119557596ae20cd68a1207 -https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-17.0.0-he0c23c2_13_cpu.conda#dd78096e1335abc3c7bf6915d0ac7c34 -https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-17.0.0-h1f0e801_13_cpu.conda#b618c36e7eff7a28a53bde4d9aa017e0 -https://conda.anaconda.org/conda-forge/win-64/pyarrow-17.0.0-py312h7e22eef_2.conda#5601751f674c1ecc1629cdf76f22ab61 -https://conda.anaconda.org/conda-forge/win-64/lz4-4.3.3-py312h0608a1d_1.conda#90e9d18bcbfe59ac7d6064a58432f365 +https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda#42856184560e5cf901551fd414ad25c1 +https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda#6fd6233ec7bd1ee2423a28eab21e8f75 +https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda#1f948e54f605402994877c1df640c668 +https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda#592863e63dbcec46b876042faf6158b2 +https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda#d28db517034293dbb393d649c5063b68 +https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda#7c6d60d53f7b0b09e424b58cd9fdfd37 +https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda#8e9b2740fe44700706e8e4ca7e4fb8b6 https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88bb644823094f436792f80fba3207e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda#433699cba6602098ae8957a323da2664 -https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda#9f6ebef672522cb9d9a6257215ca5743 -https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda#a2724c93b745fc7861948eb8b9f6679a -https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.1-py312h78d62e6_1.conda#0ac1766b278a8547f22cd0820b8a7e96 +https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda#0cd9b88826d0f8db142071eb830bce56 +https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda#deeef9494cfffa58f5fd20bc6eb57664 +https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda#921a2c141bc15cf74a238a8fb928554a https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda#ffc17e785d64e12fc311af9184221839 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.6.0-pyhd8ed1ab_0.conda#7d7e6c826ba0743fc491ebee0e7b899c +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e +https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c -https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.6.0-pyhc364b38_0.conda#56a3cae23de84509452da890fb2bfd85 -https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py312he06e257_2.conda#978c009bc3f0add939e44aff97bfaee1 -https://conda.anaconda.org/conda-forge/noarch/distributed-2026.6.0-pyhc364b38_1.conda#2a6851a6c69ce7c0b03a89d5d4209257 -https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.1-pyhd8ed1ab_0.conda#123fcfe0df4b6fc21804538e59cdaafe -https://conda.anaconda.org/conda-forge/noarch/dask-2026.6.0-pyhc364b38_0.conda#a2d2a05abf6076c3d041ec91b2235823 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 +https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda#8309b55a55dac8050260de935d31724a +https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda#67d0ed593951a4933024453b76ef1355 +https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda#e79afa78ebd31bb1e76de5fe117edb29 https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda#36a2d7837350f7e8da83bcaad552d0b5 -https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py312h7fb921c_2.conda#7a20e585f458d44b5fd1e59bb7d95e32 -https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py312h196c9fc_0.conda#caa98e34aa51b84fc70effb79404a89d -https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a -https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda#9e21f087f087f805debe877d88e00a14 +https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda#3d84e97e6606a7e6bb8096afb53e9bfc +https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda#bfc434b47d15ca2f2afa329394fa138c +https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 +https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/win-64/curl-8.9.1-h1ee3ff0_0.conda#45d3504d24c6a853f22bb93d721d9d20 +https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda#b0dcd6182bfd56a219563a153d550d20 https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index 8aa9d16..a28cccd 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -2,7 +2,6 @@ name: module channels: - conda-forge - bioconda -- https://conda.anaconda.org/ddahawkins/label/dev - nodefaults dependencies: - curl >=8.9.1 @@ -21,4 +20,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 -- ddahawkins/label/dev::tclean ==0.1.1 +- tclean ==0.1.1 From f668335b8d1f406d3d21d80a2f225f9d1c19b355 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:19:49 +0000 Subject: [PATCH 156/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/integration/profiles/default/config.yaml | 2 +- workflow/profiles/default/config.yaml | 2 +- workflow/rules/automatic.smk | 16 ++++++---------- workflow/scripts/download_load_entsoe.py | 1 - workflow/scripts/sources/entsoe/prepare.py | 4 +--- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tests/integration/profiles/default/config.yaml b/tests/integration/profiles/default/config.yaml index 4c2f606..4122207 100644 --- a/tests/integration/profiles/default/config.yaml +++ b/tests/integration/profiles/default/config.yaml @@ -9,4 +9,4 @@ cores: 2 #Download Resources: conservative defaults to minimise API traffic. resources: entsoe_download: 2 - neso_download: 2 \ No newline at end of file + neso_download: 2 diff --git a/workflow/profiles/default/config.yaml b/workflow/profiles/default/config.yaml index 4c2f606..4122207 100644 --- a/workflow/profiles/default/config.yaml +++ b/workflow/profiles/default/config.yaml @@ -9,4 +9,4 @@ cores: 2 #Download Resources: conservative defaults to minimise API traffic. resources: entsoe_download: 2 - neso_download: 2 \ No newline at end of file + neso_download: 2 diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index c32e3c5..9c821c4 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -38,10 +38,7 @@ rule download_load_entsoe_country_year: input: token_entsoe="", output: - annual_file=( - "/automatic/entsoe/raw/" - "{country}/{year}.parquet" - ), + annual_file=("/automatic/entsoe/raw/" "{country}/{year}.parquet"), log: "/download_load_entsoe_{country}_{year}.log", wildcard_constraints: @@ -51,11 +48,11 @@ rule download_load_entsoe_country_year: conda: "../envs/module.yaml" threads: 1 + resources: + entsoe_download=1, params: country_code=lambda wildcards: wildcards.country, year=lambda wildcards: int(wildcards.year), - resources: - entsoe_download=1 message: ( "Download ENTSO-E electricity load for " @@ -89,12 +86,11 @@ rule download_load_neso_year: localrule: True conda: "../envs/module.yaml" + threads: 1 + resources: + neso_download=1, params: year=lambda wildcards: int(wildcards.year), - threads: - 1 - resources: - neso_download=1 message: "Download NESO historic electricity demand for {wildcards.year}." script: diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index e32dfc6..302534a 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any import pandas as pd - from sources.entsoe.download import download_entsoe if TYPE_CHECKING: diff --git a/workflow/scripts/sources/entsoe/prepare.py b/workflow/scripts/sources/entsoe/prepare.py index 7d8b5c9..dbe4be1 100644 --- a/workflow/scripts/sources/entsoe/prepare.py +++ b/workflow/scripts/sources/entsoe/prepare.py @@ -8,9 +8,7 @@ from tclean import TimeGrid -def _read_country_year_files( - input_paths: Iterable[str | Path], -) -> dict[str, pd.Series]: +def _read_country_year_files(input_paths: Iterable[str | Path]) -> dict[str, pd.Series]: """Read and combine reusable ENTSO-E country-year files by country.""" annual_series: dict[str, list[pd.Series]] = defaultdict(list) From 709dfbc205f7d9e61b8b903cd72a30d00def0fcb Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:26:37 +0200 Subject: [PATCH 157/241] Updated spelling for the spelling police. --- workflow/rules/_utils.smk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 96098fb..75e98fa 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -4,7 +4,7 @@ from datetime import datetime, timedelta, timezone def _as_utc(value): - """Interpret naive timestamps as UTC and convert aware timestamps to UTC. Mirros tclean logic.""" + """Interpret naive timestamps as UTC and convert aware timestamps to UTC. Mirrors tclean logic.""" parsed = datetime.fromisoformat(str(value)) if parsed.tzinfo is None: @@ -14,7 +14,7 @@ def _as_utc(value): def years_for_period(start, end): - """Return UTC calendar years intersected by the half-open period [start, end). Mirros tclean logic.""" + """Return UTC calendar years intersected by the half-open period [start, end). Mirrors tclean logic.""" start = _as_utc(start) end = _as_utc(end) From 93c5edc3a89785a9671e231fb18dfe0bea814cec Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:28:00 +0200 Subject: [PATCH 158/241] Unfreezing pixi tclean dependency --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 6f737ef..da32d06 100644 --- a/pixi.toml +++ b/pixi.toml @@ -42,7 +42,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = "==0.1.1" +tclean = ">=0.1.1" [feature.test-module.dependencies] pytest = ">=8.3.5" From 758c9a45fe95bb6556224e50df11e03aa1d5bd6d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 12:44:40 +0200 Subject: [PATCH 159/241] Updating the README to explain the optional user flags for snakemake resources. --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d12ae2..f96d0f1 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,19 @@ To test a minimal example of a workflow using this module: ```shell pixi shell # activate this project's environment cd tests/integration/ # navigate to the integration example -snakemake --use-conda --cores 2 # run the workflow! +snakemake # run the workflow! +``` + +The integration workflow's default Snakemake profile enables Conda, uses 2 cores, and limits concurrent ENTSO-E and NESO downloads to 2 each. These execution settings can be overridden with the corresponding Snakemake command-line options: + +- **Cores**: Defaults can be overridden using the `--cores` flag. +- **ENTSOE Downloads**: Defaults can be overridden using the `--enstoe_download` flag. +- **NESO Downloads**: Defaults can be overridden using the `--neso_download` flag. + +A complete example: + +```shell +snakemake --cores 4 --resources entsoe_download=1 neso_download=1 ``` ## References From bdd8133a686d8030b896ed27196393fefc4f9e92 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 14:33:57 +0200 Subject: [PATCH 160/241] Implemented ENTSOE Power Statistics as a new source (2019-2025 inclusive) --- workflow/rules/_utils.smk | 17 + workflow/rules/automatic.smk | 27 ++ workflow/rules/clean.smk | 49 ++- .../download_load_entsoe_power_statistics.py | 36 ++ .../prepare_load_entsoe_power_statistics.py | 48 +++ .../entsoe_power_statistics/download.py | 355 ++++++++++++++++++ .../entsoe_power_statistics/prepare.py | 81 ++++ 7 files changed, 608 insertions(+), 5 deletions(-) create mode 100644 workflow/scripts/download_load_entsoe_power_statistics.py create mode 100644 workflow/scripts/prepare_load_entsoe_power_statistics.py create mode 100644 workflow/scripts/sources/entsoe_power_statistics/download.py create mode 100644 workflow/scripts/sources/entsoe_power_statistics/prepare.py diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 75e98fa..150a1b6 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -3,6 +3,11 @@ from datetime import datetime, timedelta, timezone +ENTSOE_POWER_STATISTICS_YEARS = set( + range(2019, 2026) +) + + def _as_utc(value): """Interpret naive timestamps as UTC and convert aware timestamps to UTC. Mirrors tclean logic.""" parsed = datetime.fromisoformat(str(value)) @@ -43,6 +48,18 @@ def entsoe_annual_files(countries, years): ] +def entsoe_power_statistics_annual_files(years): + """Return supported annual ENTSO-E Power Statistics files.""" + return [ + ( + "/automatic/" + "entsoe_power_statistics/raw/" + f"{int(year)}.parquet" + ) + for year in years + if int(year) in ENTSOE_POWER_STATISTICS_YEARS + ] + def additional_config_validation(): """Validate configuration relationships that require no module dependencies.""" gap_filling = config["gap_filling"] diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 9c821c4..d042291 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -62,6 +62,33 @@ rule download_load_entsoe_country_year: "../scripts/download_load_entsoe.py" +rule download_load_entsoe_power_statistics_year: + output: + annual_file=( + "/automatic/entsoe_power_statistics/raw/{year}.parquet" + ), + log: + ( + "/download_load_entsoe_power_statistics_{year}.log" + ), + wildcard_constraints: + year="2019|2020|2021|2022|2023|2024|2025", + localrule: True + conda: + "../envs/module.yaml" + threads: 1 + resources: + entsoe_download=1 + params: + year=lambda wildcards: int(wildcards.year), + message: + ( + "Download ENTSO-E Power Statistics " + "electricity load for {wildcards.year}." + ) + script: + "../scripts/download_load_entsoe_power_statistics.py" + rule download_load_opsd: output: load=update("/automatic/opsd/raw_load.parquet"), diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 5585787..1b7f4f2 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,3 +1,10 @@ +LOAD_SOURCE_PATHS = { + "entsoe": ("/automatic/" "load_entsoe.parquet"), + "neso": ("/automatic/" "load_neso.parquet"), + "opsd": ("/automatic/" "load_opsd.parquet"), + "entsoe_power_statistics": ("/automatic/" "load_entsoe_power_statistics.parquet") +} + def entsoe_raw_files(_wildcards): """Return ENTSO-E country-year files required by the configured period.""" years = years_for_period( @@ -18,6 +25,17 @@ def neso_raw_files(_wildcards): return neso_annual_files(years) +def entsoe_power_statistics_raw_files(_wildcards): + """Return Power Statistics annual files required by the target period.""" + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], + ) + + return entsoe_power_statistics_annual_files( + years + ) + rule prepare_load_entsoe: input: validation="/automatic/temporal_config_validation.json", @@ -82,11 +100,32 @@ rule prepare_load_neso: "../scripts/prepare_load_neso.py" -LOAD_SOURCE_PATHS = { - "entsoe": ("/automatic/" "load_entsoe.parquet"), - "neso": ("/automatic/" "load_neso.parquet"), - "opsd": ("/automatic/" "load_opsd.parquet"), -} +rule prepare_load_entsoe_power_statistics: + input: + validation=( + "/automatic/" + "temporal_config_validation.json" + ), + annual_files=entsoe_power_statistics_raw_files, + output: + load=( + "/automatic/" + "load_entsoe_power_statistics.parquet" + ), + params: + temporal_start=config["temporal_scope"]["start"], + temporal_end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], + log: + "/prepare_load_entsoe_power_statistics.log", + conda: + "../envs/module.yaml" + message: + "Prepare electricity-demand data from ENTSO-E Power Statistics." + script: + "../scripts/prepare_load_entsoe_power_statistics.py" + def configured_load_inputs(_wildcards): diff --git a/workflow/scripts/download_load_entsoe_power_statistics.py b/workflow/scripts/download_load_entsoe_power_statistics.py new file mode 100644 index 0000000..3263bdd --- /dev/null +++ b/workflow/scripts/download_load_entsoe_power_statistics.py @@ -0,0 +1,36 @@ +"""Snakemake entry point for annual ENTSO-E Power Statistics data.""" + +import logging +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from sources.entsoe_power_statistics.download import ( + download_entsoe_power_statistics_year, +) + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Download and harmonise one ENTSO-E Power Statistics year.""" + download_entsoe_power_statistics_year( + year=int(snakemake.params.year), + output_path=Path(snakemake.output.annual_file), + ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + main(snakemake) diff --git a/workflow/scripts/prepare_load_entsoe_power_statistics.py b/workflow/scripts/prepare_load_entsoe_power_statistics.py new file mode 100644 index 0000000..e2b8da2 --- /dev/null +++ b/workflow/scripts/prepare_load_entsoe_power_statistics.py @@ -0,0 +1,48 @@ +"""Snakemake entry point for preparing ENTSO-E Power Statistics load.""" + +import logging +import sys +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from sources.entsoe_power_statistics.prepare import prepare_entsoe_power_statistics +from tclean import TimeGrid + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Prepare ENTSO-E Power Statistics for the configured scope.""" + grid = TimeGrid( + start=snakemake.params.temporal_start, + end=snakemake.params.temporal_end, + frequency=snakemake.params.frequency, + ) + + prepare_entsoe_power_statistics( + input_paths=[ + Path(path) + for path in snakemake.input.annual_files + ], + output_path=snakemake.output.load, + grid=grid, + country_codes=list( + snakemake.params.country_codes + ), + ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + main(snakemake) diff --git a/workflow/scripts/sources/entsoe_power_statistics/download.py b/workflow/scripts/sources/entsoe_power_statistics/download.py new file mode 100644 index 0000000..89ed9a7 --- /dev/null +++ b/workflow/scripts/sources/entsoe_power_statistics/download.py @@ -0,0 +1,355 @@ +"""Download and harmonise annual ENTSO-E Power Statistics load data.""" + +import logging +import shutil +from pathlib import Path +from tempfile import TemporaryDirectory +from urllib.request import Request, urlopen +from warnings import warn + +import pandas as pd +import pycountry +from tclean import TimeGrid + +logger = logging.getLogger(__name__) + +FIRST_SUPPORTED_YEAR = 2019 +LAST_SUPPORTED_YEAR = 2025 + +URL_TEMPLATE = ( + "https://www.entsoe.eu/publications/data/power-stats/" + "{year}/monthly_hourly_load_values_{year}.csv" +) + +USER_AGENT = ( + "modelblocks-module-demand-electricity/" + "ENTSO-E-Power-Statistics" +) + + +def _validate_year(year: int) -> None: + """Validate that a Power Statistics annual file is supported.""" + if not FIRST_SUPPORTED_YEAR <= year <= LAST_SUPPORTED_YEAR: + raise ValueError( + "ENTSO-E Power Statistics annual downloads are supported " + f"for {FIRST_SUPPORTED_YEAR}-{LAST_SUPPORTED_YEAR}, " + f"received {year}." + ) + + +def _detect_delimiter(path: Path) -> str: + """Detect the delimiter used by an ENTSO-E Power Statistics CSV.""" + with path.open( + "r", + encoding="utf-8-sig", + errors="replace", + ) as file: + header = file.readline() + + if "\t" in header: + return "\t" + + if ";" in header: + return ";" + + raise ValueError( + "Could not determine the delimiter used by " + f"ENTSO-E Power Statistics file {path}." + ) + + +def _build_interval_start( + date_short: pd.Series, + time_from: pd.Series, +) -> pd.Series: + """Build the hourly interval-start timestamps from source labels.""" + available = date_short.dropna() + + if available.empty: + raise ValueError( + "ENTSO-E Power Statistics file contains no dates." + ) + + sample = str(available.iloc[0]).strip() + + if "/" in sample: + date_format = "%d/%m/%Y" + elif "-" in sample: + date_format = "%d-%m-%Y" + else: + raise ValueError( + "Unsupported ENTSO-E Power Statistics date " + f"format: {sample!r}." + ) + + dates = pd.to_datetime( + date_short, + format=date_format, + utc=True, + errors="raise", + ) + + time_strings = ( + time_from + .astype("string") + .str.strip() + ) + + missing_times = time_strings.isna() + + if missing_times.any(): + raise ValueError( + "ENTSO-E Power Statistics contains missing " + "TimeFrom values." + ) + + needs_seconds = ( + time_strings.str.count(":") == 1 + ) + + time_strings = time_strings.where( + ~needs_seconds, + time_strings + ":00", + ) + + times = pd.to_timedelta( + time_strings, + errors="raise", + ) + + return dates + times + + +def _get_map_alpha2_to_alpha3( + countries_alpha_2, +) -> dict[str, str]: + """Map ISO alpha-2 country codes to ISO alpha-3 codes.""" + mapping = {} + + for alpha2 in countries_alpha_2: + country = pycountry.countries.get( + alpha_2=alpha2 + ) + + if country is not None: + mapping[alpha2] = country.alpha_3 + else: + warn( + f"Country with alpha-2 code {alpha2!r} " + "not found in pycountry." + ) + + return mapping + + +def harmonise_entsoe_power_statistics_csv( + *, + input_path: str | Path, + output_path: str | Path, + year: int, +) -> None: + """Convert one annual Power Statistics CSV to canonical Parquet.""" + _validate_year(year) + + input_path = Path(input_path) + output_path = Path(output_path) + + delimiter = _detect_delimiter(input_path) + + data = pd.read_csv( + input_path, + sep=delimiter, + usecols=[ + "DateShort", + "TimeFrom", + "CountryCode", + "Value_ScaleTo100", + ], + ) + + data["CountryCode"] = ( + data["CountryCode"] + .astype("string") + .str.strip() + ) + + data["timestamp"] = _build_interval_start( + data["DateShort"], + data["TimeFrom"], + ) + + data["Value_ScaleTo100"] = pd.to_numeric( + data["Value_ScaleTo100"], + errors="raise", + ) + + country_mapping = _get_map_alpha2_to_alpha3( + data["CountryCode"] + .dropna() + .unique() + ) + + data = data.loc[ + data["CountryCode"].isin(country_mapping) + ].copy() + + data["country"] = data["CountryCode"].map( + country_mapping + ) + + duplicate_mask = data.duplicated( + subset=["country", "timestamp"], + keep=False, + ) + + if duplicate_mask.any(): + examples = ( + data.loc[ + duplicate_mask, + ["country", "DateUTC"], + ] + .drop_duplicates() + .head(10) + .to_dict("records") + ) + + raise ValueError( + "ENTSO-E Power Statistics contains duplicate " + "country/timestamp observations. " + f"Examples: {examples}." + ) + + grid = TimeGrid( + start=f"{year}-01-01", + end=f"{year + 1}-01-01", + frequency="1h", + ) + + outside_year = ( + (data["timestamp"] < grid.start) + | (data["timestamp"] >= grid.end) + ) + + if outside_year.any(): + examples = ( + data.loc[ + outside_year, + "DateUTC", + ] + .drop_duplicates() + .sort_values() + .head(10) + .astype(str) + .tolist() + ) + + raise ValueError( + f"ENTSO-E Power Statistics file for {year} " + "contains timestamps outside its UTC calendar " + f"year. Examples: {examples}." + ) + + wide = data.pivot( + index="timestamp", + columns="country", + values="Value_ScaleTo100", + ) + + wide = wide.sort_index(axis=1) + + wide = wide.reindex( + index=grid.target_index, + ) + + wide = wide.astype(float) + + wide.index.name = "timestamp" + wide.columns.name = None + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + temporary_output = output_path.with_suffix( + ".tmp.parquet" + ) + + try: + wide.to_parquet( + temporary_output, + ) + + temporary_output.replace( + output_path, + ) + + finally: + temporary_output.unlink( + missing_ok=True, + ) + + logger.info( + "Saved ENTSO-E Power Statistics %s to %s " + "with shape %s.", + year, + output_path, + wide.shape, + ) + + +def download_entsoe_power_statistics_year( + *, + year: int, + output_path: str | Path, +) -> None: + """Download and harmonise one annual Power Statistics file.""" + _validate_year(year) + + output_path = Path(output_path) + + url = URL_TEMPLATE.format( + year=year, + ) + + logger.info( + "Downloading ENTSO-E Power Statistics for %s.", + year, + ) + + request = Request( + url, + headers={ + "User-Agent": USER_AGENT, + }, + ) + + with TemporaryDirectory() as temporary_directory: + csv_path = ( + Path(temporary_directory) + / f"monthly_hourly_load_values_{year}.csv" + ) + + with ( + urlopen( + request, + timeout=300, + ) as response, + csv_path.open("wb") as output_file, + ): + shutil.copyfileobj( + response, + output_file, + ) + + if csv_path.stat().st_size == 0: + raise RuntimeError( + "Downloaded ENTSO-E Power Statistics " + f"file for {year} is empty." + ) + + harmonise_entsoe_power_statistics_csv( + input_path=csv_path, + output_path=output_path, + year=year, + ) diff --git a/workflow/scripts/sources/entsoe_power_statistics/prepare.py b/workflow/scripts/sources/entsoe_power_statistics/prepare.py new file mode 100644 index 0000000..6d4cc36 --- /dev/null +++ b/workflow/scripts/sources/entsoe_power_statistics/prepare.py @@ -0,0 +1,81 @@ +"""Prepare cached ENTSO-E Power Statistics electricity-demand data.""" + +from collections.abc import Iterable +from pathlib import Path + +import pandas as pd +from tclean import TimeGrid + + +def prepare_entsoe_power_statistics( + *, + input_paths: Iterable[str | Path], + output_path: str | Path, + grid: TimeGrid, + country_codes: list[str], +) -> None: + """Prepare ENTSO-E Power Statistics demand on the target grid.""" + input_paths = [ + Path(path) + for path in input_paths + ] + + frames = [] + + for input_path in input_paths: + frame = pd.read_parquet(input_path) + + frame.index = pd.to_datetime( + frame.index, + utc=True, + ) + + frames.append(frame) + + if frames: + data = pd.concat( + frames, + axis=0, + sort=False, + ).sort_index() + + duplicate_mask = data.index.duplicated( + keep=False, + ) + + if duplicate_mask.any(): + duplicate_timestamps = ( + data.index[duplicate_mask] + .unique() + .astype(str) + .tolist() + ) + + raise ValueError( + "ENTSO-E Power Statistics annual files " + "contain overlapping UTC timestamps: " + f"{duplicate_timestamps[:10]}." + ) + + else: + data = pd.DataFrame( + dtype=float, + ) + + data = data.reindex( + index=grid.target_index, + columns=country_codes, + ) + + data = data.astype(float) + + output_path = Path(output_path) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + data.to_parquet( + output_path, + ) From ff7e3fca536a51844bceaf7085d472ac48df1f84 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 14:36:24 +0200 Subject: [PATCH 161/241] Fixing typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f96d0f1..b045043 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ snakemake # run the workflow! The integration workflow's default Snakemake profile enables Conda, uses 2 cores, and limits concurrent ENTSO-E and NESO downloads to 2 each. These execution settings can be overridden with the corresponding Snakemake command-line options: - **Cores**: Defaults can be overridden using the `--cores` flag. -- **ENTSOE Downloads**: Defaults can be overridden using the `--enstoe_download` flag. +- **ENTSOE Downloads**: Defaults can be overridden using the `--entsoe_download` flag. - **NESO Downloads**: Defaults can be overridden using the `--neso_download` flag. A complete example: From 68d6a4caaa40b42241427a4248c494d8d4111eab Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 15:55:32 +0200 Subject: [PATCH 162/241] Advanced pipeline for entsoe Power Statistics source --- config/config.yaml | 4 +- tests/integration/test_config.yaml | 5 +- workflow/internal/config.schema.yaml | 3 +- workflow/rules/advanced.smk | 50 +++++++++++++++++++ workflow/scripts/_plot_timeline.py | 2 +- workflow/scripts/_tclean_config.py | 2 +- .../prepare_load_entsoe_power_statistics.py | 41 ++++++++++++--- workflow/scripts/source_declaration.py | 7 +++ 8 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 workflow/scripts/source_declaration.py diff --git a/config/config.yaml b/config/config.yaml index 75f46cc..6bc8096 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,9 +5,11 @@ temporal_scope: # Load sources are listed in priority order. Where multiple sources provide # data for the same country and timestamp, the earlier source takes priority. -# Supported sources include: entsoe, neso, opsd. +# Supported sources include: entsoe, entsoe_power_statistics, neso, opsd. +# NOTE: Power Statistics is a backup for ENTSOE and both are not necessary together. load_sources: - entsoe + # - entsoe_power_statistics - neso - opsd diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index d005eab..2c0e463 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -10,14 +10,15 @@ integration_test: # data for the same country and timestamp, the earlier source takes priority. # Supported sources include: entsoe, neso, opsd. load_sources: - - entsoe + # - entsoe + - entsoe_power_statistics - neso - opsd # Exercise the full advanced gap-filling workflow. See config/README.md for # the complete configuration reference and method descriptions. gap_filling: - mode: advanced + mode: "advanced" basic: rules: diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index e4bdb6f..f0c79a7 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -428,6 +428,7 @@ properties: type: string enum: - entsoe + - entsoe_power_statistics - neso - opsd @@ -444,7 +445,7 @@ properties: mode: type: string enum: - - off + - "off" - basic - advanced description: > diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index ba1df57..7a43fba 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -40,6 +40,26 @@ def auxiliary_entsoe_raw_files(wildcards): return entsoe_annual_files(batch["countries"], batch["years"]) +def auxiliary_entsoe_power_statistics_raw_files( + wildcards, +): + """Return Power Statistics annual files required by one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == wildcards.batch_id + and batch["source"] == "entsoe_power_statistics" + ) + ) + + return entsoe_power_statistics_annual_files( + batch["years"] + ) + + def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" plan = _read_auxiliary_plan() @@ -105,6 +125,7 @@ def advanced_external_profile_files(_wildcards): ] + checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, @@ -197,6 +218,35 @@ rule prepare_auxiliary_load_neso: "../scripts/prepare_load_neso.py" +rule prepare_auxiliary_load_entsoe_power_statistics: + input: + plan=auxiliary_acquisition_plan, + annual_files=auxiliary_entsoe_power_statistics_raw_files, + output: + load=( + "/automatic/" + "auxiliary/entsoe_power_statistics/" + "{batch_id}.parquet" + ), + log: + ( + "/auxiliary/" + "entsoe_power_statistics/" + "prepare_{batch_id}.log" + ), + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + ( + "Prepare auxiliary electricity-demand data " + "from ENTSO-E Power Statistics." + ) + script: + "../scripts/prepare_load_entsoe_power_statistics.py" + + rule clean_auxiliary_group: input: plan=auxiliary_acquisition_plan, diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 07f8b2d..cc1941f 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -382,7 +382,7 @@ def _build_cleaning_method_metadata( def _format_source_name(source_name: str) -> str: - mapping = {"entsoe": "ENTSO-E", "neso": "NESO", "opsd": "OPSD"} + mapping = {"entsoe": "ENTSO-E", "entsoe_power_statistics": "ENTSO-E Power Stats.", "neso": "NESO", "opsd": "OPSD"} return mapping.get(source_name, source_name) diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 2ae7868..ce18bb0 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -146,7 +146,7 @@ def build_source_capabilities(source_names: Sequence[str]) -> pd.DataFrame: if source_name == "neso": capabilities.append({"source": "neso", "context": "GBR"}) - elif source_name in {"entsoe", "opsd"}: + elif source_name in {"entsoe", "entsoe_power_statistics","opsd"}: capabilities.append({"source": source_name, "context": None}) else: diff --git a/workflow/scripts/prepare_load_entsoe_power_statistics.py b/workflow/scripts/prepare_load_entsoe_power_statistics.py index e2b8da2..0cb8f1e 100644 --- a/workflow/scripts/prepare_load_entsoe_power_statistics.py +++ b/workflow/scripts/prepare_load_entsoe_power_statistics.py @@ -5,7 +5,10 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from sources.entsoe_power_statistics.prepare import prepare_entsoe_power_statistics +from _advanced_execution import get_batch, load_execution_plan +from sources.entsoe_power_statistics.prepare import ( + prepare_entsoe_power_statistics, +) from tclean import TimeGrid if TYPE_CHECKING: @@ -13,10 +16,36 @@ def main(snakemake: Any) -> None: - """Prepare ENTSO-E Power Statistics for the configured scope.""" + """Prepare ENTSO-E Power Statistics for the requested workflow period.""" + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="entsoe_power_statistics", + ) + + start = batch["start"] + end = batch["end"] + country_codes = list(batch["countries"]) + + else: + start = snakemake.params.temporal_start + end = snakemake.params.temporal_end + country_codes = list( + snakemake.params.country_codes + ) + grid = TimeGrid( - start=snakemake.params.temporal_start, - end=snakemake.params.temporal_end, + start=start, + end=end, frequency=snakemake.params.frequency, ) @@ -27,9 +56,7 @@ def main(snakemake: Any) -> None: ], output_path=snakemake.output.load, grid=grid, - country_codes=list( - snakemake.params.country_codes - ), + country_codes=country_codes, ) diff --git a/workflow/scripts/source_declaration.py b/workflow/scripts/source_declaration.py new file mode 100644 index 0000000..e3297e4 --- /dev/null +++ b/workflow/scripts/source_declaration.py @@ -0,0 +1,7 @@ +SOURCES = { + "entsoe": "ENTSO-E", + "entsoe_power_statistics": "ENTSO-E Power Stats.", + "opsd": "OPSD", + "neso:": "NESO", +} + From 97ffec3f6913969cf3319cd02d1773523826ef66 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 18:59:34 +0200 Subject: [PATCH 163/241] Source registry implemented --- README.md | 2 +- workflow/internal/config.schema.yaml | 6 +- workflow/internal/source_registry.yaml | 31 +++++++ workflow/rules/_utils.smk | 93 ++++++++++++++++++- workflow/rules/advanced.smk | 1 + workflow/rules/automatic.smk | 4 +- workflow/rules/clean.smk | 17 ++-- workflow/scripts/_plot_timeline.py | 28 ++++-- workflow/scripts/_tclean_config.py | 39 ++++++-- workflow/scripts/plan_auxiliary_data.py | 13 ++- workflow/scripts/plot_cleaning_timeline.py | 3 +- workflow/scripts/source_declaration.py | 7 -- .../entsoe_power_statistics/download.py | 25 ++--- 13 files changed, 205 insertions(+), 64 deletions(-) create mode 100644 workflow/internal/source_registry.yaml delete mode 100644 workflow/scripts/source_declaration.py diff --git a/README.md b/README.md index b045043..36c7ab0 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Available provider identifiers are: Three modes are available: -- `off`: do not fill gaps; +- `"off"`: do not fill gaps. Please note that quotation marks are necessary here (i.e. "not") otherwise python reads this as a falsey boolean value; - `basic`: apply configured deterministic rules; - `advanced`: run basic cleaning first, then execute active advanced rules. diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index f0c79a7..0fa8d81 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -415,6 +415,7 @@ properties: additionalProperties: false + # Load Sources are validated against ./source_registry.yaml load_sources: type: array description: > @@ -426,11 +427,6 @@ properties: items: type: string - enum: - - entsoe - - entsoe_power_statistics - - neso - - opsd gap_filling: diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml new file mode 100644 index 0000000..96b5fe5 --- /dev/null +++ b/workflow/internal/source_registry.yaml @@ -0,0 +1,31 @@ +# This registry defines the data sources currently integrated into +# Module Demand Electricity. +# +# Sources may optionally declare: +# - display_name: human-readable name used for display and plotting. +# - temporal_scope: valid source interval [start, end). +# Either bound may be omitted to indicate no declared bound. +# - contexts: eligible contexts for this source. +# If omitted or empty, no context restriction is declared. + +entsoe: + display_name: ENTSO-E + temporal_scope: + start: "2005-01-01" + +entsoe_power_statistics: + display_name: ENTSO-E Power Statistics + temporal_scope: + start: "2019-01-01" + end: "2026-01-01" + +neso: + display_name: NESO + contexts: + - GBR + +opsd: + display_name: OPSD + temporal_scope: + start: "2005-01-01" + end: "2019-03-01" \ No newline at end of file diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 150a1b6..1efbc7b 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -1,12 +1,25 @@ """Collection of auxiliary functions for this module.""" from datetime import datetime, timedelta, timezone +import yaml -ENTSOE_POWER_STATISTICS_YEARS = set( - range(2019, 2026) +SOURCE_REGISTRY_PATH = workflow.source_path( + "../internal/source_registry.yaml" ) +with open( + SOURCE_REGISTRY_PATH, + encoding="utf-8", +) as file: + SOURCE_REGISTRY = yaml.safe_load(file) or {} + +if not isinstance(SOURCE_REGISTRY, dict): + raise ValueError( + "Source registry must contain a mapping " + "of source identifiers to metadata." + ) + def _as_utc(value): """Interpret naive timestamps as UTC and convert aware timestamps to UTC. Mirrors tclean logic.""" @@ -18,6 +31,36 @@ def _as_utc(value): return parsed.astimezone(timezone.utc) +def source_overlaps_period( + source_name, + start, + end, +): + """Return whether a source can supply any part of a period.""" + metadata = SOURCE_REGISTRY[source_name] + temporal_scope = metadata.get("temporal_scope") or {} + + source_start = temporal_scope.get("start") + source_end = temporal_scope.get("end") + + start = _as_utc(start) + end = _as_utc(end) + + if source_start is not None: + source_start = _as_utc(source_start) + + if end <= source_start: + return False + + if source_end is not None: + source_end = _as_utc(source_end) + + if start >= source_end: + return False + + return True + + def years_for_period(start, end): """Return UTC calendar years intersected by the half-open period [start, end). Mirrors tclean logic.""" start = _as_utc(start) @@ -31,6 +74,25 @@ def years_for_period(start, end): return list(range(start.year, final_included_time.year + 1)) +def source_year_pattern(source_name): + """Return a year wildcard pattern for a bounded source.""" + temporal_scope = ( + SOURCE_REGISTRY[source_name].get("temporal_scope") + or {} + ) + + start = temporal_scope.get("start") + end = temporal_scope.get("end") + + if start is None or end is None: + return "[0-9]{4}" + + return "|".join( + str(year) + for year in years_for_period(start, end) + ) + + def neso_annual_files(years): """Return reusable annual NESO raw-file paths for the requested years.""" return [ @@ -57,9 +119,14 @@ def entsoe_power_statistics_annual_files(years): f"{int(year)}.parquet" ) for year in years - if int(year) in ENTSOE_POWER_STATISTICS_YEARS + if source_overlaps_period( + "entsoe_power_statistics", + f"{int(year)}-01-01", + f"{int(year) + 1}-01-01", + ) ] + def additional_config_validation(): """Validate configuration relationships that require no module dependencies.""" gap_filling = config["gap_filling"] @@ -100,3 +167,23 @@ def additional_config_validation(): f"Advanced rule {rule['name']!r} references unknown " f"advanced source {source_name!r}." ) + + +def validate_source_names(source_names): + """Require configured demand sources to exist in the source registry.""" + unknown_sources = [ + source_name + for source_name in source_names + if source_name not in SOURCE_REGISTRY + ] + + if unknown_sources: + raise ValueError( + "Unsupported electricity-demand source(s): " + f"{unknown_sources}. " + "Available sources are: " + f"{sorted(SOURCE_REGISTRY)}." + ) + + +validate_source_names(config["load_sources"]) \ No newline at end of file diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 7a43fba..3250fef 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -137,6 +137,7 @@ checkpoint plan_auxiliary_data: temporal_scope=config["temporal_scope"], gap_filling=config["gap_filling"], source_names=config["load_sources"], + source_registry=SOURCE_REGISTRY, message: "Plan auxiliary electricity-demand acquisition." script: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index d042291..020434b 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -72,7 +72,9 @@ rule download_load_entsoe_power_statistics_year: "/download_load_entsoe_power_statistics_{year}.log" ), wildcard_constraints: - year="2019|2020|2021|2022|2023|2024|2025", + year=source_year_pattern( + "entsoe_power_statistics" + ), localrule: True conda: "../envs/module.yaml" diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 1b7f4f2..b70d59c 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,10 +1,3 @@ -LOAD_SOURCE_PATHS = { - "entsoe": ("/automatic/" "load_entsoe.parquet"), - "neso": ("/automatic/" "load_neso.parquet"), - "opsd": ("/automatic/" "load_opsd.parquet"), - "entsoe_power_statistics": ("/automatic/" "load_entsoe_power_statistics.parquet") -} - def entsoe_raw_files(_wildcards): """Return ENTSO-E country-year files required by the configured period.""" years = years_for_period( @@ -129,7 +122,14 @@ rule prepare_load_entsoe_power_statistics: def configured_load_inputs(_wildcards): - return [LOAD_SOURCE_PATHS[source_name] for source_name in config["load_sources"]] + """Return prepared demand files for configured sources.""" + return [ + ( + "/automatic/" + f"load_{source_name}.parquet" + ) + for source_name in config["load_sources"] + ] rule clean_demand: @@ -172,6 +172,7 @@ rule plot_cleaning_timeline: params: source_names=config["load_sources"], gap_filling=config["gap_filling"], + source_registry=SOURCE_REGISTRY, message: "Plot electricity-demand cleaning provenance." script: diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index cc1941f..1d36aba 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -1,6 +1,7 @@ """Plot electricity demand and cleaning-method provenance through time.""" import logging +from collections.abc import Mapping from pathlib import Path from typing import Any @@ -24,6 +25,7 @@ def main( output_path: str | Path, source_names: list[str], gap_filling_config: dict[str, Any], + source_registry: Mapping[str, Mapping[str, Any]], ) -> None: """Create the electricity-demand cleaning diagnostic.""" demand = pd.read_parquet(demand_path) @@ -37,7 +39,9 @@ def main( ) metadata = _build_cleaning_method_metadata( - source_names=source_names, gap_filling_config=gap_filling_config + source_names=source_names, + source_registry=source_registry, + gap_filling_config=gap_filling_config, ) rank_colours = _build_rank_colours(metadata) @@ -331,7 +335,7 @@ def _add_mean_load_labels( def _build_cleaning_method_metadata( - *, source_names: list[str], gap_filling_config: dict[str, Any] + *, source_names: list[str], gap_filling_config: dict[str, Any],source_registry: Mapping[str, Mapping[str, Any]], ) -> pd.DataFrame: """Build complete method metadata in configured rank order.""" rows: list[dict[str, Any]] = [] @@ -343,7 +347,9 @@ def _build_cleaning_method_metadata( "cleaning_method": (f"observed_{source_name}"), "cleaning_method_rank": rank, "label": ( - f"Rank {rank}: Observed ({_format_source_name(source_name)})" + "Rank " + f"{rank}: Observed " + f"({_format_source_name(source_name, source_registry)})" ), "category": "observed", } @@ -381,9 +387,19 @@ def _build_cleaning_method_metadata( return pd.DataFrame(rows) -def _format_source_name(source_name: str) -> str: - mapping = {"entsoe": "ENTSO-E", "entsoe_power_statistics": "ENTSO-E Power Stats.", "neso": "NESO", "opsd": "OPSD"} - return mapping.get(source_name, source_name) +def _format_source_name( + source_name: str, + source_registry: Mapping[str, Mapping[str, Any]], +) -> str: + """Return the configured human-readable source name.""" + metadata = source_registry.get(source_name, {}) + + return str( + metadata.get( + "display_name", + source_name, + ) + ) def _format_rule_name(name: str) -> str: diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index ce18bb0..bdfd6e2 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -135,24 +135,43 @@ def build_all_constructed_source_periods( return result -def build_source_capabilities(source_names: Sequence[str]) -> pd.DataFrame: +def build_source_capabilities( + source_names: Sequence[str], + *, + source_registry: Mapping[str, Mapping[str, Any]], +) -> pd.DataFrame: """Describe which contexts configured providers can supply.""" if len(source_names) != len(set(source_names)): - raise ValueError("Configured load source names must be unique.") + raise ValueError( + "Configured load source names must be unique." + ) capabilities: list[dict[str, object]] = [] for source_name in source_names: - if source_name == "neso": - capabilities.append({"source": "neso", "context": "GBR"}) - - elif source_name in {"entsoe", "entsoe_power_statistics","opsd"}: - capabilities.append({"source": source_name, "context": None}) + if source_name not in source_registry: + raise ValueError( + "Unsupported electricity-demand source: " + f"{source_name!r}." + ) + + contexts = ( + source_registry[source_name].get("contexts") + or [None] + ) - else: - raise ValueError(f"Unsupported electricity-demand source: {source_name!r}.") + capabilities.extend( + { + "source": source_name, + "context": context, + } + for context in contexts + ) - return pd.DataFrame(capabilities, columns=["source", "context"]) + return pd.DataFrame( + capabilities, + columns=["source", "context"], + ) def get_advanced_source_definitions( diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 999661c..1dbd9c1 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -39,6 +39,7 @@ def build_advanced_execution_plan( temporal_scope: Mapping[str, Any], gap_filling_config: Mapping[str, Any], source_names: Sequence[str], + source_registry: Mapping[str, Mapping[str, Any]], ) -> dict[str, object]: """Build the advanced Snakemake execution manifest.""" if gap_filling_config["mode"] != "advanced": @@ -74,7 +75,10 @@ def build_advanced_execution_plan( ), ) - source_capabilities = build_source_capabilities(source_names) + source_capabilities = build_source_capabilities( + source_names, + source_registry=source_registry, + ) requests = build_auxiliary_source_requests( requirements, source_capabilities=source_capabilities, grid=grid @@ -219,9 +223,10 @@ def main(snakemake: Any) -> None: plan = build_advanced_execution_plan( target_contexts=target_contexts, - temporal_scope=(snakemake.params.temporal_scope), - gap_filling_config=(snakemake.params.gap_filling), - source_names=(snakemake.params.source_names), + temporal_scope=snakemake.params.temporal_scope, + gap_filling_config=snakemake.params.gap_filling, + source_names=snakemake.params.source_names, + source_registry=snakemake.params.source_registry, ) write_execution_plan(plan=plan, output_path=snakemake.output.plan) diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index bad95a5..e259b03 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -12,8 +12,9 @@ main( demand_path=snakemake.input.demand, cleaning_method_path=snakemake.input.cleaning_method, - cleaning_method_rank_path=(snakemake.input.cleaning_method_rank), + cleaning_method_rank_path=(nakemake.input.cleaning_method_rank), output_path=snakemake.output.plot, source_names=snakemake.params.source_names, + source_registry=snakemake.params.source_registry, gap_filling_config=snakemake.params.gap_filling, ) diff --git a/workflow/scripts/source_declaration.py b/workflow/scripts/source_declaration.py deleted file mode 100644 index e3297e4..0000000 --- a/workflow/scripts/source_declaration.py +++ /dev/null @@ -1,7 +0,0 @@ -SOURCES = { - "entsoe": "ENTSO-E", - "entsoe_power_statistics": "ENTSO-E Power Stats.", - "opsd": "OPSD", - "neso:": "NESO", -} - diff --git a/workflow/scripts/sources/entsoe_power_statistics/download.py b/workflow/scripts/sources/entsoe_power_statistics/download.py index 89ed9a7..780ca04 100644 --- a/workflow/scripts/sources/entsoe_power_statistics/download.py +++ b/workflow/scripts/sources/entsoe_power_statistics/download.py @@ -13,8 +13,6 @@ logger = logging.getLogger(__name__) -FIRST_SUPPORTED_YEAR = 2019 -LAST_SUPPORTED_YEAR = 2025 URL_TEMPLATE = ( "https://www.entsoe.eu/publications/data/power-stats/" @@ -27,16 +25,6 @@ ) -def _validate_year(year: int) -> None: - """Validate that a Power Statistics annual file is supported.""" - if not FIRST_SUPPORTED_YEAR <= year <= LAST_SUPPORTED_YEAR: - raise ValueError( - "ENTSO-E Power Statistics annual downloads are supported " - f"for {FIRST_SUPPORTED_YEAR}-{LAST_SUPPORTED_YEAR}, " - f"received {year}." - ) - - def _detect_delimiter(path: Path) -> str: """Detect the delimiter used by an ENTSO-E Power Statistics CSV.""" with path.open( @@ -149,8 +137,6 @@ def harmonise_entsoe_power_statistics_csv( year: int, ) -> None: """Convert one annual Power Statistics CSV to canonical Parquet.""" - _validate_year(year) - input_path = Path(input_path) output_path = Path(output_path) @@ -206,7 +192,12 @@ def harmonise_entsoe_power_statistics_csv( examples = ( data.loc[ duplicate_mask, - ["country", "DateUTC"], + [ + "country", + "timestamp", + "DateShort", + "TimeFrom", + ], ] .drop_duplicates() .head(10) @@ -234,7 +225,7 @@ def harmonise_entsoe_power_statistics_csv( examples = ( data.loc[ outside_year, - "DateUTC", + "timestamp", ] .drop_duplicates() .sort_values() @@ -304,8 +295,6 @@ def download_entsoe_power_statistics_year( output_path: str | Path, ) -> None: """Download and harmonise one annual Power Statistics file.""" - _validate_year(year) - output_path = Path(output_path) url = URL_TEMPLATE.format( From a9236aba9776085502116b102afd9145ebdc94b3 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 19:27:07 +0200 Subject: [PATCH 164/241] Reorganised to separate out source-specific rules. --- workflow/Snakefile | 15 +- workflow/rules/_utils.smk | 36 +---- workflow/rules/advanced.smk | 130 ------------------ workflow/rules/automatic.smk | 94 +------------ workflow/rules/clean.smk | 123 ----------------- workflow/rules/source_entsoe.smk | 101 ++++++++++++++ .../rules/source_entsoe_power_statistics.smk | 126 +++++++++++++++++ workflow/rules/source_neso.smk | 90 ++++++++++++ workflow/rules/source_opsd.smk | 56 ++++++++ workflow/scripts/plot_cleaning_timeline.py | 2 +- 10 files changed, 387 insertions(+), 386 deletions(-) create mode 100644 workflow/rules/source_entsoe.smk create mode 100644 workflow/rules/source_entsoe_power_statistics.smk create mode 100644 workflow/rules/source_neso.smk create mode 100644 workflow/rules/source_opsd.smk diff --git a/workflow/Snakefile b/workflow/Snakefile index 747ac98..0f2e318 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -37,18 +37,25 @@ workflow.source_path("scripts/_schemas.py") # Add all your includes here. - +# IMPORTANT: General .smk files must be included after _utils.smk include: "rules/_utils.smk" - additional_config_validation() - +# Generic demand workflow rules. include: "rules/automatic.smk" include: "rules/clean.smk" -include: "rules/prepare_demand.smk" include: "rules/advanced.smk" +# Source-specific acquisition and preparation rules. +include: "rules/source_entsoe.smk" +include: "rules/source_entsoe_power_statistics.smk" +include: "rules/source_neso.smk" +include: "rules/source_opsd.smk" + +# Spatial demand preparation. +include: "rules/prepare_demand.smk" + rule all: default_target: True diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 1efbc7b..f440910 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -91,40 +91,6 @@ def source_year_pattern(source_name): str(year) for year in years_for_period(start, end) ) - - -def neso_annual_files(years): - """Return reusable annual NESO raw-file paths for the requested years.""" - return [ - "/automatic/neso/" f"historic_demand_{int(year)}.csv" - for year in years - ] - - -def entsoe_annual_files(countries, years): - """Return reusable ENTSO-E country-year raw-file paths.""" - return [ - "/automatic/entsoe/raw/" f"{country}/{int(year)}.parquet" - for country in countries - for year in years - ] - - -def entsoe_power_statistics_annual_files(years): - """Return supported annual ENTSO-E Power Statistics files.""" - return [ - ( - "/automatic/" - "entsoe_power_statistics/raw/" - f"{int(year)}.parquet" - ) - for year in years - if source_overlaps_period( - "entsoe_power_statistics", - f"{int(year)}-01-01", - f"{int(year) + 1}-01-01", - ) - ] def additional_config_validation(): @@ -186,4 +152,4 @@ def validate_source_names(source_names): ) -validate_source_names(config["load_sources"]) \ No newline at end of file +validate_source_names(config["load_sources"]) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 3250fef..0294b51 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -14,52 +14,6 @@ def auxiliary_acquisition_plan(_wildcards): return checkpoints.plan_auxiliary_data.get().output.plan -def auxiliary_neso_raw_files(wildcards): - """Return annual NESO files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() - - batch = next( - batch - for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") - ) - - return neso_annual_files(batch["years"]) - - -def auxiliary_entsoe_raw_files(wildcards): - """Return ENTSO-E country-year files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() - - batch = next( - batch - for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") - ) - - return entsoe_annual_files(batch["countries"], batch["years"]) - - -def auxiliary_entsoe_power_statistics_raw_files( - wildcards, -): - """Return Power Statistics annual files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() - - batch = next( - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == wildcards.batch_id - and batch["source"] == "entsoe_power_statistics" - ) - ) - - return entsoe_power_statistics_annual_files( - batch["years"] - ) - - def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" plan = _read_auxiliary_plan() @@ -125,7 +79,6 @@ def advanced_external_profile_files(_wildcards): ] - checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, @@ -165,89 +118,6 @@ rule finalise_clean_demand: "../scripts/finalise_clean_demand.py" -rule prepare_auxiliary_load_entsoe: - input: - plan=auxiliary_acquisition_plan, - annual_files=auxiliary_entsoe_raw_files, - output: - load=("/automatic/" "auxiliary/entsoe/" "{batch_id}.parquet"), - log: - ("/auxiliary/" "entsoe/prepare_{batch_id}.log"), - conda: - "../envs/module.yaml" - params: - frequency=config["temporal_scope"]["frequency"], - message: - "Prepare auxiliary electricity-demand data from ENTSO-E." - script: - "../scripts/prepare_load_entsoe.py" - - -rule prepare_auxiliary_load_opsd: - input: - load=rules.download_load_opsd.output.load, - plan=auxiliary_acquisition_plan, - output: - load=("/automatic/" "auxiliary/opsd/" "{batch_id}.parquet"), - log: - ("/auxiliary/" "opsd/{batch_id}.log"), - conda: - "../envs/module.yaml" - params: - frequency=config["temporal_scope"]["frequency"], - message: - "Prepare auxiliary electricity-demand data from OPSD." - script: - "../scripts/prepare_load_opsd.py" - - -rule prepare_auxiliary_load_neso: - input: - plan=auxiliary_acquisition_plan, - annual_files=auxiliary_neso_raw_files, - output: - load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), - log: - "/auxiliary/neso/{batch_id}.log", - conda: - "../envs/module.yaml" - params: - frequency=config["temporal_scope"]["frequency"], - message: - "Prepare auxiliary electricity-demand data from NESO." - script: - "../scripts/prepare_load_neso.py" - - -rule prepare_auxiliary_load_entsoe_power_statistics: - input: - plan=auxiliary_acquisition_plan, - annual_files=auxiliary_entsoe_power_statistics_raw_files, - output: - load=( - "/automatic/" - "auxiliary/entsoe_power_statistics/" - "{batch_id}.parquet" - ), - log: - ( - "/auxiliary/" - "entsoe_power_statistics/" - "prepare_{batch_id}.log" - ), - conda: - "../envs/module.yaml" - params: - frequency=config["temporal_scope"]["frequency"], - message: - ( - "Prepare auxiliary electricity-demand data " - "from ENTSO-E Power Statistics." - ) - script: - "../scripts/prepare_load_entsoe_power_statistics.py" - - rule clean_auxiliary_group: input: plan=auxiliary_acquisition_plan, diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 020434b..f94c824 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,4 +1,4 @@ -"""Rules used to download automatic resource files.""" +"""Rules used for generic automatic resources and validation.""" rule validate_temporal_config_semantics: @@ -34,98 +34,6 @@ rule validate_gap_filling_config_semantics: "../scripts/validate_config.py" -rule download_load_entsoe_country_year: - input: - token_entsoe="", - output: - annual_file=("/automatic/entsoe/raw/" "{country}/{year}.parquet"), - log: - "/download_load_entsoe_{country}_{year}.log", - wildcard_constraints: - country="[A-Z]{3}", - year="[0-9]{4}", - localrule: True - conda: - "../envs/module.yaml" - threads: 1 - resources: - entsoe_download=1, - params: - country_code=lambda wildcards: wildcards.country, - year=lambda wildcards: int(wildcards.year), - message: - ( - "Download ENTSO-E electricity load for " - "{wildcards.country} in {wildcards.year}." - ) - script: - "../scripts/download_load_entsoe.py" - - -rule download_load_entsoe_power_statistics_year: - output: - annual_file=( - "/automatic/entsoe_power_statistics/raw/{year}.parquet" - ), - log: - ( - "/download_load_entsoe_power_statistics_{year}.log" - ), - wildcard_constraints: - year=source_year_pattern( - "entsoe_power_statistics" - ), - localrule: True - conda: - "../envs/module.yaml" - threads: 1 - resources: - entsoe_download=1 - params: - year=lambda wildcards: int(wildcards.year), - message: - ( - "Download ENTSO-E Power Statistics " - "electricity load for {wildcards.year}." - ) - script: - "../scripts/download_load_entsoe_power_statistics.py" - -rule download_load_opsd: - output: - load=update("/automatic/opsd/raw_load.parquet"), - log: - "/download_load_opsd.log", - localrule: True - conda: - "../envs/module.yaml" - params: - url=internal["resources"]["automatic"]["load_opsd"], - message: - "Download load profiles from Open Power System Data (OPSD)." - script: - "../scripts/download_load_opsd.py" - - -rule download_load_neso_year: - output: - annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), - log: - "/download_load_neso_{year}.log", - localrule: True - conda: - "../envs/module.yaml" - threads: 1 - resources: - neso_download=1, - params: - year=lambda wildcards: int(wildcards.year), - message: - "Download NESO historic electricity demand for {wildcards.year}." - script: - "../scripts/download_load_neso.py" - - rule download_population: output: population=update("/automatic/population.zip"), diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index b70d59c..a647e32 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,126 +1,3 @@ -def entsoe_raw_files(_wildcards): - """Return ENTSO-E country-year files required by the configured period.""" - years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], - ) - - return entsoe_annual_files(internal["load_entsoe"]["countries"], years) - - -def neso_raw_files(_wildcards): - """Return annual NESO input files for the configured period.""" - years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], - ) - - return neso_annual_files(years) - - -def entsoe_power_statistics_raw_files(_wildcards): - """Return Power Statistics annual files required by the target period.""" - years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], - ) - - return entsoe_power_statistics_annual_files( - years - ) - -rule prepare_load_entsoe: - input: - validation="/automatic/temporal_config_validation.json", - annual_files=entsoe_raw_files, - output: - load="/automatic/load_entsoe.parquet", - log: - "/prepare_load_entsoe.log", - localrule: True - conda: - "../envs/module.yaml" - params: - temporal_start=config["temporal_scope"]["start"], - temporal_end=config["temporal_scope"]["end"], - frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], - message: - "Prepare electricity load from ENTSOE." - script: - "../scripts/prepare_load_entsoe.py" - - -rule prepare_load_opsd: - input: - validation="/automatic/temporal_config_validation.json", - load="/automatic/opsd/raw_load.parquet", - output: - load="/automatic/load_opsd.parquet", - log: - "/prepare_load_opsd.log", - conda: - "../envs/module.yaml" - params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], - message: - "Prepare electricity-demand data from OPSD." - script: - "../scripts/prepare_load_opsd.py" - - -rule prepare_load_neso: - input: - validation="/automatic/temporal_config_validation.json", - annual_files=neso_raw_files, - output: - load="/automatic/load_neso.parquet", - log: - "/prepare_load_neso.log", - conda: - "../envs/module.yaml" - params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe"]["countries"], - frequency=config["temporal_scope"]["frequency"], - message: - "Prepare electricity-demand data from NESO." - script: - "../scripts/prepare_load_neso.py" - - -rule prepare_load_entsoe_power_statistics: - input: - validation=( - "/automatic/" - "temporal_config_validation.json" - ), - annual_files=entsoe_power_statistics_raw_files, - output: - load=( - "/automatic/" - "load_entsoe_power_statistics.parquet" - ), - params: - temporal_start=config["temporal_scope"]["start"], - temporal_end=config["temporal_scope"]["end"], - frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], - log: - "/prepare_load_entsoe_power_statistics.log", - conda: - "../envs/module.yaml" - message: - "Prepare electricity-demand data from ENTSO-E Power Statistics." - script: - "../scripts/prepare_load_entsoe_power_statistics.py" - - - def configured_load_inputs(_wildcards): """Return prepared demand files for configured sources.""" return [ diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk new file mode 100644 index 0000000..2644662 --- /dev/null +++ b/workflow/rules/source_entsoe.smk @@ -0,0 +1,101 @@ +"""Rules for the ENTSO-E Transparency Platform demand source.""" + + +def entsoe_annual_files(countries, years): + """Return reusable ENTSO-E country-year raw-file paths.""" + return [ + "/automatic/entsoe/raw/" f"{country}/{int(year)}.parquet" + for country in countries + for year in years + ] + + +def entsoe_raw_files(_wildcards): + """Return ENTSO-E country-year files required by the configured period.""" + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], + ) + + return entsoe_annual_files(internal["load_entsoe"]["countries"], years) + + +def auxiliary_entsoe_raw_files(wildcards): + """Return ENTSO-E country-year files required by one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") + ) + + return entsoe_annual_files(batch["countries"], batch["years"]) + + +rule download_load_entsoe_country_year: + input: + token_entsoe="", + output: + annual_file=("/automatic/entsoe/raw/" "{country}/{year}.parquet"), + log: + "/download_load_entsoe_{country}_{year}.log", + wildcard_constraints: + country="[A-Z]{3}", + year="[0-9]{4}", + localrule: True + conda: + "../envs/module.yaml" + threads: 1 + resources: + entsoe_download=1, + params: + country_code=lambda wildcards: wildcards.country, + year=lambda wildcards: int(wildcards.year), + message: + ( + "Download ENTSO-E electricity load for " + "{wildcards.country} in {wildcards.year}." + ) + script: + "../scripts/download_load_entsoe.py" + + +rule prepare_load_entsoe: + input: + validation="/automatic/temporal_config_validation.json", + annual_files=entsoe_raw_files, + output: + load="/automatic/load_entsoe.parquet", + log: + "/prepare_load_entsoe.log", + localrule: True + conda: + "../envs/module.yaml" + params: + temporal_start=config["temporal_scope"]["start"], + temporal_end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], + message: + "Prepare electricity load from ENTSOE." + script: + "../scripts/prepare_load_entsoe.py" + + +rule prepare_auxiliary_load_entsoe: + input: + plan=auxiliary_acquisition_plan, + annual_files=auxiliary_entsoe_raw_files, + output: + load=("/automatic/" "auxiliary/entsoe/" "{batch_id}.parquet"), + log: + ("/auxiliary/" "entsoe/prepare_{batch_id}.log"), + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare auxiliary electricity-demand data from ENTSO-E." + script: + "../scripts/prepare_load_entsoe.py" diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk new file mode 100644 index 0000000..fc22551 --- /dev/null +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -0,0 +1,126 @@ +"""Rules for the ENTSO-E Power Statistics demand source.""" + + +def entsoe_power_statistics_annual_files(years): + """Return supported annual ENTSO-E Power Statistics files.""" + return [ + ( + "/automatic/" + "entsoe_power_statistics/raw/" + f"{int(year)}.parquet" + ) + for year in years + if source_overlaps_period( + "entsoe_power_statistics", + f"{int(year)}-01-01", + f"{int(year) + 1}-01-01", + ) + ] + + +def entsoe_power_statistics_raw_files(_wildcards): + """Return Power Statistics annual files required by the target period.""" + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], + ) + + return entsoe_power_statistics_annual_files(years) + + +def auxiliary_entsoe_power_statistics_raw_files(wildcards): + """Return Power Statistics annual files required by one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == wildcards.batch_id + and batch["source"] == "entsoe_power_statistics" + ) + ) + + return entsoe_power_statistics_annual_files(batch["years"]) + + +rule download_load_entsoe_power_statistics_year: + output: + annual_file=( + "/automatic/entsoe_power_statistics/raw/{year}.parquet" + ), + log: + "/download_load_entsoe_power_statistics_{year}.log", + wildcard_constraints: + year=source_year_pattern("entsoe_power_statistics"), + localrule: True + conda: + "../envs/module.yaml" + threads: 1 + resources: + entsoe_download=1 + params: + year=lambda wildcards: int(wildcards.year), + message: + ( + "Download ENTSO-E Power Statistics " + "electricity load for {wildcards.year}." + ) + script: + "../scripts/download_load_entsoe_power_statistics.py" + + +rule prepare_load_entsoe_power_statistics: + input: + validation=( + "/automatic/" + "temporal_config_validation.json" + ), + annual_files=entsoe_power_statistics_raw_files, + output: + load=( + "/automatic/" + "load_entsoe_power_statistics.parquet" + ), + params: + temporal_start=config["temporal_scope"]["start"], + temporal_end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], + log: + "/prepare_load_entsoe_power_statistics.log", + conda: + "../envs/module.yaml" + message: + "Prepare electricity-demand data from ENTSO-E Power Statistics." + script: + "../scripts/prepare_load_entsoe_power_statistics.py" + + +rule prepare_auxiliary_load_entsoe_power_statistics: + input: + plan=auxiliary_acquisition_plan, + annual_files=auxiliary_entsoe_power_statistics_raw_files, + output: + load=( + "/automatic/" + "auxiliary/entsoe_power_statistics/" + "{batch_id}.parquet" + ), + log: + ( + "/auxiliary/" + "entsoe_power_statistics/" + "prepare_{batch_id}.log" + ), + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + ( + "Prepare auxiliary electricity-demand data " + "from ENTSO-E Power Statistics." + ) + script: + "../scripts/prepare_load_entsoe_power_statistics.py" diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk new file mode 100644 index 0000000..81a8fed --- /dev/null +++ b/workflow/rules/source_neso.smk @@ -0,0 +1,90 @@ +"""Rules for the NESO historic demand source.""" + + +def neso_annual_files(years): + """Return reusable annual NESO raw-file paths for the requested years.""" + return [ + "/automatic/neso/" f"historic_demand_{int(year)}.csv" + for year in years + ] + + +def neso_raw_files(_wildcards): + """Return annual NESO input files for the configured period.""" + years = years_for_period( + config["temporal_scope"]["start"], + config["temporal_scope"]["end"], + ) + + return neso_annual_files(years) + + +def auxiliary_neso_raw_files(wildcards): + """Return annual NESO files required by one auxiliary batch.""" + plan = _read_auxiliary_plan() + + batch = next( + batch + for batch in plan["batches"] + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") + ) + + return neso_annual_files(batch["years"]) + + +rule download_load_neso_year: + output: + annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), + log: + "/download_load_neso_{year}.log", + localrule: True + conda: + "../envs/module.yaml" + threads: 1 + resources: + neso_download=1, + params: + year=lambda wildcards: int(wildcards.year), + message: + "Download NESO historic electricity demand for {wildcards.year}." + script: + "../scripts/download_load_neso.py" + + +rule prepare_load_neso: + input: + validation="/automatic/temporal_config_validation.json", + annual_files=neso_raw_files, + output: + load="/automatic/load_neso.parquet", + log: + "/prepare_load_neso.log", + conda: + "../envs/module.yaml" + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe"]["countries"], + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare electricity-demand data from NESO." + script: + "../scripts/prepare_load_neso.py" + + +rule prepare_auxiliary_load_neso: + input: + plan=auxiliary_acquisition_plan, + annual_files=auxiliary_neso_raw_files, + output: + load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), + log: + "/auxiliary/neso/{batch_id}.log", + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare auxiliary electricity-demand data from NESO." + script: + "../scripts/prepare_load_neso.py" diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk new file mode 100644 index 0000000..be6d87f --- /dev/null +++ b/workflow/rules/source_opsd.smk @@ -0,0 +1,56 @@ +"""Rules for the Open Power System Data demand source.""" + + +rule download_load_opsd: + output: + load=update("/automatic/opsd/raw_load.parquet"), + log: + "/download_load_opsd.log", + localrule: True + conda: + "../envs/module.yaml" + params: + url=internal["resources"]["automatic"]["load_opsd"], + message: + "Download load profiles from Open Power System Data (OPSD)." + script: + "../scripts/download_load_opsd.py" + + +rule prepare_load_opsd: + input: + validation="/automatic/temporal_config_validation.json", + load="/automatic/opsd/raw_load.parquet", + output: + load="/automatic/load_opsd.parquet", + log: + "/prepare_load_opsd.log", + conda: + "../envs/module.yaml" + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], + message: + "Prepare electricity-demand data from OPSD." + script: + "../scripts/prepare_load_opsd.py" + + +rule prepare_auxiliary_load_opsd: + input: + load=rules.download_load_opsd.output.load, + plan=auxiliary_acquisition_plan, + output: + load=("/automatic/" "auxiliary/opsd/" "{batch_id}.parquet"), + log: + ("/auxiliary/" "opsd/{batch_id}.log"), + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare auxiliary electricity-demand data from OPSD." + script: + "../scripts/prepare_load_opsd.py" diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index e259b03..f7d010a 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -12,7 +12,7 @@ main( demand_path=snakemake.input.demand, cleaning_method_path=snakemake.input.cleaning_method, - cleaning_method_rank_path=(nakemake.input.cleaning_method_rank), + cleaning_method_rank_path=(snakemake.input.cleaning_method_rank), output_path=snakemake.output.plot, source_names=snakemake.params.source_names, source_registry=snakemake.params.source_registry, From b70b3f7c59a97cb9fbdea2c2751aadf99a715978 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 19:37:21 +0200 Subject: [PATCH 165/241] Updated READMEs --- README.md | 40 +++++++++++++++++++++++++++++++--------- config/README.md | 15 ++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 36c7ab0..b1b4bf9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This Modelblocks module prepares regular electricity-demand time series for European target regions. National demand observations from multiple providers are combined and cleaned on a user-defined time grid, then spatially disaggregated using population data and aggregated to user-provided shapes. -Demand cleaning is performed with T-Clean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs. +Demand cleaning is performed with tlean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs.

@@ -26,7 +26,7 @@ The workflow first prepares a cleaned national electricity-demand time series on The main processing stages are: -1. Download demand data from the configured providers: ENTSO-E, NESO, and/or OPSD. +1. Download demand data from the configured providers. 2. Prepare each provider dataset on the configured time grid. 3. Combine available providers according to the configured source-priority order. 4. Apply deterministic basic cleaning rules. @@ -102,6 +102,7 @@ The configured start timestamp also defines the phase of the grid. Provider and load_sources: - entsoe - neso + - entsoe_power_statistics - opsd ``` @@ -109,15 +110,18 @@ Where multiple providers supply a value for the same country and timestamp, the Available provider identifiers are: -- `entsoe`: ENTSO-E Transparency Platform; -- `neso`: National Energy System Operator historic demand; -- `opsd`: Open Power System Data. +- `entsoe`: ENTSO-E Transparency Platform API. A valid ENTSO-E API token is required when this source is configured; +- `entsoe_power_statistics`: official ENTSO-E Power Statistics historical archive, currently integrated for 2019–2025. No API token is required; +- `neso`: National Energy System Operator historic demand, restricted to Great Britain (`GBR`); +- `opsd`: Open Power System Data, with the currently integrated historical coverage ending at 2019-03-01. + +Source identifiers, human-readable names, declared temporal bounds, and context restrictions are defined centrally in [`workflow/internal/source_registry.yaml`](./workflow/internal/source_registry.yaml). Missing temporal bounds or context restrictions in the registry mean that the module declares no corresponding restriction. ## Cleaning and gap handling Three modes are available: -- `"off"`: do not fill gaps. Please note that quotation marks are necessary here (i.e. "not") otherwise python reads this as a falsey boolean value; +- `"off"`: do not fill gaps. Quotation marks are required because YAML may interpret an unquoted `off` as the boolean value `false`; - `basic`: apply configured deterministic rules; - `advanced`: run basic cleaning first, then execute active advanced rules. @@ -203,11 +207,11 @@ cd tests/integration/ # navigate to the integration example snakemake # run the workflow! ``` -The integration workflow's default Snakemake profile enables Conda, uses 2 cores, and limits concurrent ENTSO-E and NESO downloads to 2 each. These execution settings can be overridden with the corresponding Snakemake command-line options: +The integration workflow's default Snakemake profile enables Conda, uses 2 cores, and limits concurrent ENTSO-E downloads (including Transparency Platform and Power Statistics acquisition) and NESO downloads to 2 each. These execution settings can be overridden with the corresponding Snakemake command-line options: - **Cores**: Defaults can be overridden using the `--cores` flag. -- **ENTSOE Downloads**: Defaults can be overridden using the `--entsoe_download` flag. -- **NESO Downloads**: Defaults can be overridden using the `--neso_download` flag. +- **ENTSO-E downloads**: Override with `--resources entsoe_download=`. +- **NESO downloads**: Override with `--resources neso_download=`. A complete example: @@ -215,12 +219,30 @@ A complete example: snakemake --cores 4 --resources entsoe_download=1 neso_download=1 ``` +## Adding a demand source + +Demand-provider metadata is registered centrally in [`workflow/internal/source_registry.yaml`](./workflow/internal/source_registry.yaml), while provider-specific workflow behaviour lives in a matching `workflow/rules/source_.smk` file. + +A new provider normally requires: + +1. Add the source identifier and metadata to `workflow/internal/source_registry.yaml`. `display_name` gives the human-readable label; optional `temporal_scope` uses the module-wide half-open convention `[start, end)`; optional `contexts` restricts the source to listed country contexts. +2. Add `workflow/rules/source_.smk` containing the provider-specific acquisition, main preparation, and auxiliary preparation rules and helpers that are required. +3. Add the provider implementation under `workflow/scripts/sources//` together with any thin Snakemake wrapper scripts needed by the rules. +4. Include the new source rule file directly from `workflow/Snakefile`. +5. Add credentials or other user-facing inputs to `INTERFACE.yaml` only when the provider requires them. +6. Add tests covering the provider and, where applicable, both main-period and advanced auxiliary acquisition. + +Prepared national-demand outputs follow the `load_.parquet` naming convention. Generic source validation, display names, and tlean source capabilities are derived from the registry where applicable, so adding a provider should not require separate source-name mappings in those parts of the workflow. + +Provider-specific behaviour should remain explicit rather than being encoded as generic registry metadata: APIs, raw cache layouts, download resources, preparation logic, and auxiliary-file resolution belong in the provider implementation and its source rule file. + ## References This module is based on the following research and datasets: * ENTSOE Transparency Platform (https://transparency.entsoe.eu) +* ENTSO-E Power Statistics (https://www.entsoe.eu/data/power-stats/) * Open Power System Data (https://data.open-power-system-data.org) * NESO Data Portal (https://www.neso.energy/data-portal/historic-demand-data) * Schiavina M., Freire S., Carioli A., MacManus K. (2023): diff --git a/config/README.md b/config/README.md index 0863d75..6ae12c8 100644 --- a/config/README.md +++ b/config/README.md @@ -8,6 +8,7 @@ Useful references are: - [`config/config.yaml`](./config.yaml): example configuration; - [`workflow/internal/config.schema.yaml`](../workflow/internal/config.schema.yaml): authoritative configuration schema; +- [`workflow/internal/source_registry.yaml`](../workflow/internal/source_registry.yaml): available demand providers and their metadata; - [`INTERFACE.yaml`](../INTERFACE.yaml): module input/output interface; - [`tests/integration/test_config.yaml`](../tests/integration/test_config.yaml): a richer integration configuration; - [`tests/integration/resources/user/external_profiles`](../tests/integration/user/external_profiles): example of an external profile. @@ -44,17 +45,21 @@ Date-only timestamps represent midnight. Date-time strings may be used when the load_sources: - entsoe - neso + - entsoe_power_statistics - opsd ``` Available identifiers are: -- `entsoe`: ENTSO-E Transparency Platform; -- `neso`: National Energy System Operator historic demand; -- `opsd`: Open Power System Data. +- `entsoe`: ENTSO-E Transparency Platform API. The module declares availability from 2005-01-01 onward; a valid ENTSO-E API token is required when this source is configured; +- `entsoe_power_statistics`: official ENTSO-E Power Statistics historical archive, currently integrated for 2019–2025; no API token is required; +- `neso`: National Energy System Operator historic demand, restricted to Great Britain (`GBR`); +- `opsd`: Open Power System Data, with module-declared coverage from 2005-01-01 up to, but not including, 2019-03-01. Sources are combined in the listed order. When more than one provider supplies a value for the same country and timestamp, the higher-priority provider is retained. +The authoritative list of provider identifiers and source metadata is [`workflow/internal/source_registry.yaml`](../workflow/internal/source_registry.yaml). Its `temporal_scope` bounds use the same half-open `[start, end)` convention as the model time grid. An omitted bound means no restriction is declared in that direction, and omitted or empty `contexts` means no context restriction is declared. + ## Gap filling Gap handling is configured below `gap_filling`. @@ -66,7 +71,7 @@ gap_filling: Three modes are available: -- `off`: no gap filling; +- `"off"`: no gap filling. Quotation marks are required because YAML may interpret an unquoted `off` as the boolean value `false`; - `basic`: apply deterministic basic rules in configured order; - `advanced`: run basic cleaning first and then apply configured advanced rules that are active for the current target countries and time grid. @@ -275,7 +280,7 @@ Scaling periods can require auxiliary demand data outside the main target grid; ## Advanced source: `external_profile` -An `external_profile` source reads a user-supplied CSV. By default, external profiles are resolved from `resources/user/external_profiles/`. This location can re-wired through the `external_profiles` pathvar when importing the module. +An `external_profile` source reads a user-supplied CSV. By default, external profiles are resolved from `resources/user/external_profiles/`. This location can be re-wired through the `external_profiles` pathvar when importing the module. ```yaml advanced: From d8c4169923fbb4850f732a99220416b4d497a945 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 31 Aug 2026 19:52:34 +0200 Subject: [PATCH 166/241] Added rate-limit to protect from API bans --- tests/integration/profiles/default/config.yaml | 7 ++++++- workflow/profiles/default/config.yaml | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/integration/profiles/default/config.yaml b/tests/integration/profiles/default/config.yaml index 4122207..5b17513 100644 --- a/tests/integration/profiles/default/config.yaml +++ b/tests/integration/profiles/default/config.yaml @@ -6,7 +6,12 @@ use-conda: True #Cores: conservative but useful default value cores: 2 -#Download Resources: conservative defaults to minimise API traffic. +# Download Resources: conservative defaults to minimise API traffic. +# IMPORTANT: ENTSO-E imposes bans on tokens which exceed 400 requests/min. resources: entsoe_download: 2 neso_download: 2 + +# Safety margin below ENTSO-E's 400 requests/minute API limit. +# Limit applies to all jobs but unlikely to impact workflow. +max-jobs-per-timespan: 300/1m \ No newline at end of file diff --git a/workflow/profiles/default/config.yaml b/workflow/profiles/default/config.yaml index 4122207..5b17513 100644 --- a/workflow/profiles/default/config.yaml +++ b/workflow/profiles/default/config.yaml @@ -6,7 +6,12 @@ use-conda: True #Cores: conservative but useful default value cores: 2 -#Download Resources: conservative defaults to minimise API traffic. +# Download Resources: conservative defaults to minimise API traffic. +# IMPORTANT: ENTSO-E imposes bans on tokens which exceed 400 requests/min. resources: entsoe_download: 2 neso_download: 2 + +# Safety margin below ENTSO-E's 400 requests/minute API limit. +# Limit applies to all jobs but unlikely to impact workflow. +max-jobs-per-timespan: 300/1m \ No newline at end of file From 0af171fd73166fa51bf461c49db4e0969c64ef54 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:13:46 +0200 Subject: [PATCH 167/241] Auxiliary grouping bug. tclean requires all sources to have the same fields. --- workflow/scripts/clean_auxiliary_group.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index d89d17d..cf38729 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -67,6 +67,19 @@ def main(snakemake: Any) -> None: sources[source_name] = _read_prepared_source(path) + contexts = sorted( + { + context + for data in sources.values() + for context in data.columns + } + ) + + sources = { + source_name: data.reindex(columns=contexts) + for source_name, data in sources.items() + } + basic_rules = ( list(snakemake.params.basic_rules) if snakemake.params.basic_cleaning_enabled From 58646c78fd21703633e44cdd4fad55f063a2d827 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:13:59 +0000 Subject: [PATCH 168/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../integration/profiles/default/config.yaml | 2 +- workflow/Snakefile | 5 +- workflow/internal/source_registry.yaml | 4 +- workflow/profiles/default/config.yaml | 2 +- workflow/rules/_utils.smk | 18 +- workflow/rules/clean.smk | 5 +- .../rules/source_entsoe_power_statistics.smk | 46 +--- workflow/scripts/_plot_timeline.py | 15 +- workflow/scripts/_tclean_config.py | 31 +-- workflow/scripts/clean_auxiliary_group.py | 6 +- .../download_load_entsoe_power_statistics.py | 14 +- workflow/scripts/plan_auxiliary_data.py | 3 +- .../prepare_load_entsoe_power_statistics.py | 36 +-- .../entsoe_power_statistics/download.py | 217 ++++-------------- .../entsoe_power_statistics/prepare.py | 43 +--- 15 files changed, 102 insertions(+), 345 deletions(-) diff --git a/tests/integration/profiles/default/config.yaml b/tests/integration/profiles/default/config.yaml index 5b17513..607b621 100644 --- a/tests/integration/profiles/default/config.yaml +++ b/tests/integration/profiles/default/config.yaml @@ -14,4 +14,4 @@ resources: # Safety margin below ENTSO-E's 400 requests/minute API limit. # Limit applies to all jobs but unlikely to impact workflow. -max-jobs-per-timespan: 300/1m \ No newline at end of file +max-jobs-per-timespan: 300/1m diff --git a/workflow/Snakefile b/workflow/Snakefile index 0f2e318..8d0991a 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -37,22 +37,23 @@ workflow.source_path("scripts/_schemas.py") # Add all your includes here. + # IMPORTANT: General .smk files must be included after _utils.smk include: "rules/_utils.smk" + additional_config_validation() + # Generic demand workflow rules. include: "rules/automatic.smk" include: "rules/clean.smk" include: "rules/advanced.smk" - # Source-specific acquisition and preparation rules. include: "rules/source_entsoe.smk" include: "rules/source_entsoe_power_statistics.smk" include: "rules/source_neso.smk" include: "rules/source_opsd.smk" - # Spatial demand preparation. include: "rules/prepare_demand.smk" diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index 96b5fe5..c4ce109 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -3,7 +3,7 @@ # # Sources may optionally declare: # - display_name: human-readable name used for display and plotting. -# - temporal_scope: valid source interval [start, end). +# - temporal_scope: valid source interval [start, end). # Either bound may be omitted to indicate no declared bound. # - contexts: eligible contexts for this source. # If omitted or empty, no context restriction is declared. @@ -28,4 +28,4 @@ opsd: display_name: OPSD temporal_scope: start: "2005-01-01" - end: "2019-03-01" \ No newline at end of file + end: "2019-03-01" diff --git a/workflow/profiles/default/config.yaml b/workflow/profiles/default/config.yaml index 5b17513..607b621 100644 --- a/workflow/profiles/default/config.yaml +++ b/workflow/profiles/default/config.yaml @@ -14,4 +14,4 @@ resources: # Safety margin below ENTSO-E's 400 requests/minute API limit. # Limit applies to all jobs but unlikely to impact workflow. -max-jobs-per-timespan: 300/1m \ No newline at end of file +max-jobs-per-timespan: 300/1m diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index f440910..929ec19 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -3,10 +3,7 @@ from datetime import datetime, timedelta, timezone import yaml - -SOURCE_REGISTRY_PATH = workflow.source_path( - "../internal/source_registry.yaml" -) +SOURCE_REGISTRY_PATH = workflow.source_path("../internal/source_registry.yaml") with open( SOURCE_REGISTRY_PATH, @@ -16,8 +13,7 @@ with open( if not isinstance(SOURCE_REGISTRY, dict): raise ValueError( - "Source registry must contain a mapping " - "of source identifiers to metadata." + "Source registry must contain a mapping " "of source identifiers to metadata." ) @@ -76,10 +72,7 @@ def years_for_period(start, end): def source_year_pattern(source_name): """Return a year wildcard pattern for a bounded source.""" - temporal_scope = ( - SOURCE_REGISTRY[source_name].get("temporal_scope") - or {} - ) + temporal_scope = SOURCE_REGISTRY[source_name].get("temporal_scope") or {} start = temporal_scope.get("start") end = temporal_scope.get("end") @@ -87,10 +80,7 @@ def source_year_pattern(source_name): if start is None or end is None: return "[0-9]{4}" - return "|".join( - str(year) - for year in years_for_period(start, end) - ) + return "|".join(str(year) for year in years_for_period(start, end)) def additional_config_validation(): diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index a647e32..9f68e9c 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,10 +1,7 @@ def configured_load_inputs(_wildcards): """Return prepared demand files for configured sources.""" return [ - ( - "/automatic/" - f"load_{source_name}.parquet" - ) + ("/automatic/" f"load_{source_name}.parquet") for source_name in config["load_sources"] ] diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index fc22551..784b0ee 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -4,11 +4,7 @@ def entsoe_power_statistics_annual_files(years): """Return supported annual ENTSO-E Power Statistics files.""" return [ - ( - "/automatic/" - "entsoe_power_statistics/raw/" - f"{int(year)}.parquet" - ) + ("/automatic/" "entsoe_power_statistics/raw/" f"{int(year)}.parquet") for year in years if source_overlaps_period( "entsoe_power_statistics", @@ -46,9 +42,7 @@ def auxiliary_entsoe_power_statistics_raw_files(wildcards): rule download_load_entsoe_power_statistics_year: output: - annual_file=( - "/automatic/entsoe_power_statistics/raw/{year}.parquet" - ), + annual_file=("/automatic/entsoe_power_statistics/raw/{year}.parquet"), log: "/download_load_entsoe_power_statistics_{year}.log", wildcard_constraints: @@ -58,39 +52,30 @@ rule download_load_entsoe_power_statistics_year: "../envs/module.yaml" threads: 1 resources: - entsoe_download=1 + entsoe_download=1, params: year=lambda wildcards: int(wildcards.year), message: - ( - "Download ENTSO-E Power Statistics " - "electricity load for {wildcards.year}." - ) + ("Download ENTSO-E Power Statistics " "electricity load for {wildcards.year}.") script: "../scripts/download_load_entsoe_power_statistics.py" rule prepare_load_entsoe_power_statistics: input: - validation=( - "/automatic/" - "temporal_config_validation.json" - ), + validation=("/automatic/" "temporal_config_validation.json"), annual_files=entsoe_power_statistics_raw_files, output: - load=( - "/automatic/" - "load_entsoe_power_statistics.parquet" - ), + load=("/automatic/" "load_entsoe_power_statistics.parquet"), + log: + "/prepare_load_entsoe_power_statistics.log", + conda: + "../envs/module.yaml" params: temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], frequency=config["temporal_scope"]["frequency"], country_codes=internal["load_entsoe"]["countries"], - log: - "/prepare_load_entsoe_power_statistics.log", - conda: - "../envs/module.yaml" message: "Prepare electricity-demand data from ENTSO-E Power Statistics." script: @@ -108,19 +93,12 @@ rule prepare_auxiliary_load_entsoe_power_statistics: "{batch_id}.parquet" ), log: - ( - "/auxiliary/" - "entsoe_power_statistics/" - "prepare_{batch_id}.log" - ), + ("/auxiliary/" "entsoe_power_statistics/" "prepare_{batch_id}.log"), conda: "../envs/module.yaml" params: frequency=config["temporal_scope"]["frequency"], message: - ( - "Prepare auxiliary electricity-demand data " - "from ENTSO-E Power Statistics." - ) + ("Prepare auxiliary electricity-demand data " "from ENTSO-E Power Statistics.") script: "../scripts/prepare_load_entsoe_power_statistics.py" diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 1d36aba..f140c35 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -335,7 +335,10 @@ def _add_mean_load_labels( def _build_cleaning_method_metadata( - *, source_names: list[str], gap_filling_config: dict[str, Any],source_registry: Mapping[str, Mapping[str, Any]], + *, + source_names: list[str], + gap_filling_config: dict[str, Any], + source_registry: Mapping[str, Mapping[str, Any]], ) -> pd.DataFrame: """Build complete method metadata in configured rank order.""" rows: list[dict[str, Any]] = [] @@ -388,18 +391,12 @@ def _build_cleaning_method_metadata( def _format_source_name( - source_name: str, - source_registry: Mapping[str, Mapping[str, Any]], + source_name: str, source_registry: Mapping[str, Mapping[str, Any]] ) -> str: """Return the configured human-readable source name.""" metadata = source_registry.get(source_name, {}) - return str( - metadata.get( - "display_name", - source_name, - ) - ) + return str(metadata.get("display_name", source_name)) def _format_rule_name(name: str) -> str: diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index bdfd6e2..16e1617 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -136,42 +136,25 @@ def build_all_constructed_source_periods( def build_source_capabilities( - source_names: Sequence[str], - *, - source_registry: Mapping[str, Mapping[str, Any]], + source_names: Sequence[str], *, source_registry: Mapping[str, Mapping[str, Any]] ) -> pd.DataFrame: """Describe which contexts configured providers can supply.""" if len(source_names) != len(set(source_names)): - raise ValueError( - "Configured load source names must be unique." - ) + raise ValueError("Configured load source names must be unique.") capabilities: list[dict[str, object]] = [] for source_name in source_names: if source_name not in source_registry: - raise ValueError( - "Unsupported electricity-demand source: " - f"{source_name!r}." - ) - - contexts = ( - source_registry[source_name].get("contexts") - or [None] - ) + raise ValueError(f"Unsupported electricity-demand source: {source_name!r}.") + + contexts = source_registry[source_name].get("contexts") or [None] capabilities.extend( - { - "source": source_name, - "context": context, - } - for context in contexts + {"source": source_name, "context": context} for context in contexts ) - return pd.DataFrame( - capabilities, - columns=["source", "context"], - ) + return pd.DataFrame(capabilities, columns=["source", "context"]) def get_advanced_source_definitions( diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index cf38729..939d7a5 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -68,11 +68,7 @@ def main(snakemake: Any) -> None: sources[source_name] = _read_prepared_source(path) contexts = sorted( - { - context - for data in sources.values() - for context in data.columns - } + {context for data in sources.values() for context in data.columns} ) sources = { diff --git a/workflow/scripts/download_load_entsoe_power_statistics.py b/workflow/scripts/download_load_entsoe_power_statistics.py index 3263bdd..ce3eb68 100644 --- a/workflow/scripts/download_load_entsoe_power_statistics.py +++ b/workflow/scripts/download_load_entsoe_power_statistics.py @@ -16,21 +16,13 @@ def main(snakemake: Any) -> None: """Download and harmonise one ENTSO-E Power Statistics year.""" download_entsoe_power_statistics_year( - year=int(snakemake.params.year), - output_path=Path(snakemake.output.annual_file), + year=int(snakemake.params.year), output_path=Path(snakemake.output.annual_file) ) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main(snakemake) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 1dbd9c1..3da2b65 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -76,8 +76,7 @@ def build_advanced_execution_plan( ) source_capabilities = build_source_capabilities( - source_names, - source_registry=source_registry, + source_names, source_registry=source_registry ) requests = build_auxiliary_source_requests( diff --git a/workflow/scripts/prepare_load_entsoe_power_statistics.py b/workflow/scripts/prepare_load_entsoe_power_statistics.py index 0cb8f1e..a49d7c1 100644 --- a/workflow/scripts/prepare_load_entsoe_power_statistics.py +++ b/workflow/scripts/prepare_load_entsoe_power_statistics.py @@ -6,9 +6,7 @@ from typing import TYPE_CHECKING, Any from _advanced_execution import get_batch, load_execution_plan -from sources.entsoe_power_statistics.prepare import ( - prepare_entsoe_power_statistics, -) +from sources.entsoe_power_statistics.prepare import prepare_entsoe_power_statistics from tclean import TimeGrid if TYPE_CHECKING: @@ -17,11 +15,7 @@ def main(snakemake: Any) -> None: """Prepare ENTSO-E Power Statistics for the requested workflow period.""" - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) @@ -39,21 +33,12 @@ def main(snakemake: Any) -> None: else: start = snakemake.params.temporal_start end = snakemake.params.temporal_end - country_codes = list( - snakemake.params.country_codes - ) + country_codes = list(snakemake.params.country_codes) - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_entsoe_power_statistics( - input_paths=[ - Path(path) - for path in snakemake.input.annual_files - ], + input_paths=[Path(path) for path in snakemake.input.annual_files], output_path=snakemake.output.load, grid=grid, country_codes=country_codes, @@ -61,15 +46,8 @@ def main(snakemake: Any) -> None: if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main(snakemake) diff --git a/workflow/scripts/sources/entsoe_power_statistics/download.py b/workflow/scripts/sources/entsoe_power_statistics/download.py index 780ca04..19ceb40 100644 --- a/workflow/scripts/sources/entsoe_power_statistics/download.py +++ b/workflow/scripts/sources/entsoe_power_statistics/download.py @@ -19,19 +19,12 @@ "{year}/monthly_hourly_load_values_{year}.csv" ) -USER_AGENT = ( - "modelblocks-module-demand-electricity/" - "ENTSO-E-Power-Statistics" -) +USER_AGENT = "modelblocks-module-demand-electricity/ENTSO-E-Power-Statistics" def _detect_delimiter(path: Path) -> str: """Detect the delimiter used by an ENTSO-E Power Statistics CSV.""" - with path.open( - "r", - encoding="utf-8-sig", - errors="replace", - ) as file: + with path.open("r", encoding="utf-8-sig", errors="replace") as file: header = file.readline() if "\t" in header: @@ -46,17 +39,12 @@ def _detect_delimiter(path: Path) -> str: ) -def _build_interval_start( - date_short: pd.Series, - time_from: pd.Series, -) -> pd.Series: +def _build_interval_start(date_short: pd.Series, time_from: pd.Series) -> pd.Series: """Build the hourly interval-start timestamps from source labels.""" available = date_short.dropna() if available.empty: - raise ValueError( - "ENTSO-E Power Statistics file contains no dates." - ) + raise ValueError("ENTSO-E Power Statistics file contains no dates.") sample = str(available.iloc[0]).strip() @@ -66,75 +54,44 @@ def _build_interval_start( date_format = "%d-%m-%Y" else: raise ValueError( - "Unsupported ENTSO-E Power Statistics date " - f"format: {sample!r}." + f"Unsupported ENTSO-E Power Statistics date format: {sample!r}." ) - dates = pd.to_datetime( - date_short, - format=date_format, - utc=True, - errors="raise", - ) + dates = pd.to_datetime(date_short, format=date_format, utc=True, errors="raise") - time_strings = ( - time_from - .astype("string") - .str.strip() - ) + time_strings = time_from.astype("string").str.strip() missing_times = time_strings.isna() if missing_times.any(): - raise ValueError( - "ENTSO-E Power Statistics contains missing " - "TimeFrom values." - ) + raise ValueError("ENTSO-E Power Statistics contains missing TimeFrom values.") - needs_seconds = ( - time_strings.str.count(":") == 1 - ) + needs_seconds = time_strings.str.count(":") == 1 - time_strings = time_strings.where( - ~needs_seconds, - time_strings + ":00", - ) + time_strings = time_strings.where(~needs_seconds, time_strings + ":00") - times = pd.to_timedelta( - time_strings, - errors="raise", - ) + times = pd.to_timedelta(time_strings, errors="raise") return dates + times -def _get_map_alpha2_to_alpha3( - countries_alpha_2, -) -> dict[str, str]: +def _get_map_alpha2_to_alpha3(countries_alpha_2) -> dict[str, str]: """Map ISO alpha-2 country codes to ISO alpha-3 codes.""" mapping = {} for alpha2 in countries_alpha_2: - country = pycountry.countries.get( - alpha_2=alpha2 - ) + country = pycountry.countries.get(alpha_2=alpha2) if country is not None: mapping[alpha2] = country.alpha_3 else: - warn( - f"Country with alpha-2 code {alpha2!r} " - "not found in pycountry." - ) + warn(f"Country with alpha-2 code {alpha2!r} not found in pycountry.") return mapping def harmonise_entsoe_power_statistics_csv( - *, - input_path: str | Path, - output_path: str | Path, - year: int, + *, input_path: str | Path, output_path: str | Path, year: int ) -> None: """Convert one annual Power Statistics CSV to canonical Parquet.""" input_path = Path(input_path) @@ -145,60 +102,26 @@ def harmonise_entsoe_power_statistics_csv( data = pd.read_csv( input_path, sep=delimiter, - usecols=[ - "DateShort", - "TimeFrom", - "CountryCode", - "Value_ScaleTo100", - ], + usecols=["DateShort", "TimeFrom", "CountryCode", "Value_ScaleTo100"], ) - data["CountryCode"] = ( - data["CountryCode"] - .astype("string") - .str.strip() - ) + data["CountryCode"] = data["CountryCode"].astype("string").str.strip() - data["timestamp"] = _build_interval_start( - data["DateShort"], - data["TimeFrom"], - ) + data["timestamp"] = _build_interval_start(data["DateShort"], data["TimeFrom"]) - data["Value_ScaleTo100"] = pd.to_numeric( - data["Value_ScaleTo100"], - errors="raise", - ) + data["Value_ScaleTo100"] = pd.to_numeric(data["Value_ScaleTo100"], errors="raise") - country_mapping = _get_map_alpha2_to_alpha3( - data["CountryCode"] - .dropna() - .unique() - ) + country_mapping = _get_map_alpha2_to_alpha3(data["CountryCode"].dropna().unique()) - data = data.loc[ - data["CountryCode"].isin(country_mapping) - ].copy() + data = data.loc[data["CountryCode"].isin(country_mapping)].copy() - data["country"] = data["CountryCode"].map( - country_mapping - ) + data["country"] = data["CountryCode"].map(country_mapping) - duplicate_mask = data.duplicated( - subset=["country", "timestamp"], - keep=False, - ) + duplicate_mask = data.duplicated(subset=["country", "timestamp"], keep=False) if duplicate_mask.any(): examples = ( - data.loc[ - duplicate_mask, - [ - "country", - "timestamp", - "DateShort", - "TimeFrom", - ], - ] + data.loc[duplicate_mask, ["country", "timestamp", "DateShort", "TimeFrom"]] .drop_duplicates() .head(10) .to_dict("records") @@ -210,23 +133,13 @@ def harmonise_entsoe_power_statistics_csv( f"Examples: {examples}." ) - grid = TimeGrid( - start=f"{year}-01-01", - end=f"{year + 1}-01-01", - frequency="1h", - ) + grid = TimeGrid(start=f"{year}-01-01", end=f"{year + 1}-01-01", frequency="1h") - outside_year = ( - (data["timestamp"] < grid.start) - | (data["timestamp"] >= grid.end) - ) + outside_year = (data["timestamp"] < grid.start) | (data["timestamp"] >= grid.end) if outside_year.any(): examples = ( - data.loc[ - outside_year, - "timestamp", - ] + data.loc[outside_year, "timestamp"] .drop_duplicates() .sort_values() .head(10) @@ -240,49 +153,31 @@ def harmonise_entsoe_power_statistics_csv( f"year. Examples: {examples}." ) - wide = data.pivot( - index="timestamp", - columns="country", - values="Value_ScaleTo100", - ) + wide = data.pivot(index="timestamp", columns="country", values="Value_ScaleTo100") wide = wide.sort_index(axis=1) - wide = wide.reindex( - index=grid.target_index, - ) + wide = wide.reindex(index=grid.target_index) wide = wide.astype(float) wide.index.name = "timestamp" wide.columns.name = None - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - temporary_output = output_path.with_suffix( - ".tmp.parquet" - ) + temporary_output = output_path.with_suffix(".tmp.parquet") try: - wide.to_parquet( - temporary_output, - ) + wide.to_parquet(temporary_output) - temporary_output.replace( - output_path, - ) + temporary_output.replace(output_path) finally: - temporary_output.unlink( - missing_ok=True, - ) + temporary_output.unlink(missing_ok=True) logger.info( - "Saved ENTSO-E Power Statistics %s to %s " - "with shape %s.", + "Saved ENTSO-E Power Statistics %s to %s with shape %s.", year, output_path, wide.shape, @@ -290,55 +185,31 @@ def harmonise_entsoe_power_statistics_csv( def download_entsoe_power_statistics_year( - *, - year: int, - output_path: str | Path, + *, year: int, output_path: str | Path ) -> None: """Download and harmonise one annual Power Statistics file.""" output_path = Path(output_path) - url = URL_TEMPLATE.format( - year=year, - ) + url = URL_TEMPLATE.format(year=year) - logger.info( - "Downloading ENTSO-E Power Statistics for %s.", - year, - ) + logger.info("Downloading ENTSO-E Power Statistics for %s.", year) - request = Request( - url, - headers={ - "User-Agent": USER_AGENT, - }, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) with TemporaryDirectory() as temporary_directory: - csv_path = ( - Path(temporary_directory) - / f"monthly_hourly_load_values_{year}.csv" - ) + csv_path = Path(temporary_directory) / f"monthly_hourly_load_values_{year}.csv" with ( - urlopen( - request, - timeout=300, - ) as response, + urlopen(request, timeout=300) as response, csv_path.open("wb") as output_file, ): - shutil.copyfileobj( - response, - output_file, - ) + shutil.copyfileobj(response, output_file) if csv_path.stat().st_size == 0: raise RuntimeError( - "Downloaded ENTSO-E Power Statistics " - f"file for {year} is empty." + f"Downloaded ENTSO-E Power Statistics file for {year} is empty." ) harmonise_entsoe_power_statistics_csv( - input_path=csv_path, - output_path=output_path, - year=year, + input_path=csv_path, output_path=output_path, year=year ) diff --git a/workflow/scripts/sources/entsoe_power_statistics/prepare.py b/workflow/scripts/sources/entsoe_power_statistics/prepare.py index 6d4cc36..56e06d4 100644 --- a/workflow/scripts/sources/entsoe_power_statistics/prepare.py +++ b/workflow/scripts/sources/entsoe_power_statistics/prepare.py @@ -15,40 +15,25 @@ def prepare_entsoe_power_statistics( country_codes: list[str], ) -> None: """Prepare ENTSO-E Power Statistics demand on the target grid.""" - input_paths = [ - Path(path) - for path in input_paths - ] + input_paths = [Path(path) for path in input_paths] frames = [] for input_path in input_paths: frame = pd.read_parquet(input_path) - frame.index = pd.to_datetime( - frame.index, - utc=True, - ) + frame.index = pd.to_datetime(frame.index, utc=True) frames.append(frame) if frames: - data = pd.concat( - frames, - axis=0, - sort=False, - ).sort_index() + data = pd.concat(frames, axis=0, sort=False).sort_index() - duplicate_mask = data.index.duplicated( - keep=False, - ) + duplicate_mask = data.index.duplicated(keep=False) if duplicate_mask.any(): duplicate_timestamps = ( - data.index[duplicate_mask] - .unique() - .astype(str) - .tolist() + data.index[duplicate_mask].unique().astype(str).tolist() ) raise ValueError( @@ -58,24 +43,14 @@ def prepare_entsoe_power_statistics( ) else: - data = pd.DataFrame( - dtype=float, - ) + data = pd.DataFrame(dtype=float) - data = data.reindex( - index=grid.target_index, - columns=country_codes, - ) + data = data.reindex(index=grid.target_index, columns=country_codes) data = data.astype(float) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - data.to_parquet( - output_path, - ) + data.to_parquet(output_path) From 0d8d4b2a83ca30be8e45524c13795da4d1557968 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:23:54 +0200 Subject: [PATCH 169/241] Improved representative examples for test config. --- tests/integration/test_config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 2c0e463..013a00e 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -11,9 +11,9 @@ integration_test: # Supported sources include: entsoe, neso, opsd. load_sources: # - entsoe - - entsoe_power_statistics - neso - opsd + - entsoe_power_statistics # Exercise the full advanced gap-filling workflow. See config/README.md for # the complete configuration reference and method descriptions. @@ -75,8 +75,8 @@ integration_test: method: match_total periods: - country: ALB - start: "2024-01-01" - end: "2024-02-01" + start: "2024-02-01" + end: "2024-03-01" weight: 1 # Construct an Albanian profile from a later period of Albanian data. @@ -94,8 +94,8 @@ integration_test: method: match_total periods: - country: ALB - start: "2024-01-01" - end: "2024-01-03" + start: "2024-02-01" + end: "2024-03-01" weight: 1 # Construct a second Albanian profile from the same 2024 source period, From eabbd9e748f9af740b0a3b6508607b1930460166 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:24:55 +0200 Subject: [PATCH 170/241] Updated the plot to provide more complete diagnostic information and use a Paper friendly format. --- workflow/rules/clean.smk | 4 +- workflow/scripts/_plot_timeline.py | 495 ++++++++++++++++++--- workflow/scripts/plot_cleaning_timeline.py | 6 +- 3 files changed, 435 insertions(+), 70 deletions(-) diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index a647e32..74f683e 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -36,12 +36,14 @@ rule clean_demand: rule plot_cleaning_timeline: input: demand=("/automatic/" "load_cleaned.parquet"), + basic_cleaning_method=("/automatic/" "load_cleaning_method.parquet"), cleaning_method=("/automatic/" "load_final_cleaning_method.parquet"), cleaning_method_rank=( "/automatic/" "load_final_cleaning_method_rank.parquet" ), output: plot=("/{shape}/" "load_cleaning_timeline.pdf"), + summary=("/{shape}/" "load_cleaning_summary.html"), log: "/{shape}/plot_cleaning_timeline.log", conda: @@ -51,7 +53,7 @@ rule plot_cleaning_timeline: gap_filling=config["gap_filling"], source_registry=SOURCE_REGISTRY, message: - "Plot electricity-demand cleaning provenance." + "Plot electricity-demand cleaning provenance and summarise completeness." script: "../scripts/plot_cleaning_timeline.py" diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 1d36aba..bfc9962 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -16,24 +16,30 @@ logger = logging.getLogger(__name__) +FIGURE_SIZE_INCHES = (7.5, 9.8) + def main( *, demand_path: str | Path, + basic_cleaning_method_path: str | Path, cleaning_method_path: str | Path, cleaning_method_rank_path: str | Path, output_path: str | Path, + summary_output_path: str | Path, source_names: list[str], gap_filling_config: dict[str, Any], source_registry: Mapping[str, Mapping[str, Any]], ) -> None: - """Create the electricity-demand cleaning diagnostic.""" + """Create the electricity-demand cleaning diagnostic and summary.""" demand = pd.read_parquet(demand_path) + basic_cleaning_method = pd.read_parquet(basic_cleaning_method_path) cleaning_method = pd.read_parquet(cleaning_method_path) cleaning_method_rank = pd.read_parquet(cleaning_method_rank_path) _validate_alignment( demand=demand, + basic_cleaning_method=basic_cleaning_method, cleaning_method=cleaning_method, cleaning_method_rank=cleaning_method_rank, ) @@ -52,43 +58,73 @@ def main( rank_colours=rank_colours, ) + summary = _build_country_summary( + demand=demand, + basic_cleaning_method=basic_cleaning_method, + cleaning_method=cleaning_method, + gap_filling_config=gap_filling_config, + ) + logger.info( - "Loaded %s timestamps for %s countries.", len(demand), len(demand.columns) + "Loaded %s timestamps for %s countries.", + len(demand), + len(demand.columns), ) logger.info("Cleaning-method ranks:\n%s", metadata.to_string(index=False)) - figure, axis = _plot_cleaning_background( - demand=demand, background=background, background_cmap=background_cmap + figure, axis, summary_axis, legend_axis = _plot_cleaning_background( + demand=demand, + background=background, + background_cmap=background_cmap, ) - mean_load_gw = _add_normalised_demand_traces(axis=axis, demand=demand) + _add_normalised_demand_traces( + axis=axis, + demand=demand, + ) - _add_mean_load_labels( - axis=axis, mean_load_gw=mean_load_gw, countries=demand.columns + _add_summary_panel( + axis=summary_axis, + summary=summary, + gap_filling_config=gap_filling_config, ) legend_handles = _build_legend_handles(metadata, rank_colours) - figure.legend( + legend_axis.legend( handles=legend_handles, - loc="center left", - bbox_to_anchor=(1.01, 0.5), + loc="center", frameon=False, + ncol=min(3, max(1, len(legend_handles))), + fontsize=6.5, + handlelength=1.5, + handletextpad=0.5, + columnspacing=0.8, ) output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) - figure.savefig(output_path, bbox_inches="tight") + # Do not use bbox_inches="tight": preserving the fixed portrait figure + # dimensions makes the PDF predictable when placed in a manuscript. + figure.savefig(output_path) plt.close(figure) + _write_summary_html( + summary=summary, + output_path=summary_output_path, + gap_filling_config=gap_filling_config, + ) + logger.info("Saved cleaning timeline to %s.", output_path) + logger.info("Saved cleaning summary to %s.", summary_output_path) def _build_legend_handles( - metadata: pd.DataFrame, rank_colours: dict[int, tuple[float, float, float, float]] + metadata: pd.DataFrame, + rank_colours: dict[int, tuple[float, float, float, float]], ) -> list[Patch]: """Create handles for every configured rank.""" handles: list[Patch] = [] @@ -113,11 +149,13 @@ def _build_legend_handles( def _validate_alignment( *, demand: pd.DataFrame, + basic_cleaning_method: pd.DataFrame, cleaning_method: pd.DataFrame, cleaning_method_rank: pd.DataFrame, ) -> None: - """Require all plot inputs to use the same time-country grid.""" + """Require all diagnostic inputs to use the same time-country grid.""" for name, frame in { + "basic_cleaning_method": basic_cleaning_method, "cleaning_method": cleaning_method, "cleaning_method_rank": cleaning_method_rank, }.items(): @@ -137,9 +175,7 @@ def _build_rank_colours( ordered = metadata.sort_values("cleaning_method_rank") observed = ordered.loc[ordered["category"] == "observed"] - imputed = ordered.loc[ordered["category"] == "imputed"] - missing = ordered.loc[ordered["category"] == "missing"] # Primary source is white. Subsequent observed sources @@ -149,12 +185,10 @@ def _build_rank_colours( for (_, row), shade in zip(observed.iterrows(), observed_shades, strict=True): rank = int(row["cleaning_method_rank"]) - colours[rank] = (float(shade), float(shade), float(shade), 1.0) if not imputed.empty: colourtheme = Colormap("bids:viridis").to_mpl() - positions = np.linspace(0.05, 0.95, len(imputed)) for (_, row), position in zip(imputed.iterrows(), positions, strict=True): @@ -166,7 +200,6 @@ def _build_rank_colours( colours[rank] = to_rgba("#ff0000") expected_ranks = set(metadata["cleaning_method_rank"].astype(int)) - missing_colours = expected_ranks - set(colours) if missing_colours: @@ -183,11 +216,9 @@ def _encode_rank_background( ) -> tuple[np.ndarray, ListedColormap]: """Encode ranks as contiguous plotting codes.""" rank_order = metadata["cleaning_method_rank"].astype(int).tolist() - rank_to_code = {rank: code for code, rank in enumerate(rank_order)} encoded = cleaning_method_rank.apply(lambda column: column.map(rank_to_code)) - colour_list = [rank_colours[rank] for rank in rank_order] # Input frames are time × country, whereas imshow expects @@ -198,8 +229,11 @@ def _encode_rank_background( def _plot_cleaning_background( - *, demand: pd.DataFrame, background: np.ndarray, background_cmap: ListedColormap -) -> tuple[plt.Figure, plt.Axes]: + *, + demand: pd.DataFrame, + background: np.ndarray, + background_cmap: ListedColormap, +) -> tuple[plt.Figure, plt.Axes, plt.Axes, plt.Axes]: """Plot cleaning-method ranks over time by country.""" country_count = len(demand.columns) @@ -216,9 +250,25 @@ def _plot_cleaning_background( start = demand.index[0] end = demand.index[-1] + time_step - figure_height = max(6.0, country_count * 0.3) + figure = plt.figure(figsize=FIGURE_SIZE_INCHES) + + grid = figure.add_gridspec( + nrows=2, + ncols=2, + width_ratios=(3.9, 2.5), + height_ratios=(8.6, 1.25), + left=0.09, + right=0.95, + bottom=0.06, + top=0.89, + wspace=0.04, + hspace=0.12, + ) - figure, axis = plt.subplots(figsize=(16, figure_height), constrained_layout=True) + axis = figure.add_subplot(grid[0, 0]) + summary_axis = figure.add_subplot(grid[0, 1], sharey=axis) + legend_axis = figure.add_subplot(grid[1, :]) + legend_axis.axis("off") axis.imshow( background, @@ -238,30 +288,68 @@ def _plot_cleaning_background( row_centres = np.arange(country_count) axis.set_yticks(row_centres) - axis.set_yticklabels(demand.columns) + axis.set_yticklabels(demand.columns, fontsize=7) axis.set_xlim(start, end) axis.set_ylim(country_count - 0.5, -0.5) # Light boundaries make individual country strips clear # without obscuring the provenance colours. - axis.set_yticks(np.arange(-0.5, country_count, 1), minor=True) + row_boundaries = np.arange(-0.5, country_count, 1) + axis.set_yticks(row_boundaries, minor=True) axis.grid(axis="y", which="minor", linewidth=0.4, alpha=0.35) - axis.tick_params(axis="y", which="minor", left=False) - axis.set_xlabel("Time") + axis.set_xlabel("Date-Time") axis.set_ylabel("Country") - date_locator = mdates.AutoDateLocator(minticks=4, maxticks=12) + date_locator = mdates.AutoDateLocator(minticks=4, maxticks=8) axis.xaxis.set_major_locator(date_locator) - axis.xaxis.set_major_formatter(mdates.ConciseDateFormatter(date_locator)) + axis.xaxis.set_major_formatter( + mdates.ConciseDateFormatter( + date_locator, + show_offset=False, + ) + ) + axis.tick_params(axis="x", labelsize=7) + + summary_axis.set_xlim(0, 1) + summary_axis.tick_params( + axis="both", + which="both", + left=False, + bottom=False, + labelleft=False, + labelbottom=False, + ) + + for spine in summary_axis.spines.values(): + spine.set_visible(False) + + for boundary in row_boundaries: + summary_axis.axhline( + boundary, + linewidth=0.4, + alpha=0.35, + color="0.5", + zorder=0, + ) + + summary_axis.axvline( + 0.0, + linewidth=0.6, + color="0.7", + ) - axis.set_title("Electricity demand and cleaning provenance") + figure.suptitle( + "Electricity demand and cleaning provenance", + fontsize=11, + y=0.965, + ) - return figure, axis + return figure, axis, summary_axis, legend_axis def _add_normalised_demand_traces( @@ -270,21 +358,17 @@ def _add_normalised_demand_traces( demand: pd.DataFrame, half_height: float = 0.35, quantile: float = 0.99, -) -> dict[str, float]: - """Overlay mean-normalised hourly demand traces.""" - mean_load_gw: dict[str, float] = {} - +) -> None: + """Overlay mean-normalised demand traces.""" for row_index, country in enumerate(demand.columns): series = demand[country].astype(float) mean_load = series.mean(skipna=True) - mean_load_gw[country] = mean_load / 1000 if pd.isna(mean_load) or mean_load == 0: continue relative = (series / mean_load) - 1 - scale = relative.abs().quantile(quantile) if pd.isna(scale) or scale == 0: @@ -297,45 +381,308 @@ def _add_normalised_demand_traces( plotted_y = row_index - scaled * half_height axis.plot( - series.index, plotted_y, color="black", linewidth=0.6, alpha=0.9, zorder=3 + series.index, + plotted_y, + color="black", + linewidth=0.55, + alpha=0.9, + zorder=3, + ) + + +def _build_country_summary( + *, + demand: pd.DataFrame, + basic_cleaning_method: pd.DataFrame, + cleaning_method: pd.DataFrame, + gap_filling_config: dict[str, Any], +) -> pd.DataFrame: + """Summarise load and completion at each cleaning stage.""" + summary = pd.DataFrame(index=demand.columns) + + summary.index.name = "country" + + summary["mean_load_gw"] = ( + demand.mean(axis=0, skipna=True) / 1000 + ) + + raw_present = basic_cleaning_method.apply( + lambda column: column.str.startswith( + "observed_", + na=False, ) + ) + + basic_present = ( + basic_cleaning_method.notna() + & basic_cleaning_method.ne("missing") + ) + + final_present = ( + cleaning_method.notna() + & cleaning_method.ne("missing") + ) + + summary["raw_completion"] = raw_present.mean(axis=0) + + mode = gap_filling_config["mode"] + + if mode in {"basic", "advanced"}: + summary["basic_completion"] = ( + basic_present.mean(axis=0) + ) + + if mode == "advanced": + summary["advanced_completion"] = ( + final_present.mean(axis=0) + ) + + summary["final_complete"] = final_present.all(axis=0) - return mean_load_gw + return summary -def _add_mean_load_labels( - *, axis: plt.Axes, mean_load_gw: dict[str, float], countries: pd.Index +def _add_summary_panel( + *, + axis: plt.Axes, + summary: pd.DataFrame, + gap_filling_config: dict[str, Any], ) -> None: - """Annotate country rows with mean load in GW.""" - for row_index, country in enumerate(countries): - mean_value = mean_load_gw[country] + """Add paper-friendly completeness columns beside the timeline.""" + columns: list[tuple[str, str, str]] = [ + ("Mean\n(GW)", "mean_load_gw", "mean"), + ("Raw\n(%)", "raw_completion", "completion"), + ] + + mode = gap_filling_config["mode"] + + if mode in {"basic", "advanced"}: + columns.append( + ("Basic\n(%)", "basic_completion", "completion") + ) - label = "—" if pd.isna(mean_value) else f"{mean_value:.1f}" + if mode == "advanced": + columns.append( + ("Advanced\n(%)", "advanced_completion", "completion") + ) + + columns.append(("Status", "final_complete", "status")) + + x_positions = np.linspace( + 0.08, + 0.92, + len(columns), + ) + for x_position, (header, _, _) in zip( + x_positions, + columns, + strict=True, + ): axis.text( + x_position, 1.01, - row_index, - label, - transform=axis.get_yaxis_transform(), - ha="left", - va="center", - fontsize=8, - clip_on=False, + header, + transform=axis.transAxes, + ha="center", + va="bottom", + fontsize=7, + fontweight="bold", ) - axis.text( - 1.01, - 1.01, - "Mean\n(GW)", - transform=axis.transAxes, - ha="left", - va="bottom", - fontsize=8, + for row_index, (_, row) in enumerate(summary.iterrows()): + for x_position, (_, field, kind) in zip( + x_positions, + columns, + strict=True, + ): + if kind == "mean": + value = row[field] + + label = ( + "—" + if pd.isna(value) + else f"{float(value):.1f}" + ) + + axis.text( + x_position, + row_index, + label, + ha="center", + va="center", + fontsize=7, + ) + + elif kind == "completion": + axis.text( + x_position, + row_index, + _format_completion(row[field]), + ha="center", + va="center", + fontsize=7, + ) + + else: + complete = bool(row[field]) + + axis.text( + x_position, + row_index, + "✓" if complete else "✗", + ha="center", + va="center", + fontsize=9, + fontweight="bold", + color=( + "#2e7d32" + if complete + else "#c62828" + ), + ) + + +def _format_completion(value: float) -> str: + """Format completion without ever rounding incomplete data to 100.0.""" + if pd.isna(value): + return "—" + + value = float(value) + + if value >= 1.0: + return "100.0" + + percentage = np.floor(value * 1000) / 10 + percentage = min(percentage, 99.9) + + return f"{percentage:.1f}" + + +def _write_summary_html( + *, + summary: pd.DataFrame, + output_path: str | Path, + gap_filling_config: dict[str, Any], +) -> None: + """Write a standalone, human-readable HTML completeness table.""" + table = pd.DataFrame( + { + "Country": summary.index, + "Mean load (GW)": [ + "—" if pd.isna(value) else f"{float(value):.1f}" + for value in summary["mean_load_gw"] + ], + "Raw completion": [ + f"{_format_completion(value)}%" + for value in summary["raw_completion"] + ], + } + ) + + mode = gap_filling_config["mode"] + + if mode in {"basic", "advanced"}: + table["Basic completion"] = [ + f"{_format_completion(value)}%" + for value in summary["basic_completion"] + ] + + if mode == "advanced": + table["Advanced completion"] = [ + f"{_format_completion(value)}%" + for value in summary["advanced_completion"] + ] + + table["Status"] = [ + ( + '✓ Complete' + if bool(complete) + else '✗ Incomplete' + ) + for complete in summary["final_complete"] + ] + + output_path = Path(output_path) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + table_html = table.to_html( + index=False, + border=0, + escape=False, + classes="summary-table", + ) + + document = f""" + + + +Electricity-demand cleaning summary + + + +

Electricity-demand cleaning summary

+

+Completion is measured against all expected periods on the configured time grid. +Incomplete percentages are floored to one decimal place, so an incomplete series +can never be displayed as 100.0%. +

+{table_html} + + +""" + + output_path.write_text( + document, + encoding="utf-8", ) def _build_cleaning_method_metadata( - *, source_names: list[str], gap_filling_config: dict[str, Any],source_registry: Mapping[str, Mapping[str, Any]], + *, + source_names: list[str], + gap_filling_config: dict[str, Any], + source_registry: Mapping[str, Mapping[str, Any]], ) -> pd.DataFrame: """Build complete method metadata in configured rank order.""" rows: list[dict[str, Any]] = [] @@ -344,7 +691,7 @@ def _build_cleaning_method_metadata( for source_name in source_names: rows.append( { - "cleaning_method": (f"observed_{source_name}"), + "cleaning_method": f"observed_{source_name}", "cleaning_method_rank": rank, "label": ( "Rank " @@ -354,25 +701,35 @@ def _build_cleaning_method_metadata( "category": "observed", } ) + rank += 1 basic_rules = build_basic_rules(gap_filling_config) advanced_rules = build_advanced_rules(gap_filling_config) - rule_names = [rule["name"] for rule in basic_rules] + rule_names = [ + rule["name"] + for rule in basic_rules + ] if not advanced_rules.empty: - rule_names.extend(advanced_rules["rule_name"].tolist()) + rule_names.extend( + advanced_rules["rule_name"].tolist() + ) for rule_name in rule_names: rows.append( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), + "label": ( + f"Rank {rank}: " + f"{_format_rule_name(rule_name)}" + ), "category": "imputed", } ) + rank += 1 rows.append( @@ -392,7 +749,10 @@ def _format_source_name( source_registry: Mapping[str, Mapping[str, Any]], ) -> str: """Return the configured human-readable source name.""" - metadata = source_registry.get(source_name, {}) + metadata = source_registry.get( + source_name, + {}, + ) return str( metadata.get( @@ -403,4 +763,5 @@ def _format_source_name( def _format_rule_name(name: str) -> str: + """Format one configured rule name for display.""" return name.replace("_", " ").capitalize() diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index f7d010a..47ece6b 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -1,4 +1,4 @@ -"""Plots the diagnostic figure.""" +"""Plots the diagnostic figure and writes its completeness summary.""" import logging import sys @@ -11,9 +11,11 @@ main( demand_path=snakemake.input.demand, + basic_cleaning_method_path=snakemake.input.basic_cleaning_method, cleaning_method_path=snakemake.input.cleaning_method, - cleaning_method_rank_path=(snakemake.input.cleaning_method_rank), + cleaning_method_rank_path=snakemake.input.cleaning_method_rank, output_path=snakemake.output.plot, + summary_output_path=snakemake.output.summary, source_names=snakemake.params.source_names, source_registry=snakemake.params.source_registry, gap_filling_config=snakemake.params.gap_filling, From d0ccd2dd791c946d4024d230a043a4c767b70ea3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:25:12 +0000 Subject: [PATCH 171/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/_plot_timeline.py | 177 ++++++----------------------- 1 file changed, 36 insertions(+), 141 deletions(-) diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 14dd6e9..f6848f4 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -66,28 +66,19 @@ def main( ) logger.info( - "Loaded %s timestamps for %s countries.", - len(demand), - len(demand.columns), + "Loaded %s timestamps for %s countries.", len(demand), len(demand.columns) ) logger.info("Cleaning-method ranks:\n%s", metadata.to_string(index=False)) figure, axis, summary_axis, legend_axis = _plot_cleaning_background( - demand=demand, - background=background, - background_cmap=background_cmap, + demand=demand, background=background, background_cmap=background_cmap ) - _add_normalised_demand_traces( - axis=axis, - demand=demand, - ) + _add_normalised_demand_traces(axis=axis, demand=demand) _add_summary_panel( - axis=summary_axis, - summary=summary, - gap_filling_config=gap_filling_config, + axis=summary_axis, summary=summary, gap_filling_config=gap_filling_config ) legend_handles = _build_legend_handles(metadata, rank_colours) @@ -123,8 +114,7 @@ def main( def _build_legend_handles( - metadata: pd.DataFrame, - rank_colours: dict[int, tuple[float, float, float, float]], + metadata: pd.DataFrame, rank_colours: dict[int, tuple[float, float, float, float]] ) -> list[Patch]: """Create handles for every configured rank.""" handles: list[Patch] = [] @@ -229,10 +219,7 @@ def _encode_rank_background( def _plot_cleaning_background( - *, - demand: pd.DataFrame, - background: np.ndarray, - background_cmap: ListedColormap, + *, demand: pd.DataFrame, background: np.ndarray, background_cmap: ListedColormap ) -> tuple[plt.Figure, plt.Axes, plt.Axes, plt.Axes]: """Plot cleaning-method ranks over time by country.""" country_count = len(demand.columns) @@ -308,10 +295,7 @@ def _plot_cleaning_background( axis.xaxis.set_major_locator(date_locator) axis.xaxis.set_major_formatter( - mdates.ConciseDateFormatter( - date_locator, - show_offset=False, - ) + mdates.ConciseDateFormatter(date_locator, show_offset=False) ) axis.tick_params(axis="x", labelsize=7) @@ -329,25 +313,11 @@ def _plot_cleaning_background( spine.set_visible(False) for boundary in row_boundaries: - summary_axis.axhline( - boundary, - linewidth=0.4, - alpha=0.35, - color="0.5", - zorder=0, - ) + summary_axis.axhline(boundary, linewidth=0.4, alpha=0.35, color="0.5", zorder=0) - summary_axis.axvline( - 0.0, - linewidth=0.6, - color="0.7", - ) + summary_axis.axvline(0.0, linewidth=0.6, color="0.7") - figure.suptitle( - "Electricity demand and cleaning provenance", - fontsize=11, - y=0.965, - ) + figure.suptitle("Electricity demand and cleaning provenance", fontsize=11, y=0.965) return figure, axis, summary_axis, legend_axis @@ -381,12 +351,7 @@ def _add_normalised_demand_traces( plotted_y = row_index - scaled * half_height axis.plot( - series.index, - plotted_y, - color="black", - linewidth=0.55, - alpha=0.9, - zorder=3, + series.index, plotted_y, color="black", linewidth=0.55, alpha=0.9, zorder=3 ) @@ -402,40 +367,25 @@ def _build_country_summary( summary.index.name = "country" - summary["mean_load_gw"] = ( - demand.mean(axis=0, skipna=True) / 1000 - ) + summary["mean_load_gw"] = demand.mean(axis=0, skipna=True) / 1000 raw_present = basic_cleaning_method.apply( - lambda column: column.str.startswith( - "observed_", - na=False, - ) + lambda column: column.str.startswith("observed_", na=False) ) - basic_present = ( - basic_cleaning_method.notna() - & basic_cleaning_method.ne("missing") - ) + basic_present = basic_cleaning_method.notna() & basic_cleaning_method.ne("missing") - final_present = ( - cleaning_method.notna() - & cleaning_method.ne("missing") - ) + final_present = cleaning_method.notna() & cleaning_method.ne("missing") summary["raw_completion"] = raw_present.mean(axis=0) mode = gap_filling_config["mode"] if mode in {"basic", "advanced"}: - summary["basic_completion"] = ( - basic_present.mean(axis=0) - ) + summary["basic_completion"] = basic_present.mean(axis=0) if mode == "advanced": - summary["advanced_completion"] = ( - final_present.mean(axis=0) - ) + summary["advanced_completion"] = final_present.mean(axis=0) summary["final_complete"] = final_present.all(axis=0) @@ -443,10 +393,7 @@ def _build_country_summary( def _add_summary_panel( - *, - axis: plt.Axes, - summary: pd.DataFrame, - gap_filling_config: dict[str, Any], + *, axis: plt.Axes, summary: pd.DataFrame, gap_filling_config: dict[str, Any] ) -> None: """Add paper-friendly completeness columns beside the timeline.""" columns: list[tuple[str, str, str]] = [ @@ -457,28 +404,16 @@ def _add_summary_panel( mode = gap_filling_config["mode"] if mode in {"basic", "advanced"}: - columns.append( - ("Basic\n(%)", "basic_completion", "completion") - ) + columns.append(("Basic\n(%)", "basic_completion", "completion")) if mode == "advanced": - columns.append( - ("Advanced\n(%)", "advanced_completion", "completion") - ) + columns.append(("Advanced\n(%)", "advanced_completion", "completion")) columns.append(("Status", "final_complete", "status")) - x_positions = np.linspace( - 0.08, - 0.92, - len(columns), - ) + x_positions = np.linspace(0.08, 0.92, len(columns)) - for x_position, (header, _, _) in zip( - x_positions, - columns, - strict=True, - ): + for x_position, (header, _, _) in zip(x_positions, columns, strict=True): axis.text( x_position, 1.01, @@ -491,27 +426,14 @@ def _add_summary_panel( ) for row_index, (_, row) in enumerate(summary.iterrows()): - for x_position, (_, field, kind) in zip( - x_positions, - columns, - strict=True, - ): + for x_position, (_, field, kind) in zip(x_positions, columns, strict=True): if kind == "mean": value = row[field] - label = ( - "—" - if pd.isna(value) - else f"{float(value):.1f}" - ) + label = "—" if pd.isna(value) else f"{float(value):.1f}" axis.text( - x_position, - row_index, - label, - ha="center", - va="center", - fontsize=7, + x_position, row_index, label, ha="center", va="center", fontsize=7 ) elif kind == "completion": @@ -535,11 +457,7 @@ def _add_summary_panel( va="center", fontsize=9, fontweight="bold", - color=( - "#2e7d32" - if complete - else "#c62828" - ), + color=("#2e7d32" if complete else "#c62828"), ) @@ -574,8 +492,7 @@ def _write_summary_html( for value in summary["mean_load_gw"] ], "Raw completion": [ - f"{_format_completion(value)}%" - for value in summary["raw_completion"] + f"{_format_completion(value)}%" for value in summary["raw_completion"] ], } ) @@ -584,14 +501,12 @@ def _write_summary_html( if mode in {"basic", "advanced"}: table["Basic completion"] = [ - f"{_format_completion(value)}%" - for value in summary["basic_completion"] + f"{_format_completion(value)}%" for value in summary["basic_completion"] ] if mode == "advanced": table["Advanced completion"] = [ - f"{_format_completion(value)}%" - for value in summary["advanced_completion"] + f"{_format_completion(value)}%" for value in summary["advanced_completion"] ] table["Status"] = [ @@ -605,16 +520,10 @@ def _write_summary_html( output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) table_html = table.to_html( - index=False, - border=0, - escape=False, - classes="summary-table", + index=False, border=0, escape=False, classes="summary-table" ) document = f""" @@ -672,10 +581,7 @@ def _write_summary_html( """ - output_path.write_text( - document, - encoding="utf-8", - ) + output_path.write_text(document, encoding="utf-8") def _build_cleaning_method_metadata( @@ -707,25 +613,17 @@ def _build_cleaning_method_metadata( basic_rules = build_basic_rules(gap_filling_config) advanced_rules = build_advanced_rules(gap_filling_config) - rule_names = [ - rule["name"] - for rule in basic_rules - ] + rule_names = [rule["name"] for rule in basic_rules] if not advanced_rules.empty: - rule_names.extend( - advanced_rules["rule_name"].tolist() - ) + rule_names.extend(advanced_rules["rule_name"].tolist()) for rule_name in rule_names: rows.append( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": ( - f"Rank {rank}: " - f"{_format_rule_name(rule_name)}" - ), + "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), "category": "imputed", } ) @@ -748,10 +646,7 @@ def _format_source_name( source_name: str, source_registry: Mapping[str, Mapping[str, Any]] ) -> str: """Return the configured human-readable source name.""" - metadata = source_registry.get( - source_name, - {}, - ) + metadata = source_registry.get(source_name, {}) return str(metadata.get("display_name", source_name)) From f885a94b9f9deb969a6741fda35d455558669b24 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:29:28 +0200 Subject: [PATCH 172/241] Updated README to better explain how users can use the gap_report to develop advanced data cleaning rules. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b1b4bf9..9f23457 100644 --- a/README.md +++ b/README.md @@ -146,12 +146,12 @@ The workflow retains cleaning provenance alongside national demand so observed v Important diagnostic outputs include: -- **Gap report**: unresolved missing periods after cleaning; +- **Gap report**: in `advanced` mode, provides a complete record of the contiguous gaps that remain after basic cleaning, including the affected country, start and end timestamps, gap duration, and whether the gap reaches a boundary of the requested time series. This report can be used to identify which periods still require attention and to inform the design of targeted advanced rules; - **Cleaning method**: the source or rule responsible for each output value; - **Cleaning-method rank**: numeric ordering used to represent cleaning provenance consistently; -- **Cleaning timeline**: a visual diagnostic of demand and provenance through time. +- **Cleaning timeline and summary**: visual and tabular diagnostics showing demand provenance and completeness through the raw, basic, and advanced cleaning stages. -These diagnostics are intended to make gap handling explicit rather than conceal unresolved data behind automatic imputation. +Together, these diagnostics are intended to make gap handling explicit rather than conceal unresolved data behind automatic imputation. A typical advanced workflow is therefore to run the basic cleaning stage, inspect the gap report to identify any remaining missing periods, and then configure advanced rules for gaps that require explicit reconstruction or replacement. ## Input / output structure From 50b4ff13ebe4a99b520a032a93abea1a61f0d762 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:34:18 +0200 Subject: [PATCH 173/241] Testing a more comprehensive example figure. --- README.md | 2 +- figures/readme_cleaning_timeline.jpg | Bin 0 -> 469322 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 figures/readme_cleaning_timeline.jpg diff --git a/README.md b/README.md index 9f23457..c5b937e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This Modelblocks module prepares regular electricity-demand time series for Euro Demand cleaning is performed with tlean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs.

- +

Example diagnostic showing electricity-demand provenance and basic gap filling for selected countries (May–October 2021). diff --git a/figures/readme_cleaning_timeline.jpg b/figures/readme_cleaning_timeline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6598021b070a4ffb0ca27b9c5b33e19a31242225 GIT binary patch literal 469322 zcmeFZcT`l%mp0shfP#W3L8Ji%$>}1onTW786>Z*NCpYu>>?_JN{#m~u~-$8ffq-3Q)7#J9! zZr}j@oB@f0u(593xPgTYRM^*0FM9N&LU&{{aEbV!`sn%2&Y(xpjEcV#&T>*mp&I>3syI4ul zL21Oh0=AU>>`Dz8ma)RYd~CD!L9Sx>(NT-MZkiW@1X~Y^nQuej`F?Fr6a>b64MP7vAMXO+{krM4qRx2PN#ps*Q+xpREB=2~ynvhofk zVOPVEI;+0a>8a z3(FzBs&Z^>>(q*zdP@tm$`!>(Zw&~2`fokwAEDwYzyBzXrBUEaO>IQR_#eCf@A$@} zn8RCOa{VkJbm}aYz$eeZJLd0Z7}O_rewcNpH*f6$eQ(mPCH{wnq=@xZ+u`vQ456%G z2d*5e;8sT;D@}s1l8$aWpPvQ!H*0O=x#%eVD9Fey&GL^eX9%zbc1z*gUzw}HiP)|+qY0a5#D?$m)|zD zKV*xYzy|y-7XD!pTDE5e9x2Lfg2 z&~@l4Z7`nzZDRbhz@ch9!ICiGg3np2#LCFL?s9zsJ*c420`anm?b0U`njrImzwX54PzpPtF%eHI>RV)|eT=U-|F3X|jTivGdn{E@4> zV(OY&V?HC)Wx!6f^uXF%9I@q}vi0eY1dg6|*;`jD5x&fGCGx_#BLknJI_aK)W;wN7 z`{XlnPoUsV| zEryNa3uo|hHwvtQho>o2q1rqyrAh%u{(<7v&TWzznKEg;z`RKncRuY^@K4ao!fDu5 zV3DO&?SE0d@~lroH`M2y2+Th%klXH)EUGwV+?t&cB7!nAqHw zd1Y@py1z-EA0ho;9FpQ0!}dBp@Jl|y)wI>X;kfsvr*|*VU!GRnE*59G+xtcM$8~FW z6KMYG;~4z(?}wjO5Y~PAGjuHu$OyPaL9}AXwF002K}K!4C-w=En^X^Rn)OJl00Lix zo3e~Su!a0<6WuzJ7atu??DS2^`mS>~9JVmdV_AU`OJC9^Xg}@2HHO(MMXvk=F{r2< zNCiH#_l4_ zQ|)2^H;P>^>`$UUc#te*8BgLXTKDXQ2|-bjRHsw05?ihFF*T>7x~Xg|?iBymKvuY` z-#SThHPP2u)aT>o-UG_)-FUf#W!yGn%}Z7`FZ0E?2Tmb1Di78t*s;ICL+%Ixrcxu6 zIiFt!GAJSJ2f_@~R}Xib%NmTaN*}fY6q5TrLrgrcn~-BiueiP+USsY zl_3BhO%~k;HGaQ=!oU*BalCk0$^Guc;`MA;4A?);b>qCV#S@1s()$R{oqXpfs4agv zBFEa-$zN*)Piky#$N|w3MepdX-x08^)zGAk9RCS=n^&{Mf?wAouVX>rS~E;fpjt2? z1TmP<1-~4x04F&_u=L$5JLf9bjMsao%T~DN819o>4BOTpC3OsvfHS2$g<5o$&QApy z@UD|aBgJi|N^u|0uIc78c8r<8UFs$8w%*sti|FqfCJT`gEiD$1RCO6};~($RKtmXs zJ-;srJH5}!4be3WY-i*a<~xjBmlZ2xuqjUsZ4y^b*n73}Y6nl~J8V-gf4H-eZ~_1U zlAuWJ%OMOtblE&o@opd#1%Z2Yb{Nr=6rWS}UH9@{fYpg;sp(oeU>tb|Y7?2L88)M-_ z6jXccc^bhz43Q50xFz`*|)LG(AAi>ZiIdqjZkBp!cPM+fX)f7`CB58erK5MT9?orIIUy9{@!^y7Avn!_ zwaPC*IA8+Tu{V(J5?-6Xabw(9QAVCOH*&PTxv-VOF=%4r>!D7zQ?nq{$iIXj@$P!~ zY{LVt{)V3*!NtaAOEF}?4fP?6K3f;%tO)XvCYC78G6Hwpi5~Qm=iqjw$|-23Mx0{xaBJ-AF0;aKK4^*$ltEP=k3MyK=bqQh+SAvArDP zvOWyX>U?>(x+}{#p}ya>_ti#PDzq+Mc=s z#YfnaIo*+2w=>Id=2rrg`*3v`zC;(~XxrDTQmvKo06V)kuioj^;bES8_iMyc_BRLm zP-)c8iPQ6IO@*1I8!O*-vf>TcGHg(5q0Y+KAS(1;%8?%M9Y?f|$pcqhREmsFY!2{4 z6IR5@VK7ez?mRQLPr$f-O*OvO-_vXPhm+7j%CSi5^yuUa1Xp0YTpHXpAq}2y8SR#A z#L&E`x{OOt8GjnxV8(bzdO_4>TN`{YUvcvc$+xT9?T`*F4MqyBCn~{KdS9;!AC<(! zcRLMaNB6ztg?ony_Xt zl*lI6ocgQd*p9foH(fpaYjfaC0qHhKDw@4RPgl{l}Te%qhoQD--2OIQ15d#7^D%IW%V zFI!NU+J~CMr{BIzIq)CHTiL%8XahfYUGntKhaba5w(4H+Wx{jo2+P%h_uoZpG~V0k z<~`h;=REL{6>4dfvt=}{@hP033xB|iKGShVuH{znNZg@Qx2h@q0y3t)i<44=%Kv@6 zF68DW@pLE5<*Arg{*^(U5-g~!BlU;*;Gj}nRfjNqAiu`}p=%N8;qqSJXu&O)b;Y2M zU8OFxR2AW>{UeAqJ>k^XEXZoX(3YO{*0$av z8HNA}ykAY=D|TWlmNHEFwWQqVIL{U4ZdDIuzI6Z#l=uDglu16Hn>`<@|^u}NL1u9Tc zi->}l{(?omSv!B5PFz-$SY$Ja?Mo7?!8M5kZVA&Cq5=+*rg#i|x^kWx(=RToFAjIJ zyMUwLUBdp$hv3=ihtPx1_je2}?L7p6Lzw%t!$59>Sp_CmU&YO$KZR{Pu(l5zUAonH zdCmPA%cftLEj7F=->(y1yVFvwos;>cm?e$;G}Wwcb4{m+RMH}zESZMxai4PYhW&P* z|M8>$rN-}P#n5F9X?>Tg;SXhz=X}26P4+fnzSKv6VE#!p+3ndZ%a#w(9dY`l2ts}T zm(VL{=ECU4*zCKv``xwhdjBLVgasKp(pjX`%!^NW9nByLb>jA%D5mZMltM@VM-6 z1C(gCH4uo(Q-jVCR$zR%ec!FT`%pL$1R}cN9(ddL7E7;r{gZWkNwRG>=!Vz32OpP; z&y?j3&$BFeR6nl*Dq{aouY?VA;?Kmv7Mo+?Bx}ujr@#4`i+62hY;&1h?W_(iNjYO> zA>?F0;EqxlgKTE(q>ihc@;jz%b(d-he=(!TBaloexJoskwitup7pR4x21?yl53WdM z^0ZHJ>Qcn@>D^Q|g{-xfd`|AbV!V#8Oyb;q5D+^b1dU@$M2uUOWpa#%5>cFfgjmw6a?w&k}QoIwB7&0OD-Za$WV-p zvqEbEsmZf={bu+hfwjRlcUD;9JPSbpWHU=Lj*vqaTke#pR>N+8#O&0+l`A#~)at!s{OG14@ePb@pD{5uj zGTaiGN^j*#iU`~?oZ7GUV?=*pYaCV|L<3Pe6p3c?&mc+ zwAo8&thUWT!d&KR=X{!@CT+;dq5PLey%0F(|Gi@we~YeIR)phIZdGHGA+&CIRCpB1+JG;i#+9_)k}?v9+`& z>nKBQM{w*PC~!rhB5LClWhW2Bd@aZaK6W`QGE^OJf2e_PN)^BIfR_a&F3%k_-%s^I z($ngTEh&bmkH;K(TgM9duEma@E?*4ZO07{iq$cR2?spCDRS)TWF1W5wa;~Fqyj}DH z&ABvI8J9F$-d+OZH!16ch3#8Yf2=&;Ef4s|jresVBrJBFKS8nCp2NJ*G}0>C>=Rq@ zZd+o+T>2x}hn-@`k>G5)z~rZ^?Mo*wCut*;5Q{;@kW?4-wMtgCd;UbRww_T1txmH8 z5Ol8)V!7sJOT3%|A({$>I-fw8n9Rf4yw1xep^7>#pn5+L$tFRtZqH$Z_2lM#Q<6@E z18@(ItzcDJ9=c0>+<%Y(KA3p=z>ob&QT_UHBJs+ab@}jTb!}Y~Y;WAw!@a|FwXQDA zMmi2t-49P_mRkbTJI^{|hEW-~T@%@)$`X2c;Bof|=Cbfxj|&bP5A|YsGJDW>URshk zXQ+?A(bU2I?>Fm=qNWyCD1HDg8>YWcaQx_zQ9bGr`*W^I559#IM1Fl{wV5$MPiQ=j zd;h;^0GGcmc>6|bbv-CZ2y~inq^1p>PeI3L{S$<%ma<4jgGLEcQudu7Lr_uD8t+%N z2;Ql@KWhEDZq5YNK%MT5e z=%Snw;7YKjw3vI=;7o_t1&&KVbTZH7`JNv%@}iiCvPC<9UAq-MDcjSZ?CKH&;IL_R5)i48(#yk!BW8ncxGeW z?*p5oB>qh8IdYrqmUz7K)FCk=yl$b(*;C^2fH0K3Ud({I*JH3n#k;jH73s6vPihBsv|I7d$>aRdJlbbhVe6AxsyN)T-70Z*9utYoOgWU z$@7??aB9WzUmE*CWF^jshz!D8nqpfP>o-^O>As?ZIU47)7wsX%zwsvxoD{4Dsef}K z*!NDAX^!co@ab=gdsr`OErUIeG{G$RaGLyzdQ#8h!-d@l=`^%gOVz{eMvRveGgTyn z@VJ|5LN%YG`seBCKTUXhcyZYg71SSuRP@9qNPcB(8C;L@Sw0gyTr)rGSl?{h9UNa9 ziJRcQHz@L+Dhl3`xZ2#u)m0%EWF-Z4~R4XT{-RRNmCAF<)@q7KJ{pWn8bKXbUIsrbYunA3j(m=0_E?$HMZkN(UY;6ud_^%!gZw(|^s;lf9}kXdF=UQR5|MB|>_ zz*^rXiy12Mes^!~8nZ7hpKj4%^qE#zru(`~?GU~x`~GUj(l+Tqrxa&Y zn5hZ*zuo}euIqAhiur|=sv$Bbe-znV<*!icpzq%mieuHic>tE{bSi@@u8+_9%<1~n z^VUyhL*Pl~pg|=6j~19=Q@+? zo_a;EH(SB^7*pQMq%`OiVM^UOgHk7S3BxU)Hrc&KnZq}ZP5ZL6C6|Ya*%`|#Rc50* z5mZj^b#7?86j+(jKi-!kI$=JcxhhiJTUWi*G~LjKJV_d%-H2&~Ox@oAsSh_=v44u_0orkCm)I9C1D@iR}aaQ8&dy072;v13$nnnAsrs zrkAhbcD+PRf5_@(#=G!ZylOHju)lbvCBSJkXTS(c^D7;NcG4RhTl^W0B)xm?=;ofC0+Xe zjxcIQ(G5YdYsyHK9_K6AT$3a{s%$sy-#RGQkVk6E{u?JE(;a>yf#ena0}i;>m3W=9 zpo&IuO&0#Ft5xzj(s|gRx#7CW0SdW3n|fHaq*s`=eayrfYUj>Ktw5 z2H178STjV&UMqH@)cby9n$le#q?@SB)HW`C*OVatJi=dL#pn18mg;E|{#Wlri|pi; z?cwD5E6kGkZ%z=xLH#iZSB)|M97;OpLH6mAybqpVOnX6M^!v#w;&t738Os`;@!~O^ zC+}&KJiR}wEawyjta_9;N@_OK8T;hQMA*gOk3XMiQ4((++Djd^UfOD}Vk0)39=_mn z8o>!{f*VuMysKUo+QI2Hc#~hB|B-2e@@b99_#~uDIiLct<5VUX12z})lICr6ez{H` zf&m{W|Ga`-)Ju6iYLoOS&Jy(_5N4)*VWL<1Ua`Hc9LYQbjN%gr#B!eF@+yquk1D&y z5uiWfFkeJ4Ee)^Uc|)>XTCmf3J5V~is}*;2kw4bU^qSS+d1~>+Yt_T;$nEr2+-iMg zd+If%@%3zSg3IEp8|eNuK-OgL0oXT5Z`p;vw$Xo?Uw0qgNjk={o^M3c^s03fY?< z@*;izae+xd>7yU0V21fd!WZA5j>7aN+m+BgpbI4aN@kS!L(B6&$YCb22r@^*KzPaw zn|Cb)53}H0B~zIM)(P3Y;7;?aSX2Y0gd>|z$ARN5_Vg1+3dK6efRBgeiU-1Td6_~; zcWZTIf>Y|Og+P~l%123=$Mng4D4!9wVoO(q8tSgFGRzxl(HaB(iWc_23isxJvS@KdJ~Ze zH@GwHgcN5QIZ=|GHdzTh1N3zr5PSJ+bn1d=ItGBk5c%jwULgF|3nF&F>mONWPks z3WRV1uNJ6qI>0{KxRI%e=FlF1iZDF`sDBr3lb&S_m1hODKrHe6g@@<_!Q0{~Wl<2; zt)!xtY{}(LwE3$mQ%Y6a^pm!NM<9$+8_TZIzLSZ^{m!i|CiMEl6oG(;j)AAh^b>?V zpmEM-<()bO@Gm&tmLW{z8SCWoKX%!AosPH*qV`XKbQv?RI`2ca`$ZSMTnKgPH2QRa z)4VOE_hR<@=DkbhDoWztnpkchT0AvKoHG1(W`~@S{j*I{bz!e4&1Tj=)O6v~CmVGc zdefX{!5Ai-fV%dlpyRH4%7&`pMN0w8#C;-kfuz54vl4ybkuUIwCs3}IV*{~{H;M3v|ey8`#52h6jeW@NyKmr zQoTH8nQ&2b70HZ9Jr-A1u&dQ}Ox<`n+GFKErxf z-=3uZG&nnHX@d&fNUgDldI0ru&`k>PgKFrDA2`4Cv2A!+$iG9!j4~k!Wtc3poav%F z-$>5o?OA!$x;g9<8s_w*f>SZ33VbS})~{+>jM>=8%v4iaw&$qL9*$}0Nxf&gj*6ry z8lSl}z{dARe$C_FZ(i(eD{#lVqUa-3Gd(-Ns}Kt;%VKPoegCjrxlO`htDgca#OU3> zEf$nGe$s5JGhv<(B>x0@z8ksxaAH9!E_Ie!UP|>ZK&U?~eTO(v(LB_;xE2Yfi_$TBM^58?{l(2=xYs7rOd$hH*&5(BF&`wkuK|Si zGgQS0FSaQ4Lvvs~el6#fuHq0adHGPlqtW8ONZ|Od5<<3nrR~_|6`%YqFwDshFuKN!ZzcaP<{|d)zk=< zl1%NI2rI)zDX&YY#d3}`x`$HNGd0QIIlZA z8czYdto^2(n~CxP%vR|G#Kk8Tl0T>jM)&bQ&T+wMG_S(|B;ONc;B<<>0U3jpPX9nwKIX8tC5l|b#UI`HZ2;-Mq|A3BVvaS8`a z0fb1TkzT*@f1bAYg!z~~)J+S6c>gET><=n$7~-1f3Jm->w#a)G6zWi;AD0)zk6F`K zBSvss;8Q0Zh4$Gr_!_O$tEjb``ZNNiWh8ssG1O^Y8&~NAE+%yl@6_rdiW?VVwR*F4 z>4+@cP(zVtoes+W4KhF6sEbZ~S&w7#inyef`f=J(x5|sg2=Wb$NNGiu0q^+iXw;Bzd zj^kD=D(~0=ffzehv?ka#SkHM2u1GUL7}8O!8}K*Y;!>m?IwK%ZcEUSda$DHuE5-sd zHUOm?w>8`p{eHuRA=j`LH@SY9>r?Xed){z`bnh~6^Fk!*Tc zbF85UjKb9)fml~+DGz!g@+N&u<92zu4hhOOEJqJc42Dy1SNYa&w!ab3hXuJ$J`8fs>NQZ3CMylAg}7B@!IR| z`JrU#TU}^ZjPsbmp)~6BVoLTrHP`LE057vVnN{|L$klt44SJVjbQ7f(EAy?xmAEUY zH7zc9xAXL{Y?oXAii#9-?nuRTNz^h=%( zI#5y$oXB6Kdnc^iFi1Vs1Bf)5U*|}kKO;j4UvT`nK=S&kza&T} z1VTfQl#s!ezJs^Fghgf*|!QaFMum%0>w zyDwAu*(8!vCu=jvXmT+cS2qMz5v17BAMmX&`4Rzxg>EB4I8wSu8?wG?!uq91@mues z1x)RqeuaWfGvFp&X9nfkzUQ{#rbv=m``VZR z0zG0_!s{GRLfU(`^(VV3>3#*QKm@IJJg?K!HPSU;k?k=m-vGR+bC6)Ou9Af!+iYaI zw$syVrH{8tjy9hDqx3N-<*PONg$Vc*Dy(cnz9PSmgqDXZmglq~#U30jOFW^ReNiC$Eq$VV``s16)c%r%PnEix+cw z=($}SBO<~+-+MX>xh% zY~SZK1u+2)m|i*KHuDJN^fHBo+H($bQTJ`T>X$iZ9jR-0Et#;Gr@JJDkwb*9Eln4lIWuvgIwWZ1IMmI&da z^?S2A5}O3F4s3E>!&Q3zWeMI^ueY_`oeDey3)GPj=}83nxkxVo`az0W=t%uK6Sh_$ z)O8;6RX{h+i_*ACJ{*dD=y#f(<$oPGBML|kx8j@a%OMv9Vfq=2u{my_4m*Ms(ZC7= z)BllfY%^A71}%PcCa|b@E`N|2R$t%zFdATQUG zv2S^PW%|)~b+;(2e&CyNgNnvHUh)b%%-+LyxUXp=I}YjukyW-}vHm*M+qA)%i(x+U zINIf2eCw;&pP;nHyRf)+a@7-yHGg!CGI(NV{}D{GQKpQ+HlOD-+dFjznSNVElhK&m z;+#cy{DP?a;JCT!eHl@QkxgZ4>t@*^gnB}KmVYMmRc!1JvHDZ5WQa6^WownCt4$7F zKS`iHXUrgfy&eNHt$Ud&W7?CP!ho0Yf0YI#scJWN&v+1tegv$oWCK!GbDN2tm>muu z?q+~+xM-zn^`fG3^V1btYtVD+Lr;m9=j2upX= zaPR0$c={_>R?p!NKZ7@!HU6)!!^j@So{@itlNrC9j;_QSdwc&KaW;K0??cVn%Fxb` z_d5c;*V(hMhrZwxzT&L>kIA3ErUk7PZ?b!{d*19jpEhJDb8G5k-^*@2`oe=;*kPH` z>J9rwE!mccP9ON#ewP=Nm%x#|vT0((he2}~H9ky>H_hD~J0V=R&?4V5553QC@?{_) z{Y?8YPfJAn%R_N+-np(0_xtc!kB&kq{{p+vB5I)(H9{R3+88~pTVWjsg&6j z>nIsbK;~tH07i8j0ZZ;#M9|d2y5rcR#9KfhT%q~lcc#$1VetTV8>(W0|5ryW(_i=Z0tv$_ z404a3bsrwTKpr?@$c?pz*n|h^>pa!IThuQ@81eLGxck2HSTo*F(CnIDK(h&Rv?`Od z?B#NRi*3cya2Yw@od_!iCGQB=XDyE^`)?Z1FWbINc@=Ap8b7q@9W_B|I(WM=HVO|T zkD+d;5k8E*Z4D#Y^CrBws$q(B8^iDSTM zW=&0hw4JV3Y1vdTM@DBsZZ#eh5?3P?6gPtNAxIQnwD@iYhJg2}7AUTvOEP;;4MA1o ze{JuvANR7ecOh->-5t{L3Ci$>)uPf`XWU}H`VMSfk{E1? z$%ye8*~Xcehpquys!Ow!l4~clQcu{)GG89>1KwDlCif(iG2ah}nE#c)hEtk9AVB&P zBz@Ee$p0?VhwxI!{Dc@$s|r@EgZ;4HEUGjmcBPSwc`9%4!176emFimXL3^@#+_VmO zU}%Lv{cz$~Np0Ot$*IXIjwG8&<)EirZTwq-ig3f7K(O=I69VbeNyk+E4%cE!bVO9| zX$B}XHg4uSEZxH^!34#vv$8Xszs`@EH?wnZVL2SDV-s{3<6!|$jc|hZhq+Qm=dx2* z?g{BKb(uCusm7UZWJhcsGwCXmbWI(zfP*%(&HTYZDCkh>L&5o9+U&f4v?KruIDznb4lZ0lWmBP{(e<$}z_2Ah?cxkYH z2YfAycLe60JbSx%E5OF9J31m_#Fz1Hckp3A^hEhJY#sl7K~H}Oke>zkZ6%LfyG98tOG1bLfcvQSrqbWt!mM zrI><+&@L-ssyke6nR~oJ`oT$!V2U0-Cp>>Q-~d;xnrUS9$6U)RZ_cZBO*?2N`_m+Qz_RDEBc{g?|4PIlcW4};r0ptF?0UL>?ex0%|i8_WOOQQ2YQ=XpD*uRXB{zA z1jI1lX8tAV!e%?tQEWc-rn^@-&GCLMY`#(5l>UKxMjxpoUO4UBFoCoK%{G-Rw+ z@3*hvI>L0I`ZQZ*g&L7do}G5Lx;VKo~BU6&B3G> zcc@>HK-^#WGfaLRkG(rh`bIITJVbeHNPH=k1VNFJWuy1yNvRQj%_AD8V&mKz6XMj9 z{abIpFi(@c5s$3BSpfF$hkT)WSFA#B@y=jz{BcWeho{sWqsq8L=17`L4TZ{Hr4np^%H?BN=D2V@)$r)vL70W9qd`pWt3zu4j0r=pCspmY ze2qHthWlEOqo>_;@Sx-VC4#o1o~M*CNDERI0&j|&`n(^6$$o;|QTb-=-OB{EsA$O! zg()_D*tU4yn#v|hF{AyGjGr;nb}|PZ%F``vIJ25|Ew3D;b-vtap$)K%be;5fm9{hj zetc8YXH8A9+?YC-Dwu46xNW3+<;kqYC7Zq&vKso_H~x|E&m_XRjYg$#Z)!(a$2$W{ zHTP|;xE^F~=wP+tVmoa=+7JYi-Wuh_XTN#J@iZT!RHyb;-6Igu0kQECylO#+WpFh( zbxavQ@jqoCVFpL<&{&-=;Bv&mb{@}Vzy-JTQ#Hvf0wJN<@Ym})C?RLUs(8H zbd?%tkab18Mv%cjmv0*gu|T-$tr)yCFkJfKoZ0i^0tW_HI|(xH zPr+ranL9{rXtzZ}zWG*mf~#<#JDSlH1M5?&(B2FQT(;$jF$=$;@<2 zBdt+F)+@quga$;*$z{sW&j}BmEO`5!cx<$Zv~Ll+U>cPyR0qCc-|@cTjDmJLB~Tg!$F(JMk)tytpLI19lEGLupY4h@z%KOnLCk`Cl2{@Bp<^4Os^g)u7F|W2Nf0F2LokSSX z8lAsS@zLC`@>P85E3Qa~-C;eyLbe|dN1aN{^!Hn=z0y%_-h4#M;P9BqCe|M{V~hUNPM zaZX;q#MA2fNF{zg{|(J`jTwUrxrtGNVM(%Ezkq+r{~^ zDDo61qrSDnp|1lmLpXx|ngbR2Arhiy$#-XT7YXDo#k~80IE&2G@bjK8wqwPw{ZHjYD(g`!F zfl9kLY`6V(zYVZI57+@a^}ozC`gQuB2gMEXeb=E>N~sAF==IyM`%oPdbGf^^Pu8dq4EAwn&WvbM+G4MRDA$XuP;XZm zZ@aZSC5dhJ$*c3y#)BP>lqn3flhBmtCFl=fZDN;@I5Kjli<~okt|45Nm?afl1K7FP zWWf$8E_4JMYpb8*gns#2`_b$0o`z3ry0h)0#k~&SVv0R`d5|qjX^23~H&jk?0nf|N z4eljRzg-aN7lzmDGMx$VDnSmMuhZP}b&5thq6zhEUDw>cVE z^5tfD_XThhrFC@@w=n;iB6#NVZaIt1A=~en1VuhROKl|61jmci81YvXTZ7w%njnWC zmLZz+T66qcL7GR)7r-{6CG^de3PeJCsPQ(`f(E^jl27R(;veHVB@7obT%(D~9+t!+ zlK}S;R&RpMCY+QETRomr-ZxyeJ~2UD9l5th8*aHNN$*;o^OqqaL|2sF6sl;7yiIG| z2A{)HApu>oGZ4x6HArW=+I14sE3iMZEC;6f3<+~p0oU#3^`|=8=XrxiabN7MRwZEc zEj1Ewg?YKyFi<%)$HYT_f~LC68tkU}EOw0FjU1W&PBOY-6Zv*3miWRq<2q)|lENj% z(Bpt52&`Cj2Xi) z`s9o2y;A-N_nuQ^bfI=w8E&yJy(!gt3y`8&rp!Uj%_g4XIj|CXO=Haw`WP4BN=`0P zf~%Xpn7^9n)12U{CB?>jU0&qEHR1o&`+1Z10o7Y2K)b`A#BGZW$3!@ZIcJswJ7>>N zQuTZ+^(k?eHga@g!MKvAiPn@RNTUWPI;@eaZCp;buEEF1x?S?4ZChLa@o?fbq~?*c za+>cA=VMpHWH+IMdO3j9eP+zDt#>0-s#Gu6&E5v^5P$LDQGsYun|<}VfA(M%$3O9@ zcv=E@X!_A%zghC4$|oVTqE*7Qw@gjze}}|b=z=N2;?=iMAHu$b{zITYF_pTYv7q<^ z6h;ra!!pmjdWYn!J{{lg_C?U6W$LU)1S}}Safrk>c&(PYU_cY;+`8V$%ATnGmPE40 zNqu`NRAvp2l0gr;=Mi%udz+46Wy_B$doo1lPk0H7$L6E$ zXsL$}xAJ?)Pnj~jlteG@3&dqsltp)HkbZ*^uTYaWXGa_QpG>?jW}wFd_Nj5@Nxs1G zqEPL4=@dre^B_Xlr|u5b=|F?K@2@yj{w%Ewei&(+YU`M-ACOz5XT|W@7-iWt$CjmbTQv>QtuH9fd3f>Wysa_WO-OAOBbo(_XJf)*-u?fba)hO7 zA44g?z%dSmH+$G1)aiP6QKfa-EL>?5eL}5aD+2N~dDCn_HT!wzp^vp8ORX)axv}z5>S_Gy` z7tcJ0oo{MW0Yz>B@Q*y~f*_$#uJk2_($ovlO}OlVi>pY*{q6^!7qihXzizAS>lKK{ z8z2ynJ(1qe}6;WcK)BcM49!~WyLgE&@QaOk#hEAXELsJAX zXYN%3;9^Vlh)20w)zQck6J!bqUDIWfva+{o(|}*Uj+|QuK8L=l2+)GrTsg40>|4&M9!NL?`U}@U*y`mH6S#sd|r>IpegfN?X|%>GNFDo z-mS#GP}3o6eAr}OQ~Lxec?K|vhNxb7q-f)VH`y} zNA>)?yKlcS-n+NE-|aEp>spK@wY26gs%Fjk&96=aKP90pqvwCo zj&9m!OZ{0d=Ut)Z_z2!F>zw@|ckZ|}H&0fVIc_tMoHC^2*})B;?wFwhV3W;G5xBdN z8k?9J`TjtTo-%&Beb~DI9B7_#FN?U`yGtfupiwyycws5*ITGUL3&x1w&Q()(J zf}&0^^}?wTBbvCJihsd-zjD%XK6+V71ZdIkv^}$|c=OCqveoPyObu!uhnNw^wj2De zz9cht@I>nnA3%m~rY6Z&rcjS76)v2D=`v`=R{hN&!PCEEq>9Famzw1{jHR{e9bMQn zXeg@Uw5rmtb6UK(=2#L{x6X1#9T7XjQ_S#ed_s@$QpPNeB6Gej8gyCJh{PQ}d$x&Q zN)cDO4T&nRY^NYD>?I?cRa}{vZ;L4*pKq!ym9flfHy)NT4NrF&+Cqy~qMNW-2@o%3tjdkqZ+)LX$=c8$Va zc8em#;hP&?aZg!x2^dV=z+6OL_L{=)7H+J@?3XzP6jUoO5YB5HKgOZ71R7YcU#Jk% za*Fb3=YC$(K+E6b?F`OFetvgs`S}Kw^ z(g2Nn9=o~h_$LDbd54Nu)>SN^2|O(%6Y!8<4PJ+1y|jx4U`DcyvywbAcgm~{1#oBa z&5;`bj5`I3w^a6%?cd@9Hh)_0O#H23uKP#e5b9N?ib=#*2L$J??&}1DOA1g=r45hh z9Ca=uYM0*#9WlM+o43=%f~%X5Y`yQy&OuQ>fV&W0dR3rH*SoyS1`gi&A5XBZV@CS^ z-j6?&W{~WBDd8Yt`QZ6G>f=9U_4`YK1l6j)M|#QtC6?}ck?yS1^kn1`wf2#O#q2o` zuQV3MX@Imn{#fwS*c*YqGQ|o!%5a^UuLk9f4Ds-@hlOpCK70qiK$^L88b}&@#`SXC zRf-kprTvjV3b$J}DLtHR;!WJcZyXyL!RVhd5ag}O4r={MGq5wXmWf@g+ylJ@c?58B zmCXl~Z_}1x`>As(B39=ja*`J5w{L z4>cj`wo&tq66qb*m&N=m=I>vtI7lFw35um5;2}EIJ?eUp4SL1ZEBCTSu|BP1?ii+`@v9^7D-;fXgRnC3T!(9FCkI4Er8t0I zNHxn2m8%Ov*Ctb%jNsDh0=8|Ac&0vrPYHkI2Sm!&3Ur1I6WNQuStR)FxCSxbzy!W4 zh~jUZ38r2;Ad1&Z#AJ8|@Mg~l9Em#)Qr5-n-nRm%ZuhYjwII;!655@_!f!xPd|?>L zs;{7=FNn_My;D>WJIlNb?l`0f(a9+W(BUEbDy6FwvQB`? zumfYxR}Q0(*`-60@3OowcmIajvy2y)(C0PY04U?X13cm}&F)*cx~rLQ|G`p0%vu|P z$r?Py)w9j>S0D)CF3cxJ1ibMa|4$SDmd=9UuQCH5o&qTp^OaCCI4S#q`;8M`PX*Is z%Etz;KeDkjDa^byC~nl_R^)e<=ZfA!gB;`b8G{c z306qLQ-+yD3fcP0lgp4XUBqfoSL)i)_`rf1iu}FA6r-0*&qC>h{10-_baz#yUjz5&l6a|`1SfzK z__HivmLJV54`lx(Gnj_)?h#|FrF?X)b$Ak!T2X@SU!YvK?s3okg)xsty-LjU=V#2e zMeNabKYCsQMV^yOQKrWR>GY!yT9NF@6HUx4 z{SAK*e3{ljI0ncpEo1>|Yjz3Wo$GFp$J-YK?OL7Z7W1SBH+!UAly=kpr~1QKUSFI?&zFKV+VS2QzoCEWAvx*&ATqb_#BvjS^dsIw&@j zG{dWasoctv=>W*!J=62b)!f3Br`MCxh-MbYzUV*cEdJd*aDTJM{Z%i3KBgPbx5n(? zGs=b7x90o#>Wh~wGp76K9UmKhz4Lw>jZWLF5kKO`Lar09o%^u2y<2f0>M!rds~s7#XE^p~HCDZCiE} zitr&zhNu0W^kfAxUCz!M%zvMK=P0T80e&n|((35x<+{v^IQ!1c2?mFnNupAyNh$&K zx9`6R3U+zDOOo*)GOdCiUwx}Zb?zhb9G14p8HH)se|6dZ!}{Vc7c^XA1HoiQ?!vH& z!ZvrrAP6K^#Wh%Gapda8tGvqcv;LVj8wAG{X{@?9F_Nv3$NUrFNQ!nJfDgIuktX9e z230gfb9fm|3~F`(Q5j!N4#G`!DE=X-!WC?9Yu}Gdj{$@){JyjAkZa$w?Rhs}*_Sc5 z$$NhyN-6U~jT})e8W)Fa01rAm)26bI&kLa9&LI4anTd|N- zNGX*A=)eJxsO1o(>+u%z;o)CFh5!UnM@@8N*FW3Z$IBhV%Z+~t$-CU}@-q0~z5Op3 zqQ7JH{uWC~QZKHYoibVEj8L=q?yBfz%|szP!AYlPd?C(RhM<#TFb76kC;ODft(9=XCL@9iVZu3lG+2U!Y)L4Noqhw#z?N4q#x8*I2MK2PUvrDBllX&spT_DY#oC{(9 zo-kXz*U~O(xxM6-O0gT|%l_~S)Z`$1M98nf2*hVP6DK}ht(b84E!Im*Tu(8Dq4yP1 z1ZcXzeHbki%cWix=3SmS7;d0dAgAwHa2k`koH93LVXvqK#~pCk7yJUr78E(dtqt&H zvHb##Ye%zZ-YPz_C9lp4mAMgjs7%WTN{qCLYk&TnXj5{uIrP}ZbUyLox22gaaRVS~ zxcZ+{|1<0pXpVjND}Ac9(Jbh1ez^Z$F6a^zH^)Mv#;o)&3D2*uxPNe60Xhfw5BJNR zhZ{yL+K6=q`sYl*+=buv&hwoou&r6N^`*p|bj(6IwJT?1qQl5~G*W(@0+Q21Q z+1Gn7^@1hiM4ym(yxws=e8A$ue|q5Yy7Dyq;X(S2R;T+PNTL53@AEI1q5nl5IG~Kb z)B8I0SJDMOu31&qGh=zA>Ty)*8rjzADj)Q#a3A!Ya~+J8dR!rK<5 z$vm}RC+`r$!i$c_f6`Q&cV^pj0N@uk@de+{#b1`q7Oz7WqTn`bE;a2?EYGXjvnVrk zIH#wZ*dAi14{RKDYPuL6F1Yca6sS;MlJ{m-*5K&Y&BAIXevqze?A;y9LlX?+gh}R} z=46Sc{O0)ZR`MX{mat7g&54S%Wqkl}`0ZdJMrss!W_ZJdmOE!4YE^2PXR*{~B;7_7 zZny3-v6yCwnhpSfrZq&B3#W=qsbc5S3=~+O(mrXVjDggv?d(6$JJCVD_bi825yg&C zHdAl2{~~>3C9uq+%`buV+RLB@T*(aYrv~~10^AHEh9VvtXRNwF6s)J#lG_hILK_uL zMm3x2(1FSX*O|nj|NWnOF=lN@#Z%8)$EH922>fWm!a&But&eM(fvkrclqhiqzv+So z^0v{t_7*sN_geAZ{8nxuF0lN6QwiZ${(!K#>q4*|K#&D@HpsGz-b;Xf<8t5iN^m#s zDzDFR@;htGo4wZg?y)+ZKAy$f1JyciU<`)sc(f03n_3Ls(RV@BJE{&+18f!!tzex64Gazx2Tq)gB z!`3hX9>+9#SG@FMf%MMA`Y$}e>#rryM@L!X=)BD4!W$sa<(jjjOsY53$?dptIBR{C zL^0^)0M9xtkNg#42*%+ASzijx+@)b z>fsSlVXN?e*K1|@G6b_0MV3=5$jDCMs`keH{43M@zi23W@ZhsU*dBF5%h!Q|L!Lp3KWVnY3C+=DtoVTAsks{Y z=#H>{D&4Xr0FZy116P#PJzfN0A5zDF>nZ2Bh9~|>-vML*F5q*<5}?*jdaF>2R>fWc zEo;v~8K!hLm8g(%ndoN+x45+mcq)mH)geo)Iy{**0`Z4n&jU)b*eTp)_KAc8dArMl zqwW1`z<>p;&m{r=iAf^|H{{cqLBQ+qdjaTq?(>ohw7J^YcO$Bq>pob~0Eir58GNu% z2oIV?R5l)iq)lQddY=QY2R;BPr9h3=H0;Y{esk65zqu!jy1eL*F((;~|8nMHrg+lV zGvL^8BVaN9?FRmLVRX79i3J6RVF4*cN=Z%MDpiWkvipxKc(A3tIs$XTdkg4qi;boLTa>rpoF0S4p7*H0v zK+=32d}io(pBl+SwsD@UL+K6?@U8U13(8ZPBLnBXxUZJgoCMU#kn?=&Rpgk7#%*QIB2i z5^($+*aoz}t=LojQEmBd%~hl!)6FWDd?1SXR4nk`Z2O#TaF;}We6&aBA1gV4H*pG~IKiZBR&gv@nfRU8~#-|_0>s;|z z^Mr2mLsk)i4>ant>$%n_q>x!40fxRB_2aAQD5_1wG%`9o(slxqq3;J+hS0? z#(ChhNVLJjQ;a2xX_9!UPLjp1Jm#;FH-INkR1wfQcCOV^-At3;RQ9+N2FKP&d{i)` zQ;Bg07l1U=LBR&UoZ;?Vo*wLZa0r>y6W$_I-3*;|Pj!NGBF36N_59HMmsaerV58VW zIPMOc*D2f%RDp5x-b!gFYHhDv8+0sulWD-d$7`HYt!B$-k=)LPU_VIxlkV^**t2U|F0kM`@ywZ zQ1xp3Rq_Ayj;&2aIaqgu#$NzfKzvA+S!FYP11lU|z^w zMFsAnE7orpo8rCslcS(79lncbS-Vv-H&zPdymh0TjW2ZLuZjODzY&%HCx;4bkk1b8 zO&gFX%kcek7r&x(%$|$9$yTriCow)BkoSL+UXM@gHQbCoRp@Z9zY>|i~ zKZXk6Z376hd$055>)kOcd)9n`Lm;4T;v+}KE5Z3sy1*f#8>{JW+kkeZBR?b?_|3;5 z<${^V+5t4qcRV~@KphEY*2g{aJ|-}K#x%3`1#bD@NH20qWa^VJSh8qTj}+@F%jk9= zSWS>{PVlGHYn6yb9-`?~QQe?^kJJ7m3DIHFVq!iD^inuC%{B);x@AmWz$R%yBB~)k zTz^3MkvdNCli&O`8opLLM4mKC+TWXw-SCS?pLW0}d(Fc=8Q>i+m6Potu8upC5!e`- zI@lUs5w&Qe8ro-jyj>UtTAo8 z9C-YUV{+Uz3)y#9q85v#)0QBK0fn;cdv30EzFc?mstFRodr(E{@FWjn99k^E0F6IzB!IB@DZ2T zWHr>G0wzZiThoL)Of$YrF45fnOC+f{kYOCtSP;HDOw>7~37~2jSMMKDF}=V2^JbVz zM%RB70oX!y>P8Z2__0hPvB+{Y4J5XQrYK}C*ILCT;^6XAY1HadJ;d`Gd2pRgie$zn zQ`rKuVN}3Q=t+3-Y{Q98o!$?Qhl5Q6S7WZ`zyhQHcrFYr@rnf&^SuH9-kNr##1a@v_1`l*o1&n&-1V zU-_BH==;fJLK*ei_qHyh@_=p!LZGzT!w^Mf z2qv&ew$O+K4jLkDTHL>Q`-WqHFPe!?Bam@K(e0)Ag)A^O`DcPlv2=Jby)u%QlD=fw zNTt3D7Rw?-y3ReE`a)xlvFNTuG8D*bSeLUkqTi{&VK6*oVmFkRUm_PQ=Flv^mj4-@ znVjBMNp~!wPy(eiYSK{g7-l=o2BV%O$IxLhZmPf_*^^@MWe3&q}ze1C~#To?<(LqHIqqfeSDw$Tjgh$lK1 zJI)$=j+H*62XnOJn2#?S$mav|Sj99^p%YVmuFJDXO@}OPeTh2RZfxESIzq0gmT5i{ zfMY5#6tYf}_G!>k(#q{B6N`sCZ8n#02v@JTP@mw~O;K4SpS_#fDdo!`9X;L+#4402 zwePJhxluWA*q#hf?@a9AJ6sx!1of3ql7hSGEO^7cp~7>Xn=1_3&z)++B7a5U@y*+ z5N-c@;a#Do8?nelaZ&OVOW9-1N)M+D9Phm zXUigYp~4#CHfNf23z~Q&b*A!x?omlq3%3V1CwO$h1>MxsA|N#V7`{#u>E z7sR}uY>iN|wvT8(eU&jXSjExt&RRJyXu)RTel#K`a|h3}Y;b-zqR#0+00HZbV^-=~ zv!&dw1irPE9!<&t#^0a%uO$Si9u;+^x=;cyY5rHpmxJY!bVnMZEYcgq2F%j-^uL*5pf)` z3awoDXEF2LVdjhHkkRh|WQ|U56}zdipT=tv@V6{7M5woJzn+r|N1vFsMxbtmVm;(D zH(1B4^##bE^L{@;=ZvDKgowG;2)mUhB;w+wgb6|KF!Qxxfbvhh3JeZHW8Xio`!}l> z;}6izW4H%Veu3-7F3+M|i*>yl!pAnlsA&PPlPr~zhfBxJN>r= z{{JHZA7Bzq9)NTl3JRbFeg*yV<*OI3pnx?LH0T8k5;DqrR5Su&Iwlr$LLz3q*Yvt1 zjIyK*{I0i-O zUaM=i#Xx$}8N}d&UYNONUBfP~E|&Ji_IY^Olpass zm16ZakW{|DMzM0Yf7Kp)=lnhCePbQvjhmMl9GK+xJ$2{RFVM*^kV;*Re(hqFd}}L> zS5MMBcqt=tI(G9&Mp4}V9P6E&6e3GWXk2|d_);@M~ix&uK zv;-CYmHUoE%UX*iJvV*s&PgW^Ss2Ym2e!XJ)xEw#?G>wqib-958=tai+WjjFEceSR zq%5G&m)5Eqqom+s)YcZLdgf|t@q|Nhz|2P-;KQ3iZZ|{9eBE255KVe>2_yQWy)T}9 z;88s`zka2wqlL9jj8k0FY-?)N8t3n&I%>?QF%{M6``re;=r41@@l%nPdvyrysYyu% zjuN%MK*&U&IH43(R#ukCg)MueHCK>K&{>iB!TD1kYXbjeRW&p^tEHN>hJ@WHTGufs)XhXtqPG&e;+E|Lkx&kf7syFZRNwabYt#; zz$4Vw3@0VQ@Duk4x2B{{}e(^7r;A}kb3 zc^0raX-^^dtqV4d6R1rolcFAO3mqzy@ozAih}Usz2iD^>63~aLczi5&<(@IfscPg`wH(o`@C?g<)j9 zwP2V1{m!Ja<8=-B(csL@220_p5rqg7OgXNI!$dQ^iP;BP>|7Fj`G)yXjcKIhjLrUX zLBrtmA|ZSw?8DBFCTc1>mhEg(b6)N9`Z&_UQY~_Tu;HyGbptDIB3aUNdD&;;F`Xan(xDg``BeIp*wWgdp zKvR)aK|XY?)Nb+%By1d40xb zk(wMb+7}U`usK4u-75!nQyRB0plmuy@M;r%e8X(SgRGVa-K||ZC3DUg1$QBR%0*~k zfAPeuqr_JdMgS{eQdYe}-pkdzNw9Apo|04CcUL<+#gkcsV9JmbHgnQk-naELcTuSr@I}%fIg7xhRf{Liy|Rjf zXMy3lV3XQauEi2GnzolNqK(>u!F%oVKf@E_9vbQzt15;^4o6T_77On!PUJj~mdSb# zv$Tg~+>VnG(o_;h7`C%js5SgP7TeV~4${Q^0PA1WBd~0sXQcV_Ge@vs+#}cODEGBh zphz~Mjhg-_i-#r9eax5Z*Lffg=-~bZdiU1)+}CQmgNPVEyw8dEiiNl6%UYQ$uUA@? z7vj?ZR;T1I5OW58h7|bZ=ohFcFk%0v=-XeQ;;o_*?UT|gFFvU%8>wer>QMd*Hi_-^ zX0g*`PzWo>AVjwUZn_zowzzv^8~+3MQ;=DiET3f8R8tOTvFPS`JO*8@GMM{yzk~P zai-kdqcx1ItYFjpUU}LxFisDjwn;Ynqk&Rws#t1OaFZ3nyuwoevJoOxleA5RkrG3L zBZ68F7f*DNqKDM{Qgt#?LpsG`^lR#h%_et8XQQIdVz1J&`V(t5?@@fhIt(OQ18{d@ zrAp5)kOjvCiq1JSi#I{xcrWVWonIR7dj*p%ILjw~sjQHXb^m?pe@z-Sua#VRaMGDO zb{L!Ao(gitjQtny@Fov@uyP*AsDW6zE#v=h?`&;-@Zx)Ib@Sqo^NsCM)gq?K)abp{ znVo8OEy$5D#M?-zyKO-*!`D4JNrw}3jR8V1Nz&n}z28k9>*5xE|Ju^c=yK#!kYDaN zb;qCGOf}Do5y;=y{HsigM9fVimG%b1apqJ3E~E4`INR_pq<}mJQja}hE$%V3B<8DUpiPU)>(``p7>Y~15Jj_A@N61 z82xC`)^oU2JQ3>~G z#TGvOAUU_DBui}JWl3wy;5}{7b8?rC!lwHOR99?1tyOhsjS$Z}{? z_nliI5Z-1S=Xeo{b-T*GLuEl)%W|N~4MD8!?!QxGn;|yWemuilLYtL8Wtmhpz6{3F zOVoZ54Y%5U7re6^WmqX~(vs9koqOs98ZDKz>4r=2uhubzDemWN26xS zhXQBrt7P7#hn|<}iLbqWAMJeE$K4Q3sSN~99FjdcwvI)qK9`KiGZr!PeoH8ajEY_7OXVLg{f5}KDxqk0vLh!IS#LouiR0vN{KJ=-n+X4qr;WT68ZH0w_Hn<(9@8AzT*`mKnts@j6nO&Qd?H#UF&EMRff6liL z+@o5N38{oZFI6iGG(k3XW-8*a(R{oE%d-kRqMdGE$eh1>m-#|_X&+Yc#}V@sZ)f;y znqpM7|Mx}`7azV}ejT!&d^SzSNSC2j-C+H86cu$JyxFa(@G#J;{kb^GIkDI8-bD~d z=@vKG4J`8xB{gq!L%l~cAB`4Q7<nKQti0{$R8d-<0ynJXEXSIidSZx{JbC$*;-zAV@d3)XkPPeROm`x; z{SruSL2RLf8l_=lTA^riZc%1XraN{Dw&HcIr~+OB6aD-%`K91hI{c%GZ%5I9`W!el z!l^U9!b0PG4qWwnn*^ruY`0J!vXH;8(WE!If3mw7Pv&uRfcytm8z3t@xiK2Ykr@^S zJ#c#f$C}>{2oXN=!m%EEh%Rf2I>Vf$;aG}$5O4)eU-ZUeM0~FSMcLEj zs}_|nKIT7g!G#|G z>G1fWz9twDMsa+LvrjYg0B`FCuTrqfIeN<2V6I`^7sNX5&0KN92(Ug}mTJM)FiVq> zkzqILwtk-R>se+IAUD+gFi%V%a-2%+Y?+?M^ti117=>t$yGW1u$@eQopPIhVrAzJT z$~7qIyPK2sbTFz3{z^Y{Kt75V}Vw-lCsGyfy|8tR$IlD4r^jEltM z17Dkq9<^d_=-u$l0z5U5S?%k2!{*Ki7VKvf^}%m;oF#|yHFJ$P zPA%oD3rskCgMM@pK}NRQEWbec$x=4%^)7O2x9wpbSiD8+86SXul5fvj6xeD)LA3_M zD(Oid6$RAOxi2%qRs>x~-c$CPQ6=6ym+z;?Upt!^&dr(_Dtqq__jwbx=tVQ7sLAhI zEi4Jdj?0!-Gg50R;)QgczAESMWqoa6Ltw1jtgftVHp2=j)1so$oTeK{zvtcp=7v4L z`_fD~_S`wE3@QSzw&WqKYoW+nn-oI*fOeAISle6fT(6mbAQP&6viG z&|ZXbHw5>K3-Tif8Cw-P!gUNe=*hitbKD!5WcLKBh%1C$@N=oMy+jg&p1r>}-q6iu zGmk3!cZZ}~n!88_Dd`NrP$J`_4|KbCcIM~l9%Y=pHD<~+n*`s8f-SWlEzD2s@Fw-p z3G~#?Nezw^MN-{MH;+E{ArhCRYj3@C3T(I8lJM)2snlDvBUSR7g;S7F!sR(9)ak9i zm`>K-tF+)n(LjdRAX8Pfqfzp^W=4~b6vh$3b3v9n8`ba(S5-afdjXyNlaumG3a%iV zYPr}Ev3jbIo-92tBHN>(UBqj&p7JmYbCCkn2E|Ait~4xCnQ~-H3=Q!}lLUjzBg`3p z`Q96BGv_Z72m%HHTcfUT<)U6B%h`c$=g`S&OS}GPzB>N=x5>zE=7J9f*mkR{StP;{WS)N+W*Am?`-V$nlZ)}+Z zHxtbmQw9~a&(uwpD%{K#COacVFgbnGd-Vd*dGmWWR^9>BajVp1g$nq4qLe66IG&+! zh|LiF__W3#iQ9(RDA$#kgScrq1!bFPSL$3T7xXm`$6VfK@sZ9aptESV@Wz?vwz#$QJu`rXbatM zV+Hzdssi)&;s&=dMGoV0zja%hQ+c&of2g%bp1eUcWq4uaj_m&Mkt#)tf;>#Eo)N?W zky_?KC%9z_`_%zE`&#`dqx3D5_ae|GLgVEam6^ko&>ns+^Q}QShj7PzHL?cW3t;R= z?_O?aZXm>(klr#of45hhPD4_bv>7gimWN@0>a}4_T*pbAo{m+Pp7KuhWF`89i+z^( zDGu3Z`GjEFlm#y3U8p$BS$}FHuks`{jmX=V*5&nOJqe_a6!j908(K?66I9jKR?GfP z&rLx@Ya<4PWa7#TvEmDi4XKl`xEbN{WB2whX$Mt}u!LA5>WmUWCSVYz)JFB2mWKIh zsAc481%Bs+2%5aK#*z`a*zjuVMHOuHUEFbJ+Nn>_6_pE8cpX)up5AH(p`HU`!rJZk z$Y{-d@5E*0tsBQY(|q@@6R}Bacw;?3;)L+A6ZURCCt}Z$%6k(W9jv&65H|y)2BmqDHWZZF{iXPWlmwE}%>_f5%Z|S5C&Ky37 zPIsjA%dYzh34V4WKeBHcDUadmL70}2*RIKv?1_O8BGZpv&RIFQS!)dS%dHSMkN*Pk zoLFSuRvZn;C)Fig{sPgZZ*GozUdkqIrCu2Msl5L*#NPYtcgu=pp$0pQJa=0mq7fzT zFQ#v}^+n4tXQr_{0YU-c-uNcK=TC6^%>5h=jC?kZI25@k+`4$|JKV`E*%C-Xc?kH=_1FV}1% z$0YjuG(~QR4L{r0w%@Z{5|3ACF`f9t>Z!+Hh*Z1^%}1ROA&h{dMMEEBdo;IYfZiwpHVjAfcDe^Zh+o+vx zy~%V2n$HHfuiGZRfjH{wpC?1CWgZ(VteHo`8xEli zpLuiMIJg%>q1HZXiK$dgZyP1)*!(yqtkWLQww;J@^*%%jr?xMdhYvqip?>X@LU!MOtHIS&<>1I+Y+1(oniU9&Fkf~ z9G?cGWvwtK$=BgtlSRwBA}Y}iNKQG?Rk)4PDfyHORC4cw^s^~~G&gjX+Mus_FCK82 zWPq6_mpSH|?dL4i=j`Ws)W_6!N7O1T)F$5^MA^>=Q}0$8UsoLP4XQN_xaIU{ob(W5 z4)n-IH~>As#$bacFj7F&Nh|%{$I+U)mT7r)X;PHN&9!*2H!j0MmLH(4jrV>m#No$o zdYj7|8&*6Uxd?-}0Mc~uh+WiWA$tzI2GK<9BN1c~-B~yz*2Gk83Eeq3?fq?gh{xDO zv;B_=;?)Jg_I-glXW=wl)>O>vV#N19b}Jw>4nJlLs=UD??B1SrNzEouKW8_%FHFif z>i*yQyZ5xa`F>_H1zs^?t^gVHNZ*dAgB9HMFR*779&A0@Ov+r!4J}oUX(wcuM%OV~ zA5FLoZv3=`+Se&t)tDUCa71R0Fu^Sa;fyD=B^zBoB_a`IXWJnR{k`V;$wcWmw(u~> zhrvS$d3b6XrdS&KtPFGChgpv(ZAkSiqgR4OI=Mal)A?p-<`+@G@HDELlGLYHPq7V? zFH1x{ZY(^XJ8~PnTED)DZCitMb=CvJT%TTfIYG>*oyC zbbnUT^j@*WsvIGlRYMA@(u{jtUB)XsiTKu6Z5i_;U>D&oe-e#j6w4uOvT36-x}r|E zTU^f28LzqqE^oCp?dvh7-yXYE`t3$MvQp$Pkk31gar5?zqRi?uAsu5t4Gs-mMK*Sy zq56zrX>QPZFmqNg_uM~>^;v#6iJ-0`)$+qPGA3K}D7cp`GyIT*9nP4+k*4NR_I}LK zu5a!ma&O)(d8(z?p)282acq((w=p@G3Rj!!ORwYvoFTNc+RF=IG@IYe`!^KPB5pS6 ztr^zFiW6?Y5`5iRl-h{jgAH}niq)u0d^WT8XwxZ8?Q?~0fT`91(}T$|696^0(&KO) zviWxtwAo8SHb!$;7ZQr!q$aFna`6`GWAA(~_0lUB4bQl#rZHU8N`=MiW)G$25~tH+ zBx$m?6u5`fM6U;U=?z@2m9;{jlA}X6Bo7^1KB2qRJt6&3{Ct}U)uh=OsiDJQW-9mx z@fa3f5{<<8tidwn`uo*bHawDpoLD-+z8pHKGLnFYCpn>9#ZZ;>{1{lgV}-eDX@-xk z{Tfj=9&1lNKm1_KCE@faO0EpMC6LBGW*UDj!W<-t;PPo~O#1PWPA)^?mFaA3OlrYb z8Kp>Tj=9@r3%m89sK$>BZQW@8#3_lA&CIYi+A|BO-`UNL^^V_!m~FvI<%l^1fMIRc z+s{01F8WXz)pp*s(>1Fkw-}N8-2&HnalTJ$(V+v9S# zOYH1AE2gzySvje8#y)$^30*U>xDzEi$q}pILozbYPOh{VinIbRsIk+Tt7(Msg6*i5 zToc1(9NtNI@>}JGUU5e25IM}u=O8Q2aLxr@6AWv-i!}|G2tDHje-j~rB)B2 z_|Ht{mlr*_uhA7=jBdUW!JPV`>;9&m>`0l5R1#t1P$eW>5>{{mGCqa2OX=i7dW4E9 z68D2b_=U(QFB@ubb;$sSWRW6mn}t!q@?Q?Z_4C)4#>`(mx_yG@a<51 zWJaMVPMRG?N}VOR*gqCn$p-bDMs6_ zL^zZR=IW-CPcy-8#c_V}ScHf|)#U+1qvFsdlW78pjP68N$I^@iyfjLj^QGdD+?v_a z=KZ?dXZK1CzL=NP8&dWqf9#;)zgzjA>)TJijXk%WjI-2-Nlp?R1sU>VB_@h>rE34D zGk%*)>RKBvRQM<7tsDl7@Ix;$8hx?lb{8|+6K1)nizoxA9TgQDP!KHK*N4sfjBeV; z26r=RV4P0JOL%QwnCFgL$?S6LqLEOUqTMf56|p+*k@QF8n_$`Os`4=H1XeFGXXNm$ zGKZ@3G*&#-N)uz*2v>yt@EBSJw^RJGMdWdRwu`#Zm~pB&RKInY=l-c4kw;@~H&ZJQWF5Kx&N@?{F)Fce2r7t6 zFsSf$zh_L)tJ+?}%2KX0%2^|4tu&0m(fN!%r^yo9@J(Y4oNmKr6V%3jMj}2!RUV+{ zDlHelnRoKVHxotoJ@waDBG1)Otd=Pz-3GDxXC6t{&90z2SS8Pm-tj_}fmLkV#09Qz zw{fRm`XZ4R3fXF``M?be0q(ZU$X<*6rTMEb_0u=isVmv&Jul)J+Ah0<=E zk0^afYUue2*a?{1kKCUckls&0+LmF~4yhmQ!w)}mtaBR$`o_o6cBlJ~NToaFl)+29 zp@te0CMY6|mrd#?djCo*52WjseP$nRCD<2u-EN~`Why;4uO33(C7f8T_+ua?tc=%C zWd4ULqi)x$;Sq6h$2>zRG!59{B0S z>j@gc$xDq8TuGeaE5-RiJv4=_N&+Al`KKS{8fmGphN*4Mu)72%|0!vCfc(}c=Sd}6 z%J%tG@S9+*-dVK6WzuiQNTuYjU*DzXa!6mUkSK|Uof;I;Cg8r`au2(%Fl(|)X%y)S z*6W=~ooe_6A`-%NPIu9rKflWq7ddVDED=J9rCs8VI9{eOS$+mT)mn5^j-=d$t0btH zh-rc5+bv0BwiN#=RxZ>KOfRM^ji_Glp}|>!k9#|!u~z4?c02A_ZID6&CqCqIH2C$1 zigX7oEyuRY6>@H7gvP467fhVixcW(PQ2bGsXbeiq4;xkZ&^S*+I`I=FdcsQ%>3k6B z6o;MX%4Oa5QqMKNOt9yzif)0)D{BgRA$5ATenlC%*tkO;po!1!&&Fi0-@#P7m<;g zQ)(?O8gvX)>flAvBAX>@1f9zl^Bs3hA3*OE#dE(;N1fkm)EvW*Ho0VExu=? ze{IrqPAsK{p|a41m(Wz89!R}MSIV?&5|xd(?#f= zr4zH=lXALONoZVMU9*AcdPO@H2Tp|P?*`s++=&wO_O&D z%*!?p(FmUDX=lvh=iC|G6nzC`Eb~f4Cl&YHmG;zRqstwK22jPF8q|A3Kh6g-{paNL2T>l>EpL_o) zFDIPaaHRzn7cpfE)z~>RCzmCe~N)QCTD9?7jXodCizHZ)RDB zzZoyi|5&@Og2(yzB2@#6PNEf|Dz*8Ol}C!SzOE2s@YIIzy?8Zqt*XoGQ>W@uR+`jl z3C-jx-`iQ4jZY;%tiTCjb4tg_phFYZ!L@5VDt&(;yIGWp&x~*KF@w^EnlA_r4fXtC z?I@PU_`^{#cvz+m5ex!jJpwL_ILYC@$rYqw9m~GNT(+QK{*DfPVM5WPGtlOEv}18B z+QiOwj)j|a7s}qS4J#t6+W(%gU%Jp54~W@&Y3!n)F&0N=eIXxmMw&xz+noQLtssqfy0YXE8ez~J!p`F?fIPum;NAp+0xk^^>1esyxQ~O$II)FaB})?Q zGn#Yq4aG2W=yPN^>@@V55Yy1d8FEyL&U@ep(wWq`7;a zyQ?x3WODbWm@4yhlI_e6^$BQHSvo@Z&T^LQUID)y{MO=Q(m+j{w|Gy3x6!P4_jqqK z$7P23 z+Kf<=Y1&I+!g*fK&#WXJE4*!@q#|aUo~!+wAU3X1@#7q;{xE{f1jHpEqlL`(-2WngK%Be! z$AXjUI|EKFsB0Rgq9A3?SOQQ|ig8qd02;mA>wuDNcOHCSzWDb*Kz4cIZ}k%4u;w9i z>+=V4OM2xkk{>p?;yfc=BYjreqmkf0CJ^u@K+yyrHpY-O&%!S(0Cp%qXTYznSE<#g zPzm9>M(H3SQp!`h1OgNOOM!&7^6 zPannSj4|9%RvN`pSu;5E4phTlc_ODC^#!-wqJU`XN_$C~xeGlofzhdC2E$|-xGLzqFzjnpKIN$=f%_u{KM}S z84C(U?$M9S)kG!z&Io4yYbM!LPd@3EtF4@qqx=m;ug>no2tG0dD*w#B;n}ktoQe7R zU;pqg&f<8tOp}3>DyG`97y$TKQ1uyc1gT~hp!%_2_0^&^nN17id}VH@j^3eK71E?+ zL5yS(Aoj$V9s8{&Q02?(=OIwv*Rp>h#8j9`>ra>u$vENIcKZGChOLLUSX@3HZ_KFs zAV0-RlUeRs*pURHb}1JB)gmphXjPUd;M*=c?|ZASqg*I>q0_C0?n6X$`0)eGDAc95 z&VgZRX7~@<--+GhsoKJOl{HW>IZ@=U%=(R#^r^-a1hCAQrfIXNLPs!Zo;Vu8M9$N% zrarsuFCpogIY5(2QLTMPB^fS!wZnLH)|JZH*`YNyp>k}+d|w1ATw1GZp1uNMH)G!& zbW&twr=}R%3hZ+9`>S=bSm_jQs|7X~TRA-I>4p36aW)1LQg5BTuAba<*QU%B9|WK& z$8^VNHsA4KCVB`H$97qQNt#5fwB;s%uCqhHZcX31`4PqfcAam(VR(zfbbt0ihmZh& zft#}Tl)RMTaqZ7U_xKlDN9E0{=g_Tvz9`!FmF}@Bike_cNGvQY?B!c-sj7dwM_SF= z20S0gaeDRrZ$bix8Ww-|JS$KbeF>_Ttw??%38vLXv>UC1rI?7YlO>q2etEVrRo{@i z+9QfR0>FQAr2<=`IKpGTKGl>24hgNG^vu0Pft_}{?w853|Is~qFcrd8;t@8>R z>rWj{^3S7HT+B#e+;)L{G_lOuH38=RuR{6>2I7@RLCXWXzTe-JCIy6&x zH-(%ax{90%Sj~mb(mGU8C3>3Xsm|Bk??e?8WyhObhJGGMke; zz45GCjEMVr4UQOL3#c*y0g&l@m3~jhGq)nkG^Q_;o@fZKhd*Hl(pqZEso7HMfhd7{qhi!EJcmc%-0vHOcAK$VU}(!3SFJwd8}lolDQ~{~ z)M*|iow1WjTU|9V^`#*Ej4`#XtK9FW!X)n<5+R|QODPRmTZJX9K}ynIx5_a&?KvT- zU#H=TeR=9kS==cSYO?a4MGm8KEA3F@blx%1e)0*mvdZVcJZh0VOMyYx#69X0{e^ce zh}&WScXKnYNOhErD0G`1Kmaf4dH<_$5q{Fk3HUuN)3AP%_G)Y$I`J+#yg4?3>5s2M z+yDClmI?!-(UH!qxJl0%sbh)7t$+EvDe9npi!eil^giyXA8p=OSN)d1-m6NwDUPMoBA*FPPX2~l@PKK z`#2eWYuja-e$(Bc7gp8I;qJpb{oz+!3Eg}B4U4l*l*D_c95fbz>o!e}4hb$?c5R@eL(|M#KAg&6fy#%9HBnH&n4DW?aLpmw^l4(-CbW~cF+2*= zxOg!*fvqo^Di{N~3b;|ot~8be&O1z{UXsK4KT(^<8UL;OB*9(CbU;D1VH|2JHU1ss zQDlMA5RP{Ilmv#iIUA0iyKjL$r;*#>-d%cTA5B$Z;;X7`gi;YLsE@66G${6M?kaF! zv3>!Q7d(R@F8))h29e+{i)HIzXwdakz{f^o&-`@+WHX~i?EdSQb*Oc`IqXY?i~A;5 z2Osjw*uF5s0ijcKLqU5W{X)tzB)3&*^dZh}4w1YRN+2-TV3l{DKKUe@sFJnX@K+-P3FGEXQr%mctH~RaHKWU$~cfGsK}o`ca5jQL)O0BpN0hv`ZL)l z3gwEt(w@3h8ul^MK?+VF{&EbGeay1eGH$^3uhNBkH`e!MajifudNEW*;BMEa8g8W@ zH>Pc0J4}BYX^Jfj8v)QVBLx(JUvW_H-GW?B_dX}tEaS08F?@OXL?!=4r~mwi8zz7R zx1RXWXe8O3);{8pbi&AyYb0{9n)FuNlEGj6v6*>XB+a@az|Vyj{pxk~5q;&P`B@1U zB{(orMm-!4BN(eoz{I+}%iq^Qi7H?64bUiO+d1eXrSNpNyD^Fy?oxqox@uu2(KE4j zQBQKZ!TbtKuV5GOW6OE;t0bQAUmnK`95&FdW9e%p(kvl;2i?j+G+kZ!TV6A3+(lL2 zO*aJNYUMm1GGC7eIFA5aetfhw{ctFqW`oJHibf`@*`!*g&3q-xg4vb1dhtjH2s*2n zn}g%g?}Wg$CHL2u9%T-1Q-Gl`qC?hnc_+1$-*yz1f7!a}o|YI^9qM~!_Tv%|FhY4B zZ$V9ph!Nm#7Z9dkXN4jl*Dn=bc9CWYAW}~?$?8#NK_+#`67{b&1iodJqJ_wQ#7_j{ z4bw)|hX*_U*s%pml@fNu(<*cyRSr#Ak)Zg=Jnsld^#!9xzjnjzq$i!ACmm5yFD4_?+Syh>soA&o#D>H4I1HfzU#K9RcmGU6V8Xa0=^-y0otl66_Xs4eZcT4e`?8+g4Lxxa4a3Ua`%ei8^b$M?&)k><;GI|1-8=+&j}&x{Qu?&jy*+0mZIqEAdWu2eSQEUcoIPwvHcj&e@3k%kryRTG^liKez$9~ zXGV=On$e_sLS;ZkETErz5xNVxyP?|?Ai9?kFyZKG{)I};!5Hv!90F|5qHth39Y zsNXA9>*WLM(_=Dx@9PYTznco7qPA#kblxRO6zmNPI!unmsrfECVYJM>Kh2(`UZ=ka zbq_rbFBbUtJZ6DYbJ8`ou`IA~$)1!HWa}1JdHq&+vFLZ`m<2og$tNXjwAX(`&vrr4 zH3Zn4mm-;aZT8seI-kb+{)OPQD)RRJXVE>Qy=V$EfA!X5=k{@rFP6F*Eo9;eiAyFo z@3AIe>MCGc>^0e1^+Z5gr;YxMvB%JNeG_D_>@4;sN~G03+piuS#BcEN1E@vt#WJi? z&b}u83qb@EnefLLl(K2~fH;xNKhD>dloKilGGYD-;t_QAxCw6|Lp+i~Z=F0)BEX~S zoV9>1aHaBN;nQ&`^pIY;W&Zf}Ach#EYQVuz)f^y{*W#wRa_!gm!NqhQ9j4m#iRp3e z_mszT?bdG*-Tt-3g`JD5S%v4;$@gXPqBFl2?4MiHzbp)GFq5Wy4FfZ~ZuRJr(Lejv zZp>`f{;6U9db-a}9IiA#LC>rf_3F8`yXD7?*r@Z6wTcens%JdAE<Y< zz+-;w%BkKT(PU|BRniBz0AVeiTP?ND(5{^&wu3WiJxmsz57LTEr#x4Df}qcg8e2Eg z^6u%D7)0swR?ye#QX)tc(`QU-ytJej_LG5ISmu$aM-3CYwWDoh9_+HsB@E za7xl}e#uH`L9dSF$KwMB{A=b#N)B3T9UE;JOgFYt*Mhf&ElE(kX?twZv9WeU>-Y2SKl?MwMyLI-|d8sahtHZ*erN~oj^{AW>Yzv()dVRI_yb+)uo#j~f(-i&6$hQTc@nM9 zqtvoc1t-+QjSTbxq=%xKQ8S6Ekiy59Pvn@&1f4Z89Wm_IDY%L1Y`T;J?4|H&-X(tg z>K4x>u3NuV#db6la`~9lkrq?shUa3$Xo0^7CoTQypoobES$FP6NZ2Z?)0D;cgr@0ct|Uj>VdGNFP%0+;N&|83^bi@dHOA+k%w7&+QP=c zvSuX!56UP~k?|SPAH%>zl*s`zU3ylN@>+Rk;Y9o$E*iGetj-@q%QYj-0q%ZdCL`t3 z-1Z*uQkI$1$74D8dVf>hY|>ZZQp#kQ(DFyBI)YciW%6>>4zMi57-n^%ZY=!q{L}p< ziXk~;Wb|x(9HZ!>h0Hil=x{#^G4!UR);;r;MgQ0hxbhcTlcsS$H#(KMp5BlqtdrmT zR?kDq_UBYU*n0ZT>lB=rp zNp)!T=y9gkHSL8iIe?pDPx!;7ugl0yu-uDGSSGdu;9=5!vxB=I#!UcAC<_tU5 zfBkF>_>2Cdg??zQ;56_drLImhoLV zNryg1nW<*OIDTLKUa)=S5%Y2V$|1M2^*M6?G3T>)K+Z4dReQ{1CWhx9l3n)Nf#6@= z9hYB{&23+=R2fKqx!G17fsL4)b1)>oa?wz>Vuyj_t5|kUV1E;7_KomfyeYC?HF7|W ztn&l+X~QX9!S#z7;?grH5~ z)INJ~%v_x_=8TcA!8T7vk7XIXgsyoY0JU!VnS60@(+*p&^vfswZx7Eftk?lcWK#3G zXIpItnqSJV|BcV$mQd@lRg0SrT;GXN!@)hMGS$LKatg!*IeAU&#}5!n$GbZXafq-H z1FW_twA3O^nmGxbsJ@mw93rkDw<$zAm3cn6Qgt3#_(qm&@+3O6t|rU|A}#;r02h&3 z>?>Tg{1?Lh$SC7d`=QXIdZ|B4n-G&1O1{P}3sE*NFhhpk60NdRA=QY7G4USq6TVL8 zh+`@8p2^BUw6fl&jl5@Q_T%kvK%z3;W;)a01`+dA=@?=&QAug^>%A2pK?ioO5CJWx zmdd8lYH@$X*C`d3AVE$s?h6Y2;yg9BLKHjsS(bcy6b!o&;&Go)5L8-qD#+l94}n&t ze3e1N?X8kWC`c+03Js`ros!go12tZM#xZz;^t&NI3?|6K?4V)_rb{nW7qEm!4< z!J_^ikKi67#{>$zzl}A={2=clw7)cS>|n6v1>)`YKd#N@lfX`Nr3+l^JkS_^ho4bg zo7!~?YnjoXp^{fSayEe7{Y_%NYipdVK7o0Hgn0AcPAAyPHWusucTfCb2AqwE@ztj5 zOt)pj5AjTEkaPG7|JA^Q-tK4y)4Strty+DsI1Sq6Vsf;O(Yo0`5cVMJ%&TLl%D^^m z6viE@urVp2I^^xy5o}h+bS(C6=eg8eO4AH?f?V&F3!m&bPga2OLU@%vg;VH}n{k}4 zlhC-g|Ty$sGv_@>>RCmf6t!gG+PBNP$ZVLYNdc=ql#@@_DWy3s1&4wy~ttXHKz zxN2ZU!Hq(AYaOqWLbcC4jSJ&(VFwvs@g%)zIB!*O=(wH_DW2Ey zx=Kq_TUN&tTi9PqBVXz58A$RQUSE#;zSjmWmAt4$X zbS=|*IgB=ZmVZc+vG-z72Xj-#Mhs5LKS#sbQP9d!KvAbUg87DacL;WZngs6s9 z;6QT=+NJk7rr8g-A;L;&Jzn*ZoTRb$@B_$qukYg^;FU#$g&TR*P4t$$#6&;T!)lVeU~X2p{sqOC#xUG_I=wAbtA z$*QGjq2i|CU~oTkONzFFq|H=rdrs(Cq+!k|bk7h=R8h>~C-A8x!Wvmm1>;_0xS}ds z7`?jptAuS+EQpT7v(>*y+KI?O|22`E^87NVx0Z2qk4e=^Y+T9vB=Ohb|2bV;L`RS* zAk#T5gWJRrBjNJsl&cya^(8Lu?p<@Mw|-^Q52|Q`rv?e*<0Q2-;@2WH0@6KHZt>JR zS~?6(u{j&MuWb)QmR=)pU2<*x?uhphEyu0}BOi1MS?0o8_Vn)KIWOWy{;t)33#b1A zgTi&H!elh^0Y2RZxm3Zuy$R(h_+R6X%Vh}`_s^I;&cAeq2Nzys%b|k;ufM-0CBT|} z=&j33T-(WGBs<>#_Nphux^Kh_Osq-vzxxC6@`>7dqWC>)#lyUgK!3bA{!7dp#S#GaBRY{=4++!LAbZt|(Rni?MM({erg>2$;= zx+2)ByOujO{hd?5k&tS}t^|cV#*drZ0*x;m-?q&rpcaZpoLUJ|+ZYyXOmTGm@mNJJ z+qo_&7*TE{h{xeK?WH&MVC3fY!mK z2M_aK%fd(fAM8&Vmu*e>v7fYaJ>$7ClT#f;1UiM084_1& z2+IC2?K8E2pF*d&N~C)UF~Se8O6Q7lip*Lbshi;ZmA6V z%Ehj*a|4>80S*>#a$FRb=EsO-_cA!rRF=9J6fSk9+Sb-Y>1|D(aHZHj3S&iRho09Zu2;Xy^uI*%KyVmjpuK znWKpy;L~*`lb2FvgW*OC%@$P$s4yD&aC9Rd8dCW{**_o)DomNT!pkNn;bU2Bl1M*{rqU`3euckm|Mz+a|6uM78a)r+*0dQzZJP3RVcOU=_X6) z!0@8ElvV7SzRQVJv`-_UvM*>I!u&xL@r5$^!p9SSL=$XT$E4-; zq+<(TX|)DhRvAEsTkcy9!doz{h$N6&eFiu>x$-WO9G$mKQe&%-Ep|BLpv|H^COO%U z;C_g}#o8$Bi?d(-1Tgd<80iuh#;X9#HPM_^B?_4pw39| z1Ft!)LrD3wP##Tey~Nv+RQ|5ugkXALY~-hUIYoM6e2-e7U7E5H0Qu4e$w#HAN;=s&ZE%u=mgf|}1Jzn)CGP&LE4sX0;)y)or{|yg zn?GBqXbFf5tXNBu?O)PNQ3P;43dj58x4NHx36bQ!8u5qSqYdN5TfYAcuz^B+xm#DF z&G9e?zmcI64d4gZqN$0bw88s%^y5YfmFK?g3*EiG_=2ieLVr4bx8%M#*Z=BEcrlrS z1;vB4xRZ?bez`2?TrjOTO|aaKMtu8x)tP9rdvp|;`*MRue1ij$)sOr39|9m-LM$dM z%LhlAm8Q@&3)*m>3!2^n8CO|&fv|nU~xl z-wx4Kdvl{~rD1PJczn6bwsO#G{hJ1VSGmyfSdI6<{H9J3)x#&sXD<}s2&_b z;QoU|IEb<(DegOfWGdJ3+9INSv=N#4NIpNFfB?z!!EI=T0PnHRJlGrF*8q7H@}4?c zFjl5{2&~g@E?llyAqT|#91WZ zsU9oC4;+slO9I!XeQCPTM@gz9cTd|V?bpZ)I`pJ*nuxYG1TMwoux+N6uAS#s?&I9; zCl1ni1i-rP=%}&vnAd5eay5ezp#O`vc0bKbfwW=*y*%}J-1Ocgw*X%G!RLm^UHOuo z=k^u_$uZ9P$?oo*c^HER+-H4!a43*vnn!F=0Vdn) zK#BG$OREN^Jw!?=_*{k>X`3&78UEmxuqFtKlAlmU`<9xqezKt!6(-r1^5 zBK_ls`JUM7V_Y{ZdSc&>s2n5_;elVV6k^Lnt?%6z{WsMixU>4 zbA!Yd(o2(pV8#h6&lL(V)re4(CHg`K#Sb4php0P~Y`YkY>$28d$pt%1PbAuQ=O#x${do5xT5;`~OdB#?_8s$`;J(MxFYZg4 z9TC>9neW>|?D{0^YC62k*~SbG80N769I8Iwc%4(JC5?z^$vGVe-l4e?iq6MdQ)2m( zoh~x0f*G>&Y2T8T$Fz>eQ`XOY60g;gx%qb)PNjy*kSXJv3hFjd{W7 z>_t_~D7XqKX*6tPMpy~1$mRiHU43R{1S%qJYco$gvmxBL(hTFm8r&KV@3YFshMHH1 z+L>B55(E@Qcy8cK2L#+;j^x&nfd94oKTJm08+q}A;TWFukp& z87+@W!6k~bL@zm^;(U$i^5g&RiN6JsW2Qz*5+Ta@Zf8$U-1LR$!Gb_j=Vqx@DKVC3D!9Su>Bvpd?o^oag#s-8`GO5y`9$CpkMzUlWQst6a=n1 zP0p?`>;oRVKynR6%EPNFV@J`eO?U4!(rxgfWGykR^l5diOht|D1>1_=cs%0nQb3%n zi&EU_&fgqvM#t_>pht(>9SlJ_Vl^6%2%h-)j7P5zeyMEq9MvUL9ZWRvMVPgbC}cIf zPwE@QHeT@o8~h7F6V`C2jBzp>rmMM_9!RfLDM~|!A@+8==!VOTRB_v1=!%m-O3o^U z?(y44v&!^L_W?uL@8mS=TBo+Mvy|3?v_A##&XgP#6P8m3<6{Q7@15FK7X>`Xrg-YG ztB3$WkL=Guo@G95gWLu@A@Pfs)+lZwa6_w(~?knk(G5sVzPN7tuJ2gfebK1?B? z)I6%yStflVH=aFj@oPPgzE=1O2R>@4R$Pi|cVsrVwq{n3mOC9&v#|F!N|Qe^CcK^L zjk!!Nh^>mdiG{|Er0*_bdcn+KHRGcPD=hj7{DYV?ath6A^Q=^5RDZLs%60sTf=*@L zQ=g(uC;;qeW?waS#qV#Dzb*=!V6^m?fJ`o-+?MN{+J-E2;oYjQY65?OA=S z7nXdGT##NvD{4T^R;|M5vTYXF@!?kXLrH{7u7;-yx9j_hcSz599^z}w-)dA6TOmVG z%@CKs`9{O0O8!5;tb;eb>huInb&Dn<_Gg{xEs(3e*O~@g1%Z1A)~As=(u!t?UC+f8 z8zaGlzOD_i8^(rA^@H6cQNEwuq@tepT8I~2Gk5sf?SIy&V{LMI+t`ArcA2@LP1mUhRRBW6j@EC5D&!iSx z>Q~D8hsbsBsOyQ}D*h@%o90G)Kw(92YE!DnoEU~Cv{D3IjLL>OA+$P+Xqg2i_St1LU6Kwj?SNHX#Nf(_N*=11L=Cjo z`3WcjnmEae9DFhQmYZp|y29s;ogZfV|J8xD|D=p+Ut^u)cXDBr>HoKb9hdQ{UiZZ8 z|1bD0{^R=fH{8waziu=#iPGIWySxuL>v@wT_1f=eV&l|AJYJuh1<(6^!n>PT-(>>r zNY*-H+FO&JT7|tEe=d48yb6om);5VOBSS+bmly}HU;(w;?=`sMo-LL7<3~&Wy}H}W z&8*~F8lmy%-vYB0xhCGx)#?yFfYwk(V&+w*)|Smp)MbqJEZ#E!<1Ie1)DmcCjO!_E zF{|i$4W;MDb}SYUlD%GelHJpiyvw>{z8>IAvQsTr7_^*2ZDrY;vb-u!1Vytn?8lDUQ49-;Zird8uhR*9 zt7@(# zQ}KipC_szY{rmH%$KYPefs}7dby)a)TV3hD;0X+9<{h_3`ItP#N~tjls(F#H>-2qM z)d5yCne>U7M{~yN+$U4^5QdVTUkZ2b{(7(Cxv+{k6IdpwX16Jl19DUGBL+7TrR2a} zP`b`dkm+9YU$Lgf)qb}ZPJ3#yz?pJbjt8Jb4t2&E^{Vi*P-3lp_Q-egc| zN6+iBUi{LZ6J$L5K0OAhoF1+IJ(nK)BIP;o&YrsgJicDv6hT{1sBLDW*IF)&_jv1$T`D3VQ>j7d-M8Irkk6LTZ5pT=O!k|Yz!WRcMnWe*cnK4 zlS76fd8`H-FqKH;)(L|Pwfb8xGM50vA|g<}okx73s4Tn;3AhCcfZ3HSzCO1YHnz`B zhK+WYiAs$aY(2Ue84Oe-HCWcDlS)zh>_V>epi4RDWDiqJn+CF6UC`L?-TSmR3Vak9 z$PDq#lH7TnNI(>OeWA1#n?DCiB=RGbtM2Zr53rll3~1|>fnt3N(OR{SoAy*QXW>g9U?Me2;*dLa$cTE|=J~1@Y5jQK>)$JKnA)h(0fZxXQ#S2~16>sM> zKM_}Vli(V#0A74F^=CsfI%V98x`y#2K77mRQ?Xh?vVi|vthIGb$jfUsN@G33&wx2e zJGYJ(uHgX#y8+CW8{BL2rr~-tm9C%JG{aU_33d^^qXxWY#f{jzldEb?+U$YLPLroF z4wIalhEkPT4n4#r{K&d^u6}5EVymd~G8!176y`N^o$umS@WwN!Oq`Z!o9)k~^xh0;ciS5A8n&?cs(4Uru$mKN_8mv} zV0W#($J9#&U56RcXQD%2RrF&$Mf@~r^(d)8Hfaru+<~3Xy-hDF7H4NRNGaLITJrf9 z0y>hK;dOhp-3HZk%R4-f17_IF{0Qn-4b$-LRLPxvXW%YfQ(744YI`?y{_A}#1YE{C)i=TAh_GA9A7ycUmCEI%JNd z7dXrwPNVv0Fu!a;{bBFd2kQ#C&82s>mNxU})7J*$9oWKG|1m(?BW`}HL^Cx=Z*DeQ zeePH?fY=apL8D1Z_O}3SO2581^g21RCZw&>m-|%)BB2s?=+Tfn%fKWB?`5*+Sjt7^ zIyEQnBu{)e>5rie8tu#H7WbbkvyNV;+RfJ(>z(g?*(>dB3v>^dgA~hKE&2AcnI6>2;~CQU`++#r9zpi1f9tnnbqpuau=J6)CWT zq_PbjYlW5&!L( zF%V0}G6B4j?jP|WYW<$}BqF|xTj`*xrF>_9Udb>C+-`DxBW=^XzD=fAJIJrhb(G4= zVg%_Rmj?=-5$T{<$Hj)#ll<;Yj~0mB`R;UpHNMQMI6fs5`Em~eTUX`SkluY|?9?^= zL;MH{aP;|hql;J2N416>>Sj>%!C==tK20diIztH${IduGuK18^)K0(9GvV729|~CE zo_!d5pdRtaIit91kX4v);QD4Zt^s#gxa#LhYlMe7flGpG9u)oviw?JZU?9oabCNX* z$(u%VhS2t7OUI(|+tUor`sLAajbtOM`j^LG^f$a!Sv-8a+(w#Y)J&@CeIIw~GE|kV z@?v_>hJiQwserc5js0E847l0kDiXc)z9!sq!@{jlJT;kdcOtM6P9d=Bf(uHIGm{zF zbPB^{Q@YJkKlCxIRayHg8OB4OT&e;)y&zXX1Sc;~IPPQ^=Q7zi7G^|TJ{Y3s-n=|q zHetzQk{Z_=EnU9^HXTu?zcX>Reb20 zmn4Zx*8}l=PYgRI;>Wfq>%nv#Of6;=fR^j8!HCcJ3lplw0Xc2eFDj1m0LN@|^FgQ% z=HjBZA}^s6sn>>rTo13)Ex_Eeh43}8=}#Sn&_0zyKiv|6Hhg7|5bA{L4l_zhM@Fbb z3r0EG0^!n;5C~Bm&d$T@TIX;a8R`$)n|u;9&T|$&<9bRhW=g30DywI^#X?mT=Bhd= z-#(8n)0abaDsH3^nqP9Bc`y*}Zqa`@gV|@x?>7^GLi6?^rewoB@u@meOF$u5WeV&^ z(b}pwO*|qgax&?DGZJll+PE`PF-lXB5+tq?`H@tXI|It}-Exapukg-OY40A77IQun z|A*C+Y(7AmHCR2}B1O=FXoXh=KWa8?=|tiP>hO)tvV6qLqqel zO&F3QnEVZb29{Xqi=}%2K-QwqF6NRn=kxf-?N-l)T4^@Yuq=T$1_^R-)0qMPV+cG* zO~iXqY4P+i|JIiV}-74M430s_zhs-1Ru(g{Ey7~ zdDuZ{`Q__>Y|v_j?*6N;$2X3&LcFb2JBynbLtj$iFFqCg#*|7mRO*2!Bz|qHB|!vv zQa6NfSWm?_3&_v{`MKItQUZ>QpT20R+?}ZY#NJBu0rE}Hx`Uc+oY>4(RS4O$Ez-?* zmFJLfvlOZqwrgd^hQ&cSF0^*}dWe~dI%h+0^Sy5K{LEOsER7qZ<#yeT-%eR!}3*DKs@MK&!W%aHiMrtn44`A>!s$iEKxu0RPxWy6X+K3}_N6&K+f?}e&&F`0goKRnx5js(RiP1p>N}F-c)W=ErpO6EXv4PUEPF;q zZpR|d(yA?fiWLnzkwTq)-dbLh826`sM->}l-zsiM(IA$Ifzl>_>FRl=uGko>W;Ncn zB?+5C^tv!!oqL@)VF>l4)QsR2iJ%0vp&%NERpGb$LGui?sm)8n4M_zI5TErCdEY~e zbK<*AdGyk8ZWpRvjs#(0PN?6O~NTTt=1EFhUEEieZLK zcozwu^U$i4?Q=nQSuzRq@%3x9m>Mftwz)Ie-^eje#?l)Fpw9I*q=X;Zk);YMsdZC2gl!LYQdpZEoNm0NJ za&s)>?4@X6%#5Zy0O7_;L?nIZt^i5x=QPa{hn$!HNC1-wE{qmWJvY7#7J ziU8bZ{b=dDUb5T)ILApzvle**%`MCnTxg-lib22K@>C;Io+Oy3n9?{8ut}QjUur|g zsaQSxaOr|7S2OV;H9ozZLX%QmcDr!I@olP!o>q9uJj6r))cKJBoYzum{p@8_8b9>z zS7Y(VCKg$G$Q(hA*6!|p9h!JZ>y`+l&kW|8AwLOo*xNN06d4#Ybjb}$lh!Nt4 zO*>_7jkqR7n|lsrpNu3I50ezbhzRKbqQ$$jTNW#O-GRAOU^?}45ZCm<+4^Fbyr0b5 z!dM)jgS2zCm3mVo%wH-?sEkgA1%XLlJ36$wdY0-?{Y`4omTICw9}Fr2;4xr0%{>EV zaOG5lIHvTi)>o=p|B$`*jJlEqi#j;eav~Fjj+8Ms5}A>x7vnT7v7ywR8RglkZdkFX zU`2_eu<}x~sxBd?sz#y8bbXn9+=3`64uD>n!FP*+aJ$T1@A0WclHGGojMA<{(kGFB z3iiGLY?VDS8Z&?XQvvnYg3@oP^K8q@MW`j;a2j%~bNAmparZsgM(c-?V`1F6Nnioo z`uZ^}gs5KY`SQ)7v+H7(ldE|xEivl`d6@Hxq}`+CMfGfAcMIefxu;?hcKA>{6yU4s z7D;*kirD3|wn|xcenQ{t`IfKQm#}zkSF8Y1HgQB%`u9y&m1N6$*kcX7x*?Y#_;$M+ zLBDr>Ix*xi-fVgjm(DxAgUz1vYuh)+5{o<86>q)xo##hpjdz~@5(~La6wG>eD$TP1+QzcmLDQLU8gf_B7KcE8p?4bkd0EvOUZb z9cQ&mU_48H$H07bw;gWH^c}m7^z3@G*7%J|LR!v@dKyT( zExrhI?3}FgtC*2x$-~8Dz{{hmUC85Vs`Akx;EaQxlEc0Chwz*y%8-7|eLT{-_hG7} zqq~-iw0BtN2WxW&9lnWp9xUnhS2hKsM&ZX;;fLt&ZjG5X#1$PbR5QQ-s{S~=CUQ~8 za+@Pf2*`QJcJu= zE%wNPsN~A{<5%Fx3*7&kIN?dA)i0>(vkR>xWONBKfcVw*;~&=SC@K3>uMQDn%UA)e z{dFV#o*%<&Om6Rc>?$*fOwOka!li2V9AfXCQg*%3mI&Z}5-J*$jDDB+EX6}UTnm|s zIB0I7GXAip4S~t^Vl%0_JqHzg!_O&z@v)g*BTcZ;l;QVoBUb!z}dNz)hf8Uy;~)#{lDl zw*IXHZM)hmSMp;T7h1IL#E{G5V?HyvtO!uA7Q7wqtT>d!bJamt(0BAWdvM zoo+<(RZ@8v8Knc)V`JNpukP5ev){NEI_EcRK5Ly&0>GE9|8pa?=%#@}3M7tQHx?WJ zQVPAgeYGRUnim>s>8?Vb>{^*3?}*ivh`MM?+&g&mpzuO`h$=9mn9-`|)-GG%l*qO% zZQLRfd_Gh(wk>+0XMT|JQW|%wFqObZi1ZvuA1AkQ2$NFMfHjtiT1gA57|defmn{}o z+Fyc}f?a<)b7Jg zJXrPPQ-yVB1V=v+=1n};e3#MuwY1^7aE>Qof-;c|V*)^DHxo&(h4KL}{WAI_U2 z#r!v(fK?ZmbM4;o?upsaPIHb`SSG(4Vyk@&zp)TkkA(!g&jYG3DI9sfL{3yvptixs zhin45i+o1fJwA$u zsPgJ>z$>Y2^)Ek;0QN!^%N(or!Wu7>Dc(-!_W*K|ee6UYrGf2b}7=1o9ZN z9>2!(^fzAk7&f+{{!A-B`trWZJ(vfcgCBRc1TAMRvS@ahN?Dlmv0wI@X+%niprrEQ ze*6;h?R4-VFGX_In>zHD*-3SiNIH1Ag!dTJ)X^X?x#Q1@WO2*yP1*b+bjecH2n7s8o+yKmd)6A>ciTTe#J|7yv=quNiX&IENbttJA2=MgtvjyEIqQ7y zhX0z42A~%$5@)>G{F^`yQ7H&wbp|BL%1x_jhK_HR>`caWTVq48H+}1$IWzO!_`}5C zKG9ot^P`P;^FzdPOr-L?75A#~_S=ISW7Z?zg)CFGRv6L!Ro?wJ?!F<^m>wDo2j10f zE~3y=IWzx3Sc2Z4_kCQ#XF=|17jfYSkMaAHhMcu`wUEbrch2)x86$EG74f|HPtQh8r{pKHaMEVfX!_tZ0QF>4G&%kF;VhvksaDr0S=nK`$(pFVi3e-r zP5#3AP9Lr5_wT)8Z|A-*0Z4H%eETxb(hn6a;a?+=0D@gpmmHS#Tw&<3;y@%`$Rvg? zWreFw1`aZP7t`IP!w9MpyGoV&JG^9^i}P;qfTupFd{iw$^vRG>w(xXO4;10De2Mh> z8ncyX z&MiB<1VHNq>>6WkuXoi^`pc+gn3%nkyE~4&1H2^dhX!JMy_6__qe*YRb#(1 z7md%sneIIbD zGiqUA#Rx&>j}q2YWXPqf+;FbgY!aM#qSC*Abx*WkgTjr@E_g9;Oro8+iCJ$x=W1Y= znU1 zG&A7~%`->6H1OHC^&eR*794lQ{dHV`zqlX?nqX>bHiMfZhX5@h6ZW7nfBRt-oqZ}D zTG99<1^9S8@@n=Kin4qy2}&_K)AnF(abS zuPb+*nGrbL#^gS=$+gDL<=IA8>Ae%o2P0a`B8bM_Y{nWpTNVVUSY?*}SgJ_1nh!c`#q}o3AAxQLWYJ#(3BbjafNvp5t)C14{ zAk4+iFut=SqK>ZPh#B*;^j{TR$@?&lJ=j~tGHd_?uo;||CCVY;X{?2bS{uMJ4XHC~ z-2v#xy%VSOXe64aX~pKlTR*&p*Yrd7D}Zf8-yp=Ox(>~~p?)PA@xn09VUGMU?{tTj zXo%28(t2{fJ(D*Ad7}J55sjE0Md2ByAFN?uc6!~hDowQWOTyCG;wim#JMArK`5m$3 zqN`$KW<$~47*)5`ww2?Os8z1PKloI@cR~N`wV}{$Vk=MSas*qG1EEwx0O+R3SO3*~;MG)2LtBvkrlrZINUG{cZ%v|| z^`IXv!XV$u=xg^^fge&wITo*M@!(9TOg|gTb<@@py-Q`F2sJ+k2VPt}HMctVrcL7D z4hvY^n!cB7<>_CQweblGQ4ghwG`R*?%_}UEzKyFNNN+ZdWm~O< zknk=Jq-WQt1r;M=Y@bYu8ZcPy$XwV52#9B@M=D=LwK%#o>-5s5`glQRe)hJ5kly$; zwcu}+i`qUZRN9i7^+knDl5CofuZfo&JQ-_MB83M?hYOqvob^=iTzrirMh$qd)6?W_ zns*tDx4pv9 zIrXqUM6+bu%+4XmVl(vDOmJZYjn+t8(<{71Zj$f2XS?5Aa2fd)3kn(xDk*WA~dzZ>SDolDhP_BIZS*oKg2OBs=hzo0_6zwS2TTAZ!d&kpP*pTZA($ z6+7!A=zdwqL%vB>-E6xuYF7;;FQw0&0F`ywR7XRW6M znME5v%PBViRQT477tOt<1eRP@e%hTBIVDO%CS8n4G5Y~qBca-3rkkS1yJX0)2jj({ z{bw&;Wj4(Mpep@Rh$tyw{4Yi!M{N^jx22S*&Sy+g!(;F;CVXMl9ybX&^ zHs4F^fD*q9Q_eAIBD7~ft>NyE%9M_R>KCi#BY8M{^xa8u*bDjB|*f^V5+M=Gojw!SM@iJ^q3Y5=x16+1x@sDoUhYp0KPkeka41@W2@+&X2o6a@^Y z-y{}O){2riy&3b42&@t49UEjsyzkg_Qd$j(id(Gzk4*pRS@=b4V_ANpDVhZ(=$=%U z8$WVyuq93~SuNtH`UUlq-rJk;uH2R{8=l?YaEuInK~tu~jB<+f2z zZAn$mR(=>pR>1Qw=^Arf!!Xzg1e`;nxLtnd=U;lNHjq=^BRaa`w73D4S;IAyxLZ-W zRu+=ISLT_9T-7>~1vAAAgZh_wW0+x737TP0;L~ePoHBx$0LU|hs`^5=`!PkH;YoHh zNshW(^$SI4I7XKQ_0W4CAVCIxvmuQbr%}pjttjY;7#$y7GBS!AGe$2DE7)RT1_U0z z3&1dMT9!d^Qr?tyseToEOg3+t&mDm%kpRf~=fPHj9%E@91zDBq@d>ImNU|Q$2O-ap z(w{Yw+51f#Lw_9yhcSTD+14w}e2Vq0b;;~sf)_CQh)M4-40nNXK?ee=@1nMM60N&S z*v;c#43Sz;mvepJ5s12)&0_nBE09h=+TyNuOHaKr&?>tl zrl$94V!aafyt^+hgf#s~N~RseC7SjbE!pquPyb%*^8HaFX7k~K^))YAC*2Ixl|C$` z=TS8J(i!k6AU1HBtZPEoIFIc%5Nf4jsZ|?bKG@CAa*XDtCM0aCU;zqJWV=A6HF_an zgO5#ZnKeuu+~OXN4e3ThZf-l{b{1r8PTwdoWY6rhHuQ6-ZJ~5BwIk6;*|Fq6hT|_E7e(43gUgf^#(gjTyo+r%nx;%!Fe-_5l$I zT{wM=XtY+4o_REpf5Of#<5*vtYBqR?YUiH0sT(-Xy4^DQR(y=FmwnIAdLBRmbg9^F z)fZ+Bt63_lNMhDvO9#jPn8W5(sbqBGlzo2_Jp2Ei%kE155!YtBSv`xpGaexAFt1}v;L;N&KK-$ z9(=O)-0>vo>gB>@M6paB-?vtM)Oq|~9Qo4_l=gwF^8rO%k0|KoGOYCvLYt$w9!~9p zPkRV8j@E@oAsfl!(KgLDHN3~v$HqSh3j^H0M67zJBYZ4^3mGqdeAFX={m!Dlf4x*u zTGk^v_VelwLQ}jR9^5tPUTifNZ_lyJ zTXi11T=T$jeph1dv$CUKGA}(E2~{`84P91K?nfft3s_tx@?%h-b@g@XN0 z=a;g29`2jUc=v+(%N?v20{RRrgO|)}QCOVa@%UtFYO2#EB|GxHJXvG?q%33yo)eWd z&{X^yD6LPw<-NAo8OCCr%q#&bD5B}>C06NBHi{`RvWcDGTAP zoi9a!m}jo*F;+U&itfkhr6lOP?N(_7Q~%IFZUrD0w{9>e&W%Tl!|4~5M0iSZI)M7} z;mN>A`JGgF$hLqaB>o4~*Xr7yz0;T0H8M%51;$9a08ZOTG?cn&6z(;mUT<@A!7G(e z|EXfC=2os}Z4RXoE7{KqC$F5v%$c|9(HM(kfKPx;`;fF72$6HhK>2(9UwP_ggK2>h zzqd@^)XP3`3#(Iq#SMaW;tCS)QpaiqFJ;P0RqB?PgfK{xX!s`(B2Q9;n}r1Ewh3rRA}l$#PT4dLfXYx@V1@1%95@t&S(!a^qC)OqkqvF|Be=ae33n| zUjY!x$sxZ9#8UyT&_7r?OvBaxAD}|77sVj^oyP`KMo&OCIHhgPjU|gVB)um7uJ(ZB z2&>)7U-A{{W__4p#y0x3lJ&Zt6m6dRV6`A&a^cd3$HZvkVJUZ=1@1oM4jP|i3ELDY z-jDlor&bqT=HY>QgmJVK?Am2|N=O+?cs+CVddEuIEq@iV>66oYP&v(@s4bEtNX>N!(Rz}B1 zUrC%@T62wrOvu6E9UMJm`>(9)d6 zF8KT0`O-g=L)C~^vvo5oG+UexHy&@l9#g8Q8(s@HfzUiBBYrFk;S#7$wSDjO3xp1i zZ07N>n2$_(62`ej|ER5r45|F!bi7vBy;M{8!4BNj%Zj_`68%}8P#2$58(#f-=>wD) z3*{;3*S6j7$lIY`&R7QC1|HH8GL|P;?<1YMcj@balBA{k^W^t^l;bK-2hNEB&cCS? zhD4Svb56-VjhJroD~g)B_^sIN#dUVus1Jcrag;w1mR1LlIF~RS<--q>& zsoCuX_eTudzkV@a~9fLz)%}tFnCnw>DlX=EZ3r zT9n{_-LPCxMa40i2_6#gx%$rj>}BBuoBxiswP9rmO+*_@47Fi25Ul$A_4*CoYgB9O zSKq(Dn zZt8>Z5|u{H(&jb++uy-#of_36Wg+3>68OxD*qe-6(09*1MPn0FYExmdx*aZ3v7jWv zBZ=MfF`cKqvp%bu1UtlF+}Fwy*=uw6gS(OOWTHxjG#2Y2up2xAT>UEjp>?i|eOdDG z+49PxQkI*<`dOSF)aT>?4B{BHSOZL3a-#Hmqq5OdD{_b%hSF6W$40BCl!8qDZJ78E zrs3BZ2pC`ibT*R>$P@?q{^`-8c*4TlIL1M-dth)2Y?ILDjw67|`9HOUPb7jSFIv6j zzB%nyS_21L_eF7Mk+U!PhA{Za+GgbikkEax$Oqxh7y3W+6pujEOQ!=O+~d`IUS_sC zO%>P#9Qydc0h`9RPEMEgV7kq|MJd;wl3|kk4K$a$#pC&eCNk@%c5Fmrvq+Ba=8hP& z*}KY<4?RN`t-8flg!4Fy;Ey~8NQYs#rDc=a=~@{?p<_-?Rb3cX6Ab?R?|@~ zKY^6m6YXy;PQg30MdI*32!9YHMn&yaZByxr*9)0H=`pxUetHP-vab3VEX(*m>q8f_Hj$0^ z`BEuE8J!h91mmPL4EL!u1?&+>8DG&1n{u+O zqxnR4_wU`BcCK-;2**8-AED&vf4Pjih1 z?xth!$U^R3MQ22lXhH!W5WGoM7Uf6zEB%Poaa4QAMpfq3@k(d2LhG2zu%h}3Kk;ch zxP@n7?qzShRR$ot57K4jf=-ewoys}f7gbg}xddr60hEt9GzL*+NxCpy{Y2F;_66x^D|hsyUm-JrAjim+;{ss-k~+jUkkn2GCkjFrq18}+CBr&KH{yZ zkL|(t5OKuoC;qn`bC30Fi|f_?^J*O}TE!RNP&@-jk=EhgJw8OC3D6CIW^q5p60hG6 zmU-^>r&9aGa3*fON@$ZmyssJrKJTKOQ*_m;$JiSXtovHfw9U@S@fC(8vvFdN@(k~R z;n`?#Mn)0Gh3A>7RKAU02vr(UY0}rk5-blz^^>9e;8=)$kGv)~rJ`_jU0chnv&Oqq3vIW7qg6(SI4Z5-8lQ8&jSK2jdE!Cq z`9o?v!wNgIb3@oQXyejqyg#|x_L@~#DpGr!l&s1W@em65u!5IGHp7|emaOm*#1?74*=!-(CDr)uhqiW7HDKZYShK)yMQ*b@|v{csFqso&R~@E za^9v;vvGY@t~&TAF0p}{q(5Sc{v6QLx4{u5l}06Q9_6DJ5)wl^0$|~%n|I;CR<0bP z8fYaAWuuTHrF=zpqHguzD~3{WwKL}XpRlcW5H`C)( zYEGqILpdd7YwSEy6VM%| zQkD*wuRu&X>)8iqb!n-xHB_A3R4uk>w>mtj&SJbf4Bukwb;bWkr5R0S81e&vCm-{+ zlPq`+Hn)9_j_`SV><|5TX5~C1FuZ_Xt+HrkDO*3EeuBE~TBQgB`MOCt3&}gM>~?9J z1?01emH$CVz0eu>6tfU}>wI{367W|eu;~cb_WvSgJS7=0yG;4tK%S7EVNfxhH3`7= zj3A!1zFTQWy~#EI0dl-;wot!a51Io%)9tC$e>^pgL`ChX6&MI|uPyO&es$RVjMj9j zn+G5RmIS;#?E;WLl%h81>URfqom$n(% z7>jGBun@4pNOkaqR+fd%IBJFJ8y%6n+TSDj9wEHx$?FZ{cUZFZ$2nX#`_VK{W2&L7 zWmJH1J8~DjhFnr7F zmbYv(l$4{0EJ}P|s4#jF(eN$6E&(X-nm-JqSr5F?@9$4a+sN~Z;64dn%m0;RqO9~E z9pdo@WbnI-em4Z<$e(Tx0)o&BLQ4Bj+$>lPZq+@VJ4t_V@m?COILnit(Nh z{+8s>yPVOFSk&m#TflWqe_=-h;xTjTHJLuH>0o1T$(o`sZ$6|idLhh6C&9#rH1ovd z7arkQ3t$;mtLVcflB?&f5>!xNw-Nfifx$-Jdy_}u|1uLh_bFeh>!cJHpe}q9m%@)# zOtgv8V`}w8J~CPV4SBweMOb{ucObWT05&Tf*a&fRS+<+Y*wu}$p+L4c1M--7rsI%2 zOtoC8j$MwIm5SJY{8IN$!JQ^SY=Ih5O-vL=G7_F~6-HD>y+olcZ7is1yVfJ3kb2(o zR9}tkHn0t4TCsOgSF+#8^l_C3=fw1R1FF}KyoR&fbV%+bXI91LJU#;V-f?4D*NhPN z9|S=F3a!E27$3V$_lhGSdH3x}PLZzTXSRZmexy{Q!n$;z4!I2R>yFra+qW7Pzt4ZW zz5o6Pp}qLSEK&-W&Mp-kzJmA@)0pQs@O47Me-V`n%F}wD^Gr`0En$ZPyXIw?hWey- z!SbTaJNliZD@N^t+|H9v4uJqLj=M3U#Z-^(jL7T*vshO9>s*4EV!X;ON4!0B`5?*Q1_?b8si*NUg5{)waN*!krvry)KN_@ zp6Y~Z_S>c17qBN^ZZvEB5IAr%DbX)fy>MbHbGPMi9hAZW~S+?VvtV zG*jZd`U`;*-2V;W)*9)O!}o2Y`F`aV^$-ejD!m8^(r=MucUsD8{IT09R1sPm+TGdl zx0vO`QMzKO$stw!4?>b~GZM{QONvx`p`Wb)pA6b7t2l-`bZU+uBlr}mStNbidr9jv ze>o>h6SuWHlleqJT9}G6WKEyPId5v+pT|(?#Z$?{w=B4jvRqDVBZBkJK)xmS6 zRmXfT<;C4^&nBhPVEaV8)>&KB=rwVaI4ONt8W!W94d>0}pr6pZSZx=T9pV8GA?Ck& z{W3%f5$2a_=Bm=h>8gE2hl%rH zp8c;gI)6Ut5svG+#m{5-FAjWjvbEq6ArVvytJphO+zQ%IP3U(|*(VwF#H&I_02&jz zztnRwT6zvY4!C5x9o*R)e*d&jIia^=_{wy6_hH6cksp?0h{KNsd*owB!*i?Gef*9bcwI;qjGTwW z1Bb~=xw?ri3#h6T<+=#Gk_iiW*v8?{-KgKi?@mU*_nk~|aN%MR?pUY>5LXXEjiAya z*Kx2WT5g%Z&haq+OB@0e8M0Ar!-?5!nHU;~D28a~^bSKG)Kdz!sX87Zq?;!+x``3LOehU6sk`>aGM@MyA&G;maMmdu6gY$_(vOZF=&x<%QUYeveSm%$&yEC1ig|c+$&w^)kO0PQEX!`D2%I6(nxtB@b60Q3>GR4 zw6G&-J-g#fE1{iv++&1fMpcFTaattOn`zDf8Pc!CU;}M~k`u70%=ZtZ7P`JCA4v{UFpsN{BKOYckyCzxu+@ zxMoA|0GEI=C4a}rV+0{iS6rGL;2-<@bzA2oyN5xBO#4B2C&CsS`|J+QfF%ib(?|K9 z?y8pMjC8Hjcq684$dad1abngN)ljT6*Q&@M7S8GyZH8p>L1DBfUTh4LaBKiZUD-92 zs=55t`gHz8Pmn-r8-U%c{|1?F;plj+Xc5#?Xyesm3pWdKAc1{~TFp)EKfupS`;4U9 zU}(x6CYwCsw4y`Oc59L=rl($)6g-Ui%NT41oCJ6P-l{3&%-1B%6gJkGM4Tm6mDPpT;-6nW-v_*XyMZ#gYD8j~GuSmX zbsxa6hy|gsUEYY0=~`pV-?5VZ@CLo=y~tXPZrO;_j|VdWRc-+!W;9x!XY0}CHP)*2 zm5TZy3WT_f;tRKOOT%-Rllt@HTHuyrX>M?9z@wQqnzCI6ridb2U_8;-d)734oSPqJ`!LwWzuUjjHNOlP0AjknXui5jCoF zL4FMOEjQU#YT6MGR=rl_Sy|Q0`-7m`NuEzvnx|VJJIgB?3Zrz?A{~^+5wHjs+=C9X z;-VOGzIRoGs?g3MW9~@o3t7xo>Lx@wXSo&lrp=TQMdW59qKP z=1dJu%Dx!Kv}_}ufShUSg>VnUcA1Z4apmWp(T7faBX;s25Q;r69XDrxz}<@BrD(?w=2k_POB;-am*oOr4Y@Eh zhRT73x+qemGrPi%cm|UcTZF*--k3ZueZ0f&a0NbAF}pg--ULwmci@f-9Ysf>WZ6nu z9=UZ_2XbU9B~=L6Lc$2RP51oOp^8mTqTx6UNAG`7sXX^=!j5Jpr|VIip)Ml>+~pFs zk}yDu@2rb0jH#jvhO9y=T^gMxgu6HdGn#ME@7TaMh>(a>ZbP=0sg4TWpNDtB4lSk3&8Cgf=t(i!WgA zUJk8tapV$?a@|*Uwbf}XtPDEfKZ|h7@Otwu2XHASYDR0Yj=IF(9wo-;SJf`oE3a}Z zh4BiUy=C%d98@r1Th`}6z&4;$8P)hQ?lhybm!9LSY7nT^oktVkbyw+Q5BWaMOod(l zpDu%N9Am67tS|QB)~|Rvo0`^0Nh>;lWjR&JEzyQyKi$5pYwzVnY6yyBVPj~kzFSNi zSMSDi0p`XgKDH(3uKC_nGYJsbS+^I{C_OLpT@(u;W*WrH&HG*zCF(#DM1VfN-C)g% zdf=+-U3io^R`)&Ixm|G5?;>MYbnmY743UKjDHUC}JvQVx>e~Ex6SDtcyvDqudS*1` zm)_=Ha$EV;SKqekI41S8nX={qz9TB*+9>v$;}Pvtl6SnHw`{zW?z>uUz&FCs>c(i} zECXg6FP1W%ch|wop!w#CsEU3U*Oc+*>|AC}M>0@h>qDAV{gHH9;KqH({;>Ro!#R2hLr|1J-!1(2*%)Pu%+Er< z7ER#TUq7CmxG#9le-8vR&wV@Hh6gq0WmpnviOvlHjs|TYFQ2ebHSR`HQKZO`N8~=o!&r#f=hf=XF^PCFDR6AOn zH#Jo)Vz1+4neApvP#?jGdRWIae z)aX0M!Fy6tV-@r~1Mk&e^43x`)>(yw;VkI0YiZ~(%xg55ExOW@KP@y5H(Fh^e4f#8 z-Ui6@%nQT{%X)z)paK`W(Qu9{)Khr?xl-3CVlFx_=X*$U1kQ8G0xUM_8}QmO1B6M? z$rK;T5tk+;g?UT_Poha&mM8y^YAo{S7pyA6S=1a{6kSGq&lgMDRh&9Z39-XnTN<>C zvJ6ueY4Nd=HLHnCuRF-Z)x*@*IPCjK8>Bj!=7EA*P{&TE&#P9QAj_Jn*n$Pu9OJ@H z1OQNlJ!2etm(3apnV>WI!pdTdli*fi(^cpq(Vn)60`UlBx;^qxNy~=E=cM6;C;l8= zCDGD#g(hbKz<*6r_*dM*%+&p)Y>FaDQrx*j2YbF(OyxAL4?NBMOnC+mz9O|>Ig+P$ zCzay4=4lsJvRQi}!?Au7sD3?VlFq})Z6SIOnsZr{0RK<>uXai#r+Eo$tWE;REE|pA zULR*Q-v333!TwfJ0xTAKK;IuT`U_|Q%FzGi-MM%57Kv=7S%gZcONwcHxY|6py}CU% zY8;~xP|}Faum0gSX|fghDtt4yZzxaHI2Z1y?(C|M4YcN{&c6>J6VGNR>g8T!Cp*px z76|u4zBL{)oML0^{A4&n*HDKRO1vb~DJv8WpQI-dZ zU%Q}1xdrMlfctEO-y3)EdWH0%Ity}*~eOBwEs_Szqj;Q>#3XzzL)@BC; zMdA7UD|vZLvYO}Z@NW0@gSLFzhG2LJep(X(R}Krhc)~v3PGe2%vhxcZ$i)?Z?(jyG z+pR1J+IpNFyGtg0XBsH#C46+`ei`R3AeDr#f7=T>8uwgSU?0F1Wp3Tx zTni1bQSSoNWU)6Co6$zej+B=CsJ~Kldp2G`tgj7@S+8CDcv+PoFiFGUg+>QLw8GtZ ze}Pfeax(9}AYX$`$N50nqk0m)QB<)ubk-AIzEhBRh@Y@V>Uo*LLRW|y?|5NPPK%=! z4Xpz-ud)P<+F(jI30iAg)TnLl|O>s)5gdG1pfgg{pXJ3Sg; z1CFZtzxpY2H==xxCE^M_vD5fhdQCVxn333u8C6Y)pE`IHj_{!FTB0t-6DF*4$EK&D zXRfoly9hCU=)Cm_85J9A4cnl*o?9x84L8Jdvk;*ALp^;+&&H(?OyJL?xV)a&ibzEAJjGIy~tKnub(k2W4cJ{JDqMskh!kNNaw-uzTYd6ooUk=Qx zjpjIpGVVpUfIsF6pX7RrB=QderE+%~=sdiup5;>?^QDS^IHRER5+AxqNMBr73Gbdh z&$tonuJW-N1uQ=N_r)EN{r=T=waLGAwjBphH^9uiQ4h>=Ck@7Y|J!tY7R`=}VT12RX)HvjwuMeQGb4oa(H!@AS7QHXI8JrPG5Y;lOnW7s~ zgzvKB-CWn3PSUwoJvsnM8HZZ&{u6sux=pLUr7_^Z*F(r|nMn^S`C z0c3ti6FFkkRuMaIL-C7m8Q0Yf*A62T_PzLvoly6^T~Wg}YN16|C`$+@zqph*<7{%N zqNB%TGLVfqTp)ZeL^qNh*brh&qTa zd%u9z?DFG_bq$DR8o$ZDPb{UJYh$oJl$c1{9C>w&kEo!tn=YfWH=~83Kpr41>} zN;w=Lg0>p$l_yUBB2(Qv(w>;Dp7h9H3ozl1ev0H>4-z;g|($(ce&_rNb8 zZTk1Z`{e&QN$c`o{Fd1KN=1$NZ~eSv_8*!5DWGcQUaUBVc^(VFGa-|mHYWAe2LII; ze!mCSAK{I7*-QTuK9uCY(R$i2o%d*dVj?=8DqY#l2*72xPG0XD+R7t5mUpmIzhj^(Zs z8D|`wvVbd|NvGa=;1r#WOU4wUQ^Q#W8EI;;umEE+RQ7+$gH^ncX|b_h7;77JAT}3! zPBS2GWAqrgdTRB;w<^jWQ;_ZCv6t~xtqK0~%)Yu_(tdW)tJ{L*L>e~3#9WW6-FBWf z7S_HclMM+4G#|wm?JpykJv>926$vMPcp|c_h=BE{$(?4t2s1hvhLpQbY$PX%W`!Yl zWdLZ%2HE657S;!hgi8ziRrnI5O<=dJXZhT_HZy_Tv*_x~fO9WhCxJx;)L!y@q(=#s zaPTT^PTfmE*}5~^&|xA1^-8qUBsr@}^T7Iw3|(Yq^AlaKjS06a;Mj&F7*88;GLx;) zSH#q5lEt(X)vzuF&$Unl34iicH(_TuJI!ihU1dR5hHuv`6 z+`rV`YYj)OJbr#o-@nL3jc*I54*1;PRF&tXrY%^i|9)0Eu=Zce<>jL)K_pmCbn&8A z7DV<3p@h~=MF-QrtSE=lw z9(7E zG^W_Y^`~m-jJU)Tp7tgazfCp_CsRHCdmAp*99`S46~w6>Yk6T=%3?lG+;9CGeF3g# z`nukI)>};l#A`XX@~q6(yN;8)kIs3ANkS*=ai;T|m4RS}6X6Jxx&8`}Ukb!<3(XN% zEq4ua|0b)b6FI&pccX#56j5{OLzKS$>A6 z*6P($Nog2uC%V6)7Whq!P;CMpc6x9@m~0vUo|J;A!*GetTno6SN>T+})mzppK`zfg3-a@dU5o7cI&jS+R~L7>AaH<0TieU1+Br-@tizv4gZ181H4$ zjFe;LSs8H(L`zE36s29l9UQ^s+a;g+no1wG6kF_S3m2ex2|AF`pM5-(_A1ZO*>$7w zU_-C>;8Ti=FT=Y!EDZ3JvNAhS)Sab8KZFY7@(WZObCKA4+Il{&G#+<1iAktxYU3b1 zw+EH{bFlhbAqYh7dzLm^y*3wGh9DR7o)v&_;$|pi7G_kuqCxFQ` zJFq^dh&?n8_KrI}J9XM7$QV54w!P1ta?xqRO^?Bbip+c{sWc!iTd7Bs^D=oT4x)g; zJ3;!sjO=DFL`is`GQ1aXa4iz_KyNhLEljy5hy`Z$dL$KstGdPv^vq=@j^j5lTxeYD z?L2!g3ckit^}h-X)_u;Hv>=zz0>W|nan0aBMDltqBVnzO1@69AwYyr;1bg*0(U(e+ zE1+8umj$_nK1T`NHKh^_4WS&#fe@_JWAzl_t2!W`UHvExMZygTYC2!=Yfwy1ys~UHy$W_BGn-K@;f}cW}QGDba9(s7G}2Dy{7miRmzE)w2Z-9kb2 zLB*Yl)B1}h{{f2<_=s)?E|r!P@{4AeH1Yv)u;0 z&vXlNcy1OZx&>4DWK(NdNV6*#vK{5ZY3f)vcq@q&-@Q=Jk!#pATV55X*HnJ`uA9NT zWz1kv>}yN6)Dec4x8OM~>lmN;8PRwC&zsb*l5ks5V|+#+m5Ae>#0yc4FH(ePGvCY$ z4;)5B5y}@P0!oXz!-bv}G#3XdbRkd{DZSM~ghiZ}oc=!JE};#V?1bESN!z-L@B z4_BWaIQMJbx%nJh?^pH*fxGWWfuijlr|M@)>+c)a_3OX7qKdSSYr15Dv_hL+mE_l) z(fX<3)Y_ey9Xf}zHfV^2xgPJRLeg%i6w|^W(_IBbN@iEA*mhx(0x#bW0r^sPLz7k> zO51jYhr1yp6*_?kKeGD4e)Vo4nS*LTiIEsyiEL zhjnVB9(m&*CHb?Jpd3I^sPr+A%UYLb1$(3?plkC!AIw}o!bQjZ{y|vo``G+9u!-lYY@SaS4OeKoJxreuk-4u_=Iom;To+}!&dw!ByZIE`owu3X zEP)?oS(T!s9qpu*msLMKR{DU=k*IZs6KQ;?L4f;?>Lrc|%|FZM-xcTSpDpg^AB2CP zvFFNgC85Gh&HsnEw}6V{+x|T}SbzY*8h3XKE+M!Dr}5zK+IUC^uE7cJ+Bl69+}+(R zxNDHtzkA=zn>90Q)}8;%yrECCs8dBlo$5Yk*WTa#*<&S^8nv*SQ6W!l8U9Hn<6L8yjd5B*mvKIA;DEB>$Ig z!T%uM4ebbKt-yS78X0J)W#X17ww0nsMu#On4m&6)|Ey*(sk_LK zAWReZPZoXbxc((qO8BS8!8fdLcA*HO1} zX$$IuVwSMNn5x%kB<0NdWRD8J5Jy6P2v88wyS2`EflXmbms>~gIc`t3x2yopyKxqd z&yZW4>u#UOT+?B06GvQSHhkXRRf zpw)z=d1tf8dfKS%&SuBda(2agc(H=zI9W@7S$S181F$>f2F5jn>-&LCWEcKb|EXk;^=jN`Uq` zUHyy)EXHpelWMROLfe+5G#X3q3~XkoNIh^gw94#~-&IK$r<0Ix*@PYljXTAH$e5d= z)0^2asA#J9hx8x;}xwpnDu$*O@ zz;Y>rGyKd8e7|(ggs9UR2^TXZ;E@$eIzL{PH$@)F^Nvb>t>2?{FBuzK&&>ZDx3XyB zS<`Ejn#ZTz!#VH=&>!he5M~q!(W)@1RVEysR^H1%0h;iR%e=9ga4Xp3^B|kca#UTW zU;B%6Am0{T*Cx3mwQ_B-=a|)gt48>j38G@ft($ zYZ|kwv5PJc=s`?yS1rkFW{>GmJ^07Bw&}qTMy^RZUET;k4cXYo5hz_^)u~wj-9Ibm zZqs5hm1{rzt*@`bnq~D1YYHK-T=zI`cxCUT$lV5MPcmRy>s{Yi4GLuRi&Q8PVw?E! z$(i$=BXgMw&z{P=&-!da^0RpGKxidmK?P%y>8Njnq^|Kq^c(z(JA$+If4O6Q%+)f- z|2QwEN4RGL8XlI$rci+NL8d@w9LN*KQAS;ljgGAB}U@MKow3 zq=L?!utHMN-VllXggagPy{8Ro4=D) zNeW*RT*3B|Fynlu0`$R5;O&101pRwqe$oto$Mel(bPAQNkTneQeOY7PJ_eY&m}GMrNf6t#}V&ggD-lqV2uX--OB2-YC1%m zz&vh(Ds42jj+pIv4A*)kM4qWh9DJ66x?6P8eb%Kb$|f5Q+JlDgEo7aCHYffV<*xsd zUwf~?$a-$Gb9M4QTs6JZe>H_#EVJ+Y4_Y=){^YH+{au4@oRmm0l_`E;PD9diCblpe zu-8lqFec(!XDBpy{C$fKK;rX~y+AkiJ^+TXDesordoWt})*C+g$ni&-Si79fd0DF0p5v4>1i-8BH4kR=+*XkMi6iVyT~iKwHCGXqppq$qm0kFi_~ z?*DJ|p)T%YyBTVjIJhRcyar;pwVjTRN!dsRbP6~n2`>fScEdh9wm#M`P@>BB#PE%J{;$-y@?M|hw(h(Oa~XvrgD!|BZuVv0(218 zrnIwKTIfA;IZUj;dqlRJV-2&Zm(6aEXysqlR(y|H6KW@pGr33RgNsD0!Jpx@7u_kK zJ<@sLJd%A^a)DH+;V98D(P~g#$~5{ZXi%*o`VkZKt6r_VsFs`1CM>0Nw$nG-L)Lny-hZq>EC|X1Av;jP7y`#A2+L%Kxfp7%SiP!Jw!P(nNkbV42d5+ zu-FmGkuyob?~J{D*6ySyf02!!X0hfH0Gj|iptq~i$IP+8>T#VmM|!q$&oXXYVkzHh8f;rZ+D(CRiSymxsz#F-(6;+fSC$Z^k-8xq1RLBd%j=qvxkMjW zMrM)V-O>g8F`^^JFPG4LMokbxw@Ygo^MCh$Z=9tapFbvr9tu{?2^RGJ{3=0u+8t{R!;h!`0+e*YN>I;!%m-PA@sFht zv**5VA#wuW$dSQZWy~u}s5fFfo0x1IN90QATiX+2)1J1JeTmLghdpE7h{Ssh~JcQvL`o#C#PIgX~Omw6FQtt_DV_#i_^`?r*9R!wHgz9f*UrNlBQtH zKrUGJ&i{Va4^c6_|5xnpv30y>JS+?6Bc1Q(|5=DB<$v@UAF5qlgUYH0R-h12C4++wF)L*WV*6f2k*F|=_XTVjW>pu71m z0k3O{5g}V=Uf8#v{%m6tw&u3ocHmnJo#qyFzSN|K%R&A~vqp8*68|yrrEa-Dc->Q= z`X!?_)uRUy)wT4dle&VRi?07xe%$j89isDTxN5&u7x?{uoXgh~$7A2BQ=<_mgh|i2 zplN3}UDPM5*pjeBtkB0ZF7j()N3kr7n|veaTh;-&X@1vStrht<1cA_=zffBsC*M=n&2@1$6+Nfmi>Jr-r^J*IQo<0M~f|d z0_5<{2y)Hg()5l0AW8pM+MP3ZGFJZ0N;6aGe#sP^NsTr8=&& z=I!j_<=Cn_QqpjdCT|yVk+;6dVoeteBLc&OAK}$63ygaT(Pbl)50L~d@Nz6vLQ%{h zybA7A_9?nRzer-?6ZP+8P3b#YzZiDghTZ8V$f`{38c0ut-hF;&TxI8}FaWk?k#>61 zk?6TpRE_<|@3NkNjd;P4&mpr`%OaY`Y8Wtd%Ufob9*N(9L7<4#`>UDR-5hu_OGDu0 z^qu$(ui}tkz-@SvgKh<9wbdrHWL&jZp^CGE_}|`do^r5CvQIcHWUlay`678>t2(7l zi$|~wl2>DPqwoMdIjELqhu{R#<;F1ftKy3 zEl2P?+zQCDoIH%D96plY*{P}9mqjNn{S4rcR7`Hl(xdg#o z&fUBifD*T7osK%V9_3%mn~NV1OW*Mn==Uk&bn^<>NMO;YlH3VokAS%sGMQCe%osi9 z3XI^B620llu#l%k)CyA$f=HnX(|ESg@V{K%`EeZ{B#XMq2w0URh!gB7c9mDeFgW<& z-mvN2o~o@$>qS4QRSNMR=4;Gac}+Hc*BXuX9_`beZ}3@rj*Tp6(}K1-QzEh^-?&n0YOY{R(SHk5e94!W!UtP@r0Vt}cFvylb~yh( z15Pcz84)|bu9B;iGHm*jdlbY0&HPFBbCw-#);q&QO!ZjeTh3)+JSivwm(EGV<-rU& zta!e>#G%RHYb3f7ciEvP4^ifHA=^Hx`AtBHF49Cl1gdTyg%DT_^9ENPh2G2e76j(a z4Km4Dl)M|lEnMMK+N&RBboMqKQkX{NM6}7xgqGWMe&I!Y1Nf}tBh&9>oB*MT~ zt~y*i6Mt~6*%F-hES{IFQZ^sxTP)N(2Mxv?`H1*b`Y%82lk4qV3A>j!?dgBmwCBI% z$IbGE=uNV_{%6g3X&9<5UvrZGjD08apEaV=R=C-lc?a4*$xZ)RYt;4W3@?9IG;yu4 z@}IX7rM+Hd$+q$TxQ!u1Zi6;+hNYgp9{oJ?;_0UsV%gc7cF_=3F4R{*qzC1E zY=UKZ98spYYUXdvL3CwGhH*6-Y8IIHC%HyKeP9e{5F2w%+$n1~mXgrSlF)1r+4Iwn z%omL?WJhZLVb^p@SdPz&2CUb{d8LS7!tnl7Vt33bxK=%S7X}fBdE@1sVD6n>j|%TC z)TN#yHs}Xvc6dXW!x7^wq_*CzI; zZ&~V+`GmiHcL*8CyNnoh$x1v{q_*1v1iFvd)zh5%E}j3UQNCBIH1%tJ9={Vd`}($ zFC5jkh}rhD?l#s(;;ITbVg6okcBK#}B&}Ggc~q5@_Fmd?RpmLVljakAAdZ8+t>z^Z z`lWuFV5O@5L3g&b++NGRu8)s}H9e}Nd<$G3#8-;cy{p7~)d1B=LoM$-qCYma@&Rkt zt}2oksP9%2o2=+mj3#n#Jm-O{>P+U)gz5}tgDQusgK|!e@hzqjs;laFHjWpoj>o2& z4zcB)zv@2lQP*0GU;Q)(3TPds`9w$}n?rSpIt{5_`9ti^Q_N3TwAH_e=6T`Vq~ zJ)<^CHx?A!nm}99;jqAQfW{#Mv=|%LwUDQZKWt((m_6g}08gZ#U>qINLLur#3cA^S zv-M)}|ER?>v4&=$-hImoe=^O;%x%rInPsqtV`qcsGqhBdM(Kun+dR?0VP!!}@Hh{O z!%lkRwVByt-aiD?Oo;eSGPwJek92G8!h5Z+=K^?9g_z1%9ir7{j1PIgChv*O%*9lp z-MQAPty0;SaVH5iP=1+Iq9JrRdRw0XQeid9dow%h&|j9inn;ZJWyOK{){(=|(4Ysd zOlLO*yMBPnM2VV&?TDU-Wy87qoV?JemsyLVjzK zK)OZ2ZL=hsAo+XqzQ36tcSYcnSTKoPd6ytRq%jPhNxyXu8nqpv$5@}J9^PAkk$S?r z_SPmfL-y!GadS}1*sAcTuN7{Zy*YVf)ZgZPu=jcV`MCCA$BoNon}{hJ3EM7Rk5E5e zz1sGco!hc#5ZGDsS(^^hwBIz5c-vfpN58C zBi3`2-D+u2^$XOpqklO9^9YruF-|aGDBzFWnj zP}S)A7)L1OJn+MzbKy{Q`fpD6=&(-=P)BI5*#D`TmGR#Fhmg4f9K+7>E5P2$Y5lGC zb5}xZ1?G}0%FqYWU?>^z6%dtR%5oATWIDF+oC=Fc!vvtPa(uAbv2!rxCoE_U+ZfZ` zPu16T>`!XigO*z);VQ@N1^c zXU^!l&j~s$7fZAf*)Am!n6a-(&n(4g9wl;QvAFqgDR*#d;CBCF_jk(a>KDg3+;MGn z95+S#dW76zeG1q6!il9|KC9e*+$~ku9d6(u7hdPF-y}Vjaw9zvQ8kVPbtZEr-Fmx! zQmmN+<7Jcx+Sh!R8%oQPcR&j?)6+MY^j2vy7h*Zhg{*cz*n|%cr0@ZMs*j{_jPc=i z{D$gy&4V)ov6Ln8!;~noW#&>JHa><+FA=SuYm#eiINttv8Ik|f7@qy@EqbZ6W}}?R z0>K%cTS7)3Vmjkrn*fHIZ674~RTv^ zOCWPB_X>tfX|73)3KCDE#*a4P#KdonpisLs7d*_ZXIl_BEBEdm1~xVyFFD^d{OehE zGFP~`ShJ%2Rf*pL|77%C^BkI{F{wE2c`bJ=r@hVYc+rm=@=tA)-krIWp5Ju%(4}g6 zo2h{12IV+kLe;;HS#p-i_as)ES&ElV))p^Ce&FIC#wxUuuCzCf8y&-=RFJbfusz2A z;1oEfWhj)yDc>-P{zA-YptUP{?ftTb`_Q|pTQT!X6i3@}gv_pPE40tX@O(0St8sqS z{OP`p)&D6>?5Be0z9{h4K#&`w+GaJ3M!QCnL~sZX9Bkd@)D1^#T5DA@53d;3q9YHq zh<{6^wxlT~@SU_LiBAhfX7p>-+QS~O%q?U#3S)WSV=dz+h`lT^c)NQV3$x+-$VDmL z%#ozP;zDoVwiaEOkgDS4d@W8x4>LsCfzT^)wpPXlx1SlD;Nl<)3oRG;>|A1xQ?jgn zy(_jY1gMy}&7vktl*FVdij`H(ve70>D%3B4JD3iQ4QcVG9*lmHA(xu&5te=@1DYYE zLaVTPRTo2NNXVe2x6>2uxFJ<9!*zd-lEdb`@g-W4MOS4YWvQKYvR?nZmw7aQ^&uZO4|Su@RKpYu z%a1QW)Hj+hWL`9A4WOn9o1gV`lgmbd{t^F~RuQHvVuw*R5vU`g)>r5K8!dxpr&TF? zWCciKn5gzwCrBEMqE*T9<~CqdUv^?b+a2n@QK^ReT>K#R7>Y}vcRkyHt}bC_(x7gw zatjZ`cb#Gn5)hDAaNeQt@Mw>%8f-iz09UbL@G}}thMH9-c5O4 zy#03S2Q9%z*+RpfozzTbxDfeD`@HT^!i4*(SQ2YK9Xih%SJJmmsLPL&PY_5BQgZX@ zJa*@=8olBJn!TQ$3(cD{wODy;(r1?PFy+M2Is^QSz)j{Ph;&I5yT~Cc$?*bbkptLo z3L%w2@nvvKJ9#8cc(68jpYbfLh;yRjJFz3zd7)lxUpgL|*W>(F&H3Sq;Y{bwL}{aI5g@IcwfudSD(eMhsXM$1a^?>F{EY=AGq*N_F2BI$Cf?#ty5nxi5AF87d0yGsdFfOw@ihBoD zu!vI1ig#sJP@1%~Oq7hw>ua_(F}XCuvh}Xpi7{(XA+l$EJ1Uz9qRWd3tF1bH0Z(Sa zoGJ_Gt_Q%ZF$cQ394Y;>6aZZ~zZy10Gr?U@QUwPU&Q2c_HUPtohwv`V>RtgV81 zrw{}h3pn)xz9Hk{dFu8R=s}ea<-Bt-3)QV=7v{P2U?#m7SpN1--Ka^y%$rH}z%YblM}?#{qP` z>;f$PJgpao_ra|m)PCa($5CD)JK-3zwSe7T8P7t+3Xfgsh1bP9+k~j0K2WTv?dAHF z${0VNfKot2vngQpWt7-FaGJLZ!s#jdSY6Yj#c_&guvbD-ezL3)ObPm4SI}!ilhWu% zAKbx|n`zJGbxJ2zeiS}FQ~nC*s4phQwuYqp zZ->6YT)*_t)T@}^YP8&HG~!Q_X1JT-(iUN6T7J_QH^wkGbSXZJw9_P%dnUkZM3ZF+ z#!4`c5rU?FDEYwxnUnu$ZODeF$>DaXmq4myX$}Pq^MR{U+iN%v^X94Bo6 z6}~CiHLZ2f9DN`<(dEUiIU5AgZ2T># z;*T|kYObog*xA+9G*^3w6|*K=-;CkC^)e^AK-}mx75-^Vjl@Abi2W{|1^oxi7nz-? zx-@PJ85)%uOgH*+n`fklkL)jOfm!L>%$a!YrLqOZ($Mqza6~*JoujJ zt7b1oU{aDMENA8wz}L65HuopfO_`aR{UhwoqJ@5omMDNkgcVa?O15-Q7(83Kiq zi@jE&em)7nGz~j|DDa(vF9oXUpFfpLg0!xrnZ#v>$EYfXp7cGL1#u(!#7UCVz}8L2kNpKl;`2b^32e=>c!wzxd540|S*P+(uK>N5TwHj@0fYUQ$ctp3 zLY--rO~>4=69>=~idvghivxA^FG;6wjkO`|vKRGl?n}4EP^1f-5C!kcaUpZl>5>!6 zsjh)2;PhdGPVZ7N_I1?8w;gfEN8$W>)iB*%D%Td3L5QG%#r;(C+=S*r_l*?I>Dz|)H31_Wm8}R@ z1EdoxusOUkQ^yZIf5t`}v$rq-)MgEG`@mFkVf4oMrzst{f7P0nXf#kLxKAXGnnZSJ z!^{|@a%59pU79;2x%mzr(HlU`P&i1SV_{MwSI!k_KL6V}a{FK7;6>|1N#K-A=w^&O zNyUg2#n$S+Ozttvz6(RHsg*$zQsn4(5rGC&BnTfT{`L|Kmj?>P&HjPN&dv8c=eVCZ z+53Q^`!s0ldS3zD7I35wyRPHRnO^#H4+jxUHzNWaKhHUP2t850;Wojj1p1vuhK%uH z&K|vcer-;tK1$B;hL7rzq7?r~-9qIJoJ5b0pQ9;rR>ZokwaIwP945B~%yTtnN9$b< z)*0F{dO@c%)4F2}recty6v@-$1yr^;PPOote^VzI4u$Hn_flOz)s>&X&AC z9MUu0=#9s1c=TCYIJyK_oO&)xi+z}g3I&Nj2^+HB6IhytH92O_ojn?t)5CR*#`cy8 zBn{3N>bh(D6_gOij=uu(E0}Lzz7j^m+Pe@RGqPss8oxAZbsLjeSF^8eqKp(8;5aFA z8ZiEsD0;P;dU@ooKHII)*dxUHZdI&RLL{myWYUD=sk9GDjT6;hMXzsdBkFN}s6@p{ z2{D5lrC5auRYWk>l@hz=>%O;M@$4D9^*qTl{f;I#X-I&&}5*0P-97H*ZAWyr3O14TAFA zrPTH^odcAth4T>7r@W+51oAHhdAU$=@X3NQA5pi|4$CXZ~*nt zlm!m1Vl;F%s;1HrZD^{dlDKU0L{x&So|>%d(<_gFAxj(j47+9pjnfAfb&$_I9nFPT z*aBXQY^^nnAW=?<&}K!77;vm${x54I z49T8XKo9d(USc&i=;H$6*2H~-zt!XAZQ8qJd)7(@)fWxdJ^z~I{>Pe_aV6s>EtU0m zVC(oJ66@%N<^SS093}bkg4s`7<2V|diLP<<0TQK+gE)x#WDLN@u&VFu?5;`swoOeK3WK6Zo1w4vuKfJxv&Rlwb|G_@5=!oYR%WD&bu zQ4uL)z+|MVpir0Dz=ayg?C&=0xiwKmYrdPbK z_MFYP={v9p#4W*fM>8oy@N~8>Lbe*dZ9hiq!1PTj#%hRTC}8dl6CL|V^iQdW*ePTv ztDKBEi8u*D?8tkNmR3Tk&G{#!-()Jq+k=~%TAyY&exiFt02q6IWb(<-jg*C=qiQ&E zGu1`kLx~jBZN<6ZhdJQVWd*h_W&ixY`P_C>u$K>bxEN63Xt7n01X8MI^?gi=B>7-&ZyC2=l{-zwASFNW#^!m9#C8<&QT=k z8T#)RL7O{W2+2hYuQy%+?_sGlS(P6#>ZcJaCMp;F9wHxYdDf* z1QrPtsMG)J2RbZWPS zfdeX{*=*Dh^WU!N)tg&Bg>(8nK3MH}M#I=gM=g8`j~GdMtZxGFGiVAqpOX_OXY>-J zEHo?5Dyy3fzl(CV+0(_vwbxz~&-4ypDIR_Ta$wxw%I0Haeua^50X={sRe5Dm203#L zh%sVL{w29p*En?Aa~rvU$40qH+xfDZe;tbRA{EItr{ae_5@Zumf{BL0wmd zLZy3%;H0${J%^B#JE&6w>$q4$GX5ki%++uV9e$2aF4}L%zhOe`PgpM^Kk{+WgN%wo z9YT{+q?gRThi>tN)*^qdrsC{puJl*HRHC<3`{{U-!CB!|dD5(u1(`|Pry|^DEv+!v zSZ~8FNQ3l`?k?D8N=fkED@r=^U)O`--`60Mj8S8D;~Tp7#^J@G4Uoi3aN`~sG{NIZ zgXVuLyh&ja(LgD{d1Vc4{>jh3xz~W|` zlph|TVncma5DSvJwB-zBVWpHvqvccWB$aYcXl5whLfZ?>r%`b=%B2p}(xB1kM@Vs| zq{kP^^s~#xmAXs`#?xF--J1hu_8iWU{fQ*0{d?Op1nbka7QyPD$2|l)!P0gmsR$8gmLcs4n7! zP2G~6UJXC7Lx;eA!bPsCcf?*i^Qjmw8454!q>fKQbKU`Yf1ZKt5-J=!U9=^?o3m1L zZbJ@#oyO$bJ<(qhr^$s&sr@zem+I`3Ig1fK`_Ml9vw=IUszON0d5JlCQXd??U8hBw z+js)(xB4HQVc$19)vot*i~iV^I{R}k{7gG4F7c~o{Qc)qdH`Q(Z-y>bqYd8Q3<2#G z;FZ}*C+Nv1u=i=;UYn+GSYnuvQWGi?{jC~`$D3PVL&>o?EG`TVUM*G6CwF{CWFdv;PKM@0i6YV!8jnRB``oA6}M zY=61S+mjnZY6Tdbmx+GzU6ppGQ>Xzss$!#>$6Y(M8KU?_ldhr+M++2IR@yT=YhrWw zAMGd|1f4l&0ps$ryl?yXQ=`d~h4tT3Nd_5jfTyoR*GamBy%gSDVQp1?WleZFf+3T< z4_qr<=mTn|+&LWWP$Lwy->f!{%{yremLYrwcPyrNPP-+eq?hqnYt)O{t zAA8nF#VZcyqFOIZBaTj^JXF^o)vEDOKr*)Mbom}J;`F%ox&Jte#Q`NFS^%LN0}+S9 zfy6BPv+Jo>o106AXIJDVY5n#N**pBzMGMg_Q8RO)5M&|bz)3nREMi@*kNC)+ts1w- zS4gTj8`JkY9u-RtwdHkLXj$>Flfn`htA?ffrfub@*Uff`11!z5jfOI6H|bQI;RNrv zTu=Ttr@z)+lvflMof)}v<(zOinQ%Z|q8j zS0=%w!%L~_pVws=^2Pt$Yxy!WZ zhim4V^Z)vjg|Z_`bS64re1zckV2gUQmR>Z)D}F)TFGTs%OWq98l<)Atr1H0I5~$kd zffkJJ@JCy?$%#a3#-i<=+#PBU!Zw|kt$pI$H-I5*o&@r)8j&K;tr^PZQYY0fA1rjR z-x-eZ+P}Yj1wa+I^58?-#ouCe`pHmPCPB4JbiV=LB0gdCzZjmTbo2$sIEEM{1FB|b z!AK-c1G^`nFQ4C)5rQ~|#g3WL__3=sX1%}f$@~NT?*Zsz0^ndqGW=U)BqU@+IC!}C z|Cr2J2-uW3oLq=hVyec@v6#3NAH=`Mad@cn6d7-uN@uMUDKtGzQ9M9$|+ z8b`1W;(+#KIkbd?5VeCh^e^V zRnTEt_2L*6s9!9|afG|_YmflcV}fZQ<2oWQ`ylZykOO)~!`i(qZyJJTxsZ{<;*v1$ zZ*Nkqz2J1jEw2Map*T=rr#Ess1ndto^0LlA2k+kovlm6yHpr)u&f;>4I}?|V~Kq!+|!Cf|d~w}u6M>%nDm%G!q#}(Mqi7;$Z5?=ceKXAMKvpq!_zhR zmdF*-))iS!Hl)2w6EyuDQ-;r#brZC63jK*GK+pgbg0K(X=CubF8aL0jt(3Lb^eEEngdJM{ICG zBU4&%-xje_pnu~$ZZm|eBdsz@ZU|GSBg7{8VCp*FBkQ+Be1`Iehv2G&C<+Odn>G*y|VdbuI)!mx`f_A5rEFfFc1+w;7k<~K#A ze~{>PG#S5{df{jVf#!#2J-@DkZWlHpMm(G(xjo_KTQwIC7UR5dIls@ zW{oX!%-XEdUZ(Ft_$}>Pv{Cl%){6S1OGgP1;9LCYsN<{n&0`o^J}ZkfyF{1G3UqrlI zmm-CdXBcxRzk_{FO;9n~Y#59Kw>VwXq%i*;ja{UW(4@G}&fy6pXn3Y5ZpS)^9)6V% zH^YcTpShmD1(R<;%$$og^gkfNXX`el-IB8`%>mC_Yq7;m3AIK$hZhxww8vCFOCVor zOC`(i7WjYJC%XI6`qMxE>4@}2!f*CR!@`qoT>Fq?QyG%^iqk7VW_|3^$bYtS#r74T z<4**{U*MP?JJRv9C(2R1@@5;zt{%G8a34kUhu3YS zJ1!wkGQYknezPTe>@wwSEsXbS(SL)XyVLRt7<6np!hZ!gUW9jkhcRSCgPUxNO~%)3 zKAT*Ib!^rqM)ObYui3(*r5rfhSKHQ}5%DGtB>%O)*A#u`&g!z}x_NhJ&x~xku~6O$ zW83&Zgx%WXQ3ic8k8l^#;+fy=0>jd$WyT@0xnf+q=n$({X{>C-g zo8A2CG(10gnI&*wZ5-aP^3eQ1_mstaq|KU9UQH$*lWj@811Gyt5G|%qKktf7EV<6)MB#CUe7iufE@`+8T!X&eZe<+b=WDSl-9X_8NDN zFFwq1=a{6};O z9zK;N$0#lMo#@BDK)wQhbZp31P~$yFRBezCLOw4<8}u}4r^F@z+*S=Av7b-04OU>7 z4I9JjtVEZe$&o!=9m)zu1)O5xt>OD^lSXzX#SH{h`opmh|Fs^F-mH%TV znm+$_kf+plto4h^S3sC0Z2dEQaTlc@kKh`(s38o$xz*6dGv>pB=7Z-M0tWCXGME?n+nrLsYzeUe5@?5x=Nr^!y2 zNiLyeID3JKllE^_{-hL48qQ_53=js6b=@Lst?oR8o$=)Ip*e9dIsES6Zps22ZWXna z$X9N+yL(mh9Qw)hteATFxLz%c-_hHbW*&8w+LJqqk7Y0{NUB~D_}e1~=jelM3nm|o zKMzF0FVM<(VauXV8l%GVau zzTt)?ubCDF^LpL*NBTtH<@eMUi*;f(cN!%5&Xp|17}J7BloS!Sn(v4k2KVyQn;cV% zx-EvGQSm$t`f8VEH&%!iMT%l>tFWG>xQqO5`jzhyb5+O;URD+&F5r-ZJg~$uV__a zh@V8KbE~7;R-faf>@tL@t~MIJW-t4+OjR0Cf=4kOExW7P-O=}#k$MbKG}AN+E-s^W69x8UMjpq1vH{}xj7SAaKO6Y3d zXe{$6lc=lH7C>E7)SMN6+w_4#|G}}PCcE2Wv?fZOr(r>pkGNrcUy`KBKB;yL-&&L~QgkCVJZ-st3_y9ev>6SibW z(Ph`u16oX@LV_5hZ#Q@PWa>T5J`1+G(A_xL%RI9WWx66*Y|+M0I6V5|&6}pK95+Ct zWPoR};%n{sUP1&5EUI{}Nrx|oG2}yy+fhmEnoTaHlhXK_cvR60TamYF)g=mM57Sai z!7_M8-d6pC`c)GhiajpUw}muYbv8wZlr$6(4N&vE{(C&Bg#?bpSIs$O$(=48!IQzimUX#RE#|x*S8IF zuaq@xMOfAj-_Lj!Tl%tbv7xA66fffsdu>aj$5Osat4jTRJgG?7>QZ=9p62ojn7oPP zA64x#_X>~ta@X+ed^O`@O`Z$b_~& zgfBC$3HheD^`M}nFSmeZ}xua2(zzjR%+@6Hh?vA+>I5*I-s|<3%Y> z{&WZYH$TmM`PXa-KjI{s_X={<<(s?Bx>YDH%KPr)@+6$SxECaS)yl%e&MG1KEb%BZ(+9w-)(fPL1y&|Lb!7`;G7zANIqDh5Q7urYmzLW5GB^U7RR{n z`lGjqgL6{({ zJ+Dl_qWSRG2nPrmrNF1c4`|=PEgzZPmf{mFcJX>--wJVqo1kqottI<}pUf>H=q`mE z4X4(eN5`{F;|o_kj^?Mc-eu9vqQd@p86*K0u@Iqc%mwlcnL%gy$?bE@=XBOus1(sj#Z!7Y47OTxO@mO)2Sd(k!A;OKnwY~BK zuX_txZMc{140xM=>rL+D?j|M#JF&=#aC?b77`Ltv_XUtHT_0(q(Px@nDcf55t_yaW z3U6EPm8Y>O?9Grhzr=NH6Tb~_;16oFNk$yfp6}(3$+AvnGdR@eyMFqCk=wyS!P9cc z@)aL8YZqUS;oTMN%6W-hVC!Y1itNq7{6xo`UL0?&hb(!C7*4V|J-q3?_z!`R3{9qLCIwn%F`5HB#TS^+?O50MB{A_{CqfktR+BRJ zykv4w6T9F6mz+bo966M6p`9oV{ZwC#5^5^P0XA#;uv(gkU&=F;bbu3q&wJfcq;*bb zoXx+b?Lw#;RCy5Cl%~K#;fTCUX0dtd6=FVW3d9yNgb6n>%FMFgo5NsdNT1}a<{ZX_ z(dqhtx=Y9zP7}wq2y7OZd(hw~d>}MRiH}oTTK?jp#h5Y7jdt+DZ1ML@o5b`_BLt^* zy)puZ6DhfH>=kZ#Jb4Ag>sS9!)~Nj=qgiw)py|b*N6d5D){eT1Y!r{ax3G^b)FhC1 zRLoh9+ZZLh9-sSV2ZO$(L)w7BJnF9G0C!+HFD2--N;GEXa*R%i+dkzK79UJyCJ&C* z=jhzpTNaL1vbt$1z8FPV2uUDt!mM~sS$n*hqsXl((TbLGE=|+ad^y!rU2VU3%+%?X z>!TSELLA>=Cj7B%oD47g;&fRpFyZz*(|FgdD14vD{3FN%LcKI!QOzYyKrMYZ)D7Y- z?)C>C=e11vB}0Z>?q;elpR_w8*vpOzq|=lB+TnjLXzH-Qdzjkqk~G!l*$}viZDtq$ ztf8MqWiaw{)B`qmKTxS~oQwt4+T7JY53*`ka!k(e%qZ z)%vW&I7Mei$LVTGHVl4xvUXawPyf7aTL@_b1@{>h z5m;}iMYxR=c$aHO(xJ(P_bB-MZIJb3JssHXQiQLlp1NS-~c7Zrk(B!e0A9Nb!@#%W!Qx~0oM0;R2n zF>)R><*LGv80p5SFffcA+Wm~`87W0BNhesY z^FvW72H8!%9kiw)Zuwo)vT3EH5)4;zQoa|QsI>FqQaR5aBFe$`3<~4dqn;+BRUx{N zeOnmgIn<;e=+H012I!-ONWlF|9@tg{fHB@n;5@_?LEzK>s!any_F#YQG%NcX>z~rD zk7j7q&Q{RTqw9I5--FI;cHRFJ)oGG`W975h+=Xh65g*Y1dF~(4{lv8BU5I`~^soQ> z0-|X#p68mLV>NC|0h`1WeXrNJtl&hg4i?61_603Gk><`Fx7pRave6sJ_-;HR~n9o=LtrN08$v(#_4(pLQuXga56+Uh(?B%087ioY) zn2(MI4lgDDqNTM^Y=`d)Q5DVY^pag{!@qIMiIy$PPyIQI-kC>{r} z2Q1QJ#ct`DpF^zP8J_!1d)NxsBQJU@S5Hg&WtNKFK@uwJ{=u&vfL!1P$L!7r6hmM9 z2c=01oy}ht_={HhN1r)3H0a>VgZhSpM_Hc74M}tU#2Uq0Z)2QD$>}?BGFraQ@ohVr1VmXHtxz|nnp>trSYNI`FGB^I| z<&gkVWbIz(dzo|1(UgXl=ppC0X%(C>RXnwMSunt8w<=F%i|@3>dRYQsv~Z5D6QD*sZD&G^%C;FpqFGS)NosBog#gdZ3-HftS4B{n}_B^uZUKfd7+_j~M~`OHO@eZJ6Hxc;)k_|_p9+LbrgKDwerm>IdOIcdLN{6c1Ca?Ti+%{s3X%y& zZ9J3a=@-TDR>En={n1B%9WZ0yXh2QJ!Q1}O@y(B>P!TMR;6Gn2nQ0n{hf}VxoL{1* z<3wLYwxAC9eg_aGRj!o}Jh4nIO;I|18{?Fwp3j~oo|bOy^8llx3{-M{^S$k8mnhto z#bz-G>c@Gv4*?`QY^_+KY5o*+fQl1EMKn-|nmzqC3=4>V_X|XDs4g z#q|^Q-~OaGfiOx9F1;6c^jToGO!msiQ1|ZsXv`$|V$=B-YqTlD%;Q`r_nX+K+as#S zKz1(7*R6h@?!30sWr23A17cz;Ki)h=#?NoI!NhcW6ta?Mmi76Dfa!?i>f_=EwtEwz zzHy=?yn`i^)1(CyXKFQt(&f)R)8ZLx+!4|Gnsgmh^&xL%y}K0Y`(PoGMUtp~Ref5W zZl=4iY)4Z^!%)05YxRd3h>W{9wx|=?LK=2-H0+{!__u2>i5vTw@~N-F7i3 zc~Zke06Npt?Uz&kQ>|Dmd7}?er|3`$363g_WY*d;Fe*tNe>uV>+(LEq>sVWg-6d&h zx!|NQ&3?ipC`p2X)VI?q>x^K*|0>~6mZ;&V!%?&*=Vi%5VSAJFk0VSaM!p*4b0cP% zT-(<5Gl7XLZc%|6cah+*whM(-gl6**Y1JH33iih-H$q~&_`!U+INP*~&&R>EU1Z*V z5BSz&Rd7`*#&)_%PDQ>6_4RLCt19a7)O=;`^nUjX`uS#(RJRTY3G5(JccG~Q z%mwlDZ~OS#Iigci)*R!=29gA@x@O%hon_=<%UpV-A7=SimET?Fv#6#oCQ0esS6k4s zdL;E-KP$j*PsR;bNi3+R`S(?8?8p!e>0PZf-j=&Q3@NAjK-`2>=@47KbMnl; ztG2TnZR#Nf%BBKx>8NxcP(rKr)}1rjRR5(Q-NtvEP(Rr9?17P*m4Opr8}$g^Ll)CDHfnU2yQ)KnJoT~h@2c-6qFT$&YK1#xP0wk;ywqbH zjxQEGqVZFidrlt$2vCa&YubO&s)GOXg1zKGqfrjQoHKsnxo3}t%nlK#euY18kJjZG zB5Cv|;fv$OK+i9-yvxTorj_^XbCRL{<-hB69*Ga~d%Lv%=vM_}U8%4CzdH1WN1zEW zDm5zMr9j@wYHHnSEjm)q74)9gdK&#&mP?nZ{#v`ZX8&oW*qMSi(7CUuq_t&>sTF_U zuxsw$6VB~mLN-o!2UJDZDTI6fW)5E)Dm-t#eJrK<$k_v;v#>&5{u*2m29~NnqGCql zb1WR)RfAn?uDW6K$Th&>|ES$ZSmdet29dV;Aj%LopR&dv4ZU`j8h0*qX!giR7vBky zrrH>4W#P|Lm5z?U$f(+8pLDYP0f2mI|MJyxm3>ZG!tGV`p`lf(VNC$KoPe7FcH-4D zhH8TDp&ytKtI0AbJ$<`tmvaf8YF$Cin}n!2#ZF*T>RR7ks=D&va@wDSHDf?x^kI#4H?+!Vf?LE9FK7=pZ^&A_`ef|!S?%Z{# zxv>GvzI?~DekzvD4`nFt&7R;s>jmc|+4dw8{><-Z+YL^~C5BYOgASS$Lawuf^|`8D z2TK}5`nlBL0tK0J<_lx2L7|e4C8Nag4NFR{matVN#nIFzG!b!hI^zYB780hd+()VO zoi70zsX~*Pid2V)t4lj;&Q_hJlcR4oZTkfWccQqx(Rm&%7o0**9|GF)lvVP4pZEp$vNI=G>rlGooWRpGt_V((sb^wR`)S}CD zB5~-sVZ7?8{5Vmj=yQh7D5_Zf4zL4_be*eQzKEMIyfNG5urP_3!pT$NtRZHr$>5g6 zvMr=LwZ$A$F7G3~9#WjtWR6h{z$f8`V(p8vq-g08v&OF(VYuz~PCi&|m-{VF3Z6R? z?Xv~qnKf|nX1AIY7D0H>i0^9S5(MwJ6BEh!np08cyjJNf229n~o5LT@QR`Pg`A0Lc zrtI^oj)y4cKx`~fcUZg}3?LxwV$aH1%eAm{9zX4hJmyzhP+so2?cy789vS0}%htVa zPYf{n8v6NyZ!xzwS3$%W50SD#o~u^XjdY9QF#VNS(_RgaJatJDi{##0TUBNEI-xd| z+8Z!%q0fK4Df+&FvT8BTW4Rc7s*#hk#Av-Xrx#Eb5|5QiDns8YUVAHUUw$zD|^z8J&j2}cQh{R1;~XsCs>aqCD`jkB}#wJmXl|Dqkj{)3eLEaNa_ zvk91FeOA7dcFn>`Idgo`^Kw=n{Ph@!(*;L(=MPJ^)3-4)mP{3tV!qOR+LTUrC#BU4 z33CoYP^m5K^-D`N?i+~Vk*KCDw!J_Y-RfXxsXY6W-rpx?Q)GwETmO?Ef>Q4XtXswD zMLm~&M%BgXAO89eEOG`3DK|1>t=e_ zcZZHQav7aJ(-j!i2j+(++c3S&Iybu2m1}vRj_8Ln30opQo?w{Eot0X;Cs?iFRWmTr z2xwZlr1h9cN7D~1q+B80*4^Ku z$niJ@!}EyGpB#HVyh%)=)id*?xOC-pC7CRFa3kcxfkZJFz-u~0*)|K$DW-5Ig07e| z9C2E~;|-nSl6O_bQ|;TJ#HIMqk~)bq1b9+oB`iwJH1~TzY1oNGk5;$o(}+c(*fq^E zD5!zI0Xfb4QPO^oJ-LJ`Gb(%xUqWuwvRr<}5u-;?<>d?i+l5*=%}Px^n(t|C3=iga z@yT}K15n}%Gm)AD2fOV|cb*tn%;59e-lpxDhdMyDM&RdNw|ZomM_FHE=s;Z!|42F8 z>F+|UkeA&^)|8dtjW4Of>gpG(S75VS*rs8rO2NDh?KpC$e{gMLQ&tx*KesyPMKw+J zgKqD>)2t`=j$)slrl)Eq(DnL$Z@(kBK-by^6C7*fU|gqrAIZFQ=`mQ^nI4IK2xJK( zX@C_7tZuDs0rH4PM7yXO`Ug`IJ5BSgVJLocwhs4HN}+`C_Ev^O5M#6HNAEr0K67&0 zrM=@d82>#0wOHx(vk9Q&da-=+FC=>&mCE-$ydNXO=zhHY+vsqqH37qIf8xoAJU=bi z^4G?z3#Mn@8e5O5+f-^idA(E;P7rXpY9BO>=O`L1&jicmtRJ+=4(8u(@zCdBg@;9E zi#};H@vX)cB!Jdi&<9TAQNgDS#%M%TlZQmx(H}N)W!n!hhcIG(+|Vya3#MYSsCle! z_2r04%H%ANLiAa_kM}{?pGtnnURj>`uDGk5M3ibRC^lo9IdaoY?8)9r`6U*Yi5Y8I zMS|jKnRIKDmmULp3^->}Y5P^I^|KYDdd1i=$Z^{I%D7?+lhA7qi}F5aXt+nK(0oH@ zF-dD+G>+@_anDTO#I8eA9HyKTHSS^Zt!Dg{aV%B*wWnLw`!Sx0urIic2;70;?o?)G z%WNiQ91}fJr?|D2K{uym zNo3Au7sLm!x_D|GbTf$|M?gb$fjUF(eiwPplsQLj=fgBI_&~e+K^Ic_c;%U zbkrFQemg8}@3}CAoWjOuORWUO1t0yZ=b(otM_o>oxL1}n>&uR4m6UbCVD5yF`I(+~ zyLyRNF!}bz#?SBkLmTOIx&~mAk@DQwMqjqc!!`k`NjoRUU0f7L1gZnv2$P6|{ZU6? zZZi|&boKEHL|5yYx@Q8KDerlxx>Y-U=*cxH+g%1#9W3PwuFv@!k50d<3%=8z&d$sB z(Oz|-^s(`Lh3jhgmJ}WIB2l)c4oFq?91i!CBEpHDetF#TZfco+n`n^B`!2BOXLg3d zbpWWvaPpLfVy;o}^Z|BvS$t`YxU1Hc9ByHyi$1-bKZ)yEy5GMZBP|F5H4?qI~2i%=H;*yUPA+(|9&RI`7vxB_khG8JR59IK<(-y9@ zuAcl@rpt9pw==+20qPbTjp9bA79H!HnJh(Z+H_r_ z>#CtOAnGG$>qWf5!bQ>KYaQd|auC4!o;)o3FkEH+7V){+nG4v_b6)5>UR-_cs%k$x zIxCsiKF^1`+s0KOn0)ZIf;zvDx?X;+KU`q1m?Hc0K(#HsF{~@1GGul4-5)=@8}QGt z5PYT(><5#O7UNIlNrMoHvTp{uKS*hm34*KYNr(fmZ#iU#?%DsrG~$%|+x7KG*ys^d z`${?3p%F=LbXTYRW@WCeTlfExQe9DgD^0-f^y|;P{0KwVfdXya6f0}EF0NrPp*}$N`u`RO@DF|enNR?jQ`nz?9lU{oqZvLrC7xNL z#y-=d$$=fBX3*JW0GCC8-)rYKPcJ{q-mOh7*Wfp#tKB7O3qMK_sqnPTvLgL@{hs%^ z^Ta!!aIY3dBT$|ky4VX_pT*1IU!;{zFEI{lDLZYGwxAn|$e`cMN}P7gxc){On$-S9 zv(`~M+a6O8IXNh7c7q9$Rt!#p-!xu7=ux7u>qwCv)CmzMhq1iJkr_#k)RgMuCWR&a zjQM%0(DVvzzsxJeM%z*?#je$2%Bnj}XQdZqgqgX>awwtr7i~`RUp8-$OEuIkb&eF0 z)%swC{k3;J$${s|gop}s)YKxj*ROL>yofkiq`p?G-&y{Ol~LJtV}wmi4PX}`-nCHS z9x50h9SG~{%=l-R3^P{pvV`$2hgS_kGl#iIer^H?sK2GZ7dq~;wjHrFah+L}`M{gt z-%_F6V-tpa{=`G?`uL#CTfGEsV>%N3YLC!1c4pCmLR|HB^aP?Z^NMPz@uWLdOsjA= zY{b8=J(bbpyN_wAVI+3@*NS=f8MR+vs`$*bQm0dk6mOkMI@?_npxU%ssvxyIAV`-i z(*OqTg~~|xWjzp?Ia1bpO*^L~`PNOcGdAqZwhTy4QKju@UgSBg6=)bQtN(V=-1?Ae zt#?15CKGeYWG=#5ki=~m8m(iavn{>h@#r5SXRssNdD#V*car7totFnVAr*MMeZZwrse~ftIA;}T* zBr|1DXY3q=Syk~xnBcB${qIr))z?Q*MCLYJGDX$Q%S#;a#FPz+q9rq2=K z*6j5^Ah&!Tl{UHm0lp{-8;OYHr~}`$wwNqt?RsvC4yp2&dv6~R7uVlRKd{?p`kWq@ z?zRkW`Zo@FW=A*vMN56&FQF*fTwRU6s+6QuWt7jtYdczTP|RiC2C|q_7EWLjJe>a~ zzQ*O~AXpG848E**5_{^+=4996oU3016IMG@e(Bky?Zyh#B4$3X#G`A5QTR+P79zaN zK-M27usus(*FMkYLMDDKga#accx5!T4fjWUTngonZIgxD#ojxK#c?{tA$&jJnQw?z z-QbU-g*fhJ<=b8jG@F){Gd~6d{RgY0$nFw;uep#}QU_6&#J@4jbvgFz&f`7`O^F%p z^qzemL-UTr9mrc%Jq7^Q7Q|O(*;6v?wLQY&bp{-2w8Rm>l0&L$#!_g=<<0D${z1zk z9@!#!CS7L8?8nm|EFbOi-em1s-zt0S7>F@mh`qz+pXx||ISWFD`I^~|9b~K5zg#nT z$^9qtN1i)QC1YvRmt!aGO{>9;4f{z$hGe$WHRV-JdKEgp5eD9e(0ze){D>jo`5M!k z{C!N5>;qD(H_=YKAE)un2`g*WFZ_1dBhzgdJ(K_f4>bXffq}j&#?F~fixjOl(1)L~ zOJ?8pXBwH+cL^RBwK|*?1!fIJ*zMxC*di+@6NF)HM5K+;YqG?7vQhXX>x6&aa+9r1v?ttmx-!q>IG?GE& zD02gUL$$Tl{Pa`oM$@PeGZI$!GuAom3Jx^ed_BgSgtwxzD zatB=V^K($^H(bx}uBr`wezw`A?u}lSE_GXz?r6WdbZ-JwwW{6eUaR+I`^d^tnsJ0m z1%RgPEE{oFes)*!2?AReSPsgQ_;<>XyT$M1nyp|WkU*%WXLpvuCP!uG%zFwhB;m*2 z?V7Z>X{M*wJ+dffgtRP4wy}DYsJb4jEjiG^HfICPDs16WyQTlF4^+ zFDfrc^dHk~yjmVk`J)u`7t~|mZa6o6lrt}> zIu+Mx;JgyatBN9dK9z&>&GoQyQ*x!!(jh@^AiZezB?)fFa3)#hQMDN~-oE9#v|yjrZA6A$Hozv5 zkK(k%MH9&cje2TDqa+_joi+j;_P#_~U*&Yd$UVjEUj&L97Hw8aO@&nm+{mB+21Y9%~@uzdUFt|^Q2S&^yV zc14JOr?Q^Mg2z*l4V!F(qXYRc$9VX4pfVC{oJee2zX!n+;2t-9sr|~FA=f4K6!NS! zkpFBbbZlCY!$v6Dh7=hYxQX{CqGmi+pGV@Lmx>0Pkm$Y7Yq7Ws;J`Xf71Lc@IXwxYA z0qw4udX045aM3ZUKM-j4`0m8<%RS0E`CK;^6*uSixuxWYYkNI7Nq#H+9;4td8fVaf zC$JxU6`K*~VfL<1L18MZZ(HSyUst81gLGwXh=>g&u|oHNIJ)P)opyu&%yelsddh}s z2aN)?@PZ;fQ5q8aVye#vKC@U1BqQ+C*189Rn=JgvhMIeE-g0*~NnbrCs+{i%bafkR!rZD0s9|g(6+K$jidNz~)R6q9Lvu0GZN>6FQ z)Z8S!G&J~^D(|Y;@SCTzdq^KZ;rh+rX z2czY=48}WJDw-RIYwp-o_=oN~fAp661P&7tx4>pZOwXTlZho+jnx8e+Sv$!8eLit= z*RzQs6`c#qb`1WP2j1G7hH$VU^<@Fd`m$d7Sby}^O=xmsBPu-!CKBpfutR|QSg?~? z;+e*_0=!eEPqGFEepW4QwPZh=>(5QUuyY+cDN=0VaC)B9vfMjGNm6XY54IMc{Z|Ig zB*VGAZidyZ+x_q}uc~}ikB-22^0{ZKx^K0E<1G3VAgslm0^3{1<+Uj1g+q5;lRDOw z`@O)yOmCNwuYNTu=df=4vfReFh!*4+0iFY90Xjw*8VlhrQEhROVa>7pM|FDnxQ^B9 zAD?O9J+Nm~b@dHT>CcQ7pwCR#Z9&t{|BJ>%5O$o@V*JngJ-Ggd2OY)Q=~3LPD_5yr zT96Z6q-qm&z519J)otZuw(Cewc0u#me&>m?ml@cd$h&X;`$bU# zJK0pToH#!Fm5e#hN2H$kyMBDiC3NjC;?Ir?A8tWn{6!nddq2a287wO_S!){W=z8_< zq-*UmjrI5Wuf#X~VoG(CPOf0Zkw}V?ANc$SFkHvjHR4Y{oE2U{Pbu=BdK@J^cVF-b zZ2bpE?>Y1Ede)6GMcI;vbXEI}W?s~nwWfyF?7dFk@b^fA20DDo>((wQQ}Y^b%D{IzR4 z)RTBbUl}z2RW$b-1&)Ee5LOap*lRN$(JLasIMvYbR9lUg)R>b=46N)-cQeoC)TN6` z7eIADhs_E1H^OzAKb6V;D3mH%0eGM(XCbE2E(dQD$tWG>v`l$-d02yLtH|9p-#HXg zDW-^co*HkcawNGeeonsh;OO+kU^UC7frETmJp^Y>XZwQ8!GP2 zV#((*B1bF;5}k$OVeDwj2%iS_uV8tGdX8&|wg@!YaTKg+Zz-CE17_pkK|U${jOO9K zT`!;|EC(gA>F-I~L=V0bc?vzw5utq7ug$1t(ZmWAzwVh%0N#mcMGVKY{KP}H^kL(% z38gmfj6J{68XQ-uUEfmF_s^ag@#0uGhjTflo949xa~uha?hg0o@>Eu-b)@9iTkk8t zL}~l{bg7L3!)>dwTjnC+CLc92a$N(G35$W zdn1uq+;_Ca#(+JurU92|Jc#rEAy^^&&WX|O1C1SrVm!*LV4(bVDAZh-=dEx0E5p)%+Hkp<{-tGThAZ}V&e~&%3`96(5EIx6Q4&=+ z3?F`@Nk)Oojvtvm&%4y|y@i=6(^^H$4Ol_|)!Mm!dKI%Zn7SQFZ1c6P4egZl^7onF z2^S~{(-tutoNC_2W8+(#z@JPc$cPs`GVJLPB7@tW4dRS?qRBM+sdOV}eAzh=A+X-`mif9EYH^>Z z{jSGwn=qz7BYWa4$;Ia^v#P5&6C6!GdFa#AwS=vQLDPfxlmW2Q^Y*R>;S zWI=G98O$z5l=P<=cY3V2W0`A*f_fbHQ84jYJQic^>G)5XQPwt+ zwuMPS(SfS16(ngXouLSv2*lWKH8YM@3DIW3HU`+qdi4EZ4b4gc8ZdUv@mvju za#wRv64z-ip;dWFW4Y`zmMV1g^5QfBj=&@Z=#Cd?-_VeZU{cmCUk%k4U?X6AO*^4% ziqdD&P;X53c>iKGyA%1QF`Fi9oquf?wj$Pa*L}*_kYVUfV7p2|!oFPALL-F#H4jG& zYu@iPPfx__(43t(kZ}`!;|X@jgHv%4CN~6mjq!GT)3$Z(5t2nyotQOeHdWikh<{;& zCr5SYZsF3tb)pxJ&v)DL`pjl`+uf?%%;rOk0aH9~XyoVS%9JJ{4T0!hoSrfLaA1>TLYHvLtYeuPDnV%3)Y} zXn&mAHX#kQwHyPEzkhXdn-R-fP`KbUcjrAsq=Ut(|Mful=hqR|Dy55gwN?G*X3ry2 z)8w3m<9J7Y9SH0}sBoVp4`;APT#cewigm>i0J+U)1wmft1)dK9UmOQLvilf$epFMj z(>1*k0q%c~rpg|XV=3~IoQu1g$4s^QZ1`HhI+fUO9Z)vAQreKlA?D^>jNsDR%1(Bv zN>3WcCS;syiV;>A)_=C*G}T>CX7V*z&?ffU^7dUP%FgLin4#N+bdx8=4$HSa;GDsU zb`oi*-ADs_w4g5KV=3cjbjIy>2xb*x5ou9NA#|%S=p}F*5&OO(5L)q6$_5mx|=i5U1}%BYg$ZmgAR6E z(M?uSJceNjz@dgdY>ZO*t}=Czt#WN)Rp#Sr+Pui&f@a1HUl__uG-brm5tm|VV%oO! z(m-7|8P3P(ZwAZu!0aR1lg5$#3ynnFs=#|N| zb7wu>XWO_|UK?gVi&!4gRl)p=#`_oTKeN!kXeyQgGk?M#KK#qw8oHt|CHkf@=3py+ ztQ#%GFA0HffleWR7M`5_uUafwvw^yTQ0!;FOUM-?!)m74`8_J@+l~s!EiyyDA-j@l z&CBzG*@g)(Lb@vHBk-VIijh0VI*3)H890~5)X!hkmkAu!wv-bl_CeLGfpDjNJsX{H z(}!77tOI?ncCUq*s>W+v2SE~#(V&y6#gPK>+86uC*1pe6$o2}U0_UsUECrn%z3cD$ zHN5zDj;zKNcC;eqWF|h>V590v;ZGtOUk5cK4ohG0^82|i_}g!f_PIMwesQhl+8HU@ z>5e0Dxo`xR$T|x{_iOE?T<0_!d5vg=|KN-+Y_lIaB59NZk7WxB!yxm%#t>VyT*NI`|C42XjLn&rtLm)wdW$7# zWi++*;J1q*qK>~-{JpzdJe=7Mq^T`Gr=jKcJ}Nh$dCv{anJm8Zd$3CWov;bON`A># zk6wW7T0DFlOST6URk@Yo$vzRRbblEe$b=)TkVeNR-c_Wk#5_o)1lSv;!x261?kSTj zU3)JF&K_)2jhrs%CmykhHC@#wu6)`+$D6BNejT0;cI-zaD0pjr~sA@&X}?WB=PZ{9kLr>g2%3y$6=&x))VipHWYhR;O=hde zwh?wNP0HNlAH4jCOP=klJb(5=1^iAGrd%juvsYa*x}s{!hZ54@HCumO!iZw&KX1Ss zHGii{8BHvBj@gonx2Y6Us4tQ$%37>Q)OdRSdW;AcR8yJhaS13(IVL9WN&=PYWGaH^ z2D=4pfW>AW^P^b(I~#&~weCH##+14PHj&DdW=d;XgbZM2l8yuENEeIUfKx;09wyM( z<(#cOjdM@{Cpnz0y8o87M zx%GJmdYe%zZ-(*_`uK?HZ$dZ8P8}AQS278V*Qro$6x%_+NeNj_d?qPC_}t z%D9;Dhjp?Yp=iEC?5R5T@Ag!YOFa?^wLXZ!b*eX|_(RDx;J}ad_u00C_x8A@E4)@^ zYoU9HfT8)z@iWpEi{f6oC@Nz$8i6U7_MZ){onA6_X+E-?M=X<1<}@<1MGLJt7pxrn ze(|77&~Whd6NzT*jJz{|JVWQAUN@W*dG&MfXCB)6=7@Ei)-I3GS*_~UzQAXhbEO_9 zmPsw?P-!Z%ikJ2a;k=jg%#<-Pih}g|O}S?w&S(b^{u#<)%aW_b<=-XWH}oCCzu>kk zT>ZdhA4E*_rR z4_8T&>#^dV1*>MxQrfm4*(XYArNSmzAK%RjbDW1wz|Tjs-?y%?(e6n*bHO_g=@V&> z?he!xwwW&i5f`8TOH#e*z|eWo-nu1@hW+;GQ8O*iYA>7UoAVB zX6nxJnPGTZ?oOnTksa;XQslkc+o;S(d{AwSXcnwa_SD-uw}~Z>H-}71DB{cOGVoz= zg6G(~nnu#pMm|lChg1fQz=^-ARy%dnW|WH*%OJiMF=2IKZrH~ zKodnKR+iPBpW2f+GqvvO+94M#)Rsx_8-YXz^J|7{^NLP!RY>bDn6@aGsdG7Y*7Pl< z?yk#5kvGtyM-^fEg_SP0U-tqQ>zNzxcfR81QPEb;w-bX)+Fh4+&WDRB_ZRKDrlvdN zpy+|9O~Ve`a(0{d=*m18>$3(Q5cWjD#<*=@D3QT9Qy-#Frv5u>?8xAH-go7TlOOIj z*yo~JJDAP^4Q*NZu)C*fV0;`mR4pUphas?BEro`N^n(Z5n$jnnuPE!YRoOg3i)og! zx${KJcXIrRpYx&?D@pbQS7kF+a#anCZ~YouPXs(%WuCyrTWmW{YS5Y8Yf9YNUeM3+`2A&wnl8`B@}x=Lv<~Qn2#oIuS%VG}uyAHreLgUM>ik zSe|~Rl}ElGmq=GnMcEOZ^G!sjN2$M$m%f(j{&Ik_?7MxF$sMM7IFp}YboFWb1>Ivy znDgN4J`LGSSH%%BRK#BSXEyY5<_5`~`vw)i| zUU&;&vRS-YPL1i~uz)C?PB&1MM~9nF(?zkIdQ^!#h)M7WQ%70!)$P9=?@=}PQ^tRO zMi#L1g?pBJ)~vRt#NEI%OkrbbfRdZT71dA-4dqk!7t?&}TX<+F$MeYQm!ZA=t>Jo4 z)j-Y6IgS$Az2A4yZ5qfNob*@M>-PQpi-L1kIBn*n}k9S|zAe}Hr z4D1DyzPmFDhr9*A8uL@mH2hj$9a+ZIGlPc(E8B!{KP_|FUif_tVZmKkHQ-<(c_X2x zld{v2j&P(Z4eiTO5(BVW={rk{J-nRn^ccuM*eS*og=kl2Hm zkQ7&FK6;r8t<%HamR*=26$3o4&~TrU>CoN!2L9jB9g>sMVRp95{qFb~3DYQ2J0PeF zV0BUY?$u5-*2?DN0j(l4kWsNs*3t#q*&m;t7JQ}tAN9i8<={UPoZwl$f91deA)@U8 z>|cEsUYykuY#K8Rw#vu4#Z#=)2=t0M{V!S^|F4$7(5>1P!Cx)1tZD$7S{edBuMJ?Q zr_5>V_Abo$MrL`zH1b7m zmytyJWO~+7C%s0DQ={bZ$YeE}P^$2(2EJEb^VuwoRbgFwBH5LsL{jvd!39PeeSQTa zE-zZkJSX2tryQp6Q0teIHz~B+9G9smJF4^cZOgFaIM0S4y$>~^M0Z9juG2+aFVLt9 z(Eau*Ls&PR*3)5cxGN zdA=39ku>VE@;(Eb7;K6l-yL^vf5Q@>KWWL#kqxD!owOo!4qT1v69i%hZJ7OMWuddM zfo}*UCO&o~usBOl+FjCbJ0~+A_`nfjT=iZMnWR`xWYuT6o;oDcf*#c`j|W`J2EVjO zw3b^Lals{j3;JksH26*P!dOMyW-S#4md_rF3f;cL`jy2yK|lB!leS?BRO4f)qdN7wyY%v89eiI6x5ZAj3fJXri+swCr%-Qoa5hlwc$S zSQl!Sr4#GFT4NBgG4P!s396ew*e{M|@V`k0zBLh-yIJ~Y{ruQ0*}Z@Q5>9L`0(ZMA)^bHy@4L7yWd zM+oQi$Q#wx&G4gjeo+SSRWtF@6wBbfI-!?uRks;4~$h z%f)V66_mi6Hy&lM8R4meop)fjfw?4FHAKKXu!X_DL+Cu$!XgI`7cFXH85_I4L0sw- zjtRwc^f3X85Z!Tdloj2UjXZ4(?Q<{2gQCkJ)55d#HZT)*IS=Z>-!)-lR7*`XNM8Y{ z?hCn0psAzYv2)w-UNt+haiPLteT##KJp=c+z;xzBv+MR)jC2XzK~~gaP4oIpchr_K z(y&r#uu$%7LH|eQyFT?uI$M)He3#U>z14dptJE!FG2*wM@>W4#lM6yI*K5kIS%`kt zqL4|}c+3Z*h2m7h3H4;Y24)byrCb95*GCHy>()%KY(u(NY23i{ar6|5ywKmpj;2Vr zf0sGHXL^(wXXeL`j`Q_;zy0cs6m~dE`~AL|ZeyIQoYQpK?C+&`6XWblErqfHRfWEQ z*MS`dh=~O)X?8oflUcEmo%XanJmnQC6dXH2f}yR*T6^vb6m& zYv3yej#_ln-n*)0*HqwJXSiWad1B9mYwLfMW~Tn^K2d|4b42p^SN0H**7(?0vfpcR zUJbntmUoe@zslE+Fwo7|Gt~D392)1`XX%zkzY5Rj#BLv|ZZFSIQTUkjoXn5PQyxJ) z((tX+Z9~_0pr&8wz8agMFp%FMHBjp5{n|(ymzuf_*hts}BaK1&iYkZh^ZPm~*biv; z!4`!7^BI`2RBc0Qx^M}t+dT|eKrO5m*6@0QqQi10*};9;NspjJh>p0&oj%ouJPn7& z8nZCkwN~RZPns};qEhS<%->g_?>p58c*zvWDt(&}K3%7Ml5)xk=agQAjj`P86HG0HQ;K6t$rSOXbevK{BTES^zt--IM7(sx>aS&bv4f74h6me+u&vjh z^-|*8pf0PZO~s?257>FF41B%I`DW`r&$Z118*9nZ(opNBN#|nyx4S%jYSWWDnP|hT zjd`XL-&)h{kXo-g|A3(?%2%RwS0qpK-cNSxk1CQYlQ3_q?0XEU`d0&M>H>3#h^5e6 zo~FMp)c*h$%7VWZWpL^M$wvg(B)jdGSzsF*+*RkrbZtPqBmnuW&8lwSj2bs^a|A<0 zL!FDe0c%FSM!$q~Uu;r#dyT)c1@6=(#CcoQA~CU?{XbAF#*Fcg0DFEA%MP#V!wM-} z#+O_HPsk^h)orm}EoQ}uVpkz9(LGhab6~VkH8fF`8?VlRBUT*^Kc;!}Lyd1}-~t;# zt|Zyr1y2WiBjcEyT{5S2;z^X90}d>fnPNI&V;xzS2yeK;ZiU(C*DU@so1UmUn5vQYqG#1<8mh8LWLWw zgJm}lx(|_CE#gT-QgwVguG^>dumy(=arv~=1=Ups3k|oxgd`3=wMkhNeI`XXZ!o4? zwC{vx4tz}#b!xGEArP5ZDnGdK(mTM@(+h81F&&;yhBguKRUAYjl;Zue9@7d(pN_CI z`RdiCj)%`Arq1&<4gX6B6B|jSd#e@LHDSdwRETBsT4_T(MRU-F$SeD5o(WI=z16dE z6ABsWsJpvE+%H=xF9Tk9t_C$|b%J@w{%kiAlH}8NG zwi)CqNL${qnpK=pGmGmrv;p~rGP;ta0ApP_*EMB_`f4In4F-wIe9^7NQvYLq52KEi z;G`%ix=l>#db+A#xLl~`KuZtM=r%vCpqVv~3V}%FXDGd@HEix3gLgcaI}N`R(HU8q zHFk#Y=US4wbVtQHlMMxy_YA)=12)mh@w?lS&a7>D=+9#d*_R=yckcGeRA9#KUK+qm z({DagY-6NvLyiyYip&wX4@~KAvq5+J-4T^$Ei;7JiN2l z-`z!HDA^mluJ9=g><3!_1G%@zv5kg`>et70*UiO?7xs+ua3o@IqxJXk%}Rzh|M@Ga z>)OVC!JA-t;hGqg_rRtlMD<@*xhbRURrIH}f=?rRHyEh+0gxctEEdB&|VoQnqCkEDdiMRG)Ym2l_+q5kZ8$g z8@Q%nlR3|Cax^3@!@^Uq_y%=W7S|SAMH_R`*GYv&x#djN?~n_HL=Y08TLNuT3)dU-e702d*b7H)?OG1ftD80PqejAK^fMXxizg7&mDm)~{ zvWTf)FKpUS+c0}H+cmD-eSf3^V3ks7{hUT_-#ne!sPrQ#A1Jp8Fjg6TZMxu?M~+LKRcKIY?vYQ&QJAOqd~%GX(O|@W5xM3l z%Hq$@I6vo?GghFl%mXRLe|rj?Bh!1vy-VAq;%!~wTo9L~xuo9IfLX9`6GUv@@MS@Z zM5sq}?Z5NI=XwNJQHrD7ha>NdGtSV;ywZd@r_ov~iG5P(^k^q;dGUo}*gB`WmP8d` zvm0)|qlf7Yop4Dclg&eIEGSe7ZJR*UY{s^4=mGB}WV(pO203GVBM}BgcWgd-B=RzU z>>Jqrggn3PN%b(!$s0j7d$i+n$bC8BA+ty0eUD~*J^O0&R*>x3E~Dqir7)M`(qOr| zinxg=aj@d;Jn7?Kv`v8|-z43xqeFLtg6AQF$`+@{$ofSPF2gqJ`I;2Kqop36T)#%j za;RpQRqU&NGMg&c-B3rk+Mt3(O8(C8oHSgI)5Z8DvS#GQPvg#f2G+?2dd3v`AK356 zw8j*3xzx?8Gh5a#rbh{ik{89a+AtxP-OCcz%RafPZ;FV^nd_w&5_*>h&jzwdeX%o#Fdk}Q5n)~tkUUDx;f`Id{#<=L=v`Q1(G=M0Jx zYMak)=}`}WIUyAGEI#9yKMg2im6chqUgmHFe>e*S25XK!-@(KE;x4L{jjREpci zk95$i=IvF2-6Vp^Jp$J*A$NAi`WYMj1bAYEFx!Pjo`YA~t9A{`K4QS_Tht-^utXzH z6yeg_7gdKmDc_D8-RkbE>|y6=bI-!JHxs$g%e(K$VDh^u9@&|QMziVYrfZF&9u~t` zK(7yFkH=nBx(nX%p{)1(qrT$$9MLZoa$yLO7Cx-^O5r zDN5L`ELD{T!hT?|<|n{|MNTzTb(oBhV;&gGA~nwwc_Zej6-K;cO*FdIKbE`lHXqP0#U)8E%Lcr||}v5yjg{w=dKZ5WQ+gqO%uSqZ-S74jT6v%5!-*$v}OGcC!< z88X`-lU%Eyx!^b3`;Bwg89~^L762`{U5hQ>dJXtG_)TZ7>NO?8wC*-u+3jkMVq4o^wh zM1>@Rb;E~owow`Y9F2grt23X2|3{C|OF1{e9uIfVscUk0{sy4ed4G5ZpEq7~xdUk^ z!km)&5ks56tuyHP=eb*Puy3wlrv}v;OU!@M13{celC{3Ml7SOh>XmtqIw)F3wG?u4 z(OBi@Jkf(-Zx_BZchCQ|H~z&GAaxbHkEwvL({%U=2+I6HbG*~aW2fsXZ+rQSgUC$F zclPC%;2^IZBnimSL~zp8vEog}NZsyJ+s?)QR)g904YNo%P~*PU&Z3uR(2dG%PKlUp zCF*f@EagSl;3OvH{O3y#?V%K?H#L(DbAM+{=DRyI!JS5;0qtsy;bL}yFRr%e`a%`~ zeNePAkW=Hd8cap8%D=yQ`kexesQ{(o>&r)JJDRt&ZnAIqtbZ(jE+tYNr6C4S7jtHs z9EQRte5 zZ8p(a^%YaUxOrpOLX**`=Hc z$GU&e{w;KFue^Y8^!G4=Iu9reqT95&&}B!;Pz?!cHu_Y{8CHfdHVA$ZY90(YVL31| zG^T&sMm(v4BM>=J?V~und(y$7O>SpktUyHucHaj&h-XZDI+x}VzE&JiqtF~o{F23( zkSugKfAE|L`(oJXh}v!$Xe_f0T(EP|&?Gs;{&;p)Q~{vd)F5t4dR5Iz`7vip)9p=4 z$ah+SEDwh%YI^!q>l++C^VM5*0ER)u0mJl#gTTEKhsF~`3^wnJWa1Plat`mnvV}&Q zxENaHhWi+C4ThYIrs(>_id9>_c$Ns_291b5Ygrs+Rqe-Ct^W0Tidvq~gNz;PWnrcH z!WNl3l@yjxxxUuI+YxS|q6@=JTle9k+uB+a$eGL0W+!kV$8T2h6@KKB@}dJD&vh8c zmYsD~yqxak%4+Lqf&4LR^-J_NwV9#xw-O(kbVroxCW!H`?qu|G6P-8C=( zYzKT(WN=!YBMdXGHPXUnhu$pAx%47TC_Q>}kuwZWr*eg$rN|3_C4Z8IprDJRZ$a`x zR3$#!U-vTeH(D|15<+}?Mu~FlKr_Gf3X`Q6_iJl+cVl2Ai z8bo>leW-6Sof52MEbs|o`v+BcGWS;nP03lN4Xl_s=}FUu05P>@gq*|ZX<8@zgEfrI z(#2g;FGaYb%(8uh`k$722c9Qej=pf#_ur&z8@^G4zns&<^m~;iU!v63gm)PQUtA@l z$&U?IXTL1`b*9Bd$Tyci-foI~$DwA%g-eT=dwR~(+!^t4*7CLjjIjQFO z**on8X9C&I`#hOlHctGxUKXQ=R8gT*S#hkti$z&K*vz_H-cW~5)t!zQXt%B5tlxi` zDz-yow%g{r&YC0U?RrVO7(=t#kC}1m2d1lP`70)?Yx?^iw9=$g(xJ)~c9QCjJ6$io zcEX7h2-|s72D}t_ncZIpy4gaE*FQs)5`OR`n`_Y{|5~l;b9}`t!i4jkf4QNnxE?*A zLguK}>=2)X5j*8;!rC!TQ+w7hZuow*lzPW~B_4UkCIDMk2t~SUK`dbC8W^+)zry)c zM35{FE$O=X;y2h(7hv8#Tr>zOj|AHg&A)g zAF6nF19mR6*aOF()FHc!u-gCnwvA8=;Y$%;g6ja|3i1V8V|AJ5Zv%D$U5Y_7*ErfvGeH=p`!_nG%S?++d&rVcS*G(0@EJ7(P~+9epzi4_O-s2pg2-ph zu3-PWNO?3fnKhuH;cN|Hmepp&Luh|mMZ4+s8!7`^PH&=yW!{~KJ>C`nBIiOJLi>2h zL~NI}I>2P=&Zg(z`h+sf|1vqIDsCoO9~EQ5&aY?QDP9pmYv-2@x(@pG!np5fr>GI{DY>MKC$nvAIB4SSN`Lb zpZS7?$LDsD(Ikxh{ps@Q&HD5&;k|qg{kp^D9Utm(zii`wr3mHNvl!LQxm7d&na&|& zK1XH(@%`rJbHL$9Ot|wa{W~yal+ruAVmH^Xli39?E_E4!CL!1#p!zz2r1B4%9u@j& zwRru8kU_ght4iwQojVnZ;N9!h&n!3eDdR(}X2oCM&qI5##fXL)+Bk2v1w(rN+I{Et zWxnus#z&A&3^Sa|)taj|6|Yz48cQwJu?rpq(Gw4k&+)Xr@Kv7YXH^-p+oFX}Kb^n@GadSmz{=dJvvVB=ktZiii)}}3fuG~PZuBd zg62%zeB4mp(6Y-2y>FC}DKMeJywak#%XJaeuw%IUv6*U zBYsO*W%3S!Z*0wYoMUONN^sBb_1@m;A2fR**8IBTo!{iSe6z7MkxNYMAw#{386psJ59!fgw5PGsJE3qVRL%Ipvb0ZvuDpf6G;O}+V(1RM z0!*%~H8d~xx33uB0)^E;@fi%qdxf|*QM^B9qSxV!t_pgJ=Ul37Aty#9LllS`&j-x21Y53hm;W@eFYWuw%5AD1{Rtcppo}V#sVD zTVMQUdwp#iP`MI9Fi};>gpkzbig{uNYZ*WHsUmZav0hO?Bjl-vP%?HeQyhehF9yGy zk2;83ZXvKpM}Mj2r?{sUv(-)(t*3SouSlAZd244RZGi1e!Vnn9cdcn4528Zp!i<0Y zV6t}j4(+k$vKpzNnr)0)x`zH%F*B1rrp^Ix?+a9a$wtT@Gzmv=PIjhZlTD;W)xdeX zctutrj5?pe>yGDj#q1 zuzXiD*^k+gT1V)Us~gbWfqC}W7wJ}k2giCcT?4mPp+3XMC+U5jq;6N(GC0hoRxUZ| zlGBeOp~Oybj6lC;WRCPojO}JiI^K=ixppk^t5T*GmKZN!W^>cZ047VxD1>{(Qo-04yH`dJO z;ypG1GLS^kG=(C|1Z!6vqznRaev#s2UNR&hrBlrmJkTFoV&D@au70BGUsntY@=TEa z5eoQw9mry%qCm4CUqi{1|p5Qi6L_PKO72%au|2&_f zLnSI5$Y3konXoz`CG)^1L}A!Co8T2=B~y%skR0=J)!jAe8S9S4a6NhbsCQNJIQsKn z=5-E>g{=N|htH5KqFs7y_5<5(8yzIle;-f z{x#D~PMWX8`b((jid1NJB2lZ~Tp>C&q2(T9&!T`~1mhi!{}>@nv1_eiCZMDBo>K;A zZWxW6dav5`C3XisG{*;4GIVL>7Mt0g$nTja#k0!#2kpP+=c@9QSoje1)^tz zr+NhYZHb}iljD-D6?ry_kFir0Lb2rU7u9^#)Tq>`d2;24se#sO`glL!`yv`^pObj$*kv>c`dof9au{z*RCnlX##x_2|i=`HXA3fQdsJ-@9?L6~l)W!Tp;lHvbq zyip0-7k-2D(W|PR4rK>Vp=1y_?%Mr5+w^+jK$7~0<(vZYc1{KCqy2lc%tDseAsSUZ+;(LnKHN2KXXZ~Y( z%}1}==}PTh?2rlJ@1Co;s9^isyS#;r)~4;E!oW3Eh)pAG7l2Uf^r+gNzI~`tu3x*N z(yrGQ9624cC(zxNct7E;e6vn7IS0|+qHN$b^G|7r%ZB)gHX1c9tG$0UjPO%C{-C#( z$7<%sQKAk&mIZA8XAZz8Ez~d5ed`KDY9iil&e<0DZoJzV#KoVlJ~!X+IDCt*LgLth zOh0~VH2+Zf&e@?Ep?0$08TOW~A1%z9fiUK_HtT@CKR|^oUFAq2GuGD0ABOfWySW_8 zDw_p{obfK-tZz;T&to3rE7g-W2p;-%78XUch*ULfKqwi7Jqzj;qLEb)ooA`-FX&q@ zyYN*NeiFHZK^Mg4t4@z9R(-5}TcprGp--#YT((vXq$@n?+P>(lJK_}+H>KH83Ua&L zpRqez7?tX^pcJxX1L>L;7=(N~5ADX2A#>xkW>bGCt^1`)_NML-mkZwcQB>lJOc*;H z^)w%$!#j8#vbq-&l54~w&lN@>%Yk&6o9|0sDn{xBRK`5HnWHl}qvkNBK{Ac_KZ?dE z#q@26ZxYjQHLDirm+Sugcq?Q6H7CMq(IoZ$JhOZKr)##T)ZwJ>fkb8;ZE!E1Bonc< zlIrl`jJ+Kw%PBmkQH2TWU*K3<5o@S@On3V0`QMUBZ<;H7$5zj=B1fR()Qo>ZL)H&! zb!!uE6d;B#ls#wXEH9pp-9-I9c9jh zwpI(E|5)6nV+36T{bY;R{Mutw|S~_=&UF@nq&@5ir>2>hxjn ztpyfYmzmJanxbG=Bk~qtac73IUtYZw8jo@DzWME@Cv6J#w6o8k8xUE2o(}-YVb;t< zWPUf@N#zAE>p-q7xY&j{p?jk}ysw@1VCpMBXk5`~M2)g{pWZi}1i`p`$q3f@}O_O-d|g)3Y;H zVQ&gGXhW>2HTmJ+030?0^p8!Gaw(p2J5NFtpR9`HWEy4hMPM@?h(3gplbMc3VikVF zeZh1L#ky8Wb|fnu8Izy|LglAihnE|IZDu5nC&5#x?xKgt+Iy|xM=HB}q0-%a)NX!p zQ?&y$`-A4WEhEPD_d7@wQj?mA;Vvn+j{5{Nl%iT26241Afw@rr&pXS%UHg(mUbcf|2-!|YWx9CQZ7nfX^I za;$>7^I+5d|DzwL*{2rHpSpXEF~I-_@iSG?$y&7#VtE@aSK6zm?Q6De#x3V&K=(=8 zVturQcyHTwqT)*Yn$o*l-oIxW&EIJYlhZm7v8{>5Q90)~c7~G!wg=}SjK(K)rmoLU z1>IlAy+p zCzKjT!jV#BLMGk!6|}||_O>cWvlPaOdu znI|O8JPu-lY{n5kP9*f)0eFe%j>CjT2laMkFpW1Bc-eq*E78jRO*P3K=EvF8JFWT+ zGC?@Qd;Zs$MYk~7AOT%#w6)F>hk3#)A6>T*G~mg+y8?)r<+|hQ&2+{a6VXa$@h#VO z{ff5j8&misJ|74sWS|Be6Rw_8){tuz4;pap5P5&G{O>-Z>`V4X{FYr;Pc4YsK~eC9 zsxATj(jyp(;=2z&P3xujKgoh~a?v*-mWK+7(CRvhJT2XEKjbM-SV-C8Ok}h;T}L zd$U~IP^?AW$Dx+0nCE>5M!i~Nj$uR9iqz}!?+U4gKn`aXVyYy9pF?52a#ac}`UZy; z(FWqGKc!qo_@0_O^>%0d< zv%W`7dw(Dv{f?s>DcHE&X{5ksADp#V0>L=VYMQ3esHP5zxylLEd>%EcMy34Sz#tN^ z+%fOb*1^L2=3~(D)>N%N>6i!AnvATq=;^$l0_(2B4esp9qBnWIpK0wzMA-B-{vp6-uoK${C&>{T@(hu@M2>X*Xs;LfEDKb;096Nd% zr>5#CjU9h^IT;mhG_t5Yz_v3E$E;E&3{Lp4O{w9ICw}ZFQheGW`$jmC*nb$7)Hz%8 zren!VT~jUO@?4tU^?Dwq`et**=iEAA7=Auk{VcR2wJ?9rGqQP>I$XPwRTDaGq|LI} zmHb{>sCrJQ)cu2=XKrP^P6-F3J?deiFzVV#t`JPbVf&o9XEGMWH=Y~zv7ej!4;U&8 zXY{OwF@dqNcw4&5QweF%)RRc0xS<$27P^isOG2e?uKhuqJ^=OfN+rpwoHPqp6O9C3 z04<7U2C4boIV&AJUg0=vSup6i>pw}Z4Ulu`LFHQ{h~%%JTb3aWyz27B7wow9-xF*3mMYZ|iibaHMNg-SOzMO$`P3fc=X3c;Phk zYmP+-g-TM*S*%KNIT<2wy!TV@+qC?A!Fnj6J3Udu3^Bg)@ULqx`f{Q3Xl5Zh+OyOp zRqkwDCcr4=M8XrCCTz&!=Mu^}OmQYAp6|31MLn~SQ%J)+IDq4$h--~l)A@Lop=7tJ zYdc=yT0jeH91z!T&B7*LqP0N`Mf}Y>XXJrva|gKZ55@(Wb=KCi{6Q1+2nqZA+q<(? zvac07tuiKljBHX3o#JWru$wj{H(r7n4lSJ7t+f^sk~uv3xf2enD7DBx&SuQ5Y?Rgg zL1Tthz4&ll^_Foa(cu?<_%!a9Vgl(=?h<>+!mIeeGA@XxQ@Mvyu9`<`aiTmfwVRI~N7WmV&vK?p9}Zh1 z8C<1kHm5bn9!l4=F13;jFM?SX%GfK_p5`o zTiqo2MJut8rh@cBT=1g2&~qea?NDu+yP@0x^N3%0>vC%reJq$%^m0npvbCUjt~iv3 zpLTZV_6Y_`_-Ee~AB!mY#JrJVI9-qg#pd#CyB zWU8JBo5@nl`b}&U($w{lb=b!tgZ~(C>)q^MvauxY71H>oo+Le6o}Dl%@x4Awl%-kI zZD6Br$sjzu{*2_8Vq=KUj?YDe*x21ZkMl-7?-m-ijO%4lWTIESK~RfFVlLr~XlK5K z2J3lvf>Uw?rqCn0awIEbvN(X1$MG{mX>qT>)CMmRE+v!ba=Gpp88>N|s&xBge-Hbs z?t&f$CA?1!DT`T-d*!7>BN%6&R4C_Li-*ru7@?U;jKbEE>AktS+nnVX=rAe*=qyot zVsU)d0AKEBLB@VVn_N>?E79f3(xJ#c8N6MAy8e|p2}!}Q2qQ%uf`dH`|JJqM?bMcse7Jjdts6^Gsf-UjtC*I%bIhRTcT(B(Ayz>vRl)`WOc^`ct z7fg3>#{FKGm`*?rZh_Eobrvb&-UuGVGzRt}laEp1+T61jPkl`&z371Szn|iX(nUfg zrlYDBr)H!--0WMLmUyo1dJocTY4sJn6@HWkkYoCRY|~cW=T;(1FRtY3I^Ma{LMLIJ^@F;qc!_E}vwu9e^vMP^7 z_GIoNAp;Ol^VsawPA|1nQTeo=G2pCh&TuEfXX&562V8{vzcE~#VcqXaJ(8mB(sOOC z5iet?0+v61#Q5U3`fq@0R)B&A$jOOnbycvV9qjXY?)nURya`_mMF~XHxgKJCy#A?{yV`z%!A#ogQKE8PZyW% zi}Y!%8`xn~xg5d}^7nJ3!K9(a^Bt7w7Yh0)IYU)J=@%<%xmEEvue6NLuTVD5zVkJs zfMQ4nIHdEylG!3!5WS|JXrL8*dQk4(D$ALM%0Cp@RZhJxe_aPsF@vL8Is&!VW92&^ zIa=b}U9EIZ%QuAE(-3Wgq&d=%?kmraMNPNN1`RdpbgQ1*+RvHgA-%b;7~X0Q<qq zO?4Y=nRI{Eu!KB0AS=JlKctAg**~mSwc+e-ak@v5s3{Y*4QOKgTU&(aQm)gcHhTP? zwq%4OXSI1d0a34kC&k(i{nI+yN9RpGfdci*ITkcmGxsArls@~G4&fQG)<-a}Hrp0g zn-0E;oeL=mt0`-7NV4b5r{>8Ugx>Rr9`q0N59cmR_2fJ7AH6MhjLhGU!Ph_BXVON} zDjU7Jr&XwVjMoEii~>^$!Kcr2;?&=Cz2NwpF&tvSuf`s}5lwAvDT5x@Q_R_6`SRkX3QY+kY7(QGy2>bSz zwu2;XWTQZaN{Y1$w>x0CYML9}p|m%Z{C)dH(Z6+G_-$!L_h{K3}xru8}-pFWdjVWYn^7_#

|8f-DcsDN2 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 wI2 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�sz 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

@?BqY5702Qt$@E;?Fub!sS!`g@~(`W88ZJBx% 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

hJS(qo8I$@J^z}(p1CB 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�&`KkF|!N2d&yPZP;@ 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 literal 0 HcmV?d00001 From dd3ccf653da7e2e448aeef7dc89bec73decdb545 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 1 Sep 2026 12:35:57 +0200 Subject: [PATCH 174/241] Updated caption. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5b937e..133b1f7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Demand cleaning is performed with tlean, while this module remains responsible f

- Example diagnostic showing electricity-demand provenance and basic gap filling for selected countries (May–October 2021). + Example diagnostic showing electricity-demand provenance, basic, and advanced gap filling.

## About From 07a2f87f82d9cdfcc86265b58923f6474e9a4a52 Mon Sep 17 00:00:00 2001 From: ddahawkins-TUDelft Date: Tue, 1 Sep 2026 12:37:33 +0200 Subject: [PATCH 175/241] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 133b1f7..c3b09d2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Demand cleaning is performed with tlean, while this module remains responsible f

- Example diagnostic showing electricity-demand provenance, basic, and advanced gap filling. + Example diagnostic showing electricity-demand data provenance.

## About From ac09f96ef298e107ba0c3a5854b7ed22386903ea Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 11:26:59 +0200 Subject: [PATCH 176/241] Updated so the plot shows only the filtered countries not all. --- workflow/scripts/demand_electricity_raster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/demand_electricity_raster.py b/workflow/scripts/demand_electricity_raster.py index 31664f9..ee572ae 100644 --- a/workflow/scripts/demand_electricity_raster.py +++ b/workflow/scripts/demand_electricity_raster.py @@ -84,7 +84,7 @@ def main( map_raster(countries, demand_raster) plt.savefig(plot_raster, bbox_inches="tight") - plot_national_profiles(demand) + plot_national_profiles(demand_filtered) plt.savefig(plot_profiles, bbox_inches="tight", dpi=110) From 85d1e24a84f8e8f000bd42dd370b9960f4a14742 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 12:24:13 +0200 Subject: [PATCH 177/241] Modified _advanced_execution batch_id generator to help avoid Windows file length issues. --- workflow/scripts/_advanced_execution.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py index 08479e8..f161bc4 100644 --- a/workflow/scripts/_advanced_execution.py +++ b/workflow/scripts/_advanced_execution.py @@ -87,18 +87,22 @@ def build_group_id(*, start: pd.Timestamp, end: pd.Timestamp) -> str: def build_batch_id( *, source: str, start: pd.Timestamp, end: pd.Timestamp, countries: Sequence[str] ) -> str: - """Build a deterministic identifier for one provider batch.""" - countries_key = ",".join(sorted(countries)) - - countries_hash = hashlib.sha1(countries_key.encode("utf-8")).hexdigest()[:8] - - return ( - f"{source}__" - f"{start.strftime('%Y%m%dT%H%M')}__" - f"{end.strftime('%Y%m%dT%H%M')}__" - f"{countries_hash}" + """Build a compact deterministic identifier for one provider batch.""" + batch_key = json.dumps( + { + "source": str(source), + "start": pd.Timestamp(start).isoformat(), + "end": pd.Timestamp(end).isoformat(), + "countries": sorted(str(country) for country in countries), + }, + sort_keys=True, + separators=(",", ":"), ) + return hashlib.sha1( + batch_key.encode("utf-8") + ).hexdigest()[:16] + def index_batch_ids_by_source( batches: Sequence[Mapping[str, object]], From 53093a166244c75668a02c4eda3ac5e00dd5fa41 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 14:43:30 +0200 Subject: [PATCH 178/241] Fixed bug where shapes.parquet was not determining the set of countries. Transferred the country sets from settings.yaml to source_registry.yaml per source. --- tests/unit/test_advanced_execution.py | 86 +- workflow/internal/settings.yaml | 41 +- workflow/internal/source_registry.yaml | 107 ++- workflow/rules/advanced.smk | 105 ++- workflow/rules/automatic.smk | 50 ++ workflow/rules/clean.smk | 76 +- workflow/rules/prepare_demand.smk | 2 +- workflow/rules/source_entsoe.smk | 32 +- .../rules/source_entsoe_power_statistics.smk | 28 +- workflow/rules/source_neso.smk | 22 +- workflow/rules/source_opsd.smk | 21 +- workflow/scripts/_plot_timeline.py | 807 ++++++++++++++++-- workflow/scripts/_tclean_config.py | 70 ++ workflow/scripts/clean_demand.py | 18 + workflow/scripts/plan_auxiliary_data.py | 7 + workflow/scripts/plan_target_data.py | 188 ++++ 16 files changed, 1450 insertions(+), 210 deletions(-) create mode 100644 workflow/scripts/plan_target_data.py diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py index a6ace8c..d97b5ea 100644 --- a/tests/unit/test_advanced_execution.py +++ b/tests/unit/test_advanced_execution.py @@ -38,18 +38,66 @@ def test_group_id_depends_only_on_period() -> None: assert build_group_id(start=start, end=end) == "20200101T0000__20200201T0000" -def test_batch_id_is_independent_of_country_order() -> None: - """Test Batch ignores country order.""" +def test_batch_id_is_compact_and_independent_of_country_order() -> None: + """Test batch id is compact and ignores country order.""" start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") + first = build_batch_id( - source="entsoe", start=start, end=end, countries=["ALB", "GRC"] + source="entsoe", + start=start, + end=end, + countries=["ALB", "GRC"], ) second = build_batch_id( - source="entsoe", start=start, end=end, countries=["GRC", "ALB"] + source="entsoe", + start=start, + end=end, + countries=["GRC", "ALB"], ) + assert first == second - assert first.startswith("entsoe__20200101T0000__20200201T0000__") + assert len(first) == 16 + assert set(first) <= set("0123456789abcdef") + + +def test_batch_id_depends_on_source() -> None: + """Test batch id changes when the source changes.""" + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + + entsoe = build_batch_id( + source="entsoe", + start=start, + end=end, + countries=["ALB"], + ) + opsd = build_batch_id( + source="opsd", + start=start, + end=end, + countries=["ALB"], + ) + + assert entsoe != opsd + + +def test_batch_id_depends_on_period() -> None: + """Test batch id changes when the period changes.""" + first = build_batch_id( + source="entsoe", + start=pd.Timestamp("2020-01-01", tz="UTC"), + end=pd.Timestamp("2020-02-01", tz="UTC"), + countries=["ALB"], + ) + second = build_batch_id( + source="entsoe", + start=pd.Timestamp("2020-02-01", tz="UTC"), + end=pd.Timestamp("2020-03-01", tz="UTC"), + countries=["ALB"], + ) + + assert first != second def test_source_batches_group_countries_by_source_and_period() -> None: @@ -68,17 +116,22 @@ def test_batch_indexes_preserve_compiled_ids() -> None: batches = build_source_batches(_requests()) by_source = index_batch_ids_by_source(batches) by_group = index_batch_ids_by_group(batches) + assert set(by_source) == {"entsoe", "opsd"} assert len(by_source["entsoe"]) == 1 assert len(by_source["opsd"]) == 1 - assert list(by_group.values())[0] == [batch["batch_id"] for batch in batches] + assert list(by_group.values())[0] == [ + batch["batch_id"] for batch in batches + ] def test_serialize_batch_produces_json_safe_values() -> None: """Test for json safety.""" batch = build_source_batches(_requests())[0] serialized = serialize_batch(batch) + json.dumps(serialized) + assert isinstance(serialized["start"], str) assert isinstance(serialized["end"], str) @@ -98,19 +151,32 @@ def test_empty_execution_plan_has_stable_contract() -> None: def test_load_execution_plan_and_get_batch(tmp_path) -> None: - """Test load execution plan.""" + """Test load execution plan and batch lookup.""" batch = serialize_batch(build_source_batches(_requests())[0]) + plan = empty_execution_plan() plan["batches"] = [batch] + path = tmp_path / "plan.json" path.write_text(json.dumps(plan), encoding="utf-8") loaded = load_execution_plan(path) - selected = get_batch(loaded, batch_id=batch["batch_id"], source=batch["source"]) + selected = get_batch( + loaded, + batch_id=batch["batch_id"], + source=batch["source"], + ) + assert selected == batch def test_get_batch_requires_exactly_one_match() -> None: """Test get batch requires a single match.""" - with pytest.raises(ValueError, match="Expected exactly one auxiliary batch"): - get_batch(empty_execution_plan(), batch_id="missing") + with pytest.raises( + ValueError, + match="Expected exactly one auxiliary batch", + ): + get_batch( + empty_execution_plan(), + batch_id="missing", + ) diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index fc2a317..963e9a8 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -4,43 +4,4 @@ resources: # Links for automatically downloaded files load_opsd: "https://data.open-power-system-data.org/time_series/2019-06-05/time_series_60min_stacked.csv" population: "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/GHSL/GHS_POP_GLOBE_R2023A/GHS_POP_E2020_GLOBE_R2023A_54009_1000/V1-0/GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.zip" - population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" - -load_entsoe: - countries: - # Download all ENTSO-E countries - - "ALB" - - "AUT" - - "BIH" - - "BEL" - - "BGR" - - "CHE" - - "CYP" - - "CZE" - - "DEU" - - "DNK" - - "EST" - - "ESP" - - "FIN" - - "FRA" - - "GBR" - - "GRC" - - "HRV" - - "HUN" - - "IRL" - - "ITA" - - "LTU" - - "LUX" - - "LVA" - - "MNE" - - "MKD" - - "NLD" - - "NOR" - - "POL" - - "PRT" - - "ROU" - - "SRB" - - "SWE" - - "SVN" - - "SVK" - # - "TUR" #Data is unavailable for Turkey + population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" \ No newline at end of file diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index c4ce109..ba2b1be 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -12,12 +12,82 @@ entsoe: display_name: ENTSO-E temporal_scope: start: "2005-01-01" + contexts: + - "ALB" + - "AUT" + - "BIH" + - "BEL" + - "BGR" + - "CHE" + - "CYP" + - "CZE" + - "DEU" + - "DNK" + - "EST" + - "ESP" + - "FIN" + - "FRA" + - "GBR" + - "GRC" + - "HRV" + - "HUN" + - "IRL" + - "ITA" + - "LTU" + - "LUX" + - "LVA" + - "MNE" + - "MKD" + - "NLD" + - "NOR" + - "POL" + - "PRT" + - "ROU" + - "SRB" + - "SWE" + - "SVN" + - "SVK" entsoe_power_statistics: display_name: ENTSO-E Power Statistics temporal_scope: start: "2019-01-01" end: "2026-01-01" + contexts: + - "ALB" + - "AUT" + - "BIH" + - "BEL" + - "BGR" + - "CHE" + - "CYP" + - "CZE" + - "DEU" + - "DNK" + - "EST" + - "ESP" + - "FIN" + - "FRA" + - "GBR" + - "GRC" + - "HRV" + - "HUN" + - "IRL" + - "ITA" + - "LTU" + - "LUX" + - "LVA" + - "MNE" + - "MKD" + - "NLD" + - "NOR" + - "POL" + - "PRT" + - "ROU" + - "SRB" + - "SWE" + - "SVN" + - "SVK" neso: display_name: NESO @@ -28,4 +98,39 @@ opsd: display_name: OPSD temporal_scope: start: "2005-01-01" - end: "2019-03-01" + end: "2019-01-01" + contexts: + - "ALB" + - "AUT" + - "BIH" + - "BEL" + - "BGR" + - "CHE" + - "CYP" + - "CZE" + - "DEU" + - "DNK" + - "EST" + - "ESP" + - "FIN" + - "FRA" + - "GBR" + - "GRC" + - "HRV" + - "HUN" + - "IRL" + - "ITA" + - "LTU" + - "LUX" + - "LVA" + - "MNE" + - "MKD" + - "NLD" + - "NOR" + - "POL" + - "PRT" + - "ROU" + - "SRB" + - "SWE" + - "SVN" + - "SVK" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 0294b51..024601e 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,30 +1,38 @@ import json -def _read_auxiliary_plan(_wildcards=None): - """Read the resolved advanced execution plan.""" - plan_file = checkpoints.plan_auxiliary_data.get().output.plan +def _read_auxiliary_plan(wildcards): + """Read the resolved advanced execution plan for one shape.""" + plan_file = checkpoints.plan_auxiliary_data.get( + shape=wildcards.shape, + ).output.plan - with plan_file.open() as file: + with open(plan_file, encoding="utf-8") as file: return json.load(file) -def auxiliary_acquisition_plan(_wildcards): +def auxiliary_acquisition_plan(wildcards): """Return the execution plan after the checkpoint completes.""" - return checkpoints.plan_auxiliary_data.get().output.plan + return checkpoints.plan_auxiliary_data.get( + shape=wildcards.shape, + ).output.plan def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) batch_ids = plan["groups"][wildcards.group_id] - batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} + batches_by_id = { + batch["batch_id"]: batch + for batch in plan["batches"] + } return [ ( "/automatic/" + f"{wildcards.shape}/" f"auxiliary/{batches_by_id[batch_id]['source']}/" f"{batch_id}.parquet" ) @@ -34,44 +42,62 @@ def auxiliary_group_source_files(wildcards): def auxiliary_rule_cleaned_files(wildcards): """Return cleaned auxiliary files required by one advanced override.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) group_ids = plan["rules"][wildcards.rule_name]["required_group_ids"] return [ - ("/automatic/" "auxiliary/cleaned/" f"{group_id}.parquet") + ( + "/automatic/" + f"{wildcards.shape}/" + "auxiliary/cleaned/" + f"{group_id}.parquet" + ) for group_id in group_ids ] -def advanced_constructed_profiles(_wildcards): +def advanced_constructed_profiles(wildcards): """Return constructed profiles required by active advanced overrides.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) return [ - ("/automatic/" "auxiliary/constructed/" f"{rule_name}.parquet") + ( + "/automatic/" + f"{wildcards.shape}/" + "auxiliary/constructed/" + f"{rule_name}.parquet" + ) for rule_name in plan["constructed_profile_rule_names"] ] -def final_clean_demand_input(_wildcards): +def final_clean_demand_input(wildcards): """Return the cleaned demand appropriate for the configured mode.""" if config["gap_filling"]["mode"] == "advanced": - return "/automatic/" "load_advanced_cleaned.parquet" + return ( + "/automatic/" + f"{wildcards.shape}/" + "load_advanced_cleaned.parquet" + ) return rules.clean_demand.output.demand -def final_cleaning_method_input(_wildcards): +def final_cleaning_method_input(wildcards): """Return cleaning provenance appropriate for the configured mode.""" if config["gap_filling"]["mode"] == "advanced": - return "/automatic/" "load_advanced_cleaning_method.parquet" + return ( + "/automatic/" + f"{wildcards.shape}/" + "load_advanced_cleaning_method.parquet" + ) return rules.clean_demand.output.cleaning_method -def advanced_external_profile_files(_wildcards): - plan = _read_auxiliary_plan() +def advanced_external_profile_files(wildcards): + plan = _read_auxiliary_plan(wildcards) return [ f"/{filename}" @@ -83,7 +109,7 @@ checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, output: - plan=("/automatic/" "auxiliary/advanced_execution_plan.json"), + plan=("/automatic/{shape}/" "auxiliary/advanced_execution_plan.json"), conda: "../envs/module.yaml" params: @@ -99,18 +125,17 @@ checkpoint plan_auxiliary_data: rule finalise_clean_demand: input: + target_plan=target_data_plan, demand=final_clean_demand_input, cleaning_method=final_cleaning_method_input, output: - demand=("/automatic/" "load_cleaned.parquet"), - cleaning_method=("/automatic/" "load_final_cleaning_method.parquet"), - cleaning_method_rank=( - "/automatic/" "load_final_cleaning_method_rank.parquet" - ), + demand=("/automatic/{shape}/""load_cleaned.parquet"), + cleaning_method=("/automatic/{shape}/" "load_final_cleaning_method.parquet"), + cleaning_method_rank=("/automatic/{shape}/" "load_final_cleaning_method_rank.parquet"), conda: "../envs/module.yaml" params: - source_names=config["load_sources"], + source_names=active_load_sources, gap_filling=config["gap_filling"], message: "Finalise cleaned electricity demand and provenance." @@ -123,14 +148,18 @@ rule clean_auxiliary_group: plan=auxiliary_acquisition_plan, sources=auxiliary_group_source_files, output: - demand=("/automatic/" "auxiliary/cleaned/" "{group_id}.parquet"), + demand=( + "/automatic/{shape}/" + "auxiliary/cleaned/" + "{group_id}.parquet" + ), data_source=( - "/automatic/" + "/automatic/{shape}/" "auxiliary/cleaned/" "{group_id}_data_source.parquet" ), cleaning_method=( - "/automatic/" + "/automatic/{shape}/" "auxiliary/cleaned/" "{group_id}_cleaning_method.parquet" ), @@ -144,6 +173,8 @@ rule clean_auxiliary_group: "enabled" ] ), + log: + "/{shape}/auxiliary/clean_{group_id}.log", message: "Combine and clean auxiliary electricity-demand sources." script: @@ -156,7 +187,9 @@ rule construct_auxiliary_profile: sources=auxiliary_rule_cleaned_files, output: profile=( - "/automatic/" "auxiliary/constructed/" "{rule_name}.parquet" + "/automatic/{shape}/" + "auxiliary/constructed/" + "{rule_name}.parquet" ), conda: "../envs/module.yaml" @@ -171,16 +204,20 @@ rule construct_auxiliary_profile: rule apply_advanced_overrides: input: - demand=("/automatic/" "load_basic_cleaned.parquet"), + demand=rules.clean_demand.output.demand, data_source=rules.clean_demand.output.data_source, - cleaning_method=("/automatic/" "load_cleaning_method.parquet"), + cleaning_method=rules.clean_demand.output.cleaning_method, plan=auxiliary_acquisition_plan, constructed_profiles=advanced_constructed_profiles, external_profiles=advanced_external_profile_files, output: - demand=("/automatic/" "load_advanced_cleaned.parquet"), + demand=( + "/automatic/{shape}/" + "load_advanced_cleaned.parquet" + ), cleaning_method=( - "/automatic/" "load_advanced_cleaning_method.parquet" + "/automatic/{shape}/" + "load_advanced_cleaning_method.parquet" ), conda: "../envs/module.yaml" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index f94c824..7cc6c0e 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,6 +1,9 @@ """Rules used for generic automatic resources and validation.""" +import json + + rule validate_temporal_config_semantics: output: "/automatic/temporal_config_validation.json", @@ -34,6 +37,53 @@ rule validate_gap_filling_config_semantics: "../scripts/validate_config.py" +checkpoint plan_target_data: + input: + shapes="", + temporal_validation=( + "/automatic/temporal_config_validation.json" + ), + output: + plan=( + "/automatic/{shape}/" + "target_data_plan.json" + ), + conda: + "../envs/module.yaml" + params: + source_names=config["load_sources"], + source_registry=SOURCE_REGISTRY, + temporal_scope=config["temporal_scope"], + message: + "Plan target electricity-demand data acquisition." + script: + "../scripts/plan_target_data.py" + + +def target_data_plan(wildcards): + """Return the target-data plan after the checkpoint completes.""" + return checkpoints.plan_target_data.get( + shape=wildcards.shape, + ).output.plan + + +def read_target_data_plan(wildcards): + """Read the resolved target-data plan for one shape.""" + plan_file = checkpoints.plan_target_data.get( + shape=wildcards.shape, + ).output.plan + + with open(plan_file, encoding="utf-8") as file: + return json.load(file) + + +def target_source_contexts(wildcards, source_name): + """Return target contexts assigned to one source for one shape.""" + plan = read_target_data_plan(wildcards) + + return plan["source_contexts"].get(source_name, []) + + rule download_population: output: population=update("/automatic/population.zip"), diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 53a0b7c..6ac7c9b 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,27 +1,56 @@ -def configured_load_inputs(_wildcards): - """Return prepared demand files for configured sources.""" +def configured_load_inputs(wildcards): + """Return prepared demand files for active target sources.""" + plan = read_target_data_plan(wildcards) + return [ - ("/automatic/" f"load_{source_name}.parquet") - for source_name in config["load_sources"] + ( + "/automatic/" + f"{wildcards.shape}/" + f"load_{source_name}.parquet" + ) + for source_name in plan["active_sources"] ] +def active_load_sources(wildcards): + """Return active demand sources for one target shape.""" + plan = read_target_data_plan(wildcards) + + return plan["active_sources"] + + rule clean_demand: input: load_inputs=configured_load_inputs, + target_plan=target_data_plan, validation="/automatic/gap_filling_config_validation.json", output: - demand=("/automatic/load_basic_cleaned.parquet"), - data_source=("/automatic/load_data_source.parquet"), - cleaning_method=("/automatic/load_cleaning_method.parquet"), - cleaning_method_rank=("/automatic/load_cleaning_method_rank.parquet"), - gap_report=("/automatic/load_gap_report.parquet"), + demand=( + "/automatic/{shape}/" + "load_basic_cleaned.parquet" + ), + data_source=( + "/automatic/{shape}/" + "load_data_source.parquet" + ), + cleaning_method=( + "/automatic/{shape}/" + "load_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/{shape}/" + "load_cleaning_method_rank.parquet" + ), + gap_report=( + "/automatic/{shape}/" + "load_gap_report.parquet" + ), log: - "/clean_demand.log", + "/{shape}/clean_demand.log", conda: "../envs/module.yaml" params: - source_names=config["load_sources"], + source_names=active_load_sources, temporal_scope=config["temporal_scope"], gap_filling=config["gap_filling"], message: @@ -32,21 +61,32 @@ rule clean_demand: rule plot_cleaning_timeline: input: - demand=("/automatic/" "load_cleaned.parquet"), - basic_cleaning_method=("/automatic/" "load_cleaning_method.parquet"), - cleaning_method=("/automatic/" "load_final_cleaning_method.parquet"), + target_plan=target_data_plan, + demand=( + "/automatic/{shape}/" + "load_cleaned.parquet" + ), + basic_cleaning_method=( + "/automatic/{shape}/" + "load_cleaning_method.parquet" + ), + cleaning_method=( + "/automatic/{shape}/" + "load_final_cleaning_method.parquet" + ), cleaning_method_rank=( - "/automatic/" "load_final_cleaning_method_rank.parquet" + "/automatic/{shape}/" + "load_final_cleaning_method_rank.parquet" ), output: - plot=("/{shape}/" "load_cleaning_timeline.pdf"), - summary=("/{shape}/" "load_cleaning_summary.html"), + plot="/{shape}/load_cleaning_timeline.pdf", + summary="/{shape}/load_cleaning_summary.html", log: "/{shape}/plot_cleaning_timeline.log", conda: "../envs/module.yaml" params: - source_names=config["load_sources"], + source_names=active_load_sources, gap_filling=config["gap_filling"], source_registry=SOURCE_REGISTRY, message: diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 6390e18..0e2cbba 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -1,6 +1,6 @@ rule demand_electricity_raster: input: - demand="/automatic/load_cleaned.parquet", + demand=rules.finalise_clean_demand.output.demand, shapes="", population="/automatic/{shape}/population_clean.tif", output: diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 2644662..53200ec 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -10,19 +10,28 @@ def entsoe_annual_files(countries, years): ] -def entsoe_raw_files(_wildcards): - """Return ENTSO-E country-year files required by the configured period.""" +def entsoe_raw_files(wildcards): + """Return ENTSO-E country-year files required for one target shape.""" + plan = read_target_data_plan(wildcards) + + countries = plan["source_contexts"].get("entsoe", []) + years = years_for_period( config["temporal_scope"]["start"], config["temporal_scope"]["end"], ) - return entsoe_annual_files(internal["load_entsoe"]["countries"], years) + return entsoe_annual_files(countries, years) + + +def target_entsoe_countries(wildcards): + """Return ENTSO-E target countries for one shape.""" + return target_source_contexts(wildcards, "entsoe") def auxiliary_entsoe_raw_files(wildcards): """Return ENTSO-E country-year files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) batch = next( batch @@ -64,11 +73,12 @@ rule download_load_entsoe_country_year: rule prepare_load_entsoe: input: validation="/automatic/temporal_config_validation.json", + target_plan=target_data_plan, annual_files=entsoe_raw_files, output: - load="/automatic/load_entsoe.parquet", + load="/automatic/{shape}/load_entsoe.parquet", log: - "/prepare_load_entsoe.log", + "/{shape}/prepare_load_entsoe.log", localrule: True conda: "../envs/module.yaml" @@ -76,7 +86,7 @@ rule prepare_load_entsoe: temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], + country_codes=target_entsoe_countries, message: "Prepare electricity load from ENTSOE." script: @@ -88,9 +98,13 @@ rule prepare_auxiliary_load_entsoe: plan=auxiliary_acquisition_plan, annual_files=auxiliary_entsoe_raw_files, output: - load=("/automatic/" "auxiliary/entsoe/" "{batch_id}.parquet"), + load=( + "/automatic/{shape}/" + "auxiliary/entsoe/" + "{batch_id}.parquet" + ), log: - ("/auxiliary/" "entsoe/prepare_{batch_id}.log"), + "/{shape}/auxiliary/entsoe/prepare_{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index 784b0ee..0c55469 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -26,7 +26,7 @@ def entsoe_power_statistics_raw_files(_wildcards): def auxiliary_entsoe_power_statistics_raw_files(wildcards): """Return Power Statistics annual files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) batch = next( batch @@ -61,21 +61,33 @@ rule download_load_entsoe_power_statistics_year: "../scripts/download_load_entsoe_power_statistics.py" +def target_entsoe_power_statistics_countries(wildcards): + """Return Power Statistics target countries for one shape.""" + return target_source_contexts(wildcards,"entsoe_power_statistics",) + + rule prepare_load_entsoe_power_statistics: input: - validation=("/automatic/" "temporal_config_validation.json"), + validation=( + "/automatic/" + "temporal_config_validation.json" + ), + target_plan=target_data_plan, annual_files=entsoe_power_statistics_raw_files, output: - load=("/automatic/" "load_entsoe_power_statistics.parquet"), + load=( + "/automatic/{shape}/" + "load_entsoe_power_statistics.parquet" + ), log: - "/prepare_load_entsoe_power_statistics.log", + "/{shape}/prepare_load_entsoe_power_statistics.log", conda: "../envs/module.yaml" params: temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], + country_codes=target_entsoe_power_statistics_countries, message: "Prepare electricity-demand data from ENTSO-E Power Statistics." script: @@ -88,12 +100,14 @@ rule prepare_auxiliary_load_entsoe_power_statistics: annual_files=auxiliary_entsoe_power_statistics_raw_files, output: load=( - "/automatic/" + "/automatic/{shape}/" "auxiliary/entsoe_power_statistics/" "{batch_id}.parquet" ), log: - ("/auxiliary/" "entsoe_power_statistics/" "prepare_{batch_id}.log"), + "/{shape}/auxiliary/" + "entsoe_power_statistics/" + "prepare_{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index 81a8fed..36b8fd4 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -21,7 +21,7 @@ def neso_raw_files(_wildcards): def auxiliary_neso_raw_files(wildcards): """Return annual NESO files required by one auxiliary batch.""" - plan = _read_auxiliary_plan() + plan = _read_auxiliary_plan(wildcards) batch = next( batch @@ -51,35 +51,43 @@ rule download_load_neso_year: "../scripts/download_load_neso.py" +def target_neso_countries(wildcards): + """Return NESO target countries for one shape.""" + return target_source_contexts(wildcards,"neso",) + + rule prepare_load_neso: input: validation="/automatic/temporal_config_validation.json", + target_plan=target_data_plan, annual_files=neso_raw_files, output: - load="/automatic/load_neso.parquet", + load="/automatic/{shape}/load_neso.parquet", log: - "/prepare_load_neso.log", + "/{shape}/prepare_load_neso.log", conda: "../envs/module.yaml" params: start=config["temporal_scope"]["start"], end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe"]["countries"], + country_codes=target_neso_countries, frequency=config["temporal_scope"]["frequency"], message: "Prepare electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" - rule prepare_auxiliary_load_neso: input: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), + load=( + "/automatic/{shape}/" + "auxiliary/neso/{batch_id}.parquet" + ), log: - "/auxiliary/neso/{batch_id}.log", + "/{shape}/auxiliary/neso/{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index be6d87f..ad6c48b 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -17,21 +17,28 @@ rule download_load_opsd: "../scripts/download_load_opsd.py" + +def target_opsd_countries(wildcards): + """Return OPSD target countries for one shape.""" + return target_source_contexts(wildcards,"opsd",) + + rule prepare_load_opsd: input: validation="/automatic/temporal_config_validation.json", + target_plan=target_data_plan, load="/automatic/opsd/raw_load.parquet", output: - load="/automatic/load_opsd.parquet", + load="/automatic/{shape}/load_opsd.parquet", log: - "/prepare_load_opsd.log", + "/{shape}/prepare_load_opsd.log", conda: "../envs/module.yaml" params: start=config["temporal_scope"]["start"], end=config["temporal_scope"]["end"], frequency=config["temporal_scope"]["frequency"], - country_codes=internal["load_entsoe"]["countries"], + country_codes=target_opsd_countries, message: "Prepare electricity-demand data from OPSD." script: @@ -43,9 +50,13 @@ rule prepare_auxiliary_load_opsd: load=rules.download_load_opsd.output.load, plan=auxiliary_acquisition_plan, output: - load=("/automatic/" "auxiliary/opsd/" "{batch_id}.parquet"), + load=( + "/automatic/{shape}/" + "auxiliary/opsd/" + "{batch_id}.parquet" + ), log: - ("/auxiliary/" "opsd/{batch_id}.log"), + "/{shape}/auxiliary/opsd/{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index f6848f4..2425ecc 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -11,12 +11,43 @@ import pandas as pd from _tclean_config import build_advanced_rules, build_basic_rules from cmap import Colormap -from matplotlib.colors import ListedColormap, to_rgba -from matplotlib.patches import Patch +from matplotlib.backends.backend_pdf import PdfPages +from matplotlib.colors import BoundaryNorm, ListedColormap, to_rgba +from matplotlib.patches import Rectangle logger = logging.getLogger(__name__) -FIGURE_SIZE_INCHES = (7.5, 9.8) +FIGURE_DPI = 100 + +# Layout is defined in pixels. Matplotlib requires physical figure dimensions, +# so conversion to its internal units happens only in _new_figure(). +PAGE_WIDTH_PX = 750 +REFERENCE_PAGE_HEIGHT_PX = 980 +COUNTRIES_PER_PAGE = 35 + +COUNTRY_ROW_HEIGHT_PX = 20 +MIN_COUNTRY_PANEL_HEIGHT_PX = 60 + +PAGE_LEFT_MARGIN_PX = 68 +PAGE_RIGHT_MARGIN_PX = 38 +PAGE_TOP_MARGIN_PX = 80 +PAGE_BOTTOM_MARGIN_PX = 40 +PLOT_SUMMARY_GAP_PX = 8 +PANEL_LEGEND_GAP_PX = 14 +X_AXIS_FOOTER_HEIGHT_PX = 48 + +LEGEND_FONT_SIZE = 6.5 +LEGEND_SWATCH_WIDTH_PX = 15 +LEGEND_SWATCH_HEIGHT_PX = 10 +LEGEND_TEXT_GAP_PX = 6 +LEGEND_ITEM_GAP_PX = 18 +LEGEND_ROW_GAP_PX = 7 +LEGEND_LINE_GAP_PX = 2 +LEGEND_PADDING_TOP_PX = 8 +LEGEND_PADDING_BOTTOM_PX = 8 + +LEGEND_PAGE_TOP_MARGIN_PX = 82 +LEGEND_PAGE_BOTTOM_MARGIN_PX = 45 def main( @@ -50,6 +81,12 @@ def main( gap_filling_config=gap_filling_config, ) + _validate_cleaning_method_rank_mapping( + cleaning_method=cleaning_method, + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + ) + rank_colours = _build_rank_colours(metadata) background, background_cmap = _encode_rank_background( @@ -65,43 +102,37 @@ def main( gap_filling_config=gap_filling_config, ) - logger.info( - "Loaded %s timestamps for %s countries.", len(demand), len(demand.columns) - ) - - logger.info("Cleaning-method ranks:\n%s", metadata.to_string(index=False)) - - figure, axis, summary_axis, legend_axis = _plot_cleaning_background( - demand=demand, background=background, background_cmap=background_cmap + legend_metadata = _filter_legend_metadata( + metadata=metadata, + basic_cleaning_method=basic_cleaning_method, + cleaning_method=cleaning_method, ) - _add_normalised_demand_traces(axis=axis, demand=demand) - - _add_summary_panel( - axis=summary_axis, summary=summary, gap_filling_config=gap_filling_config + legend_rows = _build_legend_rows( + metadata=legend_metadata, + rank_colours=rank_colours, + available_width_px=( + PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX + ), ) - legend_handles = _build_legend_handles(metadata, rank_colours) - - legend_axis.legend( - handles=legend_handles, - loc="center", - frameon=False, - ncol=min(3, max(1, len(legend_handles))), - fontsize=6.5, - handlelength=1.5, - handletextpad=0.5, - columnspacing=0.8, + logger.info( + "Loaded %s timestamps for %s countries.", len(demand), len(demand.columns) ) + logger.info("Cleaning-method metadata:\n%s", metadata.to_string(index=False)) output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) - # Do not use bbox_inches="tight": preserving the fixed portrait figure - # dimensions makes the PDF predictable when placed in a manuscript. - figure.savefig(output_path) - - plt.close(figure) + _write_timeline_pdf( + demand=demand, + background=background, + background_cmap=background_cmap, + summary=summary, + legend_rows=legend_rows, + output_path=output_path, + gap_filling_config=gap_filling_config, + ) _write_summary_html( summary=summary, @@ -113,28 +144,491 @@ def main( logger.info("Saved cleaning summary to %s.", summary_output_path) -def _build_legend_handles( - metadata: pd.DataFrame, rank_colours: dict[int, tuple[float, float, float, float]] -) -> list[Patch]: - """Create handles for every configured rank.""" - handles: list[Patch] = [] +def _new_figure(*, height_px: int) -> plt.Figure: + """Create a figure from pixel dimensions.""" + return plt.figure( + figsize=(PAGE_WIDTH_PX / FIGURE_DPI, height_px / FIGURE_DPI), + dpi=FIGURE_DPI, + ) + + +def _build_legend_rows( + *, + metadata: pd.DataFrame, + rank_colours: dict[int, tuple[float, float, float, float]], + available_width_px: int, +) -> list[dict[str, Any]]: + """Measure, wrap, and pack legend entries into variable-width rows.""" + if metadata.empty: + return [] + + measurement_figure = _new_figure(height_px=200) + measurement_figure.canvas.draw() + renderer = measurement_figure.canvas.get_renderer() + + line_height_px = _measure_text_px( + figure=measurement_figure, + renderer=renderer, + text="Ag", + )[1] + + max_text_width_px = ( + available_width_px + - LEGEND_SWATCH_WIDTH_PX + - LEGEND_TEXT_GAP_PX + ) + + items: list[dict[str, Any]] = [] ordered = metadata.sort_values("cleaning_method_rank") for row in ordered.itertuples(index=False): rank = int(row.cleaning_method_rank) + lines = _wrap_legend_label( + label=str(row.label), + max_width_px=max_text_width_px, + figure=measurement_figure, + renderer=renderer, + ) + + line_widths = [ + _measure_text_px( + figure=measurement_figure, + renderer=renderer, + text=line, + )[0] + for line in lines + ] + + text_width_px = max(line_widths, default=0.0) + text_height_px = ( + len(lines) * line_height_px + + max(0, len(lines) - 1) * LEGEND_LINE_GAP_PX + ) + + items.append( + { + "rank": rank, + "colour": rank_colours[rank], + "lines": lines, + "width_px": ( + LEGEND_SWATCH_WIDTH_PX + + LEGEND_TEXT_GAP_PX + + text_width_px + ), + "height_px": max(LEGEND_SWATCH_HEIGHT_PX, text_height_px), + "line_height_px": line_height_px, + } + ) + + plt.close(measurement_figure) + + packed_rows: list[dict[str, Any]] = [] + current_items: list[dict[str, Any]] = [] + current_width_px = 0.0 + current_height_px = 0.0 + + for item in items: + separator_px = LEGEND_ITEM_GAP_PX if current_items else 0 + proposed_width_px = current_width_px + separator_px + item["width_px"] + + if current_items and proposed_width_px > available_width_px: + packed_rows.append( + { + "items": current_items, + "height_px": current_height_px, + } + ) + current_items = [] + current_width_px = 0.0 + current_height_px = 0.0 + separator_px = 0 + + current_items.append(item) + current_width_px += separator_px + item["width_px"] + current_height_px = max(current_height_px, item["height_px"]) + + if current_items: + packed_rows.append( + { + "items": current_items, + "height_px": current_height_px, + } + ) + + return packed_rows + + +def _measure_text_px( + *, + figure: plt.Figure, + renderer: Any, + text: str, +) -> tuple[float, float]: + """Measure one legend text line in rendered pixels.""" + artist = figure.text( + 0, + 0, + text, + fontsize=LEGEND_FONT_SIZE, + ha="left", + va="bottom", + ) + bounds = artist.get_window_extent(renderer=renderer) + artist.remove() + + return float(bounds.width), float(bounds.height) + + +def _wrap_legend_label( + *, + label: str, + max_width_px: int, + figure: plt.Figure, + renderer: Any, +) -> list[str]: + """Wrap a legend label to the available rendered width.""" + words = label.split() + + if not words: + return [""] + + lines: list[str] = [] + current = words[0] + + for word in words[1:]: + candidate = f"{current} {word}" + candidate_width = _measure_text_px( + figure=figure, + renderer=renderer, + text=candidate, + )[0] + + if candidate_width <= max_width_px: + current = candidate + continue + + lines.extend( + _split_oversized_legend_token( + token=current, + max_width_px=max_width_px, + figure=figure, + renderer=renderer, + ) + ) + current = word + + lines.extend( + _split_oversized_legend_token( + token=current, + max_width_px=max_width_px, + figure=figure, + renderer=renderer, + ) + ) + + return lines + + +def _split_oversized_legend_token( + *, + token: str, + max_width_px: int, + figure: plt.Figure, + renderer: Any, +) -> list[str]: + """Split an unusually long unbroken token if it cannot fit on one line.""" + if ( + _measure_text_px( + figure=figure, + renderer=renderer, + text=token, + )[0] + <= max_width_px + ): + return [token] + + pieces: list[str] = [] + current = "" + + for character in token: + candidate = f"{current}{character}" + width_px = _measure_text_px( + figure=figure, + renderer=renderer, + text=candidate, + )[0] + + if current and width_px > max_width_px: + pieces.append(current) + current = character + else: + current = candidate + + if current: + pieces.append(current) + + return pieces + + +def _legend_rows_height_px(rows: list[dict[str, Any]]) -> int: + """Return total height required for a set of legend rows.""" + if not rows: + return 0 + + row_height = sum(float(row["height_px"]) for row in rows) + gaps = LEGEND_ROW_GAP_PX * max(0, len(rows) - 1) + + return int(round(LEGEND_PADDING_TOP_PX + row_height + gaps + LEGEND_PADDING_BOTTOM_PX)) + + +def _take_legend_rows( + rows: list[dict[str, Any]], + *, + available_height_px: int, +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + """Take as many complete legend rows as fit in the available height.""" + if not rows or available_height_px <= 0: + return [], rows + + selected: list[dict[str, Any]] = [] + + for row in rows: + candidate = [*selected, row] + + if _legend_rows_height_px(candidate) > available_height_px: + break + + selected.append(row) + + return selected, rows[len(selected):] + + +def _write_timeline_pdf( + *, + demand: pd.DataFrame, + background: np.ndarray, + background_cmap: ListedColormap, + summary: pd.DataFrame, + legend_rows: list[dict[str, Any]], + output_path: Path, + gap_filling_config: dict[str, Any], +) -> None: + """Write a paginated timeline diagnostic to one PDF.""" + country_count = len(demand.columns) + + if country_count == 0: + raise ValueError("At least one country is required to plot the timeline.") + + country_slices = [ + slice(start, min(start + COUNTRIES_PER_PAGE, country_count)) + for start in range(0, country_count, COUNTRIES_PER_PAGE) + ] + + remaining_legend_rows = legend_rows + + with PdfPages(output_path) as pdf: + for page_index, country_slice in enumerate(country_slices): + page_demand = demand.iloc[:, country_slice] + page_summary = summary.iloc[country_slice] + page_background = background[country_slice, :] + + country_panel_height_px = max( + MIN_COUNTRY_PANEL_HEIGHT_PX, + len(page_demand.columns) * COUNTRY_ROW_HEIGHT_PX, + ) + + is_final_country_page = page_index == len(country_slices) - 1 + page_legend_rows: list[dict[str, Any]] = [] + + base_height_px = ( + PAGE_TOP_MARGIN_PX + + country_panel_height_px + + X_AXIS_FOOTER_HEIGHT_PX + + PAGE_BOTTOM_MARGIN_PX + ) + + if is_final_country_page and remaining_legend_rows: + available_legend_height_px = max( + 0, + REFERENCE_PAGE_HEIGHT_PX + - base_height_px + - PANEL_LEGEND_GAP_PX, + ) + + page_legend_rows, remaining_legend_rows = _take_legend_rows( + remaining_legend_rows, + available_height_px=available_legend_height_px, + ) - handles.append( - Patch( - facecolor=rank_colours[rank], + legend_height_px = _legend_rows_height_px(page_legend_rows) + + required_height_px = base_height_px + + if page_legend_rows: + required_height_px += PANEL_LEGEND_GAP_PX + legend_height_px + + if not is_final_country_page or remaining_legend_rows: + page_height_px = max(required_height_px, REFERENCE_PAGE_HEIGHT_PX) + else: + page_height_px = required_height_px + + figure, axis, summary_axis, legend_top_px = _plot_country_page( + demand=page_demand, + background=page_background, + background_cmap=background_cmap, + page_height_px=page_height_px, + country_panel_height_px=country_panel_height_px, + legend_height_px=legend_height_px, + country_start=country_slice.start + 1, + country_end=country_slice.stop, + country_total=country_count, + ) + + _add_normalised_demand_traces(axis=axis, demand=page_demand) + + _add_summary_panel( + axis=summary_axis, + summary=page_summary, + gap_filling_config=gap_filling_config, + ) + + if page_legend_rows: + _draw_legend_rows( + figure=figure, + rows=page_legend_rows, + top_px=legend_top_px, + ) + + pdf.savefig(figure) + plt.close(figure) + + legend_page_index = 0 + + while remaining_legend_rows: + legend_page_index += 1 + + available_height_px = ( + REFERENCE_PAGE_HEIGHT_PX + - LEGEND_PAGE_TOP_MARGIN_PX + - LEGEND_PAGE_BOTTOM_MARGIN_PX + ) + + page_rows, new_remaining_rows = _take_legend_rows( + remaining_legend_rows, + available_height_px=available_height_px, + ) + + if not page_rows: + raise ValueError( + "A legend row is too tall to fit on a diagnostic PDF page." + ) + + page_rows_height_px = _legend_rows_height_px(page_rows) + + if new_remaining_rows: + page_height_px = REFERENCE_PAGE_HEIGHT_PX + else: + page_height_px = ( + LEGEND_PAGE_TOP_MARGIN_PX + + page_rows_height_px + + LEGEND_PAGE_BOTTOM_MARGIN_PX + ) + + figure = _new_figure(height_px=page_height_px) + + title = "Cleaning provenance legend" + + if legend_page_index > 1: + title += " (continued)" + + figure.text( + 0.5, + 1.0 - (34 / page_height_px), + title, + ha="center", + va="center", + fontsize=11, + ) + + _draw_legend_rows( + figure=figure, + rows=page_rows, + top_px=LEGEND_PAGE_TOP_MARGIN_PX, + ) + + pdf.savefig(figure) + plt.close(figure) + + remaining_legend_rows = new_remaining_rows + + +def _draw_legend_rows( + *, + figure: plt.Figure, + rows: list[dict[str, Any]], + top_px: int, +) -> None: + """Draw packed legend rows directly in figure coordinates.""" + figure_height_px = int(round(figure.get_figheight() * FIGURE_DPI)) + usable_width_px = PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX + + y_px = top_px + LEGEND_PADDING_TOP_PX + + for row in rows: + items = row["items"] + total_width_px = sum(float(item["width_px"]) for item in items) + total_width_px += LEGEND_ITEM_GAP_PX * max(0, len(items) - 1) + + x_px = PAGE_LEFT_MARGIN_PX + max(0.0, (usable_width_px - total_width_px) / 2) + row_height_px = float(row["height_px"]) + + for item in items: + item_height_px = float(item["height_px"]) + item_top_px = y_px + (row_height_px - item_height_px) / 2 + + swatch_top_px = item_top_px + max( + 0.0, + (item_height_px - LEGEND_SWATCH_HEIGHT_PX) / 2, + ) + swatch_bottom_fraction = 1.0 - ( + (swatch_top_px + LEGEND_SWATCH_HEIGHT_PX) / figure_height_px + ) + + swatch = Rectangle( + ( + x_px / PAGE_WIDTH_PX, + swatch_bottom_fraction, + ), + LEGEND_SWATCH_WIDTH_PX / PAGE_WIDTH_PX, + LEGEND_SWATCH_HEIGHT_PX / figure_height_px, + transform=figure.transFigure, + facecolor=item["colour"], edgecolor="0.65", linewidth=0.8, - label=str(row.label), ) - ) + figure.add_artist(swatch) + + text_x_px = x_px + LEGEND_SWATCH_WIDTH_PX + LEGEND_TEXT_GAP_PX + text_y_px = item_top_px + + for line_index, line in enumerate(item["lines"]): + line_top_px = text_y_px + line_index * ( + item["line_height_px"] + LEGEND_LINE_GAP_PX + ) - return handles + figure.text( + text_x_px / PAGE_WIDTH_PX, + 1.0 - (line_top_px / figure_height_px), + line, + ha="left", + va="top", + fontsize=LEGEND_FONT_SIZE, + ) + + x_px += item["width_px"] + LEGEND_ITEM_GAP_PX + y_px += row_height_px + LEGEND_ROW_GAP_PX def _validate_alignment( *, @@ -156,6 +650,68 @@ def _validate_alignment( raise ValueError(f"{name} does not use the same country columns as demand.") +def _validate_cleaning_method_rank_mapping( + *, + cleaning_method: pd.DataFrame, + cleaning_method_rank: pd.DataFrame, + metadata: pd.DataFrame, +) -> None: + """Require provenance strings and numeric codes to describe the same methods.""" + method_to_rank = ( + metadata + .set_index("cleaning_method")["cleaning_method_rank"] + .astype(int) + .to_dict() + ) + + methods = { + str(value) + for value in pd.unique(cleaning_method.to_numpy().ravel()) + if pd.notna(value) + } + + unknown_methods = sorted(methods - set(method_to_rank)) + + if unknown_methods: + raise ValueError( + "Cleaning provenance contains methods absent from plotting " + f"metadata: {unknown_methods!r}." + ) + + expected = cleaning_method.apply( + lambda column: column.map(method_to_rank) + ) + + actual = cleaning_method_rank.apply( + pd.to_numeric, + errors="coerce", + ) + + mismatch = expected.ne(actual) & ~( + expected.isna() & actual.isna() + ) + + if not mismatch.any().any(): + return + + row_index, column_index = np.argwhere( + mismatch.to_numpy() + )[0] + + timestamp = cleaning_method.index[row_index] + country = cleaning_method.columns[column_index] + method = cleaning_method.iloc[row_index, column_index] + expected_rank = expected.iloc[row_index, column_index] + actual_rank = actual.iloc[row_index, column_index] + + raise ValueError( + "Cleaning-method rank is inconsistent with cleaning provenance " + f"at {timestamp!r}, country {country!r}: " + f"method {method!r} expects code {expected_rank}, " + f"but rank data contains {actual_rank}." + ) + + def _build_rank_colours( metadata: pd.DataFrame, ) -> dict[int, tuple[float, float, float, float]]: @@ -218,10 +774,19 @@ def _encode_rank_background( return (background, ListedColormap(colour_list)) -def _plot_cleaning_background( - *, demand: pd.DataFrame, background: np.ndarray, background_cmap: ListedColormap -) -> tuple[plt.Figure, plt.Axes, plt.Axes, plt.Axes]: - """Plot cleaning-method ranks over time by country.""" +def _plot_country_page( + *, + demand: pd.DataFrame, + background: np.ndarray, + background_cmap: ListedColormap, + page_height_px: int, + country_panel_height_px: int, + legend_height_px: int, + country_start: int, + country_end: int, + country_total: int, +) -> tuple[plt.Figure, plt.Axes, plt.Axes, int]: + """Plot one country page and return the top position available for a legend.""" country_count = len(demand.columns) if len(demand.index) < 2: @@ -237,31 +802,47 @@ def _plot_cleaning_background( start = demand.index[0] end = demand.index[-1] + time_step - figure = plt.figure(figsize=FIGURE_SIZE_INCHES) - - grid = figure.add_gridspec( - nrows=2, - ncols=2, - width_ratios=(3.9, 2.5), - height_ratios=(8.6, 1.25), - left=0.09, - right=0.95, - bottom=0.06, - top=0.89, - wspace=0.04, - hspace=0.12, + figure = _new_figure(height_px=page_height_px) + + usable_width_px = PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX + content_width_px = usable_width_px - PLOT_SUMMARY_GAP_PX + + timeline_width_px = content_width_px * (3.9 / (3.9 + 2.5)) + summary_width_px = content_width_px - timeline_width_px + + axis_bottom_px = page_height_px - PAGE_TOP_MARGIN_PX - country_panel_height_px + + axis = figure.add_axes( + [ + PAGE_LEFT_MARGIN_PX / PAGE_WIDTH_PX, + axis_bottom_px / page_height_px, + timeline_width_px / PAGE_WIDTH_PX, + country_panel_height_px / page_height_px, + ] + ) + + summary_axis = figure.add_axes( + [ + (PAGE_LEFT_MARGIN_PX + timeline_width_px + PLOT_SUMMARY_GAP_PX) + / PAGE_WIDTH_PX, + axis_bottom_px / page_height_px, + summary_width_px / PAGE_WIDTH_PX, + country_panel_height_px / page_height_px, + ], + sharey=axis, ) - axis = figure.add_subplot(grid[0, 0]) - summary_axis = figure.add_subplot(grid[0, 1], sharey=axis) - legend_axis = figure.add_subplot(grid[1, :]) - legend_axis.axis("off") + background_norm = BoundaryNorm( + np.arange(-0.5, background_cmap.N + 0.5, 1), + background_cmap.N, + ) axis.imshow( background, aspect="auto", interpolation="nearest", cmap=background_cmap, + norm=background_norm, extent=( mdates.date2num(start), mdates.date2num(end), @@ -313,14 +894,47 @@ def _plot_cleaning_background( spine.set_visible(False) for boundary in row_boundaries: - summary_axis.axhline(boundary, linewidth=0.4, alpha=0.35, color="0.5", zorder=0) + summary_axis.axhline( + boundary, + linewidth=0.4, + alpha=0.35, + color="0.5", + zorder=0, + ) summary_axis.axvline(0.0, linewidth=0.6, color="0.7") - figure.suptitle("Electricity demand and cleaning provenance", fontsize=11, y=0.965) + figure.text( + 0.5, + 1.0 - (34 / page_height_px), + "Electricity demand and cleaning provenance", + ha="center", + va="center", + fontsize=11, + ) + + if country_total > COUNTRIES_PER_PAGE: + figure.text( + 1.0 - (PAGE_RIGHT_MARGIN_PX / PAGE_WIDTH_PX), + 1.0 - (62 / page_height_px), + f"Countries {country_start}–{country_end} of {country_total}", + ha="right", + va="center", + fontsize=6.5, + color="0.4", + ) + + legend_top_px = ( + PAGE_TOP_MARGIN_PX + + country_panel_height_px + + X_AXIS_FOOTER_HEIGHT_PX + + PANEL_LEGEND_GAP_PX + ) - return figure, axis, summary_axis, legend_axis + if legend_height_px == 0: + legend_top_px = page_height_px - PAGE_BOTTOM_MARGIN_PX + return figure, axis, summary_axis, legend_top_px def _add_normalised_demand_traces( *, @@ -590,7 +1204,7 @@ def _build_cleaning_method_metadata( gap_filling_config: dict[str, Any], source_registry: Mapping[str, Mapping[str, Any]], ) -> pd.DataFrame: - """Build complete method metadata in configured rank order.""" + """Build cleaning-method metadata in configured order.""" rows: list[dict[str, Any]] = [] rank = 0 @@ -600,9 +1214,8 @@ def _build_cleaning_method_metadata( "cleaning_method": f"observed_{source_name}", "cleaning_method_rank": rank, "label": ( - "Rank " - f"{rank}: Observed " - f"({_format_source_name(source_name, source_registry)})" + f"[Src.] " + f"{_format_source_name(source_name, source_registry)}" ), "category": "observed", } @@ -611,30 +1224,41 @@ def _build_cleaning_method_metadata( rank += 1 basic_rules = build_basic_rules(gap_filling_config) - advanced_rules = build_advanced_rules(gap_filling_config) - - rule_names = [rule["name"] for rule in basic_rules] - if not advanced_rules.empty: - rule_names.extend(advanced_rules["rule_name"].tolist()) + for rule in basic_rules: + rule_name = rule["name"] - for rule_name in rule_names: rows.append( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), + "label": f"[Bsc.] {_format_rule_name(rule_name)}", "category": "imputed", } ) rank += 1 + advanced_rules = build_advanced_rules(gap_filling_config) + + if not advanced_rules.empty: + for rule_name in advanced_rules["rule_name"]: + rows.append( + { + "cleaning_method": rule_name, + "cleaning_method_rank": rank, + "label": f"[Adv.] {_format_rule_name(rule_name)}", + "category": "imputed", + } + ) + + rank += 1 + rows.append( { "cleaning_method": "missing", "cleaning_method_rank": rank, - "label": f"Rank {rank}: Missing", + "label": "[Missing]", "category": "missing", } ) @@ -642,6 +1266,33 @@ def _build_cleaning_method_metadata( return pd.DataFrame(rows) +def _filter_legend_metadata( + *, + metadata: pd.DataFrame, + basic_cleaning_method: pd.DataFrame, + cleaning_method: pd.DataFrame, +) -> pd.DataFrame: + """Keep only cleaning methods represented in this workflow result.""" + used_methods: set[str] = set() + + for frame in (basic_cleaning_method, cleaning_method): + values = pd.unique(frame.to_numpy().ravel()) + + used_methods.update( + str(value) + for value in values + if pd.notna(value) + ) + + # Keep missing in the legend as the fixed diagnostic reference colour, + # even when this particular result is complete. + used_methods.add("missing") + + return metadata.loc[ + metadata["cleaning_method"].isin(used_methods) + ].copy() + + def _format_source_name( source_name: str, source_registry: Mapping[str, Mapping[str, Any]] ) -> str: diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 16e1617..922340a 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -181,3 +181,73 @@ def _build_source_periods(periods: Sequence[Mapping[str, Any]]) -> pd.DataFrame: ], columns=["context", "start", "end", "weight"], ) + + +def filter_source_requests_by_temporal_scope( + requests: pd.DataFrame, + *, + requirements: pd.DataFrame, + source_registry: Mapping[str, Mapping[str, Any]], +) -> pd.DataFrame: + """Remove auxiliary source requests outside provider availability.""" + if requests.empty: + return requests.copy() + + keep: list[bool] = [] + + for request in requests.itertuples(index=False): + metadata = source_registry[str(request.source)] + temporal_scope = metadata.get("temporal_scope") or {} + + source_start = temporal_scope.get("start") + source_end = temporal_scope.get("end") + + request_start = pd.to_datetime(request.start, utc=True) + request_end = pd.to_datetime(request.end, utc=True) + + overlaps = True + + if source_start is not None: + overlaps &= request_end > pd.to_datetime( + source_start, + utc=True, + ) + + if source_end is not None: + overlaps &= request_start < pd.to_datetime( + source_end, + utc=True, + ) + + keep.append(overlaps) + + filtered = requests.loc[keep].reset_index(drop=True) + + required_periods = requirements[ + ["context", "start", "end"] + ].drop_duplicates() + + covered_periods = filtered[ + ["context", "start", "end"] + ].drop_duplicates() + + coverage = required_periods.merge( + covered_periods, + on=["context", "start", "end"], + how="left", + indicator=True, + ) + + uncovered = coverage.loc[ + coverage["_merge"] == "left_only", + ["context", "start", "end"], + ] + + if not uncovered.empty: + raise ValueError( + "No configured auxiliary source overlaps the declared " + "temporal scope for required context-period(s): " + f"{uncovered.to_dict(orient='records')}." + ) + + return filtered diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 6d0c2e6..c5f8e96 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -1,5 +1,6 @@ """Combine and basic-clean prepared electricity-demand sources.""" +import json import logging from pathlib import Path from typing import TYPE_CHECKING, Any @@ -34,6 +35,15 @@ def main(snakemake: Any) -> None: for source_name, path in zip(source_names, input_paths, strict=True) } + target_contexts = _read_target_contexts( + snakemake.input.target_plan + ) + + sources = { + source_name: data.reindex(columns=target_contexts) + for source_name, data in sources.items() + } + config = build_tclean_config(snakemake.params.temporal_scope) basic_rules = build_basic_rules(snakemake.params.gap_filling) @@ -93,6 +103,14 @@ def _read_prepared_source(path: str | Path) -> pd.DataFrame: return data +def _read_target_contexts(path: str | Path) -> list[str]: + """Read the planned target contexts for one shape.""" + with open(path, encoding="utf-8") as file: + plan = json.load(file) + + return [str(context) for context in plan["target_contexts"]] + + def _log_source_counts(data_source: pd.DataFrame) -> None: """Log observed-value counts by source.""" counts = data_source.stack().value_counts() diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 3da2b65..e5df489 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -21,6 +21,7 @@ build_basic_rules, build_source_capabilities, build_time_grid, + filter_source_requests_by_temporal_scope, get_advanced_source_definitions, ) from tclean.advanced import ( @@ -83,6 +84,12 @@ def build_advanced_execution_plan( requirements, source_capabilities=source_capabilities, grid=grid ) + requests = filter_source_requests_by_temporal_scope( + requests, + requirements=requirements, + source_registry=source_registry, + ) + raw_batches = build_source_batches(requests) batches = [serialize_batch(batch) for batch in raw_batches] diff --git a/workflow/scripts/plan_target_data.py b/workflow/scripts/plan_target_data.py new file mode 100644 index 0000000..1babb5b --- /dev/null +++ b/workflow/scripts/plan_target_data.py @@ -0,0 +1,188 @@ +"""Plan target electricity-demand data acquisition.""" + +import json +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import geopandas as gpd +import pandas as pd +from _schemas import Shapes + +if TYPE_CHECKING: + snakemake: Any + + +TARGET_DATA_PLAN_VERSION = 1 + + +def _as_utc(value: object) -> pd.Timestamp: + """Interpret naive timestamps as UTC and convert aware timestamps to UTC.""" + timestamp = pd.Timestamp(value) + + if timestamp.tzinfo is None: + return timestamp.tz_localize("UTC") + + return timestamp.tz_convert("UTC") + + +def source_overlaps_period( + metadata: Mapping[str, Any], + *, + start: object, + end: object, +) -> bool: + """Return whether a source can supply any part of the requested period.""" + temporal_scope = metadata.get("temporal_scope") or {} + + source_start = temporal_scope.get("start") + source_end = temporal_scope.get("end") + + start = _as_utc(start) + end = _as_utc(end) + + if source_start is not None: + if end <= _as_utc(source_start): + return False + + if source_end is not None: + if start >= _as_utc(source_end): + return False + + return True + + +def supported_target_contexts( + target_contexts: Sequence[str], + *, + metadata: Mapping[str, Any], +) -> list[str]: + """Return target contexts supported by one source.""" + contexts = metadata.get("contexts") + + # Missing or empty contexts means that the source declares + # no geographic restriction. + if not contexts: + return list(target_contexts) + + supported = set(contexts) + + return [ + context + for context in target_contexts + if context in supported + ] + + +def build_target_data_plan( + *, + target_contexts: Sequence[str], + source_names: Sequence[str], + source_registry: Mapping[str, Mapping[str, Any]], + temporal_scope: Mapping[str, Any], +) -> dict[str, object]: + """Build the target-data acquisition plan.""" + target_contexts = sorted(set(target_contexts)) + + if not target_contexts: + raise ValueError( + "The supplied shapes contain no land-country contexts." + ) + + source_contexts: dict[str, list[str]] = {} + active_sources: list[str] = [] + + for source_name in source_names: + if source_name not in source_registry: + raise ValueError( + f"Unsupported electricity-demand source: {source_name!r}." + ) + + metadata = source_registry[source_name] + + if not source_overlaps_period( + metadata, + start=temporal_scope["start"], + end=temporal_scope["end"], + ): + source_contexts[source_name] = [] + continue + + contexts = supported_target_contexts( + target_contexts, + metadata=metadata, + ) + + source_contexts[source_name] = contexts + + if contexts: + active_sources.append(source_name) + + covered_contexts = { + context + for contexts in source_contexts.values() + for context in contexts + } + + uncovered_contexts = sorted( + set(target_contexts).difference(covered_contexts) + ) + + if uncovered_contexts: + raise ValueError( + "No configured electricity-demand source can supply " + "the following target context(s) within the requested " + f"temporal scope: {uncovered_contexts}. " + f"Configured sources: {list(source_names)}." + ) + + return { + "version": TARGET_DATA_PLAN_VERSION, + "target_contexts": target_contexts, + "active_sources": active_sources, + "source_contexts": source_contexts, + } + + +def write_target_data_plan( + *, + plan: Mapping[str, object], + output_path: str | Path, +) -> None: + """Write the target-data acquisition plan as JSON.""" + output_path = Path(output_path) + + output_path.parent.mkdir(parents=True, exist_ok=True) + + with output_path.open("w", encoding="utf-8") as file: + json.dump(plan, file, indent=2) + + +def main(snakemake: Any) -> None: + """Build and write the target-data acquisition plan.""" + shapes = gpd.read_parquet(snakemake.input.shapes) + shapes = Shapes.validate(shapes) + + land_shapes = shapes.loc[shapes["shape_class"] == "land"] + + target_contexts = ( + land_shapes["country_id"] + .drop_duplicates() + .tolist() + ) + + plan = build_target_data_plan( + target_contexts=target_contexts, + source_names=snakemake.params.source_names, + source_registry=snakemake.params.source_registry, + temporal_scope=snakemake.params.temporal_scope, + ) + + write_target_data_plan( + plan=plan, + output_path=snakemake.output.plan, + ) + + +if __name__ == "__main__": + main(snakemake) From 52a9f570fe117c3ae3d7e93d530c8ed0083fb3ef Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 14:58:58 +0200 Subject: [PATCH 179/241] Added temporal clipping based on source-registry into primary download workflow. --- workflow/rules/automatic.smk | 30 +++++++++++++ workflow/rules/source_entsoe.smk | 20 +++++++-- .../rules/source_entsoe_power_statistics.smk | 22 +++++++--- workflow/rules/source_neso.smk | 24 +++++++--- workflow/rules/source_opsd.smk | 10 ++++- workflow/scripts/clean_demand.py | 9 ++-- workflow/scripts/plan_target_data.py | 44 +++++++++++++------ 7 files changed, 126 insertions(+), 33 deletions(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 7cc6c0e..86433d4 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -84,6 +84,36 @@ def target_source_contexts(wildcards, source_name): return plan["source_contexts"].get(source_name, []) +def target_source_temporal_scope(wildcards, source_name): + """Return the planned target temporal scope for one source.""" + plan = read_target_data_plan(wildcards) + + temporal_scope = plan["source_temporal_scopes"].get(source_name) + + if temporal_scope is None: + raise ValueError( + f"Source {source_name!r} has no active target temporal scope." + ) + + return temporal_scope + + +def target_source_start(wildcards, source_name): + """Return the planned target start timestamp for one source.""" + return target_source_temporal_scope( + wildcards, + source_name, + )["start"] + + +def target_source_end(wildcards, source_name): + """Return the planned target end timestamp for one source.""" + return target_source_temporal_scope( + wildcards, + source_name, + )["end"] + + rule download_population: output: population=update("/automatic/population.zip"), diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 53200ec..207c91b 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -17,8 +17,14 @@ def entsoe_raw_files(wildcards): countries = plan["source_contexts"].get("entsoe", []) years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], + target_source_start( + wildcards, + "entsoe", + ), + target_source_end( + wildcards, + "entsoe", + ), ) return entsoe_annual_files(countries, years) @@ -83,8 +89,14 @@ rule prepare_load_entsoe: conda: "../envs/module.yaml" params: - temporal_start=config["temporal_scope"]["start"], - temporal_end=config["temporal_scope"]["end"], + temporal_start=lambda wildcards: target_source_start( + wildcards, + "entsoe", + ), + temporal_end=lambda wildcards: target_source_end( + wildcards, + "entsoe", + ), frequency=config["temporal_scope"]["frequency"], country_codes=target_entsoe_countries, message: diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index 0c55469..ffa6bdb 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -14,11 +14,17 @@ def entsoe_power_statistics_annual_files(years): ] -def entsoe_power_statistics_raw_files(_wildcards): +def entsoe_power_statistics_raw_files(wildcards): """Return Power Statistics annual files required by the target period.""" years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], + target_source_start( + wildcards, + "entsoe_power_statistics", + ), + target_source_end( + wildcards, + "entsoe_power_statistics", + ), ) return entsoe_power_statistics_annual_files(years) @@ -84,8 +90,14 @@ rule prepare_load_entsoe_power_statistics: conda: "../envs/module.yaml" params: - temporal_start=config["temporal_scope"]["start"], - temporal_end=config["temporal_scope"]["end"], + temporal_start=lambda wildcards: target_source_start( + wildcards, + "entsoe_power_statistics", + ), + temporal_end=lambda wildcards: target_source_end( + wildcards, + "entsoe_power_statistics", + ), frequency=config["temporal_scope"]["frequency"], country_codes=target_entsoe_power_statistics_countries, message: diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index 36b8fd4..b612b28 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -9,11 +9,17 @@ def neso_annual_files(years): ] -def neso_raw_files(_wildcards): - """Return annual NESO input files for the configured period.""" +def neso_raw_files(wildcards): + """Return annual NESO input files for the planned target period.""" years = years_for_period( - config["temporal_scope"]["start"], - config["temporal_scope"]["end"], + target_source_start( + wildcards, + "neso", + ), + target_source_end( + wildcards, + "neso", + ), ) return neso_annual_files(years) @@ -68,8 +74,14 @@ rule prepare_load_neso: conda: "../envs/module.yaml" params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], + start=lambda wildcards: target_source_start( + wildcards, + "neso", + ), + end=lambda wildcards: target_source_end( + wildcards, + "neso", + ), country_codes=target_neso_countries, frequency=config["temporal_scope"]["frequency"], message: diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index ad6c48b..c74b68e 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -35,8 +35,14 @@ rule prepare_load_opsd: conda: "../envs/module.yaml" params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], + start=lambda wildcards: target_source_start( + wildcards, + "opsd", + ), + end=lambda wildcards: target_source_end( + wildcards, + "opsd", + ), frequency=config["temporal_scope"]["frequency"], country_codes=target_opsd_countries, message: diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index c5f8e96..a04f19b 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -39,13 +39,16 @@ def main(snakemake: Any) -> None: snakemake.input.target_plan ) + config = build_tclean_config(snakemake.params.temporal_scope) + sources = { - source_name: data.reindex(columns=target_contexts) + source_name: data.reindex( + index=config.grid.target_index, + columns=target_contexts, + ) for source_name, data in sources.items() } - config = build_tclean_config(snakemake.params.temporal_scope) - basic_rules = build_basic_rules(snakemake.params.gap_filling) (cleaned, data_source, cleaning_method) = clean( diff --git a/workflow/scripts/plan_target_data.py b/workflow/scripts/plan_target_data.py index 1babb5b..35594aa 100644 --- a/workflow/scripts/plan_target_data.py +++ b/workflow/scripts/plan_target_data.py @@ -13,7 +13,7 @@ snakemake: Any -TARGET_DATA_PLAN_VERSION = 1 +TARGET_DATA_PLAN_VERSION = 2 def _as_utc(value: object) -> pd.Timestamp: @@ -26,31 +26,43 @@ def _as_utc(value: object) -> pd.Timestamp: return timestamp.tz_convert("UTC") -def source_overlaps_period( +def effective_source_temporal_scope( metadata: Mapping[str, Any], *, start: object, end: object, -) -> bool: - """Return whether a source can supply any part of the requested period.""" +) -> dict[str, str] | None: + """Return the intersection of requested and source temporal scopes.""" temporal_scope = metadata.get("temporal_scope") or {} + requested_start = _as_utc(start) + requested_end = _as_utc(end) + source_start = temporal_scope.get("start") source_end = temporal_scope.get("end") - start = _as_utc(start) - end = _as_utc(end) + effective_start = requested_start + effective_end = requested_end if source_start is not None: - if end <= _as_utc(source_start): - return False + effective_start = max( + effective_start, + _as_utc(source_start), + ) if source_end is not None: - if start >= _as_utc(source_end): - return False + effective_end = min( + effective_end, + _as_utc(source_end), + ) - return True + if effective_start >= effective_end: + return None + return { + "start": effective_start.isoformat(), + "end": effective_end.isoformat(), + } def supported_target_contexts( target_contexts: Sequence[str], @@ -90,6 +102,7 @@ def build_target_data_plan( ) source_contexts: dict[str, list[str]] = {} + source_temporal_scopes: dict[str, dict[str, str] | None] = {} active_sources: list[str] = [] for source_name in source_names: @@ -100,11 +113,15 @@ def build_target_data_plan( metadata = source_registry[source_name] - if not source_overlaps_period( + effective_temporal_scope = effective_source_temporal_scope( metadata, start=temporal_scope["start"], end=temporal_scope["end"], - ): + ) + + source_temporal_scopes[source_name] = effective_temporal_scope + + if effective_temporal_scope is None: source_contexts[source_name] = [] continue @@ -141,6 +158,7 @@ def build_target_data_plan( "target_contexts": target_contexts, "active_sources": active_sources, "source_contexts": source_contexts, + "source_temporal_scopes": source_temporal_scopes, } From 4c70bd9685640724791f2c05d7b51d380e33fff6 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 15:10:13 +0200 Subject: [PATCH 180/241] Advancing temporal alignment of sources with target data and added tests. --- tests/unit/test_plan_target_data.py | 599 +++++++++++++++++++++++++++ workflow/scripts/plan_target_data.py | 98 ++++- 2 files changed, 685 insertions(+), 12 deletions(-) create mode 100644 tests/unit/test_plan_target_data.py diff --git a/tests/unit/test_plan_target_data.py b/tests/unit/test_plan_target_data.py new file mode 100644 index 0000000..5f172bf --- /dev/null +++ b/tests/unit/test_plan_target_data.py @@ -0,0 +1,599 @@ +"""Tests for target electricity-demand acquisition planning.""" + +import json +import sys +from pathlib import Path + +import pandas as pd +import pytest + + +# workflow/scripts is not a Python package, so expose it for direct imports. +ROOT = Path(__file__).resolve().parents[2] +SCRIPTS_DIR = ROOT / "workflow" / "scripts" + +sys.path.insert(0, str(SCRIPTS_DIR)) + +from plan_target_data import ( # noqa: E402 + TARGET_DATA_PLAN_VERSION, + _as_utc, + build_target_data_plan, + effective_source_temporal_scope, + supported_target_contexts, + uncovered_temporal_intervals, + write_target_data_plan, +) + +# --------------------------------------------------------------------------- +# Timestamp handling +# --------------------------------------------------------------------------- + + +def test_as_utc_localises_naive_timestamp(): + """Naive timestamps are interpreted as UTC.""" + result = _as_utc("2020-01-01") + + assert result == pd.Timestamp("2020-01-01T00:00:00Z") + + +def test_as_utc_converts_aware_timestamp(): + """Timezone-aware timestamps are converted to UTC.""" + result = _as_utc("2020-01-01T01:00:00+01:00") + + assert result == pd.Timestamp("2020-01-01T00:00:00Z") + + +# --------------------------------------------------------------------------- +# Source temporal-scope intersection +# --------------------------------------------------------------------------- + + +def test_effective_scope_uses_full_requested_period_without_source_bounds(): + """An unbounded source inherits the complete requested period.""" + result = effective_source_temporal_scope( + {}, + start="2018-01-01", + end="2021-01-01", + ) + + assert result == { + "start": "2018-01-01T00:00:00+00:00", + "end": "2021-01-01T00:00:00+00:00", + } + + +def test_effective_scope_clips_source_end(): + """A source end bound clips the requested period.""" + metadata = { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + } + } + + result = effective_source_temporal_scope( + metadata, + start="2018-01-01", + end="2021-01-01", + ) + + assert result == { + "start": "2018-01-01T00:00:00+00:00", + "end": "2019-01-01T00:00:00+00:00", + } + + +def test_effective_scope_clips_source_start(): + """A source start bound clips the requested period.""" + metadata = { + "temporal_scope": { + "start": "2019-01-01", + "end": "2026-01-01", + } + } + + result = effective_source_temporal_scope( + metadata, + start="2018-01-01", + end="2021-01-01", + ) + + assert result == { + "start": "2019-01-01T00:00:00+00:00", + "end": "2021-01-01T00:00:00+00:00", + } + + +@pytest.mark.parametrize( + ("source_start", "source_end"), + [ + ("2000-01-01", "2018-01-01"), + ("2021-01-01", "2025-01-01"), + ], +) +def test_effective_scope_returns_none_without_overlap( + source_start, + source_end, +): + """Sources touching but not overlapping the request are inactive.""" + metadata = { + "temporal_scope": { + "start": source_start, + "end": source_end, + } + } + + result = effective_source_temporal_scope( + metadata, + start="2018-01-01", + end="2021-01-01", + ) + + assert result is None + + +# --------------------------------------------------------------------------- +# Geographic support +# --------------------------------------------------------------------------- + + +def test_supported_contexts_respects_source_contexts(): + """Only explicitly supported target contexts are returned.""" + result = supported_target_contexts( + ["AAA", "BBB", "CCC"], + metadata={ + "contexts": ["AAA", "CCC", "DDD"], + }, + ) + + assert result == ["AAA", "CCC"] + + +@pytest.mark.parametrize( + "metadata", + [ + {}, + {"contexts": None}, + {"contexts": []}, + ], +) +def test_missing_or_empty_contexts_means_no_geographic_restriction( + metadata, +): + """Missing or empty source contexts allow all target contexts.""" + result = supported_target_contexts( + ["AAA", "BBB"], + metadata=metadata, + ) + + assert result == ["AAA", "BBB"] + + +# --------------------------------------------------------------------------- +# Temporal coverage helper +# --------------------------------------------------------------------------- + + +def test_uncovered_intervals_returns_full_period_without_sources(): + """No source intervals means the complete request is uncovered.""" + gaps = uncovered_temporal_intervals( + [], + start="2018-01-01", + end="2021-01-01", + ) + + assert gaps == [ + ( + pd.Timestamp("2018-01-01T00:00:00Z"), + pd.Timestamp("2021-01-01T00:00:00Z"), + ) + ] + + +def test_uncovered_intervals_accepts_adjacent_half_open_intervals(): + """Adjacent half-open source intervals provide continuous coverage.""" + gaps = uncovered_temporal_intervals( + [ + ("2018-01-01", "2019-01-01"), + ("2019-01-01", "2021-01-01"), + ], + start="2018-01-01", + end="2021-01-01", + ) + + assert gaps == [] + + +def test_uncovered_intervals_merges_overlapping_intervals(): + """Overlapping source intervals jointly provide continuous coverage.""" + gaps = uncovered_temporal_intervals( + [ + ("2018-01-01", "2020-01-01"), + ("2019-01-01", "2021-01-01"), + ], + start="2018-01-01", + end="2021-01-01", + ) + + assert gaps == [] + + +def test_uncovered_intervals_reports_internal_gap(): + """An uncovered interval between two sources is reported exactly.""" + gaps = uncovered_temporal_intervals( + [ + ("2018-01-01", "2019-01-01"), + ("2019-02-01", "2021-01-01"), + ], + start="2018-01-01", + end="2021-01-01", + ) + + assert gaps == [ + ( + pd.Timestamp("2019-01-01T00:00:00Z"), + pd.Timestamp("2019-02-01T00:00:00Z"), + ) + ] + + +# --------------------------------------------------------------------------- +# Complete target plans +# --------------------------------------------------------------------------- + + +def test_target_plan_clips_source_temporal_scopes(): + """The plan records the effective interval for every active source.""" + source_registry = { + "old_source": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + }, + "contexts": ["AAA"], + }, + "new_source": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2026-01-01", + }, + "contexts": ["AAA"], + }, + } + + plan = build_target_data_plan( + target_contexts=["AAA"], + source_names=["old_source", "new_source"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + assert plan["version"] == TARGET_DATA_PLAN_VERSION + + assert plan["source_temporal_scopes"] == { + "old_source": { + "start": "2018-01-01T00:00:00+00:00", + "end": "2019-01-01T00:00:00+00:00", + }, + "new_source": { + "start": "2019-01-01T00:00:00+00:00", + "end": "2021-01-01T00:00:00+00:00", + }, + } + + +def test_target_plan_accepts_contiguous_source_coverage(): + """Adjacent source intervals can jointly cover a target context.""" + source_registry = { + "source_a": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + }, + "contexts": ["AAA"], + }, + "source_b": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2026-01-01", + }, + "contexts": ["AAA"], + }, + } + + plan = build_target_data_plan( + target_contexts=["AAA"], + source_names=["source_a", "source_b"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + assert plan["active_sources"] == [ + "source_a", + "source_b", + ] + + assert plan["source_contexts"] == { + "source_a": ["AAA"], + "source_b": ["AAA"], + } + + +def test_target_plan_rejects_temporal_coverage_gap(): + """Reject a target context with a gap between source periods.""" + source_registry = { + "source_a": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + }, + "contexts": ["AAA"], + }, + "source_b": { + "temporal_scope": { + "start": "2019-02-01", + "end": "2026-01-01", + }, + "contexts": ["AAA"], + }, + } + + with pytest.raises(ValueError) as exc_info: + build_target_data_plan( + target_contexts=["AAA"], + source_names=["source_a", "source_b"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + message = str(exc_info.value) + + assert "AAA" in message + assert "2019-01-01T00:00:00+00:00" in message + assert "2019-02-01T00:00:00+00:00" in message + + +def test_target_plan_rejects_completely_unsupported_context(): + """A target context with no usable source is rejected.""" + source_registry = { + "source_a": { + "contexts": ["AAA"], + }, + } + + with pytest.raises(ValueError) as exc_info: + build_target_data_plan( + target_contexts=["AAA", "BBB"], + source_names=["source_a"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + message = str(exc_info.value) + + assert "BBB" in message + assert "2018-01-01T00:00:00+00:00" in message + assert "2021-01-01T00:00:00+00:00" in message + + +def test_target_plan_checks_coverage_per_context(): + """Temporal coverage must be complete independently for each context.""" + source_registry = { + "old_source": { + "temporal_scope": { + "start": "2010-01-01", + "end": "2019-01-01", + }, + "contexts": ["AAA", "BBB"], + }, + "new_source": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2025-01-01", + }, + "contexts": ["AAA"], + }, + } + + with pytest.raises(ValueError) as exc_info: + build_target_data_plan( + target_contexts=["AAA", "BBB"], + source_names=["old_source", "new_source"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + message = str(exc_info.value) + + # AAA is completely covered by old_source + new_source. + # BBB loses coverage at the old_source boundary. + assert "BBB" in message + assert "2019-01-01T00:00:00+00:00" in message + assert "2021-01-01T00:00:00+00:00" in message + + +def test_target_plan_excludes_temporally_inactive_source(): + """A source outside the requested interval remains in the plan but inactive.""" + source_registry = { + "inactive_source": { + "temporal_scope": { + "start": "2000-01-01", + "end": "2018-01-01", + }, + "contexts": ["AAA"], + }, + "active_source": { + "temporal_scope": { + "start": "2018-01-01", + "end": "2025-01-01", + }, + "contexts": ["AAA"], + }, + } + + plan = build_target_data_plan( + target_contexts=["AAA"], + source_names=["inactive_source", "active_source"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + assert plan["active_sources"] == ["active_source"] + + assert plan["source_contexts"]["inactive_source"] == [] + + assert ( + plan["source_temporal_scopes"]["inactive_source"] + is None + ) + + +def test_target_plan_preserves_configured_source_priority(): + """Active sources retain the order supplied in configuration.""" + source_registry = { + "source_a": { + "contexts": ["AAA"], + }, + "source_b": { + "contexts": ["AAA"], + }, + "source_c": { + "contexts": ["AAA"], + }, + } + + plan = build_target_data_plan( + target_contexts=["AAA"], + source_names=["source_c", "source_a", "source_b"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + assert plan["active_sources"] == [ + "source_c", + "source_a", + "source_b", + ] + + +def test_target_plan_sorts_and_deduplicates_target_contexts(): + """Target contexts are stored uniquely and deterministically.""" + source_registry = { + "source_a": {}, + } + + plan = build_target_data_plan( + target_contexts=["CCC", "AAA", "BBB", "AAA"], + source_names=["source_a"], + source_registry=source_registry, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + assert plan["target_contexts"] == [ + "AAA", + "BBB", + "CCC", + ] + + assert plan["source_contexts"]["source_a"] == [ + "AAA", + "BBB", + "CCC", + ] + + +def test_target_plan_rejects_unknown_source(): + """Configured sources must exist in the source registry.""" + with pytest.raises( + ValueError, + match="Unsupported electricity-demand source", + ): + build_target_data_plan( + target_contexts=["AAA"], + source_names=["not_a_source"], + source_registry={}, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + +def test_target_plan_rejects_empty_target_contexts(): + """Planning requires at least one land-country context.""" + with pytest.raises( + ValueError, + match="no land-country contexts", + ): + build_target_data_plan( + target_contexts=[], + source_names=["source_a"], + source_registry={ + "source_a": {}, + }, + temporal_scope={ + "start": "2018-01-01", + "end": "2021-01-01", + }, + ) + + +# --------------------------------------------------------------------------- +# Plan serialization +# --------------------------------------------------------------------------- + + +def test_write_target_data_plan_creates_json_file(tmp_path): + """Target plans are written as readable JSON and parent dirs are created.""" + plan = { + "version": 2, + "target_contexts": ["AAA"], + "active_sources": ["source_a"], + "source_contexts": { + "source_a": ["AAA"], + }, + "source_temporal_scopes": { + "source_a": { + "start": "2018-01-01T00:00:00+00:00", + "end": "2021-01-01T00:00:00+00:00", + } + }, + } + + output = tmp_path / "nested" / "target_data_plan.json" + + write_target_data_plan( + plan=plan, + output_path=output, + ) + + assert output.exists() + + with output.open(encoding="utf-8") as file: + written = json.load(file) + + assert written == plan \ No newline at end of file diff --git a/workflow/scripts/plan_target_data.py b/workflow/scripts/plan_target_data.py index 35594aa..5bce629 100644 --- a/workflow/scripts/plan_target_data.py +++ b/workflow/scripts/plan_target_data.py @@ -86,6 +86,49 @@ def supported_target_contexts( ] +def uncovered_temporal_intervals( + intervals: Sequence[tuple[object, object]], + *, + start: object, + end: object, +) -> list[tuple[pd.Timestamp, pd.Timestamp]]: + """Return gaps in the requested period not covered by any interval.""" + requested_start = _as_utc(start) + requested_end = _as_utc(end) + + covered_intervals = sorted( + ( + max(_as_utc(interval_start), requested_start), + min(_as_utc(interval_end), requested_end), + ) + for interval_start, interval_end in intervals + if ( + _as_utc(interval_start) < requested_end + and _as_utc(interval_end) > requested_start + ) + ) + + gaps: list[tuple[pd.Timestamp, pd.Timestamp]] = [] + cursor = requested_start + + for interval_start, interval_end in covered_intervals: + if interval_end <= cursor: + continue + + if interval_start > cursor: + gaps.append((cursor, interval_start)) + + cursor = max(cursor, interval_end) + + if cursor >= requested_end: + break + + if cursor < requested_end: + gaps.append((cursor, requested_end)) + + return gaps + + def build_target_data_plan( *, target_contexts: Sequence[str], @@ -135,21 +178,52 @@ def build_target_data_plan( if contexts: active_sources.append(source_name) - covered_contexts = { - context - for contexts in source_contexts.values() - for context in contexts - } + uncovered_by_context: dict[ + str, + list[tuple[pd.Timestamp, pd.Timestamp]], + ] = {} - uncovered_contexts = sorted( - set(target_contexts).difference(covered_contexts) - ) + for context in target_contexts: + intervals = [] + + for source_name, contexts in source_contexts.items(): + if context not in contexts: + continue + + source_temporal_scope = source_temporal_scopes[source_name] + + if source_temporal_scope is None: + continue + + intervals.append( + ( + source_temporal_scope["start"], + source_temporal_scope["end"], + ) + ) + + gaps = uncovered_temporal_intervals( + intervals, + start=temporal_scope["start"], + end=temporal_scope["end"], + ) + + if gaps: + uncovered_by_context[context] = gaps + + if uncovered_by_context: + gap_descriptions = { + context: [ + f"[{gap_start.isoformat()}, {gap_end.isoformat()})" + for gap_start, gap_end in gaps + ] + for context, gaps in uncovered_by_context.items() + } - if uncovered_contexts: raise ValueError( - "No configured electricity-demand source can supply " - "the following target context(s) within the requested " - f"temporal scope: {uncovered_contexts}. " + "Configured electricity-demand sources do not provide " + "complete temporal coverage for the following target " + f"context(s): {gap_descriptions}. " f"Configured sources: {list(source_names)}." ) From 565015de10e865eb006497dcd10f11cb8304cbd5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 15:26:24 +0200 Subject: [PATCH 181/241] Enabled temporal clipping on aux data. --- tests/unit/test_plan_target_data.py | 1 - workflow/scripts/_advanced_execution.py | 72 +++++++++--- workflow/scripts/_tclean_config.py | 134 +++++++++++++++++----- workflow/scripts/clean_auxiliary_group.py | 23 ++-- 4 files changed, 177 insertions(+), 53 deletions(-) diff --git a/tests/unit/test_plan_target_data.py b/tests/unit/test_plan_target_data.py index 5f172bf..9f7b98b 100644 --- a/tests/unit/test_plan_target_data.py +++ b/tests/unit/test_plan_target_data.py @@ -7,7 +7,6 @@ import pandas as pd import pytest - # workflow/scripts is not a Python package, so expose it for direct imports. ROOT = Path(__file__).resolve().parents[2] SCRIPTS_DIR = ROOT / "workflow" / "scripts" diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py index f161bc4..63ffc3e 100644 --- a/workflow/scripts/_advanced_execution.py +++ b/workflow/scripts/_advanced_execution.py @@ -10,7 +10,7 @@ import pandas as pd -EXECUTION_PLAN_VERSION = 1 +EXECUTION_PLAN_VERSION = 2 def build_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: @@ -28,23 +28,48 @@ def build_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: f"columns: {sorted(missing_columns)}." ) + requests = requests.copy() + + if "group_start" not in requests.columns: + requests["group_start"] = requests["start"] + + if "group_end" not in requests.columns: + requests["group_end"] = requests["end"] + batches: list[dict[str, object]] = [] - grouped = requests.groupby(["source", "start", "end"], sort=False) + grouped = requests.groupby( + ["source", "start", "end", "group_start", "group_end"], + sort=False, + ) - for (source, start, end), group in grouped: + for (source, start, end, group_start, group_end), group in grouped: start = pd.Timestamp(start) end = pd.Timestamp(end) + group_start = pd.Timestamp(group_start) + group_end = pd.Timestamp(group_end) if end <= start: raise ValueError("Auxiliary batch end must be later than its start.") + if group_end <= group_start: + raise ValueError("Auxiliary group end must be later than its start.") + + if start < group_start or end > group_end: + raise ValueError( + "Auxiliary provider batch must lie within its logical group period." + ) + countries = sorted(group["context"].drop_duplicates().tolist()) - group_id = build_group_id(start=start, end=end) + group_id = build_group_id(start=group_start, end=group_end) batch_id = build_batch_id( - source=str(source), start=start, end=end, countries=countries + source=str(source), + start=start, + end=end, + countries=countries, + group_id=group_id, ) batches.append( @@ -54,6 +79,8 @@ def build_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: "source": str(source), "start": start, "end": end, + "group_start": group_start, + "group_end": group_end, "countries": countries, } ) @@ -65,16 +92,23 @@ def serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: """Convert one auxiliary batch to JSON-compatible values.""" start = pd.Timestamp(batch["start"]) end = pd.Timestamp(batch["end"]) + group_start = pd.Timestamp(batch.get("group_start", start)) + group_end = pd.Timestamp(batch.get("group_end", end)) if end <= start: raise ValueError("Auxiliary batch end must be later than its start.") + if group_end <= group_start: + raise ValueError("Auxiliary group end must be later than its start.") + final_included_time = end - pd.Timedelta(nanoseconds=1) return { **batch, "start": start.isoformat(), "end": end.isoformat(), + "group_start": group_start.isoformat(), + "group_end": group_end.isoformat(), "years": list(range(start.year, final_included_time.year + 1)), } @@ -85,16 +119,26 @@ def build_group_id(*, start: pd.Timestamp, end: pd.Timestamp) -> str: def build_batch_id( - *, source: str, start: pd.Timestamp, end: pd.Timestamp, countries: Sequence[str] + *, + source: str, + start: pd.Timestamp, + end: pd.Timestamp, + countries: Sequence[str], + group_id: str | None = None, ) -> str: """Build a compact deterministic identifier for one provider batch.""" + batch_key_data = { + "source": str(source), + "start": pd.Timestamp(start).isoformat(), + "end": pd.Timestamp(end).isoformat(), + "countries": sorted(str(country) for country in countries), + } + + if group_id is not None: + batch_key_data["group_id"] = str(group_id) + batch_key = json.dumps( - { - "source": str(source), - "start": pd.Timestamp(start).isoformat(), - "end": pd.Timestamp(end).isoformat(), - "countries": sorted(str(country) for country in countries), - }, + batch_key_data, sort_keys=True, separators=(",", ":"), ) @@ -162,8 +206,8 @@ def resolve_required_group_ids( for batch in batches if ( period.context in batch["countries"] - and pd.Timestamp(batch["start"]) <= start - and pd.Timestamp(batch["end"]) >= end + and pd.Timestamp(batch.get("group_start", batch["start"])) <= start + and pd.Timestamp(batch.get("group_end", batch["end"])) >= end ) } diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 922340a..8cf4f36 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -189,65 +189,137 @@ def filter_source_requests_by_temporal_scope( requirements: pd.DataFrame, source_registry: Mapping[str, Mapping[str, Any]], ) -> pd.DataFrame: - """Remove auxiliary source requests outside provider availability.""" + """Clip auxiliary source requests to provider temporal availability.""" if requests.empty: return requests.copy() - keep: list[bool] = [] + output_columns = list(requests.columns) + + for column in ("group_start", "group_end"): + if column not in output_columns: + output_columns.append(column) + + clipped_rows: list[dict[str, object]] = [] for request in requests.itertuples(index=False): + row = request._asdict() + metadata = source_registry[str(request.source)] temporal_scope = metadata.get("temporal_scope") or {} - source_start = temporal_scope.get("start") - source_end = temporal_scope.get("end") - request_start = pd.to_datetime(request.start, utc=True) request_end = pd.to_datetime(request.end, utc=True) - overlaps = True + effective_start = request_start + effective_end = request_end + + source_start = temporal_scope.get("start") + source_end = temporal_scope.get("end") if source_start is not None: - overlaps &= request_end > pd.to_datetime( - source_start, - utc=True, + effective_start = max( + effective_start, + pd.to_datetime(source_start, utc=True), ) if source_end is not None: - overlaps &= request_start < pd.to_datetime( - source_end, - utc=True, + effective_end = min( + effective_end, + pd.to_datetime(source_end, utc=True), ) - keep.append(overlaps) + if effective_start >= effective_end: + continue + + row["group_start"] = request_start + row["group_end"] = request_end + row["start"] = effective_start + row["end"] = effective_end + + clipped_rows.append(row) - filtered = requests.loc[keep].reset_index(drop=True) + filtered = pd.DataFrame(clipped_rows, columns=output_columns) required_periods = requirements[ ["context", "start", "end"] ].drop_duplicates() - covered_periods = filtered[ - ["context", "start", "end"] - ].drop_duplicates() + uncovered: list[dict[str, object]] = [] - coverage = required_periods.merge( - covered_periods, - on=["context", "start", "end"], - how="left", - indicator=True, - ) + for requirement in required_periods.itertuples(index=False): + context = str(requirement.context) + required_start = pd.to_datetime(requirement.start, utc=True) + required_end = pd.to_datetime(requirement.end, utc=True) + + candidates = filtered.loc[ + (filtered["context"] == requirement.context) + & (filtered["group_start"] == required_start) + & (filtered["group_end"] == required_end) + ] - uncovered = coverage.loc[ - coverage["_merge"] == "left_only", - ["context", "start", "end"], - ] + gaps = _uncovered_temporal_intervals( + candidates[["start", "end"]], + start=required_start, + end=required_end, + ) - if not uncovered.empty: + uncovered.extend( + { + "context": context, + "start": gap_start.isoformat(), + "end": gap_end.isoformat(), + } + for gap_start, gap_end in gaps + ) + + if uncovered: raise ValueError( - "No configured auxiliary source overlaps the declared " - "temporal scope for required context-period(s): " - f"{uncovered.to_dict(orient='records')}." + "Configured auxiliary sources do not provide complete " + "temporal coverage for required context-period(s): " + f"{uncovered}." ) return filtered + + +def _uncovered_temporal_intervals( + intervals: pd.DataFrame, + *, + start: object, + end: object, +) -> list[tuple[pd.Timestamp, pd.Timestamp]]: + """Return gaps in one required period not covered by source intervals.""" + required_start = pd.to_datetime(start, utc=True) + required_end = pd.to_datetime(end, utc=True) + + covered_intervals = sorted( + ( + max(pd.to_datetime(row.start, utc=True), required_start), + min(pd.to_datetime(row.end, utc=True), required_end), + ) + for row in intervals.itertuples(index=False) + if ( + pd.to_datetime(row.start, utc=True) < required_end + and pd.to_datetime(row.end, utc=True) > required_start + ) + ) + + gaps: list[tuple[pd.Timestamp, pd.Timestamp]] = [] + cursor = required_start + + for interval_start, interval_end in covered_intervals: + if interval_end <= cursor: + continue + + if interval_start > cursor: + gaps.append((cursor, interval_start)) + + cursor = max(cursor, interval_end) + + if cursor >= required_end: + break + + if cursor < required_end: + gaps.append((cursor, required_end)) + + return gaps diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index 939d7a5..e3cd3ec 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -26,17 +26,23 @@ def main(snakemake: Any) -> None: if not batches: raise ValueError(f"Auxiliary group {group_id!r} contains no source batches.") - starts = {pd.Timestamp(batch["start"]) for batch in batches} + group_starts = { + pd.Timestamp(batch.get("group_start", batch["start"])) + for batch in batches + } - ends = {pd.Timestamp(batch["end"]) for batch in batches} + group_ends = { + pd.Timestamp(batch.get("group_end", batch["end"])) + for batch in batches + } - if len(starts) != 1 or len(ends) != 1: + if len(group_starts) != 1 or len(group_ends) != 1: raise ValueError( - f"Auxiliary group {group_id!r} contains inconsistent batch periods." + f"Auxiliary group {group_id!r} contains inconsistent logical periods." ) - group_start = next(iter(starts)) - group_end = next(iter(ends)) + group_start = next(iter(group_starts)) + group_end = next(iter(group_ends)) grid = TimeGrid( start=group_start, end=group_end, frequency=(snakemake.params.frequency) @@ -72,7 +78,10 @@ def main(snakemake: Any) -> None: ) sources = { - source_name: data.reindex(columns=contexts) + source_name: data.reindex( + index=grid.target_index, + columns=contexts, + ) for source_name, data in sources.items() } From 73b723880c1cc370b8010885d5d3e60d6a364dde Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 15:29:42 +0200 Subject: [PATCH 182/241] Added advanced temporal clipping tests --- tests/unit/test_advanced_execution.py | 56 ++++++++ tests/unit/test_tclean_config.py | 195 ++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py index d97b5ea..0a8656d 100644 --- a/tests/unit/test_advanced_execution.py +++ b/tests/unit/test_advanced_execution.py @@ -14,6 +14,7 @@ index_batch_ids_by_group, index_batch_ids_by_source, load_execution_plan, + resolve_required_group_ids, serialize_batch, ) @@ -180,3 +181,58 @@ def test_get_batch_requires_exactly_one_match() -> None: empty_execution_plan(), batch_id="missing", ) + +def test_clipped_batches_share_logical_group() -> None: + """Check provider slices retain one logical auxiliary period.""" + group_start = pd.Timestamp("2018-01-01", tz="UTC") + boundary = pd.Timestamp("2019-01-01", tz="UTC") + group_end = pd.Timestamp("2021-01-01", tz="UTC") + + requests = pd.DataFrame( + { + "source": ["opsd", "entsoe_power_statistics"], + "context": ["AAA", "AAA"], + "start": [group_start, boundary], + "end": [boundary, group_end], + "group_start": [group_start, group_start], + "group_end": [group_end, group_end], + } + ) + + batches = build_source_batches(requests) + + assert len(batches) == 2 + + assert batches[0]["start"] == group_start + assert batches[0]["end"] == boundary + + assert batches[1]["start"] == boundary + assert batches[1]["end"] == group_end + + expected_group_id = build_group_id( + start=group_start, + end=group_end, + ) + + assert batches[0]["group_id"] == expected_group_id + assert batches[1]["group_id"] == expected_group_id + + by_group = index_batch_ids_by_group(batches) + + assert list(by_group) == [expected_group_id] + assert by_group[expected_group_id] == [ + batch["batch_id"] for batch in batches + ] + + source_periods = pd.DataFrame( + { + "context": ["AAA"], + "start": [group_start], + "end": [group_end], + } + ) + + assert resolve_required_group_ids( + batches, + source_periods=source_periods, + ) == [expected_group_id] diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index 63126a8..8f9ee3e 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -1,12 +1,14 @@ """Tests for the Modelblocks-to-T-Clean configuration adapter.""" import pandas as pd +import pytest from _tclean_config import ( build_advanced_rules, build_all_constructed_source_periods, build_basic_rules, build_scaling_source_periods, build_time_grid, + filter_source_requests_by_temporal_scope, ) from tclean import TimeGrid @@ -195,3 +197,196 @@ def test_match_total_scaling_adds_auxiliary_periods() -> None: assert set(result) == {"scaled_source"} assert result["scaled_source"]["context"].tolist() == ["GBR", "ALB"] + +def test_auxiliary_temporal_filter_preserves_fully_available_request() -> None: + """Check a provider covering the whole requirement is not clipped.""" + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + + requests = pd.DataFrame( + { + "source": ["source_a"], + "context": ["AAA"], + "start": [start], + "end": [end], + } + ) + + requirements = pd.DataFrame( + { + "context": ["AAA"], + "start": [start], + "end": [end], + } + ) + + result = filter_source_requests_by_temporal_scope( + requests, + requirements=requirements, + source_registry={ + "source_a": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2021-01-01", + } + } + }, + ) + + assert len(result) == 1 + assert result.loc[0, "start"] == start + assert result.loc[0, "end"] == end + assert result.loc[0, "group_start"] == start + assert result.loc[0, "group_end"] == end + + +def test_auxiliary_temporal_filter_clips_adjacent_providers() -> None: + """Check adjacent providers are clipped to a shared requirement.""" + group_start = pd.Timestamp("2018-01-01", tz="UTC") + boundary = pd.Timestamp("2019-01-01", tz="UTC") + group_end = pd.Timestamp("2021-01-01", tz="UTC") + + requests = pd.DataFrame( + { + "source": ["old_source", "new_source"], + "context": ["AAA", "AAA"], + "start": [group_start, group_start], + "end": [group_end, group_end], + } + ) + + requirements = pd.DataFrame( + { + "context": ["AAA"], + "start": [group_start], + "end": [group_end], + } + ) + + result = filter_source_requests_by_temporal_scope( + requests, + requirements=requirements, + source_registry={ + "old_source": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + } + }, + "new_source": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2026-01-01", + } + }, + }, + ) + + old_source = result.loc[result["source"] == "old_source"].iloc[0] + new_source = result.loc[result["source"] == "new_source"].iloc[0] + + assert old_source["start"] == group_start + assert old_source["end"] == boundary + + assert new_source["start"] == boundary + assert new_source["end"] == group_end + + assert old_source["group_start"] == group_start + assert old_source["group_end"] == group_end + assert new_source["group_start"] == group_start + assert new_source["group_end"] == group_end + + +def test_auxiliary_temporal_filter_removes_non_overlapping_provider() -> None: + """Check an unavailable provider is removed when another covers the period.""" + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2021-01-01", tz="UTC") + + requests = pd.DataFrame( + { + "source": ["old_source", "current_source"], + "context": ["AAA", "AAA"], + "start": [start, start], + "end": [end, end], + } + ) + + requirements = pd.DataFrame( + { + "context": ["AAA"], + "start": [start], + "end": [end], + } + ) + + result = filter_source_requests_by_temporal_scope( + requests, + requirements=requirements, + source_registry={ + "old_source": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + } + }, + "current_source": { + "temporal_scope": { + "start": "2019-01-01", + "end": "2025-01-01", + } + }, + }, + ) + + assert result["source"].tolist() == ["current_source"] + assert result.loc[0, "start"] == start + assert result.loc[0, "end"] == end + + +def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: + """Check a temporal gap between providers is rejected.""" + start = pd.Timestamp("2018-01-01", tz="UTC") + end = pd.Timestamp("2021-01-01", tz="UTC") + + requests = pd.DataFrame( + { + "source": ["old_source", "new_source"], + "context": ["AAA", "AAA"], + "start": [start, start], + "end": [end, end], + } + ) + + requirements = pd.DataFrame( + { + "context": ["AAA"], + "start": [start], + "end": [end], + } + ) + + with pytest.raises(ValueError) as exc_info: + filter_source_requests_by_temporal_scope( + requests, + requirements=requirements, + source_registry={ + "old_source": { + "temporal_scope": { + "start": "2005-01-01", + "end": "2019-01-01", + } + }, + "new_source": { + "temporal_scope": { + "start": "2019-02-01", + "end": "2026-01-01", + } + }, + }, + ) + + message = str(exc_info.value) + + assert "AAA" in message + assert "2019-01-01T00:00:00+00:00" in message + assert "2019-02-01T00:00:00+00:00" in message From 90298315d23f46a94e4aee9ca0a6d3bb13c4d459 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 16:18:12 +0200 Subject: [PATCH 183/241] Tidied helpers to avoid code duplication --- workflow/rules/_utils.smk | 30 ------- .../rules/source_entsoe_power_statistics.smk | 7 +- workflow/scripts/_prepared_data.py | 23 +++++ workflow/scripts/_source_capabilities.py | 86 ++++++++++++++++++ workflow/scripts/_tclean_config.py | 81 ++++------------- workflow/scripts/apply_advanced_overrides.py | 5 +- workflow/scripts/clean_auxiliary_group.py | 32 +------ workflow/scripts/clean_demand.py | 21 +---- .../scripts/construct_auxiliary_profile.py | 6 +- workflow/scripts/plan_target_data.py | 89 ++++--------------- 10 files changed, 152 insertions(+), 228 deletions(-) create mode 100644 workflow/scripts/_prepared_data.py create mode 100644 workflow/scripts/_source_capabilities.py diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 929ec19..766c313 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -27,36 +27,6 @@ def _as_utc(value): return parsed.astimezone(timezone.utc) -def source_overlaps_period( - source_name, - start, - end, -): - """Return whether a source can supply any part of a period.""" - metadata = SOURCE_REGISTRY[source_name] - temporal_scope = metadata.get("temporal_scope") or {} - - source_start = temporal_scope.get("start") - source_end = temporal_scope.get("end") - - start = _as_utc(start) - end = _as_utc(end) - - if source_start is not None: - source_start = _as_utc(source_start) - - if end <= source_start: - return False - - if source_end is not None: - source_end = _as_utc(source_end) - - if start >= source_end: - return False - - return True - - def years_for_period(start, end): """Return UTC calendar years intersected by the half-open period [start, end). Mirrors tclean logic.""" start = _as_utc(start) diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index ffa6bdb..7cef3df 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -2,15 +2,10 @@ def entsoe_power_statistics_annual_files(years): - """Return supported annual ENTSO-E Power Statistics files.""" + """Return annual ENTSO-E Power Statistics files.""" return [ ("/automatic/" "entsoe_power_statistics/raw/" f"{int(year)}.parquet") for year in years - if source_overlaps_period( - "entsoe_power_statistics", - f"{int(year)}-01-01", - f"{int(year) + 1}-01-01", - ) ] diff --git a/workflow/scripts/_prepared_data.py b/workflow/scripts/_prepared_data.py new file mode 100644 index 0000000..30a2cce --- /dev/null +++ b/workflow/scripts/_prepared_data.py @@ -0,0 +1,23 @@ +"""Shared helpers for prepared electricity-demand data.""" + +from pathlib import Path + +import pandas as pd + + +def read_prepared_source(path: str | Path) -> pd.DataFrame: + """Read a prepared source and normalise its timestamp index to UTC.""" + data = pd.read_parquet(path) + + if not isinstance(data.index, pd.DatetimeIndex): + data.index = pd.to_datetime(data.index, utc=True) + + elif data.index.tz is None: + data.index = data.index.tz_localize("UTC") + + else: + data.index = data.index.tz_convert("UTC") + + data.index.name = "timestamp" + + return data diff --git a/workflow/scripts/_source_capabilities.py b/workflow/scripts/_source_capabilities.py new file mode 100644 index 0000000..c620bc8 --- /dev/null +++ b/workflow/scripts/_source_capabilities.py @@ -0,0 +1,86 @@ +"""Shared source-capability helpers for electricity-demand planning.""" + +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + + +def as_utc(value: object) -> pd.Timestamp: + """Interpret naive timestamps as UTC and convert aware timestamps to UTC.""" + timestamp = pd.Timestamp(value) + + if timestamp.tzinfo is None: + return timestamp.tz_localize("UTC") + + return timestamp.tz_convert("UTC") + + +def intersect_source_temporal_scope( + metadata: Mapping[str, Any], + *, + start: object, + end: object, +) -> tuple[pd.Timestamp, pd.Timestamp] | None: + """Return the intersection of a request and one source's temporal scope.""" + temporal_scope = metadata.get("temporal_scope") or {} + + effective_start = as_utc(start) + effective_end = as_utc(end) + + source_start = temporal_scope.get("start") + source_end = temporal_scope.get("end") + + if source_start is not None: + effective_start = max(effective_start, as_utc(source_start)) + + if source_end is not None: + effective_end = min(effective_end, as_utc(source_end)) + + if effective_start >= effective_end: + return None + + return effective_start, effective_end + + +def uncovered_temporal_intervals( + intervals: Sequence[tuple[object, object]], + *, + start: object, + end: object, +) -> list[tuple[pd.Timestamp, pd.Timestamp]]: + """Return gaps in a required period not covered by any supplied interval.""" + required_start = as_utc(start) + required_end = as_utc(end) + + covered_intervals = sorted( + ( + max(as_utc(interval_start), required_start), + min(as_utc(interval_end), required_end), + ) + for interval_start, interval_end in intervals + if ( + as_utc(interval_start) < required_end + and as_utc(interval_end) > required_start + ) + ) + + gaps: list[tuple[pd.Timestamp, pd.Timestamp]] = [] + cursor = required_start + + for interval_start, interval_end in covered_intervals: + if interval_end <= cursor: + continue + + if interval_start > cursor: + gaps.append((cursor, interval_start)) + + cursor = max(cursor, interval_end) + + if cursor >= required_end: + break + + if cursor < required_end: + gaps.append((cursor, required_end)) + + return gaps diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 8cf4f36..96cb941 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -6,6 +6,10 @@ from typing import Any import pandas as pd +from _source_capabilities import ( + intersect_source_temporal_scope, + uncovered_temporal_intervals, +) from tclean import TCleanConfig, TimeGrid @@ -205,32 +209,21 @@ def filter_source_requests_by_temporal_scope( row = request._asdict() metadata = source_registry[str(request.source)] - temporal_scope = metadata.get("temporal_scope") or {} request_start = pd.to_datetime(request.start, utc=True) request_end = pd.to_datetime(request.end, utc=True) - effective_start = request_start - effective_end = request_end - - source_start = temporal_scope.get("start") - source_end = temporal_scope.get("end") - - if source_start is not None: - effective_start = max( - effective_start, - pd.to_datetime(source_start, utc=True), - ) - - if source_end is not None: - effective_end = min( - effective_end, - pd.to_datetime(source_end, utc=True), - ) + effective_scope = intersect_source_temporal_scope( + metadata, + start=request_start, + end=request_end, + ) - if effective_start >= effective_end: + if effective_scope is None: continue + effective_start, effective_end = effective_scope + row["group_start"] = request_start row["group_end"] = request_end row["start"] = effective_start @@ -257,8 +250,11 @@ def filter_source_requests_by_temporal_scope( & (filtered["group_end"] == required_end) ] - gaps = _uncovered_temporal_intervals( - candidates[["start", "end"]], + gaps = uncovered_temporal_intervals( + [ + (candidate.start, candidate.end) + for candidate in candidates.itertuples(index=False) + ], start=required_start, end=required_end, ) @@ -280,46 +276,3 @@ def filter_source_requests_by_temporal_scope( ) return filtered - - -def _uncovered_temporal_intervals( - intervals: pd.DataFrame, - *, - start: object, - end: object, -) -> list[tuple[pd.Timestamp, pd.Timestamp]]: - """Return gaps in one required period not covered by source intervals.""" - required_start = pd.to_datetime(start, utc=True) - required_end = pd.to_datetime(end, utc=True) - - covered_intervals = sorted( - ( - max(pd.to_datetime(row.start, utc=True), required_start), - min(pd.to_datetime(row.end, utc=True), required_end), - ) - for row in intervals.itertuples(index=False) - if ( - pd.to_datetime(row.start, utc=True) < required_end - and pd.to_datetime(row.end, utc=True) > required_start - ) - ) - - gaps: list[tuple[pd.Timestamp, pd.Timestamp]] = [] - cursor = required_start - - for interval_start, interval_end in covered_intervals: - if interval_end <= cursor: - continue - - if interval_start > cursor: - gaps.append((cursor, interval_start)) - - cursor = max(cursor, interval_end) - - if cursor >= required_end: - break - - if cursor < required_end: - gaps.append((cursor, required_end)) - - return gaps diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index f07dd69..17b6412 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -1,15 +1,14 @@ """Apply advanced T-Clean rules to basic-cleaned demand.""" -import json from pathlib import Path import pandas as pd +from _advanced_execution import load_execution_plan from tclean import TimeGrid from tclean.advanced import read_external_profile from tclean.advanced.apply import apply_advanced_rules -with open(snakemake.input.plan, encoding="utf-8") as file: - plan = json.load(file) +plan = load_execution_plan(snakemake.input.plan) grid = TimeGrid( start=snakemake.params.temporal_scope["start"], diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index e3cd3ec..c339344 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -1,10 +1,10 @@ """Combine and clean one auxiliary electricity-demand group.""" -import json -from pathlib import Path from typing import TYPE_CHECKING, Any import pandas as pd +from _advanced_execution import load_execution_plan +from _prepared_data import read_prepared_source from tclean import TCleanConfig, TimeGrid, clean if TYPE_CHECKING: @@ -13,7 +13,7 @@ def main(snakemake: Any) -> None: """Combine and basic-clean one auxiliary source group.""" - plan = _read_plan(snakemake.input.plan) + plan = load_execution_plan(snakemake.input.plan) group_id = str(snakemake.wildcards.group_id) @@ -71,7 +71,7 @@ def main(snakemake: Any) -> None: f"{source_name!r}." ) - sources[source_name] = _read_prepared_source(path) + sources[source_name] = read_prepared_source(path) contexts = sorted( {context for data in sources.values() for context in data.columns} @@ -102,29 +102,5 @@ def main(snakemake: Any) -> None: cleaning_method.to_parquet(snakemake.output.cleaning_method) -def _read_plan(path: str | Path) -> dict[str, Any]: - """Read the advanced execution manifest.""" - with Path(path).open(encoding="utf-8") as file: - return json.load(file) - - -def _read_prepared_source(path: str | Path) -> pd.DataFrame: - """Read one prepared auxiliary provider frame.""" - data = pd.read_parquet(path) - - if not isinstance(data.index, pd.DatetimeIndex): - data.index = pd.to_datetime(data.index, utc=True) - - elif data.index.tz is None: - data.index = data.index.tz_localize("UTC") - - else: - data.index = data.index.tz_convert("UTC") - - data.index.name = "timestamp" - - return data - - if __name__ == "__main__": main(snakemake) diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index a04f19b..4cc1460 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any import pandas as pd +from _prepared_data import read_prepared_source from _tclean_config import build_basic_rules, build_tclean_config from tclean import clean from tclean.advanced import build_gap_report @@ -31,7 +32,7 @@ def main(snakemake: Any) -> None: ) sources = { - source_name: _read_prepared_source(path) + source_name: read_prepared_source(path) for source_name, path in zip(source_names, input_paths, strict=True) } @@ -88,24 +89,6 @@ def main(snakemake: Any) -> None: _log_gap_report(gap_report) -def _read_prepared_source(path: str | Path) -> pd.DataFrame: - """Read one prepared electricity-demand source.""" - data = pd.read_parquet(path) - - if not isinstance(data.index, pd.DatetimeIndex): - data.index = pd.to_datetime(data.index, utc=True) - - elif data.index.tz is None: - data.index = data.index.tz_localize("UTC") - - else: - data.index = data.index.tz_convert("UTC") - - data.index.name = "timestamp" - - return data - - def _read_target_contexts(path: str | Path) -> list[str]: """Read the planned target contexts for one shape.""" with open(path, encoding="utf-8") as file: diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index be5dac2..9e91a1d 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,8 +1,7 @@ """Construct an auxiliary profile from cleaned source data.""" -import json - import pandas as pd +from _advanced_execution import load_execution_plan from _tclean_config import ( build_constructed_source_periods, build_scaling_source_periods, @@ -10,8 +9,7 @@ from tclean import TimeGrid from tclean.advanced import construct_from_sources -with open(snakemake.input.plan, encoding="utf-8") as file: - plan = json.load(file) +plan = load_execution_plan(snakemake.input.plan) rule_name = snakemake.wildcards.rule_name rule = plan["rules"][rule_name] diff --git a/workflow/scripts/plan_target_data.py b/workflow/scripts/plan_target_data.py index 5bce629..a78bcfa 100644 --- a/workflow/scripts/plan_target_data.py +++ b/workflow/scripts/plan_target_data.py @@ -8,6 +8,11 @@ import geopandas as gpd import pandas as pd from _schemas import Shapes +from _source_capabilities import ( + as_utc, + intersect_source_temporal_scope, + uncovered_temporal_intervals, +) if TYPE_CHECKING: snakemake: Any @@ -16,14 +21,7 @@ TARGET_DATA_PLAN_VERSION = 2 -def _as_utc(value: object) -> pd.Timestamp: - """Interpret naive timestamps as UTC and convert aware timestamps to UTC.""" - timestamp = pd.Timestamp(value) - - if timestamp.tzinfo is None: - return timestamp.tz_localize("UTC") - - return timestamp.tz_convert("UTC") +_as_utc = as_utc def effective_source_temporal_scope( @@ -33,37 +31,23 @@ def effective_source_temporal_scope( end: object, ) -> dict[str, str] | None: """Return the intersection of requested and source temporal scopes.""" - temporal_scope = metadata.get("temporal_scope") or {} - - requested_start = _as_utc(start) - requested_end = _as_utc(end) - - source_start = temporal_scope.get("start") - source_end = temporal_scope.get("end") - - effective_start = requested_start - effective_end = requested_end - - if source_start is not None: - effective_start = max( - effective_start, - _as_utc(source_start), - ) - - if source_end is not None: - effective_end = min( - effective_end, - _as_utc(source_end), - ) + effective_scope = intersect_source_temporal_scope( + metadata, + start=start, + end=end, + ) - if effective_start >= effective_end: + if effective_scope is None: return None + effective_start, effective_end = effective_scope + return { "start": effective_start.isoformat(), "end": effective_end.isoformat(), } + def supported_target_contexts( target_contexts: Sequence[str], *, @@ -86,49 +70,6 @@ def supported_target_contexts( ] -def uncovered_temporal_intervals( - intervals: Sequence[tuple[object, object]], - *, - start: object, - end: object, -) -> list[tuple[pd.Timestamp, pd.Timestamp]]: - """Return gaps in the requested period not covered by any interval.""" - requested_start = _as_utc(start) - requested_end = _as_utc(end) - - covered_intervals = sorted( - ( - max(_as_utc(interval_start), requested_start), - min(_as_utc(interval_end), requested_end), - ) - for interval_start, interval_end in intervals - if ( - _as_utc(interval_start) < requested_end - and _as_utc(interval_end) > requested_start - ) - ) - - gaps: list[tuple[pd.Timestamp, pd.Timestamp]] = [] - cursor = requested_start - - for interval_start, interval_end in covered_intervals: - if interval_end <= cursor: - continue - - if interval_start > cursor: - gaps.append((cursor, interval_start)) - - cursor = max(cursor, interval_end) - - if cursor >= requested_end: - break - - if cursor < requested_end: - gaps.append((cursor, requested_end)) - - return gaps - - def build_target_data_plan( *, target_contexts: Sequence[str], From 63310c957d071547578a9a75ca234baac75bcf7d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 18:13:23 +0200 Subject: [PATCH 184/241] Uses strderr redirect as previous implementation didnt close-out the handover. --- workflow/scripts/demand_electricity_polygon.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/workflow/scripts/demand_electricity_polygon.py b/workflow/scripts/demand_electricity_polygon.py index 721358c..79c25ca 100644 --- a/workflow/scripts/demand_electricity_polygon.py +++ b/workflow/scripts/demand_electricity_polygon.py @@ -1,6 +1,7 @@ """Prepare electricity demand timeseries, aggregated to shapes.""" import sys +from contextlib import redirect_stderr from typing import TYPE_CHECKING, Any from warnings import warn @@ -148,11 +149,12 @@ def main( if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - main( - path_demand_raster=snakemake.input.demand_raster, - path_demand_profiles=snakemake.input.demand_profiles, - path_shapes=snakemake.input.shapes, - path_output_data=snakemake.output.output_data, - path_output_map=snakemake.output.output_map, - ) + with open(snakemake.log[0], "w", buffering=1) as log: + with redirect_stderr(log): + main( + path_demand_raster=snakemake.input.demand_raster, + path_demand_profiles=snakemake.input.demand_profiles, + path_shapes=snakemake.input.shapes, + path_output_data=snakemake.output.output_data, + path_output_map=snakemake.output.output_map, + ) From e881cbf394423875364fc85b2d0ae1b83391d245 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 19:02:28 +0200 Subject: [PATCH 185/241] Updated gitignore to ignore dev workspace --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 68f0b69..5e4ae80 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ gurobi.log #ignore ENTSOE token resources/user/token_entsoe.txt + +#dev workspace +**/dev/ \ No newline at end of file From 7018001fcd8d5c8fff0880ecba32bfc2f10259c9 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 19:05:34 +0200 Subject: [PATCH 186/241] Ensure workflow runs in advanced mode without advanced rules applied (to generate gap report). --- workflow/scripts/apply_advanced_overrides.py | 108 ++++++++++--------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 17b6412..7f3178b 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -20,55 +20,65 @@ data_source = pd.read_parquet(snakemake.input.data_source) cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) -rules = pd.DataFrame( - [ - { - "rule_name": rule_name, - "method": plan["rules"][rule_name]["method"], - "source": plan["rules"][rule_name]["source"], - "context": plan["rules"][rule_name]["context"], - "start": plan["rules"][rule_name]["start"], - "end": plan["rules"][rule_name]["end"], - "scope": plan["rules"][rule_name]["scope"], - } - for rule_name in plan["active_rule_names"] - ] -) - -advanced_sources = {} - -for path in snakemake.input.constructed_profiles: - rule_name = Path(path).stem - rule = plan["rules"][rule_name] - source_name = rule["source"] - - profile = pd.read_parquet(path) - - if profile.shape[1] != 1: - raise ValueError( - f"Constructed profile for rule {rule_name!r} " - "must contain exactly one column." - ) - - advanced_sources[source_name] = profile.iloc[:, 0] - -external_profile_paths = { - Path(path).name: path for path in snakemake.input.external_profiles -} - -for source_name, filename in plan["external_profile_files"].items(): - path = external_profile_paths[filename] - - advanced_sources[source_name] = read_external_profile(path, grid=grid) - -filled, _, cleaning_method = apply_advanced_rules( - data, - data_source, - cleaning_method, - rules=rules, - advanced_sources=advanced_sources, - grid=grid, -) +active_rule_names = plan["active_rule_names"] + +if not active_rule_names: + print( + "No advanced cleaning rules apply to the target contexts and period; " + "passing basic-cleaned demand through unchanged." + ) + filled = data.copy() + +else: + rules = pd.DataFrame( + [ + { + "rule_name": rule_name, + "method": plan["rules"][rule_name]["method"], + "source": plan["rules"][rule_name]["source"], + "context": plan["rules"][rule_name]["context"], + "start": plan["rules"][rule_name]["start"], + "end": plan["rules"][rule_name]["end"], + "scope": plan["rules"][rule_name]["scope"], + } + for rule_name in active_rule_names + ] + ) + + advanced_sources = {} + + for path in snakemake.input.constructed_profiles: + rule_name = Path(path).stem + rule = plan["rules"][rule_name] + source_name = rule["source"] + + profile = pd.read_parquet(path) + + if profile.shape[1] != 1: + raise ValueError( + f"Constructed profile for rule {rule_name!r} " + "must contain exactly one column." + ) + + advanced_sources[source_name] = profile.iloc[:, 0] + + external_profile_paths = { + Path(path).name: path for path in snakemake.input.external_profiles + } + + for source_name, filename in plan["external_profile_files"].items(): + path = external_profile_paths[filename] + + advanced_sources[source_name] = read_external_profile(path, grid=grid) + + filled, _, cleaning_method = apply_advanced_rules( + data, + data_source, + cleaning_method, + rules=rules, + advanced_sources=advanced_sources, + grid=grid, + ) filled.to_parquet(snakemake.output.demand) cleaning_method.to_parquet(snakemake.output.cleaning_method) From 30d036c13e66fdb42f597610dae10173d34f5822 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:10:23 +0000 Subject: [PATCH 187/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .gitignore | 2 +- tests/unit/test_advanced_execution.py | 67 +---- tests/unit/test_plan_target_data.py | 283 ++++-------------- tests/unit/test_tclean_config.py | 75 +---- workflow/internal/settings.yaml | 2 +- workflow/rules/advanced.smk | 31 +- workflow/rules/automatic.smk | 14 +- workflow/rules/clean.smk | 41 +-- workflow/rules/source_entsoe.smk | 6 +- .../rules/source_entsoe_power_statistics.smk | 19 +- workflow/rules/source_neso.smk | 11 +- workflow/rules/source_opsd.smk | 12 +- workflow/scripts/_advanced_execution.py | 13 +- workflow/scripts/_plot_timeline.py | 176 +++-------- workflow/scripts/_source_capabilities.py | 10 +- workflow/scripts/_tclean_config.py | 8 +- workflow/scripts/clean_auxiliary_group.py | 11 +- workflow/scripts/clean_demand.py | 7 +- .../scripts/demand_electricity_polygon.py | 1 - workflow/scripts/plan_auxiliary_data.py | 4 +- workflow/scripts/plan_target_data.py | 72 +---- 21 files changed, 200 insertions(+), 665 deletions(-) diff --git a/.gitignore b/.gitignore index 5e4ae80..d4da1e5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,4 @@ gurobi.log resources/user/token_entsoe.txt #dev workspace -**/dev/ \ No newline at end of file +**/dev/ diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py index 0a8656d..4a7bbd3 100644 --- a/tests/unit/test_advanced_execution.py +++ b/tests/unit/test_advanced_execution.py @@ -45,16 +45,10 @@ def test_batch_id_is_compact_and_independent_of_country_order() -> None: end = pd.Timestamp("2020-02-01", tz="UTC") first = build_batch_id( - source="entsoe", - start=start, - end=end, - countries=["ALB", "GRC"], + source="entsoe", start=start, end=end, countries=["ALB", "GRC"] ) second = build_batch_id( - source="entsoe", - start=start, - end=end, - countries=["GRC", "ALB"], + source="entsoe", start=start, end=end, countries=["GRC", "ALB"] ) assert first == second @@ -67,18 +61,8 @@ def test_batch_id_depends_on_source() -> None: start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") - entsoe = build_batch_id( - source="entsoe", - start=start, - end=end, - countries=["ALB"], - ) - opsd = build_batch_id( - source="opsd", - start=start, - end=end, - countries=["ALB"], - ) + entsoe = build_batch_id(source="entsoe", start=start, end=end, countries=["ALB"]) + opsd = build_batch_id(source="opsd", start=start, end=end, countries=["ALB"]) assert entsoe != opsd @@ -121,9 +105,7 @@ def test_batch_indexes_preserve_compiled_ids() -> None: assert set(by_source) == {"entsoe", "opsd"} assert len(by_source["entsoe"]) == 1 assert len(by_source["opsd"]) == 1 - assert list(by_group.values())[0] == [ - batch["batch_id"] for batch in batches - ] + assert list(by_group.values())[0] == [batch["batch_id"] for batch in batches] def test_serialize_batch_produces_json_safe_values() -> None: @@ -162,25 +144,16 @@ def test_load_execution_plan_and_get_batch(tmp_path) -> None: path.write_text(json.dumps(plan), encoding="utf-8") loaded = load_execution_plan(path) - selected = get_batch( - loaded, - batch_id=batch["batch_id"], - source=batch["source"], - ) + selected = get_batch(loaded, batch_id=batch["batch_id"], source=batch["source"]) assert selected == batch def test_get_batch_requires_exactly_one_match() -> None: """Test get batch requires a single match.""" - with pytest.raises( - ValueError, - match="Expected exactly one auxiliary batch", - ): - get_batch( - empty_execution_plan(), - batch_id="missing", - ) + with pytest.raises(ValueError, match="Expected exactly one auxiliary batch"): + get_batch(empty_execution_plan(), batch_id="missing") + def test_clipped_batches_share_logical_group() -> None: """Check provider slices retain one logical auxiliary period.""" @@ -209,10 +182,7 @@ def test_clipped_batches_share_logical_group() -> None: assert batches[1]["start"] == boundary assert batches[1]["end"] == group_end - expected_group_id = build_group_id( - start=group_start, - end=group_end, - ) + expected_group_id = build_group_id(start=group_start, end=group_end) assert batches[0]["group_id"] == expected_group_id assert batches[1]["group_id"] == expected_group_id @@ -220,19 +190,12 @@ def test_clipped_batches_share_logical_group() -> None: by_group = index_batch_ids_by_group(batches) assert list(by_group) == [expected_group_id] - assert by_group[expected_group_id] == [ - batch["batch_id"] for batch in batches - ] + assert by_group[expected_group_id] == [batch["batch_id"] for batch in batches] source_periods = pd.DataFrame( - { - "context": ["AAA"], - "start": [group_start], - "end": [group_end], - } + {"context": ["AAA"], "start": [group_start], "end": [group_end]} ) - assert resolve_required_group_ids( - batches, - source_periods=source_periods, - ) == [expected_group_id] + assert resolve_required_group_ids(batches, source_periods=source_periods) == [ + expected_group_id + ] diff --git a/tests/unit/test_plan_target_data.py b/tests/unit/test_plan_target_data.py index 9f7b98b..e5526b1 100644 --- a/tests/unit/test_plan_target_data.py +++ b/tests/unit/test_plan_target_data.py @@ -49,11 +49,7 @@ def test_as_utc_converts_aware_timestamp(): def test_effective_scope_uses_full_requested_period_without_source_bounds(): """An unbounded source inherits the complete requested period.""" - result = effective_source_temporal_scope( - {}, - start="2018-01-01", - end="2021-01-01", - ) + result = effective_source_temporal_scope({}, start="2018-01-01", end="2021-01-01") assert result == { "start": "2018-01-01T00:00:00+00:00", @@ -63,17 +59,10 @@ def test_effective_scope_uses_full_requested_period_without_source_bounds(): def test_effective_scope_clips_source_end(): """A source end bound clips the requested period.""" - metadata = { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - } - } + metadata = {"temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"}} result = effective_source_temporal_scope( - metadata, - start="2018-01-01", - end="2021-01-01", + metadata, start="2018-01-01", end="2021-01-01" ) assert result == { @@ -84,17 +73,10 @@ def test_effective_scope_clips_source_end(): def test_effective_scope_clips_source_start(): """A source start bound clips the requested period.""" - metadata = { - "temporal_scope": { - "start": "2019-01-01", - "end": "2026-01-01", - } - } + metadata = {"temporal_scope": {"start": "2019-01-01", "end": "2026-01-01"}} result = effective_source_temporal_scope( - metadata, - start="2018-01-01", - end="2021-01-01", + metadata, start="2018-01-01", end="2021-01-01" ) assert result == { @@ -105,27 +87,14 @@ def test_effective_scope_clips_source_start(): @pytest.mark.parametrize( ("source_start", "source_end"), - [ - ("2000-01-01", "2018-01-01"), - ("2021-01-01", "2025-01-01"), - ], + [("2000-01-01", "2018-01-01"), ("2021-01-01", "2025-01-01")], ) -def test_effective_scope_returns_none_without_overlap( - source_start, - source_end, -): +def test_effective_scope_returns_none_without_overlap(source_start, source_end): """Sources touching but not overlapping the request are inactive.""" - metadata = { - "temporal_scope": { - "start": source_start, - "end": source_end, - } - } + metadata = {"temporal_scope": {"start": source_start, "end": source_end}} result = effective_source_temporal_scope( - metadata, - start="2018-01-01", - end="2021-01-01", + metadata, start="2018-01-01", end="2021-01-01" ) assert result is None @@ -139,31 +108,16 @@ def test_effective_scope_returns_none_without_overlap( def test_supported_contexts_respects_source_contexts(): """Only explicitly supported target contexts are returned.""" result = supported_target_contexts( - ["AAA", "BBB", "CCC"], - metadata={ - "contexts": ["AAA", "CCC", "DDD"], - }, + ["AAA", "BBB", "CCC"], metadata={"contexts": ["AAA", "CCC", "DDD"]} ) assert result == ["AAA", "CCC"] -@pytest.mark.parametrize( - "metadata", - [ - {}, - {"contexts": None}, - {"contexts": []}, - ], -) -def test_missing_or_empty_contexts_means_no_geographic_restriction( - metadata, -): +@pytest.mark.parametrize("metadata", [{}, {"contexts": None}, {"contexts": []}]) +def test_missing_or_empty_contexts_means_no_geographic_restriction(metadata): """Missing or empty source contexts allow all target contexts.""" - result = supported_target_contexts( - ["AAA", "BBB"], - metadata=metadata, - ) + result = supported_target_contexts(["AAA", "BBB"], metadata=metadata) assert result == ["AAA", "BBB"] @@ -175,27 +129,17 @@ def test_missing_or_empty_contexts_means_no_geographic_restriction( def test_uncovered_intervals_returns_full_period_without_sources(): """No source intervals means the complete request is uncovered.""" - gaps = uncovered_temporal_intervals( - [], - start="2018-01-01", - end="2021-01-01", - ) + gaps = uncovered_temporal_intervals([], start="2018-01-01", end="2021-01-01") assert gaps == [ - ( - pd.Timestamp("2018-01-01T00:00:00Z"), - pd.Timestamp("2021-01-01T00:00:00Z"), - ) + (pd.Timestamp("2018-01-01T00:00:00Z"), pd.Timestamp("2021-01-01T00:00:00Z")) ] def test_uncovered_intervals_accepts_adjacent_half_open_intervals(): """Adjacent half-open source intervals provide continuous coverage.""" gaps = uncovered_temporal_intervals( - [ - ("2018-01-01", "2019-01-01"), - ("2019-01-01", "2021-01-01"), - ], + [("2018-01-01", "2019-01-01"), ("2019-01-01", "2021-01-01")], start="2018-01-01", end="2021-01-01", ) @@ -206,10 +150,7 @@ def test_uncovered_intervals_accepts_adjacent_half_open_intervals(): def test_uncovered_intervals_merges_overlapping_intervals(): """Overlapping source intervals jointly provide continuous coverage.""" gaps = uncovered_temporal_intervals( - [ - ("2018-01-01", "2020-01-01"), - ("2019-01-01", "2021-01-01"), - ], + [("2018-01-01", "2020-01-01"), ("2019-01-01", "2021-01-01")], start="2018-01-01", end="2021-01-01", ) @@ -220,19 +161,13 @@ def test_uncovered_intervals_merges_overlapping_intervals(): def test_uncovered_intervals_reports_internal_gap(): """An uncovered interval between two sources is reported exactly.""" gaps = uncovered_temporal_intervals( - [ - ("2018-01-01", "2019-01-01"), - ("2019-02-01", "2021-01-01"), - ], + [("2018-01-01", "2019-01-01"), ("2019-02-01", "2021-01-01")], start="2018-01-01", end="2021-01-01", ) assert gaps == [ - ( - pd.Timestamp("2019-01-01T00:00:00Z"), - pd.Timestamp("2019-02-01T00:00:00Z"), - ) + (pd.Timestamp("2019-01-01T00:00:00Z"), pd.Timestamp("2019-02-01T00:00:00Z")) ] @@ -245,17 +180,11 @@ def test_target_plan_clips_source_temporal_scopes(): """The plan records the effective interval for every active source.""" source_registry = { "old_source": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - }, + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"}, "contexts": ["AAA"], }, "new_source": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2026-01-01", - }, + "temporal_scope": {"start": "2019-01-01", "end": "2026-01-01"}, "contexts": ["AAA"], }, } @@ -264,10 +193,7 @@ def test_target_plan_clips_source_temporal_scopes(): target_contexts=["AAA"], source_names=["old_source", "new_source"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) assert plan["version"] == TARGET_DATA_PLAN_VERSION @@ -288,17 +214,11 @@ def test_target_plan_accepts_contiguous_source_coverage(): """Adjacent source intervals can jointly cover a target context.""" source_registry = { "source_a": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - }, + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"}, "contexts": ["AAA"], }, "source_b": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2026-01-01", - }, + "temporal_scope": {"start": "2019-01-01", "end": "2026-01-01"}, "contexts": ["AAA"], }, } @@ -307,38 +227,23 @@ def test_target_plan_accepts_contiguous_source_coverage(): target_contexts=["AAA"], source_names=["source_a", "source_b"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) - assert plan["active_sources"] == [ - "source_a", - "source_b", - ] + assert plan["active_sources"] == ["source_a", "source_b"] - assert plan["source_contexts"] == { - "source_a": ["AAA"], - "source_b": ["AAA"], - } + assert plan["source_contexts"] == {"source_a": ["AAA"], "source_b": ["AAA"]} def test_target_plan_rejects_temporal_coverage_gap(): """Reject a target context with a gap between source periods.""" source_registry = { "source_a": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - }, + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"}, "contexts": ["AAA"], }, "source_b": { - "temporal_scope": { - "start": "2019-02-01", - "end": "2026-01-01", - }, + "temporal_scope": {"start": "2019-02-01", "end": "2026-01-01"}, "contexts": ["AAA"], }, } @@ -348,10 +253,7 @@ def test_target_plan_rejects_temporal_coverage_gap(): target_contexts=["AAA"], source_names=["source_a", "source_b"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) message = str(exc_info.value) @@ -363,21 +265,14 @@ def test_target_plan_rejects_temporal_coverage_gap(): def test_target_plan_rejects_completely_unsupported_context(): """A target context with no usable source is rejected.""" - source_registry = { - "source_a": { - "contexts": ["AAA"], - }, - } + source_registry = {"source_a": {"contexts": ["AAA"]}} with pytest.raises(ValueError) as exc_info: build_target_data_plan( target_contexts=["AAA", "BBB"], source_names=["source_a"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) message = str(exc_info.value) @@ -391,17 +286,11 @@ def test_target_plan_checks_coverage_per_context(): """Temporal coverage must be complete independently for each context.""" source_registry = { "old_source": { - "temporal_scope": { - "start": "2010-01-01", - "end": "2019-01-01", - }, + "temporal_scope": {"start": "2010-01-01", "end": "2019-01-01"}, "contexts": ["AAA", "BBB"], }, "new_source": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2025-01-01", - }, + "temporal_scope": {"start": "2019-01-01", "end": "2025-01-01"}, "contexts": ["AAA"], }, } @@ -411,10 +300,7 @@ def test_target_plan_checks_coverage_per_context(): target_contexts=["AAA", "BBB"], source_names=["old_source", "new_source"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) message = str(exc_info.value) @@ -430,17 +316,11 @@ def test_target_plan_excludes_temporally_inactive_source(): """A source outside the requested interval remains in the plan but inactive.""" source_registry = { "inactive_source": { - "temporal_scope": { - "start": "2000-01-01", - "end": "2018-01-01", - }, + "temporal_scope": {"start": "2000-01-01", "end": "2018-01-01"}, "contexts": ["AAA"], }, "active_source": { - "temporal_scope": { - "start": "2018-01-01", - "end": "2025-01-01", - }, + "temporal_scope": {"start": "2018-01-01", "end": "2025-01-01"}, "contexts": ["AAA"], }, } @@ -449,115 +329,69 @@ def test_target_plan_excludes_temporally_inactive_source(): target_contexts=["AAA"], source_names=["inactive_source", "active_source"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) assert plan["active_sources"] == ["active_source"] assert plan["source_contexts"]["inactive_source"] == [] - assert ( - plan["source_temporal_scopes"]["inactive_source"] - is None - ) + assert plan["source_temporal_scopes"]["inactive_source"] is None def test_target_plan_preserves_configured_source_priority(): """Active sources retain the order supplied in configuration.""" source_registry = { - "source_a": { - "contexts": ["AAA"], - }, - "source_b": { - "contexts": ["AAA"], - }, - "source_c": { - "contexts": ["AAA"], - }, + "source_a": {"contexts": ["AAA"]}, + "source_b": {"contexts": ["AAA"]}, + "source_c": {"contexts": ["AAA"]}, } plan = build_target_data_plan( target_contexts=["AAA"], source_names=["source_c", "source_a", "source_b"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) - assert plan["active_sources"] == [ - "source_c", - "source_a", - "source_b", - ] + assert plan["active_sources"] == ["source_c", "source_a", "source_b"] def test_target_plan_sorts_and_deduplicates_target_contexts(): """Target contexts are stored uniquely and deterministically.""" - source_registry = { - "source_a": {}, - } + source_registry = {"source_a": {}} plan = build_target_data_plan( target_contexts=["CCC", "AAA", "BBB", "AAA"], source_names=["source_a"], source_registry=source_registry, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) - assert plan["target_contexts"] == [ - "AAA", - "BBB", - "CCC", - ] + assert plan["target_contexts"] == ["AAA", "BBB", "CCC"] - assert plan["source_contexts"]["source_a"] == [ - "AAA", - "BBB", - "CCC", - ] + assert plan["source_contexts"]["source_a"] == ["AAA", "BBB", "CCC"] def test_target_plan_rejects_unknown_source(): """Configured sources must exist in the source registry.""" - with pytest.raises( - ValueError, - match="Unsupported electricity-demand source", - ): + with pytest.raises(ValueError, match="Unsupported electricity-demand source"): build_target_data_plan( target_contexts=["AAA"], source_names=["not_a_source"], source_registry={}, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) def test_target_plan_rejects_empty_target_contexts(): """Planning requires at least one land-country context.""" - with pytest.raises( - ValueError, - match="no land-country contexts", - ): + with pytest.raises(ValueError, match="no land-country contexts"): build_target_data_plan( target_contexts=[], source_names=["source_a"], - source_registry={ - "source_a": {}, - }, - temporal_scope={ - "start": "2018-01-01", - "end": "2021-01-01", - }, + source_registry={"source_a": {}}, + temporal_scope={"start": "2018-01-01", "end": "2021-01-01"}, ) @@ -572,9 +406,7 @@ def test_write_target_data_plan_creates_json_file(tmp_path): "version": 2, "target_contexts": ["AAA"], "active_sources": ["source_a"], - "source_contexts": { - "source_a": ["AAA"], - }, + "source_contexts": {"source_a": ["AAA"]}, "source_temporal_scopes": { "source_a": { "start": "2018-01-01T00:00:00+00:00", @@ -585,14 +417,11 @@ def test_write_target_data_plan_creates_json_file(tmp_path): output = tmp_path / "nested" / "target_data_plan.json" - write_target_data_plan( - plan=plan, - output_path=output, - ) + write_target_data_plan(plan=plan, output_path=output) assert output.exists() with output.open(encoding="utf-8") as file: written = json.load(file) - assert written == plan \ No newline at end of file + assert written == plan diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index 8f9ee3e..ecc97a2 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -198,38 +198,23 @@ def test_match_total_scaling_adds_auxiliary_periods() -> None: assert set(result) == {"scaled_source"} assert result["scaled_source"]["context"].tolist() == ["GBR", "ALB"] + def test_auxiliary_temporal_filter_preserves_fully_available_request() -> None: """Check a provider covering the whole requirement is not clipped.""" start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") requests = pd.DataFrame( - { - "source": ["source_a"], - "context": ["AAA"], - "start": [start], - "end": [end], - } + {"source": ["source_a"], "context": ["AAA"], "start": [start], "end": [end]} ) - requirements = pd.DataFrame( - { - "context": ["AAA"], - "start": [start], - "end": [end], - } - ) + requirements = pd.DataFrame({"context": ["AAA"], "start": [start], "end": [end]}) result = filter_source_requests_by_temporal_scope( requests, requirements=requirements, source_registry={ - "source_a": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2021-01-01", - } - } + "source_a": {"temporal_scope": {"start": "2019-01-01", "end": "2021-01-01"}} }, ) @@ -256,11 +241,7 @@ def test_auxiliary_temporal_filter_clips_adjacent_providers() -> None: ) requirements = pd.DataFrame( - { - "context": ["AAA"], - "start": [group_start], - "end": [group_end], - } + {"context": ["AAA"], "start": [group_start], "end": [group_end]} ) result = filter_source_requests_by_temporal_scope( @@ -268,16 +249,10 @@ def test_auxiliary_temporal_filter_clips_adjacent_providers() -> None: requirements=requirements, source_registry={ "old_source": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - } + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"} }, "new_source": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2026-01-01", - } + "temporal_scope": {"start": "2019-01-01", "end": "2026-01-01"} }, }, ) @@ -311,29 +286,17 @@ def test_auxiliary_temporal_filter_removes_non_overlapping_provider() -> None: } ) - requirements = pd.DataFrame( - { - "context": ["AAA"], - "start": [start], - "end": [end], - } - ) + requirements = pd.DataFrame({"context": ["AAA"], "start": [start], "end": [end]}) result = filter_source_requests_by_temporal_scope( requests, requirements=requirements, source_registry={ "old_source": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - } + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"} }, "current_source": { - "temporal_scope": { - "start": "2019-01-01", - "end": "2025-01-01", - } + "temporal_scope": {"start": "2019-01-01", "end": "2025-01-01"} }, }, ) @@ -357,13 +320,7 @@ def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: } ) - requirements = pd.DataFrame( - { - "context": ["AAA"], - "start": [start], - "end": [end], - } - ) + requirements = pd.DataFrame({"context": ["AAA"], "start": [start], "end": [end]}) with pytest.raises(ValueError) as exc_info: filter_source_requests_by_temporal_scope( @@ -371,16 +328,10 @@ def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: requirements=requirements, source_registry={ "old_source": { - "temporal_scope": { - "start": "2005-01-01", - "end": "2019-01-01", - } + "temporal_scope": {"start": "2005-01-01", "end": "2019-01-01"} }, "new_source": { - "temporal_scope": { - "start": "2019-02-01", - "end": "2026-01-01", - } + "temporal_scope": {"start": "2019-02-01", "end": "2026-01-01"} }, }, ) diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index 963e9a8..b3b1857 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -4,4 +4,4 @@ resources: # Links for automatically downloaded files load_opsd: "https://data.open-power-system-data.org/time_series/2019-06-05/time_series_60min_stacked.csv" population: "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/GHSL/GHS_POP_GLOBE_R2023A/GHS_POP_E2020_GLOBE_R2023A_54009_1000/V1-0/GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.zip" - population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" \ No newline at end of file + population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 024601e..b13b5b8 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -24,10 +24,7 @@ def auxiliary_group_source_files(wildcards): batch_ids = plan["groups"][wildcards.group_id] - batches_by_id = { - batch["batch_id"]: batch - for batch in plan["batches"] - } + batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} return [ ( @@ -129,9 +126,13 @@ rule finalise_clean_demand: demand=final_clean_demand_input, cleaning_method=final_cleaning_method_input, output: - demand=("/automatic/{shape}/""load_cleaned.parquet"), - cleaning_method=("/automatic/{shape}/" "load_final_cleaning_method.parquet"), - cleaning_method_rank=("/automatic/{shape}/" "load_final_cleaning_method_rank.parquet"), + demand=("/automatic/{shape}/" "load_cleaned.parquet"), + cleaning_method=( + "/automatic/{shape}/" "load_final_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" + ), conda: "../envs/module.yaml" params: @@ -149,9 +150,7 @@ rule clean_auxiliary_group: sources=auxiliary_group_source_files, output: demand=( - "/automatic/{shape}/" - "auxiliary/cleaned/" - "{group_id}.parquet" + "/automatic/{shape}/" "auxiliary/cleaned/" "{group_id}.parquet" ), data_source=( "/automatic/{shape}/" @@ -163,6 +162,8 @@ rule clean_auxiliary_group: "auxiliary/cleaned/" "{group_id}_cleaning_method.parquet" ), + log: + "/{shape}/auxiliary/clean_{group_id}.log", conda: "../envs/module.yaml" params: @@ -173,8 +174,6 @@ rule clean_auxiliary_group: "enabled" ] ), - log: - "/{shape}/auxiliary/clean_{group_id}.log", message: "Combine and clean auxiliary electricity-demand sources." script: @@ -211,13 +210,9 @@ rule apply_advanced_overrides: constructed_profiles=advanced_constructed_profiles, external_profiles=advanced_external_profile_files, output: - demand=( - "/automatic/{shape}/" - "load_advanced_cleaned.parquet" - ), + demand=("/automatic/{shape}/" "load_advanced_cleaned.parquet"), cleaning_method=( - "/automatic/{shape}/" - "load_advanced_cleaning_method.parquet" + "/automatic/{shape}/" "load_advanced_cleaning_method.parquet" ), conda: "../envs/module.yaml" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 86433d4..26f4a41 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,6 +1,5 @@ """Rules used for generic automatic resources and validation.""" - import json @@ -40,14 +39,9 @@ rule validate_gap_filling_config_semantics: checkpoint plan_target_data: input: shapes="", - temporal_validation=( - "/automatic/temporal_config_validation.json" - ), + temporal_validation=("/automatic/temporal_config_validation.json"), output: - plan=( - "/automatic/{shape}/" - "target_data_plan.json" - ), + plan=("/automatic/{shape}/" "target_data_plan.json"), conda: "../envs/module.yaml" params: @@ -91,9 +85,7 @@ def target_source_temporal_scope(wildcards, source_name): temporal_scope = plan["source_temporal_scopes"].get(source_name) if temporal_scope is None: - raise ValueError( - f"Source {source_name!r} has no active target temporal scope." - ) + raise ValueError(f"Source {source_name!r} has no active target temporal scope.") return temporal_scope diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 6ac7c9b..9bef3a2 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -3,11 +3,7 @@ def configured_load_inputs(wildcards): plan = read_target_data_plan(wildcards) return [ - ( - "/automatic/" - f"{wildcards.shape}/" - f"load_{source_name}.parquet" - ) + ("/automatic/" f"{wildcards.shape}/" f"load_{source_name}.parquet") for source_name in plan["active_sources"] ] @@ -25,26 +21,15 @@ rule clean_demand: target_plan=target_data_plan, validation="/automatic/gap_filling_config_validation.json", output: - demand=( - "/automatic/{shape}/" - "load_basic_cleaned.parquet" - ), - data_source=( - "/automatic/{shape}/" - "load_data_source.parquet" - ), + demand=("/automatic/{shape}/" "load_basic_cleaned.parquet"), + data_source=("/automatic/{shape}/" "load_data_source.parquet"), cleaning_method=( - "/automatic/{shape}/" - "load_cleaning_method.parquet" + "/automatic/{shape}/" "load_cleaning_method.parquet" ), cleaning_method_rank=( - "/automatic/{shape}/" - "load_cleaning_method_rank.parquet" - ), - gap_report=( - "/automatic/{shape}/" - "load_gap_report.parquet" + "/automatic/{shape}/" "load_cleaning_method_rank.parquet" ), + gap_report=("/automatic/{shape}/" "load_gap_report.parquet"), log: "/{shape}/clean_demand.log", conda: @@ -62,21 +47,15 @@ rule clean_demand: rule plot_cleaning_timeline: input: target_plan=target_data_plan, - demand=( - "/automatic/{shape}/" - "load_cleaned.parquet" - ), + demand=("/automatic/{shape}/" "load_cleaned.parquet"), basic_cleaning_method=( - "/automatic/{shape}/" - "load_cleaning_method.parquet" + "/automatic/{shape}/" "load_cleaning_method.parquet" ), cleaning_method=( - "/automatic/{shape}/" - "load_final_cleaning_method.parquet" + "/automatic/{shape}/" "load_final_cleaning_method.parquet" ), cleaning_method_rank=( - "/automatic/{shape}/" - "load_final_cleaning_method_rank.parquet" + "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" ), output: plot="/{shape}/load_cleaning_timeline.pdf", diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 207c91b..55ce2ef 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -110,11 +110,7 @@ rule prepare_auxiliary_load_entsoe: plan=auxiliary_acquisition_plan, annual_files=auxiliary_entsoe_raw_files, output: - load=( - "/automatic/{shape}/" - "auxiliary/entsoe/" - "{batch_id}.parquet" - ), + load=("/automatic/{shape}/" "auxiliary/entsoe/" "{batch_id}.parquet"), log: "/{shape}/auxiliary/entsoe/prepare_{batch_id}.log", conda: diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index 7cef3df..dd9ba1d 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -64,22 +64,19 @@ rule download_load_entsoe_power_statistics_year: def target_entsoe_power_statistics_countries(wildcards): """Return Power Statistics target countries for one shape.""" - return target_source_contexts(wildcards,"entsoe_power_statistics",) + return target_source_contexts( + wildcards, + "entsoe_power_statistics", + ) rule prepare_load_entsoe_power_statistics: input: - validation=( - "/automatic/" - "temporal_config_validation.json" - ), + validation=("/automatic/" "temporal_config_validation.json"), target_plan=target_data_plan, annual_files=entsoe_power_statistics_raw_files, output: - load=( - "/automatic/{shape}/" - "load_entsoe_power_statistics.parquet" - ), + load=("/automatic/{shape}/" "load_entsoe_power_statistics.parquet"), log: "/{shape}/prepare_load_entsoe_power_statistics.log", conda: @@ -112,9 +109,7 @@ rule prepare_auxiliary_load_entsoe_power_statistics: "{batch_id}.parquet" ), log: - "/{shape}/auxiliary/" - "entsoe_power_statistics/" - "prepare_{batch_id}.log", + "/{shape}/auxiliary/" "entsoe_power_statistics/" "prepare_{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index b612b28..5d0f7b2 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -59,7 +59,10 @@ rule download_load_neso_year: def target_neso_countries(wildcards): """Return NESO target countries for one shape.""" - return target_source_contexts(wildcards,"neso",) + return target_source_contexts( + wildcards, + "neso", + ) rule prepare_load_neso: @@ -89,15 +92,13 @@ rule prepare_load_neso: script: "../scripts/prepare_load_neso.py" + rule prepare_auxiliary_load_neso: input: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=( - "/automatic/{shape}/" - "auxiliary/neso/{batch_id}.parquet" - ), + load=("/automatic/{shape}/" "auxiliary/neso/{batch_id}.parquet"), log: "/{shape}/auxiliary/neso/{batch_id}.log", conda: diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index c74b68e..52b0e58 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -17,10 +17,12 @@ rule download_load_opsd: "../scripts/download_load_opsd.py" - def target_opsd_countries(wildcards): """Return OPSD target countries for one shape.""" - return target_source_contexts(wildcards,"opsd",) + return target_source_contexts( + wildcards, + "opsd", + ) rule prepare_load_opsd: @@ -56,11 +58,7 @@ rule prepare_auxiliary_load_opsd: load=rules.download_load_opsd.output.load, plan=auxiliary_acquisition_plan, output: - load=( - "/automatic/{shape}/" - "auxiliary/opsd/" - "{batch_id}.parquet" - ), + load=("/automatic/{shape}/" "auxiliary/opsd/" "{batch_id}.parquet"), log: "/{shape}/auxiliary/opsd/{batch_id}.log", conda: diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py index 63ffc3e..f9de7a5 100644 --- a/workflow/scripts/_advanced_execution.py +++ b/workflow/scripts/_advanced_execution.py @@ -39,8 +39,7 @@ def build_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: batches: list[dict[str, object]] = [] grouped = requests.groupby( - ["source", "start", "end", "group_start", "group_end"], - sort=False, + ["source", "start", "end", "group_start", "group_end"], sort=False ) for (source, start, end, group_start, group_end), group in grouped: @@ -137,15 +136,9 @@ def build_batch_id( if group_id is not None: batch_key_data["group_id"] = str(group_id) - batch_key = json.dumps( - batch_key_data, - sort_keys=True, - separators=(",", ":"), - ) + batch_key = json.dumps(batch_key_data, sort_keys=True, separators=(",", ":")) - return hashlib.sha1( - batch_key.encode("utf-8") - ).hexdigest()[:16] + return hashlib.sha1(batch_key.encode("utf-8")).hexdigest()[:16] def index_batch_ids_by_source( diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 2425ecc..b685305 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -111,9 +111,7 @@ def main( legend_rows = _build_legend_rows( metadata=legend_metadata, rank_colours=rank_colours, - available_width_px=( - PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX - ), + available_width_px=(PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX), ) logger.info( @@ -147,8 +145,7 @@ def main( def _new_figure(*, height_px: int) -> plt.Figure: """Create a figure from pixel dimensions.""" return plt.figure( - figsize=(PAGE_WIDTH_PX / FIGURE_DPI, height_px / FIGURE_DPI), - dpi=FIGURE_DPI, + figsize=(PAGE_WIDTH_PX / FIGURE_DPI, height_px / FIGURE_DPI), dpi=FIGURE_DPI ) @@ -167,16 +164,10 @@ def _build_legend_rows( renderer = measurement_figure.canvas.get_renderer() line_height_px = _measure_text_px( - figure=measurement_figure, - renderer=renderer, - text="Ag", + figure=measurement_figure, renderer=renderer, text="Ag" )[1] - max_text_width_px = ( - available_width_px - - LEGEND_SWATCH_WIDTH_PX - - LEGEND_TEXT_GAP_PX - ) + max_text_width_px = available_width_px - LEGEND_SWATCH_WIDTH_PX - LEGEND_TEXT_GAP_PX items: list[dict[str, Any]] = [] @@ -192,18 +183,13 @@ def _build_legend_rows( ) line_widths = [ - _measure_text_px( - figure=measurement_figure, - renderer=renderer, - text=line, - )[0] + _measure_text_px(figure=measurement_figure, renderer=renderer, text=line)[0] for line in lines ] text_width_px = max(line_widths, default=0.0) text_height_px = ( - len(lines) * line_height_px - + max(0, len(lines) - 1) * LEGEND_LINE_GAP_PX + len(lines) * line_height_px + max(0, len(lines) - 1) * LEGEND_LINE_GAP_PX ) items.append( @@ -212,9 +198,7 @@ def _build_legend_rows( "colour": rank_colours[rank], "lines": lines, "width_px": ( - LEGEND_SWATCH_WIDTH_PX - + LEGEND_TEXT_GAP_PX - + text_width_px + LEGEND_SWATCH_WIDTH_PX + LEGEND_TEXT_GAP_PX + text_width_px ), "height_px": max(LEGEND_SWATCH_HEIGHT_PX, text_height_px), "line_height_px": line_height_px, @@ -233,12 +217,7 @@ def _build_legend_rows( proposed_width_px = current_width_px + separator_px + item["width_px"] if current_items and proposed_width_px > available_width_px: - packed_rows.append( - { - "items": current_items, - "height_px": current_height_px, - } - ) + packed_rows.append({"items": current_items, "height_px": current_height_px}) current_items = [] current_width_px = 0.0 current_height_px = 0.0 @@ -249,31 +228,16 @@ def _build_legend_rows( current_height_px = max(current_height_px, item["height_px"]) if current_items: - packed_rows.append( - { - "items": current_items, - "height_px": current_height_px, - } - ) + packed_rows.append({"items": current_items, "height_px": current_height_px}) return packed_rows def _measure_text_px( - *, - figure: plt.Figure, - renderer: Any, - text: str, + *, figure: plt.Figure, renderer: Any, text: str ) -> tuple[float, float]: """Measure one legend text line in rendered pixels.""" - artist = figure.text( - 0, - 0, - text, - fontsize=LEGEND_FONT_SIZE, - ha="left", - va="bottom", - ) + artist = figure.text(0, 0, text, fontsize=LEGEND_FONT_SIZE, ha="left", va="bottom") bounds = artist.get_window_extent(renderer=renderer) artist.remove() @@ -281,11 +245,7 @@ def _measure_text_px( def _wrap_legend_label( - *, - label: str, - max_width_px: int, - figure: plt.Figure, - renderer: Any, + *, label: str, max_width_px: int, figure: plt.Figure, renderer: Any ) -> list[str]: """Wrap a legend label to the available rendered width.""" words = label.split() @@ -299,9 +259,7 @@ def _wrap_legend_label( for word in words[1:]: candidate = f"{current} {word}" candidate_width = _measure_text_px( - figure=figure, - renderer=renderer, - text=candidate, + figure=figure, renderer=renderer, text=candidate )[0] if candidate_width <= max_width_px: @@ -320,10 +278,7 @@ def _wrap_legend_label( lines.extend( _split_oversized_legend_token( - token=current, - max_width_px=max_width_px, - figure=figure, - renderer=renderer, + token=current, max_width_px=max_width_px, figure=figure, renderer=renderer ) ) @@ -331,19 +286,11 @@ def _wrap_legend_label( def _split_oversized_legend_token( - *, - token: str, - max_width_px: int, - figure: plt.Figure, - renderer: Any, + *, token: str, max_width_px: int, figure: plt.Figure, renderer: Any ) -> list[str]: """Split an unusually long unbroken token if it cannot fit on one line.""" if ( - _measure_text_px( - figure=figure, - renderer=renderer, - text=token, - )[0] + _measure_text_px(figure=figure, renderer=renderer, text=token)[0] <= max_width_px ): return [token] @@ -353,11 +300,7 @@ def _split_oversized_legend_token( for character in token: candidate = f"{current}{character}" - width_px = _measure_text_px( - figure=figure, - renderer=renderer, - text=candidate, - )[0] + width_px = _measure_text_px(figure=figure, renderer=renderer, text=candidate)[0] if current and width_px > max_width_px: pieces.append(current) @@ -379,13 +322,13 @@ def _legend_rows_height_px(rows: list[dict[str, Any]]) -> int: row_height = sum(float(row["height_px"]) for row in rows) gaps = LEGEND_ROW_GAP_PX * max(0, len(rows) - 1) - return int(round(LEGEND_PADDING_TOP_PX + row_height + gaps + LEGEND_PADDING_BOTTOM_PX)) + return int( + round(LEGEND_PADDING_TOP_PX + row_height + gaps + LEGEND_PADDING_BOTTOM_PX) + ) def _take_legend_rows( - rows: list[dict[str, Any]], - *, - available_height_px: int, + rows: list[dict[str, Any]], *, available_height_px: int ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: """Take as many complete legend rows as fit in the available height.""" if not rows or available_height_px <= 0: @@ -401,7 +344,7 @@ def _take_legend_rows( selected.append(row) - return selected, rows[len(selected):] + return selected, rows[len(selected) :] def _write_timeline_pdf( @@ -450,10 +393,7 @@ def _write_timeline_pdf( if is_final_country_page and remaining_legend_rows: available_legend_height_px = max( - 0, - REFERENCE_PAGE_HEIGHT_PX - - base_height_px - - PANEL_LEGEND_GAP_PX, + 0, REFERENCE_PAGE_HEIGHT_PX - base_height_px - PANEL_LEGEND_GAP_PX ) page_legend_rows, remaining_legend_rows = _take_legend_rows( @@ -495,9 +435,7 @@ def _write_timeline_pdf( if page_legend_rows: _draw_legend_rows( - figure=figure, - rows=page_legend_rows, - top_px=legend_top_px, + figure=figure, rows=page_legend_rows, top_px=legend_top_px ) pdf.savefig(figure) @@ -515,8 +453,7 @@ def _write_timeline_pdf( ) page_rows, new_remaining_rows = _take_legend_rows( - remaining_legend_rows, - available_height_px=available_height_px, + remaining_legend_rows, available_height_px=available_height_px ) if not page_rows: @@ -552,9 +489,7 @@ def _write_timeline_pdf( ) _draw_legend_rows( - figure=figure, - rows=page_rows, - top_px=LEGEND_PAGE_TOP_MARGIN_PX, + figure=figure, rows=page_rows, top_px=LEGEND_PAGE_TOP_MARGIN_PX ) pdf.savefig(figure) @@ -564,10 +499,7 @@ def _write_timeline_pdf( def _draw_legend_rows( - *, - figure: plt.Figure, - rows: list[dict[str, Any]], - top_px: int, + *, figure: plt.Figure, rows: list[dict[str, Any]], top_px: int ) -> None: """Draw packed legend rows directly in figure coordinates.""" figure_height_px = int(round(figure.get_figheight() * FIGURE_DPI)) @@ -588,18 +520,14 @@ def _draw_legend_rows( item_top_px = y_px + (row_height_px - item_height_px) / 2 swatch_top_px = item_top_px + max( - 0.0, - (item_height_px - LEGEND_SWATCH_HEIGHT_PX) / 2, + 0.0, (item_height_px - LEGEND_SWATCH_HEIGHT_PX) / 2 ) swatch_bottom_fraction = 1.0 - ( (swatch_top_px + LEGEND_SWATCH_HEIGHT_PX) / figure_height_px ) swatch = Rectangle( - ( - x_px / PAGE_WIDTH_PX, - swatch_bottom_fraction, - ), + (x_px / PAGE_WIDTH_PX, swatch_bottom_fraction), LEGEND_SWATCH_WIDTH_PX / PAGE_WIDTH_PX, LEGEND_SWATCH_HEIGHT_PX / figure_height_px, transform=figure.transFigure, @@ -630,6 +558,7 @@ def _draw_legend_rows( y_px += row_height_px + LEGEND_ROW_GAP_PX + def _validate_alignment( *, demand: pd.DataFrame, @@ -658,8 +587,7 @@ def _validate_cleaning_method_rank_mapping( ) -> None: """Require provenance strings and numeric codes to describe the same methods.""" method_to_rank = ( - metadata - .set_index("cleaning_method")["cleaning_method_rank"] + metadata.set_index("cleaning_method")["cleaning_method_rank"] .astype(int) .to_dict() ) @@ -678,25 +606,16 @@ def _validate_cleaning_method_rank_mapping( f"metadata: {unknown_methods!r}." ) - expected = cleaning_method.apply( - lambda column: column.map(method_to_rank) - ) + expected = cleaning_method.apply(lambda column: column.map(method_to_rank)) - actual = cleaning_method_rank.apply( - pd.to_numeric, - errors="coerce", - ) + actual = cleaning_method_rank.apply(pd.to_numeric, errors="coerce") - mismatch = expected.ne(actual) & ~( - expected.isna() & actual.isna() - ) + mismatch = expected.ne(actual) & ~(expected.isna() & actual.isna()) if not mismatch.any().any(): return - row_index, column_index = np.argwhere( - mismatch.to_numpy() - )[0] + row_index, column_index = np.argwhere(mismatch.to_numpy())[0] timestamp = cleaning_method.index[row_index] country = cleaning_method.columns[column_index] @@ -833,8 +752,7 @@ def _plot_country_page( ) background_norm = BoundaryNorm( - np.arange(-0.5, background_cmap.N + 0.5, 1), - background_cmap.N, + np.arange(-0.5, background_cmap.N + 0.5, 1), background_cmap.N ) axis.imshow( @@ -894,13 +812,7 @@ def _plot_country_page( spine.set_visible(False) for boundary in row_boundaries: - summary_axis.axhline( - boundary, - linewidth=0.4, - alpha=0.35, - color="0.5", - zorder=0, - ) + summary_axis.axhline(boundary, linewidth=0.4, alpha=0.35, color="0.5", zorder=0) summary_axis.axvline(0.0, linewidth=0.6, color="0.7") @@ -936,6 +848,7 @@ def _plot_country_page( return figure, axis, summary_axis, legend_top_px + def _add_normalised_demand_traces( *, axis: plt.Axes, @@ -1214,8 +1127,7 @@ def _build_cleaning_method_metadata( "cleaning_method": f"observed_{source_name}", "cleaning_method_rank": rank, "label": ( - f"[Src.] " - f"{_format_source_name(source_name, source_registry)}" + f"[Src.] {_format_source_name(source_name, source_registry)}" ), "category": "observed", } @@ -1278,19 +1190,13 @@ def _filter_legend_metadata( for frame in (basic_cleaning_method, cleaning_method): values = pd.unique(frame.to_numpy().ravel()) - used_methods.update( - str(value) - for value in values - if pd.notna(value) - ) + used_methods.update(str(value) for value in values if pd.notna(value)) # Keep missing in the legend as the fixed diagnostic reference colour, # even when this particular result is complete. used_methods.add("missing") - return metadata.loc[ - metadata["cleaning_method"].isin(used_methods) - ].copy() + return metadata.loc[metadata["cleaning_method"].isin(used_methods)].copy() def _format_source_name( diff --git a/workflow/scripts/_source_capabilities.py b/workflow/scripts/_source_capabilities.py index c620bc8..1334a4a 100644 --- a/workflow/scripts/_source_capabilities.py +++ b/workflow/scripts/_source_capabilities.py @@ -17,10 +17,7 @@ def as_utc(value: object) -> pd.Timestamp: def intersect_source_temporal_scope( - metadata: Mapping[str, Any], - *, - start: object, - end: object, + metadata: Mapping[str, Any], *, start: object, end: object ) -> tuple[pd.Timestamp, pd.Timestamp] | None: """Return the intersection of a request and one source's temporal scope.""" temporal_scope = metadata.get("temporal_scope") or {} @@ -44,10 +41,7 @@ def intersect_source_temporal_scope( def uncovered_temporal_intervals( - intervals: Sequence[tuple[object, object]], - *, - start: object, - end: object, + intervals: Sequence[tuple[object, object]], *, start: object, end: object ) -> list[tuple[pd.Timestamp, pd.Timestamp]]: """Return gaps in a required period not covered by any supplied interval.""" required_start = as_utc(start) diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 96cb941..f9baabe 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -214,9 +214,7 @@ def filter_source_requests_by_temporal_scope( request_end = pd.to_datetime(request.end, utc=True) effective_scope = intersect_source_temporal_scope( - metadata, - start=request_start, - end=request_end, + metadata, start=request_start, end=request_end ) if effective_scope is None: @@ -233,9 +231,7 @@ def filter_source_requests_by_temporal_scope( filtered = pd.DataFrame(clipped_rows, columns=output_columns) - required_periods = requirements[ - ["context", "start", "end"] - ].drop_duplicates() + required_periods = requirements[["context", "start", "end"]].drop_duplicates() uncovered: list[dict[str, object]] = [] diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index c339344..638e793 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -27,13 +27,11 @@ def main(snakemake: Any) -> None: raise ValueError(f"Auxiliary group {group_id!r} contains no source batches.") group_starts = { - pd.Timestamp(batch.get("group_start", batch["start"])) - for batch in batches + pd.Timestamp(batch.get("group_start", batch["start"])) for batch in batches } group_ends = { - pd.Timestamp(batch.get("group_end", batch["end"])) - for batch in batches + pd.Timestamp(batch.get("group_end", batch["end"])) for batch in batches } if len(group_starts) != 1 or len(group_ends) != 1: @@ -78,10 +76,7 @@ def main(snakemake: Any) -> None: ) sources = { - source_name: data.reindex( - index=grid.target_index, - columns=contexts, - ) + source_name: data.reindex(index=grid.target_index, columns=contexts) for source_name, data in sources.items() } diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 4cc1460..7933a1b 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -36,16 +36,13 @@ def main(snakemake: Any) -> None: for source_name, path in zip(source_names, input_paths, strict=True) } - target_contexts = _read_target_contexts( - snakemake.input.target_plan - ) + target_contexts = _read_target_contexts(snakemake.input.target_plan) config = build_tclean_config(snakemake.params.temporal_scope) sources = { source_name: data.reindex( - index=config.grid.target_index, - columns=target_contexts, + index=config.grid.target_index, columns=target_contexts ) for source_name, data in sources.items() } diff --git a/workflow/scripts/demand_electricity_polygon.py b/workflow/scripts/demand_electricity_polygon.py index 79c25ca..eeeddcf 100644 --- a/workflow/scripts/demand_electricity_polygon.py +++ b/workflow/scripts/demand_electricity_polygon.py @@ -1,6 +1,5 @@ """Prepare electricity demand timeseries, aggregated to shapes.""" -import sys from contextlib import redirect_stderr from typing import TYPE_CHECKING, Any from warnings import warn diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index e5df489..666c157 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -85,9 +85,7 @@ def build_advanced_execution_plan( ) requests = filter_source_requests_by_temporal_scope( - requests, - requirements=requirements, - source_registry=source_registry, + requests, requirements=requirements, source_registry=source_registry ) raw_batches = build_source_batches(requests) diff --git a/workflow/scripts/plan_target_data.py b/workflow/scripts/plan_target_data.py index a78bcfa..d98966d 100644 --- a/workflow/scripts/plan_target_data.py +++ b/workflow/scripts/plan_target_data.py @@ -25,33 +25,21 @@ def effective_source_temporal_scope( - metadata: Mapping[str, Any], - *, - start: object, - end: object, + metadata: Mapping[str, Any], *, start: object, end: object ) -> dict[str, str] | None: """Return the intersection of requested and source temporal scopes.""" - effective_scope = intersect_source_temporal_scope( - metadata, - start=start, - end=end, - ) + effective_scope = intersect_source_temporal_scope(metadata, start=start, end=end) if effective_scope is None: return None effective_start, effective_end = effective_scope - return { - "start": effective_start.isoformat(), - "end": effective_end.isoformat(), - } + return {"start": effective_start.isoformat(), "end": effective_end.isoformat()} def supported_target_contexts( - target_contexts: Sequence[str], - *, - metadata: Mapping[str, Any], + target_contexts: Sequence[str], *, metadata: Mapping[str, Any] ) -> list[str]: """Return target contexts supported by one source.""" contexts = metadata.get("contexts") @@ -63,11 +51,7 @@ def supported_target_contexts( supported = set(contexts) - return [ - context - for context in target_contexts - if context in supported - ] + return [context for context in target_contexts if context in supported] def build_target_data_plan( @@ -81,9 +65,7 @@ def build_target_data_plan( target_contexts = sorted(set(target_contexts)) if not target_contexts: - raise ValueError( - "The supplied shapes contain no land-country contexts." - ) + raise ValueError("The supplied shapes contain no land-country contexts.") source_contexts: dict[str, list[str]] = {} source_temporal_scopes: dict[str, dict[str, str] | None] = {} @@ -91,16 +73,12 @@ def build_target_data_plan( for source_name in source_names: if source_name not in source_registry: - raise ValueError( - f"Unsupported electricity-demand source: {source_name!r}." - ) + raise ValueError(f"Unsupported electricity-demand source: {source_name!r}.") metadata = source_registry[source_name] effective_temporal_scope = effective_source_temporal_scope( - metadata, - start=temporal_scope["start"], - end=temporal_scope["end"], + metadata, start=temporal_scope["start"], end=temporal_scope["end"] ) source_temporal_scopes[source_name] = effective_temporal_scope @@ -109,20 +87,14 @@ def build_target_data_plan( source_contexts[source_name] = [] continue - contexts = supported_target_contexts( - target_contexts, - metadata=metadata, - ) + contexts = supported_target_contexts(target_contexts, metadata=metadata) source_contexts[source_name] = contexts if contexts: active_sources.append(source_name) - uncovered_by_context: dict[ - str, - list[tuple[pd.Timestamp, pd.Timestamp]], - ] = {} + uncovered_by_context: dict[str, list[tuple[pd.Timestamp, pd.Timestamp]]] = {} for context in target_contexts: intervals = [] @@ -137,16 +109,11 @@ def build_target_data_plan( continue intervals.append( - ( - source_temporal_scope["start"], - source_temporal_scope["end"], - ) + (source_temporal_scope["start"], source_temporal_scope["end"]) ) gaps = uncovered_temporal_intervals( - intervals, - start=temporal_scope["start"], - end=temporal_scope["end"], + intervals, start=temporal_scope["start"], end=temporal_scope["end"] ) if gaps: @@ -178,9 +145,7 @@ def build_target_data_plan( def write_target_data_plan( - *, - plan: Mapping[str, object], - output_path: str | Path, + *, plan: Mapping[str, object], output_path: str | Path ) -> None: """Write the target-data acquisition plan as JSON.""" output_path = Path(output_path) @@ -198,11 +163,7 @@ def main(snakemake: Any) -> None: land_shapes = shapes.loc[shapes["shape_class"] == "land"] - target_contexts = ( - land_shapes["country_id"] - .drop_duplicates() - .tolist() - ) + target_contexts = land_shapes["country_id"].drop_duplicates().tolist() plan = build_target_data_plan( target_contexts=target_contexts, @@ -211,10 +172,7 @@ def main(snakemake: Any) -> None: temporal_scope=snakemake.params.temporal_scope, ) - write_target_data_plan( - plan=plan, - output_path=snakemake.output.plan, - ) + write_target_data_plan(plan=plan, output_path=snakemake.output.plan) if __name__ == "__main__": From 7dc06c48a3fa12f9468361177fa9bf3e674d5d76 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 19:21:19 +0200 Subject: [PATCH 188/241] Fixing linting complaints --- tests/unit/test_plan_target_data.py | 9 ++++++--- tests/unit/test_tclean_config.py | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_plan_target_data.py b/tests/unit/test_plan_target_data.py index e5526b1..2c5bf5a 100644 --- a/tests/unit/test_plan_target_data.py +++ b/tests/unit/test_plan_target_data.py @@ -248,7 +248,10 @@ def test_target_plan_rejects_temporal_coverage_gap(): }, } - with pytest.raises(ValueError) as exc_info: + with pytest.raises( + ValueError, + match=r"2019-01-01T00:00:00\+00:00", + ) as exc_info: build_target_data_plan( target_contexts=["AAA"], source_names=["source_a", "source_b"], @@ -267,7 +270,7 @@ def test_target_plan_rejects_completely_unsupported_context(): """A target context with no usable source is rejected.""" source_registry = {"source_a": {"contexts": ["AAA"]}} - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match="BBB") as exc_info: build_target_data_plan( target_contexts=["AAA", "BBB"], source_names=["source_a"], @@ -295,7 +298,7 @@ def test_target_plan_checks_coverage_per_context(): }, } - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match="BBB") as exc_info: build_target_data_plan( target_contexts=["AAA", "BBB"], source_names=["old_source", "new_source"], diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index ecc97a2..6cf76da 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -322,7 +322,10 @@ def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: requirements = pd.DataFrame({"context": ["AAA"], "start": [start], "end": [end]}) - with pytest.raises(ValueError) as exc_info: + with pytest.raises( + ValueError, + match=r"2019-01-01T00:00:00\+00:00", + ) as exc_info: filter_source_requests_by_temporal_scope( requests, requirements=requirements, From 4775504d552abad42b4d7e58cc9333557028eb37 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:21:36 +0000 Subject: [PATCH 189/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/test_plan_target_data.py | 5 +---- tests/unit/test_tclean_config.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/unit/test_plan_target_data.py b/tests/unit/test_plan_target_data.py index 2c5bf5a..83a5212 100644 --- a/tests/unit/test_plan_target_data.py +++ b/tests/unit/test_plan_target_data.py @@ -248,10 +248,7 @@ def test_target_plan_rejects_temporal_coverage_gap(): }, } - with pytest.raises( - ValueError, - match=r"2019-01-01T00:00:00\+00:00", - ) as exc_info: + with pytest.raises(ValueError, match=r"2019-01-01T00:00:00\+00:00") as exc_info: build_target_data_plan( target_contexts=["AAA"], source_names=["source_a", "source_b"], diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index 6cf76da..dcaf381 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -322,10 +322,7 @@ def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: requirements = pd.DataFrame({"context": ["AAA"], "start": [start], "end": [end]}) - with pytest.raises( - ValueError, - match=r"2019-01-01T00:00:00\+00:00", - ) as exc_info: + with pytest.raises(ValueError, match=r"2019-01-01T00:00:00\+00:00") as exc_info: filter_source_requests_by_temporal_scope( requests, requirements=requirements, From 0644187cfee5152f80aeda643ba70f4049663de5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 20:02:27 +0200 Subject: [PATCH 190/241] Updated environment. --- workflow/envs/module.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index a28cccd..8f15f4e 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -20,4 +20,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 -- tclean ==0.1.1 +- tclean >=0.1.1 From db57b98e1d721c864617fd2be8f97f221a6cdc51 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 20:44:44 +0200 Subject: [PATCH 191/241] Updated code to accept plotting of a single country. --- workflow/scripts/_plots.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workflow/scripts/_plots.py b/workflow/scripts/_plots.py index 491ef86..36d7f1d 100644 --- a/workflow/scripts/_plots.py +++ b/workflow/scripts/_plots.py @@ -24,6 +24,8 @@ def plot_national_profiles(df: pd.DataFrame): n_profiles = df.shape[1] fig, axs = plt.subplots(1, n_profiles, figsize=(17, 6)) + if len(profiles_GW.columns) == 1: + axs = [axs] fig.subplots_adjust(wspace=0) for ax, column in zip(axs, profiles_GW.columns): From 54656bcc933d5b548a86549e4424089b617c39c8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 20:48:01 +0200 Subject: [PATCH 192/241] More robust fix. --- workflow/scripts/_plots.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/_plots.py b/workflow/scripts/_plots.py index 36d7f1d..b0b557d 100644 --- a/workflow/scripts/_plots.py +++ b/workflow/scripts/_plots.py @@ -24,8 +24,7 @@ def plot_national_profiles(df: pd.DataFrame): n_profiles = df.shape[1] fig, axs = plt.subplots(1, n_profiles, figsize=(17, 6)) - if len(profiles_GW.columns) == 1: - axs = [axs] + axs = np.atleast_1d(axs).ravel() fig.subplots_adjust(wspace=0) for ax, column in zip(axs, profiles_GW.columns): @@ -57,7 +56,7 @@ def plot_national_profiles(df: pd.DataFrame): ax.set_xticklabels(["", np.round(val_max, 1)]) ax.title.set_text(column) - for ax in axs.flatten()[1:]: + for ax in axs[1:]: ax.set_yticks([]) axs[0].set_ylabel("Time") From 69753b811f50854d2b9a79740bb565990791f3e8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 2 Sep 2026 22:41:40 +0200 Subject: [PATCH 193/241] Updated NESO prep, to account for inconsistent date stamps in stource --- workflow/scripts/sources/neso/prepare.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workflow/scripts/sources/neso/prepare.py b/workflow/scripts/sources/neso/prepare.py index 0b8a2f1..bb450c0 100644 --- a/workflow/scripts/sources/neso/prepare.py +++ b/workflow/scripts/sources/neso/prepare.py @@ -16,7 +16,9 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: prepared = data.copy() prepared["SETTLEMENT_DATE"] = pd.to_datetime( - prepared["SETTLEMENT_DATE"], errors="raise" + prepared["SETTLEMENT_DATE"], + format="mixed", + errors="raise", ).dt.normalize() prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( From 4f59a222071f7157391ada9ef11e455e52eb3dce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:41:58 +0000 Subject: [PATCH 194/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/sources/neso/prepare.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/workflow/scripts/sources/neso/prepare.py b/workflow/scripts/sources/neso/prepare.py index bb450c0..d6b6b53 100644 --- a/workflow/scripts/sources/neso/prepare.py +++ b/workflow/scripts/sources/neso/prepare.py @@ -16,9 +16,7 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: prepared = data.copy() prepared["SETTLEMENT_DATE"] = pd.to_datetime( - prepared["SETTLEMENT_DATE"], - format="mixed", - errors="raise", + prepared["SETTLEMENT_DATE"], format="mixed", errors="raise" ).dt.normalize() prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( From 5e83ecba3e6fde7309029c3d4c05c994a10c8bf5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 11:15:59 +0200 Subject: [PATCH 195/241] Added month chunking to match ENTSO-E API changes. --- workflow/scripts/sources/entsoe/download.py | 67 +++++++++++++++++---- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index 460b7c8..2651c38 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -18,10 +18,28 @@ def load_token(filepath: str | Path) -> str: return Path(filepath).read_text().strip() +def monthly_intervals( + start: pd.Timestamp, + end: pd.Timestamp, +) -> list[tuple[pd.Timestamp, pd.Timestamp]]: + """Split a time interval at calendar-month boundaries.""" + intervals = [] + chunk_start = start + + while chunk_start < end: + next_month = chunk_start + pd.offsets.MonthBegin(1) + chunk_end = min(next_month, end) + + intervals.append((chunk_start, chunk_end)) + chunk_start = chunk_end + + return intervals + + def download_country( *, country_alpha_3: str, start: pd.Timestamp, end: pd.Timestamp, token: str ) -> tuple[str, pd.Series, float]: - """Download ENTSO-E load for one country.""" + """Download ENTSO-E load for one country. Accounts for ENTSO-E API P1M Constraint.""" country = pycountry.countries.get(alpha_3=country_alpha_3) if country is None: @@ -33,21 +51,48 @@ def download_country( country_start = perf_counter() - try: - data = client.query_load(country_code=country_alpha_2, start=start, end=end) + chunks = [] - data = data["Actual Load"] - data.name = country_alpha_3 - - except NoMatchingDataError: - logger.warning( - "No data found for %s/%s in the given period: %s to %s.", + for chunk_start, chunk_end in monthly_intervals(start, end): + logger.debug( + "Downloading ENTSO-E load for %s/%s from %s to %s.", country_alpha_2, country_alpha_3, - start, - end, + chunk_start, + chunk_end, ) + try: + chunk = client.query_load( + country_code=country_alpha_2, + start=chunk_start, + end=chunk_end, + ) + + except NoMatchingDataError: + logger.warning( + "No data found for %s/%s from %s to %s.", + country_alpha_2, + country_alpha_3, + chunk_start, + chunk_end, + ) + continue + + chunks.append(chunk["Actual Load"]) + + if chunks: + data = pd.concat(chunks).sort_index() + + if data.index.has_duplicates: + raise ValueError( + f"ENTSO-E returned duplicate timestamps for {country_alpha_3!r} " + f"between {start} and {end}." + ) + + data.name = country_alpha_3 + + else: data = pd.Series(name=country_alpha_3, dtype=float) elapsed = perf_counter() - country_start From fe72c98f692ab4d8ce1aba7c5fcfdec7fb5f3603 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 11:16:18 +0200 Subject: [PATCH 196/241] Added entso-e back to test-config sources as servers are online. --- tests/integration/test_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 013a00e..0a6aa77 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -10,7 +10,7 @@ integration_test: # data for the same country and timestamp, the earlier source takes priority. # Supported sources include: entsoe, neso, opsd. load_sources: - # - entsoe + - entsoe - neso - opsd - entsoe_power_statistics From eb042a74553874a65737dba75e5e800538a9884b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 09:16:39 +0000 Subject: [PATCH 197/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/sources/entsoe/download.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index 2651c38..8e783d2 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -19,8 +19,7 @@ def load_token(filepath: str | Path) -> str: def monthly_intervals( - start: pd.Timestamp, - end: pd.Timestamp, + start: pd.Timestamp, end: pd.Timestamp ) -> list[tuple[pd.Timestamp, pd.Timestamp]]: """Split a time interval at calendar-month boundaries.""" intervals = [] @@ -64,9 +63,7 @@ def download_country( try: chunk = client.query_load( - country_code=country_alpha_2, - start=chunk_start, - end=chunk_end, + country_code=country_alpha_2, start=chunk_start, end=chunk_end ) except NoMatchingDataError: From 00903510dde796ac7ee426c151257779ae2e6243 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 11:41:09 +0200 Subject: [PATCH 198/241] Put log messages in log files only instead of terminal. --- workflow/scripts/sources/entsoe/download.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py index 2651c38..aef43f1 100644 --- a/workflow/scripts/sources/entsoe/download.py +++ b/workflow/scripts/sources/entsoe/download.py @@ -70,7 +70,7 @@ def download_country( ) except NoMatchingDataError: - logger.warning( + logger.debug( "No data found for %s/%s from %s to %s.", country_alpha_2, country_alpha_3, @@ -168,7 +168,7 @@ def download_entsoe( raw.to_parquet(output_path) - logger.info( + logger.debug( "Finished ENTSO-E downloads in %.1fs. Saved raw data to %s.", perf_counter() - download_start, output_path, From adb4b5221a9739625cbab9711cb46ae118fe35d7 Mon Sep 17 00:00:00 2001 From: ddahawkins-TUDelft Date: Thu, 3 Sep 2026 13:48:43 +0200 Subject: [PATCH 199/241] Update README.md Co-authored-by: Jann Launer <32454596+jnnr@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3b09d2..5710596 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This Modelblocks module prepares regular electricity-demand time series for European target regions. National demand observations from multiple providers are combined and cleaned on a user-defined time grid, then spatially disaggregated using population data and aggregated to user-provided shapes. -Demand cleaning is performed with tlean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs. +Demand cleaning is performed with tclean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs.

From eb67667d3cf250a0a2812d6a779ad85d3426a5f8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 16:24:31 +0200 Subject: [PATCH 200/241] Updated test_config --- tests/integration/test_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 0a6aa77..2c80d16 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -12,7 +12,7 @@ integration_test: load_sources: - entsoe - neso - - opsd + # - opsd - entsoe_power_statistics # Exercise the full advanced gap-filling workflow. See config/README.md for From e3c19e8bac4f96fa4eaaab637e3128526ce7efc8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 16:29:11 +0200 Subject: [PATCH 201/241] Reorganised .smk files --- workflow/Snakefile | 3 +- workflow/rules/_helpers.smk | 172 ++++++++++++++++++++++++ workflow/rules/advanced.smk | 128 ------------------ workflow/rules/automatic.smk | 55 -------- workflow/rules/{clean.smk => basic.smk} | 17 --- workflow/rules/prepare_demand.smk | 24 ++++ 6 files changed, 198 insertions(+), 201 deletions(-) create mode 100644 workflow/rules/_helpers.smk rename workflow/rules/{clean.smk => basic.smk} (83%) diff --git a/workflow/Snakefile b/workflow/Snakefile index 8d0991a..e22431c 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -46,8 +46,9 @@ additional_config_validation() # Generic demand workflow rules. +include: "rules/_helpers.smk" include: "rules/automatic.smk" -include: "rules/clean.smk" +include: "rules/basic.smk" include: "rules/advanced.smk" # Source-specific acquisition and preparation rules. include: "rules/source_entsoe.smk" diff --git a/workflow/rules/_helpers.smk b/workflow/rules/_helpers.smk new file mode 100644 index 0000000..ef3d2e3 --- /dev/null +++ b/workflow/rules/_helpers.smk @@ -0,0 +1,172 @@ +import json + + +def target_data_plan(wildcards): + """Return the target-data plan after the checkpoint completes.""" + return checkpoints.plan_target_data.get( + shape=wildcards.shape, + ).output.plan + + +def read_target_data_plan(wildcards): + """Read the resolved target-data plan for one shape.""" + plan_file = checkpoints.plan_target_data.get( + shape=wildcards.shape, + ).output.plan + + with open(plan_file, encoding="utf-8") as file: + return json.load(file) + + +def target_source_contexts(wildcards, source_name): + """Return target contexts assigned to one source for one shape.""" + plan = read_target_data_plan(wildcards) + + return plan["source_contexts"].get(source_name, []) + + +def target_source_temporal_scope(wildcards, source_name): + """Return the planned target temporal scope for one source.""" + plan = read_target_data_plan(wildcards) + + temporal_scope = plan["source_temporal_scopes"].get(source_name) + + if temporal_scope is None: + raise ValueError(f"Source {source_name!r} has no active target temporal scope.") + + return temporal_scope + + +def target_source_start(wildcards, source_name): + """Return the planned target start timestamp for one source.""" + return target_source_temporal_scope( + wildcards, + source_name, + )["start"] + + +def target_source_end(wildcards, source_name): + """Return the planned target end timestamp for one source.""" + return target_source_temporal_scope( + wildcards, + source_name, + )["end"] + + +def configured_load_inputs(wildcards): + """Return prepared demand files for active target sources.""" + plan = read_target_data_plan(wildcards) + + return [ + ("/automatic/" f"{wildcards.shape}/" f"load_{source_name}.parquet") + for source_name in plan["active_sources"] + ] + + +def active_load_sources(wildcards): + """Return active demand sources for one target shape.""" + plan = read_target_data_plan(wildcards) + + return plan["active_sources"] + + +def _read_auxiliary_plan(wildcards): + """Read the resolved advanced execution plan for one shape.""" + plan_file = checkpoints.plan_auxiliary_data.get( + shape=wildcards.shape, + ).output.plan + + with open(plan_file, encoding="utf-8") as file: + return json.load(file) + + +def auxiliary_acquisition_plan(wildcards): + """Return the execution plan after the checkpoint completes.""" + return checkpoints.plan_auxiliary_data.get( + shape=wildcards.shape, + ).output.plan + + +def auxiliary_group_source_files(wildcards): + """Return prepared source files for one auxiliary group.""" + plan = _read_auxiliary_plan(wildcards) + + batch_ids = plan["groups"][wildcards.group_id] + + batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} + + return [ + ( + "/automatic/" + f"{wildcards.shape}/" + f"auxiliary/{batches_by_id[batch_id]['source']}/" + f"{batch_id}.parquet" + ) + for batch_id in batch_ids + ] + + +def auxiliary_rule_cleaned_files(wildcards): + """Return cleaned auxiliary files required by one advanced override.""" + plan = _read_auxiliary_plan(wildcards) + + group_ids = plan["rules"][wildcards.rule_name]["required_group_ids"] + + return [ + ( + "/automatic/" + f"{wildcards.shape}/" + "auxiliary/cleaned/" + f"{group_id}.parquet" + ) + for group_id in group_ids + ] + + +def advanced_constructed_profiles(wildcards): + """Return constructed profiles required by active advanced overrides.""" + plan = _read_auxiliary_plan(wildcards) + + return [ + ( + "/automatic/" + f"{wildcards.shape}/" + "auxiliary/constructed/" + f"{rule_name}.parquet" + ) + for rule_name in plan["constructed_profile_rule_names"] + ] + + +def final_clean_demand_input(wildcards): + """Return the cleaned demand appropriate for the configured mode.""" + if config["gap_filling"]["mode"] == "advanced": + return ( + "/automatic/" + f"{wildcards.shape}/" + "load_advanced_cleaned.parquet" + ) + + return rules.clean_demand.output.demand + + +def final_cleaning_method_input(wildcards): + """Return cleaning provenance appropriate for the configured mode.""" + if config["gap_filling"]["mode"] == "advanced": + return ( + "/automatic/" + f"{wildcards.shape}/" + "load_advanced_cleaning_method.parquet" + ) + + return rules.clean_demand.output.cleaning_method + + +def advanced_external_profile_files(wildcards): + plan = _read_auxiliary_plan(wildcards) + + return [ + f"/{filename}" + for filename in dict.fromkeys(plan["external_profile_files"].values()) + ] + diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index b13b5b8..6e6b961 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -1,107 +1,3 @@ -import json - - -def _read_auxiliary_plan(wildcards): - """Read the resolved advanced execution plan for one shape.""" - plan_file = checkpoints.plan_auxiliary_data.get( - shape=wildcards.shape, - ).output.plan - - with open(plan_file, encoding="utf-8") as file: - return json.load(file) - - -def auxiliary_acquisition_plan(wildcards): - """Return the execution plan after the checkpoint completes.""" - return checkpoints.plan_auxiliary_data.get( - shape=wildcards.shape, - ).output.plan - - -def auxiliary_group_source_files(wildcards): - """Return prepared source files for one auxiliary group.""" - plan = _read_auxiliary_plan(wildcards) - - batch_ids = plan["groups"][wildcards.group_id] - - batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} - - return [ - ( - "/automatic/" - f"{wildcards.shape}/" - f"auxiliary/{batches_by_id[batch_id]['source']}/" - f"{batch_id}.parquet" - ) - for batch_id in batch_ids - ] - - -def auxiliary_rule_cleaned_files(wildcards): - """Return cleaned auxiliary files required by one advanced override.""" - plan = _read_auxiliary_plan(wildcards) - - group_ids = plan["rules"][wildcards.rule_name]["required_group_ids"] - - return [ - ( - "/automatic/" - f"{wildcards.shape}/" - "auxiliary/cleaned/" - f"{group_id}.parquet" - ) - for group_id in group_ids - ] - - -def advanced_constructed_profiles(wildcards): - """Return constructed profiles required by active advanced overrides.""" - plan = _read_auxiliary_plan(wildcards) - - return [ - ( - "/automatic/" - f"{wildcards.shape}/" - "auxiliary/constructed/" - f"{rule_name}.parquet" - ) - for rule_name in plan["constructed_profile_rule_names"] - ] - - -def final_clean_demand_input(wildcards): - """Return the cleaned demand appropriate for the configured mode.""" - if config["gap_filling"]["mode"] == "advanced": - return ( - "/automatic/" - f"{wildcards.shape}/" - "load_advanced_cleaned.parquet" - ) - - return rules.clean_demand.output.demand - - -def final_cleaning_method_input(wildcards): - """Return cleaning provenance appropriate for the configured mode.""" - if config["gap_filling"]["mode"] == "advanced": - return ( - "/automatic/" - f"{wildcards.shape}/" - "load_advanced_cleaning_method.parquet" - ) - - return rules.clean_demand.output.cleaning_method - - -def advanced_external_profile_files(wildcards): - plan = _read_auxiliary_plan(wildcards) - - return [ - f"/{filename}" - for filename in dict.fromkeys(plan["external_profile_files"].values()) - ] - - checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, @@ -120,30 +16,6 @@ checkpoint plan_auxiliary_data: "../scripts/plan_auxiliary_data.py" -rule finalise_clean_demand: - input: - target_plan=target_data_plan, - demand=final_clean_demand_input, - cleaning_method=final_cleaning_method_input, - output: - demand=("/automatic/{shape}/" "load_cleaned.parquet"), - cleaning_method=( - "/automatic/{shape}/" "load_final_cleaning_method.parquet" - ), - cleaning_method_rank=( - "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" - ), - conda: - "../envs/module.yaml" - params: - source_names=active_load_sources, - gap_filling=config["gap_filling"], - message: - "Finalise cleaned electricity demand and provenance." - script: - "../scripts/finalise_clean_demand.py" - - rule clean_auxiliary_group: input: plan=auxiliary_acquisition_plan, diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 26f4a41..bb6272b 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,8 +1,5 @@ """Rules used for generic automatic resources and validation.""" -import json - - rule validate_temporal_config_semantics: output: "/automatic/temporal_config_validation.json", @@ -54,58 +51,6 @@ checkpoint plan_target_data: "../scripts/plan_target_data.py" -def target_data_plan(wildcards): - """Return the target-data plan after the checkpoint completes.""" - return checkpoints.plan_target_data.get( - shape=wildcards.shape, - ).output.plan - - -def read_target_data_plan(wildcards): - """Read the resolved target-data plan for one shape.""" - plan_file = checkpoints.plan_target_data.get( - shape=wildcards.shape, - ).output.plan - - with open(plan_file, encoding="utf-8") as file: - return json.load(file) - - -def target_source_contexts(wildcards, source_name): - """Return target contexts assigned to one source for one shape.""" - plan = read_target_data_plan(wildcards) - - return plan["source_contexts"].get(source_name, []) - - -def target_source_temporal_scope(wildcards, source_name): - """Return the planned target temporal scope for one source.""" - plan = read_target_data_plan(wildcards) - - temporal_scope = plan["source_temporal_scopes"].get(source_name) - - if temporal_scope is None: - raise ValueError(f"Source {source_name!r} has no active target temporal scope.") - - return temporal_scope - - -def target_source_start(wildcards, source_name): - """Return the planned target start timestamp for one source.""" - return target_source_temporal_scope( - wildcards, - source_name, - )["start"] - - -def target_source_end(wildcards, source_name): - """Return the planned target end timestamp for one source.""" - return target_source_temporal_scope( - wildcards, - source_name, - )["end"] - - rule download_population: output: population=update("/automatic/population.zip"), diff --git a/workflow/rules/clean.smk b/workflow/rules/basic.smk similarity index 83% rename from workflow/rules/clean.smk rename to workflow/rules/basic.smk index 9bef3a2..55b07df 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/basic.smk @@ -1,20 +1,3 @@ -def configured_load_inputs(wildcards): - """Return prepared demand files for active target sources.""" - plan = read_target_data_plan(wildcards) - - return [ - ("/automatic/" f"{wildcards.shape}/" f"load_{source_name}.parquet") - for source_name in plan["active_sources"] - ] - - -def active_load_sources(wildcards): - """Return active demand sources for one target shape.""" - plan = read_target_data_plan(wildcards) - - return plan["active_sources"] - - rule clean_demand: input: load_inputs=configured_load_inputs, diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 0e2cbba..cbdedd6 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -1,3 +1,27 @@ +rule finalise_clean_demand: + input: + target_plan=target_data_plan, + demand=final_clean_demand_input, + cleaning_method=final_cleaning_method_input, + output: + demand=("/automatic/{shape}/" "load_cleaned.parquet"), + cleaning_method=( + "/automatic/{shape}/" "load_final_cleaning_method.parquet" + ), + cleaning_method_rank=( + "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" + ), + conda: + "../envs/module.yaml" + params: + source_names=active_load_sources, + gap_filling=config["gap_filling"], + message: + "Finalise cleaned electricity demand and provenance." + script: + "../scripts/finalise_clean_demand.py" + + rule demand_electricity_raster: input: demand=rules.finalise_clean_demand.output.demand, From 4c0f5060e042ff4c59523a17a04d311be313bf2b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 17:14:07 +0200 Subject: [PATCH 202/241] Developers define the relevant rules and python scripts for each source in the source_registry. These are automatically registered within the snakefile. --- workflow/Snakefile | 23 +++++++++++++++++------ workflow/internal/source_registry.yaml | 24 ++++++++++++++++++++++++ workflow/rules/_utils.smk | 8 -------- workflow/rules/advanced.smk | 6 ++++++ workflow/rules/automatic.smk | 6 ++++++ workflow/rules/prepare_demand.smk | 2 ++ 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index e22431c..cbca448 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -28,11 +28,23 @@ validate(config, workflow.source_path("internal/config.schema.yaml")) with open(workflow.source_path("internal/settings.yaml"), "r") as f: internal = yaml.safe_load(f) +# Load the source registry +with open(workflow.source_path("internal/source_registry.yaml"),encoding="utf-8",) as f: + SOURCE_REGISTRY = yaml.safe_load(f) # Load python files in scripts that are used in other scripts, but not run itself. # This is necessary for integrating the snakemake module via reference to GitHub. workflow.source_path("scripts/_plots.py") workflow.source_path("scripts/_schemas.py") +workflow.source_path("scripts/_advanced_execution.py") +workflow.source_path("scripts/_plot_timeline.py") +workflow.source_path("scripts/_prepared_data.py") +workflow.source_path("scripts/_source_capabilities.py") +workflow.source_path("scripts/_tclean_config.py") +# Register source-specific python scripts for remote module use. +for source in SOURCE_REGISTRY.values(): + for python_file in source["workflow"]["python"]: + workflow.source_path(python_file) # Add all your includes here. @@ -50,12 +62,11 @@ include: "rules/_helpers.smk" include: "rules/automatic.smk" include: "rules/basic.smk" include: "rules/advanced.smk" -# Source-specific acquisition and preparation rules. -include: "rules/source_entsoe.smk" -include: "rules/source_entsoe_power_statistics.smk" -include: "rules/source_neso.smk" -include: "rules/source_opsd.smk" -# Spatial demand preparation. +# Source-specific workflow rules. +for source in SOURCE_REGISTRY.values(): + for rule_file in source["workflow"]["rules"]: + include: rule_file +# Demand finalisation and spatial re-aggregation. include: "rules/prepare_demand.smk" diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index ba2b1be..52c1b25 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -47,6 +47,12 @@ entsoe: - "SWE" - "SVN" - "SVK" + workflow: + rules: + - "rules/source_entsoe.smk" + python: + - "scripts/sources/entsoe/download.py" + - "scripts/sources/entsoe/prepare.py" entsoe_power_statistics: display_name: ENTSO-E Power Statistics @@ -88,11 +94,23 @@ entsoe_power_statistics: - "SWE" - "SVN" - "SVK" + workflow: + rules: + - "rules/source_entsoe_power_statistics.smk" + python: + - "scripts/sources/entsoe_power_statistics/download.py" + - "scripts/sources/entsoe_power_statistics/prepare.py" neso: display_name: NESO contexts: - GBR + workflow: + rules: + - "rules/source_neso.smk" + python: + - "scripts/sources/neso/download.py" + - "scripts/sources/neso/prepare.py" opsd: display_name: OPSD @@ -134,3 +152,9 @@ opsd: - "SWE" - "SVN" - "SVK" + workflow: + rules: + - "rules/source_opsd.smk" + python: + - "scripts/sources/opsd/download.py" + - "scripts/sources/opsd/prepare.py" diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 766c313..39f8255 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -1,15 +1,7 @@ """Collection of auxiliary functions for this module.""" from datetime import datetime, timedelta, timezone -import yaml -SOURCE_REGISTRY_PATH = workflow.source_path("../internal/source_registry.yaml") - -with open( - SOURCE_REGISTRY_PATH, - encoding="utf-8", -) as file: - SOURCE_REGISTRY = yaml.safe_load(file) or {} if not isinstance(SOURCE_REGISTRY, dict): raise ValueError( diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 6e6b961..a0e026f 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -10,6 +10,8 @@ checkpoint plan_auxiliary_data: gap_filling=config["gap_filling"], source_names=config["load_sources"], source_registry=SOURCE_REGISTRY, + log: + "/{shape}/auxiliary/plan_auxiliary_data.log", message: "Plan auxiliary electricity-demand acquisition." script: @@ -67,6 +69,8 @@ rule construct_auxiliary_profile: params: frequency=config["temporal_scope"]["frequency"], advanced_sources=(config["gap_filling"]["advanced"]["sources"]), + log: + "/{shape}/auxiliary/construct_auxiliary_data_{rule_name}.log", message: "Construct auxiliary demand profile for {wildcards.rule_name}." script: @@ -88,6 +92,8 @@ rule apply_advanced_overrides: ), conda: "../envs/module.yaml" + log: + "/{shape}/auxiliary/apply_advanced_overrides.log", params: temporal_scope=config["temporal_scope"], message: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index bb6272b..a7ee540 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -10,6 +10,8 @@ rule validate_temporal_config_semantics: validation_config={ "temporal_scope": config["temporal_scope"], }, + log: + "/validate_temporal_config_semantics.log", message: "Validate temporal configuration semantics." script: @@ -27,6 +29,8 @@ rule validate_gap_filling_config_semantics: "temporal_scope": config["temporal_scope"], "gap_filling": config["gap_filling"], }, + log: + "/validate_gap_filling_config_semantics.log", message: "Validate gap-filling configuration semantics." script: @@ -45,6 +49,8 @@ checkpoint plan_target_data: source_names=config["load_sources"], source_registry=SOURCE_REGISTRY, temporal_scope=config["temporal_scope"], + log: + "/{shape}/plan_target_data.log", message: "Plan target electricity-demand data acquisition." script: diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index cbdedd6..2c2cdb0 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -13,6 +13,8 @@ rule finalise_clean_demand: ), conda: "../envs/module.yaml" + log: + "/{shape}/finalise_clean_demand.log", params: source_names=active_load_sources, gap_filling=config["gap_filling"], From d0345a848c4e7608364aadddaed88bf537d3dc31 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:14:27 +0000 Subject: [PATCH 203/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/Snakefile | 10 +++++++++- workflow/rules/_helpers.smk | 1 - workflow/rules/_utils.smk | 1 - workflow/rules/advanced.smk | 12 ++++++------ workflow/rules/automatic.smk | 13 +++++++------ workflow/rules/prepare_demand.smk | 4 ++-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index cbca448..31816e3 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -29,7 +29,10 @@ with open(workflow.source_path("internal/settings.yaml"), "r") as f: internal = yaml.safe_load(f) # Load the source registry -with open(workflow.source_path("internal/source_registry.yaml"),encoding="utf-8",) as f: +with open( + workflow.source_path("internal/source_registry.yaml"), + encoding="utf-8", +) as f: SOURCE_REGISTRY = yaml.safe_load(f) # Load python files in scripts that are used in other scripts, but not run itself. @@ -62,10 +65,15 @@ include: "rules/_helpers.smk" include: "rules/automatic.smk" include: "rules/basic.smk" include: "rules/advanced.smk" + + # Source-specific workflow rules. for source in SOURCE_REGISTRY.values(): for rule_file in source["workflow"]["rules"]: + include: rule_file + + # Demand finalisation and spatial re-aggregation. include: "rules/prepare_demand.smk" diff --git a/workflow/rules/_helpers.smk b/workflow/rules/_helpers.smk index ef3d2e3..d39660f 100644 --- a/workflow/rules/_helpers.smk +++ b/workflow/rules/_helpers.smk @@ -169,4 +169,3 @@ def advanced_external_profile_files(wildcards): f"/{filename}" for filename in dict.fromkeys(plan["external_profile_files"].values()) ] - diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index 39f8255..c4da78f 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -2,7 +2,6 @@ from datetime import datetime, timedelta, timezone - if not isinstance(SOURCE_REGISTRY, dict): raise ValueError( "Source registry must contain a mapping " "of source identifiers to metadata." diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index a0e026f..80e488c 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -3,6 +3,8 @@ checkpoint plan_auxiliary_data: demand=rules.clean_demand.output.demand, output: plan=("/automatic/{shape}/" "auxiliary/advanced_execution_plan.json"), + log: + "/{shape}/auxiliary/plan_auxiliary_data.log", conda: "../envs/module.yaml" params: @@ -10,8 +12,6 @@ checkpoint plan_auxiliary_data: gap_filling=config["gap_filling"], source_names=config["load_sources"], source_registry=SOURCE_REGISTRY, - log: - "/{shape}/auxiliary/plan_auxiliary_data.log", message: "Plan auxiliary electricity-demand acquisition." script: @@ -64,13 +64,13 @@ rule construct_auxiliary_profile: "auxiliary/constructed/" "{rule_name}.parquet" ), + log: + "/{shape}/auxiliary/construct_auxiliary_data_{rule_name}.log", conda: "../envs/module.yaml" params: frequency=config["temporal_scope"]["frequency"], advanced_sources=(config["gap_filling"]["advanced"]["sources"]), - log: - "/{shape}/auxiliary/construct_auxiliary_data_{rule_name}.log", message: "Construct auxiliary demand profile for {wildcards.rule_name}." script: @@ -90,10 +90,10 @@ rule apply_advanced_overrides: cleaning_method=( "/automatic/{shape}/" "load_advanced_cleaning_method.parquet" ), - conda: - "../envs/module.yaml" log: "/{shape}/auxiliary/apply_advanced_overrides.log", + conda: + "../envs/module.yaml" params: temporal_scope=config["temporal_scope"], message: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index a7ee540..69ccff5 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,8 +1,11 @@ """Rules used for generic automatic resources and validation.""" + rule validate_temporal_config_semantics: output: "/automatic/temporal_config_validation.json", + log: + "/validate_temporal_config_semantics.log", conda: "../envs/module.yaml" params: @@ -10,8 +13,6 @@ rule validate_temporal_config_semantics: validation_config={ "temporal_scope": config["temporal_scope"], }, - log: - "/validate_temporal_config_semantics.log", message: "Validate temporal configuration semantics." script: @@ -21,6 +22,8 @@ rule validate_temporal_config_semantics: rule validate_gap_filling_config_semantics: output: "/automatic/gap_filling_config_validation.json", + log: + "/validate_gap_filling_config_semantics.log", conda: "../envs/module.yaml" params: @@ -29,8 +32,6 @@ rule validate_gap_filling_config_semantics: "temporal_scope": config["temporal_scope"], "gap_filling": config["gap_filling"], }, - log: - "/validate_gap_filling_config_semantics.log", message: "Validate gap-filling configuration semantics." script: @@ -43,14 +44,14 @@ checkpoint plan_target_data: temporal_validation=("/automatic/temporal_config_validation.json"), output: plan=("/automatic/{shape}/" "target_data_plan.json"), + log: + "/{shape}/plan_target_data.log", conda: "../envs/module.yaml" params: source_names=config["load_sources"], source_registry=SOURCE_REGISTRY, temporal_scope=config["temporal_scope"], - log: - "/{shape}/plan_target_data.log", message: "Plan target electricity-demand data acquisition." script: diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 2c2cdb0..c40c00f 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -11,10 +11,10 @@ rule finalise_clean_demand: cleaning_method_rank=( "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" ), - conda: - "../envs/module.yaml" log: "/{shape}/finalise_clean_demand.log", + conda: + "../envs/module.yaml" params: source_names=active_load_sources, gap_filling=config["gap_filling"], From 0d90dac0b00e9c1e2e75ca968caffa30d368306d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 18:22:59 +0200 Subject: [PATCH 204/241] Comply with snakemake linting --- .github/workflows/pr-ci.yml | 2 +- tests/integration/Snakefile | 4 +- workflow/Snakefile | 1 + workflow/rules/_source_helpers.smk | 158 ++++++++++++++++++ workflow/rules/_utils.smk | 30 ++-- workflow/rules/source_entsoe.smk | 47 ------ .../rules/source_entsoe_power_statistics.smk | 48 ------ workflow/rules/source_neso.smk | 45 ----- workflow/rules/source_opsd.smk | 8 - 9 files changed, 177 insertions(+), 166 deletions(-) create mode 100644 workflow/rules/_source_helpers.smk diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 6df1aa6..ec4bb7b 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -25,7 +25,7 @@ jobs: continue-on-error: true - name: Run unit tests id: unit-tests - run: pixi run -e test pytest tests/unit + run: pixi run test-unit continue-on-error: true - name: Run snakemake linting id: linting diff --git a/tests/integration/Snakefile b/tests/integration/Snakefile index ec2fb0a..28937b3 100644 --- a/tests/integration/Snakefile +++ b/tests/integration/Snakefile @@ -8,8 +8,8 @@ configfile: workflow.source_path("./test_config.yaml") module module_demand_electricity: pathvars: shapes="resources/user/{shape}/shapes.parquet", - token_entsoe=workflow.source_path("resources/user/token_entsoe.txt"), - external_profiles=workflow.source_path("resources/user/external_profiles"), + token_entsoe="resources/user/token_entsoe.txt", + external_profiles="resources/user/external_profiles", # test the re-routing of resources, results and logs resources="resources/module/resources", results="resources/module/results", diff --git a/workflow/Snakefile b/workflow/Snakefile index 31816e3..2a602e7 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -62,6 +62,7 @@ additional_config_validation() # Generic demand workflow rules. include: "rules/_helpers.smk" +include: "rules/_source_helpers.smk" include: "rules/automatic.smk" include: "rules/basic.smk" include: "rules/advanced.smk" diff --git a/workflow/rules/_source_helpers.smk b/workflow/rules/_source_helpers.smk new file mode 100644 index 0000000..9ebe747 --- /dev/null +++ b/workflow/rules/_source_helpers.smk @@ -0,0 +1,158 @@ +"""Source specific helpers snakemake rules.""" + +# ENTSOE POWER STATISTICS + +def entsoe_power_statistics_annual_files(years): + """Return annual ENTSO-E Power Statistics files.""" + return [ + ("/automatic/" "entsoe_power_statistics/raw/" f"{int(year)}.parquet") + for year in years + ] + + +def entsoe_power_statistics_raw_files(wildcards): + """Return Power Statistics annual files required by the target period.""" + years = years_for_period( + target_source_start( + wildcards, + "entsoe_power_statistics", + ), + target_source_end( + wildcards, + "entsoe_power_statistics", + ), + ) + + return entsoe_power_statistics_annual_files(years) + + +def auxiliary_entsoe_power_statistics_raw_files(wildcards): + """Return Power Statistics annual files required by one auxiliary batch.""" + plan = _read_auxiliary_plan(wildcards) + + batch = next( + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == wildcards.batch_id + and batch["source"] == "entsoe_power_statistics" + ) + ) + + return entsoe_power_statistics_annual_files(batch["years"]) + + +def target_entsoe_power_statistics_countries(wildcards): + """Return Power Statistics target countries for one shape.""" + return target_source_contexts( + wildcards, + "entsoe_power_statistics", + ) + + +# ENTSOE + +def entsoe_annual_files(countries, years): + """Return reusable ENTSO-E country-year raw-file paths.""" + return [ + "/automatic/entsoe/raw/" f"{country}/{int(year)}.parquet" + for country in countries + for year in years + ] + + +def entsoe_raw_files(wildcards): + """Return ENTSO-E country-year files required for one target shape.""" + plan = read_target_data_plan(wildcards) + + countries = plan["source_contexts"].get("entsoe", []) + + years = years_for_period( + target_source_start( + wildcards, + "entsoe", + ), + target_source_end( + wildcards, + "entsoe", + ), + ) + + return entsoe_annual_files(countries, years) + + +def target_entsoe_countries(wildcards): + """Return ENTSO-E target countries for one shape.""" + return target_source_contexts(wildcards, "entsoe") + + +def auxiliary_entsoe_raw_files(wildcards): + """Return ENTSO-E country-year files required by one auxiliary batch.""" + plan = _read_auxiliary_plan(wildcards) + + batch = next( + batch + for batch in plan["batches"] + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") + ) + + return entsoe_annual_files(batch["countries"], batch["years"]) + + +# NESO + + +def neso_annual_files(years): + """Return reusable annual NESO raw-file paths for the requested years.""" + return [ + "/automatic/neso/" f"historic_demand_{int(year)}.csv" + for year in years + ] + + +def neso_raw_files(wildcards): + """Return annual NESO input files for the planned target period.""" + years = years_for_period( + target_source_start( + wildcards, + "neso", + ), + target_source_end( + wildcards, + "neso", + ), + ) + + return neso_annual_files(years) + + +def auxiliary_neso_raw_files(wildcards): + """Return annual NESO files required by one auxiliary batch.""" + plan = _read_auxiliary_plan(wildcards) + + batch = next( + batch + for batch in plan["batches"] + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") + ) + + return neso_annual_files(batch["years"]) + + +def target_neso_countries(wildcards): + """Return NESO target countries for one shape.""" + return target_source_contexts( + wildcards, + "neso", + ) + + +# OPSD + + +def target_opsd_countries(wildcards): + """Return OPSD target countries for one shape.""" + return target_source_contexts( + wildcards, + "opsd", + ) \ No newline at end of file diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk index c4da78f..a9480df 100644 --- a/workflow/rules/_utils.smk +++ b/workflow/rules/_utils.smk @@ -53,35 +53,35 @@ def additional_config_validation(): advanced = gap_filling["advanced"] sources = advanced["sources"] - advanced_rules = advanced["rules"] + advanced_entries = advanced["rules"] - seen_rule_names = set() - duplicate_rule_names = set() + seen_entry_names = set() + duplicate_entry_names = set() - for rule in advanced_rules: - rule_name = rule["name"] + for advanced_entry in advanced_entries: + entry_name = advanced_entry["name"] - if rule_name in seen_rule_names: - duplicate_rule_names.add(rule_name) + if entry_name in seen_entry_names: + duplicate_entry_names.add(entry_name) - seen_rule_names.add(rule_name) + seen_entry_names.add(entry_name) - if duplicate_rule_names: + if duplicate_entry_names: raise ValueError( - "Advanced cleaning rule names must be unique. " - f"Duplicate names: {sorted(duplicate_rule_names)}." + "Advanced_entry names must be unique. " + f"Duplicate names: {sorted(duplicate_entry_names)}." ) - for rule in advanced_rules: - source_name = rule.get("source") + for advanced_entry in advanced_entries: + source_name = advanced_entry.get("source") - # A rule without a source explicitly leaves the target values missing. + # An advanced_entry without a source explicitly leaves the target values missing. if source_name is None: continue if source_name not in sources: raise ValueError( - f"Advanced rule {rule['name']!r} references unknown " + f"Advanced_entry {advanced_entry['name']!r} references unknown " f"advanced source {source_name!r}." ) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 55ce2ef..feee3f0 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -1,53 +1,6 @@ """Rules for the ENTSO-E Transparency Platform demand source.""" -def entsoe_annual_files(countries, years): - """Return reusable ENTSO-E country-year raw-file paths.""" - return [ - "/automatic/entsoe/raw/" f"{country}/{int(year)}.parquet" - for country in countries - for year in years - ] - - -def entsoe_raw_files(wildcards): - """Return ENTSO-E country-year files required for one target shape.""" - plan = read_target_data_plan(wildcards) - - countries = plan["source_contexts"].get("entsoe", []) - - years = years_for_period( - target_source_start( - wildcards, - "entsoe", - ), - target_source_end( - wildcards, - "entsoe", - ), - ) - - return entsoe_annual_files(countries, years) - - -def target_entsoe_countries(wildcards): - """Return ENTSO-E target countries for one shape.""" - return target_source_contexts(wildcards, "entsoe") - - -def auxiliary_entsoe_raw_files(wildcards): - """Return ENTSO-E country-year files required by one auxiliary batch.""" - plan = _read_auxiliary_plan(wildcards) - - batch = next( - batch - for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") - ) - - return entsoe_annual_files(batch["countries"], batch["years"]) - - rule download_load_entsoe_country_year: input: token_entsoe="", diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index dd9ba1d..869400c 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -1,46 +1,6 @@ """Rules for the ENTSO-E Power Statistics demand source.""" -def entsoe_power_statistics_annual_files(years): - """Return annual ENTSO-E Power Statistics files.""" - return [ - ("/automatic/" "entsoe_power_statistics/raw/" f"{int(year)}.parquet") - for year in years - ] - - -def entsoe_power_statistics_raw_files(wildcards): - """Return Power Statistics annual files required by the target period.""" - years = years_for_period( - target_source_start( - wildcards, - "entsoe_power_statistics", - ), - target_source_end( - wildcards, - "entsoe_power_statistics", - ), - ) - - return entsoe_power_statistics_annual_files(years) - - -def auxiliary_entsoe_power_statistics_raw_files(wildcards): - """Return Power Statistics annual files required by one auxiliary batch.""" - plan = _read_auxiliary_plan(wildcards) - - batch = next( - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == wildcards.batch_id - and batch["source"] == "entsoe_power_statistics" - ) - ) - - return entsoe_power_statistics_annual_files(batch["years"]) - - rule download_load_entsoe_power_statistics_year: output: annual_file=("/automatic/entsoe_power_statistics/raw/{year}.parquet"), @@ -62,14 +22,6 @@ rule download_load_entsoe_power_statistics_year: "../scripts/download_load_entsoe_power_statistics.py" -def target_entsoe_power_statistics_countries(wildcards): - """Return Power Statistics target countries for one shape.""" - return target_source_contexts( - wildcards, - "entsoe_power_statistics", - ) - - rule prepare_load_entsoe_power_statistics: input: validation=("/automatic/" "temporal_config_validation.json"), diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index 5d0f7b2..2ad0a26 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -1,43 +1,6 @@ """Rules for the NESO historic demand source.""" -def neso_annual_files(years): - """Return reusable annual NESO raw-file paths for the requested years.""" - return [ - "/automatic/neso/" f"historic_demand_{int(year)}.csv" - for year in years - ] - - -def neso_raw_files(wildcards): - """Return annual NESO input files for the planned target period.""" - years = years_for_period( - target_source_start( - wildcards, - "neso", - ), - target_source_end( - wildcards, - "neso", - ), - ) - - return neso_annual_files(years) - - -def auxiliary_neso_raw_files(wildcards): - """Return annual NESO files required by one auxiliary batch.""" - plan = _read_auxiliary_plan(wildcards) - - batch = next( - batch - for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") - ) - - return neso_annual_files(batch["years"]) - - rule download_load_neso_year: output: annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), @@ -57,14 +20,6 @@ rule download_load_neso_year: "../scripts/download_load_neso.py" -def target_neso_countries(wildcards): - """Return NESO target countries for one shape.""" - return target_source_contexts( - wildcards, - "neso", - ) - - rule prepare_load_neso: input: validation="/automatic/temporal_config_validation.json", diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index 52b0e58..b75a439 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -17,14 +17,6 @@ rule download_load_opsd: "../scripts/download_load_opsd.py" -def target_opsd_countries(wildcards): - """Return OPSD target countries for one shape.""" - return target_source_contexts( - wildcards, - "opsd", - ) - - rule prepare_load_opsd: input: validation="/automatic/temporal_config_validation.json", From f9baddb85ea29876d8a1ab19d281be83e13620a7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:23:17 +0000 Subject: [PATCH 205/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/rules/_source_helpers.smk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workflow/rules/_source_helpers.smk b/workflow/rules/_source_helpers.smk index 9ebe747..8c2a456 100644 --- a/workflow/rules/_source_helpers.smk +++ b/workflow/rules/_source_helpers.smk @@ -2,6 +2,7 @@ # ENTSOE POWER STATISTICS + def entsoe_power_statistics_annual_files(years): """Return annual ENTSO-E Power Statistics files.""" return [ @@ -52,6 +53,7 @@ def target_entsoe_power_statistics_countries(wildcards): # ENTSOE + def entsoe_annual_files(countries, years): """Return reusable ENTSO-E country-year raw-file paths.""" return [ @@ -155,4 +157,4 @@ def target_opsd_countries(wildcards): return target_source_contexts( wildcards, "opsd", - ) \ No newline at end of file + ) From 72f70003edd14ff8300eadbea870b11f0a7f715a Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 3 Sep 2026 20:52:22 +0200 Subject: [PATCH 206/241] Added source_specific resource constraints to protect users from rate-limits / API bans. --- workflow/rules/source_entsoe.smk | 10 ++++++++++ workflow/rules/source_neso.smk | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index feee3f0..f9afb1f 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -1,5 +1,15 @@ """Rules for the ENTSO-E Transparency Platform demand source.""" +# Protect the user against ENTSO-E Rate limits. ENTSOE rate limits are set +# to 400calls/min. Given each download rule makes 12 API calls, we apply a +# concurrency cap of 16 entsoe download jobs (16*12=196). The user may +# specify a different value which the conditional statement respects. +if "entsoe_download" not in workflow.global_resources: + workflow.register_resource( + "entsoe_download", + 16, + ) + rule download_load_entsoe_country_year: input: diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index 2ad0a26..e7eafc9 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -1,6 +1,17 @@ """Rules for the NESO historic demand source.""" +# Protect the user against NESO Rate limits. NESO asks for users to respect +# a 2call/min limit. We cannot implement such a limit specifically but we can +# constrain usage to 2 concurrent downloads. The user may +# specify a different value which the conditional statement respects. +if "neso_download" not in workflow.global_resources: + workflow.register_resource( + "neso_download", + 2, + ) + + rule download_load_neso_year: output: annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), From eebbd4c43fb25247414045d30a00cd365f581e41 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:53:45 +0000 Subject: [PATCH 207/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/rules/source_entsoe.smk | 4 ++-- workflow/rules/source_neso.smk | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index f9afb1f..d7a3b30 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -1,8 +1,8 @@ """Rules for the ENTSO-E Transparency Platform demand source.""" -# Protect the user against ENTSO-E Rate limits. ENTSOE rate limits are set +# Protect the user against ENTSO-E Rate limits. ENTSOE rate limits are set # to 400calls/min. Given each download rule makes 12 API calls, we apply a -# concurrency cap of 16 entsoe download jobs (16*12=196). The user may +# concurrency cap of 16 entsoe download jobs (16*12=196). The user may # specify a different value which the conditional statement respects. if "entsoe_download" not in workflow.global_resources: workflow.register_resource( diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index e7eafc9..fb503c3 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -1,9 +1,8 @@ """Rules for the NESO historic demand source.""" - # Protect the user against NESO Rate limits. NESO asks for users to respect # a 2call/min limit. We cannot implement such a limit specifically but we can -# constrain usage to 2 concurrent downloads. The user may +# constrain usage to 2 concurrent downloads. The user may # specify a different value which the conditional statement respects. if "neso_download" not in workflow.global_resources: workflow.register_resource( From 2b1331963358ddc45a3c0303dcc659dd3160ee97 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 10:08:55 +0200 Subject: [PATCH 208/241] Removed odd strings. --- workflow/rules/_source_helpers.smk | 3 ++- workflow/rules/advanced.smk | 9 +++++---- workflow/rules/automatic.smk | 3 ++- workflow/rules/basic.smk | 19 ++++++++++--------- workflow/rules/prepare_demand.smk | 7 ++++--- workflow/rules/source_entsoe.smk | 5 +++-- .../rules/source_entsoe_power_statistics.smk | 7 ++++--- workflow/rules/source_neso.smk | 5 +++-- workflow/rules/source_opsd.smk | 3 ++- 9 files changed, 35 insertions(+), 26 deletions(-) diff --git a/workflow/rules/_source_helpers.smk b/workflow/rules/_source_helpers.smk index 8c2a456..4bee9b7 100644 --- a/workflow/rules/_source_helpers.smk +++ b/workflow/rules/_source_helpers.smk @@ -6,7 +6,7 @@ def entsoe_power_statistics_annual_files(years): """Return annual ENTSO-E Power Statistics files.""" return [ - ("/automatic/" "entsoe_power_statistics/raw/" f"{int(year)}.parquet") + ("/automatic/entsoe_power_statistics/raw/" f"{int(year)}.parquet") for year in years ] @@ -158,3 +158,4 @@ def target_opsd_countries(wildcards): wildcards, "opsd", ) + diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 80e488c..821db92 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -2,7 +2,7 @@ checkpoint plan_auxiliary_data: input: demand=rules.clean_demand.output.demand, output: - plan=("/automatic/{shape}/" "auxiliary/advanced_execution_plan.json"), + plan=("/automatic/{shape}/auxiliary/advanced_execution_plan.json"), log: "/{shape}/auxiliary/plan_auxiliary_data.log", conda: @@ -24,7 +24,7 @@ rule clean_auxiliary_group: sources=auxiliary_group_source_files, output: demand=( - "/automatic/{shape}/" "auxiliary/cleaned/" "{group_id}.parquet" + "/automatic/{shape}/auxiliary/cleaned/{group_id}.parquet" ), data_source=( "/automatic/{shape}/" @@ -86,9 +86,9 @@ rule apply_advanced_overrides: constructed_profiles=advanced_constructed_profiles, external_profiles=advanced_external_profile_files, output: - demand=("/automatic/{shape}/" "load_advanced_cleaned.parquet"), + demand=("/automatic/{shape}/load_advanced_cleaned.parquet"), cleaning_method=( - "/automatic/{shape}/" "load_advanced_cleaning_method.parquet" + "/automatic/{shape}/load_advanced_cleaning_method.parquet" ), log: "/{shape}/auxiliary/apply_advanced_overrides.log", @@ -100,3 +100,4 @@ rule apply_advanced_overrides: "Apply advanced electricity-demand overrides." script: "../scripts/apply_advanced_overrides.py" + diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 69ccff5..70f2f95 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -43,7 +43,7 @@ checkpoint plan_target_data: shapes="", temporal_validation=("/automatic/temporal_config_validation.json"), output: - plan=("/automatic/{shape}/" "target_data_plan.json"), + plan=("/automatic/{shape}/target_data_plan.json"), log: "/{shape}/plan_target_data.log", conda: @@ -89,3 +89,4 @@ rule unzip_population: "Unzip population data." wrapper: "v9.8.0/utils/libarchive/extract" + diff --git a/workflow/rules/basic.smk b/workflow/rules/basic.smk index 55b07df..71c77c3 100644 --- a/workflow/rules/basic.smk +++ b/workflow/rules/basic.smk @@ -4,15 +4,15 @@ rule clean_demand: target_plan=target_data_plan, validation="/automatic/gap_filling_config_validation.json", output: - demand=("/automatic/{shape}/" "load_basic_cleaned.parquet"), - data_source=("/automatic/{shape}/" "load_data_source.parquet"), + demand=("/automatic/{shape}/load_basic_cleaned.parquet"), + data_source=("/automatic/{shape}/load_data_source.parquet"), cleaning_method=( - "/automatic/{shape}/" "load_cleaning_method.parquet" + "/automatic/{shape}/load_cleaning_method.parquet" ), cleaning_method_rank=( - "/automatic/{shape}/" "load_cleaning_method_rank.parquet" + "/automatic/{shape}/load_cleaning_method_rank.parquet" ), - gap_report=("/automatic/{shape}/" "load_gap_report.parquet"), + gap_report=("/automatic/{shape}/load_gap_report.parquet"), log: "/{shape}/clean_demand.log", conda: @@ -30,15 +30,15 @@ rule clean_demand: rule plot_cleaning_timeline: input: target_plan=target_data_plan, - demand=("/automatic/{shape}/" "load_cleaned.parquet"), + demand=("/automatic/{shape}/load_cleaned.parquet"), basic_cleaning_method=( - "/automatic/{shape}/" "load_cleaning_method.parquet" + "/automatic/{shape}/load_cleaning_method.parquet" ), cleaning_method=( - "/automatic/{shape}/" "load_final_cleaning_method.parquet" + "/automatic/{shape}/load_final_cleaning_method.parquet" ), cleaning_method_rank=( - "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" + "/automatic/{shape}/load_final_cleaning_method_rank.parquet" ), output: plot="/{shape}/load_cleaning_timeline.pdf", @@ -67,3 +67,4 @@ rule clean_population: "/{shape}/clean_population.log", wrapper: "v7.2.0/geo/rasterio/clip-geotiff" + diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index c40c00f..264ae5c 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -4,12 +4,12 @@ rule finalise_clean_demand: demand=final_clean_demand_input, cleaning_method=final_cleaning_method_input, output: - demand=("/automatic/{shape}/" "load_cleaned.parquet"), + demand=("/automatic/{shape}/load_cleaned.parquet"), cleaning_method=( - "/automatic/{shape}/" "load_final_cleaning_method.parquet" + "/automatic/{shape}/load_final_cleaning_method.parquet" ), cleaning_method_rank=( - "/automatic/{shape}/" "load_final_cleaning_method_rank.parquet" + "/automatic/{shape}/load_final_cleaning_method_rank.parquet" ), log: "/{shape}/finalise_clean_demand.log", @@ -61,3 +61,4 @@ rule demand_electricity_polygon: "Aggregate annual demand to shapes and scale with profile." script: "../scripts/demand_electricity_polygon.py" + diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index d7a3b30..bb204ec 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -15,7 +15,7 @@ rule download_load_entsoe_country_year: input: token_entsoe="", output: - annual_file=("/automatic/entsoe/raw/" "{country}/{year}.parquet"), + annual_file=("/automatic/entsoe/raw/{country}/{year}.parquet"), log: "/download_load_entsoe_{country}_{year}.log", wildcard_constraints: @@ -73,7 +73,7 @@ rule prepare_auxiliary_load_entsoe: plan=auxiliary_acquisition_plan, annual_files=auxiliary_entsoe_raw_files, output: - load=("/automatic/{shape}/" "auxiliary/entsoe/" "{batch_id}.parquet"), + load=("/automatic/{shape}/auxiliary/entsoe/{batch_id}.parquet"), log: "/{shape}/auxiliary/entsoe/prepare_{batch_id}.log", conda: @@ -84,3 +84,4 @@ rule prepare_auxiliary_load_entsoe: "Prepare auxiliary electricity-demand data from ENTSO-E." script: "../scripts/prepare_load_entsoe.py" + diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index 869400c..bf964e8 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -24,11 +24,11 @@ rule download_load_entsoe_power_statistics_year: rule prepare_load_entsoe_power_statistics: input: - validation=("/automatic/" "temporal_config_validation.json"), + validation=("/automatic/temporal_config_validation.json"), target_plan=target_data_plan, annual_files=entsoe_power_statistics_raw_files, output: - load=("/automatic/{shape}/" "load_entsoe_power_statistics.parquet"), + load=("/automatic/{shape}/load_entsoe_power_statistics.parquet"), log: "/{shape}/prepare_load_entsoe_power_statistics.log", conda: @@ -61,7 +61,7 @@ rule prepare_auxiliary_load_entsoe_power_statistics: "{batch_id}.parquet" ), log: - "/{shape}/auxiliary/" "entsoe_power_statistics/" "prepare_{batch_id}.log", + "/{shape}/auxiliary/entsoe_power_statistics/prepare_{batch_id}.log", conda: "../envs/module.yaml" params: @@ -70,3 +70,4 @@ rule prepare_auxiliary_load_entsoe_power_statistics: ("Prepare auxiliary electricity-demand data " "from ENTSO-E Power Statistics.") script: "../scripts/prepare_load_entsoe_power_statistics.py" + diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index fb503c3..d8fd0e2 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -13,7 +13,7 @@ if "neso_download" not in workflow.global_resources: rule download_load_neso_year: output: - annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), + annual_file=("/automatic/neso/historic_demand_{year}.csv"), log: "/download_load_neso_{year}.log", localrule: True @@ -63,7 +63,7 @@ rule prepare_auxiliary_load_neso: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=("/automatic/{shape}/" "auxiliary/neso/{batch_id}.parquet"), + load=("/automatic/{shape}/auxiliary/neso/{batch_id}.parquet"), log: "/{shape}/auxiliary/neso/{batch_id}.log", conda: @@ -74,3 +74,4 @@ rule prepare_auxiliary_load_neso: "Prepare auxiliary electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" + diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index b75a439..127ddad 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -50,7 +50,7 @@ rule prepare_auxiliary_load_opsd: load=rules.download_load_opsd.output.load, plan=auxiliary_acquisition_plan, output: - load=("/automatic/{shape}/" "auxiliary/opsd/" "{batch_id}.parquet"), + load=("/automatic/{shape}/auxiliary/opsd/{batch_id}.parquet"), log: "/{shape}/auxiliary/opsd/{batch_id}.log", conda: @@ -61,3 +61,4 @@ rule prepare_auxiliary_load_opsd: "Prepare auxiliary electricity-demand data from OPSD." script: "../scripts/prepare_load_opsd.py" + From 7696b6a5412f4eb6c3f3ffb3b8197d37d15ac3cf Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 10:17:57 +0200 Subject: [PATCH 209/241] Removed unecessary lambda functions from .smk files --- workflow/rules/source_entsoe.smk | 3 --- workflow/rules/source_entsoe_power_statistics.smk | 2 -- workflow/rules/source_neso.smk | 2 -- workflow/scripts/download_load_entsoe.py | 4 ++-- workflow/scripts/download_load_entsoe_power_statistics.py | 2 +- workflow/scripts/download_load_neso.py | 2 +- 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index bb204ec..286c15a 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -27,9 +27,6 @@ rule download_load_entsoe_country_year: threads: 1 resources: entsoe_download=1, - params: - country_code=lambda wildcards: wildcards.country, - year=lambda wildcards: int(wildcards.year), message: ( "Download ENTSO-E electricity load for " diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index bf964e8..2f88166 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -14,8 +14,6 @@ rule download_load_entsoe_power_statistics_year: threads: 1 resources: entsoe_download=1, - params: - year=lambda wildcards: int(wildcards.year), message: ("Download ENTSO-E Power Statistics " "electricity load for {wildcards.year}.") script: diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index d8fd0e2..bc9a729 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -22,8 +22,6 @@ rule download_load_neso_year: threads: 1 resources: neso_download=1, - params: - year=lambda wildcards: int(wildcards.year), message: "Download NESO historic electricity demand for {wildcards.year}." script: diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index 302534a..4cf2738 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -13,8 +13,8 @@ def main(snakemake: Any) -> None: """Download one UTC calendar year of ENTSO-E data for one country.""" - year = int(snakemake.params.year) - country_code = str(snakemake.params.country_code) + year = int(snakemake.wildcards.year) + country_code = str(snakemake.wildcards.country) start = pd.Timestamp(year=year, month=1, day=1, tz="UTC") end = pd.Timestamp(year=year + 1, month=1, day=1, tz="UTC") diff --git a/workflow/scripts/download_load_entsoe_power_statistics.py b/workflow/scripts/download_load_entsoe_power_statistics.py index ce3eb68..a58e6f7 100644 --- a/workflow/scripts/download_load_entsoe_power_statistics.py +++ b/workflow/scripts/download_load_entsoe_power_statistics.py @@ -16,7 +16,7 @@ def main(snakemake: Any) -> None: """Download and harmonise one ENTSO-E Power Statistics year.""" download_entsoe_power_statistics_year( - year=int(snakemake.params.year), output_path=Path(snakemake.output.annual_file) + year=int(snakemake.wildcards.year), output_path=Path(snakemake.output.annual_file) ) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 9e2820b..90f2167 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -13,7 +13,7 @@ def main(snakemake: Any) -> None: """Download one annual NESO historic-demand file.""" download_annual_file( - year=int(snakemake.params.year), output_path=snakemake.output.annual_file + year=int(snakemake.wildcards.year), output_path=snakemake.output.annual_file ) From 345750abeb1a5f9780cc8103b7cddbedda1d6446 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:18:13 +0000 Subject: [PATCH 210/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/rules/_source_helpers.smk | 1 - workflow/rules/advanced.smk | 5 +---- workflow/rules/automatic.smk | 1 - workflow/rules/basic.smk | 5 +---- workflow/rules/prepare_demand.smk | 1 - workflow/rules/source_entsoe.smk | 1 - workflow/rules/source_entsoe_power_statistics.smk | 1 - workflow/rules/source_neso.smk | 1 - workflow/rules/source_opsd.smk | 1 - workflow/scripts/download_load_entsoe_power_statistics.py | 3 ++- 10 files changed, 4 insertions(+), 16 deletions(-) diff --git a/workflow/rules/_source_helpers.smk b/workflow/rules/_source_helpers.smk index 4bee9b7..e0e692b 100644 --- a/workflow/rules/_source_helpers.smk +++ b/workflow/rules/_source_helpers.smk @@ -158,4 +158,3 @@ def target_opsd_countries(wildcards): wildcards, "opsd", ) - diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 821db92..cf066de 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -23,9 +23,7 @@ rule clean_auxiliary_group: plan=auxiliary_acquisition_plan, sources=auxiliary_group_source_files, output: - demand=( - "/automatic/{shape}/auxiliary/cleaned/{group_id}.parquet" - ), + demand=("/automatic/{shape}/auxiliary/cleaned/{group_id}.parquet"), data_source=( "/automatic/{shape}/" "auxiliary/cleaned/" @@ -100,4 +98,3 @@ rule apply_advanced_overrides: "Apply advanced electricity-demand overrides." script: "../scripts/apply_advanced_overrides.py" - diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 70f2f95..99d34e9 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -89,4 +89,3 @@ rule unzip_population: "Unzip population data." wrapper: "v9.8.0/utils/libarchive/extract" - diff --git a/workflow/rules/basic.smk b/workflow/rules/basic.smk index 71c77c3..fbe655b 100644 --- a/workflow/rules/basic.smk +++ b/workflow/rules/basic.smk @@ -6,9 +6,7 @@ rule clean_demand: output: demand=("/automatic/{shape}/load_basic_cleaned.parquet"), data_source=("/automatic/{shape}/load_data_source.parquet"), - cleaning_method=( - "/automatic/{shape}/load_cleaning_method.parquet" - ), + cleaning_method=("/automatic/{shape}/load_cleaning_method.parquet"), cleaning_method_rank=( "/automatic/{shape}/load_cleaning_method_rank.parquet" ), @@ -67,4 +65,3 @@ rule clean_population: "/{shape}/clean_population.log", wrapper: "v7.2.0/geo/rasterio/clip-geotiff" - diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 264ae5c..4acffb4 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -61,4 +61,3 @@ rule demand_electricity_polygon: "Aggregate annual demand to shapes and scale with profile." script: "../scripts/demand_electricity_polygon.py" - diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 286c15a..a41869a 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -81,4 +81,3 @@ rule prepare_auxiliary_load_entsoe: "Prepare auxiliary electricity-demand data from ENTSO-E." script: "../scripts/prepare_load_entsoe.py" - diff --git a/workflow/rules/source_entsoe_power_statistics.smk b/workflow/rules/source_entsoe_power_statistics.smk index 2f88166..dc5cd27 100644 --- a/workflow/rules/source_entsoe_power_statistics.smk +++ b/workflow/rules/source_entsoe_power_statistics.smk @@ -68,4 +68,3 @@ rule prepare_auxiliary_load_entsoe_power_statistics: ("Prepare auxiliary electricity-demand data " "from ENTSO-E Power Statistics.") script: "../scripts/prepare_load_entsoe_power_statistics.py" - diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index bc9a729..abb8471 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -72,4 +72,3 @@ rule prepare_auxiliary_load_neso: "Prepare auxiliary electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" - diff --git a/workflow/rules/source_opsd.smk b/workflow/rules/source_opsd.smk index 127ddad..94fba45 100644 --- a/workflow/rules/source_opsd.smk +++ b/workflow/rules/source_opsd.smk @@ -61,4 +61,3 @@ rule prepare_auxiliary_load_opsd: "Prepare auxiliary electricity-demand data from OPSD." script: "../scripts/prepare_load_opsd.py" - diff --git a/workflow/scripts/download_load_entsoe_power_statistics.py b/workflow/scripts/download_load_entsoe_power_statistics.py index a58e6f7..84d2384 100644 --- a/workflow/scripts/download_load_entsoe_power_statistics.py +++ b/workflow/scripts/download_load_entsoe_power_statistics.py @@ -16,7 +16,8 @@ def main(snakemake: Any) -> None: """Download and harmonise one ENTSO-E Power Statistics year.""" download_entsoe_power_statistics_year( - year=int(snakemake.wildcards.year), output_path=Path(snakemake.output.annual_file) + year=int(snakemake.wildcards.year), + output_path=Path(snakemake.output.annual_file), ) From 8ae63b861da9b29afb3a0cd6155d9ce5f57ec779 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 10:56:05 +0200 Subject: [PATCH 211/241] Updated resource limits for neso and entsoe --- workflow/rules/source_entsoe.smk | 15 ++++++++------- workflow/rules/source_neso.smk | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index a41869a..7008654 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -1,13 +1,13 @@ """Rules for the ENTSO-E Transparency Platform demand source.""" # Protect the user against ENTSO-E Rate limits. ENTSOE rate limits are set -# to 400calls/min. Given each download rule makes 12 API calls, we apply a -# concurrency cap of 16 entsoe download jobs (16*12=196). The user may -# specify a different value which the conditional statement respects. -if "entsoe_download" not in workflow.global_resources: +# to 400calls/min. Given each download rule makes 12 API calls and resolves +# in circa 10-30seconds, we apply a conservative cap of 10 entsoe download jobs (10*12=120). +# The user may specify a different value which the conditional statement respects. +if "module_demand_electricity_entsoe_api" not in workflow.global_resources: workflow.register_resource( - "entsoe_download", - 16, + "module_demand_electricity_entsoe_api", + 120, ) @@ -26,7 +26,8 @@ rule download_load_entsoe_country_year: "../envs/module.yaml" threads: 1 resources: - entsoe_download=1, + # Define the resource cost as the number of API calls. + module_demand_electricity_entsoe_api=12, message: ( "Download ENTSO-E electricity load for " diff --git a/workflow/rules/source_neso.smk b/workflow/rules/source_neso.smk index abb8471..68398ac 100644 --- a/workflow/rules/source_neso.smk +++ b/workflow/rules/source_neso.smk @@ -4,9 +4,9 @@ # a 2call/min limit. We cannot implement such a limit specifically but we can # constrain usage to 2 concurrent downloads. The user may # specify a different value which the conditional statement respects. -if "neso_download" not in workflow.global_resources: +if "module_demand_electricity_neso_api" not in workflow.global_resources: workflow.register_resource( - "neso_download", + "module_demand_electricity_neso_api", 2, ) @@ -21,7 +21,7 @@ rule download_load_neso_year: "../envs/module.yaml" threads: 1 resources: - neso_download=1, + module_demand_electricity_neso_api=1, message: "Download NESO historic electricity demand for {wildcards.year}." script: From f3f261714e679b12a32937690af3c3055582f626 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 08:56:22 +0000 Subject: [PATCH 212/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/rules/source_entsoe.smk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/rules/source_entsoe.smk b/workflow/rules/source_entsoe.smk index 7008654..03bc4d1 100644 --- a/workflow/rules/source_entsoe.smk +++ b/workflow/rules/source_entsoe.smk @@ -1,8 +1,8 @@ """Rules for the ENTSO-E Transparency Platform demand source.""" # Protect the user against ENTSO-E Rate limits. ENTSOE rate limits are set -# to 400calls/min. Given each download rule makes 12 API calls and resolves -# in circa 10-30seconds, we apply a conservative cap of 10 entsoe download jobs (10*12=120). +# to 400calls/min. Given each download rule makes 12 API calls and resolves +# in circa 10-30seconds, we apply a conservative cap of 10 entsoe download jobs (10*12=120). # The user may specify a different value which the conditional statement respects. if "module_demand_electricity_entsoe_api" not in workflow.global_resources: workflow.register_resource( From daa253c0592ace3eed35a9a4845e283d7997421d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 12:20:15 +0200 Subject: [PATCH 213/241] Updated source registry for OPSD which does cover Iceland. --- workflow/internal/source_registry.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index 52c1b25..7e0ccd6 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -137,6 +137,7 @@ opsd: - "HRV" - "HUN" - "IRL" + - "ISL" - "ITA" - "LTU" - "LUX" From 9109a447b685447ac8fac3aed01bb076eb4811d5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 13:44:41 +0200 Subject: [PATCH 214/241] Designed the e2e local test --- tests/local/Snakefile | 35 ++++++ tests/local/local_test_config.yaml | 180 +++++++++++++++++++++++++++++ tests/local_test.py | 21 ++-- 3 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 tests/local/Snakefile create mode 100644 tests/local/local_test_config.yaml diff --git a/tests/local/Snakefile b/tests/local/Snakefile new file mode 100644 index 0000000..a8edcda --- /dev/null +++ b/tests/local/Snakefile @@ -0,0 +1,35 @@ +# Emulate a user configuring the module. +configfile: workflow.source_path("./local_test_config.yaml") + + +# Import the module and configure it. +# `snakefile:` specifies the module. It can use file paths and special github(...) / gitlab(...) markers +# `pathvars:` re-wires the location of user resources, intermediary module files, and module results. +module module_demand_electricity: + pathvars: + shapes="../../resources/user/{shape}/shapes.parquet", + token_entsoe="../../resources/user/token_entsoe.txt", + external_profiles="../../resources/user/external_profiles", + # test the re-routing of resources, results and logs + resources="resources/module/resources", + results="resources/module/results", + logs="resources/module/logs", + snakefile: + "../../workflow/Snakefile" + config: + config["integration_test"] + + +# rename all module rules with a prefix, to avoid naming conflicts. +use rule * from module_demand_electricity as module_demand_electricity_* + + +# Montenegro test case +rule local_end_to_end_testing: + default_target: True + input: + "resources/module/results/EUROPE_L_C34_ADM1/demand_electricity_MW.parquet", + "resources/module/results/EUROPE_L_C34_ADM1/load_cleaning_timeline.pdf", + "resources/module/results/EUROPE_L_C34_ADM1/load_cleaning_summary.html", + message: + "A comprehensive e2e test case for 34 European countries" diff --git a/tests/local/local_test_config.yaml b/tests/local/local_test_config.yaml new file mode 100644 index 0000000..6654663 --- /dev/null +++ b/tests/local/local_test_config.yaml @@ -0,0 +1,180 @@ +integration_test: + # The integration test uses a deliberately short hourly period to keep + # provider downloads and workflow execution reasonably small. + temporal_scope: + start: "2017-01-01" + end: "2023-01-01" + frequency: "1h" + + # Load sources are listed in priority order. Where multiple sources provide + # data for the same country and timestamp, the earlier source takes priority. + # Supported sources include: entsoe, neso, opsd. + load_sources: + - neso + - opsd + - entsoe + - entsoe_power_statistics + - orkustofnun + + # Exercise the full advanced gap-filling workflow. See config/README.md for + # the complete configuration reference and method descriptions. + gap_filling: + mode: "advanced" + + basic: + rules: + # Fill short internal gaps by linear interpolation. + - name: interpolate_short_gaps + method: linear_interpolation + max_gap: 3h + + # Fill longer gaps using the mean of the corresponding periods one + # week before and one week after the missing period. + - name: average_adjacent_weeks + method: average_periods + max_gap: 326h + source_offsets: + - -7d + - 7d + + # If averaging cannot fill the gap, try copying the corresponding + # period from the previous week. + - name: copy_previous_week + method: copy_periods + max_gap: 168h + source_offset: -168h + require_complete_source: true + + # Finally, try copying the corresponding period from the following + # week when a complete source period is available. + - name: copy_following_week + method: copy_periods + max_gap: 168h + source_offset: 168h + require_complete_source: true + + advanced: + auxiliary_data: + # Apply the same basic cleaning rules to acquired auxiliary data before + # it is used to construct advanced replacement profiles. + basic_cleaning: + enabled: true + + sources: + # Construct an Albanian replacement profile from a winter period of + # British data, then scale it to match Albanian energy over the + # specified scaling period. This source is defined to exercise + # cross-country construction and scaling. + alb_from_gbr_alb_winter: + method: construct_from_sources + periods: + - country: GBR + start: "2024-01-01" + end: "2024-02-01" + weight: 1 + scaling: + method: match_total + periods: + - country: ALB + start: "2024-02-01" + end: "2024-03-01" + weight: 1 + + # Construct an Albanian profile from a later period of Albanian data. + # The corresponding rule below overwrites the integration-test target + # period, ensuring construction, auxiliary acquisition, scaling, and + # overwrite behavior are exercised end to end. + alb_from_alb_2024: + method: construct_from_sources + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-03" + weight: 1 + scaling: + method: match_total + periods: + - country: ALB + start: "2024-02-01" + end: "2024-03-01" + weight: 1 + + # Construct a second Albanian profile from the same 2024 source period, + # normalised so that its arithmetic mean equals one. This exercises + # profile normalisation without requiring additional scaling data. + alb_from_alb_2024_normalised: + method: construct_from_sources + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-02" + weight: 1 + scaling: + method: normalise_mean + + # Define a Montenegro profile constructed from Serbian data. Its rule + # deliberately lies outside the target temporal scope, testing that + # inactive advanced rules do not trigger unnecessary acquisition. + mne_from_srb: + method: construct_from_sources + periods: + - country: SRB + start: "2022-03-01" + end: "2022-04-01" + weight: 1 + + # Read a small committed CSV fixture to exercise the external_profile + # source type and external_profiles pathvar resolution. + alb_external_test: + method: external_profile + file: alb_external_test.csv + + rules: + # Fill Albanian gaps in January 2022 using the cross-country constructed + # winter profile. This rule is outside the integration-test target + # period and therefore tests inactive-rule selection. + - name: build_alb_from_gbr_alb_winter + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + source: alb_from_gbr_alb_winter + + # Overwrite the full Albanian integration-test period using the + # constructed 2024 Albanian profile. This is the main active + # construct_from_sources integration case. + - name: build_alb_from_alb_2024 + country: ALB + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024 + + # Overwrite one target hour with a mean-normalised constructed profile. + # This exercises normalise_mean end to end while leaving the main + # match_total construction case in place. + - name: test_normalise_mean + country: ALB + start: "2017-01-02" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024_normalised + + # Deliberately inactive rule: both its target period and source data are + # outside the integration-test scope. It verifies that an inactive rule + # does not create unnecessary auxiliary acquisition jobs. + - name: example_inactive_mne_override + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps + source: mne_from_srb + + # Overwrite the first three Albanian hours from the external CSV fixture, + # exercising external_profile loading and overwrite application. + - name: test_external_profile_overwrite + country: ALB + start: "2017-01-01 00:00" + end: "2017-01-01 03:00" + scope: overwrite + source: alb_external_test diff --git a/tests/local_test.py b/tests/local_test.py index 9207217..5616066 100644 --- a/tests/local_test.py +++ b/tests/local_test.py @@ -1,14 +1,21 @@ """Tests to be executed locally, as they are more computationally intense.""" import subprocess +from pathlib import Path +LOCAL_TEST_PATH = Path(__file__).parent / "local" -def test_europe_nuts2(user_path): - """Test that the Europe NUTS2 shapes are correct.""" - target = "results/EUROPE_L_C34_ADM1/demand_electricity_MW.parquet" - assert subprocess.run( - f"snakemake --use-conda --cores 4 --forceall {target}", - shell=True, + +def test_europe_end_to_end(user_path: Path, token_entsoe: Path): + """Run a full European end-to-end electricity-demand test.""" + subprocess.run( + [ + "snakemake", + "--use-conda", + "--cores", + "8", + "--forceall", + ], check=True, - cwd=user_path.parent.parent, + cwd=LOCAL_TEST_PATH, ) From 829a34bb33b7e7f5ef047bb0177480c982af50b2 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 14:08:24 +0200 Subject: [PATCH 215/241] Added a curated source to extend ISL coverage to October 2024 with lots of gaps. --- workflow/internal/settings.yaml | 1 + workflow/internal/source_registry.yaml | 15 ++++ workflow/rules/source_orkustofnun.smk | 72 +++++++++++++++++++ workflow/scripts/download_load_orkustofnun.py | 23 ++++++ workflow/scripts/prepare_load_orkustofnun.py | 53 ++++++++++++++ .../scripts/sources/orkustofnun/download.py | 22 ++++++ .../scripts/sources/orkustofnun/prepare.py | 71 ++++++++++++++++++ 7 files changed, 257 insertions(+) create mode 100644 workflow/rules/source_orkustofnun.smk create mode 100644 workflow/scripts/download_load_orkustofnun.py create mode 100644 workflow/scripts/prepare_load_orkustofnun.py create mode 100644 workflow/scripts/sources/orkustofnun/download.py create mode 100644 workflow/scripts/sources/orkustofnun/prepare.py diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index b3b1857..ec255ca 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -3,5 +3,6 @@ resources: automatic: # Links for automatically downloaded files load_opsd: "https://data.open-power-system-data.org/time_series/2019-06-05/time_series_60min_stacked.csv" + load_orkustofnun: "https://zenodo.org/records/22304076/files/isl_load.parquet?download=1" population: "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/GHSL/GHS_POP_GLOBE_R2023A/GHS_POP_E2020_GLOBE_R2023A_54009_1000/V1-0/GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.zip" population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index 7e0ccd6..5b2c04e 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -159,3 +159,18 @@ opsd: python: - "scripts/sources/opsd/download.py" - "scripts/sources/opsd/prepare.py" + + +orkustofnun: + display_name: Orkustofnun + contexts: + - ISL + temporal_scope: + start: "2019-01-01" + end: "2026-01-01" + workflow: + rules: + - "rules/source_orkustofnun.smk" + python: + - "scripts/sources/orkustofnun/download.py" + - "scripts/sources/orkustofnun/prepare.py" \ No newline at end of file diff --git a/workflow/rules/source_orkustofnun.smk b/workflow/rules/source_orkustofnun.smk new file mode 100644 index 0000000..530d488 --- /dev/null +++ b/workflow/rules/source_orkustofnun.smk @@ -0,0 +1,72 @@ +"""Rules for Icelandic electricity demand from Orkustofnun.""" + + +rule download_load_orkustofnun: + output: + load=update( + "/automatic/orkustofnun/raw_load.parquet" + ), + log: + "/download_load_orkustofnun.log", + localrule: True + conda: + "../envs/module.yaml" + params: + url=internal["resources"]["automatic"]["load_orkustofnun"], + message: + "Download curated Icelandic electricity-demand data from Orkustofnun." + script: + "../scripts/download_load_orkustofnun.py" + + +rule prepare_load_orkustofnun: + input: + validation="/automatic/temporal_config_validation.json", + target_plan=target_data_plan, + load="/automatic/orkustofnun/raw_load.parquet", + output: + load=( + "/automatic/{shape}/load_orkustofnun.parquet" + ), + log: + "/{shape}/prepare_load_orkustofnun.log", + conda: + "../envs/module.yaml" + params: + start=lambda wildcards: target_source_start( + wildcards, + "orkustofnun", + ), + end=lambda wildcards: target_source_end( + wildcards, + "orkustofnun", + ), + frequency=config["temporal_scope"]["frequency"], + country_codes=lambda wildcards: target_source_contexts( + wildcards, + "orkustofnun", + ), + message: + "Prepare electricity-demand data from Orkustofnun." + script: + "../scripts/prepare_load_orkustofnun.py" + + +rule prepare_auxiliary_load_orkustofnun: + input: + load=rules.download_load_orkustofnun.output.load, + plan=auxiliary_acquisition_plan, + output: + load=( + "/automatic/{shape}/auxiliary/orkustofnun/{batch_id}.parquet" + ), + log: + "/{shape}/auxiliary/orkustofnun/{batch_id}.log" + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare auxiliary electricity-demand data from Orkustofnun." + script: + "../scripts/prepare_load_orkustofnun.py" diff --git a/workflow/scripts/download_load_orkustofnun.py b/workflow/scripts/download_load_orkustofnun.py new file mode 100644 index 0000000..d613de9 --- /dev/null +++ b/workflow/scripts/download_load_orkustofnun.py @@ -0,0 +1,23 @@ +"""Snakemake entry point for downloading Orkustofnun demand data.""" + +import sys +from typing import TYPE_CHECKING, Any + +from sources.orkustofnun.download import download_orkustofnun + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Download the curated Orkustofnun demand dataset.""" + download_orkustofnun( + url=snakemake.params.url, + output_path=snakemake.output.load, + ) + + +if __name__ == "__main__": + sys.stderr = open(snakemake.log[0], "w", buffering=1) + + main(snakemake) diff --git a/workflow/scripts/prepare_load_orkustofnun.py b/workflow/scripts/prepare_load_orkustofnun.py new file mode 100644 index 0000000..6e3fee6 --- /dev/null +++ b/workflow/scripts/prepare_load_orkustofnun.py @@ -0,0 +1,53 @@ +"""Snakemake entry point for preparing Orkustofnun demand data.""" + +import sys +from typing import TYPE_CHECKING, Any + +from _advanced_execution import get_batch, load_execution_plan +from sources.orkustofnun.prepare import prepare_orkustofnun +from tclean import TimeGrid + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Prepare Orkustofnun demand for the requested workflow period.""" + plan_path = getattr(snakemake.input, "plan", None) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="orkustofnun", + ) + + start = batch["start"] + end = batch["end"] + country_codes = batch["countries"] + + else: + start = snakemake.params.start + end = snakemake.params.end + country_codes = list(snakemake.params.country_codes) + + grid = TimeGrid( + start=start, + end=end, + frequency=snakemake.params.frequency, + ) + + prepare_orkustofnun( + input_path=snakemake.input.load, + output_path=snakemake.output.load, + grid=grid, + country_codes=country_codes, + ) + + +if __name__ == "__main__": + sys.stderr = open(snakemake.log[0], "w", buffering=1) + + main(snakemake) diff --git a/workflow/scripts/sources/orkustofnun/download.py b/workflow/scripts/sources/orkustofnun/download.py new file mode 100644 index 0000000..2079e91 --- /dev/null +++ b/workflow/scripts/sources/orkustofnun/download.py @@ -0,0 +1,22 @@ +"""Download utilities for curated Orkustofnun demand data.""" + +from pathlib import Path + +import requests + + +def download_orkustofnun( + url: str, + output_path: str | Path, +) -> None: + """Download the curated Iceland electricity-demand dataset.""" + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + with requests.get(url, stream=True, timeout=120) as response: + response.raise_for_status() + + with output_path.open("wb") as file: + for chunk in response.iter_content(chunk_size=1024 * 1024): + if chunk: + file.write(chunk) diff --git a/workflow/scripts/sources/orkustofnun/prepare.py b/workflow/scripts/sources/orkustofnun/prepare.py new file mode 100644 index 0000000..1a2ccfb --- /dev/null +++ b/workflow/scripts/sources/orkustofnun/prepare.py @@ -0,0 +1,71 @@ +"""Prepare curated Orkustofnun electricity-demand data.""" + +from pathlib import Path + +import pandas as pd +from tclean import TimeGrid + +SUPPORTED_COUNTRIES = {"ISL"} + + +def prepare_orkustofnun( + input_path: str | Path, + output_path: str | Path, + grid: TimeGrid, + country_codes: list[str], +) -> None: + """Prepare Orkustofnun demand on the requested time grid.""" + requested = set(country_codes) + + unsupported = requested - SUPPORTED_COUNTRIES + + if unsupported: + raise ValueError( + "Orkustofnun does not provide demand data for " + f"{sorted(unsupported)}." + ) + + data = pd.read_parquet(input_path) + + required_columns = { + "utc_timestamp", + "ISL", + } + + missing_columns = required_columns - set(data.columns) + + if missing_columns: + raise ValueError( + "Orkustofnun dataset is missing required columns: " + f"{sorted(missing_columns)}." + ) + + data["utc_timestamp"] = pd.to_datetime( + data["utc_timestamp"], + utc=True, + ) + + if data["utc_timestamp"].duplicated().any(): + raise ValueError( + "Orkustofnun dataset contains duplicate timestamps." + ) + + data = data.set_index("utc_timestamp") + + # Restrict to the contexts actually requested. + data = data[list(country_codes)] + + # Align exactly to the requested T-Clean grid. Missing source + # observations deliberately remain NaN. + target_index = pd.DatetimeIndex(grid.target_index) + + data = data.reindex(target_index) + data.index.name = "utc_timestamp" + + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + data.reset_index().to_parquet( + output_path, + index=False, + ) From f10b210a8992de2cfbf0f9778e4a6e8463d66cd2 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 4 Sep 2026 15:12:07 +0200 Subject: [PATCH 216/241] Bug in the design of the ISL indexing. --- workflow/scripts/sources/orkustofnun/prepare.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/workflow/scripts/sources/orkustofnun/prepare.py b/workflow/scripts/sources/orkustofnun/prepare.py index 1a2ccfb..b6cf39b 100644 --- a/workflow/scripts/sources/orkustofnun/prepare.py +++ b/workflow/scripts/sources/orkustofnun/prepare.py @@ -60,12 +60,9 @@ def prepare_orkustofnun( target_index = pd.DatetimeIndex(grid.target_index) data = data.reindex(target_index) - data.index.name = "utc_timestamp" + data.index.name = "timestamp" output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) - data.reset_index().to_parquet( - output_path, - index=False, - ) + data.to_parquet(output_path) From f33218cd83c33a06e67e637a6d6b591a1ffc56c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:12:25 +0000 Subject: [PATCH 217/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/local_test.py | 8 +------- workflow/internal/source_registry.yaml | 2 +- workflow/rules/source_orkustofnun.smk | 14 ++++---------- workflow/scripts/download_load_orkustofnun.py | 5 +---- workflow/scripts/prepare_load_orkustofnun.py | 10 ++-------- .../scripts/sources/orkustofnun/download.py | 5 +---- workflow/scripts/sources/orkustofnun/prepare.py | 17 ++++------------- 7 files changed, 14 insertions(+), 47 deletions(-) diff --git a/tests/local_test.py b/tests/local_test.py index 5616066..57ad806 100644 --- a/tests/local_test.py +++ b/tests/local_test.py @@ -9,13 +9,7 @@ def test_europe_end_to_end(user_path: Path, token_entsoe: Path): """Run a full European end-to-end electricity-demand test.""" subprocess.run( - [ - "snakemake", - "--use-conda", - "--cores", - "8", - "--forceall", - ], + ["snakemake", "--use-conda", "--cores", "8", "--forceall"], check=True, cwd=LOCAL_TEST_PATH, ) diff --git a/workflow/internal/source_registry.yaml b/workflow/internal/source_registry.yaml index 5b2c04e..b45d2c4 100644 --- a/workflow/internal/source_registry.yaml +++ b/workflow/internal/source_registry.yaml @@ -173,4 +173,4 @@ orkustofnun: - "rules/source_orkustofnun.smk" python: - "scripts/sources/orkustofnun/download.py" - - "scripts/sources/orkustofnun/prepare.py" \ No newline at end of file + - "scripts/sources/orkustofnun/prepare.py" diff --git a/workflow/rules/source_orkustofnun.smk b/workflow/rules/source_orkustofnun.smk index 530d488..ca63d95 100644 --- a/workflow/rules/source_orkustofnun.smk +++ b/workflow/rules/source_orkustofnun.smk @@ -3,9 +3,7 @@ rule download_load_orkustofnun: output: - load=update( - "/automatic/orkustofnun/raw_load.parquet" - ), + load=update("/automatic/orkustofnun/raw_load.parquet"), log: "/download_load_orkustofnun.log", localrule: True @@ -25,9 +23,7 @@ rule prepare_load_orkustofnun: target_plan=target_data_plan, load="/automatic/orkustofnun/raw_load.parquet", output: - load=( - "/automatic/{shape}/load_orkustofnun.parquet" - ), + load=("/automatic/{shape}/load_orkustofnun.parquet"), log: "/{shape}/prepare_load_orkustofnun.log", conda: @@ -57,11 +53,9 @@ rule prepare_auxiliary_load_orkustofnun: load=rules.download_load_orkustofnun.output.load, plan=auxiliary_acquisition_plan, output: - load=( - "/automatic/{shape}/auxiliary/orkustofnun/{batch_id}.parquet" - ), + load=("/automatic/{shape}/auxiliary/orkustofnun/{batch_id}.parquet"), log: - "/{shape}/auxiliary/orkustofnun/{batch_id}.log" + "/{shape}/auxiliary/orkustofnun/{batch_id}.log", conda: "../envs/module.yaml" params: diff --git a/workflow/scripts/download_load_orkustofnun.py b/workflow/scripts/download_load_orkustofnun.py index d613de9..cf5b74a 100644 --- a/workflow/scripts/download_load_orkustofnun.py +++ b/workflow/scripts/download_load_orkustofnun.py @@ -11,10 +11,7 @@ def main(snakemake: Any) -> None: """Download the curated Orkustofnun demand dataset.""" - download_orkustofnun( - url=snakemake.params.url, - output_path=snakemake.output.load, - ) + download_orkustofnun(url=snakemake.params.url, output_path=snakemake.output.load) if __name__ == "__main__": diff --git a/workflow/scripts/prepare_load_orkustofnun.py b/workflow/scripts/prepare_load_orkustofnun.py index 6e3fee6..e12216b 100644 --- a/workflow/scripts/prepare_load_orkustofnun.py +++ b/workflow/scripts/prepare_load_orkustofnun.py @@ -19,9 +19,7 @@ def main(snakemake: Any) -> None: plan = load_execution_plan(plan_path) batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="orkustofnun", + plan, batch_id=snakemake.wildcards.batch_id, source="orkustofnun" ) start = batch["start"] @@ -33,11 +31,7 @@ def main(snakemake: Any) -> None: end = snakemake.params.end country_codes = list(snakemake.params.country_codes) - grid = TimeGrid( - start=start, - end=end, - frequency=snakemake.params.frequency, - ) + grid = TimeGrid(start=start, end=end, frequency=snakemake.params.frequency) prepare_orkustofnun( input_path=snakemake.input.load, diff --git a/workflow/scripts/sources/orkustofnun/download.py b/workflow/scripts/sources/orkustofnun/download.py index 2079e91..83ec724 100644 --- a/workflow/scripts/sources/orkustofnun/download.py +++ b/workflow/scripts/sources/orkustofnun/download.py @@ -5,10 +5,7 @@ import requests -def download_orkustofnun( - url: str, - output_path: str | Path, -) -> None: +def download_orkustofnun(url: str, output_path: str | Path) -> None: """Download the curated Iceland electricity-demand dataset.""" output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/workflow/scripts/sources/orkustofnun/prepare.py b/workflow/scripts/sources/orkustofnun/prepare.py index b6cf39b..6575398 100644 --- a/workflow/scripts/sources/orkustofnun/prepare.py +++ b/workflow/scripts/sources/orkustofnun/prepare.py @@ -21,16 +21,12 @@ def prepare_orkustofnun( if unsupported: raise ValueError( - "Orkustofnun does not provide demand data for " - f"{sorted(unsupported)}." + f"Orkustofnun does not provide demand data for {sorted(unsupported)}." ) data = pd.read_parquet(input_path) - required_columns = { - "utc_timestamp", - "ISL", - } + required_columns = {"utc_timestamp", "ISL"} missing_columns = required_columns - set(data.columns) @@ -40,15 +36,10 @@ def prepare_orkustofnun( f"{sorted(missing_columns)}." ) - data["utc_timestamp"] = pd.to_datetime( - data["utc_timestamp"], - utc=True, - ) + data["utc_timestamp"] = pd.to_datetime(data["utc_timestamp"], utc=True) if data["utc_timestamp"].duplicated().any(): - raise ValueError( - "Orkustofnun dataset contains duplicate timestamps." - ) + raise ValueError("Orkustofnun dataset contains duplicate timestamps.") data = data.set_index("utc_timestamp") From 2f8878e84e925d71bd9d840977bb04d62fb43dde Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 7 Sep 2026 10:58:14 +0200 Subject: [PATCH 218/241] Refactored to make the tests safer for partial downloads and to avoid unnecessary downloads of large files. --- tests/conftest.py | 44 ++++++++++++++++++++++++++++++++------- tests/integration_test.py | 21 +++++++++++++------ tests/local_test.py | 5 ++++- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1c77757..468c86c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -29,20 +29,48 @@ def module_path(): return MODULE_PATH +def _download_test_file(user_path: Path, name: str) -> Path: + """Download a test shapes file if it is not already available.""" + file_path = user_path / name / "shapes.parquet" + + if file_path.exists(): + return file_path + + file_path.parent.mkdir(parents=True, exist_ok=True) + + partial_path = file_path.with_suffix(".parquet.part") + partial_path.unlink(missing_ok=True) + + try: + urlretrieve(TEST_FILES[name], partial_path) + partial_path.replace(file_path) + except Exception: + partial_path.unlink(missing_ok=True) + raise + + return file_path + + @pytest.fixture(scope="session") def user_path() -> Path: - """Download and unzip test files.""" + """Path to user resources used during testing.""" user_dir = Path("resources/user/") - # If test file have been downloaded, assume everything is OK. - # Otherwise, re-download. - for name, file_url in TEST_FILES.items(): - file_path = user_dir / name / "shapes.parquet" - if not file_path.exists(): - file_path.parent.mkdir(parents=True, exist_ok=True) - urlretrieve(file_url, file_path) + user_dir.mkdir(parents=True, exist_ok=True) return user_dir +@pytest.fixture(scope="session") +def europe_small_shapes(user_path: Path) -> Path: + """Small European shapes file used by the integration test.""" + return _download_test_file(user_path, "EUROPE_S_C1_ADM1") + + +@pytest.fixture(scope="session") +def europe_large_shapes(user_path: Path) -> Path: + """Large European shapes file used by the local end-to-end test.""" + return _download_test_file(user_path, "EUROPE_L_C34_ADM1") + + @pytest.fixture(scope="session") def token_entsoe() -> Path: """Fixture to get token_entsoe.txt in CI. diff --git a/tests/integration_test.py b/tests/integration_test.py index 547588f..c642feb 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -45,25 +45,34 @@ def test_snakemake_environments(module_path, pixi_platforms, tmp_path): @pytest.fixture(scope="module") -def integration_path(user_path: Path, module_path: Path, token_entsoe: Path): +def integration_path( + europe_small_shapes: Path, + user_path: Path, + module_path: Path, + token_entsoe: Path, +): """Ensures the minimal integration test is ready.""" integration_dir = Path(module_path / "tests/integration") + if integration_dir.exists(): - # clean everything shutil.rmtree(integration_dir / "resources", ignore_errors=True) shutil.rmtree(integration_dir / "results/", ignore_errors=True) + user_integ_dir = integration_dir / "resources/user" + files_to_copy = { - "EUROPE_S_C1_ADM1/shapes.parquet": Path("EUROPE_S_C1_ADM1/shapes.parquet"), - "token_entsoe.txt": Path("token_entsoe.txt"), - "external_profiles/alb_external_test.csv": Path( + europe_small_shapes: Path("EUROPE_S_C1_ADM1/shapes.parquet"), + token_entsoe: Path("token_entsoe.txt"), + user_path / "external_profiles/alb_external_test.csv": Path( "external_profiles/alb_external_test.csv" ), } + for source_file, destination in files_to_copy.items(): destination_file = user_integ_dir / destination destination_file.parent.mkdir(parents=True, exist_ok=True) - shutil.copy(user_path / source_file, destination_file) + shutil.copy(source_file, destination_file) + return integration_dir diff --git a/tests/local_test.py b/tests/local_test.py index 57ad806..4d99036 100644 --- a/tests/local_test.py +++ b/tests/local_test.py @@ -6,7 +6,10 @@ LOCAL_TEST_PATH = Path(__file__).parent / "local" -def test_europe_end_to_end(user_path: Path, token_entsoe: Path): +def test_europe_end_to_end( + europe_large_shapes: Path, + token_entsoe: Path, +): """Run a full European end-to-end electricity-demand test.""" subprocess.run( ["snakemake", "--use-conda", "--cores", "8", "--forceall"], From eade488d5b25de38be1e601773d829cbe3c6c37d Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 7 Sep 2026 10:58:51 +0200 Subject: [PATCH 219/241] Ruff formatting. --- tests/integration_test.py | 5 +---- tests/local_test.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/integration_test.py b/tests/integration_test.py index c642feb..cd549c4 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -46,10 +46,7 @@ def test_snakemake_environments(module_path, pixi_platforms, tmp_path): @pytest.fixture(scope="module") def integration_path( - europe_small_shapes: Path, - user_path: Path, - module_path: Path, - token_entsoe: Path, + europe_small_shapes: Path, user_path: Path, module_path: Path, token_entsoe: Path ): """Ensures the minimal integration test is ready.""" integration_dir = Path(module_path / "tests/integration") diff --git a/tests/local_test.py b/tests/local_test.py index 4d99036..9e38e13 100644 --- a/tests/local_test.py +++ b/tests/local_test.py @@ -6,10 +6,7 @@ LOCAL_TEST_PATH = Path(__file__).parent / "local" -def test_europe_end_to_end( - europe_large_shapes: Path, - token_entsoe: Path, -): +def test_europe_end_to_end(europe_large_shapes: Path, token_entsoe: Path): """Run a full European end-to-end electricity-demand test.""" subprocess.run( ["snakemake", "--use-conda", "--cores", "8", "--forceall"], From d321236126edeeb02a0a7672c7901c61ffd158a7 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Mon, 7 Sep 2026 11:49:29 +0200 Subject: [PATCH 220/241] Updated conftest. --- tests/conftest.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 468c86c..88bd787 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -79,13 +79,15 @@ def token_entsoe() -> Path: and if token_entsoe.txt is not present or empty, write the token to the file. """ - if TOKEN_FILE.exists(): - if not TOKEN_FILE.read_text().strip() == "": - return TOKEN_FILE - elif TOKEN_ENTSOE: + if TOKEN_FILE.exists() and TOKEN_FILE.read_text().strip(): + return TOKEN_FILE + + if TOKEN_ENTSOE: + TOKEN_FILE.parent.mkdir(parents=True, exist_ok=True) TOKEN_FILE.write_text(TOKEN_ENTSOE) - else: - raise ValueError( - "`token_entsoe.txt` is missing or empty, and the environment variable TOKEN_ENTSOE is not set." - ) - return TOKEN_FILE + return TOKEN_FILE + + raise ValueError( + "`token_entsoe.txt` is missing or empty, and the environment variable " + "`TOKEN_ENTSOE` is not set." + ) From de8e2a2ae159f838737ff1563597bb0f9dfafee5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 15:26:15 +0200 Subject: [PATCH 221/241] Updated authors. --- CITATION.cff | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CITATION.cff b/CITATION.cff index 14d6879..1b19925 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -10,3 +10,6 @@ authors: - given-names: "Jann" family-names: "Launer" email: "j.a.c.launer@tudelft.nl" + - given-names: "Dano" + family-names: "Hawkins" + email: "d.d.a.hawkins@tudelft.nl" From 5031746f59510ede003deab3d307912083f7f5c6 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 15:38:53 +0200 Subject: [PATCH 222/241] Rewire module to T-Clean 0.2.0 interfaces --- ...demand_electricity_tclean_0_2_rewire.patch | 195 + pixi.lock | 3802 ++++++++--------- pixi.toml | 3 +- workflow/envs/module.linux-64.pin.txt | 107 +- workflow/envs/module.osx-arm64.pin.txt | 104 +- workflow/envs/module.win-64.pin.txt | 109 +- workflow/envs/module.yaml | 3 +- workflow/scripts/_tclean_config.py | 7 +- workflow/scripts/apply_advanced_overrides.py | 3 +- workflow/scripts/clean_auxiliary_group.py | 9 +- workflow/scripts/clean_demand.py | 25 +- .../scripts/construct_auxiliary_profile.py | 2 +- workflow/scripts/finalise_clean_demand.py | 2 +- workflow/scripts/plan_auxiliary_data.py | 2 +- workflow/scripts/validate_config.py | 2 +- 15 files changed, 2230 insertions(+), 2145 deletions(-) create mode 100644 module_demand_electricity_tclean_0_2_rewire.patch diff --git a/module_demand_electricity_tclean_0_2_rewire.patch b/module_demand_electricity_tclean_0_2_rewire.patch new file mode 100644 index 0000000..ef4c9b0 --- /dev/null +++ b/module_demand_electricity_tclean_0_2_rewire.patch @@ -0,0 +1,195 @@ +diff --git a/pixi.toml b/pixi.toml +--- a/pixi.toml ++++ b/pixi.toml +@@ -5,6 +5,7 @@ description = "This module prepares electricity demand timeseries for Europe at + license = "Apache-2.0" + readme = "README.md" + channels = [ ++ "https://conda.anaconda.org/ddahawkins/label/dev", + "conda-forge", + "bioconda", + ] +@@ -42,7 +43,7 @@ matplotlib = ">=3.10.9" + rioxarray = ">=0.20.0" + pandera = ">=0.31.1" + pyyaml = ">=6.0.3" +-tclean = ">=0.1.1" ++tclean = "==0.2.0" + + [feature.test-module.dependencies] + pytest = ">=8.3.5" +diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py +--- a/workflow/scripts/_tclean_config.py ++++ b/workflow/scripts/_tclean_config.py +@@ -10,7 +10,7 @@ from _source_capabilities import ( + intersect_source_temporal_scope, + uncovered_temporal_intervals, + ) +-from tclean import TCleanConfig, TimeGrid ++from tclean import TimeGrid + + + def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: +@@ -22,11 +22,6 @@ def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: + ) + + +-def build_tclean_config(temporal_scope: Mapping[str, Any]) -> TCleanConfig: +- """Build the T-Clean configuration used for demand cleaning.""" +- return TCleanConfig(grid=build_time_grid(temporal_scope)) +- +- + def build_basic_rules(gap_filling_config: Mapping[str, Any]) -> list[dict[str, Any]]: + """Return configured basic-cleaning rules for T-Clean.""" + if gap_filling_config["mode"] == "off": +diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py +--- a/workflow/scripts/apply_advanced_overrides.py ++++ b/workflow/scripts/apply_advanced_overrides.py +@@ -5,8 +5,7 @@ from pathlib import Path + import pandas as pd + from _advanced_execution import load_execution_plan + from tclean import TimeGrid +-from tclean.advanced import read_external_profile +-from tclean.advanced.apply import apply_advanced_rules ++from tclean.gap_filling import apply_advanced_rules, read_external_profile + + plan = load_execution_plan(snakemake.input.plan) + +diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py +--- a/workflow/scripts/clean_auxiliary_group.py ++++ b/workflow/scripts/clean_auxiliary_group.py +@@ -5,7 +5,8 @@ from typing import TYPE_CHECKING, Any + import pandas as pd + from _advanced_execution import load_execution_plan + from _prepared_data import read_prepared_source +-from tclean import TCleanConfig, TimeGrid, clean ++from tclean import TimeGrid ++from tclean.gap_filling import fill_gaps + + if TYPE_CHECKING: + snakemake: Any +@@ -46,8 +47,6 @@ def main(snakemake: Any) -> None: + start=group_start, end=group_end, frequency=(snakemake.params.frequency) + ) + +- config = TCleanConfig(grid=grid) +- + source_paths = list(snakemake.input.sources) + + if len(source_paths) != len(batches): +@@ -86,8 +85,8 @@ def main(snakemake: Any) -> None: + else [] + ) + +- (cleaned, data_source, cleaning_method) = clean( +- sources, config=config, basic_rules=basic_rules ++ (cleaned, data_source, cleaning_method) = fill_gaps( ++ sources, grid=grid, basic_rules=basic_rules + ) + + cleaned.to_parquet(snakemake.output.demand) +diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py +--- a/workflow/scripts/clean_demand.py ++++ b/workflow/scripts/clean_demand.py +@@ -7,10 +7,13 @@ from typing import TYPE_CHECKING, Any + + import pandas as pd + from _prepared_data import read_prepared_source +-from _tclean_config import build_basic_rules, build_tclean_config +-from tclean import clean +-from tclean.advanced import build_gap_report +-from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank ++from _tclean_config import build_basic_rules, build_time_grid ++from tclean.gap_filling import ( ++ build_cleaning_method_ranks, ++ build_gap_report, ++ derive_cleaning_method_rank, ++ fill_gaps, ++) + + if TYPE_CHECKING: + snakemake: Any +@@ -38,19 +41,17 @@ def main(snakemake: Any) -> None: + + target_contexts = _read_target_contexts(snakemake.input.target_plan) + +- config = build_tclean_config(snakemake.params.temporal_scope) ++ grid = build_time_grid(snakemake.params.temporal_scope) + + sources = { +- source_name: data.reindex( +- index=config.grid.target_index, columns=target_contexts +- ) ++ source_name: data.reindex(index=grid.target_index, columns=target_contexts) + for source_name, data in sources.items() + } + + basic_rules = build_basic_rules(snakemake.params.gap_filling) + +- (cleaned, data_source, cleaning_method) = clean( +- sources, config=config, basic_rules=basic_rules ++ (cleaned, data_source, cleaning_method) = fill_gaps( ++ sources, grid=grid, basic_rules=basic_rules + ) + + basic_rule_names = [rule["name"] for rule in basic_rules] +@@ -65,7 +66,7 @@ def main(snakemake: Any) -> None: + + gap_report = build_gap_report( + cleaned, +- grid=config.grid, ++ grid=grid, + enabled=(snakemake.params.gap_filling["mode"] == "advanced"), + ) + +diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py +--- a/workflow/scripts/construct_auxiliary_profile.py ++++ b/workflow/scripts/construct_auxiliary_profile.py +@@ -7,7 +7,7 @@ from _tclean_config import ( + build_scaling_source_periods, + ) + from tclean import TimeGrid +-from tclean.advanced import construct_from_sources ++from tclean.gap_filling import construct_from_sources + + plan = load_execution_plan(snakemake.input.plan) + +diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py +--- a/workflow/scripts/finalise_clean_demand.py ++++ b/workflow/scripts/finalise_clean_demand.py +@@ -4,7 +4,10 @@ import shutil + + import pandas as pd + from _tclean_config import build_advanced_rules, build_basic_rules +-from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank ++from tclean.gap_filling import ( ++ build_cleaning_method_ranks, ++ derive_cleaning_method_rank, ++) + + shutil.copyfile(snakemake.input.demand, snakemake.output.demand) + +diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py +--- a/workflow/scripts/plan_auxiliary_data.py ++++ b/workflow/scripts/plan_auxiliary_data.py +@@ -24,7 +24,7 @@ from _tclean_config import ( + filter_source_requests_by_temporal_scope, + get_advanced_source_definitions, + ) +-from tclean.advanced import ( ++from tclean.gap_filling import ( + build_auxiliary_acquisition_requirements, + build_auxiliary_source_requests, + select_active_advanced_rules, +diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py +--- a/workflow/scripts/validate_config.py ++++ b/workflow/scripts/validate_config.py +@@ -15,7 +15,7 @@ from _tclean_config import ( + build_time_grid, + ) + from tclean import TimeGrid +-from tclean.basic import validate_basic_rules ++from tclean.gap_filling import validate_basic_rules + + if TYPE_CHECKING: + snakemake: Any diff --git a/pixi.lock b/pixi.lock index 972a7f1..6e8ef7c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -6,6 +6,7 @@ platforms: environments: default: channels: + - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -19,42 +20,40 @@ environments: - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.10.0-py311hf77984d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h8d76f0c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cgl-0.60.10-hc46dffc_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.1-py314h9e666f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314hf3b331d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h67ed8a3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.0-hfc2019e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h0804268_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.1-hfc2019e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-14.1.2-h8b86629_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/greenlet-3.5.5-py314h42812f9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h89acca1_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda @@ -63,7 +62,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda @@ -81,21 +80,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.11.0-10_h6ae95b6_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h6ba947b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-h5efa3ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314h959206d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmsgpack-c-6.1.0-h54a6638_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda @@ -103,7 +102,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.3-h4c96295_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsolv-0.7.39-h72ddc62_1.conda @@ -112,49 +111,49 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/menuinst-2.5.2-py314h9e666f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hc824af9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hfe1a184_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314hd3a7d6b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py314hd3a7d6b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py314hb4ffadd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-h7bb47b9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h89acca1_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py314h7e8cd81_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_104_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_106_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314hfe1a184_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.2.0-py312h8a5ba0d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.8.post0-hee9eb32_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.8.post0-h1c70be6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.5-hdfdb5ae_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.9-h4dbf13b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.6-hbcb56a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.11-h4dbf13b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py314h0f05182_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h89acca1_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda @@ -174,51 +173,50 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314hfe1a184_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.15.1-pyh5ded981_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.13.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -227,7 +225,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.62-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda @@ -240,7 +238,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyha191276_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda @@ -261,13 +259,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda @@ -278,9 +275,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -308,7 +305,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda osx-arm64: @@ -324,42 +321,42 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.15.1-pyh5ded981_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-1.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.13.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -368,7 +365,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.62-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda @@ -381,7 +378,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh01cf8df_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda @@ -402,13 +399,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda @@ -419,9 +415,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -449,50 +445,48 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.10.0-py311hfcb3ee1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314h618e29d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h580cee5_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cgl-0.60.10-h034796e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.1-py314hcec6336_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314h671cc75_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h026c20a_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.0-hf76c51c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h0d72ea3_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.1-hf76c51c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-14.1.2-hec8c438_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/greenlet-3.5.5-py314he609de1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hcd3153d_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.22.0-h6651222_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.1-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda @@ -504,25 +498,25 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapacke-3.11.0-10_h1b118fd_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314h12d839b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-h7215443_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314h53f8b30_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmsgpack-c-6.1.0-h784d473_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.3-he8aa2a2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h74c22ad_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsolv-0.7.39-h626c152_1.conda @@ -530,50 +524,49 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-hbf2880b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/menuinst-2.5.2-py314h6ffbfce_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hf2636f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hd98292b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py314he06036b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf9b25ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hd89b7e0_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310h2dd3e6a_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hcd3153d_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314haae224a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.2.0-py312hcedbef1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.8.post0-hdca3b7d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.8.post0-h2fd3d4c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.5-h4e39fff_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.9-hfbe1efa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.6-h7f187d3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.11-hfbe1efa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.52-py314h5583935_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-cpp-0.8.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314hd98292b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda win-64: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda @@ -587,41 +580,41 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.15.1-pyh5ded981_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/archspec-0.2.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argparse-dataclass-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/black-26.5.1-pyh866005b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.06.23-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.13.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-libmamba-solver-26.7.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distro-1.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dpath-2.2.0-pyha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 @@ -630,7 +623,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/frozendict-2.4.7-pyh851646a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.62-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda @@ -643,7 +636,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipdb-0.13.13-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.3.0-pyh6dadd2b_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyhe2676ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyhe2676ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda @@ -663,12 +656,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydot-4.0.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda @@ -679,10 +671,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.6.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.22.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -710,42 +702,40 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/unearth-0.18.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.10.0-py311h66afae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cgl-0.60.10-h7c5f3e6_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.1-py314hb821551_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.0-h11686cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.1-h11686cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphviz-14.1.2-h4c50273_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/greenlet-3.5.5-py314hb98de8c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gts-0.7.6-h6b5321d_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.22.0-hdb0ef4a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda @@ -753,9 +743,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgd-2.3.3-h4974f7c_12.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-h261a839_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda @@ -769,7 +759,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libmsgpack-c-6.1.0-h5112557_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.22-h6a83c73_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsolv-0.7.39-h8883371_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda @@ -778,8 +768,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda @@ -787,39 +777,39 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/menuinst-2.5.2-py314hb98de8c_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314h13f4da2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314hc5dbbe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.3-py314h02f10f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_106_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-312-py314hf700ef7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.2.0-py312h343a6d4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.8.post0-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.8.post0-hf5d2508_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.5-h6bcfbb0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.9-h477610d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.6-hcc30601_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.11-h477610d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda @@ -836,10 +826,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-cpp-0.8.0-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda module: channels: + - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -864,28 +854,28 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.22.0-ha042cf0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312ha17e7ad_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda @@ -894,7 +884,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda @@ -907,34 +897,34 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.13.3-h31aba09_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda @@ -942,7 +932,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda @@ -961,9 +951,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda @@ -975,16 +966,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.4-h7df9aa5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda @@ -993,46 +984,46 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.13.0-py312hdb6ebaa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.8.0-py312hb99b6cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf520645_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.1-py312h5d657d5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h5cc1888_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda @@ -1058,7 +1049,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda @@ -1072,7 +1063,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda @@ -1081,7 +1072,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -1099,7 +1090,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda @@ -1115,7 +1106,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -1126,7 +1117,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1137,21 +1127,22 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda @@ -1160,7 +1151,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -1179,7 +1170,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda @@ -1195,7 +1186,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -1206,7 +1197,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1217,7 +1207,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda @@ -1240,22 +1230,22 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h580cee5_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.22.0-h6651222_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314h7afc4b4_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda @@ -1263,7 +1253,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda @@ -1275,30 +1265,30 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.22.0-h6651222_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.1-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.13.3-h0d10833_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda @@ -1316,7 +1306,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda @@ -1328,9 +1318,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda @@ -1340,43 +1330,43 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py314he06036b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-h4d1e80c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314h709c99d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hb001647_1004.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.13.0-py314h618c026_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.8.0-py314h92bd025_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.1-py314h716b1d8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda @@ -1385,28 +1375,28 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 @@ -1425,7 +1415,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda @@ -1442,7 +1432,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda @@ -1453,7 +1443,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1465,7 +1454,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda @@ -1488,27 +1477,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.22.0-hdb0ef4a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314hcb00e3b_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda @@ -1520,27 +1509,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.22.0-hdb0ef4a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-hc903f1e_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.13.3-hcd51406_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-h261a839_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda @@ -1557,7 +1546,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda @@ -1571,10 +1560,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda @@ -1585,44 +1574,44 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.3-py314h02f10f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h90fa87c_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.13.0-py314he172734_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.8.0-py314h9d82244_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h0c0e1ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.1-py314h0f7306d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda @@ -1635,8 +1624,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda test: channels: + - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -1661,28 +1652,28 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cramjam-2.11.0-py312h848b54d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.22.0-ha042cf0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f23490_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312ha17e7ad_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda @@ -1691,7 +1682,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda @@ -1704,34 +1695,34 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.13.3-h31aba09_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda @@ -1739,7 +1730,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda @@ -1758,9 +1749,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda @@ -1772,16 +1764,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.4-h7df9aa5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda @@ -1790,47 +1782,47 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.13.0-py312hdb6ebaa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.8.0-py312hb99b6cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf520645_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.1-py312h5d657d5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.4-h9ffa6c4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h5cc1888_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda @@ -1856,7 +1848,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda @@ -1870,7 +1862,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda @@ -1880,7 +1872,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -1900,7 +1892,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 @@ -1921,7 +1913,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda @@ -1933,7 +1925,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -1945,21 +1936,22 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda @@ -1969,7 +1961,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -1990,7 +1982,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 @@ -2011,7 +2003,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda @@ -2023,7 +2015,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -2035,7 +2026,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda @@ -2058,22 +2049,22 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h580cee5_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cramjam-2.11.0-py314he20b14d_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.22.0-h6651222_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115a04_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314h7afc4b4_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda @@ -2081,7 +2072,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda @@ -2093,30 +2084,30 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.22.0-h6651222_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.1-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.13.3-h0d10833_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda @@ -2134,7 +2125,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-hdb0c161_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda @@ -2146,9 +2137,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda @@ -2158,44 +2149,44 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py314he06036b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-h4d1e80c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314h709c99d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hb001647_1004.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.13.0-py314h618c026_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.8.0-py314h92bd025_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.1-py314h716b1d8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.4-ha9f7ffe_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda @@ -2204,18 +2195,19 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda @@ -2225,7 +2217,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -2246,7 +2238,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 @@ -2268,7 +2260,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda @@ -2280,7 +2272,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -2293,7 +2284,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.7.0-pyhc364b38_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda @@ -2316,27 +2307,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-common-cpp-12.14.0-hf9fdf8e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/azure-storage-files-datalake-cpp-12.16.0-heb2e695_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.8-h6a83c73_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cramjam-2.11.0-py314h8a07082_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.22.0-hdb0ef4a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314hcb00e3b_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda @@ -2348,27 +2339,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-dataset-25.0.0-h7d8d6a5_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libarrow-substrait-25.0.0-hffb5221_3_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.22.0-hdb0ef4a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-hc903f1e_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.13.3-hcd51406_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-h261a839_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-3.7.0-hfe3f7e9_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-3.7.0-he04ea4c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.82.1-he6cc664_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h2419aca_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda @@ -2385,7 +2376,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libprotobuf-7.35.1-h1a71995_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h45f70d9_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda @@ -2399,10 +2390,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda @@ -2413,45 +2404,45 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.3-py314h02f10f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h90fa87c_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/orc-2.3.1-hf7eca67_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/prometheus-cpp-1.3.0-hcea2f5d_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.13.0-py314he172734_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.8.0-py314h9d82244_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h0c0e1ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_106_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.1-py314h0f7306d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda @@ -2464,6 +2455,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 @@ -2636,23 +2628,22 @@ packages: - alsa-lib >=1.2.16.1,<1.3.0a0 size: 594986 timestamp: 1787763412911 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.8.0-py310hd8a072f_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.10.0-py311hf77984d_0.conda noarch: python - sha256: 25c15e85cb03a1c4d43d02d0ff37c8c9a970c3e84abeaf420fc190bb053bbe3f - md5: e46fc69f093a840e98b9e2c29727862d + sha256: fb8a418604e1c79df683a24b424e99cc837cb48850ebb589cb2c049c7a92a7ab + md5: e7db28aaebccbdac109ee5aebfd8591a depends: - - python >=3.10 + - python >=3.11 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - _python_abi3_support 1.* - - cpython >=3.10 + - cpython >=3.11 constrains: - __glibc >=2.17 license: MIT - license_family: MIT run_exports: {} - size: 1116952 - timestamp: 1786328964477 + size: 1149650 + timestamp: 1788869562073 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -3001,9 +2992,9 @@ packages: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 size: 308699 timestamp: 1781538835962 -- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda - sha256: c10df0467f534472f0aa39013850d6dd9dd38ea5a6fb5c0812afb6f3fc768924 - md5: e7eb25765bdf21397cc6e30828871625 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda + sha256: b377a4f053c4e184b031253c702984194d10c98228004d7cf07c1eca86247d62 + md5: b0e9b44b494bb001c4d35b206022eba2 depends: - python - __glibc >=2.17,<3.0.a0 @@ -3012,8 +3003,8 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 run_exports: {} - size: 240967 - timestamp: 1786861419155 + size: 241113 + timestamp: 1788491295568 - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d md5: 2c2fae981fd2afd00812c92ac47d023d @@ -3032,14 +3023,14 @@ packages: - blosc >=1.21.6,<2.0a0 size: 48427 timestamp: 1733513201413 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda - sha256: 1f2ccfebe6e0113bfc473b21906372c1ee4eb69bf6faef0ad7f3d4d79af63a98 - md5: 6ff307b78fbfb7dcafb7e25ff8bc9c10 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda + sha256: 61b78574f002390b6a7fc79834fc344beaab09550068a51100eea5a485dac741 + md5: 746aa619a1bcaf4da541101179d7c60e depends: - __glibc >=2.17,<3.0.a0 - - brotli-bin 1.2.0 h9908984_3 - - libbrotlidec 1.2.0 ha411449_3 - - libbrotlienc 1.2.0 h018ffa1_3 + - brotli-bin 1.2.0 h9908984_4 + - libbrotlidec 1.2.0 ha411449_4 + - libbrotlienc 1.2.0 h018ffa1_4 - libgcc >=15 license: MIT license_family: MIT @@ -3048,24 +3039,24 @@ packages: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20676 - timestamp: 1786622810792 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda - sha256: 56b2e5e8a49f9887af74cc63e0d55a3654e60b667ad5558da089549a36ae6a0c - md5: 8929291d9efc59715df1cfa7daf5e3ed + size: 20679 + timestamp: 1788480401508 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_4.conda + sha256: ce7db81d5fd4b222ee1a27c52ccf7af44faaf88283017103a8e288f9c372b973 + md5: c48c09444f3736800ea0b9550c365baf depends: - __glibc >=2.17,<3.0.a0 - - libbrotlidec 1.2.0 ha411449_3 - - libbrotlienc 1.2.0 h018ffa1_3 + - libbrotlidec 1.2.0 ha411449_4 + - libbrotlienc 1.2.0 h018ffa1_4 - libgcc >=15 license: MIT license_family: MIT run_exports: {} - size: 21598 - timestamp: 1786622801722 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda - sha256: 32ae6e002843704af9f39395f3116815fa66f2b27de1bd9044fb2a2d53fbe3d3 - md5: d176f3ed2824f930b524c45eb8f158bb + size: 21601 + timestamp: 1788480392621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda + sha256: 6e4f440a7015d7d78120b3a3f90a4ec3d7bb6de7bc65458123c52433755db5f0 + md5: edb667e7ce56106424e8afab2dc0f0cb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 @@ -3073,15 +3064,15 @@ packages: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - - libbrotlicommon 1.2.0 h39a168f_3 + - libbrotlicommon 1.2.0 h39a168f_4 license: MIT license_family: MIT run_exports: {} - size: 367032 - timestamp: 1786622975850 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_3.conda - sha256: e52ff7e1e3c5f4423421fbcd1f1ebf1d6ce123e22890ceb225d6552b7bbc551f - md5: bd1be0851060138e038f6f4e09cc1eb4 + size: 367657 + timestamp: 1788480501027 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hcd2bdb6_4.conda + sha256: e9cc0891a0dc5c3fa8c71dd2224491b6658c069af7a49ce4443158730990b6fd + md5: d9aabceecc99b3b8b0eb7090c5776af4 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 @@ -3089,12 +3080,12 @@ packages: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 constrains: - - libbrotlicommon 1.2.0 h39a168f_3 + - libbrotlicommon 1.2.0 h39a168f_4 license: MIT license_family: MIT run_exports: {} - size: 367948 - timestamp: 1786622843866 + size: 367567 + timestamp: 1788480533506 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 md5: e675fabcf81499adc7edf58124fb1e01 @@ -3121,9 +3112,9 @@ packages: - c-ares >=1.34.8,<2.0a0 size: 228700 timestamp: 1787169971173 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda - sha256: 8395bb43e188b76be0842b8ecd440e79a8d202d320bfee009e31b6623390b40d - md5: e83331a940393006789fedddc3b492f3 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda + sha256: 6b551d9346997bc4c6fadf96b7b92442b8f3244011cc462e4ee77afc714d52c5 + md5: 7b870cffbaa8430290e567a0facd8dca depends: - __glibc >=2.17,<3.0.a0 - fontconfig >=2.18.3,<3.0a0 @@ -3148,23 +3139,8 @@ packages: run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 989707 - timestamp: 1787926031792 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py314h8d76f0c_2.conda - sha256: 9d181949eead0d4092ed9ffa3b7ec066a15572d515327939c8a074c224262528 - md5: 314853abf64fc052dea08bd17df17b65 - depends: - - __glibc >=2.17,<3.0.a0 - - libffi >=3.7.0,<3.8.0a0 - - libgcc >=15 - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - run_exports: {} - size: 306626 - timestamp: 1786775112485 + size: 991011 + timestamp: 1788221336073 - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-cbc-2.10.13-h4d16d09_1.conda sha256: 200da7fefacb1a196dd7b4b6f45106cebe017042b1491e2b27c7cc833beed8ea md5: 5f68e67b2b9463501260e731e2999dec @@ -3293,9 +3269,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 664399 timestamp: 1778500281099 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.1-py314h9e666f3_0.conda - sha256: 3651193a900fbb7221573de2e80bca0a8608e3fb1f8ba96523d81212dc4cafd0 - md5: 03ef3d814cc36f7feb7ab83e17992aa0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_0.conda + sha256: 0ac622d316fa7fe79b49ce430ba7a5ca9ff0fb5a33a4cc308ddb0e2314c748e4 + md5: 48c05606a35f2878c74bc5729869c21d depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -3328,29 +3304,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1465310 - timestamp: 1787306252426 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-pypi-0.11.0-py314h9e666f3_1.conda - sha256: f864a806c98c44a0cdc4ede1b42fa6cdcb9d327d8d0013f51af9344f553d0cbf - md5: d8cc5cf325134981f65dd72c8e082433 - depends: - - python - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 - license: MIT - license_family: MIT - run_exports: {} - size: 323834 - timestamp: 1784203010834 + size: 1466536 + timestamp: 1788559209569 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 @@ -3394,24 +3349,24 @@ packages: run_exports: {} size: 1817713 timestamp: 1763019879546 -- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda - sha256: 0ba202eb4cbf3909d196e919f12e229fbd745e44462e7e3004569d20a26cf02a - md5: 82c2a5fc14064073501f5e1b817f4d80 +- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.22.0-ha042cf0_0.conda + sha256: 1a35faf4bf079c4438c52c9795b95d6c97c1b60e0bb51802cf48e2b7352b57e5 + md5: 399a7c0b7ca511de34c5ee5762cee149 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 ha042cf0_5 + - libcurl 8.22.0 ha042cf0_0 - libgcc >=15 - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT run_exports: {} - size: 194983 - timestamp: 1787183729323 + size: 196563 + timestamp: 1788340726238 - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 md5: af491aae930edc096b58466c51c4126c @@ -3444,36 +3399,35 @@ packages: run_exports: {} size: 623770 timestamp: 1771855837505 -- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 - md5: ce96f2f470d39bd96ce03945af92e280 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + sha256: 87a0a42d4ccc527597eff3234509dfb03e0d9bf67d5e2b6fc758e2ef389d8abe + md5: a6eb37b51be1a9a654dc3a8aca039b1a depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - libzlib >=1.3.1,<2.0a0 - - libglib >=2.86.2,<3.0a0 - - libexpat >=2.7.3,<3.0a0 + - libgcc >=15 + - libstdcxx >=15 + - libexpat >=2.8.1,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libglib >=2.88.3,<3.0a0 license: AFL-2.1 OR GPL-2.0-or-later run_exports: weak: - dbus >=1.16.2,<2.0a0 - size: 447649 - timestamp: 1764536047944 -- conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314h42812f9_0.conda - sha256: c16696b23e1d75b6eea7d0c8b9c31c03d1987eeb61852f09b6d4042ca015acd3 - md5: a7eb8029c4fe320c0179085707017c2d + size: 449564 + timestamp: 1788197922783 +- conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314hf3b331d_1.conda + sha256: f899b59082800ac525faf269c004d1db13b65a8bd7e2d780ff0153095a1813ff + md5: a0037f59c7e5ab9ca76f9cf7de866949 depends: - python - - libgcc >=14 - - libstdcxx >=14 - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - libgcc >=15 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT run_exports: {} - size: 2842115 - timestamp: 1780390153580 + size: 2842844 + timestamp: 1788859389988 - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc md5: dbe3ec0f120af456b3477743ffd99b74 @@ -3531,27 +3485,27 @@ packages: run_exports: {} size: 570988 timestamp: 1778864779103 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda - sha256: 801274da37e28d0cff6d091b679d34b10df718420a97d8fe577a968805f396d3 - md5: a68cae58a81a937a6edcb3e4e6f0bbe7 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312ha17e7ad_7.conda + sha256: 4a5696a5fb891fe9f822905c47d0b092710213cf38a0bd79b477217f654c2d49 + md5: 27561c3dba1d77f3d5852318965df012 depends: - - __glibc >=2.17,<3.0.a0 + - python - attrs >=19.2.0 - click >=8.0,<9.dev0 - - click-plugins >=1.0 - cligj >=0.5 - - libgcc >=14 - - libgdal-core >=3.12.1,<3.13.0a0 - - libstdcxx >=14 + - click-plugins >=1.0 - pyparsing - - python >=3.12,<3.13.0a0 - - python_abi 3.12.* *_cp312 - shapely + - libgcc >=15 + - libstdcxx >=15 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + - libgdal-core >=3.13.3,<3.14.0a0 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1203804 - timestamp: 1767051329805 + size: 1458994 + timestamp: 1788472937680 - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda sha256: 25d14a197601fdd616f2e501431b52887e69b31e7defbc1679381d718357ceb7 md5: a70bb6d42ad121aef456e0007e92bed6 @@ -3624,26 +3578,27 @@ packages: - libiconv >=1.18,<2.0a0 - minizip >=4.2.2,<5.0a0 license: MPL-1.1 + license_family: MOZILLA run_exports: weak: - freexl >=2.0.0,<3.0a0 size: 61858 timestamp: 1788125490606 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - sha256: 4846a3ca0402f3fe33ad84ed50ab213c6aafde4a0faef3c5002f6bf753e21671 - md5: 1cd10eda5692519d01bb20e086e214c9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda + sha256: 76dd097bd65d812a9104edb0e266386e152e65928ac5c8eb4f480ce24affc470 + md5: 16d6e5a0f8dbc17bd6669dba6f49bad3 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: LGPL-2.1-or-later run_exports: weak: - fribidi >=1.0.16,<2.0a0 - size: 61782 - timestamp: 1785912528684 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_0.conda - sha256: 4345423572cb80f13acbe52987a880576046a0b42c4e22e3a85e0198ee02aab0 - md5: 10dab6a745f32ceeac6c9e00d9979797 + size: 62401 + timestamp: 1788385624706 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_2.conda + sha256: 32f3ebd1871cb6ac7eac36c78c410860f38e8d252d5857a80f6abfcb79c7e0ff + md5: d7bd6ccfc58eb6796bd6bba85878fc0e depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 @@ -3657,8 +3612,8 @@ packages: run_exports: weak: - gdk-pixbuf >=2.44.8,<3.0a0 - size: 579757 - timestamp: 1786715266831 + size: 581961 + timestamp: 1788490424411 - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda sha256: dc9f03bef913c8b3c94e93a61318b7437bd5e4f7136ef841e68df8de082405d5 md5: 5f856b72ca9cd5b5b4a1c43551804622 @@ -3699,18 +3654,18 @@ packages: - giflib >=6.1.3,<6.2.0a0 size: 87196 timestamp: 1785907555387 -- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h67ed8a3_2.conda - sha256: 966f78753d7f044df52a0b32876580ee15f006e8ec960afa5360c50e66be0fc5 - md5: 5c35ace78b7d630e77efcb37dacb62ae +- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h0804268_3.conda + sha256: 41d88bd2671de2720cebe28cbfb1b434586383efdeab1669b488a1669ce3e3b0 + md5: 164d7e0bcf4552bba8d4a8efbe36c990 depends: - - libglib ==2.88.3 he503a2a_2 + - libglib ==2.88.3 he503a2a_3 - libffi - __glibc >=2.17,<3.0.a0 - libgcc >=15 license: LGPL-2.1-or-later run_exports: {} - size: 236792 - timestamp: 1787884091247 + size: 237069 + timestamp: 1788525203514 - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda sha256: f24feeda3aeec3a2cbb9fe2e0fabc4785372e70b6cc838c96023e08e17f4bfa0 md5: 6941f96b8a8056677d79b4f5a2c4aaca @@ -3726,13 +3681,14 @@ packages: - glog >=0.7.1,<0.8.0a0 size: 149031 timestamp: 1784094370091 -- conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.0-hfc2019e_0.conda - sha256: fc9ce48c6e7c9a33ba67a35e7816209600d783bc6d84329487e36b78c0c7100d - md5: 4fef13b9643fae40e21f8392ab82669a +- conda: https://conda.anaconda.org/conda-forge/linux-64/go-shfmt-3.14.1-hfc2019e_0.conda + sha256: 0cc0a5c62bba143b540a5f266fc3bc826ce784bc244be957f3a5af77c65d2011 + md5: 386bf257712ac5572a546c1d1c781cf2 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 2061866 - timestamp: 1787948975693 + size: 2240391 + timestamp: 1788728447336 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda sha256: 7fa3b6a9c081fa3e545573152a788d061a0a0ba57df7251cc0f4f75225fc93e7 md5: f9fe2984587fa8235a6af6004760cd18 @@ -3848,18 +3804,18 @@ packages: - gts >=0.7.6,<0.8.0a0 size: 318312 timestamp: 1686545244763 -- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_0.conda - sha256: 78cf9cedd013ba49455b2c75e95d2cdc4aafd384be8f9ece0def6ff1b2a86c61 - md5: c2d4a4fe3216b26ef30e91d917c1b718 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.4.0-ha770c72_1.conda + sha256: 12b371fad335d0c27de968140088cc678233d7060c2743baf9cb687a9fa975a7 + md5: d5c10e05d7135fca0731173c288bfff5 depends: - - libharfbuzz-devel 14.4.0 h23af247_0 + - libharfbuzz-devel 14.4.0 h23af247_1 license: MIT license_family: MIT run_exports: weak: - libharfbuzz >=14.4.0 - size: 11141 - timestamp: 1787795205932 + size: 11107 + timestamp: 1788405531557 - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda sha256: 6d7e6e1286cb521059fe69696705100a03b006efb914ffe82a2ae97ecbae66b7 md5: 129e404c5b001f3ef5581316971e3ea0 @@ -3882,19 +3838,19 @@ packages: - icu >=78.3,<79.0a0 size: 14459115 timestamp: 1786545741408 -- conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h5bd0f2a_2.conda - sha256: 1c9163fd77943c55fe727f5399545141df54f696422bf101089a32526aea3234 - md5: d4d65cfb5c2569d5dbd344039471a534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/immutables-0.21-py314h89acca1_3.conda + sha256: 2b5d0b3fcba0458e836a86ce17cab9c5b6cae15ba994e77a9544849beab1cedf + md5: 926b23c378c307e8bac1b8d2e560bbfa depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - python >=3.14.0rc2,<3.15.0a0 + - libgcc >=15 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE run_exports: {} - size: 55407 - timestamp: 1757685419776 + size: 56478 + timestamp: 1788611801440 - conda: https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda sha256: 09e706cb388d3ea977fabcee8e28384bdaad8ce1fc49340df5f868a2bd95a7da md5: 38f5dbc9ac808e31c00650f7be1db93f @@ -3920,19 +3876,20 @@ packages: - keyutils >=1.6.3,<2.0a0 size: 135295 timestamp: 1786739238128 -- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda - sha256: 09f3578b5cf4484d236e02f5b9fe455e45448419b86ef16aee46d45e9fd3b715 - md5: 6ec45525bac89c279b6e5f4c47eec6bf +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_1.conda + sha256: 608716113cb671415a038654e2bc55c2372c8817c24c0a5043323fd7c06b86f3 + md5: 231fede9051444ed7d243f78f3cb4a8f depends: - python + - __glibc >=2.17,<3.0.a0 - libstdcxx >=15 - libgcc >=15 - - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 75073 - timestamp: 1787938396652 + size: 75215 + timestamp: 1788505643992 - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda sha256: 2a5c38c85e63df84c4e69ee71439841ce570d259ae3060627bb9a49a938d66f4 md5: 53318d715316929a574f83591308b1f8 @@ -4163,14 +4120,15 @@ packages: - liblapacke 3.11.0 10*_openblas - mkl <2027 license: BSD-3-Clause + license_family: BSD run_exports: weak: - libblas >=3.11.0,<4.0a0 size: 17975 timestamp: 1788076997926 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda - sha256: e5864f257f839ffc27d681659bac95901f524f602b9121e5dcc5e2df18437f2d - md5: 7a2499a177753582fb7ae7e9dc4a908a +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + sha256: c4e9854c585f3ee1785f651287ecdf8c28f30a2ea1b159a513005f8a0f6453a3 + md5: df210655e30a05e3b069c91b7aaddd2d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 @@ -4179,36 +4137,36 @@ packages: run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 80265 - timestamp: 1786622773969 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda - sha256: dad31b6d104973deb89710929a35651033aad692d4e7793cdb5b786a9bd54678 - md5: 6ab3315dc56618d652c1da42a648a129 + size: 80561 + timestamp: 1788480364653 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + sha256: d5ed24da3e583a92227fe77280978b7933f07a39a2b60156c5ad95e5c76733fa + md5: 0cfd601eb03cca403dcc248844787486 depends: - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.2.0 h39a168f_3 + - libbrotlicommon 1.2.0 h39a168f_4 - libgcc >=15 license: MIT license_family: MIT run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 34828 - timestamp: 1786622783405 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda - sha256: d37124d0f51816e7d5e3a94bfc9ed3d6174d077f9b4f832d20c5a08b52bebf1f - md5: 2ac965638d4c6b2b38383bb1aebaf543 + size: 34739 + timestamp: 1788480374261 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda + sha256: b9f3cb510337655485c2e79ce5f7dd468991b19ad29723ffa1175a0aed1b69ae + md5: 4136f6e4ef4835cc7df39a707cbd511c depends: - __glibc >=2.17,<3.0.a0 - - libbrotlicommon 1.2.0 h39a168f_3 + - libbrotlicommon 1.2.0 h39a168f_4 - libgcc >=15 license: MIT license_family: MIT run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 298639 - timestamp: 1786622792145 + size: 298134 + timestamp: 1788480383223 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda build_number: 10 sha256: 90fd992748d501f6efe2b8310aa81f5ee4a63629450dd88e851ba4ab757f3c3a @@ -4220,6 +4178,7 @@ packages: - liblapack 3.11.0 10*_openblas - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -4238,6 +4197,7 @@ packages: - libzlib >=1.3.2,<2.0a0 - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception + license_family: APACHE run_exports: weak: - libclang-cpp23.1 >=23.1.0,<23.2.0a0 @@ -4257,6 +4217,7 @@ packages: - libzlib >=1.3.2,<2.0a0 - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception + license_family: APACHE run_exports: weak: - libclang13 >=23.1.0 @@ -4291,9 +4252,9 @@ packages: - libcups >=2.3.3,<2.4.0a0 size: 4518030 timestamp: 1770902209173 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda - sha256: 0b6cc13e36cf19ae9b764740112fc591682e189c99353be9f72f3d96ccf29d79 - md5: 0ed167049513943d078a6c997df2b4e0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda + sha256: 483b7eb97f56fbb3a9cb7190d33012dfa0a5aaed99ede61465563cd97dd82504 + md5: e617deee7af8eb0c04f6296d12b54157 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 @@ -4302,15 +4263,15 @@ packages: - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT run_exports: weak: - - libcurl >=8.21.0,<9.0a0 - size: 484517 - timestamp: 1787183722915 + - libcurl >=8.22.0,<9.0a0 + size: 499651 + timestamp: 1788340719230 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda sha256: 82e134c8a08b1eed9a2ed8ab578b89aa1730dcde3dea8dd87645ed0637878e54 md5: 40f9b31aa9cf007789867df0decd0492 @@ -4390,19 +4351,20 @@ packages: - libev >=4.33,<4.34.0a0 size: 43220 timestamp: 1785917328200 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda - sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 - md5: a1cfcc585f0c42bf8d5546bb1dfb668d +- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda + sha256: f874779772878f3b509c607de9089c282f71232fde4cac876fc660d29670f28d + md5: 2e699c6e151045b2cfea34b53ed69439 depends: - - libgcc-ng >=12 - - openssl >=3.1.1,<4.0a0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - openssl >=3.5.8,<4.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 - size: 427426 - timestamp: 1685725977222 + size: 431007 + timestamp: 1788870247867 - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda sha256: 16feffd9ddbbe5b718515d38ee376c685ba95491cd901244e24671d20b952a77 md5: b24d3c612f71e7aa74158d92106318b2 @@ -4502,50 +4464,50 @@ packages: - libgd >=2.3.3,<2.4.0a0 size: 177306 timestamp: 1766331805898 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda - sha256: 386644b941e727549a88af3da791764478d6aecf42630012f6dcf0e8006fa93c - md5: 13e691ba803adfed23773eb0a46e78fc +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.13.3-h31aba09_1.conda + sha256: b447ca08212fe296e1788bb4ba74336fddfb40abf037cd377fe28aea4a625277 + md5: 44c6780e3082468d8676aebdcd5068a6 depends: - - libgcc >=15 - - libstdcxx >=15 - __glibc >=2.17,<3.0.a0 - - lz4-c >=1.10.0,<1.11.0a0 - - libexpat >=2.8.1,<3.0a0 - - libpng >=1.6.58,<1.7.0a0 - - liblzma >=5.8.3,<6.0a0 - - libiconv >=1.18,<2.0a0 - - json-c >=0.18,<0.19.0a0 + - libstdcxx >=15 + - libgcc >=15 + - geos >=3.14.1,<3.14.2.0a0 - muparser >=2.3.5,<2.4.0a0 - - openssl >=3.5.7,<4.0a0 - - libdeflate >=1.25,<1.26.0a0 - - libsqlite >=3.53.4,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - - lerc >=4.2.0,<5.0a0 - libjpeg-turbo >=3.2.0,<4.0a0 - - geos >=3.14.1,<3.14.2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - libarchive >=3.8.9,<3.9.0a0 - - blosc >=1.21.6,<2.0a0 + - libpng >=1.6.58,<1.7.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + - json-c >=0.18,<0.19.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 - giflib >=6.1.3,<6.2.0a0 - - libkml >=1.3.0,<1.4.0a0 - - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 - libjxl >=0.12.0,<0.13.0a0 - libcurl >=8.21.0,<9.0a0 - - xerces-c >=3.3.0,<3.4.0a0 - - libwebp-base >=1.6.0,<2.0a0 + - openssl >=3.5.8,<4.0a0 + - libxml2 + - libxml2-16 >=2.15.3 + - libarchive >=3.8.9,<3.9.0a0 + - libzlib >=1.3.2,<2.0a0 + - liblzma >=5.8.3,<6.0a0 - proj >=9.8.1,<9.9.0a0 - - pcre2 >=10.47,<10.48.0a0 + - libkml >=1.3.0,<1.4.0a0 + - libexpat >=2.8.1,<3.0a0 + - lerc >=4.2.0,<5.0a0 - libspatialite >=5.1.0,<5.2.0a0 + - libsqlite >=3.53.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libiconv >=1.18,<2.0a0 + - blosc >=1.21.6,<2.0a0 constrains: - - libgdal 3.12.4.* + - libgdal 3.13.3.* license: MIT license_family: MIT run_exports: weak: - - libgdal-core >=3.12.4,<3.13.0a0 - size: 14240911 - timestamp: 1786825194007 + - libgdal-core >=3.13.3,<3.14.0a0 + size: 15195348 + timestamp: 1788235554900 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda sha256: 7653be4d88a4d74676c38dd892914d027dcecc0c65c406be772d31cb641f8cfd md5: 5e92b8413fd1c8f8f3006f4661b12def @@ -4595,24 +4557,24 @@ packages: - libgl >=1.7.0,<2.0a0 size: 116212 timestamp: 1787310061570 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda - sha256: af6c14f387f810c5df491b328ae955329596d3bc73b1e2e091ab5adb46a10759 - md5: 533d342dedadf134c67760ce6fc5b748 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda + sha256: f57bf3954dbba8b27ec68540e84f8b2c2375ffa5702f89ca9741e17329fc3953 + md5: d216efacca3f34f2b13ddd1632f53833 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=15 - - pcre2 >=10.47,<10.48.0a0 - - libzlib >=1.3.2,<2.0a0 + - __glibc >=2.17,<3.0.a0 - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 - libffi >=3.7.0,<3.8.0a0 + - libzlib >=1.3.2,<2.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4758097 - timestamp: 1787884091247 + size: 4758346 + timestamp: 1788525203514 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda sha256: 6eb77601a44b78c4631d886d54ef54f321c2bdc69fdefc4065a1e0491f030c76 md5: cfcf11edcfd4acf0094771a9c3b8587f @@ -4726,9 +4688,9 @@ packages: - libgrpc >=1.82.1,<1.83.0a0 size: 6957755 timestamp: 1783588701960 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda - sha256: a064695a1c12133d6a9dcf6b3b42423b8614fa45667606201af1b5f72628df0d - md5: 4463210c2a16ff5d3bf7f502af23f1f4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda + sha256: 979b14ba13054aab9e90363809aac470a58b7c37203c330216327356a98c8f60 + md5: c44470b6bfa6a582cb4dd42cbda3401e depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 @@ -4744,11 +4706,11 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 1380045 - timestamp: 1787795176798 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_0.conda - sha256: bdecc73c22cb0a8d44ce066116562e35322fd6c1c04584a4754ae2befcab4cb3 - md5: 26e37e05324d7bb723350d50b33057fc + size: 1382623 + timestamp: 1788405508587 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.4.0-h23af247_1.conda + sha256: 11f08038c165e354c2a7064ce6bcb28dad2668b330931572666f418e2a8f623c + md5: 148cd995f5ba8af22412a4258abc2f94 depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 @@ -4759,7 +4721,7 @@ packages: - libfreetype6 >=2.14.3 - libgcc >=15 - libglib >=2.88.3,<3.0a0 - - libharfbuzz 14.4.0 h23af247_0 + - libharfbuzz 14.4.0 h23af247_1 - libpng >=1.6.58,<1.7.0a0 - libstdcxx >=15 - libzlib >=1.3.2,<2.0a0 @@ -4768,8 +4730,8 @@ packages: run_exports: weak: - libharfbuzz >=14.4.0 - size: 2142051 - timestamp: 1787795196934 + size: 2144076 + timestamp: 1788405523805 - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h57c4cff_1.conda sha256: 23c7cf726b883f5e7c1bfa6bf24bceafa8be87245a7690e0b5c974eb320a9e83 md5: d58bfbaaf51ed85771eae92c2e7f5816 @@ -4852,6 +4814,7 @@ packages: - libcblas 3.11.0 10*_openblas - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -4868,6 +4831,7 @@ packages: constrains: - blas 2.310 openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - liblapacke >=3.11.0,<3.12.0a0 @@ -4905,63 +4869,63 @@ packages: - liblzma >=5.8.3,<6.0a0 size: 112995 timestamp: 1786348617826 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h3b59866_0.conda - sha256: 1b9ca94f36f6072ff71550dfaa8dd4106a118bf01f352ed639aef134b462bab6 - md5: 444e55c79d67040779af3a0167963bea +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-2.9.0-py314h6ba947b_0.conda + sha256: 23d1709bd2db46fb58f18059064b1efce36d0b99788b440dc2e6d0b80aa23773 + md5: 6dd4342973ab963fe98e1fcd3df5d9e2 depends: - cpp-expected >=1.3.1,<1.3.2.0a0 - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - simdjson >=4.6.6,<4.7.0a0 - - yaml-cpp >=0.8.0,<0.9.0a0 + - libstdcxx >=15 + - libgcc >=15 + - libmsgpack-c >=6.1.0,<7.0a0 - libarchive >=3.8.9,<3.9.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libsolv >=0.7.39,<0.8.0a0 + - openssl >=3.5.8,<4.0a0 - libcurl >=8.21.0,<9.0a0 - - libmsgpack-c >=6.1.0,<7.0a0 - - reproc >=14.2.7.post0,<14.3.0a0 - - reproc-cpp >=14.2.7.post0,<14.3.0a0 - - openssl >=3.5.7,<4.0a0 + - reproc-cpp >=14.2.8.post0,<14.3.0a0 - fmt >=12.1.0,<12.2.0a0 + - reproc >=14.2.8.post0,<14.3.0a0 + - simdjson >=4.6.9,<4.7.0a0 - spdlog >=1.17.0,<1.18.0a0 + - yaml-cpp >=0.8.0,<0.9.0a0 - nlohmann_json-abi ==3.12.0 - - zstd >=1.5.7,<1.6.0a0 - - libsolv >=0.7.39,<0.8.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - libmamba >=2.9.0,<2.10.0a0 - size: 2850628 - timestamp: 1786125663902 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-hf1e253f_0.conda - sha256: ddae16548197cf30cf72d700733427cb314ce6fa2921fea7e2922566288de42e - md5: 1066c66681f6a1d6e36d13dc3eecbd50 + size: 2892452 + timestamp: 1788274100901 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmamba-spdlog-2.9.0-h5efa3ab_0.conda + sha256: 9830f335120fc6107bc9083ff19a87d1787439195ee63c1aa47ad122cb8014b1 + md5: ebe4fe5bfcc3e9217c94fe8926696952 depends: - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 + - libstdcxx >=15 + - libgcc >=15 - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 20529 - timestamp: 1786125663902 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314hee872f7_0.conda - sha256: 0c2f07dfcb61e14a357df8816abb236e1705b92998ba068784f87fd74070aa85 - md5: 231869ede4419e814ca7d3de6fabdf95 + size: 20564 + timestamp: 1788274100901 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmambapy-2.9.0-py314h959206d_0.conda + sha256: 24eab00fb8939d7baaaaa637b82619c319d61561f230b8992262e3fdbc2d5992 + md5: 0e62ff51c2e07727b3617619a76c0edb depends: - python - - libmamba ==2.9.0 py314h3b59866_0 - - libmamba-spdlog ==2.9.0 hf1e253f_0 + - libmamba ==2.9.0 py314h6ba947b_0 + - libmamba-spdlog ==2.9.0 h5efa3ab_0 - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 - - libgcc >=14 - - openssl >=3.5.7,<4.0a0 + - libstdcxx >=15 + - libgcc >=15 - fmt >=12.1.0,<12.2.0a0 + - openssl >=3.5.8,<4.0a0 + - nlohmann_json-abi ==3.12.0 + - yaml-cpp >=0.8.0,<0.9.0a0 - spdlog >=1.17.0,<1.18.0a0 - python_abi 3.14.* *_cp314 - - yaml-cpp >=0.8.0,<0.9.0a0 - - nlohmann_json-abi ==3.12.0 - zstd >=1.5.7,<1.6.0a0 - libmamba >=2.9.0,<2.10.0a0 - pybind11-abi ==11 @@ -4970,8 +4934,8 @@ packages: run_exports: weak: - libmambapy >=2.9.0,<2.10.0a0 - size: 967399 - timestamp: 1786125663902 + size: 992019 + timestamp: 1788274100901 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda sha256: 46820b4a835e175940ae20bec00fdddaff804cda27a1408ab1b95e77a2196437 md5: fcfed1dc5053eb1901b66e7b1fc32588 @@ -5053,6 +5017,7 @@ packages: constrains: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - libopenblas >=0.3.34,<1.0a0 @@ -5142,22 +5107,22 @@ packages: - libpng >=1.6.58,<1.7.0a0 size: 316643 timestamp: 1786616563127 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda - sha256: b1eb210c180fda9c445b11cba1286ec250ce2c2a85ccb0551a4ed5423df51e68 - md5: 72e019c04ed02a179da38c56965802d1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_1.conda + sha256: f858ecc9bcca455449f95e12a61f01264c239e6a176d86e09ea3b85bf0be4ff7 + md5: 06b25748479ce9516502e28332707f11 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=15 - openldap >=2.6.13,<2.7.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 license: PostgreSQL run_exports: weak: - libpq >=18.6,<19.0a0 - size: 2719680 - timestamp: 1786641133110 + size: 2726898 + timestamp: 1788538987316 - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda sha256: 37c51fbc936a6bb8bf5f67c8f056edacaaa41e8603738bc8a4dee186292bb114 md5: fd307b61cceb36fdca38e1718bdb2893 @@ -5190,18 +5155,30 @@ packages: - libpsl >=0.23.1,<0.24.0a0 size: 72519 timestamp: 1786970753847 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_104_cp314.conda - build_number: 104 - sha256: ea102c446220e9b8ad2125e38e0f4a0d9b04a7fc10193eca8d130bee507ce9e2 - md5: eb63e79ef1ac24c034d5b6ab3bcbd00c +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + build_number: 3 + sha256: 6be16a4906d83eb8e9e04375d524f339f426a847cffd294f1ceb0611988dc17a + md5: d247b7632f09324c11f24b5270361385 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 - libstdcxx >=15 license: Python-2.0 run_exports: {} - size: 10507939 - timestamp: 1788161778972 + size: 8770625 + timestamp: 1788392466381 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.7-hdc7f604_106_cp314.conda + build_number: 106 + sha256: 5f879892bd439c3d94f4a97b06c214b9a19c4b92f74d84f0305c6ca1b2b239b2 + md5: 28af2158bb8dbb62e55b0af3b44f9d4a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + license: Python-2.0 + run_exports: {} + size: 10520530 + timestamp: 1788383918299 - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda sha256: ab3ccb9fd5816f76b18ee8974d359cd2605ca87d8ddef55369dc461b9986f95c md5: 3ac89a48d224409739dbf6200e524373 @@ -5431,19 +5408,19 @@ packages: - libutf8proc >=2.11.3,<2.12.0a0 size: 85969 timestamp: 1768735071295 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - sha256: 9b1bdce27a7e31f7d241aeecff67a1f3101d52a2b1e33ccc2cdf2613072bf81f - md5: 01bb81d12c957de066ea7362007df642 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda + sha256: aa58bbba56644ffd062a4a9b358782c5eb7560ccead2ab8f7c5c6ede0a7d33a6 + md5: 74a0a409d9f4561265d36b789d3f398a depends: - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 license: BSD-3-Clause license_family: BSD run_exports: weak: - - libuuid >=2.42.2,<3.0a0 - size: 40017 - timestamp: 1781625522462 + - libuuid >=2.42.3,<3.0a0 + size: 39998 + timestamp: 1788347719520 - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda sha256: a70c25b66321ee77f9892b205e3247263c400803f543dc8aca53d569a59c5a77 md5: c5f5f159b66332152197893427071695 @@ -5523,77 +5500,77 @@ packages: - libxkbcommon >=1.13.2,<2.0a0 size: 942571 timestamp: 1787178780572 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda - sha256: 087d4de023f22d6a28b9e1b818961dd41e9bda6e9793590600ff16ca150bf9cb - md5: 20e4d67ec908f0405124f11612c48305 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + sha256: 9a1685c8d8b8488cd7882a9cd6e673e687211ca763ebbcd1e91b6f594eeadc45 + md5: b7545c57a73a51d72aa6eee5695cf051 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - - libgcc >=14 + - libgcc >=15 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - - libxml2 2.15.3 + - libxml2 2.15.4 license: MIT license_family: MIT run_exports: {} - size: 559721 - timestamp: 1787237579170 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - sha256: a16a576a5844a3a0e1cdfc5e162b9fe9c64dccf6a93f44c293bb42851aebe2b4 - md5: 2d34cdb31014cbc8f1e9384c89ede0a5 + size: 569196 + timestamp: 1788635197456 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda + sha256: 053d85821ce5e36b7ce03faea5930382f6af695df9e2cd68e5d8d273072d1551 + md5: ea192d1ef556bf1d55de4acb8c0f5b65 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - - libgcc >=14 + - libgcc >=15 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 hca6bf5a_1 + - libxml2-16 2.15.4 hf3af7cc_0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 46203 - timestamp: 1787237584107 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda - sha256: 443ff3b29df8eab5b075abed87e4edbf59ca5602f627a79bffe5b0855ea40ce9 - md5: f986d9c9de80813d076d235f753889ff + - libxml2-16 >=2.15.4 + size: 47077 + timestamp: 1788635202698 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.4-h7df9aa5_0.conda + sha256: 17cf9286fd38ced7b101f2f778b8288612432d451c8bd26fb9aa0a6088dfee5d + md5: 9a414200ffe436e47d72c9f4c11c3658 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - - libgcc >=14 + - libgcc >=15 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h49c6c72_1 - - libxml2-16 2.15.3 hca6bf5a_1 + - libxml2 2.15.4 h7df9aa5_0 + - libxml2-16 2.15.4 hf3af7cc_0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 80644 - timestamp: 1787237588477 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda - sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 - md5: 87e6096ec6d542d1c1f8b33245fe8300 + - libxml2-16 >=2.15.4 + size: 80619 + timestamp: 1788635207450 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_1.conda + sha256: 3beb51b5ce8d2e0690b8517d3d955972d502cfbe2899279cbb9cbb6ff134d360 + md5: 5c8ce6af1772fcfc2b86d17b825abc8e depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 license: MIT license_family: MIT run_exports: weak: - - libxslt >=1.1.43,<2.0a0 - size: 245434 - timestamp: 1757963724977 + - libxslt >=1.1.45,<2.0a0 + size: 250314 + timestamp: 1788534852474 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 md5: 0de0122d9570a8ab637c6b73db268389 @@ -5755,32 +5732,34 @@ packages: - minizip >=4.2.2,<5.0a0 size: 526042 timestamp: 1788051267376 -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda - sha256: ecc6920ef31af5a646ee162dd5ee0b0096a5b9ec07a6377aaf6715a0b9dd502d - md5: 881c695332c119ffd74b0a856f2c8fba +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda + sha256: 147ba1c9ebaeba03c95c1ac018523978c8a07387e2413799b09b69017e976b58 + md5: e9600e8080bf57f25ad2e9f78564c895 depends: - python + - __glibc >=2.17,<3.0.a0 - libstdcxx >=15 - libgcc >=15 - - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 + license_family: APACHE run_exports: {} - size: 114265 - timestamp: 1787850881233 -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_0.conda - sha256: ab2c6627819bdafc603763f599e65383879a52d4f17073baa0ea9a5920dd15a1 - md5: 66e5d65a13ac23cd29ca87eb858c7d27 + size: 114404 + timestamp: 1788525103553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py314h5383ef5_2.conda + sha256: c327cc6a76ff537529d48e3a9193efdab0d1f87e216dd9f42a757692ef278555 + md5: 12730234204506e89076693b125e3c61 depends: - python - - __glibc >=2.17,<3.0.a0 - libstdcxx >=15 - libgcc >=15 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: Apache-2.0 + license_family: APACHE run_exports: {} - size: 115256 - timestamp: 1787850879389 + size: 115255 + timestamp: 1788525124042 - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda sha256: c9322797c769560e96025d294f0daacbcf7ed95ae68903dd2e398ffb87fa2c73 md5: 7de84fd76ec2bdb7ac617507de3d3052 @@ -5789,14 +5768,15 @@ packages: - libgcc >=15 - __glibc >=2.17,<3.0.a0 license: MIT + license_family: MIT run_exports: weak: - muparser >=2.3.5,<2.4.0a0 size: 233133 timestamp: 1788122511702 -- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hc824af9_0.conda - sha256: fc92573d64890aa2b33f2ce52d35a8816569aa1423e7ec467271e608154328ce - md5: 98c8f0218a5ba4e6d9c7b8bbd9e80078 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py314hfe1a184_1.conda + sha256: 0c113f12041a8e068d2b73f535ab6630d49d419413186b7d4887b9da8c4db05a + md5: 9b210abd1f1635ae57997aae3ed6d262 depends: - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 @@ -5805,14 +5785,14 @@ packages: - python-librt >=0.13.0 - typing_extensions >=4.6.0 - psutil >=4.0 - - __glibc >=2.17,<3.0.a0 - libgcc >=15 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT run_exports: {} - size: 23721948 - timestamp: 1786887255055 + size: 23722140 + timestamp: 1788615817637 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 md5: ee6c0cd80a60961a1f48aa3e0b91f986 @@ -5835,63 +5815,65 @@ packages: run_exports: {} size: 136372 timestamp: 1785920438981 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda - sha256: be778735f693d6c1e9b877d7a28fd7d9e8c0d695c5b93a4dd5b8ec4a4317ca13 - md5: e70abe6ab4c60ecb6a51e67903bcec07 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda + sha256: 5b0e4c7757a2df883c5615a8f64bc4c113c4cd303ae6caa3ada5d397d114ce2a + md5: 5abf073f2cf066f2f3d28229b3cbe94d depends: - python - - __glibc >=2.17,<3.0.a0 - - libgcc >=15 - libstdcxx >=15 - - liblapack >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - numpy >=1.25,<3 - size: 9212503 - timestamp: 1788129260074 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py314hd3a7d6b_1.conda - sha256: d1d5c1e8025bb2389ade51a717f73d34f72b3fb4c6858f54ca637465b3941502 - md5: 8b80e8395626350a957f3b7ad1348cbe + size: 9230861 + timestamp: 1788731992112 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py314hd3a7d6b_0.conda + sha256: 2aeb4c5a0869a84e69b4e57bfd04911cd148070d667bc598f3bb85366ee40607 + md5: d1ae35d69e57534c8713ff0368b9fdb9 depends: - python - __glibc >=2.17,<3.0.a0 - - libgcc >=15 - libstdcxx >=15 - - libblas >=3.9.0,<4.0a0 - - python_abi 3.14.* *_cp314 - - libcblas >=3.9.0,<4.0a0 + - libgcc >=15 - liblapack >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 + - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - numpy >=1.25,<3 - size: 9372636 - timestamp: 1788129259187 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d - md5: 11b3379b191f63139e29c0d19dee24cd + size: 9395024 + timestamp: 1788731986404 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda + sha256: 131688a88bea8da92fe418c4558c5073435e2848911bc4c836c3a9dd5994b4aa + md5: a3f3ac7a68d01c198e276dbb99e62032 depends: + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libpng >=1.6.50,<1.7.0a0 - - libstdcxx >=14 - - libtiff >=4.7.1,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 + - libstdcxx >=15 + - libtiff >=4.7.2,<4.8.0a0 + - libzlib >=1.3.2,<2.0a0 + - libpng >=1.6.58,<1.7.0a0 license: BSD-2-Clause license_family: BSD run_exports: weak: - openjpeg >=2.5.4,<3.0a0 - size: 355400 - timestamp: 1758489294972 + size: 391242 + timestamp: 1788425045145 - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda sha256: 21c4f6c7f41dc9bec2ea2f9c80440d9a4d45a6f2ac13243e658f10dcf1044146 md5: 680608784722880fbfe1745067570b00 @@ -6057,29 +6039,29 @@ packages: run_exports: {} size: 15360863 timestamp: 1785276230218 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda - sha256: 48f27a6c3e4062bc09dafe9c7f6b288c5de5655e81095ab7f1aad920b2163b7b - md5: 6a2822aaf9a34ac3708904a47ff3dd7e +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-h7bb47b9_1.conda + sha256: 47c3d25a0bdb0ae50627f9ef2a10345fabfabe0e6620cbcff04e589a644542d3 + md5: 0b889b1c6b472d215d6332c6b195589c depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - - libgcc >=14 + - libgcc >=15 - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 + - libharfbuzz >=14.4.0 - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later run_exports: weak: - pango >=1.58.2,<2.0a0 - size: 469916 - timestamp: 1786107384537 + size: 471814 + timestamp: 1788490377724 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda sha256: 9ebb10a4eb3be51ae67d85c679f5a7a50cb040ed25c783e6331a225ea09991d4 md5: 06f6113cf1ff4a54b65f87ead132a5f1 @@ -6095,28 +6077,28 @@ packages: - pcre2 >=10.47,<10.48.0a0 size: 1218833 timestamp: 1787294571916 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda - sha256: 76b2e56c7a903a06be1210d35f35c2a8dcdc57912fda5c96b2e68acfd2b281fe - md5: d749d04e1965315078f29320565c595a +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_2.conda + sha256: fee856a3b7492ca0c6be99525443409a4b3403de3a71242949d5e6c5c3a8b024 + md5: 5752c2b6fa529061baba2819e4c2b143 depends: - python - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - libtiff >=4.7.1,<4.8.0a0 - - libxcb >=1.17.0,<2.0a0 - tk >=8.6.13,<8.7.0a0 - - openjpeg >=2.5.4,<3.0a0 - - zlib-ng >=2.3.3,<2.4.0a0 - - python_abi 3.12.* *_cp312 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - lcms2 >=2.19.1,<3.0a0 + - libxcb >=1.17.0,<2.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 + - lcms2 >=2.19.1,<3.0a0 + - python_abi 3.12.* *_cp312 + - openjpeg >=2.5.4,<3.0a0 - libwebp-base >=1.6.0,<2.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 license: HPND run_exports: {} - size: 1064129 - timestamp: 1782912080163 + size: 1060104 + timestamp: 1788610327434 - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda sha256: 829d8288764282de5a9f7b9169acb75cc7dc0b6c3fe2535cfe87dea3436bbc5d md5: 0ee5bb30034b081a1386c1e2c98ab0a7 @@ -6170,22 +6152,22 @@ packages: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 199544 timestamp: 1730769112346 -- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda - sha256: 2c6e88ac7aa5384fb843cb96023deb4382d6d5afbc81f5db1c7a1ef09c9e3090 - md5: 1653ce584b7859810f116e592eba9c0c +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda + sha256: 74522f28cf6b905f89771b5b3367966280285dca5b5b8cd09f69c3bfe95c637c + md5: e59300b9232e82a351a9390bfdfe72f9 depends: - python - - libgcc >=15 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 225418 - timestamp: 1787417371835 -- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_1.conda - sha256: d4c4c9e1bcaeb38e4c4b6a17e8e0b25f8083e03d11813b872427bd47c9bfe1ca - md5: 1dadeb3cb86afd90e106395730cd87aa + size: 225441 + timestamp: 1788189998857 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314hfe1a184_2.conda + sha256: 0ee8d587391ee0c010cdd9071554ed319fed88dff624d900af59e5d360ea4e6d + md5: 5cc16f58b51ad07ea206e5a838645824 depends: - python - libgcc >=15 @@ -6194,19 +6176,19 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 231304 - timestamp: 1787417370367 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - sha256: afc3b27b2cbb0487c1d0e963f96e71181ecfb623a24fb393bb19ff974a6382a1 - md5: df2c27f36bdb0dde779f55b5df76a352 + size: 231457 + timestamp: 1788190001815 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + sha256: 4a44fd00ea73b79ca2c89b0727b9ccf61c506ead71e67a9abfa4c590042b5a4a + md5: bb66b610707b811e3c65181a6431e7a8 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT run_exports: {} - size: 9115 - timestamp: 1786067714761 + size: 9630 + timestamp: 1788381877753 - conda: https://conda.anaconda.org/conda-forge/linux-64/pulp-2.8.0-py314h312e2f4_3.conda sha256: 32354767a80f6650ad043356a975e7562c7286f7b2cdbb2acfe15bd057508e2f md5: 057e330e50d141ee706f1d4c46594979 @@ -6220,10 +6202,10 @@ packages: run_exports: {} size: 230470 timestamp: 1757853295064 -- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_3.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/py-rattler-0.25.0-py310hefeb9ab_5.conda noarch: python - sha256: 673052cec285da1db0198313277b077103b8f235b12b9564cce8af5222b4af70 - md5: 2a75e016bb35e33e2acb65ee2aa4f0af + sha256: 6bf07e71961b9c63169056f638ab00e0504f24b6d4c57e0bb6c5375b4a9bf89f + md5: eeb15389d638a5a99098e9471ec35182 depends: - python - __glibc >=2.17,<3.0.a0 @@ -6234,9 +6216,10 @@ packages: constrains: - __glibc >=2.17 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 12066610 - timestamp: 1787913197340 + size: 12072905 + timestamp: 1788524945832 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda sha256: 9b6c4aed5da2a3b8f649e1b73596e0867a5506bed9c3aa45dbaedfb674822191 md5: 6f1918b7565c3cc5ed047a97ea510e55 @@ -6273,19 +6256,19 @@ packages: run_exports: {} size: 5395858 timestamp: 1784258391353 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h5bd0f2a_4.conda - sha256: 0d3cbeb41afef769fe2d8ab7e0e6a2140c8577018ae5e237839b2eae9d571747 - md5: 0fa51561c73d1f362997f8aceab87146 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pycosat-0.6.6-py314h89acca1_5.conda + sha256: 6e6a694d8dddf33b0909ebe6890440e7b8360f827ef103a68a1d908485459ee6 + md5: 01741d80e5912222a870ea6aa68f596a depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT run_exports: {} - size: 88896 - timestamp: 1784146233775 + size: 88467 + timestamp: 1788489357282 - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda sha256: a805a64e0e7e5ed22e8f49f0038a5d8cab4b96cf094c8822da75d6823f2377e8 md5: 2854d36e093d53d0cf81ec433ffd9c4c @@ -6298,6 +6281,7 @@ packages: constrains: - __glibc >=2.17 license: MIT + license_family: MIT run_exports: {} size: 1877711 timestamp: 1787928982283 @@ -6313,16 +6297,17 @@ packages: constrains: - __glibc >=2.17 license: MIT + license_family: MIT run_exports: {} size: 1876160 timestamp: 1787928980353 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda - sha256: 2b0a366d9066e3d9f495369b95cdb1b9d3dba2f59577e4560b7d1086e1fe3d70 - md5: f8e7e5ddfbdca16b65335b0b6615eb4c +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.13.0-py312hdb6ebaa_0.conda + sha256: 99e8fdd42f008ef9e955690efd1790a1182ae5d2eb48e8d8da6f6a1e59bd2899 + md5: 0b2a0e17bd514b53e2d12b8ac16225ae depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - libgdal-core >=3.12.0,<3.13.0a0 + - libgdal-core >=3.13.1,<3.14.0a0 - libstdcxx >=14 - numpy - packaging @@ -6331,65 +6316,68 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 661347 - timestamp: 1764402531050 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda - sha256: 8416187ef4766eb2de7bad6e751f0aced2f773c3853057c0eb848b345fedc1aa - md5: 42e2d36478a1fcc0dbc6358ffdb46786 + size: 690230 + timestamp: 1782492770379 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.8.0-py312hb99b6cc_0.conda + sha256: 3724e62961c35f5388b737f9689bf1d32a6c1ff3a667102d1f2d032f80986a8d + md5: ec4b76cc3656be76703e2d1b9ebab9ea depends: - python - proj - certifi - - libgcc >=15 - __glibc >=2.17,<3.0.a0 - - python_abi 3.12.* *_cp312 + - libgcc >=15 - proj >=9.8.1,<9.9.0a0 + - python_abi 3.12.* *_cp312 license: MIT + license_family: MIT run_exports: {} - size: 572689 - timestamp: 1787945032861 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda - sha256: 4e25d9259683126c377b219a741e081f7b8daa63aa256435d037cdaa3f453589 - md5: bfcdbf7dfa025bf855ffbf44181c143e + size: 591011 + timestamp: 1788664410288 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf520645_1.conda + sha256: b2b366746414187fc76b1c0ac5b875dcd48b51d0809c42739d6e8b21d34d3c48 + md5: c4121a6f49ae4e9e9beae7f70530b3be depends: - python - qt6-main 6.11.2.* - - __glibc >=2.17,<3.0.a0 - libgcc >=15 - libstdcxx >=15 + - __glibc >=2.17,<3.0.a0 + - libxslt >=1.1.45,<2.0a0 - python_abi 3.12.* *_cp312 - - libclang13 >=22.1.8 - libopengl >=1.7.0,<2.0a0 + - qt6-main >=6.11.2,<7.0a0 + - libclang13 >=23.1.0 - libgl >=1.7.0,<2.0a0 - libegl >=1.7.0,<2.0a0 - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.4 - libvulkan-loader >=1.4.357.0,<2.0a0 - - qt6-main >=6.11.2,<7.0a0 - - libxslt >=1.1.43,<2.0a0 license: LGPL-3.0-only license_family: LGPL run_exports: {} - size: 13943954 - timestamp: 1787219437874 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda - sha256: ceb9c724de53ee3560f121dbfcb00fe8acb22c08691158fce7b6c32425855626 - md5: e9dcdd23a1c68738eb3257d23d5f7285 + size: 13944078 + timestamp: 1788744980531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + build_number: 3 + sha256: 14c579b1016da04e4c9f1c5c857272d83ec447317d8c4074a07d59de3cef70ef + md5: 98be3cf76eca2e8871f907a03aed3b84 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.8.1,<3.0a0 - libffi >=3.7.0,<3.8.0a0 - - libgcc >=14 + - libgcc >=15 - liblzma >=5.8.3,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 + - libpython 3.12.14 h0c77377_3_cpython - libsqlite >=3.53.4,<4.0a0 - - libuuid >=2.42.2,<3.0a0 + - libuuid >=2.42.3,<3.0a0 - libxcrypt >=4.4.38 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata @@ -6401,12 +6389,12 @@ packages: - python_abi 3.12.* *_cp312 noarch: - python - size: 31590137 - timestamp: 1787353586056 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_104_cp314.conda - build_number: 104 - sha256: 5aaf3af8d4f99541fef4e746ae58677acda6ce02cfa5751abc3d1cc29ea8f732 - md5: 663015cba592a7375ca3c465af84186d + size: 23044161 + timestamp: 1788392496306 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.7-hcd007b5_106_cp314.conda + build_number: 106 + sha256: 4cd05407d6b07d00fd5b6cc78bd2f60ae5e21f777eb26ead82be2e3751c610a1 + md5: 56ee91e118243e46bfc0c41e4030d354 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 @@ -6416,9 +6404,9 @@ packages: - libgcc >=15 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - - libpython 3.14.7 hdc7f604_104_cp314 + - libpython 3.14.7 hdc7f604_106_cp314 - libsqlite >=3.53.4,<4.0a0 - - libuuid >=2.42.2,<3.0a0 + - libuuid >=2.42.3,<3.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - openssl >=3.5.8,<4.0a0 @@ -6433,22 +6421,22 @@ packages: - python_abi 3.14.* *_cp314 noarch: - python - size: 26435588 - timestamp: 1788161824322 + size: 26484459 + timestamp: 1788383955577 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314h0f05182_0.conda - sha256: 2655bda3e0f53863c9ba1239aa9212b399141582074d0c043df6f449ff21b0f1 - md5: 102786d7a63373e0441273b7d863d4ea +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py314hfe1a184_2.conda + sha256: 8efe9b332ca52740959528c1482e8dc4a1c3ad25d9e38a4c9e344892919eec9a + md5: 4d081143af3368d7a9047e6c46752ab6 depends: - python - - libgcc >=14 - __glibc >=2.17,<3.0.a0 + - libgcc >=15 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT run_exports: {} - size: 162846 - timestamp: 1786328851615 + size: 167805 + timestamp: 1788524703293 - conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py314h0f05182_2.conda sha256: 17cd445cbdf0a33a247934497c867195edc2bf51faad8011c74332460d47634b md5: 711ddd4f498e10b56f687683ae76e059 @@ -6595,9 +6583,9 @@ packages: - qt6-main >=6.11.2,<7.0a0 size: 60888449 timestamp: 1787048975419 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda - sha256: 273497d08e9742690399a6e738d227ccd6b7ca62f0fe9faa6cb3a91d5e4ba991 - md5: 38f624a32029e9fced9ed40fa8c644db +- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.1-py312h5d657d5_1.conda + sha256: 75a098544de4e265b36f05f26655bf2c62a64549d5baa0e2060d52fdbe807fcf + md5: c0d4e6e0b2f6a3e73ab81630deab839d depends: - __glibc >=2.17,<3.0.a0 - affine @@ -6607,7 +6595,7 @@ packages: - click-plugins - cligj >=0.5 - libgcc >=14 - - libgdal-core >=3.12.4,<3.13.0a0 + - libgdal-core >=3.13.2,<3.14.0a0 - libstdcxx >=14 - numpy >=1.25,<3 - proj >=9.8.1,<9.9.0a0 @@ -6618,8 +6606,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 8118849 - timestamp: 1785964755746 + size: 7505563 + timestamp: 1786313811246 - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda sha256: 7279908454f0c87b84ebc4aec6924f02f41a105d88420fb35dfaf5ecd976e0f5 md5: d83f2ee212d217a6d745a00e5be84671 @@ -6654,6 +6642,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=15 license: MIT + license_family: MIT run_exports: weak: - reproc >=14.2.8.post0,<14.3.0a0 @@ -6669,14 +6658,15 @@ packages: - libgcc >=15 - reproc >=14.2.8.post0,<14.3.0a0 license: MIT + license_family: MIT run_exports: weak: - reproc-cpp >=14.2.8.post0,<14.3.0a0 size: 29221 timestamp: 1788160449779 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_0.conda - sha256: b189c45a480760097c0e8ad376be0c2467f436539097f7a12f8bb132930ad4ed - md5: 4c5bc78879aabba9ec582ef330109996 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py312hc767a74_2.conda + sha256: de56267c9c6e31e02eddd3239cce36154d500b325a6cbd83d57fb32bcc2ebe11 + md5: c5e7bd18dff14e8fdc74d6e623c5fe52 depends: - python - __glibc >=2.17,<3.0.a0 @@ -6687,23 +6677,23 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 300808 - timestamp: 1787344306791 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_0.conda - sha256: 3425007379dc5b709b02699fa4329f1b82880a42f8dec40003cad7e7b6e2adac - md5: aefc39100f5d6c043d02a38c3b90ff47 + size: 300882 + timestamp: 1788577343073 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_2.conda + sha256: 480c7e299308442363715ff54381ad9d0c9df9b6a7da4db8eb358ae7f8ba0e81 + md5: 6b01f779e23cf522e46a7803b3445a88 depends: - python - - __glibc >=2.17,<3.0.a0 - libgcc >=15 + - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 constrains: - __glibc >=2.17 license: MIT license_family: MIT run_exports: {} - size: 300155 - timestamp: 1787344359780 + size: 300306 + timestamp: 1788577380856 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda sha256: 59f7773ed8c6f2bcab3e953e2b4816c86cf71e5508dc571f8073b8c2294b5787 md5: 8ea76c64b49b16c37769ea7c4dca993b @@ -6718,9 +6708,9 @@ packages: run_exports: {} size: 308487 timestamp: 1766175778417 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_2.conda - sha256: be8703d9d347586b1609d93bb5ec05a0e54ccedafeee5f5d03cb31fe349e80ad - md5: 12909f4b50ec672c63dc4767566189fc +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_4.conda + sha256: b22651b116c9143ac275f7ead7c61952814efc44ba25820ba8d7f5a765c2bcd0 + md5: c88364b57ab0e4564af7bd70fd99cb82 depends: - python - libgcc >=15 @@ -6729,12 +6719,12 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 152916 - timestamp: 1787264326043 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.5-hdfdb5ae_0.conda + size: 160098 + timestamp: 1788484981452 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.6-hbcb56a7_0.conda noarch: python - sha256: 6e056514a68826e940616a26fb0351d3d15d89ba47f6c2cbeb1cadd0fe60277a - md5: 7ddc6f2699c79c53286b478a6b4c9e92 + sha256: fc9e190a1cd67a8ff56b18dd7bd54e861cc68d60f85d9479a12fe95095fc5735 + md5: f5fd5d7420ce4f559441fae583f4c07d depends: - python - __glibc >=2.17,<3.0.a0 @@ -6742,9 +6732,10 @@ packages: constrains: - __glibc >=2.17 license: MIT + license_family: MIT run_exports: {} - size: 8955205 - timestamp: 1787865273472 + size: 8983605 + timestamp: 1788474545889 - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda sha256: 7369ac21f3561e2203f5b1600ef0671270057380783ca4b9e15f679ba25db575 md5: fa1c00d999e83ec20173c9775f71a1f1 @@ -6817,9 +6808,9 @@ packages: run_exports: {} size: 631649 timestamp: 1762523699384 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.9-h4dbf13b_0.conda - sha256: 58d3bd2face0c19f8680c9da0cd7df4bcbc5e32439f00f133986238783d0e26a - md5: 53a8ff86f8bd6f5d2ea089df06836fdc +- conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.11-h4dbf13b_0.conda + sha256: e313740ff17c7912994225ad62c795ef0fdc19d7e6a08a4e6bff3e597205f79c + md5: a65fbdd7742df8c2e87304a988889e4f depends: - __glibc >=2.17,<3.0.a0 - libstdcxx >=15 @@ -6828,12 +6819,12 @@ packages: license_family: APACHE run_exports: weak: - - simdjson >=4.6.9,<4.7.0a0 - size: 357796 - timestamp: 1787803683470 -- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda - sha256: da0443224cb579bd3d68fba20ff1422a2774b6e3be4e068129722bc4ddadd1a9 - md5: 75095e959e733b32c2ae343b1bcd0073 + - simdjson >=4.6.11,<4.7.0a0 + size: 358626 + timestamp: 1788630509372 +- conda: https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_1.conda + sha256: 3b178cc359fd09c63076e11e79c864c7fdf6cb209b2dccbbf948539063967f3e + md5: d9d46cc36f3998befe87df9af48c919b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 @@ -6842,8 +6833,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 167762 - timestamp: 1787824152736 + size: 168352 + timestamp: 1788525221151 - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 md5: 98b6c9dc80eb87b2519b97bcf7e578dd @@ -6922,45 +6913,45 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3566806 timestamp: 1787272857910 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda - sha256: 2ea8f8b10e869b675d2fba157b387eaf4eed1696bfc924bb7b28e67d7aa0a947 - md5: 5423c2b8f82d60320e65b9ff30babbdf +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda + sha256: 3480bf39c540d3dfa737ce5df1e14c74fef161ccb2a19485cb211a654921fa68 + md5: e15caa7ad9be633c3cba5a7937b117b7 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache run_exports: {} - size: 869730 - timestamp: 1786226754702 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h5bd0f2a_0.conda - sha256: ebec0d90f99fa7bbe91eabab41ee77d3f36dbd58ec2f08aa4220fdd713610b6d - md5: faea84d2f2ccadf70cf9e55381d75b3e + size: 868900 + timestamp: 1788524841516 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py314h89acca1_1.conda + sha256: 10bdcdc09a9b2d57447a1a721feb1588264a33597c633807ac4c26d1ec2c6ee3 + md5: db702d9e75d0257215d4ab12a57c597a depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache run_exports: {} - size: 923237 - timestamp: 1786226770518 -- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda - sha256: 895bbfe9ee25c98c922799de901387d842d7c01cae45c346879865c6a907f229 - md5: 0b6c506ec1f272b685240e70a29261b8 + size: 925213 + timestamp: 1788524844066 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h5cc1888_1.conda + sha256: c8cb48db10add790655cc28c163adec56312fd176934f206b0da62bbb64902c5 + md5: a6301c668e50512c804c5b3807c960f2 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache run_exports: {} - size: 410641 - timestamp: 1770909099497 + size: 414021 + timestamp: 1788554113694 - conda: https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda sha256: 2aad2aeff7c69a2d7eecd7b662eef756b27d6a6b96f3e2c2a7071340ce14543e md5: d71d3a66528853c0a1ac2c02d79a0284 @@ -7351,17 +7342,17 @@ packages: - xorg-libxxf86vm >=1.1.7,<2.0a0 size: 18701 timestamp: 1769434732453 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda - sha256: 051c6088bf2381840fcf8764737b829cc6c5f793718d2417d097d6e3b153eba9 - md5: 3b51576511038b50fdbd05245e22e4b1 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda + sha256: 28be920cbb36bb674f66a23f368b67733dbbb89b2321e5ed7266d9b415156795 + md5: 7386d38083163c5b5c4a92a1582daaef depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT run_exports: {} - size: 594844 - timestamp: 1786114408394 + size: 594980 + timestamp: 1788447117643 - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda sha256: d164dfa75ecd538f6fd68765defcc06aa875bc697b9b215362d79a2a73125dd0 md5: e741576fb8f89821ac7c1c537322a33d @@ -7432,21 +7423,6 @@ packages: - zlib-ng >=2.3.3,<2.4.0a0 size: 123959 timestamp: 1786736890973 -- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314hfe1a184_3.conda - sha256: 8c73ee662ea1446a322b1f0e8ddbb3cd631b9e6abf9b2dbe70662fe73b0c196b - md5: 7fe9ffe3e04a39dc12669cc0e4f23f05 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - __glibc >=2.17,<3.0.a0 - - libgcc >=15 - - python_abi 3.14.* *_cp314 - - zstd >=1.5.7,<1.6.0a0 - license: BSD-3-Clause - run_exports: {} - size: 472944 - timestamp: 1787896524818 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 md5: aa459086047c0e5e27023ab19f8cb86a @@ -7516,14 +7492,13 @@ packages: run_exports: {} size: 19461 timestamp: 1784935220549 -- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.14.2-pyhcf101f3_0.conda - sha256: e36998c5e860e26b22e5dbcd5726dd0c4eabad949c84d383cad8512757bbf6a1 - md5: fb568fbae6908ba86a090a85a089d11f +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.15.1-pyh5ded981_1.conda + sha256: fc25a84c9c1ad4b48c136ad2363100e8b0faa3b0ab8985e61c9c8fa0b3431945 + md5: 33cdc5ba7c481b371f08f88e6c048c55 depends: - - exceptiongroup >=1.0.2 - idna >=2.8 - - python >=3.10 - - typing_extensions >=4.5 + - python >=3.11 + - typing_extensions >=4.16.0 - python constrains: - trio >=0.32.0 @@ -7532,8 +7507,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 164465 - timestamp: 1783889660383 + size: 175600 + timestamp: 1788612816954 - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-1.0.0-pyhcf101f3_0.conda sha256: 52bc705ba773214cb2f8f884ee0128d007fa7dfdcf19218c910465381b91cd6e md5: 56d53a5e9740367f5f8cf83f995263c2 @@ -7587,16 +7562,16 @@ packages: run_exports: {} size: 64927 timestamp: 1773935801332 -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda noarch: generic - sha256: 19514d89d1e725e44b0650d31a4d43da8e070e4e93e4131e3b16bd139404f2b2 - md5: 92adf685875ab717f68ad4172ef6de27 + sha256: ad0f78582ee64ec1c66a3daac32986e81b400d3ac719e5a3cbdb22e55065760e + md5: 67924260a851b5273e39f9b28b6b5e24 depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 run_exports: {} - size: 7541 - timestamp: 1786861415851 + size: 7539 + timestamp: 1788491291925 - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda sha256: aed4b9dcf68ec2a75e5645fed14d77fd884d38d2e52bfa6ef4b278d90cd88781 md5: 3b261da3fe9b4168738712832410b022 @@ -7646,16 +7621,15 @@ packages: run_exports: {} size: 4454345 timestamp: 1787144786949 -- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.1.0-pyhd8ed1ab_0.conda - sha256: ca44f906756aaa908645c9bcef31a132fc82e7934777aa6bb45c12edada6a58c - md5: 92c88d22e52af7b672605b12a31bf0bb +- conda: https://conda.anaconda.org/conda-forge/noarch/boltons-26.2.0-pyhd8ed1ab_0.conda + sha256: 8fd76dc72d31a8688159039cfbb17eabe21c65792a96b5245be04e5887c2291c + md5: 52f3c30dbe025efb8050021f35648949 depends: - - python >=3.10 + - python >=3.11 license: BSD-3-Clause - license_family: BSD run_exports: {} - size: 309453 - timestamp: 1784706150046 + size: 321343 + timestamp: 1788865918329 - conda: https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda sha256: 1acf87c77d920edd098ddc91fa785efc10de871465dee0f463815b176e019e8b md5: 1fcdf88e7a8c296d3df8409bf0690db4 @@ -7725,31 +7699,16 @@ packages: run_exports: {} size: 64487 timestamp: 1786835648298 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda - sha256: 5b5c96afdd801dd9c3b78ebc2cd9a9f3ce34186257415d394dde1aa8468aa3c0 - md5: 8a0d65027e25e367f9f1754f0604e8de +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda + sha256: 9afb0c2c089330321a219c7ee5a25313bd574d4024be606cbb33e7a5735df662 + md5: dea5b13a211bbf99876deb980b408a66 depends: - - __win - - colorama - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 106227 - timestamp: 1783085395110 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda - sha256: ccc4787f511964f9a1f2d2d2859c91c5d571fb60f7f09d4c4e092c9b7a94e671 - md5: 2c4bd6aeb90bb157456841c3270a0d92 - depends: - - __unix + - python >=3.11 - python - - python >=3.10 license: BSD-3-Clause - license_family: BSD run_exports: {} - size: 107155 - timestamp: 1783085363526 + size: 112341 + timestamp: 1788802215348 - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda sha256: ba1ee6e2b2be3da41d70d0d51d1159010de900aa3f33fceaea8c52e9bd30a26e md5: e9b05deb91c013e5224672a4ba9cf8d1 @@ -7832,23 +7791,23 @@ packages: run_exports: {} size: 14690 timestamp: 1753453984907 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.12.1-pyhd8ed1ab_0.conda - sha256: 39f8f4b24e6c6b701679949950af3220bcf42bd634a68d63f7d39f64752b79a6 - md5: 1b8035639db6b74dcdc6b5412ae89011 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-index-0.13.0-pyh5ded981_0.conda + sha256: 3629dea4698cf120bb62ac9b6908c9182902a62d743f7d81bb8ecfb42d45fcbe + md5: 2f36e8e974c0c8a889870669c96a93c5 depends: + - python >=3.11 + - backports.zstd >=1.3.0 - conda >=25 - conda-package-streaming >=0.12.0 - - filelock - jinja2 - - msgpack-python >=1.0.2 - - python >=3.10 + - msgpack-python >=1.1.1 - ruamel.yaml - - zstandard + - python license: BSD-3-Clause license_family: BSD run_exports: {} - size: 210538 - timestamp: 1782239750810 + size: 214539 + timestamp: 1788522660257 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-inject-1.3.2-pyhd8ed1ab_0.conda sha256: c1b355af599e548c4b69129f4d723ddcdb9f6defb939985731499cee2e26a578 md5: e52c2a160d6bd0649c9fafdf0c813357 @@ -7888,20 +7847,20 @@ packages: run_exports: {} size: 20633 timestamp: 1783438353470 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.5.0-pyhcf101f3_0.conda - sha256: 63d5b510644a01b4c93738304cb0dc8a336542fc335b0b77f3b355ba7d119260 - md5: 3f1854f1ec2090538fb49b7078dbb9de +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda + sha256: 34b3c276e8426d851c62b905014ef4c05cfcc090f052d7ec4da1dda53e10bc57 + md5: 412f13a9700d4455dfca5c5e23be9404 depends: - - python >=3.10 - - conda-package-streaming >=0.12.0 + - python >=3.11 + - conda-package-streaming >=0.13.0 + - backports.zstd >=1.1.0 - requests - - zstandard >=0.15 - python license: BSD-3-Clause license_family: BSD run_exports: {} - size: 256182 - timestamp: 1781015592350 + size: 257896 + timestamp: 1788438543851 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda sha256: af3b666ea6043a146901cd151d6201126bbe19af8307b58fb175015e3710064c md5: b4e3dcace82b486f69bc693f30120205 @@ -7913,6 +7872,27 @@ packages: run_exports: {} size: 23954 timestamp: 1781293054998 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda + sha256: 57721a79a37837fc576bed40c5f927a6b9e827a9f446f0ac0132fb7503c8ac87 + md5: d00669954b34487bd3f60297d2c7e5b3 + depends: + - python >=3.11 + - pip >=23.0.1 + - packaging + - unearth + - python-build + - python-installer >=1.0 + - platformdirs + - conda-index >=0.11.0 + - conda-package-streaming >=0.11 + - python + constrains: + - conda >=26.1.0 + license: MIT + license_family: MIT + run_exports: {} + size: 255320 + timestamp: 1788608958589 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda sha256: 77ea286de14f5d2dd0c376627ec41d0c226ad94ad8b25262f36712070324aa05 md5: f5d15171925a157707e1ad165179449c @@ -7937,18 +7917,17 @@ packages: run_exports: {} size: 22510 timestamp: 1784724984642 -- conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.5-pyhcf101f3_0.conda - sha256: 7f8ea42a8411b433ec7244dfd30637d90564256c13a114dbe42455fe032ae89c - md5: 12389a21e7f69704b0ae77f44355e30b +- conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda + sha256: ed025680bbb12e5079cbc390481e613310dbf81e56db05ca07061e90af485de9 + md5: 7a74be255ce9f2f8a79d99fd67f144f3 depends: - - python >=3.10 + - python >=3.11 - toml - python license: MIT - license_family: MIT run_exports: {} - size: 45817 - timestamp: 1773233306055 + size: 63749 + timestamp: 1788789827928 - conda: https://conda.anaconda.org/conda-forge/noarch/connection_pool-0.0.3-pyhd3deb0d_0.tar.bz2 sha256: 799a515e9e73e447f46f60fb3f9162f437ae1a2a00defddde84282e9e225cb36 md5: e270fff08907db8691c02a0eda8d38ae @@ -7959,17 +7938,17 @@ packages: run_exports: {} size: 8331 timestamp: 1608581999360 -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_104.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.7-py314hd8ed1ab_106.conda noarch: generic - sha256: 9d8f07af6f8a205932d010cf1f3993e16a81b6e9321e6b2670f4e8be36109ed5 - md5: b710ef6c508928496586b4496e9ab52e + sha256: fd701880cf6f696420aa5b4b530dbdf0c83a16bdb50aeb760b3159eec5c416bf + md5: 3b52b830ac00367685c08f98f50963bf depends: - python >=3.14,<3.15.0a0 - python_abi * *_cp314 license: Python-2.0 run_exports: {} - size: 51095 - timestamp: 1788160819721 + size: 51289 + timestamp: 1788383537525 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 md5: 4c2a8fef270f6c69591889b93f9f55c1 @@ -7999,6 +7978,7 @@ packages: constrains: - openssl !=1.1.1e license: BSD-3-Clause + license_family: BSD run_exports: {} size: 10415 timestamp: 1787917206356 @@ -8055,6 +8035,7 @@ packages: constrains: - openssl !=1.1.1e license: BSD-3-Clause + license_family: BSD run_exports: {} size: 840592 timestamp: 1787863556779 @@ -8087,20 +8068,20 @@ packages: run_exports: {} size: 21853 timestamp: 1762165431693 -- conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda - sha256: f010c6e406605126a6a73c21df93b522e0b035882fd40fdd162c3e504064a997 - md5: 0c06dd1da8af688335732bb47dc51dc1 +- conda: https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda + sha256: 162c65a765a02dad9d915f2211f7eca7f974bc5cef2a50169fc3eaa3666b7b7e + md5: 5332c97b820ebc5a00e78b3e6ec7ce12 depends: - beautifulsoup4 >=4.11.1 - pandas >=2.2.0 - - python >=3.10 + - python >=3.11 - pytz - requests license: MIT license_family: MIT run_exports: {} - size: 952643 - timestamp: 1776204877247 + size: 948079 + timestamp: 1788568522980 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 md5: 8e662bd460bda79b1ea39194e3c4c9ab @@ -8121,15 +8102,6 @@ packages: run_exports: {} size: 30753 timestamp: 1756729456476 -- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.4-pyhd8ed1ab_0.conda - sha256: c2c2527101fea8d2fbae3883d3328a4cd225e8fc3f133b49504acb7a8cecf6fe - md5: 0171dc5d54fdbb3f6e55f285f805e0fe - depends: - - python >=3.10 - license: Unlicense - run_exports: {} - size: 78622 - timestamp: 1787521663311 - conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda sha256: 782fa186d7677fd3bc1ff7adb4cc3585f7d2c7177c30bcbce21f8c177135c520 md5: a6997a7dcd6673c0692c61dfeaea14ab @@ -8278,18 +8250,18 @@ packages: run_exports: {} size: 53136 timestamp: 1735887290843 -- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.61-pyhcf101f3_0.conda - sha256: 51cb1c112e47ccda9a0c7850fa5ff7c4eaa8bd65a3f7a81861623b8acff00d9e - md5: a4d052a35ae133a5fef5bc34fd337cd8 +- conda: https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.62-pyh5ded981_0.conda + sha256: c70321228e3b7e200791629bf2d598fa6a334407b77001511697b749e8ae171a + md5: bc66034cae0ff9168c130766eb2b33d1 depends: - - python >=3.10 + - python >=3.11 - gitdb >=4.0.1,<5 - typing_extensions >=3.10.0.2 - python license: BSD-3-Clause run_exports: {} - size: 178174 - timestamp: 1787936103205 + size: 178869 + timestamp: 1788802794188 - conda: https://conda.anaconda.org/conda-forge/noarch/gregor-0.1.0-pyhcf101f3_0.conda sha256: 4d3f8a3a42a88493cdae21f54efc57154c23403901d5d54d05b9fa6991b9bfc7 md5: 36a2d7837350f7e8da83bcaad552d0b5 @@ -8428,6 +8400,7 @@ packages: - zipp >=3.20 - python license: Apache-2.0 + license_family: APACHE run_exports: {} size: 34920 timestamp: 1787943971257 @@ -8533,9 +8506,9 @@ packages: run_exports: {} size: 138635 timestamp: 1781101665847 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyh53cf698_0.conda - sha256: 8165024016181bf7d32a027180239cf15c4447ec02080b31dcae731809c0b3e2 - md5: 6ebd34681d8bc56e474fac76999de45a +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyh53cf698_0.conda + sha256: 0fb38e0685e5065064ac007cf8317ee24b329373c9f80c31eb74c1bfcb0cfe24 + md5: 0e28933e51c1d05c179bfd595ead8aee depends: - __unix - ipython_pygments_lexers >=1.0.0 @@ -8551,12 +8524,13 @@ packages: - pexpect >4.6 - python license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 730292 - timestamp: 1787921913409 -- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.0-pyhe2676ad_0.conda - sha256: de5052db34cd414d5993dc7330c7f9ce422fadd6e5f3c1422d48040e0319c633 - md5: c2cf78253e434f1283cfd276ec9abd2f + size: 730678 + timestamp: 1788260388285 +- conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyhe2676ad_0.conda + sha256: 0269b9ccc59fad27d6787ef3e4d87cf214e6ebe2b2015fd50038df24d24701d0 + md5: 5c9214b277a8d24de70ef51bbf967c63 depends: - __win - ipython_pygments_lexers >=1.0.0 @@ -8572,9 +8546,10 @@ packages: - colorama >=0.4.4 - python license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 729408 - timestamp: 1787921907454 + size: 729793 + timestamp: 1788260382682 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda sha256: 894682a42a7d659ae12878dbcb274516a7031bbea9104e92f8e88c1f2765a104 md5: bd80ba060603cc228d9d81c257093119 @@ -8609,17 +8584,18 @@ packages: run_exports: {} size: 120685 timestamp: 1764517220861 -- conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - sha256: 301539229d7be6420c084490b8145583291123f0ce6b92f56be5948a2c83a379 - md5: 615de2a4d97af50c350e5cf160149e77 +- conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda + sha256: 96b76ace583404ce4fbc234baa6cdb3de485887913104d4ac51994f9b6656696 + md5: 4ebc70ef1af2e21cdc8edb1bfa1ec28d depends: - python >=3.10 - - setuptools + - cloudpickle >=3.0 + - python license: BSD-3-Clause license_family: BSD run_exports: {} - size: 226448 - timestamp: 1765794135253 + size: 228858 + timestamp: 1788177170105 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_1.conda sha256: 328756a4555941d57af4a5f553e5d8598e9f3b37db028f557e30f9f0b3086db6 md5: 204b5ca91eba7738790ecc333facbbbe @@ -8660,6 +8636,7 @@ packages: - typing_extensions >=4.13.0 - python license: BSD-3-Clause + license_family: BSD run_exports: {} size: 118962 timestamp: 1787929615607 @@ -8919,16 +8896,17 @@ packages: run_exports: {} size: 1198163 timestamp: 1785914482439 -- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.5-pyhcf101f3_0.conda - sha256: 4496a7e0b584a388b3580ab594a4f384696ab7d702282588fd33a7ee38af983c - md5: 152ec36401f710145a7db03475594410 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda + sha256: 8fe66c78f015fecfe096e4ff78a2626c4d926cde9b6951cf3b8e1ff0aaeb0a6c + md5: fb0f75910f804de1d8c6e91f73673d11 depends: - - python >=3.10 + - python >=3.11 - python license: MIT + license_family: MIT run_exports: {} - size: 27461 - timestamp: 1787881635603 + size: 27515 + timestamp: 1788271419073 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -8993,17 +8971,6 @@ packages: run_exports: {} size: 3105570 timestamp: 1718094617616 -- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - sha256: e27e0473fc6723311a0bd48b89b616fa1b996a2f7a2b555338cbbcfb9c640568 - md5: 9c5491066224083c41b6d5635ed7107b - depends: - - python >=3.10 - - python - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 55886 - timestamp: 1779293633166 - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda sha256: 701ed8122021f62e1c9a46bdf4932ada911c10afa56ba0459f56da940bcbe5a7 md5: 2d0f9304fc1cb1e3e94e7b37c14cd70a @@ -9015,6 +8982,7 @@ packages: - pydantic-core ==2.46.5 - python license: MIT + license_family: MIT run_exports: {} size: 346633 timestamp: 1787941273167 @@ -9121,6 +9089,7 @@ packages: constrains: - build <0 license: MIT + license_family: MIT run_exports: {} size: 33461 timestamp: 1787910624806 @@ -9147,16 +9116,16 @@ packages: run_exports: {} size: 254446 timestamp: 1786892280524 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_104.conda - sha256: 11fb369ceec6ac6cc3b3552854c399b97fa547e44d19ef699e48309310949e96 - md5: 397bdd86501d63b918ec7acfec8d5872 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.7-h4df99d1_106.conda + sha256: 801ef80bc9f9394d9b3b5cae5a90c33cbbc33004b3e93195b93af818aad8888b + md5: b672ecb44198ade19ef249bf99f4b1ff depends: - cpython 3.14.7.* - python_abi * *_cp314 license: Python-2.0 run_exports: {} - size: 51092 - timestamp: 1788160828119 + size: 51276 + timestamp: 1788383547342 - conda: https://conda.anaconda.org/conda-forge/noarch/python-installer-1.0.1-pyh332efcf_0.conda sha256: 9c18fa1ebd0c839b6ea12e99d510a0968c2288cc423185cb34bf7332b97684e9 md5: 5cc7e2e78962dc902cae9e29c2aa4b2f @@ -9177,28 +9146,28 @@ packages: run_exports: {} size: 146862 timestamp: 1783704822814 -- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - build_number: 8 - sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 - md5: c3efd25ac4d74b1584d2f7a57195ddf1 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + build_number: 9 + sha256: ee9c2922e07afc85fc82d5fa82c9ac2c79da3be3283a5e17bf2f2ae38dbd02fb + md5: 4c32076993e6270825441d059ab5c18b constrains: - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD run_exports: {} - size: 6958 - timestamp: 1752805918820 -- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - build_number: 8 - sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 - md5: 0539938c55b6b1a59b560e843ad864a4 + size: 6751 + timestamp: 1788302823694 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda + build_number: 9 + sha256: e7f8e965bbfb98e08feb2365cacdad8bb218fa5b5a0a2752f747287f425f213c + md5: 350d89b50d7de805abf2e63e29dee451 constrains: - python 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 6989 - timestamp: 1752805904792 + size: 6791 + timestamp: 1788302824440 - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda sha256: ad774abda71c759baef515983bfedbba34d56d6227974c23715c04612f812a90 md5: eb2fb9070c0232e96ae5515d8b28e4f9 @@ -9411,18 +9380,6 @@ packages: run_exports: {} size: 19397 timestamp: 1762956379123 -- conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda - sha256: 6a6329139f72dac12a8d30234f0191af03dc3bbeab1010b89b314f651180e9df - md5: 9715ba984ea99ba23d0d0399311937c1 - depends: - - python >=3.12 - - pandas >=3.0.3 - - pandera >=0.31.1 - - python - license: MIT - run_exports: {} - size: 46423 - timestamp: 1788163064651 - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda sha256: 32e75900d6a094ffe4290a8c9f1fa15744d9da8ff617aba4acaa0f057a065c34 md5: 043f0599dc8aa023369deacdb5ac24eb @@ -9641,16 +9598,17 @@ packages: run_exports: {} size: 103560 timestamp: 1778188657149 -- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.2-pyhd8ed1ab_0.conda - sha256: 4acf845da404e84cef1acccc66cc0156af1b83a5b5d7077b2ca19b705c561e57 - md5: 99f7755ec8648a042b0dbe906234f888 +- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.3-pyhcf101f3_0.conda + sha256: 3b0599d59d70bbe792702d1b5404ac501c9c25bd7b2fd9ccbb5da8ad6587a703 + md5: f62991f907a9830ab003b12d07dd3478 depends: - python >=3.10 + - python license: MIT license_family: MIT run_exports: {} - size: 132415 - timestamp: 1782771807703 + size: 146490 + timestamp: 1788174996551 - conda: https://conda.anaconda.org/conda-forge/noarch/win_inet_pton-1.1.0-pyh7428d3b_8.conda sha256: 93807369ab91f230cf9e6e2a237eaa812492fe00face5b38068735858fba954f md5: 46e441ba871f524e2b067929da3051c2 @@ -9698,16 +9656,16 @@ packages: run_exports: {} size: 1027069 timestamp: 1783633473025 -- conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - sha256: 663ea9b00d68c2da309114923924686ab6d3f59ef1b196c5029ba16799e7bb07 - md5: 4487b9c371d0161d54b5c7bbd890c0fc +- conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda + sha256: b9439f1716ff580ce56e9b4a4df7ea92e5ed2bdc9567d25cdff223d62c3183a8 + md5: 5d7c149ba9c728d4951f3fe6d3ce0f84 depends: - python >=3.9 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 51732 - timestamp: 1774900074457 + size: 53963 + timestamp: 1788530754725 - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda sha256: 1ad021f32290e72b70a84dfe0c9b278c61aaa1254f1e1c287d68c32ee4f1093f md5: 89d5edf5d52d3bc1ed4d7d3feef508ba @@ -9749,27 +9707,27 @@ packages: depends: - llvm-openmp >=9.0.1 license: BSD-3-Clause + license_family: BSD run_exports: weak: - _openmp_mutex >=4.5 size: 8016 timestamp: 1788046437162 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.8.0-py310h3b8a9b8_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.10.0-py311hfcb3ee1_0.conda noarch: python - sha256: 51c8a631b36bf2eb2f27d49c0a0df9e269a76b91a447da7e75f9d0854cabb4a7 - md5: 5ce8b03801df5eff0088bdc85a19d09a + sha256: 30c5f9e7d5b3abe2d7ac1ddb8d0229ca15147e31a3448b1f42557fe7d8e85b7e + md5: 780e8c097dc5988e364e43521337b6b8 depends: - - python >=3.10 + - python >=3.11 - __osx >=11.0 - _python_abi3_support 1.* - - cpython >=3.10 + - cpython >=3.11 constrains: - __osx >=11.0 license: MIT - license_family: MIT run_exports: {} - size: 1085485 - timestamp: 1786328966627 + size: 1108325 + timestamp: 1788869608301 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda sha256: b0747f9b1bc03d1932b4d8c586f39a35ac97e7e72fe6e63f2b2a2472d466f3c1 md5: 57301986d02d30d6805fdce6c99074ee @@ -10083,14 +10041,14 @@ packages: - blosc >=1.21.6,<2.0a0 size: 33602 timestamp: 1733513285902 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda - sha256: 7050e1b11876219bd0df375ef3d83fd00f94fb4e13c1ffb21dede0c506893079 - md5: aa7df8174ee3b34a5ad8a3160429db68 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_4.conda + sha256: 14d8592fefabdc35055b857925fd47b375cb202a15a2914e94dfbbed4fd82c64 + md5: e32ad50b0f977e3fa616a578a2643e46 depends: - __osx >=11.0 - - brotli-bin 1.2.0 he4a93e4_3 - - libbrotlidec 1.2.0 h5295a6a_3 - - libbrotlienc 1.2.0 h2ddc9cb_3 + - brotli-bin 1.2.0 he4a93e4_4 + - libbrotlidec 1.2.0 h5295a6a_4 + - libbrotlienc 1.2.0 h2ddc9cb_4 license: MIT license_family: MIT run_exports: @@ -10098,35 +10056,35 @@ packages: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20767 - timestamp: 1786622887445 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda - sha256: 1e92cea587c2eaab169e45dae01c149b9889a8249eda296b8b6db39aee708531 - md5: d9de6c0ad7e008afdf62fb2ffd450b4a + size: 20609 + timestamp: 1788480374173 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_4.conda + sha256: f95bc05d7733d1c78a2077e83c5a05235f8b743b6e2e977c688cdba4d56c4537 + md5: d065d0732d02ace747059c3d58338fcd depends: - __osx >=11.0 - - libbrotlidec 1.2.0 h5295a6a_3 - - libbrotlienc 1.2.0 h2ddc9cb_3 + - libbrotlidec 1.2.0 h5295a6a_4 + - libbrotlienc 1.2.0 h2ddc9cb_4 license: MIT license_family: MIT run_exports: {} - size: 18962 - timestamp: 1786622878369 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda - sha256: 3cdfc0a96de4717fc309e39133e2a192f4e1df96680577e1d48804021d1726eb - md5: d3a28add84f2412a562355f89ef5e0f5 + size: 18849 + timestamp: 1788480367234 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda + sha256: bafd111884ad0f5bb75286fd457999536cbeb5e0308ecbb4c5fb49b79d31e304 + md5: cfaec5b3d3d1f4877c1ebd84f8e1dfa6 depends: - __osx >=11.0 - libcxx >=21 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 constrains: - - libbrotlicommon 1.2.0 h1dcdb26_3 + - libbrotlicommon 1.2.0 h1dcdb26_4 license: MIT license_family: MIT run_exports: {} - size: 365272 - timestamp: 1786623009364 + size: 365544 + timestamp: 1788480454684 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 md5: b50612e7d190b8061ab4e7dc119cf4d5 @@ -10151,9 +10109,9 @@ packages: - c-ares >=1.34.8,<2.0a0 size: 197819 timestamp: 1787169996553 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda - sha256: 3d368a359c3b526082eb8b8c4a07371f0c1e40e6dadfeef326dbec7956c149d0 - md5: ae0e0e8ea304c6ff1608d7918703ba29 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h580cee5_3.conda + sha256: 07f86bbc50ff910689afdd8a417df6e329646b9424343f3202e32efda9e08acf + md5: bd8c9c43ff83b23c1aaaaf51f2f889cf depends: - __osx >=11.0 - fontconfig >=2.18.3,<3.0a0 @@ -10171,22 +10129,8 @@ packages: run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 841724 - timestamp: 1787926737310 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py314h618e29d_2.conda - sha256: 77b0bb0f3fd2d28b6bbd216d370d98320835b8e1df4c86a478a1d0391d1913fe - md5: 1572fc59fc2b1461f47b0d6907acef30 - depends: - - __osx >=11.0 - - libffi >=3.7.0,<3.8.0a0 - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - run_exports: {} - size: 292307 - timestamp: 1786775143512 + size: 840110 + timestamp: 1788221324814 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-cbc-2.10.13-h2032c40_1.conda sha256: 238328ef13c15fa87ff718ef6bfc32ee83c255f47af7d4ba07190ff865c652fa md5: f76979642f978340ec48d809c548732f @@ -10310,9 +10254,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 553075 timestamp: 1778500827484 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.1-py314hcec6336_0.conda - sha256: 2e7212eacb92dd19de4074c6427b34e2fdc6c9aa02d27ccc29d65160a8ab9c28 - md5: ce8660590c4e5537b767f0ac9b17ab2d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_0.conda + sha256: d9f2f2bd0d57786d030e4c8a6fd46b8e0512c6eebd4830cf1b253ad4ef394e9a + md5: c10c761f88c8b2b4fc40fb962a700792 depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -10346,30 +10290,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1469487 - timestamp: 1787306459722 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-pypi-0.11.0-py314hcec6336_1.conda - sha256: 4da4678a03488c985aed27956dd1a35281b515240ff33a3fe9238f3756fa6059 - md5: a123df8bb2f1dc4f85ba6bbddb744968 - depends: - - python - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python 3.14.* *_cp314 - - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 - license: MIT - license_family: MIT - run_exports: {} - size: 328032 - timestamp: 1784203150047 + size: 1470827 + timestamp: 1788559426835 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda sha256: 754ab72f1c1ae99ef7c57995f59224dc9632cbd6731fe7e6277437fd01d43156 md5: cddc851000ce131d757678c2f329eaad @@ -10411,23 +10333,23 @@ packages: run_exports: {} size: 1639272 timestamp: 1763019908165 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda - sha256: b9e7e07f4487109f9bcafc581f778784a19b6fb8e2ca4b5c86be6088e642821d - md5: a78919380afa9842a9c46f35fc7a518c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.22.0-h6651222_0.conda + sha256: 40e26cf024eaf44707a6368af4e34104bda844ef9213bb82d0ee4b77d83a8c07 + md5: e3e65b70a52a7819dd63c31f9dce7209 depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 h6651222_5 + - libcurl 8.22.0 h6651222_0 - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT run_exports: {} - size: 180586 - timestamp: 1787183693425 + size: 182000 + timestamp: 1788340714708 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda sha256: 8deb1f8ef80525cccc68c73cdcd690cb8614035f6e1d0eb6880c0c9b377d29ac md5: aae872d4d6847677924de8e023e00457 @@ -10442,20 +10364,19 @@ packages: run_exports: {} size: 615096 timestamp: 1771856205834 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314he609de1_0.conda - sha256: fd6df772cdb30d01fa0d405ed637f420a9f2194fe931f967e8c67d95b504f8b4 - md5: da29307f59fb7a63f686ec20724fecf9 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314h671cc75_1.conda + sha256: 977160b1d8e3991e6436cccc9b6888e2d0a92adf229087b854bf823000d47b1c + md5: c42b57f96da80e3e96c243bb22a1589f depends: - python - - libcxx >=19 - - __osx >=11.0 + - libcxx >=21 - python 3.14.* *_cp314 + - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT run_exports: {} - size: 2776045 - timestamp: 1780390212997 + size: 2754969 + timestamp: 1788859502179 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda sha256: ba685b87529c95a4bf9de140a33d703d57dc46b036e9586ed26890de65c1c0d5 md5: 3b87dabebe54c6d66a07b97b53ac5874 @@ -10486,27 +10407,26 @@ packages: run_exports: {} size: 535738 timestamp: 1778865753466 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda - sha256: ba8da8eb94e2762116a6c8877814826d638f046210f23b9da1ef444f9a55046a - md5: d30d4527aacdf33b1e80ef858f33608c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314h7afc4b4_7.conda + sha256: 83c762896bf706144e204c55212c76e832f77932394098d286d6359410402d2f + md5: 3ce7775e61c4dcd13ca709186bbd9af7 depends: - - __osx >=11.0 + - python - attrs >=19.2.0 - click >=8.0,<9.dev0 - - click-plugins >=1.0 - cligj >=0.5 - - libcxx >=19 - - libgdal-core >=3.12.1,<3.13.0a0 + - click-plugins >=1.0 - pyparsing - - python >=3.14,<3.15.0a0 - - python >=3.14,<3.15.0a0 *_cp314 - - python_abi 3.14.* *_cp314 - shapely + - libcxx >=21 + - __osx >=11.0 + - libgdal-core >=3.13.3,<3.14.0a0 + - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1059210 - timestamp: 1767051558277 + size: 1281112 + timestamp: 1788472947566 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda sha256: ca42427b99ff92228e907bed5f10a1df868180997571d29c230faad2c38e35fd md5: 891b1202d7b835f215e26a4db685ec7c @@ -10560,25 +10480,26 @@ packages: - libiconv >=1.18,<2.0a0 - minizip >=4.2.2,<5.0a0 license: MPL-1.1 + license_family: MOZILLA run_exports: weak: - freexl >=2.0.0,<3.0a0 size: 54221 timestamp: 1788125835063 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - sha256: 6dd18694340b84290bb1906cc1359502b974aada6a8476cfe6dec3ce0e860af8 - md5: 2bb7d7dd91116b8c85e805b0e08cc67b +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda + sha256: aff7bd01037dfbacb0661ef0435a224fe9fcda1d624895fb336107dd68e5bb34 + md5: b4397592e234554333719778bb01169e depends: - __osx >=11.0 license: LGPL-2.1-or-later run_exports: weak: - fribidi >=1.0.16,<2.0a0 - size: 60230 - timestamp: 1785912572097 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_0.conda - sha256: 0e91dc3531a3a7c4938abc115d7a49be61284a9a8650b404a37b2123fe5cf7dd - md5: aa0e0c67d3e89789959e0d77c8361b67 + size: 62379 + timestamp: 1788385614707 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_2.conda + sha256: 96dcac22eaea2f24b9c92e8be078c3e39058829aa4db2a8103c1a4e85f8925ed + md5: 3a84786e0c929f4a05d08c07be5ec9b3 depends: - __osx >=11.0 - libglib >=2.88.3,<3.0a0 @@ -10592,8 +10513,8 @@ packages: run_exports: weak: - gdk-pixbuf >=2.44.8,<3.0a0 - size: 551879 - timestamp: 1786715345711 + size: 552802 + timestamp: 1788490405006 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda sha256: 6bb801cb23b7e884c2e6ce96d933d141d6d87ad07f5bf8d13d6a59a3403952fa md5: e7480ad364d7a5bf03812ba272c39707 @@ -10631,18 +10552,18 @@ packages: - giflib >=6.1.3,<6.2.0a0 size: 82339 timestamp: 1785907599925 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h026c20a_2.conda - sha256: 20b9cb0c314a02e0940d2695ed5ed206e271b202f3935bc93b8c5a8529853cc4 - md5: 085d8718f37acd1e2c35ef6ec9b330f7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h0d72ea3_3.conda + sha256: 0406a0efb4962231e3ce9f67ab74cb9b2d158d5a996f3b00acd5ceaf0ad78fbb + md5: 12dc17aaf00d5e0cb3e9552c20b023a6 depends: - - libglib ==2.88.3 h89cd0c0_2 + - libglib ==2.88.3 h81decf1_3 - libffi - __osx >=11.0 - libintl >=0.25.1,<1.0a0 license: LGPL-2.1-or-later run_exports: {} - size: 202400 - timestamp: 1787884141804 + size: 202685 + timestamp: 1788525252185 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda sha256: 1ac5991fd354de81baf3892fee0ad09438623698fcad73758521dfe90711ed44 md5: 68937f90f7930d49e106b94e95d4536c @@ -10657,13 +10578,14 @@ packages: - glog >=0.7.1,<0.8.0a0 size: 112670 timestamp: 1784094909098 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.0-hf76c51c_0.conda - sha256: ce190c611e2114c6da7251e1f82f84f64786d09186734d4d44deeaec2b267881 - md5: 67acc9c808bdd91b1c5c91fcfa59dcd2 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/go-shfmt-3.14.1-hf76c51c_0.conda + sha256: d0916c2c6628cf208b7b7c1fd5c74ef730fbe1de2fe9d5025cedefc5ea910073 + md5: e432026d73defb11a5fcd1fcd2c488e8 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 1879157 - timestamp: 1787949037965 + size: 2036863 + timestamp: 1788728454973 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda sha256: 471f34a187fdb4f2df33e26f2e471b16c239a5b277903f643d9c3a8c9a9f44ec md5: 0c7b78d9ffff4f5a6ca28d346f734d8f @@ -10759,18 +10681,18 @@ packages: - gts >=0.7.6,<0.8.0a0 size: 304331 timestamp: 1686545503242 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_0.conda - sha256: 1cb2c173e80c2c166589e2e43d822db4ce84385d54800a108de39f240e0c24c6 - md5: f71b69fa28637827ec618fe783b7ec11 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.4.0-hce30654_1.conda + sha256: c0a81c23a1b09831e46d925b5a32cfaca996ca513b81ad654696fb4288575610 + md5: cdd9d9477a554951d3e0b59600e29a0f depends: - - libharfbuzz-devel 14.4.0 hcda0f7c_0 + - libharfbuzz-devel 14.4.0 hcda0f7c_1 license: MIT license_family: MIT run_exports: weak: - libharfbuzz >=14.4.0 - size: 11020 - timestamp: 1787795107813 + size: 11074 + timestamp: 1788405496569 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda sha256: 46a4958f2f916c5938f2a6dc0709f78b175ece42f601d79a04e0276d55d25d07 md5: cfb39109ac5fa8601eb595d66d5bf156 @@ -10791,19 +10713,18 @@ packages: - icu >=78.3,<79.0a0 size: 14070242 timestamp: 1786545847761 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hb84d1df_2.conda - sha256: 8fc009559f6fdce0bfc053898d62a48bdfcca1d4960f785922019e6999aca62f - md5: 0749dc3555b919277586d96a8e3240f7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/immutables-0.21-py314hcd3153d_3.conda + sha256: 7393bd7e49cdf981c3b740ab4333ad268d6c1fb7e91258e5285adccd0739c23e + md5: 1350b77940797b052115a0148cf9478b depends: - __osx >=11.0 - - python >=3.14.0rc2,<3.15.0a0 - - python >=3.14.0rc2,<3.15.0a0 *_cp314 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE run_exports: {} - size: 52567 - timestamp: 1757685575891 + size: 53264 + timestamp: 1788611810664 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda sha256: 73179a1cd0b45c09d4f631cb359d9e755e6e573c5d908df42006728e0bf8297c md5: 94f14ef6157687c30feb44e1abecd577 @@ -10816,19 +10737,20 @@ packages: - json-c >=0.18,<0.19.0a0 size: 73715 timestamp: 1726487214495 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda - sha256: cff0bfcd5685c8bb630cd2e8e99ea9a58857e6ccf943cbf96df7b1d2b70eb451 - md5: 3e24b0e86143022ba7eba8ae276fab3d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_1.conda + sha256: 8cc6630ed7586ce9380032a591b6b8f79cf7773a51b207e1e6f5047ed73378c9 + md5: dcb677490a8ad183ef65ea3b468de2c1 depends: - python - - libcxx >=21 - - __osx >=11.0 - python 3.14.* *_cp314 + - __osx >=11.0 + - libcxx >=21 - python_abi 3.14.* *_cp314 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 66681 - timestamp: 1787938517076 + size: 66680 + timestamp: 1788505778024 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda sha256: aaea1d42b07769920db2af7471ece9399d2613448863da64ad8272998db5db34 md5: 15235dd10450d67bc25ccafd5b46d2bc @@ -11048,14 +10970,15 @@ packages: - liblapacke 3.11.0 10*_openblas - mkl <2027 license: BSD-3-Clause + license_family: BSD run_exports: weak: - libblas >=3.11.0,<4.0a0 size: 18171 timestamp: 1788076987757 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda - sha256: 5e62b856b2e77ce98db44133bacab1281b42c1040dcbd69dbacfb80890cff5b0 - md5: b457450ba3f27c4749783c0204bd17b0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + sha256: 8a6e4789a6049dabb41c6fcaf6719232245484d81dfeb199b5abdb95c84c9fc2 + md5: 8f3981871d167a6cd323e636e1929dd4 depends: - __osx >=11.0 license: MIT @@ -11063,34 +10986,34 @@ packages: run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 80027 - timestamp: 1786622846050 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda - sha256: 510c9fce0d9ffcf41741dd96fc05db381672109d5665ef002c2e58f0d4ca0118 - md5: e07a99c6fdd984f4d588060f2f936bf4 + size: 79648 + timestamp: 1788480342707 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + sha256: 4e1f5989561de4546209b42c935795a41fa4265e0b670861480f1adffec623a8 + md5: da537a1643ef3e13bdccf16cca206f2c depends: - __osx >=11.0 - - libbrotlicommon 1.2.0 h1dcdb26_3 + - libbrotlicommon 1.2.0 h1dcdb26_4 license: MIT license_family: MIT run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 29935 - timestamp: 1786622857695 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda - sha256: eac412417eee2e93c62e9d53559f1f9c14f40b6c41e2c432af8b517596898dd1 - md5: 954c78a9f591bfb12c79beaff7338ec8 + size: 29864 + timestamp: 1788480352084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda + sha256: 4d2da97825b221054a39f492e4dffe361e1931c8381d0367f1e0598a50d02f34 + md5: 39a25d500b191c16996239c4afa104f1 depends: - __osx >=11.0 - - libbrotlicommon 1.2.0 h1dcdb26_3 + - libbrotlicommon 1.2.0 h1dcdb26_4 license: MIT license_family: MIT run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 295650 - timestamp: 1786622868044 + size: 295505 + timestamp: 1788480359341 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda build_number: 10 sha256: d4d786f18344c6e4a3fddd1ea19e014fa5f7bd33d11f6255b147a79dd00f9327 @@ -11102,6 +11025,7 @@ packages: - liblapack 3.11.0 10*_openblas - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -11119,9 +11043,9 @@ packages: - libcrc32c >=1.1.2,<1.2.0a0 size: 18765 timestamp: 1633683992603 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda - sha256: 59dd850def9473e7f63ed72afc750bba9670316f279c0bd409572cd47569ed5f - md5: 5ae67c128838b686894b4a3aef015c29 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.22.0-h6651222_0.conda + sha256: 46def32ec20d4dc6bce75d90fe5f2465a71cc22acef608c5ba37cc392e80e01d + md5: a7234b8d8e19a089dcb1eaaa18de1045 depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 @@ -11129,25 +11053,25 @@ packages: - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 + - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT run_exports: weak: - - libcurl >=8.21.0,<9.0a0 - size: 409852 - timestamp: 1787183686192 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda - sha256: d3e5f0b767964af25ef81edffc002f8e822b1a5c55330da1ae3a857f70c4e4ee - md5: 5303ba06fab927399ed8dfb3227b0af8 + - libcurl >=8.22.0,<9.0a0 + size: 419925 + timestamp: 1788340708264 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.1-h55c6f16_0.conda + sha256: 3f902803f0fc2643a4f82ff15a06811d974ff5fb6fa8f957720a7ef84e13ad98 + md5: b61106a0b5ac7cfe874f8b2e4f067dfa depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache run_exports: {} - size: 575940 - timestamp: 1787698697875 + size: 578565 + timestamp: 1788869180613 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda sha256: d896f4aa4ce4c590c2838678cb1917356fdb461d2a189991c0280c818c362172 md5: 78650d671cb56909bb3e5c13bce310f9 @@ -11186,18 +11110,19 @@ packages: - libev >=4.33,<4.34.0a0 size: 39991 timestamp: 1785917376956 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda - sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 - md5: 1a109764bff3bdc7bdd84088347d71dc +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda + sha256: 6dee15644c34e437920f1147974532cdcc039359b241b7eaa5fbdee275ec4621 + md5: 9de672ef7d51541ea5176779bfc3e473 depends: - - openssl >=3.1.1,<4.0a0 + - __osx >=11.0 + - openssl >=3.5.8,<4.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 - size: 368167 - timestamp: 1685726248899 + size: 369136 + timestamp: 1788870250682 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda sha256: 5af74261101e3c777399c6294b2b5d290e508153268eb2e9ff99c4d69834612f md5: a915151d5d3c5bf039f5ccc8402a436f @@ -11281,49 +11206,49 @@ packages: - libgd >=2.3.3,<2.4.0a0 size: 159247 timestamp: 1766331953491 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda - sha256: 0d2695cc7bc4e862f430d3b07e89d6e7b64a0020b9bf0293cda64ddc90b10c85 - md5: de41c6d2ce7d0138acd66d6b4122549d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.13.3-h0d10833_1.conda + sha256: 39115749e653af30b2bf9b359bd7d9839b62a6ad1934ca576c116f1109fd61dd + md5: 5b389503ff59a241b0560cf86e4b3413 depends: - __osx >=11.0 - libcxx >=21 - - libexpat >=2.8.1,<3.0a0 - - libjpeg-turbo >=3.2.0,<4.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - giflib >=6.1.3,<6.2.0a0 - - libsqlite >=3.53.4,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 - libkml >=1.3.0,<1.4.0a0 - libiconv >=1.18,<2.0a0 + - openssl >=3.5.8,<4.0a0 + - liblzma >=5.8.3,<6.0a0 - muparser >=2.3.5,<2.4.0a0 - - proj >=9.8.1,<9.9.0a0 - - json-c >=0.18,<0.19.0a0 - - geos >=3.14.1,<3.14.2.0a0 - - lerc >=4.2.0,<5.0a0 - - blosc >=1.21.6,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - xerces-c >=3.3.0,<3.4.0a0 - - libdeflate >=1.25,<1.26.0a0 - - libwebp-base >=1.6.0,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libsqlite >=3.53.4,<4.0a0 - zstd >=1.5.7,<1.6.0a0 - - pcre2 >=10.47,<10.48.0a0 - - liblzma >=5.8.3,<6.0a0 - - libcurl >=8.21.0,<9.0a0 - libpng >=1.6.58,<1.7.0a0 - - libjxl >=0.12.0,<0.13.0a0 + - json-c >=0.18,<0.19.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 - libarchive >=3.8.9,<3.9.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - blosc >=1.21.6,<2.0a0 + - libdeflate >=1.25,<1.26.0a0 - libzlib >=1.3.2,<2.0a0 + - libxml2 + - libxml2-16 >=2.15.3 + - libjxl >=0.12.0,<0.13.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - geos >=3.14.1,<3.14.2.0a0 - lz4-c >=1.10.0,<1.11.0a0 + - lerc >=4.2.0,<5.0a0 + - proj >=9.8.1,<9.9.0a0 + - xerces-c >=3.3.0,<3.4.0a0 + - libcurl >=8.21.0,<9.0a0 constrains: - - libgdal 3.12.4.* + - libgdal 3.13.3.* license: MIT license_family: MIT run_exports: weak: - - libgdal-core >=3.12.4,<3.13.0a0 - size: 10894319 - timestamp: 1786825501570 + - libgdal-core >=3.13.3,<3.14.0a0 + size: 11553949 + timestamp: 1788235755138 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda sha256: 7582efbbffc6964093afd80e01c2ac60d89aa4e404cac664544675b9d4d1c5e7 md5: aa6c627acd0f5709d9c79bf708a7b81b @@ -11348,24 +11273,24 @@ packages: run_exports: {} size: 554806 timestamp: 1787617465010 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda - sha256: 9c25def7c7f9ab98b265690c80012e917d080538875f9679b46ccd0c042e6201 - md5: 14ef48eb322e0bfb1e5fe31e047359e3 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda + sha256: aad240a33afc71c0c9cefea5304c917c256518e6724cd646d924838fdff1fcb3 + md5: f3ee2d5802e47ff391fe9a6d956e76b0 depends: - __osx >=11.0 - - libintl >=0.25.1,<1.0a0 - libzlib >=1.3.2,<2.0a0 + - libffi >=3.7.0,<3.8.0a0 - pcre2 >=10.47,<10.48.0a0 + - libintl >=0.25.1,<1.0a0 - libiconv >=1.18,<2.0a0 - - libffi >=3.7.0,<3.8.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4443052 - timestamp: 1787884141804 + size: 4443129 + timestamp: 1788525252185 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda sha256: a48050bcf3827e0fc10de5a7d251760b8b84011f3aeea23ae18b5e0ce25aff19 md5: 4f34ca73f16b37fae583ce16c48b5492 @@ -11430,9 +11355,9 @@ packages: - libgrpc >=1.82.1,<1.83.0a0 size: 4898235 timestamp: 1783587327477 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda - sha256: d152fe1ade504acaa7d4167f74565ea1dfd85d88cc9085e076e0e7010068e894 - md5: c931e6d5af7f8f824fab6c474f3a1e94 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_1.conda + sha256: a13bee65e7bcbc14b228351d3d5ba00d0c17c5840c471ab67a28b8ca930456d3 + md5: 4afba4fae30b467d6c699bb685fa35ce depends: - __osx >=11.0 - cairo >=1.18.4,<2.0a0 @@ -11447,11 +11372,11 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 972888 - timestamp: 1787795084686 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_0.conda - sha256: 1c98121d5e12ffbb86a77b1477d3bd7eca6f5f22dab3eb50ba57bf2fc98234e8 - md5: 6f23309d5939e5c7edcf57095daaa99a + size: 973351 + timestamp: 1788405475876 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.4.0-hcda0f7c_1.conda + sha256: 91d988b496749f7cd5640b283bb3de942c1e8a90d484fd1d14a52fdf103864e3 + md5: 8f48ae71e941c7e6201f80863c6757c0 depends: - __osx >=11.0 - cairo >=1.18.4,<2.0a0 @@ -11462,7 +11387,7 @@ packages: - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libglib >=2.88.3,<3.0a0 - - libharfbuzz 14.4.0 hcda0f7c_0 + - libharfbuzz 14.4.0 hcda0f7c_1 - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: MIT @@ -11470,8 +11395,8 @@ packages: run_exports: weak: - libharfbuzz >=14.4.0 - size: 1508756 - timestamp: 1787795102730 + size: 1510973 + timestamp: 1788405492213 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-hd2bdd19_1.conda sha256: d965f815878a176fb75329d02718f449230e687e100a081762688f763990abf2 md5: 0fd99c6f562545ace82194d79e697ad3 @@ -11561,6 +11486,7 @@ packages: - libcblas 3.11.0 10*_openblas - liblapacke 3.11.0 10*_openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -11577,6 +11503,7 @@ packages: constrains: - blas 2.310 openblas license: BSD-3-Clause + license_family: BSD run_exports: weak: - liblapacke >=3.11.0,<3.12.0a0 @@ -11595,71 +11522,71 @@ packages: - liblzma >=5.8.3,<6.0a0 size: 91720 timestamp: 1786348695846 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314hf3ae1d6_0.conda - sha256: 5291da681b85d4a12483b0f9a57bf18e191d0a52e749f18266922457b5bc200c - md5: 1646d42b756ad5a554903866fc2871a1 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-2.9.0-py314h12d839b_0.conda + sha256: 8b541cf490c13f401c0ca303ea7181cbb193b3eb618461db2f5417f72eba3246 + md5: d9368914eda9ece54f67917603eeed86 depends: - cpp-expected >=1.3.1,<1.3.2.0a0 - - libcxx >=19 + - libcxx >=21 - __osx >=11.0 - - libmsgpack-c >=6.1.0,<7.0a0 - - simdjson >=4.6.6,<4.7.0a0 - - reproc >=14.2.7.post0,<14.3.0a0 - - libcurl >=8.21.0,<9.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - zstd >=1.5.7,<1.6.0a0 - fmt >=12.1.0,<12.2.0a0 - - libsolv >=0.7.39,<0.8.0a0 + - nlohmann_json-abi ==3.12.0 + - libcurl >=8.21.0,<9.0a0 - yaml-cpp >=0.8.0,<0.9.0a0 + - reproc-cpp >=14.2.8.post0,<14.3.0a0 + - reproc >=14.2.8.post0,<14.3.0a0 + - libsolv >=0.7.39,<0.8.0a0 + - libmsgpack-c >=6.1.0,<7.0a0 + - simdjson >=4.6.9,<4.7.0a0 + - openssl >=3.5.8,<4.0a0 - spdlog >=1.17.0,<1.18.0a0 - - zstd >=1.5.7,<1.6.0a0 - - libarchive >=3.8.9,<3.9.0a0 - - openssl >=3.5.7,<4.0a0 - - nlohmann_json-abi ==3.12.0 - - reproc-cpp >=14.2.7.post0,<14.3.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - libmamba >=2.9.0,<2.10.0a0 - size: 1846328 - timestamp: 1786125740624 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-heb3ad76_0.conda - sha256: 7e379c5b7527d878e2617237b73d7866dbdf3d9dbaf36352ad39ab0a85802831 - md5: c0894144cb6c8d04f7699a5f5ead0c64 + size: 1855457 + timestamp: 1788274176583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmamba-spdlog-2.9.0-h7215443_0.conda + sha256: 2c6e96065eb4e0f50394a34e0a9ec2a559d27c7d698ec7db293447818031f2b9 + md5: 2b1c7e5a9f22a06fa879f9026fd325dc depends: - - libcxx >=19 - __osx >=11.0 + - libcxx >=21 - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 23751 - timestamp: 1786125740624 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314hfa70e9c_0.conda - sha256: 785d75a82bf43c123222a3933f84a7c2d1f9d83eb5a07d2552c75725f17a6309 - md5: c57c6b1c9fe4b161997ce6a6a705faf6 + size: 23785 + timestamp: 1788274176583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmambapy-2.9.0-py314h53f8b30_0.conda + sha256: a0fd36c4b242a9b95f5ba3d3d91f616182dcf76d7c6e92165d5bc7a178414258 + md5: ceffd96ca93f18820617a9d62587c17d depends: - python - - libmamba ==2.9.0 py314hf3ae1d6_0 - - libmamba-spdlog ==2.9.0 heb3ad76_0 + - libmamba ==2.9.0 py314h12d839b_0 + - libmamba-spdlog ==2.9.0 h7215443_0 - __osx >=11.0 - - libcxx >=19 + - libcxx >=21 - python 3.14.* *_cp314 - - zstd >=1.5.7,<1.6.0a0 - - libmamba >=2.9.0,<2.10.0a0 - - spdlog >=1.17.0,<1.18.0a0 - yaml-cpp >=0.8.0,<0.9.0a0 - - openssl >=3.5.7,<4.0a0 - - fmt >=12.1.0,<12.2.0a0 + - spdlog >=1.17.0,<1.18.0a0 + - zstd >=1.5.7,<1.6.0a0 + - nlohmann_json-abi ==3.12.0 - python_abi 3.14.* *_cp314 + - openssl >=3.5.8,<4.0a0 - pybind11-abi ==11 - - nlohmann_json-abi ==3.12.0 + - libmamba >=2.9.0,<2.10.0a0 + - fmt >=12.1.0,<12.2.0a0 license: BSD-3-Clause license_family: BSD run_exports: weak: - libmambapy >=2.9.0,<2.10.0a0 - size: 779694 - timestamp: 1786125740624 + size: 778026 + timestamp: 1788274176583 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda sha256: 04cc136c5a956a73aa14e0a160b5822b0b29714e67b299fa1b1fd16dbcba5366 md5: ff33a4dbd93abc8a798cc4e0e7c8136d @@ -11713,6 +11640,7 @@ packages: constrains: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - libopenblas >=0.3.34,<1.0a0 @@ -11811,17 +11739,17 @@ packages: - libpsl >=0.23.1,<0.24.0a0 size: 72673 timestamp: 1786970928205 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda - build_number: 104 - sha256: 551c2160312cbba2e8b4573c827a756d8ab7824a05f0eb64d954f0a18b2e5c21 - md5: 9ef5086783f252da744fc0f7a336e4d8 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_106_cp314.conda + build_number: 106 + sha256: b239028180e1ba2f7daff4053c89e41f7c0f1a3c21bb82f6dcb09384d123ec1f + md5: 4d7a303343eff82e8b92d52c5fd3991a depends: - __osx >=11.0 - libcxx >=20 license: Python-2.0 run_exports: {} - size: 1883387 - timestamp: 1788160791643 + size: 1875368 + timestamp: 1788383432953 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda sha256: b110f7f9b2cec61ea793c521950414e00cd64826e18aeb4ab40369acf05c0039 md5: 46ea0eb4e71bffa35ab7070678bcb053 @@ -12043,9 +11971,9 @@ packages: - libxcb >=1.17.0,<2.0a0 size: 324234 timestamp: 1787885764666 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda - sha256: 2ca61d8287339c726b151fa612e83d2afa1418b89fa17694c873dc231bc9b077 - md5: f86c0b8ac5c866049717b55df1049c2c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda + sha256: 8a26f7c94ab849c1930da36c298f4b82e892382b70ae865cd7274188d9d783e2 + md5: b40633f711a5eb8617917e0e944b9965 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 @@ -12053,49 +11981,49 @@ packages: - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - - libxml2 2.15.3 + - libxml2 2.15.4 license: MIT license_family: MIT run_exports: {} - size: 466188 - timestamp: 1787237580766 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda - sha256: ff1b70683ccb82f8aa7f3eda53405a2036d849daba4e4e622f330dcd49d37700 - md5: 06a3f8eb5cdde56b2d10b49ae3337954 + size: 469299 + timestamp: 1788635201153 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda + sha256: 81217fcd4d4abdc69ee63cdb9cdb22e05a20b9ba7a76f596579914a838fe5960 + md5: 4b97a34d06740b578c32deba195daf5a depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 h5ef1a60_1 + - libxml2-16 2.15.4 heb56d2d_0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 41264 - timestamp: 1787237585903 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda - sha256: 5628776073d5e736e3a87747d4f91c31478fbbc980c01c4455c892edf6d308d0 - md5: fff82f66f5775661123c2963930f318b + - libxml2-16 >=2.15.4 + size: 41375 + timestamp: 1788635206084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda + sha256: bf409a8a6c776479469de85eb25abbb35e750ef5b880fdbdcf3e2158255f6ba5 + md5: df2f9521a1b80a859d6d329dba7df2a0 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h5654f7c_1 - - libxml2-16 2.15.3 h5ef1a60_1 + - libxml2 2.15.4 h550178d_0 + - libxml2-16 2.15.4 heb56d2d_0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 80342 - timestamp: 1787237590812 + - libxml2-16 >=2.15.4 + size: 80435 + timestamp: 1788635210248 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 md5: f39288f0ea63ae962e1a2e4f355a0d75 @@ -12251,18 +12179,19 @@ packages: - minizip >=4.2.2,<5.0a0 size: 462531 timestamp: 1788051612824 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda - sha256: 1376d243d5f9a1145ba03950679932a70834d5db4ef9de8fdd1b030a64a7d619 - md5: 0b9070858d6aaf7f767a520063f68a3c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda + sha256: a84144f02b58e8914e9be09d726b88b843a1eb4a377f1f439e9fabfd54dd9aad + md5: 36fc4194dcbcb14ed2ff3e1db09e3e89 depends: - python - libcxx >=21 - __osx >=11.0 - python_abi 3.14.* *_cp314 license: Apache-2.0 + license_family: APACHE run_exports: {} size: 101639 - timestamp: 1787850896574 + timestamp: 1788525104390 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda sha256: cc40c57dbfa4890873835f22415760488319b03d4edfc11386b2e90b89f011da md5: 408009d803402dd1ad4ffae452f17353 @@ -12272,14 +12201,15 @@ packages: - __osx >=11.0 - llvm-openmp >=21.1.8 license: MIT + license_family: MIT run_exports: weak: - muparser >=2.3.5,<2.4.0a0 size: 171420 timestamp: 1788122548109 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hf2636f2_0.conda - sha256: 934a50e83c879f3665cc84f03f0d83f58b319376cb3e94275b2aa67d328a309d - md5: 0819586a0fc1927c72a757d872e452cc +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-2.3.1-py314hd98292b_1.conda + sha256: d969ca4b396bca199adfac53b610f9e9fc4fee9e9a6dbfd7fbc9bb0ee1f800ad + md5: ca731b7bff34d5223f0caa58fabfabc7 depends: - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 @@ -12293,8 +12223,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 13265109 - timestamp: 1786887244960 + size: 13265313 + timestamp: 1788615740938 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 md5: 3dfa0d0316dc246cd44937a557de4501 @@ -12316,41 +12246,42 @@ packages: run_exports: {} size: 137984 timestamp: 1785920576349 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda - sha256: 372dfde32c064cfcc9e2bd73956d8229053794697c3e875a9bcb916e5163d66e - md5: 07f5745d2ec9349b1401e3891240c64f +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py314he06036b_0.conda + sha256: 693fa3d8d4303c35250025409d1cbc6d0d6270fd3778303dfe9ea89e29003871 + md5: 37bb35c87a060e9ab1551c5bd987074f depends: - python - - libcxx >=21 - __osx >=11.0 + - libcxx >=21 + - libblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - - python_abi 3.14.* *_cp314 - libcblas >=3.9.0,<4.0a0 - - libblas >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 constrains: - numpy-base <0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - numpy >=1.25,<3 - size: 7257323 - timestamp: 1788129265176 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 - md5: 4b5d3a91320976eec71678fad1e3569b + size: 7273776 + timestamp: 1788732001328 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-h4d1e80c_2.conda + sha256: e71bd04b5d0e39f39a3c039d021d3b76cd65bee3032e7f859ab7082f0ace8be5 + md5: 487d92910109a711fdb77631bf1aabf0 depends: - __osx >=11.0 - - libcxx >=19 - - libpng >=1.6.55,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 + - libcxx >=21 + - libtiff >=4.7.2,<4.8.0a0 + - libzlib >=1.3.2,<2.0a0 + - libpng >=1.6.58,<1.7.0a0 license: BSD-2-Clause license_family: BSD run_exports: weak: - openjpeg >=2.5.4,<3.0a0 - size: 319697 - timestamp: 1772625397692 + size: 377958 + timestamp: 1788425205328 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda sha256: f23239eacd75c4c50705e68fae1aa3292da473e6a3a4abe2330f1e6afa680704 md5: ae71ab40048c19a389a7dcccb86c2481 @@ -12441,28 +12372,28 @@ packages: run_exports: {} size: 14402418 timestamp: 1784821943837 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - sha256: c300fba11c4cd7cdd7609b6f165981af24d2181d40280ca6af420710c4c7d42e - md5: 83c3d3d895dd96f87b0a1916e2c41f70 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf9b25ef_1.conda + sha256: 9ba23103198cffa523488ff583b184ad37eb6d90985d330a683a1039dc917771 + md5: 4a71e68873ce37dc6da671a899d09a46 depends: - __osx >=11.0 - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 + - libharfbuzz >=14.4.0 - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later run_exports: weak: - pango >=1.58.2,<2.0a0 - size: 444011 - timestamp: 1786108209790 + size: 442274 + timestamp: 1788491046895 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda sha256: f8c415329b542e1fca1ff2917b80e687c18302dfa0353530668b90cb225b494f md5: 5ab90033816cbe4097e8a297e5179f67 @@ -12477,28 +12408,27 @@ packages: - pcre2 >=10.47,<10.48.0a0 size: 851704 timestamp: 1787294559157 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda - sha256: dd7303ea135116cc1182c109825459a9eeb77d244d3899f03dd8d83a8db956f9 - md5: 4f25498ea1962ab24097fed8700e91ae +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314h709c99d_2.conda + sha256: 13f0c4747ab2e6c9e64885245a90d93221477d412bc699b726d6415bd9123744 + md5: f2da127864d9f3fcc5ccc00d68b04e63 depends: - python - - python 3.14.* *_cp314 - __osx >=11.0 - - zlib-ng >=2.3.3,<2.4.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 - openjpeg >=2.5.4,<3.0a0 + - python_abi 3.14.* *_cp314 + - libtiff >=4.7.2,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - lcms2 >=2.19.1,<3.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - - libxcb >=1.17.0,<2.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - python_abi 3.14.* *_cp314 - tk >=8.6.13,<8.7.0a0 - - lcms2 >=2.19.1,<3.0a0 license: HPND run_exports: {} - size: 1019767 - timestamp: 1782912213683 + size: 1025562 + timestamp: 1788610377472 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda sha256: 4779cd57231ce2e96fac643fcbdd4a6f51a57986264545445574e9c4acf526d3 md5: 9a99c0b60efe41c194d01c182d000733 @@ -12527,6 +12457,7 @@ packages: constrains: - proj4 ==999999999999 license: MIT + license_family: MIT run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -12548,9 +12479,9 @@ packages: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 173220 timestamp: 1730769371051 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda - sha256: 1cb01e783a9bcc350ecc1d80cbd207c0b0e1f60dc0db6ded8650e318796ba4bf - md5: bea6a028c9205ea3789bb38409cbfe43 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_2.conda + sha256: bb324f466a5df891a7f49c86b34a3b57dc2b550fdff00be10bfb162350c43ba5 + md5: 84d4b47c92ccfdfd6434146b27222be1 depends: - python - __osx >=11.0 @@ -12558,18 +12489,18 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 242631 - timestamp: 1787417424394 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda - sha256: 1c2338b9b0486af86883b9593d2f3e2845bbf216ea453dfe5d9a228e8dbe4057 - md5: d4852e6054b74645cf49ca10f6349191 + size: 242750 + timestamp: 1788190047444 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hb001647_1004.conda + sha256: 3f79452dbda3febaf7053486e4844fa945accf4d74eac4169e2c4d5dc5e4dd0b + md5: f0833c4a0b68798f50935dd83384c705 depends: - __osx >=11.0 license: MIT license_family: MIT run_exports: {} - size: 9238 - timestamp: 1786068031100 + size: 9662 + timestamp: 1788382422327 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pulp-2.8.0-py314h676fd1f_3.conda sha256: fad9646989fb16b3f5f858ffd81dbac5d9f5e51e98b829fa19cfb5897fbc66db md5: 568c38aba1768777e2e92bfd1a3e94f2 @@ -12584,22 +12515,22 @@ packages: run_exports: {} size: 229818 timestamp: 1757853438761 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310hd89b7e0_3.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/py-rattler-0.25.0-py310h2dd3e6a_5.conda noarch: python - sha256: a0fc7ff4902bf882b14c6c56c4d58365a6872e330fc34704da7fa6cc992985c8 - md5: c32ebe551aa8bac30b41c3717a15e1d6 + sha256: f1549f5f3ed4cac1d5aa252b6aa244c024c84cede8b56e95213a7d8c40e5c037 + md5: e4503f021d9250cbc247dde89f499379 depends: - python - __osx >=11.0 - - openssl >=3.5.8,<4.0a0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __osx >=11.0 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 10601531 - timestamp: 1787913213404 + size: 10609109 + timestamp: 1788524939961 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py314he55896b_0.conda sha256: cd50941d2b035acef8f54088108d464bfa1845c45b8ba63270f7cee70801157b md5: 1a1b2c10602cd3dd7241e485e71d02a2 @@ -12635,9 +12566,9 @@ packages: run_exports: {} size: 4030783 timestamp: 1784258302127 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hf2d41ac_4.conda - sha256: 640d2d29a18e3a8dda02427616bd55af5670565fd780f7b5df4c045548471909 - md5: 99e06db67584235df895feda84a4790e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pycosat-0.6.6-py314hcd3153d_5.conda + sha256: 0cb98954c0ad400cbde85489e32611fa0991a442ce3903d4a4ffcac5c5993444 + md5: 4dec0730b5dc1181f806dc99462d963f depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 @@ -12645,8 +12576,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 93498 - timestamp: 1784146274338 + size: 85925 + timestamp: 1788489352795 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py314hc05cd11_1.conda sha256: 60a639a7bd3cde997574e0f41af74c76fe37056c42b94fe1bf61a4e1b376942b md5: 586050b385a407a449fd7e5e4a5c8fef @@ -12658,16 +12589,17 @@ packages: constrains: - __osx >=11.0 license: MIT + license_family: MIT run_exports: {} size: 1691220 timestamp: 1787928985509 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda - sha256: dcaaab4d8b539f7c4ee740e0242ae09c48f68e75949476ca36d9c67e61aafc3b - md5: 9b33fa020bd4da86a2dddfd0f63a43ba +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.13.0-py314h618c026_0.conda + sha256: dafde81a5bf0ee22ef5813b54a9f8373b6bd03a085d3bbf7676c73fc3b4e2e2d + md5: 075ed1d8fa1903dd55bbbfc912bff1c9 depends: - __osx >=11.0 - libcxx >=19 - - libgdal-core >=3.12.0,<3.13.0a0 + - libgdal-core >=3.13.1,<3.14.0a0 - numpy - packaging - python >=3.14,<3.15.0a0 @@ -12676,26 +12608,27 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 599877 - timestamp: 1764402722213 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda - sha256: 98ac80a1357dd67d9e3ba4b9957020e4c30643dd5e8dcfeccdd9a77a1efbc9e8 - md5: 3bdcaa1d81e1cc32648f6a22517f92e3 + size: 625197 + timestamp: 1782493440049 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.8.0-py314h92bd025_0.conda + sha256: 771c70e43effd266991328394c7294eefe48010d470f7f44665ad574247dae10 + md5: 621a9854d7eb2ccf43e2f49d626bc5bb depends: - python - proj - certifi - __osx >=11.0 - - proj >=9.8.1,<9.9.0a0 - python_abi 3.14.* *_cp314 + - proj >=9.8.1,<9.9.0a0 license: MIT + license_family: MIT run_exports: {} - size: 518157 - timestamp: 1787944739017 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda - build_number: 104 - sha256: 90df7fc0a043b2888cce45361c55473ce37cb57ec5f8e1bc301d981b5ba8087a - md5: 86c5773c0f675287d7852068554806c9 + size: 533507 + timestamp: 1788664413993 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda + build_number: 106 + sha256: 5ffc8349ca089ec5ac39ad36a095323ad177ebe05cbfc690c1e135560cfd68b7 + md5: 5fd37c78250b4f57c2bcf9ad2e54beb1 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 @@ -12703,7 +12636,7 @@ packages: - libffi >=3.7.0,<3.8.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - - libpython 3.14.7 h4311e70_104_cp314 + - libpython 3.14.7 h4311e70_106_cp314 - libsqlite >=3.53.4,<4.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 @@ -12719,22 +12652,22 @@ packages: - python_abi 3.14.* *_cp314 noarch: - python - size: 12357045 - timestamp: 1788160826259 + size: 12223441 + timestamp: 1788383470498 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314ha14b1ff_0.conda - sha256: 07e71e940c1268e224c9a6a1f96480c10789ca521928cda30c20aa07f19aca21 - md5: e82ce608933c6feb7fc9ef479d2a2227 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py314haae224a_2.conda + sha256: b3dda52eaeaab53aa7dc4682e9bb6dc9fbd04175eca390b791e3e6d608aac259 + md5: 5971a73214e17dbe95ad868e55a38c41 depends: - python - - python 3.14.* *_cp314 - __osx >=11.0 + - python 3.14.* *_cp314 - python_abi 3.14.* *_cp314 license: MIT license_family: MIT run_exports: {} - size: 134968 - timestamp: 1786328898613 + size: 129750 + timestamp: 1788524764608 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py314ha14b1ff_2.conda sha256: 37d76254955eaa47df534ae8f3890c45f28aa4a8479597ff6fbacbd5a23810e6 md5: 45bfaa1961db6ace6fc35561ba893fae @@ -12790,9 +12723,9 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 516376 timestamp: 1720814307311 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda - sha256: 10737f408812344475097991cf313492be966f477ff6553bfc81e7d182f3f705 - md5: d2ca5fbd28cd1fd50b2dabf9ffc96df0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.1-py314h716b1d8_1.conda + sha256: d65c82d009c5e06a8433cc236c22471a99584ca58b751aab85edaac6cac00e2a + md5: 80702063a5c4bd1b8110a0c6179d4621 depends: - __osx >=11.0 - affine @@ -12802,7 +12735,7 @@ packages: - click-plugins - cligj >=0.5 - libcxx >=19 - - libgdal-core >=3.12.4,<3.13.0a0 + - libgdal-core >=3.13.2,<3.14.0a0 - numpy >=1.25,<3 - proj >=9.8.1,<9.9.0a0 - python >=3.14,<3.15.0a0 @@ -12813,8 +12746,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 7369572 - timestamp: 1785965949045 + size: 7843108 + timestamp: 1786314231681 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda sha256: ba1154085d70e8e8f94cfc38a018a13d1a734ff1bcc2ab384e4fc3e532b23066 md5: a2578dc92c16ab5b6b183cfcdc8e8b79 @@ -12847,6 +12780,7 @@ packages: depends: - __osx >=11.0 license: MIT + license_family: MIT run_exports: weak: - reproc >=14.2.8.post0,<14.3.0a0 @@ -12861,14 +12795,15 @@ packages: - libcxx >=21 - reproc >=14.2.8.post0,<14.3.0a0 license: MIT + license_family: MIT run_exports: weak: - reproc-cpp >=14.2.8.post0,<14.3.0a0 size: 29958 timestamp: 1788160507406 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_0.conda - sha256: 7d4b49a7542735c63f0ef3379f83dfed1870a5d62ae4c3e357f859014fb1fcaf - md5: aa2a7e9c0ab5322b9bd6e75a384b48a1 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_2.conda + sha256: 67b3019be7d60823e5debe3b58cb2cb64f0913edb7f5454c88a885133d025e7e + md5: fb944378df3143d95854b319fdb0ff87 depends: - python - __osx >=11.0 @@ -12878,8 +12813,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 284937 - timestamp: 1787344325551 + size: 284991 + timestamp: 1788577307849 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda sha256: 0f498e343be464219a99424260ff442144d0461a3a5eb30c9de6081af358b281 md5: 87fc3a204f105e7e61c60a72509cccbd @@ -12894,9 +12829,9 @@ packages: run_exports: {} size: 311922 timestamp: 1766175797575 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_2.conda - sha256: 8ab180c1bbe93b952c9948270e25c3f38ad9229e6a7f751084cea0f6ecf150f8 - md5: 6ed337ed07a1c97fac976e54f506e3c4 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_4.conda + sha256: 8846a7464ebe2dafc78fe83252af615923e73bb78e027cda24391825a6f72ad3 + md5: 652ce80522ce6713d9c24ed68afc9b1c depends: - python - __osx >=11.0 @@ -12904,21 +12839,22 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 121129 - timestamp: 1787264332077 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.5-h4e39fff_0.conda + size: 127816 + timestamp: 1788484961194 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.6-h7f187d3_0.conda noarch: python - sha256: 7770d275e7bab61c9f33dd646551975e546c8df5fa215691133e13207814f535 - md5: c736cce00267b24375ea10abbe2fedc7 + sha256: afa2a86da96a6438e8b8bc65b4575d0ed058fd2bbf435a9a503cd9cd46dac440 + md5: ad11c5c596f426a183f4642f66c2f5f2 depends: - python - __osx >=11.0 constrains: - __osx >=11.0 license: MIT + license_family: MIT run_exports: {} - size: 8177581 - timestamp: 1787865392183 + size: 8188680 + timestamp: 1788474751389 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda sha256: 47ec36bdd05117c974853a8dbaa9d89605a5f70b592d47c7d198b20252ddf1f4 md5: 94b3b302cd6c07c5ec68648b25c8c525 @@ -12989,9 +12925,9 @@ packages: run_exports: {} size: 618928 timestamp: 1762524178138 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.9-hfbe1efa_0.conda - sha256: 3f2e7a3d3fe479c4216a588202effbcc5496a41f6ae42ed294fb21513254cfc8 - md5: 1df792a337dc7d1a940bb32083d63210 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.11-hfbe1efa_0.conda + sha256: f71c2b4b7a26fe1dde566da612440f96eb940b2362f54b62a16415a26419eb8f + md5: 867c0f276e3e4a3db48b1a3e3f1e0636 depends: - __osx >=11.0 - libcxx >=21 @@ -12999,12 +12935,12 @@ packages: license_family: APACHE run_exports: weak: - - simdjson >=4.6.9,<4.7.0a0 - size: 311606 - timestamp: 1787803743194 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda - sha256: cc5a4251cb21aa940082f85d4359f591e2e0cc854106ac3d63002130eb7e58f8 - md5: c2ca364702b1313c4f42aa39125724e4 + - simdjson >=4.6.11,<4.7.0a0 + size: 311692 + timestamp: 1788630512409 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_1.conda + sha256: f132da3e34c12ecfa0055479aab4c874c9caf0c902af01b7f564f6a384467eda + md5: 6d4bee872fa16ae1e1145a6a3cc17c61 depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 @@ -13013,8 +12949,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 167699 - timestamp: 1787824816800 + size: 168271 + timestamp: 1788525705695 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d md5: fca4a2222994acd7f691e57f94b750c5 @@ -13084,9 +13020,9 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3342183 timestamp: 1787272852357 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda - sha256: 4af8e4394d249619b4f1641c82fd6c0a8b97d51991ca2986095bed400c2e5eb9 - md5: ab1bd70ec9c95d199f8ce456823004ab +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda + sha256: fdb4ce657a7abcac84090c80ae9a4b93fdd10fe5a3178baa66b71f7df867602f + md5: 8fc44bfcc973b7cab443539ce858161a depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 @@ -13095,11 +13031,11 @@ packages: license: Apache-2.0 license_family: Apache run_exports: {} - size: 922067 - timestamp: 1786227125229 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda - sha256: 09bfbee5a2bcf4df06f21a2aa9eb40a7af97864a569beb5ea85fd6baf6e03ce7 - md5: 4fffb3ba871bb05f34ffb705534dfef5 + size: 921286 + timestamp: 1788525172641 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda + sha256: 2286fe2a39986efa9b752d22b808843491b5a4314f0ace4533465cf73e18ab75 + md5: b3ac6c13ee65405411de670a7ea4508e depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 @@ -13108,8 +13044,8 @@ packages: license: Apache-2.0 license_family: Apache run_exports: {} - size: 416130 - timestamp: 1770909728445 + size: 418969 + timestamp: 1788554883946 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda sha256: fa0bcbfb20a508ca9bf482236fe799581cbd0eab016e47a865e9fa44dbe3c512 md5: e8ff9e11babbc8cd77af5a4258dc2802 @@ -13240,20 +13176,6 @@ packages: - zlib-ng >=2.3.3,<2.4.0a0 size: 95857 timestamp: 1786737243497 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314hd98292b_3.conda - sha256: e790bd7c15afa1d59d2d72b86825932338147cd7c832e7e54b8dc29adfb61be5 - md5: a8e20554ed5fbd1f81f1b098bfe03eb5 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - __osx >=11.0 - - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - run_exports: {} - size: 383104 - timestamp: 1787896531255 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda sha256: da867f5092eb0cb746d353694f0098031fd9817a4ce7d5743121209ae0f406ca md5: 4ec2684c73812cc2c3d78379384a39cc @@ -13284,22 +13206,21 @@ packages: - _openmp_mutex >=4.5 size: 52252 timestamp: 1770943776666 -- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.8.0-py310ha413424_0.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.10.0-py311h66afae6_0.conda noarch: python - sha256: c52d4750dce807f24a4698fb3c8894b8c6e7cfca8000f5e4050a8e43d24eaf1f - md5: ecde03792069f1f16ff12c2395635035 + sha256: 4426b41c106de5b670158ede3a283cd5302042215de68f005bffef8e03cf82b8 + md5: 34223054d0270aeb848ffc617f839b14 depends: - - python >=3.10 + - python >=3.11 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - _python_abi3_support 1.* - - cpython >=3.10 + - cpython >=3.11 license: MIT - license_family: MIT run_exports: {} - size: 1071375 - timestamp: 1786328955132 + size: 1104850 + timestamp: 1788869566649 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda sha256: cf7ab8abaac6b462463310412cc37d087a767a95920809cbe6dc0a08fe4bcfbd md5: f7069cdc81f7a5e781f1faf6aab6c171 @@ -13617,13 +13538,13 @@ packages: - blosc >=1.21.6,<2.0a0 size: 49840 timestamp: 1733513605730 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda - sha256: 85e12348d058791aabd6eeb6abc1d7ab44b8f6308f91c8475f44f778f2a158af - md5: 9eba56a007c44dc32d0b9d0a820871ea +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_4.conda + sha256: 7a4110b57f0e957ca4cfd2fc60cede2ccea42b80b404f646339bd874ace18845 + md5: bdbb2939efe8af10e7c5885367395710 depends: - - brotli-bin 1.2.0 hd477307_3 - - libbrotlidec 1.2.0 h84f9c24_3 - - libbrotlienc 1.2.0 he2a975b_3 + - brotli-bin 1.2.0 hd477307_4 + - libbrotlidec 1.2.0 h84f9c24_4 + - libbrotlienc 1.2.0 he2a975b_4 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -13634,25 +13555,25 @@ packages: - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - size: 20954 - timestamp: 1786622876247 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda - sha256: 5328e0576c729813d28efc15613f4be94d029d77c4fe5afda16cab0b8d0c9d05 - md5: dc04f7b3d82c6fb3fdf4d93e45b6ea2a - depends: - - libbrotlidec 1.2.0 h84f9c24_3 - - libbrotlienc 1.2.0 he2a975b_3 + size: 20944 + timestamp: 1788480420397 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_4.conda + sha256: 740b171b2cdf793891b36e1e2f81d7829e9b93c8a0d1a72d9ffaebef58eab016 + md5: 5353bd43aaade891fc9b614c5d537ce3 + depends: + - libbrotlidec 1.2.0 h84f9c24_4 + - libbrotlienc 1.2.0 he2a975b_4 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT run_exports: {} - size: 23119 - timestamp: 1786622866096 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda - sha256: f96c411313beb92a6a9066b823f7e8ea085f3e3889219b44306c44d59f99e611 - md5: b1ff58c1f0deedd3f25c103e37049cee + size: 23077 + timestamp: 1788480410898 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda + sha256: 77cba8469644eccf05a5be5216ae2c71471ae09e9febc6ef39eb73ac07de08c3 + md5: a0d7c645ae6b82fda8a2eb24a1de7b43 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -13660,12 +13581,12 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - libbrotlicommon 1.2.0 hf02afa3_3 + - libbrotlicommon 1.2.0 hf02afa3_4 license: MIT license_family: MIT run_exports: {} - size: 336902 - timestamp: 1786623039339 + size: 336837 + timestamp: 1788480517368 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda sha256: 04767466ee9227c9c57ab2c6503e0149177d34111c7418d2f420297acb1eb229 md5: c3301c058362f340100d91cd8be0393f @@ -13694,9 +13615,9 @@ packages: - c-ares >=1.34.8,<2.0a0 size: 210803 timestamp: 1787169972884 -- conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda - sha256: 9128dfc6a864e369ee7b1c9bd4b3875de9d06890925760aec932cf3cc67bcb24 - md5: 601e2ec8ae0ed934d8788000f984ff18 +- conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda + sha256: 167be08bde10daeefc505673966c470e8e236195c4edc2000a8d2441bc72a28b + md5: 37e4ca2dd35c4171f0587d985976b30e depends: - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem @@ -13715,23 +13636,8 @@ packages: run_exports: weak: - cairo >=1.18.4,<2.0a0 - size: 1540051 - timestamp: 1787926241225 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.1.1-py314h5a2d7ad_2.conda - sha256: e0e10d676eb67a8a4c8991cec89fb6f150f919eb1266a1b39253a857b3dc0454 - md5: 672d6ff72c6265b25eeef94ce21e71ac - depends: - - pycparser - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - run_exports: {} - size: 346657 - timestamp: 1786775160153 + size: 1535591 + timestamp: 1788221425576 - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-cbc-2.10.13-hbda3ed7_1.conda sha256: 39891566d1620cd759e8e2f7b3d2bee66f3cfceca577f23cf12bbaa3f7f0c066 md5: 779a28ebe99dde22af1b281ee2a09ecf @@ -13837,9 +13743,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 1091975 timestamp: 1778500610309 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.1-py314hb821551_0.conda - sha256: d265867efa08fbec3dad842b9da4e015d50b7c43cafa67921b0f38f96bb67e17 - md5: de36e896fdc6c9ed9950a93fc3022e27 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_0.conda + sha256: 9d3c54d8c23a059dce33616f2922166587fa0752a4ec6bcca08145511f38ac96 + md5: 5a3986bfa78332029e9beb317a76cc13 depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -13872,29 +13778,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1473305 - timestamp: 1787306256970 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-pypi-0.11.0-py314hb821551_1.conda - sha256: af3766a095bd7b2f5f0a8958474d29c139e51628230b98b7e45ff4af1702c8ae - md5: bab6a2a990c12195bb6553216ab660ec - depends: - - python - - pip >=23.0.1 - - packaging - - unearth - - python-build - - python-installer >=1.0 - - platformdirs - - conda-index >=0.11.0 - - conda-package-streaming >=0.11 - - python_abi 3.14.* *_cp314 - constrains: - - conda >=26.1.0 - license: MIT - license_family: MIT - run_exports: {} - size: 323375 - timestamp: 1784203061870 + size: 1474705 + timestamp: 1788559226199 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda sha256: f141bcbf8e490b49b2f53f517173d13a64d75e43cfae170e0d931cb0b66f4bce md5: c26934035616f7d578f9da0491aed3d8 @@ -13938,12 +13823,12 @@ packages: run_exports: {} size: 1644554 timestamp: 1763019901149 -- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda - sha256: 61185005c73fe2ebdf1656a7b730a47eaf1802409d5a76ac6a54d1f70a78ac1c - md5: b0dcd6182bfd56a219563a153d550d20 +- conda: https://conda.anaconda.org/conda-forge/win-64/curl-8.22.0-hdb0ef4a_0.conda + sha256: 51175dea0676945b30068972202eca282bbfe7774931d3e1369c91a60518d065 + md5: a21861ac889a38415844187508d6d56a depends: - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 hdb0ef4a_5 + - libcurl 8.22.0 hdb0ef4a_0 - libpsl >=0.23.1,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 @@ -13953,8 +13838,8 @@ packages: license: curl license_family: MIT run_exports: {} - size: 189161 - timestamp: 1787183770186 + size: 191978 + timestamp: 1788340795522 - conda: https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda sha256: 04dca71339134390f2d76467070f02356efb7bf21408cdd45a868a94009e93b9 md5: 8309b55a55dac8050260de935d31724a @@ -13970,9 +13855,9 @@ packages: run_exports: {} size: 582082 timestamp: 1771855937157 -- conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_0.conda - sha256: daa9397ffba6722f27c21b2f0a02b197f3ba9baedb484a155539743ec5ea3ac3 - md5: 945786ac874b8e821a675fbdd885f844 +- conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_1.conda + sha256: 7ded7fec9e8e95292ca6c55a4228323ce90f6c00c625cdb24c4be7dd3ca18d4f + md5: 036e75eb988e7a86bae1456cf4e81a64 depends: - python - vc >=14.3,<15 @@ -13980,10 +13865,9 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: MIT - license_family: MIT run_exports: {} - size: 4022782 - timestamp: 1780390190830 + size: 4028543 + timestamp: 1788859387573 - conda: https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda sha256: 09e30a170e0da3e9847d449b594b5e55e6ae2852edd3a3680e05753a5e015605 md5: 3d3caf4ccc6415023640af4b1b33060a @@ -14017,27 +13901,27 @@ packages: run_exports: {} size: 523153 timestamp: 1778864931353 -- conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda - sha256: 96a3ce5f2868a47e08d00a8fbcf68e1b8dbf9ca30a018120554ca26db3255c5e - md5: c1bae1515ff287d5bf6e287dddbc6646 +- conda: https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314hcb00e3b_7.conda + sha256: 0f30c87aeeef397b7818037cb4bb016560c9441e9a0ec69371bf5e9b2bd7cfe8 + md5: 47c71cc6a001bd356d8634af938c36f5 depends: + - python - attrs >=19.2.0 - click >=8.0,<9.dev0 - - click-plugins >=1.0 - cligj >=0.5 - - libgdal-core >=3.12.1,<3.13.0a0 + - click-plugins >=1.0 - pyparsing - - python >=3.14,<3.15.0a0 - - python_abi 3.14.* *_cp314 - shapely - - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libgdal-core >=3.13.3,<3.14.0a0 + - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 985443 - timestamp: 1767051440390 + size: 1222701 + timestamp: 1788472931375 - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda sha256: 1d6d6f1bf284e50cf617ce9a6250e0bf70bd0d30b068568364d6b1852aad9466 md5: ca8fa0e01ed34bb161f4e8e86dfda22e @@ -14098,14 +13982,15 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MPL-1.1 + license_family: MOZILLA run_exports: weak: - freexl >=2.0.0,<3.0a0 size: 79171 timestamp: 1788125519179 -- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda - sha256: 274b3e4ae5dff527062039d1dcb5cfdd9f91fa7d8eaf61358c09450b361385de - md5: 66f5ce9d0d618332023619a899ceb26f +- conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_2.conda + sha256: be42a535d36f1e075f2ae3264bb62028ab396408da6a6ab0ac98e61d0224eb7c + md5: 130c9b1af59ff14131c661cc14e4a632 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -14114,8 +13999,8 @@ packages: run_exports: weak: - fribidi >=1.0.16,<2.0a0 - size: 65318 - timestamp: 1785912637725 + size: 66044 + timestamp: 1788385642269 - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda sha256: 70c51f6e7d2e2d68dd8fafe3a2c821764b7e33f2e66379a6b5691e942c73b7d3 md5: f3405ff0366fe7ee04580acf8e422d16 @@ -14146,13 +14031,14 @@ packages: - getopt-win32 >=0.1,<0.1.1.0a0 size: 26238 timestamp: 1750744808182 -- conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.0-h11686cb_0.conda - sha256: 44320b78b03e503fb19e7897b94dda469702c16fd67ba8a90f6526211bc1b2d5 - md5: 4415fc5872344fa14b0e49e1830e1ca6 +- conda: https://conda.anaconda.org/conda-forge/win-64/go-shfmt-3.14.1-h11686cb_0.conda + sha256: 8f8036329369fdd4c354f60ae005d1441cd406bf993b29a7427e68b384b88051 + md5: 11363a4de458b3b1ce01493b69157de7 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 2062286 - timestamp: 1787948982916 + size: 2238347 + timestamp: 1788728451441 - conda: https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda sha256: 93a59bdf944fb6f947bd7ad2f293d5d80db3a90f8ff8dfabc591e3752141e46f md5: 79538e7a7bc024084eda5989f73fde35 @@ -14233,11 +14119,11 @@ packages: - icu >=78.3,<79.0a0 size: 16835644 timestamp: 1784916416303 -- conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_2.conda - sha256: 0e9f91b6e48a3d2a52b2fbbf21caafe6018ec71838f4f501d0b88e3e9f4e7cb6 - md5: 523af9223804c4eeebef3156d1bfd688 +- conda: https://conda.anaconda.org/conda-forge/win-64/immutables-0.21-py314h5a2d7ad_3.conda + sha256: af93e024ffdd95923cccbf3c2b79ecf63d3d23a0eddcfb28cc4a711c27772abf + md5: 0b76951d1ae4672f1ae3795bba5dda8a depends: - - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -14245,11 +14131,11 @@ packages: license: Apache-2.0 license_family: APACHE run_exports: {} - size: 56350 - timestamp: 1757685649329 -- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda - sha256: b34910eacb872a9b9b55318fd12332954e64b42dbc710f6a384e0500a58a2d7a - md5: 842f2582140b5567bcad1defbbd9b948 + size: 56920 + timestamp: 1788611840431 +- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_1.conda + sha256: d396583366135a781b150f31fcf1a2e6c1f0cb95545de5a5236240b36c62bb46 + md5: 112da4db82886f7c2b8496a4e44da64e depends: - python - vc >=14.3,<15 @@ -14257,9 +14143,10 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 73212 - timestamp: 1787938458524 + size: 73162 + timestamp: 1788505720829 - conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda sha256: 63ff03324e903eb01a715ccf357df56d66224e61952fd6615d86490ebefb3285 md5: 93f5a01dec294a2228f757fe2f3432d4 @@ -14474,14 +14361,15 @@ packages: - liblapack 3.11.0 10*_mkl - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause + license_family: BSD run_exports: weak: - libblas >=3.11.0,<4.0a0 size: 67014 timestamp: 1788077238349 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda - sha256: c739589318a1f8a88cd1b66d385176fd1ec2c4609e9f90d23d748be94b547e4e - md5: 8ef4beb3cb18e1a876b9af9a757cc1a5 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_4.conda + sha256: 88247c467f77d99abfe2596fa5c91c2ec0f2942d6fc292d5729ab3538d1b0a0f + md5: 35d7e4a581c8364e8c675f6b95d183d0 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -14491,13 +14379,13 @@ packages: run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 - size: 82655 - timestamp: 1786622832371 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda - sha256: f4bdb7ec97c3122e531fc867efae5ec928b649d047aa70d42893f0d8eb110fd9 - md5: 10c479888ee4e960587967b2d0c8143a + size: 82689 + timestamp: 1788480379020 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_4.conda + sha256: ddcf50274ccdd863c176190726c524903e0b875aa13e9d60c3755b2fa221fc62 + md5: 1b4637ca71b21c0d31ab28c3c0b34c8e depends: - - libbrotlicommon 1.2.0 hf02afa3_3 + - libbrotlicommon 1.2.0 hf02afa3_4 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -14506,13 +14394,13 @@ packages: run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 - size: 34788 - timestamp: 1786622843818 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda - sha256: c5e638ea9704c94b316238b425a3439ba38d4d8fba81682842e6d7d464472848 - md5: cb5d08dc81f52e87c27914b5636cd995 + size: 34700 + timestamp: 1788480390151 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_4.conda + sha256: d6744e57961728e12e4ac8e54bb0edd235a1c34d663a9df0dd153f9540a799bc + md5: 562b5e39b7797423e7fd04bae1c6ad70 depends: - - libbrotlicommon 1.2.0 hf02afa3_3 + - libbrotlicommon 1.2.0 hf02afa3_4 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -14521,8 +14409,8 @@ packages: run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 - size: 253894 - timestamp: 1786622854214 + size: 253586 + timestamp: 1788480399742 - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda build_number: 10 sha256: 683c47e93178836bbbf0213241f4beb5454b60f75622040cbc73d1ef61df311f @@ -14534,6 +14422,7 @@ packages: - liblapack 3.11.0 10*_mkl - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause + license_family: BSD run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -14552,6 +14441,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 - libzlib >=1.3.2,<2.0a0 license: Apache-2.0 WITH LLVM-exception + license_family: APACHE run_exports: weak: - libclang13 >=23.1.0 @@ -14570,9 +14460,9 @@ packages: - libcrc32c >=1.1.2,<1.2.0a0 size: 25694 timestamp: 1633684287072 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda - sha256: bf5445ab8acfaccaf511d774f131cf0d99c5eef6b8def270e832ad26970d5011 - md5: 50861643cbe90dc7267feb7854b8efdf +- conda: https://conda.anaconda.org/conda-forge/win-64/libcurl-8.22.0-hdb0ef4a_0.conda + sha256: 5fe063cffa90ad3ef04e25c76b1a79cdbc4f6c43747164a7dcdd89c4faebb84e + md5: e8405e754eaac42d66f957222fcfd876 depends: - krb5 >=1.22.2,<1.23.0a0 - libpsl >=0.23.1,<0.24.0a0 @@ -14585,9 +14475,9 @@ packages: license_family: MIT run_exports: weak: - - libcurl >=8.21.0,<9.0a0 - size: 405126 - timestamp: 1787183759927 + - libcurl >=8.22.0,<9.0a0 + size: 413226 + timestamp: 1788340784214 - conda: https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h1a1d4e4_1.conda sha256: af1cda21d4653f594fbef20aa4e1ff158a546902b3307ef8af9a9b44b43862d2 md5: e4e122e124676a49eebf241399ad8393 @@ -14602,21 +14492,21 @@ packages: - libdeflate >=1.25,<1.26.0a0 size: 157828 timestamp: 1785908793271 -- conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda - sha256: af03882afb7a7135288becf340c2f0cf8aa8221138a9a7b108aaeb308a486da1 - md5: 25efbd786caceef438be46da78a7b5ef +- conda: https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-hc903f1e_2.conda + sha256: 71fde040ce42b6e5b18aa8a8cfcae219cfaf49665bbbb940e6b98bc0c6b879c3 + md5: 2f8b5fd59f830697c007f6f9e989f063 depends: - - openssl >=3.1.1,<4.0a0 + - openssl >=3.5.8,<4.0a0 - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 - size: 410555 - timestamp: 1685726568668 + size: 413546 + timestamp: 1788870279140 - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda sha256: 1a54d874addda73b6f7164d5f3905821277a1831bcc05edd74b3085391688571 md5: ccc490c81ffe14181861beac0e8f3169 @@ -14710,68 +14600,68 @@ packages: - libgd >=2.3.3,<2.4.0a0 size: 166711 timestamp: 1766331770351 -- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda - sha256: 9ab0d75d60952f422e59ead3b24d59871c8718b9179b0a708b86057d37d6a7ef - md5: 6f86ca570061dd7f251ab5554eb610a5 +- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.13.3-hcd51406_1.conda + sha256: 08a7f54d669392f34fd95ef53da1fd7babd10c7735bf8762992f98c0c9cdd74f + md5: 0553b074c878e31b9090ec0542228ac1 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - libdeflate >=1.25,<1.26.0a0 - blosc >=1.21.6,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - libjxl >=0.12.0,<0.13.0a0 - libkml >=1.3.0,<1.4.0a0 + - muparser >=2.3.5,<2.4.0a0 + - xerces-c >=3.3.0,<3.4.0a0 - libwebp-base >=1.6.0,<2.0a0 - - libpng >=1.6.58,<1.7.0a0 - - zstd >=1.5.7,<1.6.0a0 - - lerc >=4.2.0,<5.0a0 - - libiconv >=1.18,<2.0a0 - - libdeflate >=1.25,<1.26.0a0 - - lz4-c >=1.10.0,<1.11.0a0 - - libcurl >=8.21.0,<9.0a0 - - libexpat >=2.8.1,<3.0a0 - liblzma >=5.8.3,<6.0a0 - - libsqlite >=3.53.4,<4.0a0 - pcre2 >=10.47,<10.48.0a0 + - openssl >=3.5.8,<4.0a0 - libxml2 - - libxml2-16 >=2.14.6 - - xerces-c >=3.3.0,<3.4.0a0 - - libspatialite >=5.1.0,<5.2.0a0 - - muparser >=2.3.5,<2.4.0a0 + - libxml2-16 >=2.15.3 - libjpeg-turbo >=3.2.0,<4.0a0 + - libarchive >=3.8.9,<3.9.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - lerc >=4.2.0,<5.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libexpat >=2.8.1,<3.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libjxl >=0.12.0,<0.13.0a0 - geos >=3.14.1,<3.14.2.0a0 + - libspatialite >=5.1.0,<5.2.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libiconv >=1.18,<2.0a0 + - libcurl >=8.21.0,<9.0a0 - proj >=9.8.1,<9.9.0a0 - - libarchive >=3.8.9,<3.9.0a0 + - libzlib >=1.3.2,<2.0a0 constrains: - - libgdal 3.12.4.* + - libgdal 3.13.3.* license: MIT license_family: MIT run_exports: weak: - - libgdal-core >=3.12.4,<3.13.0a0 - size: 10741883 - timestamp: 1786825244360 -- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda - sha256: 001620bac27dbf6daa25e5cd52970749fe2bc02a26649b0ad516d26609f6bcae - md5: e40bcfee79dbfda3eff751ed098b6ad3 + - libgdal-core >=3.13.3,<3.14.0a0 + size: 11341531 + timestamp: 1788235611204 +- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-h261a839_3.conda + sha256: adfe233cc1b366d51b0108e3a3b77a8e2f3c5c88d5846badd80bd6400aa2e552 + md5: 6326b3f7af94482676d8eef011b2f9d6 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - libintl >=0.22.5,<1.0a0 - pcre2 >=10.47,<10.48.0a0 - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - libffi >=3.7.0,<3.8.0a0 - - libintl >=0.22.5,<1.0a0 + - libzlib >=1.3.2,<2.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4519014 - timestamp: 1787884093942 + size: 4519383 + timestamp: 1788525218536 - conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda sha256: 9ef1a22fb50b31fbf9c01cf709e8f55b60fd3c02401d150be4a55943f9e3832f md5: 91189f0bc9ff21f385fcda6232346612 @@ -14852,9 +14742,9 @@ packages: - libgrpc >=1.82.1,<1.83.0a0 size: 11674380 timestamp: 1783586253244 -- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda - sha256: 08713208fd7e2e6f2ccde1fc09765f2585d5cc809d187865fd9190ddad271f3d - md5: 4618efa4303fdda115da50b8090d73c6 +- conda: https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_1.conda + sha256: 582f1fadfdc44e82ff937e1de5f6eff19f7114ce10a8051e306510220cb30275 + md5: d7b92075b7461e40fd8e90f08669a742 depends: - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.15,<2.0a0 @@ -14871,8 +14761,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 1053822 - timestamp: 1787795353946 + size: 1055447 + timestamp: 1788405777960 - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda sha256: 2ee12e37223dfcd0acd050c80a91150c482b6e2899198521e1800dce66662467 md5: 6a01c986e30292c715038d2788aa1385 @@ -14985,6 +14875,7 @@ packages: - libcblas 3.11.0 10*_mkl - liblapacke 3.11.0 10*_mkl license: BSD-3-Clause + license_family: BSD run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -15193,18 +15084,18 @@ packages: - libpsl >=0.23.1,<0.24.0a0 size: 73511 timestamp: 1786970749988 -- conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda - build_number: 104 - sha256: 4ffd32fd57d21d4aef34ce82dd7b5ab0248260d0481a26fff64a411f7ea9858b - md5: 83748d09d722103f7aa891ecfa13d672 +- conda: https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_106_cp314.conda + build_number: 106 + sha256: 95b7bd6fec10031b44b20276e57c11d71cd1095f966aa84c1bba5ece0eea64d2 + md5: 2d48db2a15a7b768bb563d2675c99b2c depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Python-2.0 run_exports: {} - size: 51176 - timestamp: 1788162060329 + size: 51250 + timestamp: 1788384361391 - conda: https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda sha256: 1bc52f781355e233411a7aaebbb155d9fd53efa2a1fc6c621250833d80cc5ab1 md5: df92be5685f712989830bdb7ee39383a @@ -15450,9 +15341,9 @@ packages: - libxcb >=1.17.0,<2.0a0 size: 1215534 timestamp: 1787886015478 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda - sha256: 8163de24a7ddb4ebf9587c3a45ba950caf3690b9646b76f007ca15e6e52b5881 - md5: 6e7dadff3f3bde2d29d29a3ec34f2d58 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda + sha256: 9fa71cec30425c9cc8eda6af3574af1faf4fefa5e239c9e037b1f778ef3ce8ac + md5: bab5489f4bd38494ffaa83d6ab924c1f depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 @@ -15462,20 +15353,20 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - libxml2 2.15.3 + - libxml2 2.15.4 license: MIT license_family: MIT run_exports: {} - size: 519962 - timestamp: 1787237653321 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda - sha256: a83e9adcf7c50f20289dc6890707c8e4e84151b4204b0f7344998138807458d1 - md5: 45a5a1c709a69f14cf176220788d18a9 + size: 515800 + timestamp: 1788635268845 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda + sha256: a19bb3821830f6b1281e782844d5dd265f796dfed2e4812b16c9995a8c778c88 + md5: 3ce54d0f8f24dfab99d82414411640aa depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 h3cfd58e_1 + - libxml2-16 2.15.4 h3cfd58e_0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -15485,18 +15376,18 @@ packages: run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 43610 - timestamp: 1787237661577 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda - sha256: fc3130e5ad77c01309293eac68684907003505adb365027f16136abd4ffe8e3a - md5: daa37dc0bb73bc77322b83f94b72caae + - libxml2-16 >=2.15.4 + size: 43845 + timestamp: 1788635276599 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda + sha256: 047e9b29a4489a5c7e13d319232d2f14392d789b3e6ea2cd54ace1fef77f6ecd + md5: d50e00d25a326eb487f9f5540e09b77b depends: - icu >=78.3,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libxml2 2.15.3 h8ef44ab_1 - - libxml2-16 2.15.3 h3cfd58e_1 + - libxml2 2.15.4 he095d88_0 + - libxml2-16 2.15.4 h3cfd58e_0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -15506,15 +15397,15 @@ packages: run_exports: weak: - libxml2 - - libxml2-16 >=2.15.3 - size: 124649 - timestamp: 1787237669397 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda - sha256: 13da38939c2c20e7112d683ab6c9f304bfaf06230a2c6a7cf00359da1a003ec7 - md5: 46034d9d983edc21e84c0b36f1b4ba61 + - libxml2-16 >=2.15.4 + size: 123569 + timestamp: 1788635283467 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda + sha256: 7a2402fa33b7e4a99bba6250ce21c36269a8acbfcb3652057dce0f4a60b7f6bd + md5: ea7b3abfe39c180d1dce620867e267d1 depends: - libxml2 - - libxml2-16 >=2.14.6 + - libxml2-16 >=2.15.3 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -15522,9 +15413,9 @@ packages: license_family: MIT run_exports: weak: - - libxslt >=1.1.43,<2.0a0 - size: 420223 - timestamp: 1757963935611 + - libxslt >=1.1.45,<2.0a0 + size: 421402 + timestamp: 1788534969422 - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda sha256: 0629c2cc0404d3bb29d6baa7b4ba62da80797015e86de050db81ea5a07050527 md5: 5d2ff29d465097458cc3ff6569151991 @@ -15707,9 +15598,9 @@ packages: run_exports: {} size: 114686732 timestamp: 1787725739779 -- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda - sha256: b4971dcf9b2a8f931512989e97374629a07a243e8e090a3ed365585961e3750f - md5: 921a2c141bc15cf74a238a8fb928554a +- conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda + sha256: 8abb424976b3b210d091ba7cf6c9caef600b86b93040cc946b306d4302c0c1a4 + md5: a60ac4e5c43cf1f77fe3b1d2fd4c214b depends: - python - vc >=14.3,<15 @@ -15717,9 +15608,10 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: Apache-2.0 + license_family: APACHE run_exports: {} - size: 90618 - timestamp: 1787850873390 + size: 90623 + timestamp: 1788525106432 - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda sha256: 65c4ae59ba5b4b557b4ce79632fb0d01cdcfe0894ed742d9f129442353de7b4a md5: 3084e08306d71a87b785efae1fa22564 @@ -15728,14 +15620,15 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT + license_family: MIT run_exports: weak: - muparser >=2.3.5,<2.4.0a0 size: 164026 timestamp: 1788122502315 -- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314h13f4da2_0.conda - sha256: a94fcfe3d4d01012deca8876beba5e435477a2154edba01c834d16043d7cfb6b - md5: e71d39ca28efc941844d6e6235c39e7a +- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-2.3.1-py314hc5dbbe4_1.conda + sha256: 46c1de630bdb7e9cf9fad9578a7c048910c1d6681bdc22b9daf52e4449907b5e + md5: 79c8fa375f844bf8209d2998e8fb1971 depends: - ast-serialize >=0.6.0,<1.0.0 - mypy_extensions >=1.0.0 @@ -15751,8 +15644,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 10916942 - timestamp: 1786887307539 + size: 10917024 + timestamp: 1788615834389 - conda: https://conda.anaconda.org/conda-forge/win-64/nlohmann_json-3.12.0-h5112557_2.conda sha256: 460d5a644bd7f195784f7e17d550274986ddffa1d534b18195322de1f81cf42d md5: faf4e3f7981777629dd7cbef578834ad @@ -15763,43 +15656,44 @@ packages: run_exports: {} size: 135270 timestamp: 1785920493388 -- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda - sha256: 34cdcef001ff3630bd6a54d3225959ec6298e549f4d82d2e3abe08af49411191 - md5: e4916c642c9aa7ab2d58ec75591189d3 +- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.3-py314h02f10f6_0.conda + sha256: 03e0def4425d045b9afb2e2c895bcd0233de6f1aeec53575ba00dd262c4f00e4 + md5: 806daade32d05870efd4d63931c12c9d depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - liblapack >=3.9.0,<4.0a0 - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause + license_family: BSD run_exports: weak: - numpy >=1.25,<3 - size: 7497786 - timestamp: 1788129258926 -- conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda - sha256: 24342dee891a49a9ba92e2018ec0bde56cc07fdaec95275f7a55b96f03ea4252 - md5: e723ab7cc2794c954e1b22fde51c16e4 + size: 7514382 + timestamp: 1788731986512 +- conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h90fa87c_2.conda + sha256: d1c630ccd9ae0898b48d84e986f4c330f66a25e535f99efe8cbf03f042b33da5 + md5: d9b2cb1079cf59651722c09aa3a9f840 depends: - - libpng >=1.6.55,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 + - libzlib >=1.3.2,<2.0a0 license: BSD-2-Clause license_family: BSD run_exports: weak: - openjpeg >=2.5.4,<3.0a0 - size: 245594 - timestamp: 1772624841727 + size: 274994 + timestamp: 1788425052805 - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda sha256: 9dddb559ba49744d5d94092d8d13cb0567f5c3b3f439f3acf28433d1f4256acc md5: 73cb1783f47c452be4c309430fbef89f @@ -15894,19 +15788,19 @@ packages: run_exports: {} size: 14103773 timestamp: 1785276287020 -- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_0.conda - sha256: 85fc9c2a3b805d7ad784b6b307f4cf4833d3525ab409a7f86262d476a67d441f - md5: 22d750d2ebf4109bc66c036f1e52b982 +- conda: https://conda.anaconda.org/conda-forge/win-64/pango-1.58.2-h13911b6_1.conda + sha256: 110d2731a105c0b9bd6396ad371401db90bd53179a283ff77f774872beb5bcd4 + md5: 7953b4cc0a7a47a0d91fa2e664269a0c depends: - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 - fonts-conda-ecosystem - fribidi >=1.0.16,<2.0a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 + - libharfbuzz >=14.4.0 - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 @@ -15916,8 +15810,8 @@ packages: run_exports: weak: - pango >=1.58.2,<2.0a0 - size: 466294 - timestamp: 1786107518608 + size: 465040 + timestamp: 1788490491378 - conda: https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda sha256: d8e9ea26b52a09d880d1e5371830c8dd5b4c099a6db64dbdf8236440744b7499 md5: 009af999dbe2d1db36a37187a4ca4eb4 @@ -15934,29 +15828,29 @@ packages: - pcre2 >=10.47,<10.48.0a0 size: 993241 timestamp: 1787294653206 -- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda - sha256: c9dc3212ed0021974541072ee3765a5097a0721191c34ee485d7d7e94449648f - md5: 09b8e6ac8f4a257b59e5d3025f3c9c1d +- conda: https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_2.conda + sha256: 70b239029cef17c880e70674c000a245f79363e50dac65765640d927c7f50bb6 + md5: 1fcc0165f5eb12f89438921b23f42be6 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - tk >=8.6.13,<8.7.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - openjpeg >=2.5.4,<3.0a0 - python_abi 3.14.* *_cp314 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - libwebp-base >=1.6.0,<2.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - openjpeg >=2.5.4,<3.0a0 - lcms2 >=2.19.1,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libtiff >=4.7.2,<4.8.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 - libxcb >=1.17.0,<2.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - tk >=8.6.13,<8.7.0a0 - - zlib-ng >=2.3.3,<2.4.0a0 license: HPND run_exports: {} - size: 989058 - timestamp: 1782912129930 + size: 989150 + timestamp: 1788610331353 - conda: https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.4-h5112557_3.conda sha256: cad8b94c2b00264a15d469eed6dc53aac1c59c6d46f27ad1d91bfaf227cf136a md5: 27c5be39d9e4d25fe85b89a069360d1e @@ -15987,6 +15881,7 @@ packages: constrains: - proj4 ==999999999999 license: MIT + license_family: MIT run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -16009,9 +15904,9 @@ packages: - prometheus-cpp >=1.3.0,<1.4.0a0 size: 183665 timestamp: 1730769570131 -- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda - sha256: c975e08da7627e95d1f0b70ef9787e9153118e67bb5b31f5530c5c3827d9afe8 - md5: deeef9494cfffa58f5fd20bc6eb57664 +- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_2.conda + sha256: 5bc133d5b69a7c2679a7c59caa35273460cd5b12b6c40a0ed07f760ae7df4d7d + md5: e5f28a48fe6fade84972e3aba057f400 depends: - python - vc >=14.3,<15 @@ -16021,20 +15916,20 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 249966 - timestamp: 1787417377436 -- conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda - sha256: 5546927a2e337d2903d6cdb028fb33723cdbcc45bf9abe1770fbde2c4ea8bce6 - md5: bc97d497656c9c661ffd3043aca90aa4 + size: 250098 + timestamp: 1788189997310 +- conda: https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda + sha256: 2418cf449df76b0da851cb59b7c826a9ade81914eaff799fbfd3f509ba1edf46 + md5: fda2f9fedee1b85ba855ea26a9b6bbf8 depends: - - libgcc >=14 + - libgcc >=15 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 license: MIT license_family: MIT run_exports: {} - size: 10120 - timestamp: 1786067833280 + size: 10612 + timestamp: 1788382011501 - conda: https://conda.anaconda.org/conda-forge/win-64/pulp-2.8.0-py314ha1463a0_3.conda sha256: b74d98c204593715d08863833a37317a108d2ef6f5464f93e5b6c7f013a85104 md5: 98d68f979d9c387b5147ce4a8e51fc10 @@ -16048,10 +15943,10 @@ packages: run_exports: {} size: 14442928 timestamp: 1757853584977 -- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_3.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/py-rattler-0.25.0-py310hb39080a_5.conda noarch: python - sha256: 2456978696deb80e8291ae38f0d373feb8407c2e70c6f13d6827444658ecbafa - md5: f425c172978e645bc8f823b03ccc533a + sha256: 4a2588998a517e5393d6d9b27c5f8b48ea62252d41cc0c4c2e51028a7a369dff + md5: e5e9c249bd333b8e8fe3786dfc84219b depends: - python - vc >=14.3,<15 @@ -16060,9 +15955,10 @@ packages: - _python_abi3_support 1.* - cpython >=3.10 license: BSD-3-Clause + license_family: BSD run_exports: {} - size: 13162295 - timestamp: 1787913208737 + size: 13160262 + timestamp: 1788524937530 - conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-25.0.0-py314h86ab7b2_0.conda sha256: 050a5a54d49f5877f15566cd4df55233b2c78bb240f97a53c3a5828f68f5c5c9 md5: 7c6d60d53f7b0b09e424b58cd9fdfd37 @@ -16100,9 +15996,9 @@ packages: run_exports: {} size: 3689897 timestamp: 1784258892556 -- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_4.conda - sha256: 38d9062eca5d85ac5de080b3512edc4e0f89c64199afcd2da34cd432b5d2514d - md5: c79e745df2f185732d9cdb5763452440 +- conda: https://conda.anaconda.org/conda-forge/win-64/pycosat-0.6.6-py314h5a2d7ad_5.conda + sha256: 7abed71cd8985837bd0e4a950fd70cab907147cd713578f9c6c8398a72890160 + md5: 4e13abb743c8986b1dd40cda02bf4111 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -16112,8 +16008,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 80121 - timestamp: 1784146283080 + size: 79817 + timestamp: 1788489411537 - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.5-py314h9f07db2_1.conda sha256: 5f9b6cebac7129d445193e609e99bd6e66193098bc1fd1d2f784576a67ae5720 md5: a2e1f7dbbdec163ff40ee75a65bc64dc @@ -16125,14 +16021,15 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: MIT + license_family: MIT run_exports: {} size: 1861822 timestamp: 1787928979492 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda - sha256: 3ed86ebf3d68f9ae52b5a74f9db919d367af33acf26d481fcdb00fff7079e350 - md5: da6ef5e9a5931e73a7638b60fd82fc63 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.13.0-py314he172734_0.conda + sha256: 25a520b775aabf3d85014b99736a968727cf921ee9d52fb03051aadd812b0fb6 + md5: d0026e98c1f21ed4078a3ee378dd0713 depends: - - libgdal-core >=3.12.0,<3.13.0a0 + - libgdal-core >=3.13.1,<3.14.0a0 - numpy - packaging - python >=3.14,<3.15.0a0 @@ -16143,11 +16040,11 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 899343 - timestamp: 1764402737921 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda - sha256: 84d335aabc168c716b7989e8b97c07697e5e21083a7e8bfe9be168cf4300c7f7 - md5: b7503bde09c3bce5c09d81485855f5a8 + size: 937065 + timestamp: 1782492935799 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.8.0-py314h9d82244_0.conda + sha256: 3a1a8a11526f864351b3263cba0970c87a1cdfaadcd7fd43c874c3b9afc5b958 + md5: 8a31da8c3cd2b25863db1e4e00d595c0 depends: - python - proj @@ -16160,51 +16057,51 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 893897 - timestamp: 1786351867522 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_0.conda - sha256: 692abac5636f9a1fb10639ccef0f66cbfb38c20d88cdf33132fd8458c82793f3 - md5: 518a7112a2457d2e3ab13b749306490d + size: 925319 + timestamp: 1788664408715 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyreadline3-3.5.6-py314h86ab7b2_1.conda + sha256: feefa7a7bcf6f1568bda52f43051c5cabdd3d773d8beee6de4edd79402d20444 + md5: 5a07b3257a58644ad86fb44a876016ba depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD run_exports: {} - size: 175241 - timestamp: 1778848456926 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda - sha256: bf1553eebc31e8ba04689639e8ce235c3df5317414f0c4a11417f238297f1b17 - md5: 7c3ccff96dbc9f8e3c40142c7594ec34 + size: 175975 + timestamp: 1788638509744 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h0c0e1ae_1.conda + sha256: 30b0b40b7c550cfedebf2901e06d49e7e514be69df3d674c6dc042d03543a147 + md5: 2a0080a3a7c28a372db3cb629ae1b19f depends: - python - qt6-main 6.11.2.* - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - libxslt >=1.1.45,<2.0a0 + - libclang13 >=23.1.0 - libxml2 - - libxml2-16 >=2.14.6 - - libclang13 >=22.1.8 + - libxml2-16 >=2.15.4 - qt6-main >=6.11.2,<7.0a0 - python_abi 3.14.* *_cp314 - libvulkan-loader >=1.4.357.0,<2.0a0 - - libxslt >=1.1.43,<2.0a0 license: LGPL-3.0-only license_family: LGPL run_exports: {} - size: 11552286 - timestamp: 1787219450026 -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda - build_number: 104 - sha256: 70c04a977ba943691cc9ff9c7cbe4326b3e9bd27a5a040184ca892192d6e3143 - md5: 00cc3f77b4714fd8f297d8f51762bbaf + size: 11552459 + timestamp: 1788744991512 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_106_cp314.conda + build_number: 106 + sha256: 9fdcb8a5018d91a22484e67ac6c5ae9116f082f953328f43ed4421418740dfb1 + md5: df0b471269a379db0262256e332c1e7c depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.1,<3.0a0 - libffi >=3.7.0,<3.8.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - - libpython 3.14.7 h4f90d01_104_cp314 + - libpython 3.14.7 h4f90d01_106_cp314 - libsqlite >=3.53.4,<4.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.8,<4.0a0 @@ -16221,12 +16118,12 @@ packages: - python_abi 3.14.* *_cp314 noarch: - python - size: 18570404 - timestamp: 1788162197665 + size: 18377601 + timestamp: 1788384478011 python_site_packages_path: Lib/site-packages -- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_0.conda - sha256: f69e11b4025a7467a1820ce348e39af70d4eb0508e89375d3c2e501c4b8e532f - md5: e43c348d1855554ddd3ccd385940386f +- conda: https://conda.anaconda.org/conda-forge/win-64/python-librt-0.15.0-py314hc5dbbe4_2.conda + sha256: 04bfb8acc73092fc62a2c026c460516a8ebccad92172fb330ac57ebeb585f099 + md5: 27dc063f7ddcb1bdbb2b9df2c4e776c0 depends: - python - vc >=14.3,<15 @@ -16236,8 +16133,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 108739 - timestamp: 1786328843266 + size: 108878 + timestamp: 1788524683628 - conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py314hc5dbbe4_2.conda sha256: 2c97a59ce98b9d29a30bc49f3001b9206ce789edaee7d2773cde8a8a7f78abc4 md5: 02632a618ced2283cf9a7c4a70a43026 @@ -16347,9 +16244,9 @@ packages: - qt6-main >=6.11.2,<7.0a0 size: 89682645 timestamp: 1787049046883 -- conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda - sha256: e7236607a5f8abda3c1e22afcdb8ece4a466c4120a83a6162fb623bab5f7ce2d - md5: 195d18c86203797871e15b2907579a84 +- conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.1-py314h0f7306d_1.conda + sha256: 932c536ba61e34fbf73845118a25d2ff6d3b533e0693ec5755e5fd67a33e8210 + md5: 6b5756070c6380b7a27401100cc8fff5 depends: - affine - attrs @@ -16357,7 +16254,7 @@ packages: - click >=4,!=8.2.* - click-plugins - cligj >=0.5 - - libgdal-core >=3.12.4,<3.13.0a0 + - libgdal-core >=3.13.2,<3.14.0a0 - numpy >=1.25,<3 - proj >=9.8.1,<9.9.0a0 - python >=3.14,<3.15.0a0 @@ -16370,8 +16267,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 8483184 - timestamp: 1785964794642 + size: 8892506 + timestamp: 1786313866817 - conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-haef9524_2.conda sha256: eb11c0e948c2576378a8e7f22768a967044d54bfbdea9ef358d31074b91c21f0 md5: 5d51319ef836277672d665c2fc04bab0 @@ -16393,6 +16290,7 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT + license_family: MIT run_exports: weak: - reproc >=14.2.8.post0,<14.3.0a0 @@ -16408,14 +16306,15 @@ packages: - ucrt >=10.0.20348.0 - reproc >=14.2.8.post0,<14.3.0a0 license: MIT + license_family: MIT run_exports: weak: - reproc-cpp >=14.2.8.post0,<14.3.0a0 size: 31779 timestamp: 1788160440623 -- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_0.conda - sha256: d297e7ef5f062194cf76dcf7a2e0449af24012b895a06aa942805f2933a0256c - md5: 07602f253d1ad9436bcac564c9fe183f +- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_2.conda + sha256: 48083dbd1007c26326a39b2ec89e1eba83e425c2ad2775779c51beb5cba0c46e + md5: c1662c5b6960642f24077fc9328cb0be depends: - python - vc >=14.3,<15 @@ -16425,8 +16324,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 218916 - timestamp: 1787344535083 + size: 218961 + timestamp: 1788577430937 - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda sha256: 62b73874b6c264a73e7904b33322128c4881c78dfe2fb9131becfbf84343ee2d md5: 027ff6055e5c82ba4e085857f09302cb @@ -16442,9 +16341,9 @@ packages: run_exports: {} size: 306951 timestamp: 1766175833786 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_2.conda - sha256: 530a5bdd43b9c5d4e6e84f831e5eb83c466e66ca23b119ca1236e57a25be3c96 - md5: a4bb148d2119ce0e5e85ca2cffddf5f5 +- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_4.conda + sha256: bdba6de07b5a0292da6e110101ca75f9838ecba32208c2bbd7ca3f3d9d51482c + md5: 37d18b45a1a81c9ddd8151d5956bc3bc depends: - python - vc >=14.3,<15 @@ -16454,21 +16353,22 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 115285 - timestamp: 1787264323029 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.5-h6bcfbb0_0.conda + size: 118726 + timestamp: 1788484961694 +- conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.6-hcc30601_0.conda noarch: python - sha256: 6bf30b3500b1747b7eb2f47cb1662bba32e194539137ddf1c0b6d6e4cbe9a8ba - md5: 85f44eb5b68a9181cbda89f24f0b68a9 + sha256: 62c3e491290e859197dd41e5513f3b8f4cb9b15ad349d68bf0721438bdb036dd + md5: b2888407d772e22199e6583dd2330cc9 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT + license_family: MIT run_exports: {} - size: 9299776 - timestamp: 1787865279792 + size: 9305951 + timestamp: 1788474543887 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda sha256: 3e30cc784bd5af6aa035807e5c2f12a1ecbc298d755561f6ce968b3b598b940a md5: 74bafde39f688cb95c111e74bfad6669 @@ -16525,9 +16425,9 @@ packages: run_exports: {} size: 614463 timestamp: 1762523809485 -- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.9-h477610d_0.conda - sha256: d728d2d597d250cd1cc17f92acef80bdfa8b51df1a7ee68bc891bdf7801ee7cd - md5: 03b9f62b6ffc6f22facd8f240e42b331 +- conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.11-h477610d_0.conda + sha256: b964734499b40786abfa9fbce00bd58e9977350c5527a9646b20f9841d2920c4 + md5: 075e31927fe43fbcee169227bf23aa33 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -16536,12 +16436,12 @@ packages: license_family: APACHE run_exports: weak: - - simdjson >=4.6.9,<4.7.0a0 - size: 419059 - timestamp: 1787803674143 -- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda - sha256: 6e5e7c358d34e6d0fd3078c822a38dc6d1566616704e07b6d2de0d3435590954 - md5: 2f613befc24a58c3935b1be38db94355 + - simdjson >=4.6.11,<4.7.0a0 + size: 420423 + timestamp: 1788630508984 +- conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda + sha256: 1f4117d723ab25bf231162701c763e7c502caea0a65163ede60120543712b14c + md5: 579ef7dd293c37b0e808f8b3b01559a8 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -16551,8 +16451,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 169318 - timestamp: 1787824226409 + size: 169753 + timestamp: 1788525286329 - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda sha256: d2deda1350abf8c05978b73cf7fe9147dd5c7f2f9b312692d1b98e52efad53c3 md5: 3075846de68f942150069d4289aaad63 @@ -16615,9 +16515,9 @@ packages: - libsqlite >=3.53.4,<4.0a0 size: 426888 timestamp: 1787051146089 -- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - sha256: 8a4053839b8e997a5965e2dff7d6cf3c77be62d82c0e48c8a04a5ed2d2e73035 - md5: 8ee01a693aecff5432069eaaf1183c45 +- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda + sha256: 03ba7196a7ffe842d26aa7020d8ffddb2c1589d27779cf98a8d1a278a08b41c7 + md5: 632effc5cd23068512f7661821764807 depends: - libhwloc >=2.13.0,<2.13.1.0a0 - ucrt >=10.0.20348.0 @@ -16626,8 +16526,8 @@ packages: license: Apache-2.0 license_family: APACHE run_exports: {} - size: 156515 - timestamp: 1778673901757 + size: 156747 + timestamp: 1788769928020 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda sha256: f19618a3a82cc483dacf1c70a30367ee7b0c7e71b90f1f80e14feff44fbd3688 md5: dd9eb33c99d352b6356f86964d6040f7 @@ -16641,9 +16541,9 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3782376 timestamp: 1787272860855 -- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda - sha256: 1cac25c190b60a4b6fbe2853c4989a4ce086564ee5f995c17d4b32885993068d - md5: 94dfb208db7d79437cfe9b4390706b5c +- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda + sha256: 9a86978864753ce43aede95a3856039f60ad18330a638e7f1cd320e9248033ce + md5: a92f6eff943133d045db4d017ac4297c depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -16653,8 +16553,8 @@ packages: license: Apache-2.0 license_family: Apache run_exports: {} - size: 925990 - timestamp: 1786226866739 + size: 923130 + timestamp: 1788524958062 - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda sha256: 3005729dce6f3d3f5ec91dfc49fc75a0095f9cd23bab49efb899657297ac91a5 md5: 71b24316859acd00bdb8b38f5e2ce328 @@ -16665,9 +16565,9 @@ packages: run_exports: {} size: 694692 timestamp: 1756385147981 -- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda - sha256: 9041e463044944460f73f9528f2ec491180f0ffe857e3555aa8160b81050b8d9 - md5: d6b580a13384df5155c6ca19ee66854e +- conda: https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_1.conda + sha256: e99a07861c766307fe1290df0584119e13c91e3782dc00320f19478da2b2ed6d + md5: 1a3cf506689df0c4d3a85aa2aa5ead67 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 @@ -16677,8 +16577,8 @@ packages: license: Apache-2.0 license_family: Apache run_exports: {} - size: 406126 - timestamp: 1770909191618 + size: 409862 + timestamp: 1788554214219 - conda: https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda sha256: ed0eed8ed0343d29cdbfaeb1bfd141f090af696547d69f91c18f46350299f00d md5: 28b4cf9065681f43cc567410edf8243d @@ -16968,22 +16868,6 @@ packages: - zlib-ng >=2.3.3,<2.4.0a0 size: 124542 timestamp: 1770167984883 -- conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_3.conda - sha256: ba59719687df01c0e846b0308e59d9bbd8215a55532d228d35c14d0ced6bcba4 - md5: 099ca4a359dd7b5cf5140758a433cd58 - depends: - - python - - cffi >=1.11 - - zstd >=1.5.7,<1.5.8.0a0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.14.* *_cp314 - license: BSD-3-Clause - run_exports: {} - size: 380551 - timestamp: 1787896524598 - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda sha256: ca7daae4f218a11fab82cc2857f0ea518ec3f46acec60490485347a4c22c6b3e md5: e4ac308c39d6d0e131154976da67cf3b @@ -16999,3 +16883,15 @@ packages: - zstd >=1.5.7,<1.6.0a0 size: 387535 timestamp: 1786599623274 +- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + sha256: 8ffe74fb71692b082b46c94b3a7af2bc2f831a686a4d7bd1066409dda176d65c + md5: 86235f3d91aa02c8d629f225cbe93cf3 + depends: + - python >=3.12 + - pandas >=3.0.3 + - pandera >=0.31.1 + - scipy >=1.16.1 + - python + license: MIT + size: 71113 + timestamp: 1788872612273 diff --git a/pixi.toml b/pixi.toml index da32d06..27add8f 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,6 +5,7 @@ description = "This module prepares electricity demand timeseries for Europe at license = "Apache-2.0" readme = "README.md" channels = [ + "https://conda.anaconda.org/ddahawkins/label/dev", "conda-forge", "bioconda", ] @@ -42,7 +43,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = ">=0.1.1" +tclean = ">=0.2.0" [feature.test-module.dependencies] pytest = ">=8.3.5" diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 3a0b090..961d86b 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -1,6 +1,7 @@ # Generated by `pixi workspace export` # platform: linux-64 @EXPLICIT +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda#4c32076993e6270825441d059ab5c18b https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda#0de0122d9570a8ab637c6b73db268389 https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda#89d2c1231f47bd818f5d624b9411459d @@ -12,10 +13,11 @@ https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda#69 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda#0f51e2391ade309db462a55611263e9c https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda#16e3034a330cc625f2c685edec60cf4e https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda#f7a7ff5a6ab331e037abd34f379a631d -https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda#01bb81d12c957de066ea7362007df642 +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda#74a0a409d9f4561265d36b789d3f398a https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda#2f2ef0d96de5bdd8c1270ff22fdf9352 https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda#72a381cbad04f24b1c2a43ef707f45b4 https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda#e72bbec309c2b0f37823ee7d4fabfcd3 +https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda#d247b7632f09324c11f24b5270361385 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda#1390b7c5ac0b1d8e447bc5efa6d3c8c2 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda#6525a0b06aa4fd390795f0740636a9dd @@ -23,14 +25,21 @@ https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda# https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda#aa459086047c0e5e27023ab19f8cb86a https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda#449500f2c089da11c40f5c21312e3e07 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda#e675fabcf81499adc7edf58124fb1e01 -https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h8ab3286_0_cpython.conda#e9dcdd23a1c68738eb3257d23d5f7285 +https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda#98be3cf76eca2e8871f907a03aed3b84 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda#c22348a769bb072b6184eb6f7f05e4f2 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda#5e92b8413fd1c8f8f3006f4661b12def +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda#25994250f54292352a82eb05ab4499ba +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda#e2ca3eadd889d39b4e4b6b1ecc671816 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda#7b918d4958f359c889c662aa361cf992 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda#a275bd95aa4e22c24120bf6ece6eb056 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda#5abf073f2cf066f2f3d28229b3cbe94d +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda#f8d242c552b0f7f682451ce95879af5e https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda#a61716f1640d9986d5d6de1c4d140b42 https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.4-pyhcf101f3_0.conda#880e91eac5d926568ef278e20554148e https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda#c680b5747e8c4c8f23dca0bb7042a8fc -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1 https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_1.conda#2854d36e093d53d0cf81ec433ffd9c4c https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda#108c928d2a8551832dbc762b535e90bb https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda#2d0f9304fc1cb1e3e94e7b37c14cd70a @@ -38,22 +47,15 @@ https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda# https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda#c22348a769bb072b6184eb6f7f05e4f2 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda#5e92b8413fd1c8f8f3006f4661b12def -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_hcf972fe_1.conda#25994250f54292352a82eb05ab4499ba -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h4a7cf45_openblas.conda#e2ca3eadd889d39b4e4b6b1ecc671816 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda#7b918d4958f359c889c662aa361cf992 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_h0358290_openblas.conda#a275bd95aa4e22c24120bf6ece6eb056 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.2-py312he827f4e_1.conda#e70abe6ab4c60ecb6a51e67903bcec07 https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda#85eb29ade84d1bd97be5ec55607efb00 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda#5423c2b8f82d60320e65b9ff30babbdf +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda#e15caa7ad9be633c3cba5a7937b117b7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda#2e66c929f3d879708335b6ea4557c838 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda#f06ef439c280a5f90b8bf62355008dbc -https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda#df2c27f36bdb0dde779f55b5df76a352 +https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda#bb66b610707b811e3c65181a6431e7a8 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda#61f0ffba9161cbb3a77722869b21e4bb https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda#8c282bbe4808a3cc80a5c98e9aec1cfc https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda#e5b6b28536b81b3f4cb20db4668a4642 @@ -78,8 +80,8 @@ https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0 https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda#88a47e22b53e50865b1cabe2eb648352 https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda#06f6113cf1ff4a54b65f87ead132a5f1 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda#f92233bf33e24a25668bb2119e2c51f9 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda#20e4d67ec908f0405124f11612c48305 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda#2d34cdb31014cbc8f1e9384c89ede0a5 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda#b7545c57a73a51d72aa6eee5695cf051 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda#ea192d1ef556bf1d55de4acb8c0f5b65 https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda#b233b41be0bf210989d57160ed39b394 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda#f888787e0eab7a8076a67d197e155ffc https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda#9332b53d0ea93c5d39e33be03a0c611a @@ -94,9 +96,9 @@ https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda#533 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda#af491aae930edc096b58466c51c4126c https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda#680608784722880fbfe1745067570b00 -https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda#72e019c04ed02a179da38c56965802d1 +https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_1.conda#06b25748479ce9516502e28332707f11 https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda#fcf71c8d979148873f6f8ad4cfc73d86 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda#533d342dedadf134c67760ce6fc5b748 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda#d216efacca3f34f2b13ddd1632f53833 https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda#b72a266a9317036fd0464cb98b027cd0 https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda#f8054e759d0ddddaf34a5c8fedc900a1 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda#f9fe2984587fa8235a6af6004760cd18 @@ -108,9 +110,9 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda#922776b528a470ab5afa81fd42abfa1d -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_2.conda#e83331a940393006789fedddc3b492f3 -https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_0.conda#4463210c2a16ff5d3bf7f502af23f1f4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-h280c20c_1.conda#3b51576511038b50fdbd05245e22e4b1 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda#7b870cffbaa8430290e567a0facd8dca +https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda#c44470b6bfa6a582cb4dd42cbda3401e +https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda#7386d38083163c5b5c4a92a1582daaef https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda#cfcf11edcfd4acf0094771a9c3b8587f https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda#614e10aae180f87b84f0d1ca8ceb7d9e https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda#fb00b4fb800f2d431303a5c1625ef9d0 @@ -121,34 +123,34 @@ https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.co https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda#35fa2b34bbced424e6976d30f5fde576 https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda#64cc91512b6278c315349dc13c53f680 https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda#49c553b47ff679a6a1e9fc80b9c5a2d4 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_3.conda#7a2499a177753582fb7ae7e9dc4a908a -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_3.conda#2ac965638d4c6b2b38383bb1aebaf543 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_3.conda#6ab3315dc56618d652c1da42a648a129 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda#df210655e30a05e3b069c91b7aaddd2d +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda#4136f6e4ef4835cc7df39a707cbd511c +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda#0cfd601eb03cca403dcc248844787486 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda#dbe3ec0f120af456b3477743ffd99b74 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda#ce96f2f470d39bd96ce03945af92e280 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda#a6eb37b51be1a9a654dc3a8aca039b1a https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda#7094e0d8d14de0eff6d83f0d2f1f661e https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda#2ff98660281d1c6bfebda5bffa52cc89 -https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda#87e6096ec6d542d1c1f8b33245fe8300 +https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_1.conda#5c8ce6af1772fcfc2b86d17b825abc8e https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda#d207a2e9bae9da476bc0a603215d5167 https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda#3b8c8325547a4fd8c51649ef7dfab688 https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda#d3ba2947f7d7cdd7c4eb73b206de330b https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda#6afb92887568acc8f18281c57c92c28b -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda#bfcdbf7dfa025bf855ffbf44181c143e +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf520645_1.conda#c4121a6f49ae4e9e9beae7f70530b3be https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda#de0dceacf3e33c5fc88e167885dc8274 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda#1726acec19beeed1c764385cd8622405 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda#a3f3ac7a68d01c198e276dbb99e62032 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda#b68c7304d2edb0f1a5bdfb875094d603 -https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h50c33e8_0.conda#d749d04e1965315078f29320565c595a -https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda#1cd10eda5692519d01bb20e086e214c9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_2.conda#5752c2b6fa529061baba2819e4c2b143 +https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda#16d6e5a0f8dbc17bd6669dba6f49bad3 https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda#3ac89a48d224409739dbf6200e524373 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_0.conda#6ec45525bac89c279b6e5f4c47eec6bf +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_1.conda#231fede9051444ed7d243f78f3cb4a8f https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda#2d0ea23b23603e07ca47f23f949f67b6 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda#0b6c506ec1f272b685240e70a29261b8 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h5cc1888_1.conda#a6301c668e50512c804c5b3807c960f2 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_3.conda#8929291d9efc59715df1cfa7daf5e3ed -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_3.conda#6ff307b78fbfb7dcafb7e25ff8bc9c10 +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_4.conda#c48c09444f3736800ea0b9550c365baf +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda#746aa619a1bcaf4da541101179d7c60e https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda#294fb524171e2a2748cb7fe708aba826 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda#43c2bc96af3ae5ed9e8a10ded942aa50 @@ -162,13 +164,13 @@ https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda#7 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda#7bc31538d6e3fb349e897353969237ec https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda#3ad2b403be8fc5612b9159e2ce621f69 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda#75d211f04ac87506f1f43420712a69fe -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-ha042cf0_5.conda#0ed167049513943d078a6c997df2b4e0 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda#e617deee7af8eb0c04f6296d12b54157 https://conda.anaconda.org/conda-forge/linux-64/proj-9.8.1-he0df7b0_0.conda#b23619e5e9009eaa070ead0342034027 https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-h78fed68_2.conda#f2ff0de6c209e90c53b3a195c2708f5f https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda#7de84fd76ec2bdb7ac617507de3d3052 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda#e38a3253d72ab07d471483f24947e2a2 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda#6acb86426229f96f93e5468d1df3a5e8 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_1.conda#f986d9c9de80813d076d235f753889ff +https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.4-h7df9aa5_0.conda#9a414200ffe436e47d72c9f4c11c3658 https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda#5f856b72ca9cd5b5b4a1c43551804622 https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda#df81fd57eacf341588d728c97920e86d https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda#ade84857af069283ff1e3b7aacae1232 @@ -184,26 +186,26 @@ https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda#6d9a90cfee2c7343d6c05a1b37ef75fb https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda#98b6c9dc80eb87b2519b97bcf7e578dd https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d -https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.12.4-h5fdb907_5.conda#13e691ba803adfed23773eb0a46e78fc -https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda#2c4bd6aeb90bb157456841c3270a0d92 +https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.13.3-h31aba09_1.conda#44c6780e3082468d8676aebdcd5068a6 +https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda#dea5b13a211bbf99876deb980b408a66 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 -https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.0-py312heb112dd_1.conda#38f624a32029e9fced9ed40fa8c644db -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312hb99b6cc_5.conda#42e2d36478a1fcc0dbc6358ffdb46786 +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.5.1-py312h5d657d5_1.conda#c0d4e6e0b2f6a3e73ab81630deab839d +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.8.0-py312hb99b6cc_0.conda#ec4b76cc3656be76703e2d1b9ebab9ea https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e -https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_0.conda#75095e959e733b32c2ae343b1bcd0073 +https://conda.anaconda.org/conda-forge/linux-64/simplejson-4.1.2-py312h5cc1888_1.conda#d9d46cc36f3998befe87df9af48c919b https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda#69e400d3deca12ee7afd4b73a5596905 -https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.12.1-py312h053e1f3_0.conda#f8e7e5ddfbdca16b65335b0b6615eb4c -https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312h053e1f3_6.conda#a68cae58a81a937a6edcb3e4e6f0bbe7 +https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.13.0-py312hdb6ebaa_0.conda#0b2a0e17bd514b53e2d12b8ac16225ae +https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312ha17e7ad_7.conda#27561c3dba1d77f3d5852318965df012 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda#f8d242c552b0f7f682451ce95879af5e +https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c +https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda#e6e9b5795bb495325c3b4ebd451519aa https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b @@ -212,8 +214,8 @@ https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#4 https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_3.conda#d176f3ed2824f930b524c45eb8f158bb -https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_0.conda#e7eb25765bdf21397cc6e30828871625 +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda#edb667e7ce56106424e8afab2dc0f0cb +https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda#b0e9b44b494bb001c4d35b206022eba2 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 @@ -261,7 +263,7 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9. https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda#c344b5608c65a09b341f2e784efb4422 https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda#05f6b378ae80d2d06e35dc794412da3a https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda#aff56abfcef5a031603250b7dc23e9d5 -https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d +https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda#2e699c6e151045b2cfea34b53ed69439 https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda#b6e326fbe1e3948da50ec29cee0380db https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda#d736a4f89a3b63f9970152923e4c3016 https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda#64fad2fca810b34f5bbc9738a6fb298f @@ -275,14 +277,13 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88 https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda#e741576fb8f89821ac7c1c537322a33d https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda#15878599a87992e44c059731771591cb -https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_1.conda#1653ce584b7859810f116e592eba9c0c -https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_0.conda#881c695332c119ffd74b0a856f2c8fba +https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda#e59300b9232e82a351a9390bfdfe72f9 +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda#e9600e8080bf57f25ad2e9f78564c895 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda#29fd0bdf551881ab3d2801f7deaba528 https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 @@ -294,6 +295,6 @@ https://conda.anaconda.org/conda-forge/linux-64/fastparquet-2026.5.0-py312h4f234 https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-ha042cf0_5.conda#82c2a5fc14064073501f5e1b817f4d80 +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda#5332c97b820ebc5a00e78b3e6ec7ce12 +https://conda.anaconda.org/conda-forge/linux-64/curl-8.22.0-ha042cf0_0.conda#399a7c0b7ca511de34c5ee5762cee149 https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index 7b923e9..da3c108 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -1,25 +1,36 @@ # Generated by `pixi workspace export` # platform: osx-arm64 @EXPLICIT +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda#350d89b50d7de805abf2e63e29dee451 https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda#f39288f0ea63ae962e1a2e4f355a0d75 https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda#4ec2684c73812cc2c3d78379384a39cc https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda#90a01bf394d1c594e0eb9258f967d828 https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda#3dfa0d0316dc246cd44937a557de4501 https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda#9347fd56a002e6b58946831d48fe62f6 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda#0f51e2391ade309db462a55611263e9c https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.4-h55eecbc_0.conda#ae71ab40048c19a389a7dcccb86c2481 https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda#a5efc0b42bb8b42e97d0a29ae3e3c187 https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda#fde96d40ebe9a9cb34e4122380189ddb -https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.0-h55c6f16_0.conda#5303ba06fab927399ed8dfb3227b0af8 -https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_104_cp314.conda#9ef5086783f252da744fc0f7a336e4d8 +https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.1-h55c6f16_0.conda#b61106a0b5ac7cfe874f8b2e4f067dfa +https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.7-h4311e70_106_cp314.conda#4d7a303343eff82e8b92d52c5fd3991a https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda#ff33a4dbd93abc8a798cc4e0e7c8136d https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda#8ab10323068b107661a4b9a4af84f3b5 https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda#216bbcc23c11e9695b3db4a8771d76eb https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda#a915151d5d3c5bf039f5ccc8402a436f https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda#b50612e7d190b8061ab4e7dc119cf4d5 -https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_104_cp314.conda#86c5773c0f675287d7852068554806c9 +https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda#5fd37c78250b4f57c2bcf9ad2e54beb1 +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda#0affff5130a421d11d4d77ffbb00dad7 +https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda#a2d706b4a7d603524133037c34f7fb76 +https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda#408af8d9d5226ff45ff04756ff1aa91e +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda#d54390644d893d50e739b19145aae45f +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda#aa6c627acd0f5709d9c79bf708a7b81b +https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda#0b5dfcfae89beafb81234b90d5d07729 +https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda#37ec75f777eac2ee1322e19d64df82e7 +https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda#35ee607ea2e9f75c699158c5f77ef9fd +https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda#953e30e380c03f058cacffe19bce9dc9 +https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py314he06036b_0.conda#37bb35c87a060e9ab1551c5bd987074f +https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda#e55fe08bb5d43e7120672338dd129030 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf @@ -33,22 +44,12 @@ https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda# https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda#c78daae943cb94e6e6b2eab16e16a18f https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda#0affff5130a421d11d4d77ffbb00dad7 -https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda#a2d706b4a7d603524133037c34f7fb76 -https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda#408af8d9d5226ff45ff04756ff1aa91e -https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda#d54390644d893d50e739b19145aae45f -https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda#aa6c627acd0f5709d9c79bf708a7b81b -https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h4f80526_1.conda#0b5dfcfae89beafb81234b90d5d07729 -https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-10_h51639a9_openblas.conda#37ec75f777eac2ee1322e19d64df82e7 -https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-10_hd9741b5_openblas.conda#35ee607ea2e9f75c699158c5f77ef9fd -https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-10_hb0561ab_openblas.conda#953e30e380c03f058cacffe19bce9dc9 -https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.2-py314he06036b_1.conda#07f5745d2ec9349b1401e3891240c64f https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda#b1dde16791eca59c8316d0ad1487bff0 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h6c2aa35_0.conda#ab1bd70ec9c95d199f8ce456823004ab +https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda#8fc44bfcc973b7cab443539ce858161a https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda#4621ded2fd5b36f51454d5f81bff4ec1 @@ -58,19 +59,19 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.cond https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda#e429aec4037d5cb8fd34ded9f5dadd39 https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda#b6b191267455bf202af79f973690ed5d https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda#e0466c58fd07db190b9a81b5e58539f2 -https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda#4b5d3a91320976eec71678fad1e3569b +https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-h4d1e80c_2.conda#487d92910109a711fdb77631bf1aabf0 https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda#f51e9414bf1b7bb556aac1a0b74a75fe https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda#31d71ce1b056f69b6ec67ee0712bdf97 -https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-h84a0fba_1003.conda#d4852e6054b74645cf49ca10f6349191 +https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hb001647_1004.conda#f0833c4a0b68798f50935dd83384c705 https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda#786c0680e77665d1938ce9cdd7cf82a7 https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda#881cfb44ea02cc9849f612725a959660 https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda#ee1fc5bba400ff0cae27fbf141a1ae0c https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hce41798_2.conda#39f3afa677c4f1cd499aec3b1f5c76a9 -https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314hab283cf_0.conda#4f25498ea1962ab24097fed8700e91ae +https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.3.0-py314h709c99d_2.conda#f2da127864d9f3fcc5ccc00d68b04e63 https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda#5ab90033816cbe4097e8a297e5179f67 https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda#17f4744c0873e9f77ac9b5cd0a27c187 https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda#5103f6a6b210a3912faf8d7db516918c -https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda#14ef48eb322e0bfb1e5fe31e047359e3 +https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda#f3ee2d5802e47ff391fe9a6d956e76b0 https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda#0c7b78d9ffff4f5a6ca28d346f734d8f https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda#9a99c0b60efe41c194d01c182d000733 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 @@ -80,19 +81,19 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda#8ac8cc3fe744b484de4387703134d8b2 -https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-hdbf5564_2.conda#ae0e0e8ea304c6ff1608d7918703ba29 -https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_0.conda#c931e6d5af7f8f824fab6c474f3a1e94 -https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda#2bb7d7dd91116b8c85e805b0e08cc67b +https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h580cee5_3.conda#bd8c9c43ff83b23c1aaaaf51f2f889cf +https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.4.0-hcda0f7c_1.conda#4afba4fae30b467d6c699bb685fa35ce +https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda#b4397592e234554333719778bb01169e https://conda.anaconda.org/conda-forge/osx-arm64/libraqm-0.11.0-h45af499_0.conda#46ea0eb4e71bffa35ab7070678bcb053 -https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_0.conda#3e24b0e86143022ba7eba8ae276fab3d +https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.1-py314h2e43715_1.conda#dcb677490a8ad183ef65ea3b468de2c1 https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda#2f4cc7dc2e6622591735c49dda1b92aa -https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h6c2aa35_0.conda#4fffb3ba871bb05f34ffb705534dfef5 +https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda#b3ac6c13ee65405411de670a7ea4508e https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_3.conda#b457450ba3f27c4749783c0204bd17b0 -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_3.conda#954c78a9f591bfb12c79beaff7338ec8 -https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_3.conda#e07a99c6fdd984f4d588060f2f936bf4 -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_3.conda#d9de6c0ad7e008afdf62fb2ffd450b4a -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_3.conda#aa7df8174ee3b34a5ad8a3160429db68 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda#8f3981871d167a6cd323e636e1929dd4 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda#39a25d500b191c16996239c4afa104f1 +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda#da537a1643ef3e13bdccf16cca206f2c +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-he4a93e4_4.conda#d065d0732d02ace747059c3d58338fcd +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-hf00d406_4.conda#e32ad50b0f977e3fa616a578a2643e46 https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda#0509ee74d95e5b98eb6fe2a47760e399 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda#cddc851000ce131d757678c2f329eaad @@ -108,15 +109,15 @@ https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h74c22ad_2.conda# https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h435687b_1.conda#ac9902dcbe0417f50cf95ab2bde062fa https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321h26f1114_1.conda#843ef89082f368cb889305084d3b483c https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h34f8a20_2.conda#15235dd10450d67bc25ccafd5b46d2bc -https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-h6651222_5.conda#5ae67c128838b686894b4a3aef015c29 +https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.22.0-h6651222_0.conda#a7234b8d8e19a089dcb1eaaa18de1045 https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.8.1-h51dee2d_1.conda#816e590c5cd37b19c1e5932d8052acc9 https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda#1f38144f935e874fe7e296658484e719 https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda#408009d803402dd1ad4ffae452f17353 https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda#d47f7f3481c6f2fcc4ed22802f61c6dd -https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_1.conda#f86c0b8ac5c866049717b55df1049c2c -https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_1.conda#06a3f8eb5cdde56b2d10b49ae3337954 +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda#b40633f711a5eb8617917e0e944b9965 +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda#4b97a34d06740b578c32deba195daf5a https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda#2c966485853aa27985fbec7e3fcc4e77 -https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_1.conda#fff82f66f5775661123c2963930f318b +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda#df2f9521a1b80a859d6d329dba7df2a0 https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda#e7480ad364d7a5bf03812ba272c39707 https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda#d07359797436cfc891b38e203cf0caac https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda#b7819f4118a456301e220e995afd53ac @@ -132,26 +133,26 @@ https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda#94 https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda#39725a84a4b2b6b3ae6177f58be48b91 https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5 https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda#925acfb50a750aa178f7a0aced77f351 -https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.12.4-hed0ed58_5.conda#de41c6d2ce7d0138acd66d6b4122549d -https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda#2c4bd6aeb90bb157456841c3270a0d92 +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.13.3-h0d10833_1.conda#5b389503ff59a241b0560cf86e4b3413 +https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda#dea5b13a211bbf99876deb980b408a66 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 -https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.0-py314hda3c5be_1.conda#d2ca5fbd28cd1fd50b2dabf9ffc96df0 -https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py314h92bd025_5.conda#3bdcaa1d81e1cc32648f6a22517f92e3 +https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.5.1-py314h716b1d8_1.conda#80702063a5c4bd1b8110a0c6179d4621 +https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.8.0-py314h92bd025_0.conda#621a9854d7eb2ccf43e2f49d626bc5bb https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e -https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_0.conda#c2ca364702b1313c4f42aa39125724e4 +https://conda.anaconda.org/conda-forge/osx-arm64/simplejson-4.1.2-py314h61c6340_1.conda#6d4bee872fa16ae1e1145a6a3cc17c61 https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py314h277790e_2.conda#947d1f4e3160c83140a9c8bcb046fdac -https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.12.1-py314h3da1bed_0.conda#9b33fa020bd4da86a2dddfd0f63a43ba -https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314hf8d3afe_6.conda#d30d4527aacdf33b1e80ef858f33608c +https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.13.0-py314h618c026_0.conda#075ed1d8fa1903dd55bbbfc912bff1c9 +https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314h7afc4b4_7.conda#3ce7775e61c4dcd13ca709186bbd9af7 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc -https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.0-py314h18e1515_0.conda#e55fe08bb5d43e7120672338dd129030 +https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c +https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda#72cd17b6f8016221faaa96123711f8c9 https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b @@ -160,8 +161,8 @@ https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#4 https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 -https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_3.conda#d3a28add84f2412a562355f89ef5e0f5 -https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda#92adf685875ab717f68ad4172ef6de27 +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda#cfaec5b3d3d1f4877c1ebd84f8e1dfa6 +https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda#67924260a851b5273e39f9b28b6b5e24 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 @@ -209,7 +210,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9 https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda#6c06263450ab8f5f4580effead8b2ca4 https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda#1f47858e8a29295f723b63cd318a31c4 https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py314h7e6a90b_0_cpu.conda#d617d0524dd584ed2b4dba355cc3fc52 -https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda#1a109764bff3bdc7bdd84088347d71dc +https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda#9de672ef7d51541ea5176779bfc3e473 https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda#d006875f9a58a44f92aec9a7ebeb7150 https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda#ebf0d32466e8875bf91749744b97165a https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda#5810871245167355126d623aa5ae1096 @@ -223,14 +224,13 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88 https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda#31cf6dcc138abe673c9440cd6fe6c6fe https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda#dcf51e564317816cb8d546891019b3ab -https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_1.conda#bea6a028c9205ea3789bb38409cbfe43 -https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_0.conda#0b9070858d6aaf7f767a520063f68a3c +https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314hd98292b_2.conda#84d4b47c92ccfdfd6434146b27222be1 +https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda#36fc4194dcbcb14ed2ff3e1db09e3e89 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py314h6c2aa35_2.conda#aae872d4d6847677924de8e023e00457 https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 @@ -242,6 +242,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/fastparquet-2026.5.0-py314h2115 https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-h6651222_5.conda#a78919380afa9842a9c46f35fc7a518c +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda#5332c97b820ebc5a00e78b3e6ec7ce12 +https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.22.0-h6651222_0.conda#e3e65b70a52a7819dd63c31f9dce7209 https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index 6193b60..bde9528 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -5,21 +5,35 @@ https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda#350bb67a5c8e5f1c53347ac544ab6600 https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36247-habf1de7_41.conda#ac5333bb3d429361f23adf704cc49a78 https://conda.anaconda.org/conda-forge/win-64/vc-14.5-ha367084_41.conda#aa805b5522c2a98fa286e551a1f48546 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda#350d89b50d7de805abf2e63e29dee451 https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda#5d2ff29d465097458cc3ff6569151991 https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda#e4ac308c39d6d0e131154976da67cf3b https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda#fcb489df604d100968b737f2cb6076c6 https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda#dd9eb33c99d352b6356f86964d6040f7 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-h4c7d964_0.conda#e27d2ac27b096dc51fedfcf775a53f9b https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.4-hf411b9b_0.conda#73cb1783f47c452be4c309430fbef89f https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.4-hf5d6505_1.conda#a72d495965b144bb7da033642d389047 -https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_104_cp314.conda#83748d09d722103f7aa891ecfa13d672 +https://conda.anaconda.org/conda-forge/win-64/libpython-3.14.7-h4f90d01_106_cp314.conda#2d48db2a15a7b768bb563d2675c99b2c https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_2.conda#5ae92fd6614edd024576e14069d7ad4c https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_1.conda#880a0c8549479b198af21ba5dc49b109 https://conda.anaconda.org/conda-forge/win-64/libffi-3.7.0-h3d046cb_1.conda#ceb38b0ba900847d8da4289c3c8e5708 https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_1.conda#ccc490c81ffe14181861beac0e8f3169 https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda#c3301c058362f340100d91cd8be0393f -https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_104_cp314.conda#00cc3f77b4714fd8f297d8f51762bbaf +https://conda.anaconda.org/conda-forge/win-64/python-3.14.7-h53f6dd8_106_cp314.conda#df0b471269a379db0262256e332c1e7c +https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda#a8a2abdf0f901bc4779d9b7be0845921 +https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda#e596942e8ee6ee17fdcf1e6a77757a66 +https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda#bab5489f4bd38494ffaa83d6ab924c1f +https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda#3ce54d0f8f24dfab99d82414411640aa +https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5 +https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda#6a01c986e30292c715038d2788aa1385 +https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda#632effc5cd23068512f7661821764807 +https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda#79055ec79e1b43749763122cead30976 +https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda#ace316c48c178d7faa403dee51e30c7b +https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda#0d13dde4466d62a488e58f12aedacc1c +https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda#3a442b27a0ca4153d07b3ceb12fd486f +https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda#7f19a81a13d7e167c42c096ff311a341 +https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.3-py314h02f10f6_0.conda#806daade32d05870efd4d63931c12c9d +https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda#a925cfb1429da2b1312c86516ae7c418 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda#c70ad746c22219b9700931707482992c https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda#e9c622e0d00fa24a6292279af3ab6d06 https://conda.anaconda.org/conda-forge/noarch/typing_inspect-0.9.0-pyhd8ed1ab_1.conda#fa31df4d4193aabccaf09ce78a187faf @@ -34,25 +48,12 @@ https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.c https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.3-pyhd8ed1ab_0.conda#eefc8d916bd2e708d76d40398ef9a1ee https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 -https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_3.conda#a8a2abdf0f901bc4779d9b7be0845921 -https://conda.anaconda.org/conda-forge/win-64/icu-78.3-h5112557_2.conda#e596942e8ee6ee17fdcf1e6a77757a66 -https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h3cfd58e_1.conda#6e7dadff3f3bde2d29d29a3ec34f2d58 -https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-h8ef44ab_1.conda#45a5a1c709a69f14cf176220788d18a9 -https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5 -https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda#6a01c986e30292c715038d2788aa1385 -https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda#8ee01a693aecff5432069eaaf1183c45 -https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda#79055ec79e1b43749763122cead30976 -https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda#ace316c48c178d7faa403dee51e30c7b -https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda#0d13dde4466d62a488e58f12aedacc1c -https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda#3a442b27a0ca4153d07b3ceb12fd486f -https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-10_h2a3cdd5_mkl.conda#7f19a81a13d7e167c42c096ff311a341 -https://conda.anaconda.org/conda-forge/win-64/numpy-2.5.2-py314h02f10f6_1.conda#e4916c642c9aa7ab2d58ec75591189d3 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda#a519b50147401cf6bad2fd188028ad38 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/conda-forge/noarch/tclean-0.1.1-pyhc364b38_0.conda#9715ba984ea99ba23d0d0399311937c1 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 -https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_0.conda#94dfb208db7d79437cfe9b4390706b5c +https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda#a92f6eff943133d045db4d017ac4297c https://conda.anaconda.org/conda-forge/win-64/pcre2-10.47-h8466c1e_1.conda#009af999dbe2d1db36a37187a4ca4eb4 https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_1.conda#35a9475e4cc999d52921f57c181979fc https://conda.anaconda.org/conda-forge/win-64/libvulkan-loader-1.4.357.0-h477610d_2.conda#aaeed7feddbfd7454f8853a5eae8d902 @@ -62,7 +63,7 @@ https://conda.anaconda.org/conda-forge/win-64/lerc-4.2.0-hd936e49_0.conda#add59e https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.2-h8f73337_1.conda#970dbe48f843e7fbd179a9b6c22f865a https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-hdc8cecf_1.conda#5aa7348e73691187c81d51616f17e48a https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_4.conda#36d6e0045173974521fabd42bd5033d6 -https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-he810d59_2.conda#e40bcfee79dbfda3eff751ed098b6ad3 +https://conda.anaconda.org/conda-forge/win-64/libglib-2.88.3-h261a839_3.conda#6326b3f7af94482676d8eef011b2f9d6 https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_2.conda#8157483eb7ed3fcba71aad3b50a97131 https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_2.conda#740f99c3c91079c03874b809fedace1b https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.15-h5112557_1.conda#79538e7a7bc024084eda5989f73fde35 @@ -74,39 +75,39 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.18.3-hd47e2ca_1.conda#ed95670fed91b091fe34ba6cae6677d7 -https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_2.conda#601e2ec8ae0ed934d8788000f984ff18 -https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_0.conda#4618efa4303fdda115da50b8090d73c6 -https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_3.conda#8ef4beb3cb18e1a876b9af9a757cc1a5 -https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_3.conda#cb5d08dc81f52e87c27914b5636cd995 -https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_3.conda#10c479888ee4e960587967b2d0c8143a +https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda#37e4ca2dd35c4171f0587d985976b30e +https://conda.anaconda.org/conda-forge/win-64/libharfbuzz-14.4.0-h03b5201_1.conda#d7b92075b7461e40fd8e90f08669a742 +https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hf02afa3_4.conda#35d7e4a581c8364e8c675f6b95d183d0 +https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-he2a975b_4.conda#562b5e39b7797423e7fd04bae1c6ad70 +https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-h84f9c24_4.conda#1b4637ca71b21c0d31ab28c3c0b34c8e https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h719d79b_2.conda#93f5a01dec294a2228f757fe2f3432d4 https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.4.0-hac47afa_0.conda#3d3caf4ccc6415023640af4b1b33060a https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.11.2-pl5321hfcac499_0.conda#d98a163070d05c6ed4421432ee1bafb8 -https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.43-h0fbe4c1_1.conda#46034d9d983edc21e84c0b36f1b4ba61 +https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda#ea7b3abfe39c180d1dce620867e267d1 https://conda.anaconda.org/conda-forge/win-64/libclang13-23.1.0-default_hacd6ee9_1.conda#f177ba8f56631ec8d8cc1f799642e05d -https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h447aaf0_0.conda#7c3ccff96dbc9f8e3c40142c7594ec34 +https://conda.anaconda.org/conda-forge/win-64/pyside6-6.11.2-py314h0c0e1ae_1.conda#2a0080a3a7c28a372db3cb629ae1b19f https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5 https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda#46a21c0a4e65f1a135251fc7c8663f83 -https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda#e723ab7cc2794c954e1b22fde51c16e4 +https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h90fa87c_2.conda#d9b2cb1079cf59651722c09aa3a9f840 https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda#91189f0bc9ff21f385fcda6232346612 https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda#1626967b574d1784b578b52eaeb071e7 https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda#02793cf48b68687fee158a48c92850a6 https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda#0e21a45f1bb429b6e35e82631e60554a https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda#87aee04978ab77bf4c0f42b983c216cc -https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hba3369d_1003.conda#bc97d497656c9c661ffd3043aca90aa4 +https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda#fda2f9fedee1b85ba855ea26a9b6bbf8 https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda#3658b0201b32da9607ea5e3c1c463c1f https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda#2bd9052d2e496ea54f47d9db8ae4f881 -https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_0.conda#09b8e6ac8f4a257b59e5d3025f3c9c1d -https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_1.conda#66f5ce9d0d618332023619a899ceb26f +https://conda.anaconda.org/conda-forge/win-64/pillow-12.3.0-py314h61b30b5_2.conda#1fcc0165f5eb12f89438921b23f42be6 +https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_2.conda#130c9b1af59ff14131c661cc14e4a632 https://conda.anaconda.org/conda-forge/win-64/libraqm-0.11.0-h50d6d30_0.conda#df92be5685f712989830bdb7ee39383a -https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_0.conda#842f2582140b5567bcad1defbbd9b948 +https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.1-py314hf309875_1.conda#112da4db82886f7c2b8496a4e44da64e https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda#945092a9bc1d0f250f7d5ecf51ecd471 https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_2.conda#6fc6c09c05a099d58efd9b2e96598e41 -https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_0.conda#d6b580a13384df5155c6ca19ee66854e +https://conda.anaconda.org/conda-forge/win-64/unicodedata2-17.0.1-py314h5a2d7ad_1.conda#1a3cf506689df0c4d3a85aa2aa5ead67 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_3.conda#dc04f7b3d82c6fb3fdf4d93e45b6ea2a -https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_3.conda#9eba56a007c44dc32d0b9d0a820871ea +https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hd477307_4.conda#5353bd43aaade891fc9b614c5d537ce3 +https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-hc8c2fe1_4.conda#bdbb2939efe8af10e7c5885367395710 https://conda.anaconda.org/conda-forge/noarch/fonttools-4.63.0-pyh7db6752_0.conda#0509ee74d95e5b98eb6fe2a47760e399 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda#4c2a8fef270f6c69591889b93f9f55c1 https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda#c26934035616f7d578f9da0491aed3d8 @@ -117,12 +118,12 @@ https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9a https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda#df9d7c5d34602e0f2655a524d31fce87 https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h734d217_1.conda#a21dd9ca39e74910bb96989feb916420 https://conda.anaconda.org/conda-forge/win-64/libpsl-0.23.1-h9b16d47_1.conda#39cdf8c4f59e4d253cfa8091c8b3d7de -https://conda.anaconda.org/conda-forge/win-64/libcurl-8.21.0-hdb0ef4a_5.conda#50861643cbe90dc7267feb7854b8efdf +https://conda.anaconda.org/conda-forge/win-64/libcurl-8.22.0-hdb0ef4a_0.conda#e8405e754eaac42d66f957222fcfd876 https://conda.anaconda.org/conda-forge/win-64/proj-9.8.1-hd30e2cd_1.conda#434616a1662714fe1a75a51ace8c72d6 https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda#e09251a25a8c503eb30375e187de2c93 https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda#3084e08306d71a87b785efae1fa22564 https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda#3e0691cb20fcaf922df78ccea08b771a -https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-h8ef44ab_1.conda#daa37dc0bb73bc77322b83f94b72caae +https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda#d50e00d25a326eb487f9f5540e09b77b https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda#f3405ff0366fe7ee04580acf8e422d16 https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda#7eeb5aed49853f8b3e1ca0463ef55a8e https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda#124e35907d5fb36b1e0cb3b74c20bc9d @@ -136,27 +137,26 @@ https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda#3678b https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.9-gpl_h8fdc8af_101.conda#74c32a74e1b188a4e14fc359c721e4b9 https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda#3075846de68f942150069d4289aaad63 https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda#357d7be4146d5fec543bfaa96a8a40de -https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.12.4-hbfeefb7_5.conda#6f86ca570061dd7f251ab5554eb610a5 -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyh6dadd2b_0.conda#8a0d65027e25e367f9f1754f0604e8de +https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.13.3-hcd51406_1.conda#0553b074c878e31b9090ec0542228ac1 +https://conda.anaconda.org/conda-forge/noarch/click-8.5.0-pyh5ded981_0.conda#dea5b13a211bbf99876deb980b408a66 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda#37e13edbe3b48f1095a9d085ef9cd83b https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda#aeb404cab9e719a49264ab40928f80c8 -https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.0-py314hd376f3d_1.conda#195d18c86203797871e15b2907579a84 -https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py314h9d82244_5.conda#b7503bde09c3bce5c09d81485855f5a8 +https://conda.anaconda.org/conda-forge/win-64/rasterio-1.5.1-py314h0f7306d_1.conda#6b5756070c6380b7a27401100cc8fff5 +https://conda.anaconda.org/conda-forge/win-64/pyproj-3.8.0-py314h9d82244_0.conda#8a31da8c3cd2b25863db1e4e00d595c0 https://conda.anaconda.org/conda-forge/noarch/rioxarray-0.23.0-pyhc364b38_0.conda#3e549fac4b34fd5c8a89b9c0cc5bbd8e -https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_0.conda#2f613befc24a58c3935b1be38db94355 +https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda#579ef7dd293c37b0e808f8b3b01559a8 https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.2-py314h76f3c27_2.conda#c5a64ac49215efe79837adbd7e42b605 -https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.12.1-py314h1c1cb05_0.conda#da6ef5e9a5931e73a7638b60fd82fc63 -https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314h1c1cb05_6.conda#c1bae1515ff287d5bf6e287dddbc6646 +https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.13.0-py314he172734_0.conda#d0026e98c1f21ed4078a3ee378dd0713 +https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314hcb00e3b_7.conda#47c71cc6a001bd356d8634af938c36f5 https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 -https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc -https://conda.anaconda.org/conda-forge/win-64/scipy-1.18.0-py314h221f224_0.conda#a925cfb1429da2b1312c86516ae7c418 +https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#615de2a4d97af50c350e5cf160149e77 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c +https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda#74bafde39f688cb95c111e74bfad6669 https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda#0323c95182d828bb1bcf708c4077db3b @@ -166,8 +166,8 @@ https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda#e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda#b395909221b9bd1df066e5930e18855b https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda#e652ac7756069c456d0da2a922cd7df5 -https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_3.conda#b1ff58c1f0deedd3f25c103e37049cee -https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_0.conda#92adf685875ab717f68ad4172ef6de27 +https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda#a0d7c645ae6b82fda8a2eb24a1de7b43 +https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.7.0-py314h680f03e_1.conda#67924260a851b5273e39f9b28b6b5e24 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e https://conda.anaconda.org/conda-forge/noarch/idna-3.19-pyhcf101f3_0.conda#a39ae05027e9b707742e41b30d296b75 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda#e0ac3accc64e23e40969d660e5f58ac8 @@ -214,7 +214,7 @@ https://conda.anaconda.org/conda-forge/win-64/aws-sdk-cpp-1.11.833-hdf1b151_9.co https://conda.anaconda.org/conda-forge/win-64/libarrow-25.0.0-h20c36f3_3_cpu.conda#082ecab7d62fcf79ce26236b80e76578 https://conda.anaconda.org/conda-forge/win-64/libarrow-compute-25.0.0-h081cd8e_3_cpu.conda#5231ee28028c0e342550873d25d21d07 https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-25.0.0-py314h159fc0c_0_cpu.conda#54155aba0ef1fe72c52022617e453d52 -https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-h3671451_1.conda#25efbd786caceef438be46da78a7b5ef +https://conda.anaconda.org/conda-forge/win-64/libevent-2.1.12-hc903f1e_2.conda#2f8b5fd59f830697c007f6f9e989f063 https://conda.anaconda.org/conda-forge/win-64/libthrift-0.22.0-h2e43b2f_2.conda#42856184560e5cf901551fd414ad25c1 https://conda.anaconda.org/conda-forge/win-64/libparquet-25.0.0-h7051d1f_3_cpu.conda#6fd6233ec7bd1ee2423a28eab21e8f75 https://conda.anaconda.org/conda-forge/win-64/libarrow-acero-25.0.0-h7d8d6a5_3_cpu.conda#1f948e54f605402994877c1df640c668 @@ -228,14 +228,13 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda#f88 https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda#0401a17ae845fa72c7210e206ec5647d https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda#433699cba6602098ae8957a323da2664 https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda#0cd9b88826d0f8db142071eb830bce56 -https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_1.conda#deeef9494cfffa58f5fd20bc6eb57664 -https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_0.conda#921a2c141bc15cf74a238a8fb928554a +https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_2.conda#e5f28a48fe6fade84972e3aba057f400 +https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda#a60ac4e5c43cf1f77fe3b1d2fd4c214b https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda#ba3dcdc8584155c97c648ae9c044b7a3 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.1-pyhcf101f3_0.conda#77ec68e0aa61c3fff9ecbf840f93d64e https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.7.0-pyhd8ed1ab_0.conda#b9176565976c773a0739bd83deaf06cc -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.8.0-pyhc364b38_0.conda#2ccdf45d2372e15c7edcb7a72d573e76 https://conda.anaconda.org/conda-forge/win-64/cytoolz-1.1.0-py314h5a2d7ad_2.conda#8309b55a55dac8050260de935d31724a https://conda.anaconda.org/conda-forge/noarch/distributed-2026.8.0-pyhc364b38_0.conda#f382c4eed5376ee372a6e67a0f6cd4d9 @@ -247,6 +246,6 @@ https://conda.anaconda.org/conda-forge/win-64/fastparquet-2026.5.0-py314h2dcd201 https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda#eb2fb9070c0232e96ae5515d8b28e4f9 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda#ba33e6c8a46ee373fdf6dd8665212778 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda#3b261da3fe9b4168738712832410b022 -https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.0-pyhd8ed1ab_0.conda#0c06dd1da8af688335732bb47dc51dc1 -https://conda.anaconda.org/conda-forge/win-64/curl-8.21.0-hdb0ef4a_5.conda#b0dcd6182bfd56a219563a153d550d20 +https://conda.anaconda.org/conda-forge/noarch/entsoe-py-0.8.1-pyhd8ed1ab_0.conda#5332c97b820ebc5a00e78b3e6ec7ce12 +https://conda.anaconda.org/conda-forge/win-64/curl-8.22.0-hdb0ef4a_0.conda#a21861ac889a38415844187508d6d56a https://conda.anaconda.org/conda-forge/noarch/cmap-0.7.2-pyhd8ed1ab_0.conda#992b4902eb5ff13646048cd7629a82bf diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index 8f15f4e..7cb44ab 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -1,5 +1,6 @@ name: module channels: +- https://conda.anaconda.org/ddahawkins/label/dev - conda-forge - bioconda - nodefaults @@ -20,4 +21,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 -- tclean >=0.1.1 +- tclean >=0.2.0 diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index f9baabe..e7ae26a 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -10,7 +10,7 @@ intersect_source_temporal_scope, uncovered_temporal_intervals, ) -from tclean import TCleanConfig, TimeGrid +from tclean import TimeGrid def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: @@ -22,11 +22,6 @@ def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: ) -def build_tclean_config(temporal_scope: Mapping[str, Any]) -> TCleanConfig: - """Build the T-Clean configuration used for demand cleaning.""" - return TCleanConfig(grid=build_time_grid(temporal_scope)) - - def build_basic_rules(gap_filling_config: Mapping[str, Any]) -> list[dict[str, Any]]: """Return configured basic-cleaning rules for T-Clean.""" if gap_filling_config["mode"] == "off": diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 7f3178b..21b5c88 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -5,8 +5,7 @@ import pandas as pd from _advanced_execution import load_execution_plan from tclean import TimeGrid -from tclean.advanced import read_external_profile -from tclean.advanced.apply import apply_advanced_rules +from tclean.gap_filling import apply_advanced_rules, read_external_profile plan = load_execution_plan(snakemake.input.plan) diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py index 638e793..dc7c492 100644 --- a/workflow/scripts/clean_auxiliary_group.py +++ b/workflow/scripts/clean_auxiliary_group.py @@ -5,7 +5,8 @@ import pandas as pd from _advanced_execution import load_execution_plan from _prepared_data import read_prepared_source -from tclean import TCleanConfig, TimeGrid, clean +from tclean import TimeGrid +from tclean.gap_filling import fill_gaps if TYPE_CHECKING: snakemake: Any @@ -46,8 +47,6 @@ def main(snakemake: Any) -> None: start=group_start, end=group_end, frequency=(snakemake.params.frequency) ) - config = TCleanConfig(grid=grid) - source_paths = list(snakemake.input.sources) if len(source_paths) != len(batches): @@ -86,8 +85,8 @@ def main(snakemake: Any) -> None: else [] ) - (cleaned, data_source, cleaning_method) = clean( - sources, config=config, basic_rules=basic_rules + (cleaned, data_source, cleaning_method) = fill_gaps( + sources, grid=grid, basic_rules=basic_rules ) cleaned.to_parquet(snakemake.output.demand) diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 7933a1b..c9e95e5 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -7,10 +7,13 @@ import pandas as pd from _prepared_data import read_prepared_source -from _tclean_config import build_basic_rules, build_tclean_config -from tclean import clean -from tclean.advanced import build_gap_report -from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank +from _tclean_config import build_basic_rules, build_time_grid +from tclean.gap_filling import ( + build_cleaning_method_ranks, + build_gap_report, + derive_cleaning_method_rank, + fill_gaps, +) if TYPE_CHECKING: snakemake: Any @@ -38,19 +41,17 @@ def main(snakemake: Any) -> None: target_contexts = _read_target_contexts(snakemake.input.target_plan) - config = build_tclean_config(snakemake.params.temporal_scope) + grid = build_time_grid(snakemake.params.temporal_scope) sources = { - source_name: data.reindex( - index=config.grid.target_index, columns=target_contexts - ) + source_name: data.reindex(index=grid.target_index, columns=target_contexts) for source_name, data in sources.items() } basic_rules = build_basic_rules(snakemake.params.gap_filling) - (cleaned, data_source, cleaning_method) = clean( - sources, config=config, basic_rules=basic_rules + (cleaned, data_source, cleaning_method) = fill_gaps( + sources, grid=grid, basic_rules=basic_rules ) basic_rule_names = [rule["name"] for rule in basic_rules] @@ -64,9 +65,7 @@ def main(snakemake: Any) -> None: ) gap_report = build_gap_report( - cleaned, - grid=config.grid, - enabled=(snakemake.params.gap_filling["mode"] == "advanced"), + cleaned, grid=grid, enabled=(snakemake.params.gap_filling["mode"] == "advanced") ) cleaned.to_parquet(snakemake.output.demand) diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 9e91a1d..6095cca 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -7,7 +7,7 @@ build_scaling_source_periods, ) from tclean import TimeGrid -from tclean.advanced import construct_from_sources +from tclean.gap_filling import construct_from_sources plan = load_execution_plan(snakemake.input.plan) diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 3c7c423..ca92bb4 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -4,7 +4,7 @@ import pandas as pd from _tclean_config import build_advanced_rules, build_basic_rules -from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank +from tclean.gap_filling import build_cleaning_method_ranks, derive_cleaning_method_rank shutil.copyfile(snakemake.input.demand, snakemake.output.demand) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index 666c157..f22814a 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -24,7 +24,7 @@ filter_source_requests_by_temporal_scope, get_advanced_source_definitions, ) -from tclean.advanced import ( +from tclean.gap_filling import ( build_auxiliary_acquisition_requirements, build_auxiliary_source_requests, select_active_advanced_rules, diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 5a79a2b..ed5a26e 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -15,7 +15,7 @@ build_time_grid, ) from tclean import TimeGrid -from tclean.basic import validate_basic_rules +from tclean.gap_filling import validate_basic_rules if TYPE_CHECKING: snakemake: Any From ce23d76bb942c6f2442d60f3031b69b1a855f8b8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 16:09:36 +0200 Subject: [PATCH 223/241] Updating integration-test to isolate powerstats given entsoe-transparency inconsistency. --- tests/integration/test_config.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 2c80d16..7731039 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -2,16 +2,16 @@ integration_test: # The integration test uses a deliberately short hourly period to keep # provider downloads and workflow execution reasonably small. temporal_scope: - start: "2017-01-01" - end: "2017-01-03" + start: "2020-01-01" + end: "2020-01-03" frequency: "1h" # Load sources are listed in priority order. Where multiple sources provide # data for the same country and timestamp, the earlier source takes priority. # Supported sources include: entsoe, neso, opsd. load_sources: - - entsoe - - neso + # - entsoe + # - neso # - opsd - entsoe_power_statistics @@ -144,8 +144,8 @@ integration_test: # construct_from_sources integration case. - name: build_alb_from_alb_2024 country: ALB - start: "2017-01-01" - end: "2017-01-03" + start: "2020-01-01" + end: "2020-01-03" scope: overwrite source: alb_from_alb_2024 @@ -154,8 +154,8 @@ integration_test: # match_total construction case in place. - name: test_normalise_mean country: ALB - start: "2017-01-02" - end: "2017-01-03" + start: "2020-01-02" + end: "2020-01-03" scope: overwrite source: alb_from_alb_2024_normalised @@ -173,7 +173,7 @@ integration_test: # exercising external_profile loading and overwrite application. - name: test_external_profile_overwrite country: ALB - start: "2017-01-01 00:00" - end: "2017-01-01 03:00" + start: "2020-01-01 00:00" + end: "2020-01-01 03:00" scope: overwrite source: alb_external_test From 70ab9c8f246519a3ad4f5a72e27e68211a075751 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 16:37:58 +0200 Subject: [PATCH 224/241] Updated config schema for data_quality --- config/config.yaml | 3 ++ tests/integration/test_config.yaml | 3 ++ tests/local/local_test_config.yaml | 3 ++ tests/unit/test_config_schema.py | 49 ++++++++++++++++++ tests/unit/test_tclean_config.py | 75 +++++++++++++++++++++++++++- workflow/internal/config.schema.yaml | 67 +++++++++++++++++++++++++ workflow/scripts/_tclean_config.py | 18 +++++++ 7 files changed, 216 insertions(+), 2 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 6bc8096..c04e54b 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -175,3 +175,6 @@ gap_filling: end: "2017-01-01 03:00" scope: overwrite source: alb_external_test + +data_quality: + tests: [] \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 7731039..46503ed 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -177,3 +177,6 @@ integration_test: end: "2020-01-01 03:00" scope: overwrite source: alb_external_test + + data_quality: + tests: [] \ No newline at end of file diff --git a/tests/local/local_test_config.yaml b/tests/local/local_test_config.yaml index 6654663..7901130 100644 --- a/tests/local/local_test_config.yaml +++ b/tests/local/local_test_config.yaml @@ -178,3 +178,6 @@ integration_test: end: "2017-01-01 03:00" scope: overwrite source: alb_external_test + + data_quality: + tests: [] \ No newline at end of file diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 2b26132..c4c2c7d 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -48,6 +48,7 @@ def test_advanced_mode_allows_no_sources_or_rules() -> None: "rules": [], }, }, + "data_quality": {"tests": []}, } errors = _validate(config) assert not errors, "\n".join(_format_validation_error(error) for error in errors) @@ -83,6 +84,7 @@ def _advanced_config_with_scaling(scaling: dict) -> dict: "rules": [], }, }, + "data_quality": {"tests": []}, } @@ -169,3 +171,50 @@ def test_constructed_source_rejects_unknown_scaling_method() -> None: config = _advanced_config_with_scaling({"method": "something_else"}) assert _validate(config) + + +def _config_with_data_quality(data_quality: dict) -> dict: + """Return a minimal valid config with the supplied data-quality section.""" + return { + "temporal_scope": { + "start": "2021-01-01", + "end": "2022-01-01", + "frequency": "1h", + }, + "load_sources": ["entsoe"], + "data_quality": data_quality, + "gap_filling": { + "mode": "off", + "basic": {"rules": []}, + "advanced": { + "auxiliary_data": { + "basic_cleaning": {"enabled": True}, + }, + "sources": {}, + "rules": [], + }, + }, + } + +def test_data_quality_allows_no_tests() -> None: + """Check data-quality configuration may contain no tests.""" + config = _config_with_data_quality({"tests": []}) + + assert not _validate(config) + +def test_data_quality_rejects_contexts_selector() -> None: + """Require module-facing country terminology instead of T-Clean contexts.""" + config = _config_with_data_quality( + { + "tests": [ + { + "name": "example_range", + "method": "range", + "contexts": ["ALB"], + } + ] + } + ) + + assert _validate(config) + diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index dcaf381..06ccf3e 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -6,6 +6,7 @@ build_advanced_rules, build_all_constructed_source_periods, build_basic_rules, + build_data_quality_tests, build_scaling_source_periods, build_time_grid, filter_source_requests_by_temporal_scope, @@ -30,7 +31,7 @@ def test_build_time_grid_translates_temporal_scope() -> None: def test_build_basic_rules_returns_no_rules_when_mode_is_off() -> None: """Test basic rules returns nothing when disabled.""" - config = {"mode": "off", "basic": {"rules": []}} + config = {"mode": "off", "basic": {"rules": []},"data_quality": {"tests": []},} assert build_basic_rules(config) == [] @@ -45,7 +46,7 @@ def test_build_basic_rules_preserves_configured_rule_order() -> None: "source_offsets": ["-24h"], }, ] - config = {"mode": "basic", "basic": {"rules": rules}} + config = {"mode": "basic", "basic": {"rules": rules},"data_quality": {"tests": []},} assert build_basic_rules(config) == rules @@ -55,6 +56,7 @@ def test_build_advanced_rules_returns_canonical_columns_when_empty() -> None: "mode": "advanced", "basic": {"rules": []}, "advanced": {"sources": {}, "rules": []}, + "data_quality": {"tests": []}, } result = build_advanced_rules(config) assert result.empty @@ -152,6 +154,7 @@ def test_normalisation_scaling_does_not_add_auxiliary_periods() -> None: }, } }, + "data_quality": {"tests": []}, } result = build_all_constructed_source_periods(gap_filling_config) @@ -191,6 +194,7 @@ def test_match_total_scaling_adds_auxiliary_periods() -> None: } } }, + "data_quality": {"tests": []}, } result = build_all_constructed_source_periods(gap_filling_config) @@ -341,3 +345,70 @@ def test_auxiliary_temporal_filter_rejects_coverage_gap() -> None: assert "AAA" in message assert "2019-01-01T00:00:00+00:00" in message assert "2019-02-01T00:00:00+00:00" in message + + +def test_build_data_quality_tests_preserves_test_order() -> None: + """Check data-quality test execution order is preserved.""" + config = { + "tests": [ + {"name": "first", "method": "range"}, + {"name": "second", "method": "flatline"}, + ] + } + + result = build_data_quality_tests(config) + + assert [test["name"] for test in result] == ["first", "second"] + + +def test_build_data_quality_tests_translates_countries_to_contexts() -> None: + """Translate electricity-demand countries to generic T-Clean contexts.""" + config = { + "tests": [ + { + "name": "country_range", + "method": "range", + "countries": ["ALB", "MNE"], + "minimum": { + "value_mode": "fixed", + "value": 0, + }, + } + ] + } + + result = build_data_quality_tests(config) + + assert result == [ + { + "name": "country_range", + "method": "range", + "contexts": ["ALB", "MNE"], + "minimum": { + "value_mode": "fixed", + "value": 0, + }, + } + ] + + +def test_build_data_quality_tests_preserves_sources() -> None: + """Preserve configured T-Clean source selectors.""" + config = { + "tests": [ + { + "name": "power_statistics_range", + "method": "range", + "sources": ["entsoe_power_statistics"], + "minimum": { + "value_mode": "fixed", + "value": 0, + }, + } + ] + } + + result = build_data_quality_tests(config) + + assert result[0]["sources"] == ["entsoe_power_statistics"] + diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 0fa8d81..e0c4415 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -49,7 +49,55 @@ $defs: description: > ISO alpha-3 country code. + # --------------------------------------------------------------------------- + # Data-quality tests + # --------------------------------------------------------------------------- + + data_quality_test: + type: object + description: > + Named data-quality test applied to electricity-demand source data. + Method-specific parameters are validated by T-Clean. + + properties: + name: + type: string + pattern: '^[a-z0-9_]+$' + description: > + Unique identifier for this configured data-quality test. + method: + type: string + minLength: 1 + description: > + T-Clean data-quality evaluation method. + + countries: + type: array + minItems: 1 + uniqueItems: true + items: + $ref: "#/$defs/country_code" + description: > + Optional electricity-demand countries to which this test applies. + These are translated to T-Clean contexts. + + sources: + type: array + minItems: 1 + uniqueItems: true + items: + type: string + description: > + Optional electricity-demand sources to which this test applies. + + contexts: false + + required: + - name + - method + + additionalProperties: true # --------------------------------------------------------------------------- # Basic gap-filling rules # --------------------------------------------------------------------------- @@ -428,6 +476,24 @@ properties: items: type: string + data_quality: + type: object + description: > + Data-quality tests applied to electricity-demand source data. + Test methods and method-specific parameters are validated by T-Clean. + + properties: + tests: + type: array + items: + $ref: "#/$defs/data_quality_test" + description: > + Ordered data-quality tests. Tests are evaluated in listed order. + + required: + - tests + + additionalProperties: false gap_filling: type: object @@ -513,5 +579,6 @@ required: - temporal_scope - load_sources - gap_filling + - data_quality additionalProperties: false diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index e7ae26a..11e8506 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -22,6 +22,24 @@ def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: ) +def build_data_quality_tests( + data_quality_config: Mapping[str, Any], +) -> list[dict[str, Any]]: + """Translate module data-quality configuration to T-Clean tests.""" + tests: list[dict[str, Any]] = [] + + for configured_test in data_quality_config["tests"]: + test = dict(configured_test) + + countries = test.pop("countries", None) + if countries is not None: + test["contexts"] = list(countries) + + tests.append(test) + + return tests + + def build_basic_rules(gap_filling_config: Mapping[str, Any]) -> list[dict[str, Any]]: """Return configured basic-cleaning rules for T-Clean.""" if gap_filling_config["mode"] == "off": From 2ad3080a0b9927ae09f483243fc4117910bc832c Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 16:43:19 +0200 Subject: [PATCH 225/241] Wired data_quality validation through rules and validation script. --- workflow/rules/automatic.smk | 19 +++++++++++++++++++ workflow/scripts/validate_config.py | 15 ++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 99d34e9..4dcc558 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -38,6 +38,25 @@ rule validate_gap_filling_config_semantics: "../scripts/validate_config.py" +rule validate_data_quality_config_semantics: + output: + "/automatic/data_quality_config_validation.json", + log: + "/validate_data_quality_config_semantics.log", + conda: + "../envs/module.yaml" + params: + validation_kind="data_quality", + validation_config={ + "temporal_scope": config["temporal_scope"], + "data_quality": config["data_quality"], + }, + message: + "Validate data_quality configuration semantics." + script: + "../scripts/validate_config.py" + + checkpoint plan_target_data: input: shapes="", diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index ed5a26e..6861217 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -11,10 +11,12 @@ build_advanced_rules, build_basic_rules, build_constructed_source_periods, + build_data_quality_tests, build_scaling_source_periods, build_time_grid, ) from tclean import TimeGrid +from tclean.data_quality import validate_quality_tests from tclean.gap_filling import validate_basic_rules if TYPE_CHECKING: @@ -37,6 +39,14 @@ def validate_gap_filling_config_semantics(config: Mapping[str, Any]) -> None: _validate_advanced_config(gap_filling, grid=grid) +def validate_data_quality_config_semantics(config: Mapping[str, Any]) -> None: + """Validate data-quality configuration semantics.""" + grid = build_time_grid(config["temporal_scope"]) + tests = build_data_quality_tests(config["data_quality"]) + + validate_quality_tests(tests, grid=grid) + + def config_hash(config: Mapping[str, Any]) -> str: """Return a deterministic hash of validated configuration.""" serialised = json.dumps(config, sort_keys=True, separators=(",", ":")).encode( @@ -199,10 +209,13 @@ def write_validation_marker( if validation_kind == "temporal": validate_temporal_config_semantics(validation_config) + elif validation_kind == "data_quality": + validate_data_quality_config_semantics(validation_config) + elif validation_kind == "gap_filling": validate_gap_filling_config_semantics(validation_config) else: raise ValueError(f"Unsupported validation kind {validation_kind!r}.") - write_validation_marker(snakemake.output[0], validated_config=validation_config) + write_validation_marker(snakemake.output[0], validated_config=validation_config) \ No newline at end of file From f7ca160b654be5df731785ba8433f5ba27e96e10 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 16:49:27 +0200 Subject: [PATCH 226/241] bug fix. --- workflow/internal/config.schema.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index e0c4415..3b7a18a 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -91,7 +91,8 @@ $defs: description: > Optional electricity-demand sources to which this test applies. - contexts: false + contexts: + not: {} required: - name From fd86362e590575256e5acb1b0cdb4b012415a148 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 17:34:46 +0200 Subject: [PATCH 227/241] Data quality is part of the snakemake workflow. --- tests/integration/test_config.yaml | 7 +- tests/unit/test_config_schema.py | 17 +---- tests/unit/test_tclean_config.py | 42 ++++++----- .../unit/test_validate_data_quality_config.py | 41 +++++++++++ workflow/Snakefile | 1 + workflow/rules/data_quality.smk | 23 ++++++ workflow/rules/prepare_demand.smk | 2 + workflow/scripts/_tclean_config.py | 5 ++ workflow/scripts/evaluate_data_quality.py | 73 +++++++++++++++++++ workflow/scripts/validate_config.py | 2 +- 10 files changed, 179 insertions(+), 34 deletions(-) create mode 100644 tests/unit/test_validate_data_quality_config.py create mode 100644 workflow/rules/data_quality.smk create mode 100644 workflow/scripts/evaluate_data_quality.py diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 46503ed..a0d60df 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -179,4 +179,9 @@ integration_test: source: alb_external_test data_quality: - tests: [] \ No newline at end of file + tests: + - name: non_negative + method: range + minimum: + value_mode: fixed + value: 0 \ No newline at end of file diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index c4c2c7d..1727906 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -187,34 +187,25 @@ def _config_with_data_quality(data_quality: dict) -> dict: "mode": "off", "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": {"enabled": True}, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "sources": {}, "rules": [], }, }, } + def test_data_quality_allows_no_tests() -> None: """Check data-quality configuration may contain no tests.""" config = _config_with_data_quality({"tests": []}) assert not _validate(config) + def test_data_quality_rejects_contexts_selector() -> None: """Require module-facing country terminology instead of T-Clean contexts.""" config = _config_with_data_quality( - { - "tests": [ - { - "name": "example_range", - "method": "range", - "contexts": ["ALB"], - } - ] - } + {"tests": [{"name": "example_range", "method": "range", "contexts": ["ALB"]}]} ) assert _validate(config) - diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py index 06ccf3e..cc886b7 100644 --- a/tests/unit/test_tclean_config.py +++ b/tests/unit/test_tclean_config.py @@ -3,6 +3,7 @@ import pandas as pd import pytest from _tclean_config import ( + CONSTRUCTED_SOURCE_NAME, build_advanced_rules, build_all_constructed_source_periods, build_basic_rules, @@ -31,7 +32,7 @@ def test_build_time_grid_translates_temporal_scope() -> None: def test_build_basic_rules_returns_no_rules_when_mode_is_off() -> None: """Test basic rules returns nothing when disabled.""" - config = {"mode": "off", "basic": {"rules": []},"data_quality": {"tests": []},} + config = {"mode": "off", "basic": {"rules": []}, "data_quality": {"tests": []}} assert build_basic_rules(config) == [] @@ -46,7 +47,7 @@ def test_build_basic_rules_preserves_configured_rule_order() -> None: "source_offsets": ["-24h"], }, ] - config = {"mode": "basic", "basic": {"rules": rules},"data_quality": {"tests": []},} + config = {"mode": "basic", "basic": {"rules": rules}, "data_quality": {"tests": []}} assert build_basic_rules(config) == rules @@ -369,10 +370,7 @@ def test_build_data_quality_tests_translates_countries_to_contexts() -> None: "name": "country_range", "method": "range", "countries": ["ALB", "MNE"], - "minimum": { - "value_mode": "fixed", - "value": 0, - }, + "minimum": {"value_mode": "fixed", "value": 0}, } ] } @@ -384,31 +382,37 @@ def test_build_data_quality_tests_translates_countries_to_contexts() -> None: "name": "country_range", "method": "range", "contexts": ["ALB", "MNE"], - "minimum": { - "value_mode": "fixed", - "value": 0, - }, + "sources": [CONSTRUCTED_SOURCE_NAME], + "minimum": {"value_mode": "fixed", "value": 0}, } ] -def test_build_data_quality_tests_preserves_sources() -> None: - """Preserve configured T-Clean source selectors.""" +def test_build_data_quality_tests_defaults_to_constructed_source() -> None: + """Apply data-quality tests to constructed demand by default.""" config = { "tests": [ { - "name": "power_statistics_range", + "name": "non_negative", "method": "range", - "sources": ["entsoe_power_statistics"], - "minimum": { - "value_mode": "fixed", - "value": 0, - }, + "minimum": {"value_mode": "fixed", "value": 0}, } ] } result = build_data_quality_tests(config) - assert result[0]["sources"] == ["entsoe_power_statistics"] + assert result[0]["sources"] == [CONSTRUCTED_SOURCE_NAME] + + +def test_build_data_quality_tests_preserves_sources() -> None: + """Preserve explicitly configured source selectors.""" + config = { + "tests": [ + {"name": "source_check", "method": "range", "sources": ["entsoe", "opsd"]} + ] + } + + result = build_data_quality_tests(config) + assert result[0]["sources"] == ["entsoe", "opsd"] diff --git a/tests/unit/test_validate_data_quality_config.py b/tests/unit/test_validate_data_quality_config.py new file mode 100644 index 0000000..34eaeaf --- /dev/null +++ b/tests/unit/test_validate_data_quality_config.py @@ -0,0 +1,41 @@ +"""Tests for semantic module configuration validation.""" + +import pytest +from validate_config import validate_data_quality_config_semantics + + +def _config_with_data_quality(tests: list[dict]) -> dict: + """Return minimal configuration for data-quality semantic validation.""" + return { + "temporal_scope": { + "start": "2020-01-01", + "end": "2020-01-03", + "frequency": "1h", + }, + "data_quality": {"tests": tests}, + } + + +def test_data_quality_semantics_accept_valid_range_test() -> None: + """Accept a valid module data-quality test through T-Clean.""" + config = _config_with_data_quality( + [ + { + "name": "non_negative", + "method": "range", + "minimum": {"value_mode": "fixed", "value": 0}, + } + ] + ) + + validate_data_quality_config_semantics(config) + + +def test_data_quality_semantics_reject_invalid_value_spec() -> None: + """Reject malformed method configuration through T-Clean.""" + config = _config_with_data_quality( + [{"name": "non_negative", "method": "range", "minimum": 0}] + ) + + with pytest.raises(ValueError, match="value specification"): + validate_data_quality_config_semantics(config) diff --git a/workflow/Snakefile b/workflow/Snakefile index 2a602e7..4234d37 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -66,6 +66,7 @@ include: "rules/_source_helpers.smk" include: "rules/automatic.smk" include: "rules/basic.smk" include: "rules/advanced.smk" +include: "rules/data_quality.smk" # Source-specific workflow rules. diff --git a/workflow/rules/data_quality.smk b/workflow/rules/data_quality.smk new file mode 100644 index 0000000..5f3657e --- /dev/null +++ b/workflow/rules/data_quality.smk @@ -0,0 +1,23 @@ +"""Rules for electricity-demand data-quality evaluation.""" + + +rule evaluate_data_quality: + input: + validation="/automatic/data_quality_config_validation.json", + demand=final_clean_demand_input, + load_inputs=configured_load_inputs, + output: + failures="/automatic/{shape}/load_data_quality_failures.parquet", + issues="/automatic/{shape}/load_data_quality_issues.parquet", + log: + "/{shape}/evaluate_data_quality.log", + conda: + "../envs/module.yaml" + params: + source_names=active_load_sources, + temporal_scope=config["temporal_scope"], + data_quality=config["data_quality"], + message: + "Evaluate electricity-demand data quality." + script: + "../scripts/evaluate_data_quality.py" \ No newline at end of file diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index 4acffb4..e747daf 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -3,6 +3,8 @@ rule finalise_clean_demand: target_plan=target_data_plan, demand=final_clean_demand_input, cleaning_method=final_cleaning_method_input, + data_quality_failures=rules.evaluate_data_quality.output.failures, + data_quality_issues=rules.evaluate_data_quality.output.issues, output: demand=("/automatic/{shape}/load_cleaned.parquet"), cleaning_method=( diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index 11e8506..3286c16 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -12,6 +12,8 @@ ) from tclean import TimeGrid +CONSTRUCTED_SOURCE_NAME = "processed_demand" + def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: """Build the canonical T-Clean time grid.""" @@ -35,6 +37,9 @@ def build_data_quality_tests( if countries is not None: test["contexts"] = list(countries) + if "sources" not in test: + test["sources"] = [CONSTRUCTED_SOURCE_NAME] + tests.append(test) return tests diff --git a/workflow/scripts/evaluate_data_quality.py b/workflow/scripts/evaluate_data_quality.py new file mode 100644 index 0000000..2b055ed --- /dev/null +++ b/workflow/scripts/evaluate_data_quality.py @@ -0,0 +1,73 @@ +"""Evaluate data quality of constructed electricity demand.""" + +from collections.abc import Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pandas as pd +from _prepared_data import read_prepared_source +from _tclean_config import ( + CONSTRUCTED_SOURCE_NAME, + build_data_quality_tests, + build_time_grid, +) +from tclean import TimeGrid +from tclean.data_quality import evaluate + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Evaluate configured data-quality tests.""" + source_names = list(snakemake.params.source_names) + input_paths = list(snakemake.input.load_inputs) + + grid = build_time_grid(snakemake.params.temporal_scope) + + constructed = read_prepared_source(snakemake.input.demand) + + sources = _build_evaluation_sources( + constructed, source_names=source_names, input_paths=input_paths, grid=grid + ) + + tests = build_data_quality_tests(snakemake.params.data_quality) + + evaluation = evaluate(sources, tests=tests, grid=grid) + + evaluation.failures.to_parquet(snakemake.output.failures, index=False) + + evaluation.issues.to_parquet(snakemake.output.issues, index=False) + + +def _build_evaluation_sources( + constructed: pd.DataFrame, + *, + source_names: Sequence[str], + input_paths: Sequence[str | Path], + grid: TimeGrid, +) -> dict[str, pd.DataFrame]: + """Build aligned sources for data-quality evaluation.""" + if len(input_paths) != len(source_names): + raise ValueError( + "The number of prepared load inputs must match the " + "number of configured load sources." + ) + + if CONSTRUCTED_SOURCE_NAME in source_names: + raise ValueError( + f"{CONSTRUCTED_SOURCE_NAME!r} is reserved for constructed demand." + ) + + providers = { + source_name: read_prepared_source(path).reindex( + index=grid.target_index, columns=constructed.columns + ) + for source_name, path in zip(source_names, input_paths, strict=True) + } + + return {CONSTRUCTED_SOURCE_NAME: constructed, **providers} + + +if __name__ == "__main__": + main(snakemake) diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 6861217..dce9db6 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -218,4 +218,4 @@ def write_validation_marker( else: raise ValueError(f"Unsupported validation kind {validation_kind!r}.") - write_validation_marker(snakemake.output[0], validated_config=validation_config) \ No newline at end of file + write_validation_marker(snakemake.output[0], validated_config=validation_config) From 43968fa92561926185658c326564118c908ee8a7 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 8 Sep 2026 20:26:52 +0200 Subject: [PATCH 228/241] Added data quality plot. --- tests/integration/test_config.yaml | 17 +- workflow/Snakefile | 1 + workflow/rules/data_quality.smk | 23 +- .../scripts/_plot_data_quality_timeline.py | 507 ++++++++++++++++++ .../scripts/plot_data_quality_timeline.py | 16 + 5 files changed, 558 insertions(+), 6 deletions(-) create mode 100644 workflow/scripts/_plot_data_quality_timeline.py create mode 100644 workflow/scripts/plot_data_quality_timeline.py diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index a0d60df..01184b2 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -180,8 +180,15 @@ integration_test: data_quality: tests: - - name: non_negative - method: range - minimum: - value_mode: fixed - value: 0 \ No newline at end of file + - name: non_negative + method: range + minimum: + value_mode: fixed + value: 0 + + - name: source_disagreement_example + method: source_disagreement + difference_mode: relative + threshold: + value_mode: fixed + value: 0.01 \ No newline at end of file diff --git a/workflow/Snakefile b/workflow/Snakefile index 4234d37..c296742 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -44,6 +44,7 @@ workflow.source_path("scripts/_plot_timeline.py") workflow.source_path("scripts/_prepared_data.py") workflow.source_path("scripts/_source_capabilities.py") workflow.source_path("scripts/_tclean_config.py") +workflow.source_path("scripts/_plot_data_quality_timeline.py") # Register source-specific python scripts for remote module use. for source in SOURCE_REGISTRY.values(): for python_file in source["workflow"]["python"]: diff --git a/workflow/rules/data_quality.smk b/workflow/rules/data_quality.smk index 5f3657e..ca7073c 100644 --- a/workflow/rules/data_quality.smk +++ b/workflow/rules/data_quality.smk @@ -20,4 +20,25 @@ rule evaluate_data_quality: message: "Evaluate electricity-demand data quality." script: - "../scripts/evaluate_data_quality.py" \ No newline at end of file + "../scripts/evaluate_data_quality.py" + + +rule plot_data_quality_timeline: + input: + demand=( + "/automatic/{shape}/load_cleaned.parquet" + ), + failures=rules.evaluate_data_quality.output.failures, + output: + plot=( + "/{shape}/" + "load_data_quality_timeline.pdf" + ), + log: + "/{shape}/plot_data_quality_timeline.log", + conda: + "../envs/module.yaml" + message: + "Plot electricity-demand data-quality failures." + script: + "../scripts/plot_data_quality_timeline.py" \ No newline at end of file diff --git a/workflow/scripts/_plot_data_quality_timeline.py b/workflow/scripts/_plot_data_quality_timeline.py new file mode 100644 index 0000000..dabea8f --- /dev/null +++ b/workflow/scripts/_plot_data_quality_timeline.py @@ -0,0 +1,507 @@ +"""Plot electricity demand with data-quality failure annotations.""" + +import logging +from pathlib import Path + +import matplotlib.dates as mdates +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from _tclean_config import CONSTRUCTED_SOURCE_NAME +from cmap import Colormap +from matplotlib.backends.backend_pdf import PdfPages +from matplotlib.lines import Line2D +from matplotlib.patches import Rectangle + +logger = logging.getLogger(__name__) + +FIGURE_DPI = 100 + +PAGE_WIDTH_PX = 750 +CONTEXTS_PER_PAGE = 20 + +PAGE_LEFT_MARGIN_PX = 68 +PAGE_RIGHT_MARGIN_PX = 38 +PAGE_TOP_MARGIN_PX = 72 +PAGE_BOTTOM_MARGIN_PX = 42 +LEGEND_HEIGHT_PX = 42 + +PLOT_WIDTH_PX = PAGE_WIDTH_PX - PAGE_LEFT_MARGIN_PX - PAGE_RIGHT_MARGIN_PX + +BASE_ROW_HEIGHT_PX = 32 +TRACE_HALF_HEIGHT_PX = 10 +BOX_HALF_HEIGHT_PX = 12 + +MARKER_GAP_PX = 2 +MARKER_LEVEL_SPACING_PX = 3 + +# A failure must occupy at least this much rendered horizontal space +# before an outlined interval box is useful. +BOX_MIN_WIDTH_PX = 4 + + +def main( + *, demand_path: str | Path, failures_path: str | Path, output_path: str | Path +) -> None: + """Create the electricity-demand data-quality diagnostic.""" + demand = pd.read_parquet(demand_path) + failures = pd.read_parquet(failures_path) + + _validate_demand(demand) + + time_step = _infer_time_step(demand) + plot_start = demand.index[0] + plot_end = demand.index[-1] + time_step + + failures = _prepare_failures( + failures, demand=demand, plot_start=plot_start, plot_end=plot_end + ) + + if not failures.empty: + failures["display_width_px"] = ( + ((failures["end"] - failures["start"]) / time_step) + / len(demand.index) + * PLOT_WIDTH_PX + ) + + method_colours = _build_method_colours(failures) + + logger.info( + "Plotting %s constructed-source data-quality failure periods " + "across %s contexts.", + len(failures), + failures["context"].nunique() if not failures.empty else 0, + ) + + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + _write_pdf( + demand=demand, + failures=failures, + method_colours=method_colours, + plot_start=plot_start, + plot_end=plot_end, + output_path=output_path, + ) + + logger.info("Saved data-quality timeline to %s.", output_path) + + +def _validate_demand(demand: pd.DataFrame) -> None: + """Require a plottable time-by-context demand frame.""" + if not isinstance(demand.index, pd.DatetimeIndex): + raise ValueError("Demand must use a DatetimeIndex.") + + if len(demand.index) < 2: + raise ValueError("At least two timestamps are required to plot data quality.") + + if demand.columns.empty: + raise ValueError("At least one context is required to plot data quality.") + + if not demand.index.is_monotonic_increasing: + raise ValueError("Demand timestamps must be monotonically increasing.") + + +def _infer_time_step(demand: pd.DataFrame) -> pd.Timedelta: + """Infer the regular time step represented by the demand frame.""" + time_step = demand.index.to_series().diff().dropna().median() + + if pd.isna(time_step) or time_step <= pd.Timedelta(0): + raise ValueError("Could not determine a valid temporal resolution.") + + return time_step + + +def _prepare_failures( + failures: pd.DataFrame, + *, + demand: pd.DataFrame, + plot_start: pd.Timestamp, + plot_end: pd.Timestamp, +) -> pd.DataFrame: + """Keep constructed-demand failures relevant to the plotted demand.""" + required_columns = {"context", "source", "start", "end", "method"} + + missing_columns = required_columns - set(failures.columns) + + if missing_columns: + raise ValueError( + "Data-quality failures are missing required columns: " + f"{sorted(missing_columns)!r}." + ) + + selected = failures.loc[failures["source"].eq(CONSTRUCTED_SOURCE_NAME)].copy() + + if selected.empty: + return selected.reset_index(drop=True) + + selected["start"] = pd.to_datetime(selected["start"], utc=True) + selected["end"] = pd.to_datetime(selected["end"], utc=True) + + unknown_contexts = sorted( + set(selected["context"].astype(str)) - set(demand.columns) + ) + + if unknown_contexts: + raise ValueError( + "Data-quality failures reference contexts absent from demand: " + f"{unknown_contexts!r}." + ) + + invalid_periods = selected["end"].le(selected["start"]) + + if invalid_periods.any(): + raise ValueError( + "Data-quality failures must use positive [start, end) periods." + ) + + outside_plot = selected["start"].lt(plot_start) | selected["end"].gt(plot_end) + + if outside_plot.any(): + raise ValueError( + "Data-quality failures extend outside the plotted demand period." + ) + + return selected.reset_index(drop=True) + + +def _build_method_colours( + failures: pd.DataFrame, +) -> dict[str, tuple[float, float, float, float]]: + """Assign one colour to each observed data-quality method.""" + if failures.empty: + return {} + + methods = sorted(failures["method"].astype(str).unique()) + + colourtheme = Colormap("bids:viridis").to_mpl() + + if len(methods) == 1: + positions = [0.5] + else: + positions = np.linspace(0.08, 0.92, len(methods)) + + return { + method: colourtheme(position) + for method, position in zip(methods, positions, strict=True) + } + + +def _marker_levels(failures: pd.DataFrame) -> dict[int, int]: + """Assign the lowest level that does not overlap another failure period.""" + if failures.empty: + return {} + + intervals = sorted( + ( + (failure.Index, failure.start, failure.end) + for failure in failures.itertuples() + ), + key=lambda interval: (interval[1], interval[2]), + ) + + level_ends: list[pd.Timestamp] = [] + levels: dict[int, int] = {} + + for failure_index, start, end in intervals: + for level, previous_end in enumerate(level_ends): + # Failure periods are [start, end), so touching periods + # do not overlap and may share the same marker level. + if start >= previous_end: + levels[failure_index] = level + level_ends[level] = end + break + else: + levels[failure_index] = len(level_ends) + level_ends.append(end) + + return levels + + +def _build_row_layout( + *, contexts: list[str], failures: pd.DataFrame +) -> tuple[pd.DataFrame, dict[int, int]]: + """Allocate vertical space for traces and short failure markers.""" + rows: list[dict[str, float | str]] = [] + all_marker_levels: dict[int, int] = {} + + cursor = 0.0 + + for context in contexts: + context_failures = failures.loc[failures["context"].eq(context)] + + short_failures = context_failures.loc[ + context_failures["display_width_px"].lt(BOX_MIN_WIDTH_PX) + ] + + marker_levels = _marker_levels(short_failures) + + all_marker_levels.update(marker_levels) + + level_count = max(marker_levels.values()) + 1 if marker_levels else 0 + + marker_space = 0.0 + + if level_count: + marker_space = MARKER_GAP_PX + level_count * MARKER_LEVEL_SPACING_PX + + row_height = BASE_ROW_HEIGHT_PX + marker_space + centre = cursor + marker_space + BASE_ROW_HEIGHT_PX / 2 + + rows.append( + { + "context": context, + "start": cursor, + "centre": centre, + "end": cursor + row_height, + } + ) + + cursor += row_height + + layout = pd.DataFrame(rows).set_index("context") + + return layout, all_marker_levels + + +def _write_pdf( + *, + demand: pd.DataFrame, + failures: pd.DataFrame, + method_colours: dict[str, tuple[float, float, float, float]], + plot_start: pd.Timestamp, + plot_end: pd.Timestamp, + output_path: Path, +) -> None: + """Write one or more context pages to the data-quality PDF.""" + contexts = list(demand.columns) + + context_slices = [ + slice(start, min(start + CONTEXTS_PER_PAGE, len(contexts))) + for start in range(0, len(contexts), CONTEXTS_PER_PAGE) + ] + + with PdfPages(output_path) as pdf: + for page_index, context_slice in enumerate(context_slices): + page_contexts = contexts[context_slice] + page_demand = demand.loc[:, page_contexts] + + page_failures = failures.loc[failures["context"].isin(page_contexts)] + + layout, marker_levels = _build_row_layout( + contexts=page_contexts, failures=page_failures + ) + + figure, axis = _plot_page( + demand=page_demand, + layout=layout, + method_colours=method_colours, + page_index=page_index, + page_count=len(context_slices), + plot_start=plot_start, + plot_end=plot_end, + ) + + _add_normalised_demand_traces(axis=axis, demand=page_demand, layout=layout) + + _add_failure_annotations( + axis=axis, + failures=page_failures, + layout=layout, + marker_levels=marker_levels, + method_colours=method_colours, + ) + + pdf.savefig(figure) + plt.close(figure) + + +def _plot_page( + *, + demand: pd.DataFrame, + layout: pd.DataFrame, + method_colours: dict[str, tuple[float, float, float, float]], + page_index: int, + page_count: int, + plot_start: pd.Timestamp, + plot_end: pd.Timestamp, +) -> tuple[plt.Figure, plt.Axes]: + """Create one stacked-context data-quality page.""" + panel_height_px = int(np.ceil(layout["end"].iloc[-1])) + + legend_height_px = LEGEND_HEIGHT_PX if method_colours else 0 + + page_height_px = ( + PAGE_TOP_MARGIN_PX + panel_height_px + legend_height_px + PAGE_BOTTOM_MARGIN_PX + ) + + figure = plt.figure( + figsize=(PAGE_WIDTH_PX / FIGURE_DPI, page_height_px / FIGURE_DPI), + dpi=FIGURE_DPI, + ) + + axis_bottom_px = PAGE_BOTTOM_MARGIN_PX + legend_height_px + + axis = figure.add_axes( + [ + PAGE_LEFT_MARGIN_PX / PAGE_WIDTH_PX, + axis_bottom_px / page_height_px, + PLOT_WIDTH_PX / PAGE_WIDTH_PX, + panel_height_px / page_height_px, + ] + ) + + axis.set_xlim(plot_start, plot_end) + axis.set_ylim(panel_height_px, 0) + + axis.set_yticks(layout["centre"].to_numpy()) + axis.set_yticklabels(demand.columns, fontsize=7) + + for boundary in layout["start"]: + axis.axhline(boundary, linewidth=0.4, alpha=0.3, color="0.5", zorder=0) + + axis.axhline( + layout["end"].iloc[-1], linewidth=0.4, alpha=0.3, color="0.5", zorder=0 + ) + + axis.set_xlabel("Date-Time") + axis.set_ylabel("Country") + + date_locator = mdates.AutoDateLocator(minticks=4, maxticks=8) + axis.xaxis.set_major_locator(date_locator) + axis.xaxis.set_major_formatter( + mdates.ConciseDateFormatter(date_locator, show_offset=False) + ) + axis.tick_params(axis="x", labelsize=7) + + figure.text( + 0.5, + 1.0 - (28 / page_height_px), + "Electricity demand and data-quality failures", + ha="center", + va="center", + fontsize=11, + ) + + if page_count > 1: + figure.text( + 1.0 - (PAGE_RIGHT_MARGIN_PX / PAGE_WIDTH_PX), + 1.0 - (50 / page_height_px), + f"Page {page_index + 1} of {page_count}", + ha="right", + va="center", + fontsize=6.5, + color="0.4", + ) + + if method_colours: + handles = [ + Line2D( + [0], [0], color=colour, linewidth=2.2, label=_format_method_name(method) + ) + for method, colour in method_colours.items() + ] + + figure.legend( + handles=handles, + loc="lower center", + bbox_to_anchor=(0.5, 8 / page_height_px), + frameon=False, + ncol=min(3, len(handles)), + fontsize=6.5, + handlelength=2.0, + columnspacing=1.0, + ) + + return figure, axis + + +def _add_normalised_demand_traces( + *, + axis: plt.Axes, + demand: pd.DataFrame, + layout: pd.DataFrame, + quantile: float = 0.99, +) -> None: + """Overlay mean-normalised demand traces.""" + for context in demand.columns: + series = demand[context].astype(float) + centre = float(layout.loc[context, "centre"]) + + mean_load = series.mean(skipna=True) + + if pd.isna(mean_load) or mean_load == 0: + continue + + relative = (series / mean_load) - 1 + scale = relative.abs().quantile(quantile) + + if pd.isna(scale) or scale == 0: + plotted_y = pd.Series(centre, index=series.index, dtype=float) + else: + scaled = relative.clip(lower=-scale, upper=scale) / scale + + plotted_y = centre - scaled * TRACE_HALF_HEIGHT_PX + + axis.plot( + series.index, plotted_y, color="black", linewidth=0.55, alpha=0.9, zorder=3 + ) + + +def _add_failure_annotations( + *, + axis: plt.Axes, + failures: pd.DataFrame, + layout: pd.DataFrame, + marker_levels: dict[int, int], + method_colours: dict[str, tuple[float, float, float, float]], +) -> None: + """Overlay outlined periods and staggered short-period markers.""" + for failure in failures.itertuples(): + centre = float(layout.loc[failure.context, "centre"]) + colour = method_colours[str(failure.method)] + + if failure.display_width_px >= BOX_MIN_WIDTH_PX: + start_num = mdates.date2num(failure.start) + end_num = mdates.date2num(failure.end) + + axis.add_patch( + Rectangle( + (start_num, centre - BOX_HALF_HEIGHT_PX), + end_num - start_num, + 2 * BOX_HALF_HEIGHT_PX, + facecolor="none", + edgecolor=colour, + linewidth=1.1, + zorder=4, + ) + ) + + continue + + level = marker_levels[failure.Index] + + marker_y = ( + centre + - BOX_HALF_HEIGHT_PX + - MARKER_GAP_PX + - level * MARKER_LEVEL_SPACING_PX + ) + + # The endpoints remain the true [start, end) period. + # Round caps stop extremely short vector segments from + # disappearing entirely at normal viewing scales. + axis.plot( + [failure.start, failure.end], + [marker_y, marker_y], + color=colour, + linewidth=2.2, + solid_capstyle="round", + zorder=5, + ) + + +def _format_method_name(method: str) -> str: + """Format a data-quality method for the legend.""" + return method.replace("_", " ").title() diff --git a/workflow/scripts/plot_data_quality_timeline.py b/workflow/scripts/plot_data_quality_timeline.py new file mode 100644 index 0000000..a8a04c5 --- /dev/null +++ b/workflow/scripts/plot_data_quality_timeline.py @@ -0,0 +1,16 @@ +"""Plot electricity-demand data-quality failures.""" + +import logging +import sys + +from _plot_data_quality_timeline import main + +sys.stderr = open(snakemake.log[0], "w", buffering=1) + +logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + +main( + demand_path=snakemake.input.demand, + failures_path=snakemake.input.failures, + output_path=snakemake.output.plot, +) From ddb9a9e37f1e27eb8ce6311c539e961d2ce919ab Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 13:33:33 +0200 Subject: [PATCH 229/241] Removing legacy patch. --- ...demand_electricity_tclean_0_2_rewire.patch | 195 ------------------ 1 file changed, 195 deletions(-) delete mode 100644 module_demand_electricity_tclean_0_2_rewire.patch diff --git a/module_demand_electricity_tclean_0_2_rewire.patch b/module_demand_electricity_tclean_0_2_rewire.patch deleted file mode 100644 index ef4c9b0..0000000 --- a/module_demand_electricity_tclean_0_2_rewire.patch +++ /dev/null @@ -1,195 +0,0 @@ -diff --git a/pixi.toml b/pixi.toml ---- a/pixi.toml -+++ b/pixi.toml -@@ -5,6 +5,7 @@ description = "This module prepares electricity demand timeseries for Europe at - license = "Apache-2.0" - readme = "README.md" - channels = [ -+ "https://conda.anaconda.org/ddahawkins/label/dev", - "conda-forge", - "bioconda", - ] -@@ -42,7 +43,7 @@ matplotlib = ">=3.10.9" - rioxarray = ">=0.20.0" - pandera = ">=0.31.1" - pyyaml = ">=6.0.3" --tclean = ">=0.1.1" -+tclean = "==0.2.0" - - [feature.test-module.dependencies] - pytest = ">=8.3.5" -diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py ---- a/workflow/scripts/_tclean_config.py -+++ b/workflow/scripts/_tclean_config.py -@@ -10,7 +10,7 @@ from _source_capabilities import ( - intersect_source_temporal_scope, - uncovered_temporal_intervals, - ) --from tclean import TCleanConfig, TimeGrid -+from tclean import TimeGrid - - - def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: -@@ -22,11 +22,6 @@ def build_time_grid(temporal_scope: Mapping[str, Any]) -> TimeGrid: - ) - - --def build_tclean_config(temporal_scope: Mapping[str, Any]) -> TCleanConfig: -- """Build the T-Clean configuration used for demand cleaning.""" -- return TCleanConfig(grid=build_time_grid(temporal_scope)) -- -- - def build_basic_rules(gap_filling_config: Mapping[str, Any]) -> list[dict[str, Any]]: - """Return configured basic-cleaning rules for T-Clean.""" - if gap_filling_config["mode"] == "off": -diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py ---- a/workflow/scripts/apply_advanced_overrides.py -+++ b/workflow/scripts/apply_advanced_overrides.py -@@ -5,8 +5,7 @@ from pathlib import Path - import pandas as pd - from _advanced_execution import load_execution_plan - from tclean import TimeGrid --from tclean.advanced import read_external_profile --from tclean.advanced.apply import apply_advanced_rules -+from tclean.gap_filling import apply_advanced_rules, read_external_profile - - plan = load_execution_plan(snakemake.input.plan) - -diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py ---- a/workflow/scripts/clean_auxiliary_group.py -+++ b/workflow/scripts/clean_auxiliary_group.py -@@ -5,7 +5,8 @@ from typing import TYPE_CHECKING, Any - import pandas as pd - from _advanced_execution import load_execution_plan - from _prepared_data import read_prepared_source --from tclean import TCleanConfig, TimeGrid, clean -+from tclean import TimeGrid -+from tclean.gap_filling import fill_gaps - - if TYPE_CHECKING: - snakemake: Any -@@ -46,8 +47,6 @@ def main(snakemake: Any) -> None: - start=group_start, end=group_end, frequency=(snakemake.params.frequency) - ) - -- config = TCleanConfig(grid=grid) -- - source_paths = list(snakemake.input.sources) - - if len(source_paths) != len(batches): -@@ -86,8 +85,8 @@ def main(snakemake: Any) -> None: - else [] - ) - -- (cleaned, data_source, cleaning_method) = clean( -- sources, config=config, basic_rules=basic_rules -+ (cleaned, data_source, cleaning_method) = fill_gaps( -+ sources, grid=grid, basic_rules=basic_rules - ) - - cleaned.to_parquet(snakemake.output.demand) -diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py ---- a/workflow/scripts/clean_demand.py -+++ b/workflow/scripts/clean_demand.py -@@ -7,10 +7,13 @@ from typing import TYPE_CHECKING, Any - - import pandas as pd - from _prepared_data import read_prepared_source --from _tclean_config import build_basic_rules, build_tclean_config --from tclean import clean --from tclean.advanced import build_gap_report --from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -+from _tclean_config import build_basic_rules, build_time_grid -+from tclean.gap_filling import ( -+ build_cleaning_method_ranks, -+ build_gap_report, -+ derive_cleaning_method_rank, -+ fill_gaps, -+) - - if TYPE_CHECKING: - snakemake: Any -@@ -38,19 +41,17 @@ def main(snakemake: Any) -> None: - - target_contexts = _read_target_contexts(snakemake.input.target_plan) - -- config = build_tclean_config(snakemake.params.temporal_scope) -+ grid = build_time_grid(snakemake.params.temporal_scope) - - sources = { -- source_name: data.reindex( -- index=config.grid.target_index, columns=target_contexts -- ) -+ source_name: data.reindex(index=grid.target_index, columns=target_contexts) - for source_name, data in sources.items() - } - - basic_rules = build_basic_rules(snakemake.params.gap_filling) - -- (cleaned, data_source, cleaning_method) = clean( -- sources, config=config, basic_rules=basic_rules -+ (cleaned, data_source, cleaning_method) = fill_gaps( -+ sources, grid=grid, basic_rules=basic_rules - ) - - basic_rule_names = [rule["name"] for rule in basic_rules] -@@ -65,7 +66,7 @@ def main(snakemake: Any) -> None: - - gap_report = build_gap_report( - cleaned, -- grid=config.grid, -+ grid=grid, - enabled=(snakemake.params.gap_filling["mode"] == "advanced"), - ) - -diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py ---- a/workflow/scripts/construct_auxiliary_profile.py -+++ b/workflow/scripts/construct_auxiliary_profile.py -@@ -7,7 +7,7 @@ from _tclean_config import ( - build_scaling_source_periods, - ) - from tclean import TimeGrid --from tclean.advanced import construct_from_sources -+from tclean.gap_filling import construct_from_sources - - plan = load_execution_plan(snakemake.input.plan) - -diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py ---- a/workflow/scripts/finalise_clean_demand.py -+++ b/workflow/scripts/finalise_clean_demand.py -@@ -4,7 +4,10 @@ import shutil - - import pandas as pd - from _tclean_config import build_advanced_rules, build_basic_rules --from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -+from tclean.gap_filling import ( -+ build_cleaning_method_ranks, -+ derive_cleaning_method_rank, -+) - - shutil.copyfile(snakemake.input.demand, snakemake.output.demand) - -diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py ---- a/workflow/scripts/plan_auxiliary_data.py -+++ b/workflow/scripts/plan_auxiliary_data.py -@@ -24,7 +24,7 @@ from _tclean_config import ( - filter_source_requests_by_temporal_scope, - get_advanced_source_definitions, - ) --from tclean.advanced import ( -+from tclean.gap_filling import ( - build_auxiliary_acquisition_requirements, - build_auxiliary_source_requests, - select_active_advanced_rules, -diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py ---- a/workflow/scripts/validate_config.py -+++ b/workflow/scripts/validate_config.py -@@ -15,7 +15,7 @@ from _tclean_config import ( - build_time_grid, - ) - from tclean import TimeGrid --from tclean.basic import validate_basic_rules -+from tclean.gap_filling import validate_basic_rules - - if TYPE_CHECKING: - snakemake: Any From cd1b408a94cb661cfc1ccfd4d2529e1be41d2e8b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 13:33:52 +0200 Subject: [PATCH 230/241] Updated module for tclean 0.2.1 --- pixi.lock | 22 +++++++++++----------- pixi.toml | 2 +- workflow/envs/module.linux-64.pin.txt | 2 +- workflow/envs/module.osx-arm64.pin.txt | 2 +- workflow/envs/module.win-64.pin.txt | 2 +- workflow/envs/module.yaml | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pixi.lock b/pixi.lock index 6e8ef7c..09d5e53 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1130,7 +1130,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -1375,7 +1375,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -1624,7 +1624,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda test: channels: - url: https://conda.anaconda.org/ddahawkins/label/dev/ @@ -1939,7 +1939,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2195,7 +2195,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2455,7 +2455,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda + - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 @@ -16883,9 +16883,9 @@ packages: - zstd >=1.5.7,<1.6.0a0 size: 387535 timestamp: 1786599623274 -- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda - sha256: 8ffe74fb71692b082b46c94b3a7af2bc2f831a686a4d7bd1066409dda176d65c - md5: 86235f3d91aa02c8d629f225cbe93cf3 +- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda + sha256: fb5696124a0f612c17140941a33b2f628ebf7112c19ee32508896924f5acadf8 + md5: 94b960989bc00859320a84abe9f5df2d depends: - python >=3.12 - pandas >=3.0.3 @@ -16893,5 +16893,5 @@ packages: - scipy >=1.16.1 - python license: MIT - size: 71113 - timestamp: 1788872612273 + size: 68726 + timestamp: 1788952697985 diff --git a/pixi.toml b/pixi.toml index 27add8f..b397ac0 100644 --- a/pixi.toml +++ b/pixi.toml @@ -43,7 +43,7 @@ matplotlib = ">=3.10.9" rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" -tclean = ">=0.2.0" +tclean = ">=0.2.1" [feature.test-module.dependencies] pytest = ">=8.3.5" diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 961d86b..60819db 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -49,7 +49,7 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda#85eb29ade84d1bd97be5ec55607efb00 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda#e15caa7ad9be633c3cba5a7937b117b7 diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index da3c108..d81d99c 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -46,7 +46,7 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda#b1dde16791eca59c8316d0ad1487bff0 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda#8fc44bfcc973b7cab443539ce858161a diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index bde9528..d4c1830 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -50,7 +50,7 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda#a519b50147401cf6bad2fd188028ad38 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.0-pyh129be3f_0.conda#86235f3d91aa02c8d629f225cbe93cf3 +https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda#a92f6eff943133d045db4d017ac4297c diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index 7cb44ab..f05d858 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -21,4 +21,4 @@ dependencies: - rioxarray >=0.20.0 - pandera >=0.31.1 - pyyaml >=6.0.3 -- tclean >=0.2.0 +- tclean >=0.2.1 From 4782a27843b3d51b1a49b5249ac18a3d5e189ad5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:22:10 +0200 Subject: [PATCH 231/241] Added logging. --- workflow/scripts/evaluate_data_quality.py | 53 ++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/workflow/scripts/evaluate_data_quality.py b/workflow/scripts/evaluate_data_quality.py index 2b055ed..80546d4 100644 --- a/workflow/scripts/evaluate_data_quality.py +++ b/workflow/scripts/evaluate_data_quality.py @@ -1,5 +1,6 @@ """Evaluate data quality of constructed electricity demand.""" +import logging from collections.abc import Sequence from pathlib import Path from typing import TYPE_CHECKING, Any @@ -18,6 +19,48 @@ snakemake: Any +class _ExactLevelFilter(logging.Filter): + """Allow only one exact logging level through a handler.""" + + def __init__(self, level: int) -> None: + super().__init__() + self.level = level + + def filter(self, record: logging.LogRecord) -> bool: + return record.levelno == self.level + + +def configure_tclean_data_quality_logging(log_path: str | Path | None) -> None: + """Send INFO to terminal and full tclean data-quality logs to a file.""" + tclean_logger = logging.getLogger("tclean.data_quality") + tclean_logger.setLevel(logging.DEBUG) + tclean_logger.handlers.clear() + tclean_logger.propagate = False + + # INFO only -> terminal + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + console_handler.addFilter(_ExactLevelFilter(logging.INFO)) + console_handler.setFormatter( + logging.Formatter( + "%(asctime)s | %(levelname)s | %(message)s", datefmt="%H:%M:%S" + ) + ) + tclean_logger.addHandler(console_handler) + + # DEBUG+ -> file + if log_path is not None: + file_handler = logging.FileHandler(log_path, mode="a", encoding="utf-8") + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter( + logging.Formatter( + "%(asctime)s | %(name)s | %(levelname)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + ) + tclean_logger.addHandler(file_handler) + + def main(snakemake: Any) -> None: """Evaluate configured data-quality tests.""" source_names = list(snakemake.params.source_names) @@ -33,7 +76,15 @@ def main(snakemake: Any) -> None: tests = build_data_quality_tests(snakemake.params.data_quality) - evaluation = evaluate(sources, tests=tests, grid=grid) + log_path = str(snakemake.log[0]) if snakemake.log else None + configure_tclean_data_quality_logging(log_path) + + evaluation = evaluate( + sources, + tests=tests, + grid=grid, + # threads=1, + ) evaluation.failures.to_parquet(snakemake.output.failures, index=False) From d5796d277fa1fa5071cdd7b33d52cbd0087e587f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:22:24 +0200 Subject: [PATCH 232/241] Made the quality chart part of the natural pipeline. --- workflow/rules/prepare_demand.smk | 1 + 1 file changed, 1 insertion(+) diff --git a/workflow/rules/prepare_demand.smk b/workflow/rules/prepare_demand.smk index e747daf..870d2be 100644 --- a/workflow/rules/prepare_demand.smk +++ b/workflow/rules/prepare_demand.smk @@ -51,6 +51,7 @@ rule demand_electricity_polygon: demand_raster="/automatic/{shape}/demand_electricity_raster.tif", demand_profiles="/automatic/{shape}/demand_electricity_countries_profiles.parquet", cleaning_timeline="/{shape}/load_cleaning_timeline.pdf", + data_quality_timeline="/{shape}/load_data_quality_timeline.pdf", shapes="", output: output_data="", From bc26094b8e060bd93f861b904bc4354d9653ae6f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:23:16 +0200 Subject: [PATCH 233/241] updated config files. --- config/config.yaml | 7 ++++++- tests/integration/test_config.yaml | 7 ------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index c04e54b..3809ecc 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -177,4 +177,9 @@ gap_filling: source: alb_external_test data_quality: - tests: [] \ No newline at end of file + tests: + - name: non_negative + method: range + minimum: + value_mode: fixed + value: 0 \ No newline at end of file diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 01184b2..caf1ea5 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -185,10 +185,3 @@ integration_test: minimum: value_mode: fixed value: 0 - - - name: source_disagreement_example - method: source_disagreement - difference_mode: relative - threshold: - value_mode: fixed - value: 0.01 \ No newline at end of file From 32ca2a93a4d90cd750c08b1a5f43577f65a32a11 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:43:21 +0200 Subject: [PATCH 234/241] Updated READMEs --- README.md | 38 +++++++++++++++----- config/README.md | 90 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5710596..eeb8b70 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This Modelblocks module prepares regular electricity-demand time series for European target regions. National demand observations from multiple providers are combined and cleaned on a user-defined time grid, then spatially disaggregated using population data and aggregated to user-provided shapes. -Demand cleaning is performed with tclean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs. +Demand cleaning and configurable data-quality evaluation are performed with tclean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs.

@@ -31,9 +31,10 @@ The main processing stages are: 3. Combine available providers according to the configured source-priority order. 4. Apply deterministic basic cleaning rules. 5. In `advanced` mode, determine which configured advanced rules are active for the current countries and time grid, acquire any required auxiliary demand data, construct or read advanced profiles, and apply them. -6. Finalise national demand together with cleaning provenance. -7. Download and prepare gridded population data. -8. Spatially disaggregate national demand using population weights and aggregate it to the user-provided target shapes. +6. Evaluate any configured data-quality tests against the processed demand and available provider sources. +7. Finalise national demand together with cleaning provenance and data-quality diagnostics. +8. Download and prepare gridded population data. +9. Spatially disaggregate national demand using population weights and aggregate it to the user-provided target shapes. A simplified representation is: @@ -56,10 +57,14 @@ Basic cleaning ▼ │ advanced rules ────────────────┤ ▼ - Final national demand + Processed national demand + provenance │ ▼ + Data-quality evaluation + (when tests are configured) + │ + ▼ Population-weighted spatial disaggregation │ @@ -75,7 +80,8 @@ The key configuration groups are: - `temporal_scope`: grid start, grid end, and fixed frequency; - `load_sources`: demand-provider priority; -- `gap_filling`: cleaning mode plus basic and advanced rules. +- `gap_filling`: cleaning mode plus basic and advanced rules; +- `data_quality`: ordered diagnostic tests applied to the processed demand and, where relevant, provider sources. See the [configuration README](./config/README.md), the [example configuration](./config/config.yaml), and the authoritative [configuration schema](./workflow/internal/config.schema.yaml). @@ -140,6 +146,19 @@ Configured periods use half-open intervals, `[start, end)`. See [Configuration: Advanced gap filling](./config/README.md#advanced-gap-filling) for full examples. + +## Data-quality evaluation + +The module can run ordered data-quality tests after the configured gap-filling stage and before spatial disaggregation. Evaluation is diagnostic: it records anomalous periods and evaluation limitations but does **not** alter the processed demand series. + +Data-quality methods and statistical semantics are provided by `tclean.data_quality`. The module adds the electricity-demand-specific orchestration around those methods, including source naming, configuration validation, persistence of failures/issues, and diagnostic plotting. See [Configuration: Data quality](./config/README.md#data-quality) for the module-facing configuration contract; method-level threshold and reference semantics are documented in tclean's `docs/data_quality.md`. + +The module exposes `processed_demand` as the main focal data-quality source. It represents the combined demand after the configured gap-filling stage (or the combined series when gap filling is off). Prepared provider sources selected through `load_sources` are also supplied to data-quality evaluation where available. This is particularly useful for `source_disagreement`: the processed series can be evaluated as the focal source while the underlying providers act as peer evidence for the same country and timestamp. + +Tests are evaluated in configuration order. This matters for reference-based tests because failures from preceding tests can affect the reference observations available to later tests. Optional source and country selectors can narrow individual tests; omitting the source selector uses `processed_demand` as the module's focal source by default. + +Some tests are more computationally intensive than simple pointwise checks. In particular, `contextual_level`, `contextual_profile`, and cross-source comparison over long, multi-country histories can take a few minutes to evaluate. This is expected for large diagnostic runs; users should not assume that a several-minute data-quality rule is stalled simply because simpler cleaning rules complete much faster. + ## Provenance and diagnostics The workflow retains cleaning provenance alongside national demand so observed values can be distinguished from values introduced by basic or advanced rules. @@ -149,7 +168,10 @@ Important diagnostic outputs include: - **Gap report**: in `advanced` mode, provides a complete record of the contiguous gaps that remain after basic cleaning, including the affected country, start and end timestamps, gap duration, and whether the gap reaches a boundary of the requested time series. This report can be used to identify which periods still require attention and to inform the design of targeted advanced rules; - **Cleaning method**: the source or rule responsible for each output value; - **Cleaning-method rank**: numeric ordering used to represent cleaning provenance consistently; -- **Cleaning timeline and summary**: visual and tabular diagnostics showing demand provenance and completeness through the raw, basic, and advanced cleaning stages. +- **Cleaning timeline and summary**: visual and tabular diagnostics showing demand provenance and completeness through the raw, basic, and advanced cleaning stages; +- **Data-quality failures**: structured periods where a configured test was evaluable and its failure criterion was met; +- **Data-quality issues**: structured warnings or `not_evaluable` events describing limitations such as insufficient reference or peer data; +- **Data-quality diagnostic plot**: a PDF diagnostic of configured failures on the processed demand, intended to make flagged periods easier to inspect. Together, these diagnostics are intended to make gap handling explicit rather than conceal unresolved data behind automatic imputation. A typical advanced workflow is therefore to run the basic cleaning stage, inspect the gap report to identify any remaining missing periods, and then configure advanced rules for gaps that require explicit reconstruction or replacement. @@ -159,7 +181,7 @@ The module requires user-provided target shapes. A valid ENTSO-E API token is ad Advanced `external_profile` sources may reference user-provided CSV files. -Intermediate provider data, cleaned national demand, provenance, execution plans, and auxiliary data are stored below the module resources path. Final regional electricity demand is written to the configured module results path. +Intermediate provider data, cleaned national demand, provenance, execution plans, auxiliary data, and data-quality tables are stored below the module resources path. Data-quality evaluation writes `load_data_quality_failures.parquet` and `load_data_quality_issues.parquet` alongside the automatic demand resources. Final regional electricity demand is written to the configured module results path. Please consult [`INTERFACE.yaml`](./INTERFACE.yaml) for the module's formal input/output interface. diff --git a/config/README.md b/config/README.md index 6ae12c8..d953f9a 100644 --- a/config/README.md +++ b/config/README.md @@ -1,6 +1,6 @@ # Configuration -This module is configured through `config/config.yaml`. +This module is configured through `config/config.yaml`. Gap filling and data-quality evaluation both use tclean, while this document describes their Modelblocks-facing configuration. The configuration schema is intentionally strict: malformed or unsupported configuration should fail validation rather than silently falling back to defaults. @@ -60,6 +60,92 @@ Sources are combined in the listed order. When more than one provider supplies a The authoritative list of provider identifiers and source metadata is [`workflow/internal/source_registry.yaml`](../workflow/internal/source_registry.yaml). Its `temporal_scope` bounds use the same half-open `[start, end)` convention as the model time grid. An omitted bound means no restriction is declared in that direction, and omitted or empty `contexts` means no context restriction is declared. +## Data quality + +Data-quality testing is configured separately from gap filling. Tests are diagnostic: they identify suspicious observations, profiles, or source disagreements and record evaluation limitations, but they do not change the processed demand values. + +The test definitions use the `tclean.data_quality` configuration contract. The module validates those definitions before the expensive evaluation step and then maps Modelblocks demand data into the named sources expected by tclean. Method-level fields, value specifications, failure semantics, and contextual-reference details are documented in tclean's `docs/data_quality.md`; this section focuses on the module-specific configuration behaviour. + +A representative configuration can contain tests such as: + +```yaml +data_quality: + - name: non_negative + method: range + minimum: + value_mode: fixed + value: 0 + + - name: unusual_level + method: contextual_level + reference_orders: + - period: 7D + radius: 4 + - period: 1Y + radius: 2 + robust_deviation_threshold: 6 + + - name: source_disagreement + method: source_disagreement + difference_mode: relative + threshold: + value_mode: fixed + value: 0.1 +``` + +The examples above illustrate the module shape rather than prescribing universally appropriate thresholds. Threshold choice remains a modelling decision and should be adapted to the target data and purpose of the quality check. + +### Data-quality sources + +The module supplies the following source names to tclean data-quality evaluation: + +- `processed_demand`: the combined national demand after the configured gap-filling stage. When `gap_filling.mode` is `"off"`, this is the combined but unfilled demand series; +- each configured provider in `load_sources`, such as `entsoe`, `neso`, `entsoe_power_statistics`, or `opsd`, where that provider has prepared data for the run. + +For ordinary tests, omitting `sources` makes `processed_demand` the focal source by default. A test can explicitly select one or more available source names when a provider-specific diagnostic is required. + +`source_disagreement` deserves particular attention. In tclean, `sources` selects the **focal** source or sources to test; the other supplied sources remain available as peer evidence. In this module, the usual pattern is therefore to evaluate `processed_demand` against the underlying configured providers. For example: + +```yaml +- name: source_disagreement + method: source_disagreement + sources: + - processed_demand + difference_mode: relative + threshold: + value_mode: fixed + value: 0.1 +``` + +Because `processed_demand` is the module default focal source, the explicit `sources` selector can be omitted when that is the intended target. At each country/timestamp, tclean uses the available non-focal provider values as peers; missing provider coverage can therefore make some observations not evaluable without invalidating the configuration itself. + +### Countries and ordered tests + +The optional tclean `contexts` selector corresponds to the country contexts present in the prepared national-demand frames. It can be used to restrict a test to selected countries. + +Data-quality tests are ordered and names must be unique. Earlier failures can affect the eligible reference population of later reference-based tests. Tclean's `include_failed_periods_from` option can explicitly retain failures from named **preceding** tests when that is appropriate. Forward references are invalid. + +This ordering is especially relevant for derived thresholds and the contextual methods, where obviously invalid observations identified by an earlier test would otherwise contaminate later reference evidence. + +### Contextual tests and reference periods + +`contextual_level` and `contextual_profile` compare the focal demand with analogous historical observations or profiles defined by `reference_orders`. Fixed periods such as `7D` and calendar-aware periods such as `1Y` have distinct semantics; `1Y` is a calendar shift rather than a shorthand for `365D`. + +For the full reference-lattice, robust-deviation, predictive-probability, and profile-normalisation semantics, refer to tclean's data-quality documentation rather than duplicating those rules here. + +### Runtime + +Simple pointwise tests are generally quick. Reference-heavy tests can take longer because they construct and evaluate historical comparison sets, and `source_disagreement` must also align peer-provider observations. On long histories covering many countries, `contextual_level`, `contextual_profile`, and cross-source evaluation can take **a few minutes**. This is expected and is substantially different from an hours-long or stalled workflow. + +### Data-quality outputs + +The evaluation produces two structured tables: + +- `load_data_quality_failures.parquet`: contiguous periods where a configured test failed; +- `load_data_quality_issues.parquet`: warnings and `not_evaluable` periods where valid configuration could not be fully evaluated from the available evidence. + +The workflow also produces a PDF data-quality diagnostic for visual inspection of failures on `processed_demand` as part of the normal module workflow. + ## Gap filling Gap handling is configured below `gap_filling`. @@ -350,7 +436,7 @@ focused on individual configuration features. Configuration is checked in two layers: 1. the YAML schema checks structure, permitted values, required fields, and basic types; -2. semantic validation checks constraints that depend on relationships between fields, such as time-grid alignment, unique rule/source names, valid source references, and compatible advanced periods. +2. semantic validation checks constraints that depend on relationships between fields, such as time-grid alignment, unique rule/source names, valid source references, compatible advanced periods, valid ordered data-quality tests, data-quality source selectors, and references to preceding quality-test names. Invalid configuration should be corrected at source rather than handled through silent fallbacks. From c9f3c8556bad7f5afdb4a600a66b4448a377e88b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:44:49 +0200 Subject: [PATCH 235/241] Updated plot functionality for cases where there are no data quality issues. --- .../scripts/_plot_data_quality_timeline.py | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/workflow/scripts/_plot_data_quality_timeline.py b/workflow/scripts/_plot_data_quality_timeline.py index dabea8f..9b534a0 100644 --- a/workflow/scripts/_plot_data_quality_timeline.py +++ b/workflow/scripts/_plot_data_quality_timeline.py @@ -57,12 +57,11 @@ def main( failures, demand=demand, plot_start=plot_start, plot_end=plot_end ) - if not failures.empty: - failures["display_width_px"] = ( - ((failures["end"] - failures["start"]) / time_step) - / len(demand.index) - * PLOT_WIDTH_PX - ) + failures["display_width_px"] = ( + ((failures["end"] - failures["start"]) / time_step) + / len(demand.index) + * PLOT_WIDTH_PX + ) method_colours = _build_method_colours(failures) @@ -330,7 +329,7 @@ def _plot_page( """Create one stacked-context data-quality page.""" panel_height_px = int(np.ceil(layout["end"].iloc[-1])) - legend_height_px = LEGEND_HEIGHT_PX if method_colours else 0 + legend_height_px = LEGEND_HEIGHT_PX page_height_px = ( PAGE_TOP_MARGIN_PX + panel_height_px + legend_height_px + PAGE_BOTTOM_MARGIN_PX @@ -413,6 +412,16 @@ def _plot_page( handlelength=2.0, columnspacing=1.0, ) + else: + figure.text( + 0.5, + 8 / page_height_px, + "No data-quality issues identified.", + ha="center", + va="bottom", + fontsize=6.5, + color="0.4", + ) return figure, axis From ac355bb396a4a29dc9b58f5823f55ddd7851f6b2 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 16:48:56 +0200 Subject: [PATCH 236/241] Modified visuals. --- workflow/scripts/_plot_data_quality_timeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/_plot_data_quality_timeline.py b/workflow/scripts/_plot_data_quality_timeline.py index 9b534a0..9311753 100644 --- a/workflow/scripts/_plot_data_quality_timeline.py +++ b/workflow/scripts/_plot_data_quality_timeline.py @@ -37,7 +37,7 @@ # A failure must occupy at least this much rendered horizontal space # before an outlined interval box is useful. -BOX_MIN_WIDTH_PX = 4 +BOX_MIN_WIDTH_PX = 1000 def main( From 49adac9af677e70b6ac6c65e3b8f0efc376d3ba3 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 17:07:01 +0200 Subject: [PATCH 237/241] Added some dq methods to local test --- tests/local/local_test_config.yaml | 50 +++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/local/local_test_config.yaml b/tests/local/local_test_config.yaml index 7901130..bc7cf36 100644 --- a/tests/local/local_test_config.yaml +++ b/tests/local/local_test_config.yaml @@ -180,4 +180,52 @@ integration_test: source: alb_external_test data_quality: - tests: [] \ No newline at end of file + tests: + - name: non_negative + method: range + minimum: + value_mode: fixed + value: 0 + + - name: abrupt_change + method: rate_of_change + difference_mode: fixed + threshold: + value_mode: median_absolute_increment + multiplier: 10 + + - name: repeated_daily_profile + method: repeated_pattern + pattern_duration: 24h + minimum_matches: 2 + + - name: flat_half_day + method: flatline + minimum_duration: 12h + + - name: unusual_level + method: contextual_level + reference_orders: + - period: 7D + radius: 4 + - period: 1Y + radius: 2 + robust_deviation_threshold: 10 + + - name: unusual_daily_shape + method: contextual_profile + profile_duration: 24h + profile_offset: 0h + reference_orders: + - period: 7D + radius: 4 + - period: 1Y + radius: 10 + robust_deviation_threshold: 10 + + - name: source_disagreement + method: source_disagreement + difference_mode: fixed + threshold: + value_mode: fixed + value: 0.001 \ No newline at end of file From a0a92dd6a2849d8a3e53d9e7ba52d0810016a57f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 19:46:44 +0200 Subject: [PATCH 238/241] Removed the problematic entsoe api from the local_test. --- tests/local/local_test_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/local/local_test_config.yaml b/tests/local/local_test_config.yaml index bc7cf36..fb3c37e 100644 --- a/tests/local/local_test_config.yaml +++ b/tests/local/local_test_config.yaml @@ -12,7 +12,7 @@ integration_test: load_sources: - neso - opsd - - entsoe + # - entsoe - entsoe_power_statistics - orkustofnun From 6aa2f35d8e9273235f64a0cedf6ce224bde8db1c Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 19:47:16 +0200 Subject: [PATCH 239/241] Pointed the module towards tclean in conda-forge --- pixi.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index b397ac0..84d97d3 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,7 +5,6 @@ description = "This module prepares electricity demand timeseries for Europe at license = "Apache-2.0" readme = "README.md" channels = [ - "https://conda.anaconda.org/ddahawkins/label/dev", "conda-forge", "bioconda", ] From 2aacf7f546f784c55e36857aba09a56d01c6a6c9 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Wed, 9 Sep 2026 20:36:44 +0200 Subject: [PATCH 240/241] tclean 0.2.1. --- pixi.lock | 545 +++++++++++-------------- workflow/envs/module.linux-64.pin.txt | 14 +- workflow/envs/module.osx-arm64.pin.txt | 12 +- workflow/envs/module.win-64.pin.txt | 12 +- workflow/envs/module.yaml | 1 - 5 files changed, 267 insertions(+), 317 deletions(-) diff --git a/pixi.lock b/pixi.lock index 09d5e53..9498450 100644 --- a/pixi.lock +++ b/pixi.lock @@ -6,7 +6,6 @@ platforms: environments: default: channels: - - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -20,7 +19,7 @@ environments: - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-storage-plugins-4.4.1-pyh84498cf_0.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-minimal-9.26.1-pyhdfd78af_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.10.0-py311hf77984d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.11.0-py311hf77984d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda @@ -33,7 +32,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-clp-1.17.11-hc03379b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-osi-0.108.12-hf4fecb4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coin-or-utils-2.11.13-hc93afbd_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cpp-expected-1.3.1-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py314hf3b331d_1.conda @@ -146,8 +145,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-14.2.8.post0-hee9eb32_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/reproc-cpp-14.2.8.post0-h1c70be6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.6.3-py314h7e8cd81_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.6-hbcb56a7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/simdjson-4.6.11-h4dbf13b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.17.0-hb6aa676_2.conda @@ -160,7 +158,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda @@ -173,7 +171,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-cpp-0.8.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda @@ -205,7 +203,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.12.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda @@ -258,8 +256,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -281,6 +278,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ruamel.yaml-0.19.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda @@ -345,7 +343,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.12.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda @@ -398,8 +396,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -421,6 +418,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ruamel.yaml-0.19.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda @@ -449,7 +447,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.10.0-py311hfcb3ee1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.11.0-py311hfcb3ee1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314hee34562_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda @@ -460,7 +458,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-clp-1.17.11-he934a02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-osi-0.108.12-h8aa3827_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coin-or-utils-2.11.13-h6bed822_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cpp-expected-1.3.1-h3feff0a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py314h671cc75_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda @@ -527,7 +525,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.1-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda @@ -555,8 +553,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-14.2.8.post0-hdca3b7d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproc-cpp-14.2.8.post0-h2fd3d4c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.6.3-py314hc05cd11_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.6-h7f187d3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/simdjson-4.6.11-hfbe1efa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.17.0-h760df91_2.conda @@ -603,7 +600,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/conda-lockfiles-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-handling-2.6.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-package-streaming-0.13.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.12.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/conda-self-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/configargparse-1.7.7-pyh5ded981_0.conda @@ -655,8 +652,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.32.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda @@ -678,6 +674,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.3.post1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ruamel.yaml-0.19.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/smart_open-7.7.1-pyhcf101f3_0.conda @@ -707,7 +704,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.10.0-py311h66afae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.11.0-py311h66afae6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314h85cf176_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h477c42c_3.conda @@ -716,7 +713,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-clp-1.17.11-hfe59113_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-osi-0.108.12-ha0be75d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coin-or-utils-2.11.13-hb1f2b86_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cpp-expected-1.3.1-h477610d_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.21-py314hb98de8c_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fmt-12.1.0-h1ced75a_1.conda @@ -771,7 +768,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.1-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda @@ -801,13 +798,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-14.2.8.post0-h5112557_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/reproc-cpp-14.2.8.post0-hf5d2508_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-2026.6.3-py314h9f07db2_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_4.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_5.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.6-hcc30601_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/simdjson-4.6.11-h477610d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/spdlog-1.17.0-hd227c35_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlalchemy-2.0.52-py314hc5dbbe4_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.1.0-hdcfe883_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -818,7 +814,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libice-1.1.2-hd74fcf3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libsm-1.2.6-hd74fcf3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libx11-1.8.13-hfa52320_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hfa92662_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxext-1.3.7-hfa92662_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxpm-3.5.19-hba3369d_0.conda @@ -829,7 +825,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda module: channels: - - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -878,7 +873,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda @@ -939,7 +934,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.1-h474f4eb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda @@ -983,7 +978,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-haac24f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda @@ -1037,7 +1032,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda @@ -1049,7 +1044,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda @@ -1095,7 +1090,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -1117,6 +1112,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1130,7 +1126,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -1175,7 +1170,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -1197,6 +1192,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1248,7 +1244,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h74c22ad_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda @@ -1322,14 +1318,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.1-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h6a11f2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda @@ -1369,13 +1365,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -1420,7 +1415,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -1443,6 +1438,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.6.0-pyhcf101f3_0.conda @@ -1565,14 +1561,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.1-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-hdc83b33_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda @@ -1607,7 +1603,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.1.0-hdcfe883_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -1618,16 +1614,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hfa92662_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda test: channels: - - url: https://conda.anaconda.org/ddahawkins/label/dev/ - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ packages: @@ -1676,7 +1670,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-hebe6cf0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda @@ -1737,7 +1731,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h47877c9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.1-h474f4eb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda @@ -1781,7 +1775,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-haac24f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-hee9eb32_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda @@ -1836,7 +1830,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda @@ -1848,7 +1842,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda @@ -1899,7 +1893,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -1925,6 +1919,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -1939,7 +1934,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -1989,7 +1983,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -2015,6 +2009,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -2067,7 +2062,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h74c22ad_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda @@ -2141,14 +2136,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.1-hdb3d66b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.11.1-py314h314fc0d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.11.1-py314h27b0870_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h6a11f2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h5af64e8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda @@ -2189,13 +2184,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py314h61c6340_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h38da0c5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hb001647_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-h31dac16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-3.0.1-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda @@ -2245,7 +2239,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda @@ -2272,6 +2266,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.9.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda @@ -2395,14 +2390,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.45-ha5384fd_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_3.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.1-h49e36cd_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1003.conda - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.11.1-py314h30c6bc1_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.11.1-py314h2061cd4_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-hdc83b33_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda - conda: https://conda.anaconda.org/conda-forge/win-64/msgpack-python-1.2.2-py314hf309875_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-h5112557_1.conda @@ -2438,7 +2433,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/simplejson-4.1.2-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.4-hdb435a2_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.1.0-hdcfe883_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -2449,13 +2444,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36247-habf1de7_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.51.36247-h633cb9f_41.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hfa92662_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_7.conda - - conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda sha256: bc5d29e4fa22bf6f17f69c968ffba24967ec360fbb42f5fde5df5e5e2bbab6a9 @@ -2628,22 +2622,22 @@ packages: - alsa-lib >=1.2.16.1,<1.3.0a0 size: 594986 timestamp: 1787763412911 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.10.0-py311hf77984d_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.11.0-py311hf77984d_0.conda noarch: python - sha256: fb8a418604e1c79df683a24b424e99cc837cb48850ebb589cb2c049c7a92a7ab - md5: e7db28aaebccbdac109ee5aebfd8591a + sha256: 4ace13be332324350187224eb595109d36593e4d3968de18911863b3413a9c5e + md5: 8786f3a748a0b6aaa97f69ce113b9f37 depends: - python >=3.11 - - __glibc >=2.17,<3.0.a0 - libgcc >=15 + - __glibc >=2.17,<3.0.a0 - _python_abi3_support 1.* - cpython >=3.11 constrains: - __glibc >=2.17 license: MIT run_exports: {} - size: 1149650 - timestamp: 1788869562073 + size: 1154426 + timestamp: 1788959469410 - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -3269,9 +3263,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 664399 timestamp: 1778500281099 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_0.conda - sha256: 0ac622d316fa7fe79b49ce430ba7a5ca9ff0fb5a33a4cc308ddb0e2314c748e4 - md5: 48c05606a35f2878c74bc5729869c21d +- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-26.7.2-py314h9e666f3_1.conda + sha256: 8e79a61316a31b1a90dfb43dcdf18ead20712d33abd80ec07e10c808d990c745 + md5: 8e3517e88ce9cdc7cc6cd25ec6149f21 depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -3287,7 +3281,7 @@ packages: - pycosat >=0.6.3 - python - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 + - ruamel.yaml >=0.11.14,<0.20 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 @@ -3304,8 +3298,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1466536 - timestamp: 1788559209569 + size: 1466639 + timestamp: 1788876743822 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 @@ -3425,6 +3419,7 @@ packages: - libgcc >=15 - python_abi 3.14.* *_cp314 license: MIT + license_family: MIT run_exports: {} size: 2842844 timestamp: 1788859389988 @@ -3641,19 +3636,18 @@ packages: - gflags >=2.3.1,<2.4.0a0 size: 130991 timestamp: 1785044715896 -- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda - sha256: 734cd8518063b707aa098932cb109f779b8cf5a3c31633f227d22557a6bfaf53 - md5: 6d9a90cfee2c7343d6c05a1b37ef75fb +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-hebe6cf0_2.conda + sha256: 917c3a622c3870f822d25a882f1856e757d91e31a3dd52a499849a392ca865d2 + md5: 07bdd65afe03df3fb5dbcaa8257c64a3 depends: - - libgcc >=14 + - libgcc >=15 - __glibc >=2.17,<3.0.a0 license: MIT - license_family: MIT run_exports: weak: - giflib >=6.1.3,<6.2.0a0 - size: 87196 - timestamp: 1785907555387 + size: 86117 + timestamp: 1788948683783 - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.88.3-h0804268_3.conda sha256: 41d88bd2671de2720cebe28cbfb1b434586383efdeab1669b488a1669ce3e3b0 md5: 164d7e0bcf4552bba8d4a8efbe36c990 @@ -4837,24 +4831,24 @@ packages: - liblapacke >=3.11.0,<3.12.0a0 size: 17998 timestamp: 1788077016927 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda - sha256: 50b31f6c51afe7df0639acf5dde8acf3f4f0dc9f644ab9842b717ae798507d68 - md5: 3b8c8325547a4fd8c51649ef7dfab688 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.1-h474f4eb_0.conda + sha256: 5c716beeb80cd420c37cb811102a9e76c66072f47ed319de1a931ecdd98c9e6b + md5: d96e790721dd4e9af624acbf46cdd818 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15 - libstdcxx >=15 - libxml2 - - libxml2-16 >=2.15.3 + - libxml2-16 >=2.15.4 - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache run_exports: weak: - - libllvm23 >=23.1.0,<23.2.0a0 - size: 45046595 - timestamp: 1787716721647 + - libllvm23 >=23.1.1,<23.2.0a0 + size: 45065095 + timestamp: 1788900116889 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 @@ -5712,26 +5706,25 @@ packages: run_exports: {} size: 207738 timestamp: 1788033918440 -- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda - sha256: 83837ffc5db1abfc14de88f5c9f07d1a666f149cd95249777f0ed09073ae7c22 - md5: ade84857af069283ff1e3b7aacae1232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-haac24f3_1.conda + sha256: 75384c7e6ea3cd72969c0163ec52ddaed08590bb292e959d1afd2a6ffcc237a6 + md5: 3fee8d11b0d8a54fb899749052accb4f depends: - - __glibc >=2.17,<3.0.a0 - - bzip2 >=1.0.8,<2.0a0 - - libgcc >=15 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - libstdcxx >=15 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 - libzlib >=1.3.2,<2.0a0 + - libiconv >=1.18,<2.0a0 + - bzip2 >=1.0.8,<2.0a0 - openssl >=3.5.8,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 + - liblzma >=5.8.3,<6.0a0 license: Zlib - license_family: Other run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 526042 - timestamp: 1788051267376 + size: 566663 + timestamp: 1788896575106 - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda sha256: 147ba1c9ebaeba03c95c1ac018523978c8a07387e2413799b09b69017e976b58 md5: e9600e8080bf57f25ad2e9f78564c895 @@ -6694,23 +6687,9 @@ packages: run_exports: {} size: 300306 timestamp: 1788577380856 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.18.17-py314h0f05182_2.conda - sha256: 59f7773ed8c6f2bcab3e953e2b4816c86cf71e5508dc571f8073b8c2294b5787 - md5: 8ea76c64b49b16c37769ea7c4dca993b - depends: - - python - - ruamel.yaml.clib >=0.2.15 - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - run_exports: {} - size: 308487 - timestamp: 1766175778417 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_4.conda - sha256: b22651b116c9143ac275f7ead7c61952814efc44ba25820ba8d7f5a765c2bcd0 - md5: c88364b57ab0e4564af7bd70fd99cb82 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.15-py314hfe1a184_5.conda + sha256: a1e3159f6d131fb7c6a17a1812ec9b53e03e485a78901781ccaef82c8196774c + md5: 2c8de2bca562cd68bdcfd0920a6395b9 depends: - python - libgcc >=15 @@ -6719,8 +6698,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 160098 - timestamp: 1788484981452 + size: 160103 + timestamp: 1788881212732 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.6-hbcb56a7_0.conda noarch: python sha256: fc9e190a1cd67a8ff56b18dd7bd54e861cc68d60f85d9479a12fe95095fc5735 @@ -7148,19 +7127,19 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 size: 839578 timestamp: 1787087012372 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - sha256: cbf891f6cc1a859347680af1c8562bc6b033bf182a2a3bb536016932be3206de - md5: f06ef439c280a5f90b8bf62355008dbc +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda + sha256: 3ec065b94554dc48a4ca582a960a5484bc166b26e83ce0954653e08d17e8bd53 + md5: da33efee1a93603d92e06d4a2b68def6 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 license: MIT license_family: MIT run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 - size: 16419 - timestamp: 1786381001122 + size: 18793 + timestamp: 1788960512381 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a md5: f2ba4192d38b6cef2bb2c25029071d90 @@ -7342,17 +7321,17 @@ packages: - xorg-libxxf86vm >=1.1.7,<2.0a0 size: 18701 timestamp: 1769434732453 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda - sha256: 28be920cbb36bb674f66a23f368b67733dbbb89b2321e5ed7266d9b415156795 - md5: 7386d38083163c5b5c4a92a1582daaef +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda + sha256: 8e9c7ca4fcfe9f0c300e4aa853b79c25411fc4e470decfe8f909b74f60dfb6dd + md5: c3d0a6f03ba3aa312777b41b20a71ec0 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=15 + - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT run_exports: {} - size: 594980 - timestamp: 1788447117643 + size: 596340 + timestamp: 1788874111132 - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda sha256: d164dfa75ecd538f6fd68765defcc06aa875bc697b9b215362d79a2a73125dd0 md5: e741576fb8f89821ac7c1c537322a33d @@ -7627,6 +7606,7 @@ packages: depends: - python >=3.11 license: BSD-3-Clause + license_family: BSD run_exports: {} size: 321343 timestamp: 1788865918329 @@ -7706,6 +7686,7 @@ packages: - python >=3.11 - python license: BSD-3-Clause + license_family: BSD run_exports: {} size: 112341 timestamp: 1788802215348 @@ -7872,18 +7853,17 @@ packages: run_exports: {} size: 23954 timestamp: 1781293054998 -- conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.11.0-pyh5ded981_2.conda - sha256: 57721a79a37837fc576bed40c5f927a6b9e827a9f446f0ac0132fb7503c8ac87 - md5: d00669954b34487bd3f60297d2c7e5b3 +- conda: https://conda.anaconda.org/conda-forge/noarch/conda-pypi-0.12.0-pyh5ded981_0.conda + sha256: 5c41f9946b00966e1c216697ed34219bf054cb1c2fc9a41d2934bdce0c15c48c + md5: e294f0e16705c5678d8faf71636a5455 depends: - python >=3.11 - - pip >=23.0.1 - packaging - unearth - python-build - python-installer >=1.0 - platformdirs - - conda-index >=0.11.0 + - conda-index >=0.12.0 - conda-package-streaming >=0.11 - python constrains: @@ -7891,8 +7871,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 255320 - timestamp: 1788608958589 + size: 252688 + timestamp: 1788892743969 - conda: https://conda.anaconda.org/conda-forge/noarch/conda-rattler-solver-0.1.1-pyhcf101f3_0.conda sha256: 77ea286de14f5d2dd0c376627ec41d0c226ad94ad8b25262f36712070324aa05 md5: f5d15171925a157707e1ad165179449c @@ -7925,6 +7905,7 @@ packages: - toml - python license: MIT + license_family: MIT run_exports: {} size: 63749 timestamp: 1788789827928 @@ -8259,6 +8240,7 @@ packages: - typing_extensions >=3.10.0.2 - python license: BSD-3-Clause + license_family: BSD run_exports: {} size: 178869 timestamp: 1788802794188 @@ -8730,17 +8712,16 @@ packages: run_exports: {} size: 11766 timestamp: 1745776666688 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda - sha256: e48d972fae8e32ca43eefd1a9649afa3a82b4dda5fe12caa441f0bc9e8d1938d - md5: 0117bf65e45c951a9b0004172cfaeef6 +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda + sha256: 54b91bd1aa1687b92f9e2fa5169bbf9f2bc1c428179109dd9e9fcc6b2e8c9479 + md5: afb7146667be7c8121f5f8337be60680 depends: - - python >=3.10 + - python >=3.11 - python license: MIT - license_family: MIT run_exports: {} - size: 293989 - timestamp: 1787261063562 + size: 296900 + timestamp: 1788964134075 - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.11.1-pyhcf101f3_0.conda sha256: d85d76827ff732e1639f50f45e4d7d3387a27abd857b194dcbb5bb4166c2a7c2 md5: 2fbbf92e1173ae024f0b12759c1e64a1 @@ -8886,27 +8867,16 @@ packages: run_exports: {} size: 53561 timestamp: 1733302019362 -- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh145f28c_0.conda - sha256: 0f7021cfb3ff454c91a5e28e1f5060906a52c6d1cde3f879a7d03ac33c28b1c3 - md5: 573cb3ed111004230ed3de650653cd4d - depends: - - python >=3.13.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 1198163 - timestamp: 1785914482439 -- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda - sha256: 8fe66c78f015fecfe096e4ff78a2626c4d926cde9b6951cf3b8e1ff0aaeb0a6c - md5: fb0f75910f804de1d8c6e91f73673d11 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda + sha256: 8cbae1fed8437e63c9bcc691927402edb3809fccb26b15eb095df7dd2eb73e5f + md5: 48e9ecf08b0578a4313f13b2e2b0fd72 depends: - python >=3.11 - python license: MIT - license_family: MIT run_exports: {} - size: 27515 - timestamp: 1788271419073 + size: 27718 + timestamp: 1788959137036 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -9246,6 +9216,18 @@ packages: run_exports: {} size: 65705 timestamp: 1785242371731 +- conda: https://conda.anaconda.org/conda-forge/noarch/ruamel.yaml-0.19.1-pyhcf101f3_0.conda + sha256: b48bebe297a63ae60f52e50be328262e880702db4d9b4e86731473ada459c2a1 + md5: 06ad944772941d5dae1e0d09848d8e49 + depends: + - python >=3.10 + - ruamel.yaml.clib >=0.2.15 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 98448 + timestamp: 1767538149184 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda sha256: 9e200ee5f9ff19a4d94e4b51c4856d53dec849f91032f345cf0c6bc3d51a7183 md5: 62ac906f1cd582c6c264c95625cb9d6f @@ -9380,6 +9362,19 @@ packages: run_exports: {} size: 19397 timestamp: 1762956379123 +- conda: https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda + sha256: 0abe6f4508325abbf9843aae61c82532f153dc5b2047f1d816cedbe204d48c51 + md5: 7933a68c93a95ad39e5942946ccb7789 + depends: + - python >=3.12 + - pandas >=3.0.3 + - pandera >=0.31.1 + - scipy >=1.16.1 + - python + license: MIT + run_exports: {} + size: 70744 + timestamp: 1788975469891 - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda sha256: 32e75900d6a094ffe4290a8c9f1fa15744d9da8ff617aba4acaa0f057a065c34 md5: 043f0599dc8aa023369deacdb5ac24eb @@ -9713,10 +9708,10 @@ packages: - _openmp_mutex >=4.5 size: 8016 timestamp: 1788046437162 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.10.0-py311hfcb3ee1_0.conda +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ast-serialize-0.11.0-py311hfcb3ee1_0.conda noarch: python - sha256: 30c5f9e7d5b3abe2d7ac1ddb8d0229ca15147e31a3448b1f42557fe7d8e85b7e - md5: 780e8c097dc5988e364e43521337b6b8 + sha256: 1a2b4d766aac6dca2d99823b68454d4046146631a5b73f3c6760051abea3cde7 + md5: d662846be4d614e2c57183944776d82c depends: - python >=3.11 - __osx >=11.0 @@ -9726,8 +9721,8 @@ packages: - __osx >=11.0 license: MIT run_exports: {} - size: 1108325 - timestamp: 1788869608301 + size: 1116080 + timestamp: 1788959474017 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda sha256: b0747f9b1bc03d1932b4d8c586f39a35ac97e7e72fe6e63f2b2a2472d466f3c1 md5: 57301986d02d30d6805fdce6c99074ee @@ -10254,9 +10249,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 553075 timestamp: 1778500827484 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_0.conda - sha256: d9f2f2bd0d57786d030e4c8a6fd46b8e0512c6eebd4830cf1b253ad4ef394e9a - md5: c10c761f88c8b2b4fc40fb962a700792 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/conda-26.7.2-py314hcec6336_1.conda + sha256: 97f45ec1f71cff5b8cf590027131094847e88761e1e484fa0ccafc4f4507781a + md5: 2475f0a3458f532ae0c934d2f47b0eef depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -10272,7 +10267,7 @@ packages: - pycosat >=0.6.3 - python - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 + - ruamel.yaml >=0.11.14,<0.20 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 @@ -10290,8 +10285,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1470827 - timestamp: 1788559426835 + size: 1470815 + timestamp: 1788877030397 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py314hf8a3a22_4.conda sha256: 754ab72f1c1ae99ef7c57995f59224dc9632cbd6731fe7e6277437fd01d43156 md5: cddc851000ce131d757678c2f329eaad @@ -10374,6 +10369,7 @@ packages: - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT + license_family: MIT run_exports: {} size: 2754969 timestamp: 1788859502179 @@ -10540,18 +10536,17 @@ packages: - gflags >=2.3.1,<2.4.0a0 size: 93701 timestamp: 1785044718935 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda - sha256: ebfdf94fe2fb8e8d39702e26c747a9a692c0fcf78db737b56deeba4ed16dcb82 - md5: 39725a84a4b2b6b3ae6177f58be48b91 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h74c22ad_2.conda + sha256: 36a1605ea258ea805fd5a0f23f07ce3febcf4ac041d1ea99d0b31902b79f4358 + md5: edc562dcbe5872664817e64575aa325f depends: - __osx >=11.0 license: MIT - license_family: MIT run_exports: weak: - giflib >=6.1.3,<6.2.0a0 - size: 82339 - timestamp: 1785907599925 + size: 82055 + timestamp: 1788948759889 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.88.3-h0d72ea3_3.conda sha256: 0406a0efb4962231e3ce9f67ab74cb9b2d158d5a996f3b00acd5ceaf0ad78fbb md5: 12dc17aaf00d5e0cb3e9552c20b023a6 @@ -12038,21 +12033,20 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 47822 timestamp: 1785277049190 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda - sha256: 996d3a9de61c8ccc3fcb265938f9845f11868251f38e6db4e7190de3f9a971a2 - md5: 0affff5130a421d11d4d77ffbb00dad7 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.1-hdb3d66b_0.conda + sha256: 012e606dc31de2e4e173636a2ec13bc9b075498015cb65605b29e4f0bb9e481b + md5: 22ecd4403fcaf2fbbafa2cd53a0e0f0c depends: - __osx >=11.0 constrains: - intel-openmp <0.0a0 - - openmp 23.1.0|23.1.0.* + - openmp 23.1.1|23.1.1.* license: Apache-2.0 WITH LLVM-exception - license_family: APACHE run_exports: strong: - - llvm-openmp >=23.1.0 - size: 287808 - timestamp: 1787723323710 + - llvm-openmp >=23.1.1 + size: 287782 + timestamp: 1788967337071 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py314h24f3bdd_1.conda sha256: fb105138b325e81f8dabc859cc47e9e29295b68cd6fd4dd333ed30e527e7c08b md5: aea17e1b366b814eff15fc3c8c4c1e3c @@ -12160,25 +12154,24 @@ packages: run_exports: {} size: 207305 timestamp: 1788033960061 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda - sha256: 0a737bebd276d48f0436842ca47b38b823b4f8e3c3065848c9d1f421f334a4aa - md5: b7819f4118a456301e220e995afd53ac +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h6a11f2a_1.conda + sha256: a715db5100acebb66ec81748dc25f7a7d90be4568a6307a8077797e0f4fba931 + md5: ce5e7144254351b224ac9d23ab0bddae depends: - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - libcxx >=21 - - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - - libzlib >=1.3.2,<2.0a0 + - libiconv >=1.18,<2.0a0 + - bzip2 >=1.0.8,<2.0a0 - openssl >=3.5.8,<4.0a0 - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.2,<2.0a0 license: Zlib - license_family: Other run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 462531 - timestamp: 1788051612824 + size: 501152 + timestamp: 1788896824894 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py314hb1acb19_2.conda sha256: a84144f02b58e8914e9be09d726b88b843a1eb4a377f1f439e9fabfd54dd9aad md5: 36fc4194dcbcb14ed2ff3e1db09e3e89 @@ -12815,23 +12808,9 @@ packages: run_exports: {} size: 284991 timestamp: 1788577307849 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml-0.18.17-py314ha14b1ff_2.conda - sha256: 0f498e343be464219a99424260ff442144d0461a3a5eb30c9de6081af358b281 - md5: 87fc3a204f105e7e61c60a72509cccbd - depends: - - python - - ruamel.yaml.clib >=0.2.15 - - __osx >=11.0 - - python 3.14.* *_cp314 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - run_exports: {} - size: 311922 - timestamp: 1766175797575 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_4.conda - sha256: 8846a7464ebe2dafc78fe83252af615923e73bb78e027cda24391825a6f72ad3 - md5: 652ce80522ce6713d9c24ed68afc9b1c +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel.yaml.clib-0.2.15-py314hd98292b_5.conda + sha256: ab2b09a80bd70c1fc7c589959abc8ee3394a1b3178197696a594d224bba5243f + md5: 5ce723bf0ba143a8caa534609a5be881 depends: - python - __osx >=11.0 @@ -12839,8 +12818,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 127816 - timestamp: 1788484961194 + size: 127818 + timestamp: 1788881217705 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.6-h7f187d3_0.conda noarch: python sha256: afa2a86da96a6438e8b8bc65b4575d0ed058fd2bbf435a9a503cd9cd46dac440 @@ -13086,9 +13065,9 @@ packages: - xerces-c >=3.3.0,<3.4.0a0 size: 1285603 timestamp: 1788145085997 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda - sha256: 5c5ea38ceec008e4ff40111fc166b04086fca310b0aa9e5bd46f65e6b0dcb71d - md5: 31d71ce1b056f69b6ec67ee0712bdf97 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hb001647_2.conda + sha256: cde7cecabb662b435e6537591e5845e870a0648887542d3e538c9f6c20dd6be5 + md5: 610f557e75d6a0d3f676914da5822227 depends: - __osx >=11.0 license: MIT @@ -13096,8 +13075,8 @@ packages: run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 - size: 15124 - timestamp: 1786381585975 + size: 17325 + timestamp: 1788960997645 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda sha256: 2f6915f0897ace4a1b5d66d0463079f7bc9819b0048c03677e47764f0a743499 md5: f51e9414bf1b7bb556aac1a0b74a75fe @@ -13206,10 +13185,10 @@ packages: - _openmp_mutex >=4.5 size: 52252 timestamp: 1770943776666 -- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.10.0-py311h66afae6_0.conda +- conda: https://conda.anaconda.org/conda-forge/win-64/ast-serialize-0.11.0-py311h66afae6_0.conda noarch: python - sha256: 4426b41c106de5b670158ede3a283cd5302042215de68f005bffef8e03cf82b8 - md5: 34223054d0270aeb848ffc617f839b14 + sha256: 3e0fedfeee9b70b9718d07159281ad06faf9fe12eb8a53224ae837c332f5a7b2 + md5: 7e6fd25464ec5e2bda73219ada125c2a depends: - python >=3.11 - vc >=14.3,<15 @@ -13219,8 +13198,8 @@ packages: - cpython >=3.11 license: MIT run_exports: {} - size: 1104850 - timestamp: 1788869566649 + size: 1109377 + timestamp: 1788959464777 - conda: https://conda.anaconda.org/conda-forge/win-64/aws-c-auth-0.10.4-hc6e9107_1.conda sha256: cf7ab8abaac6b462463310412cc37d087a767a95920809cbe6dc0a08fe4bcfbd md5: f7069cdc81f7a5e781f1faf6aab6c171 @@ -13743,9 +13722,9 @@ packages: - coin-or-utils >=2.11,<2.12.0a0 size: 1091975 timestamp: 1778500610309 -- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_0.conda - sha256: 9d3c54d8c23a059dce33616f2922166587fa0752a4ec6bcca08145511f38ac96 - md5: 5a3986bfa78332029e9beb317a76cc13 +- conda: https://conda.anaconda.org/conda-forge/win-64/conda-26.7.2-py314hb821551_1.conda + sha256: e81e3df1dbaab770e24c14e77039490d11a1388da9e77bb1ad6ec55ee8ed8d57 + md5: 241f2037d6bea459d990e38b9a6b0418 depends: - archspec >=0.2.3 - boltons >=23.0.0 @@ -13761,7 +13740,7 @@ packages: - pycosat >=0.6.3 - python - requests >=2.28.0,<3 - - ruamel.yaml >=0.11.14,<0.19 + - ruamel.yaml >=0.11.14,<0.20 - setuptools >=60.0.0 - tqdm >=4 - truststore >=0.8.0 @@ -13778,8 +13757,8 @@ packages: license: BSD-3-Clause license_family: BSD run_exports: {} - size: 1474705 - timestamp: 1788559226199 + size: 1474727 + timestamp: 1788876753583 - conda: https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.3-py314hf309875_4.conda sha256: f141bcbf8e490b49b2f53f517173d13a64d75e43cfae170e0d931cb0b66f4bce md5: c26934035616f7d578f9da0491aed3d8 @@ -13865,6 +13844,7 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: MIT + license_family: MIT run_exports: {} size: 4028543 timestamp: 1788859387573 @@ -15432,23 +15412,22 @@ packages: - libzlib >=1.3.2,<2.0a0 size: 58529 timestamp: 1785276664143 -- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda - sha256: f9658ec83a6744f38e2b8188c76e1c2dea2614f5aa0e14d9d274b19f60646b8c - md5: 79055ec79e1b43749763122cead30976 +- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.1-h49e36cd_0.conda + sha256: 49b0967e84eae82cc2acaf4400629c7b9c08a491b9c8d3343f3f5f2feee7138d + md5: f895db3df946e742c3ce07cfce271bdd depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - intel-openmp <0.0a0 - - openmp 23.1.0|23.1.0.* + - openmp 23.1.1|23.1.1.* license: Apache-2.0 WITH LLVM-exception - license_family: APACHE run_exports: strong: - - llvm-openmp >=23.1.0 - size: 342468 - timestamp: 1787722783678 + - llvm-openmp >=23.1.1 + size: 343428 + timestamp: 1788967655621 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.5-py314hfc2a91f_1.conda sha256: 764da65f49c2520ff5e2fdc8aaa57e94ca884dac58da2bdc294cb56950c63cec md5: 8e9b2740fe44700706e8e4ca7e4fb8b6 @@ -15566,24 +15545,23 @@ packages: run_exports: {} size: 199489 timestamp: 1788033928311 -- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda - sha256: ee989609be75355a6b9125e28e0ac7e36c29ce77dd65890b5bc469a4a6d14e86 - md5: 124e35907d5fb36b1e0cb3b74c20bc9d +- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-hdc83b33_1.conda + sha256: ea378e49e567baede08d298f43718f14833f8c0ae6066660e4d6b62f6e936f99 + md5: 08101c2ed7f91f83798aa142b964d338 depends: - - bzip2 >=1.0.8,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libzlib >=1.3.2,<2.0a0 - - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 - zstd >=1.5.7,<1.6.0a0 + - bzip2 >=1.0.8,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 license: Zlib - license_family: Other run_exports: weak: - minizip >=4.2.2,<5.0a0 - size: 116615 - timestamp: 1788051329472 + size: 128690 + timestamp: 1788896573362 - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda sha256: bb8abe071f73765446df62924031e6599577e8ac9003264dc4569e78c0fea16d md5: ace316c48c178d7faa403dee51e30c7b @@ -16326,12 +16304,11 @@ packages: run_exports: {} size: 218961 timestamp: 1788577430937 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml-0.18.17-py314hc5dbbe4_2.conda - sha256: 62b73874b6c264a73e7904b33322128c4881c78dfe2fb9131becfbf84343ee2d - md5: 027ff6055e5c82ba4e085857f09302cb +- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_5.conda + sha256: a754af80a883b2bad3ab291e6458d8a6994b60a6e4b19c1310283d7ee205e206 + md5: 73fd03bb5e578d5d0c16fc7356faf84c depends: - python - - ruamel.yaml.clib >=0.2.15 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 @@ -16339,22 +16316,8 @@ packages: license: MIT license_family: MIT run_exports: {} - size: 306951 - timestamp: 1766175833786 -- conda: https://conda.anaconda.org/conda-forge/win-64/ruamel.yaml.clib-0.2.15-py314hc5dbbe4_4.conda - sha256: bdba6de07b5a0292da6e110101ca75f9838ecba32208c2bbd7ca3f3d9d51482c - md5: 37d18b45a1a81c9ddd8151d5956bc3bc - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.14.* *_cp314 - license: MIT - license_family: MIT - run_exports: {} - size: 118726 - timestamp: 1788484961694 + size: 118716 + timestamp: 1788881215870 - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.16.6-hcc30601_0.conda noarch: python sha256: 62c3e491290e859197dd41e5513f3b8f4cb9b15ad349d68bf0721438bdb036dd @@ -16515,9 +16478,9 @@ packages: - libsqlite >=3.53.4,<4.0a0 size: 426888 timestamp: 1787051146089 -- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda - sha256: 03ba7196a7ffe842d26aa7020d8ffddb2c1589d27779cf98a8d1a278a08b41c7 - md5: 632effc5cd23068512f7661821764807 +- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.1.0-hdcfe883_0.conda + sha256: 7a65accbe954d0304b9056005f59fbee8b3928ceced38b1e3e8272aa1af0b750 + md5: 8b1275f06705cdc2ab67f3bfdf4df843 depends: - libhwloc >=2.13.0,<2.13.1.0a0 - ucrt >=10.0.20348.0 @@ -16526,8 +16489,8 @@ packages: license: Apache-2.0 license_family: APACHE run_exports: {} - size: 156747 - timestamp: 1788769928020 + size: 157295 + timestamp: 1788879168472 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h967ab96_4.conda sha256: f19618a3a82cc483dacf1c70a30367ee7b0c7e71b90f1f80e14feff44fbd3688 md5: dd9eb33c99d352b6356f86964d6040f7 @@ -16715,11 +16678,11 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 size: 954803 timestamp: 1787087422301 -- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda - sha256: 892ad69b1a9ecc3903ed5a1b18fa097013eaf462eeed3c6ff31bf5c12e71e84b - md5: 87aee04978ab77bf4c0f42b983c216cc +- conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hfa92662_2.conda + sha256: b9f063792e4d50ef2669c30dfc4ba14aefb4fdc37e384241a71093bee0493ad1 + md5: 68403004c805ce03b71a5889a4c47fa1 depends: - - libgcc >=14 + - libgcc >=15 - libwinpthread >=12.0.0.r4.gg4f2fc60ca - ucrt >=10.0.20348.0 license: MIT @@ -16727,8 +16690,8 @@ packages: run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 - size: 110446 - timestamp: 1786381242485 + size: 112892 + timestamp: 1788960775276 - conda: https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda sha256: 21b11bb98c41ece4ce6069d86d826b50b3d73020fb631b97e59a0521dd9d08a1 md5: 0e21a45f1bb429b6e35e82631e60554a @@ -16883,15 +16846,3 @@ packages: - zstd >=1.5.7,<1.6.0a0 size: 387535 timestamp: 1786599623274 -- conda: https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda - sha256: fb5696124a0f612c17140941a33b2f628ebf7112c19ee32508896924f5acadf8 - md5: 94b960989bc00859320a84abe9f5df2d - depends: - - python >=3.12 - - pandas >=3.0.3 - - pandera >=0.31.1 - - scipy >=1.16.1 - - python - license: MIT - size: 68726 - timestamp: 1788952697985 diff --git a/workflow/envs/module.linux-64.pin.txt b/workflow/envs/module.linux-64.pin.txt index 60819db..9510c8e 100644 --- a/workflow/envs/module.linux-64.pin.txt +++ b/workflow/envs/module.linux-64.pin.txt @@ -49,12 +49,12 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda#85eb29ade84d1bd97be5ec55607efb00 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d +https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda#7933a68c93a95ad39e5942946ccb7789 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda#e15caa7ad9be633c3cba5a7937b117b7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda#2e66c929f3d879708335b6ea4557c838 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda#f06ef439c280a5f90b8bf62355008dbc +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda#da33efee1a93603d92e06d4a2b68def6 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda#bb66b610707b811e3c65181a6431e7a8 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda#61f0ffba9161cbb3a77722869b21e4bb https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda#8c282bbe4808a3cc80a5c98e9aec1cfc @@ -112,7 +112,7 @@ https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2# https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda#922776b528a470ab5afa81fd42abfa1d https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda#7b870cffbaa8430290e567a0facd8dca https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda#c44470b6bfa6a582cb4dd42cbda3401e -https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda#7386d38083163c5b5c4a92a1582daaef +https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda#c3d0a6f03ba3aa312777b41b20a71ec0 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda#cfcf11edcfd4acf0094771a9c3b8587f https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda#614e10aae180f87b84f0d1ca8ceb7d9e https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda#fb00b4fb800f2d431303a5c1625ef9d0 @@ -132,7 +132,7 @@ https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.con https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda#2ff98660281d1c6bfebda5bffa52cc89 https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_1.conda#5c8ce6af1772fcfc2b86d17b825abc8e https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda#d207a2e9bae9da476bc0a603215d5167 -https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda#3b8c8325547a4fd8c51649ef7dfab688 +https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.1-h474f4eb_0.conda#d96e790721dd4e9af624acbf46cdd818 https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda#d3ba2947f7d7cdd7c4eb73b206de330b https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.conda#6afb92887568acc8f18281c57c92c28b https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf520645_1.conda#c4121a6f49ae4e9e9beae7f70530b3be @@ -173,7 +173,7 @@ https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda#6acb https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.4-h7df9aa5_0.conda#9a414200ffe436e47d72c9f4c11c3658 https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h55b0958_0.conda#5f856b72ca9cd5b5b4a1c43551804622 https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda#df81fd57eacf341588d728c97920e86d -https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-hc2c9f91_1.conda#ade84857af069283ff1e3b7aacae1232 +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.2-haac24f3_1.conda#3fee8d11b0d8a54fb899749052accb4f https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h3a84ec1_3.conda#66ee86d087145e4d91d8a75437692238 https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hab3fe16_120.conda#5947bdda89ced07ab0d8a5d6503082c3 https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 @@ -183,7 +183,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.12.0-heb2dce7_2.conda#4 https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.conda#10e0aa58ed390ba598b728badf9eb1b7 https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda#680520cff78a974d564cd99cdf5ec0b5 https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f -https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-h280c20c_1.conda#6d9a90cfee2c7343d6c05a1b37ef75fb +https://conda.anaconda.org/conda-forge/linux-64/giflib-6.1.3-hebe6cf0_2.conda#07bdd65afe03df3fb5dbcaa8257c64a3 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda#98b6c9dc80eb87b2519b97bcf7e578dd https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.13.3-h31aba09_1.conda#44c6780e3082468d8676aebdcd5068a6 @@ -203,7 +203,7 @@ https://conda.anaconda.org/conda-forge/linux-64/fiona-1.10.1-py312ha17e7ad_7.con https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda#afb7146667be7c8121f5f8337be60680 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda#e6e9b5795bb495325c3b4ebd451519aa diff --git a/workflow/envs/module.osx-arm64.pin.txt b/workflow/envs/module.osx-arm64.pin.txt index d81d99c..a362f1d 100644 --- a/workflow/envs/module.osx-arm64.pin.txt +++ b/workflow/envs/module.osx-arm64.pin.txt @@ -20,7 +20,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda#2 https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda#a915151d5d3c5bf039f5ccc8402a436f https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda#b50612e7d190b8061ab4e7dc119cf4d5 https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.7-h0ae1c2c_106_cp314.conda#5fd37c78250b4f57c2bcf9ad2e54beb1 -https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.0-hdb3d66b_0.conda#0affff5130a421d11d4d77ffbb00dad7 +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.1-hdb3d66b_0.conda#22ecd4403fcaf2fbbafa2cd53a0e0f0c https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda#a2d706b4a7d603524133037c34f7fb76 https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda#408af8d9d5226ff45ff04756ff1aa91e https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda#d54390644d893d50e739b19145aae45f @@ -46,7 +46,7 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.5-py314he609de1_0.conda#b1dde16791eca59c8316d0ad1487bff0 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d +https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda#7933a68c93a95ad39e5942946ccb7789 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.8-py314h61c6340_1.conda#8fc44bfcc973b7cab443539ce858161a @@ -61,7 +61,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda# https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-hf5e6511_1.conda#e0466c58fd07db190b9a81b5e58539f2 https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-h4d1e80c_2.conda#487d92910109a711fdb77631bf1aabf0 https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-h84a0fba_2.conda#f51e9414bf1b7bb556aac1a0b74a75fe -https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h84a0fba_2.conda#31d71ce1b056f69b6ec67ee0712bdf97 +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hb001647_2.conda#610f557e75d6a0d3f676914da5822227 https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hb001647_1004.conda#f0833c4a0b68798f50935dd83384c705 https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hbffa61f_2.conda#786c0680e77665d1938ce9cdd7cf82a7 https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda#881cfb44ea02cc9849f612725a959660 @@ -120,7 +120,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda#2c9 https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.4-h550178d_0.conda#df2f9521a1b80a859d6d329dba7df2a0 https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-ha834cd8_0.conda#e7480ad364d7a5bf03812ba272c39707 https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha909e78_20.conda#d07359797436cfc891b38e203cf0caac -https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h56c1371_1.conda#b7819f4118a456301e220e995afd53ac +https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.2-h6a11f2a_1.conda#ce5e7144254351b224ac9d23ab0bddae https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h47dffff_3.conda#fc08d48da7f739ebf2110c3491ae9eff https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_hc59e0ec_120.conda#8909f6e5df2f99065ef12a6e6c2db274 https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802 @@ -130,7 +130,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.12.0-hb71b141_2.conda# https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h74c22ad_1003.conda#7fad2bcb66997fc0a53442db98031a39 https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.9-gpl_h76147b0_101.conda#bdcec8546f15ded1b3718309e027e603 https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda#94f14ef6157687c30feb44e1abecd577 -https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h1a92334_1.conda#39725a84a4b2b6b3ae6177f58be48b91 +https://conda.anaconda.org/conda-forge/osx-arm64/giflib-6.1.3-h74c22ad_2.conda#edc562dcbe5872664817e64575aa325f https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5 https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda#925acfb50a750aa178f7a0aced77f351 https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.13.3-h0d10833_1.conda#5b389503ff59a241b0560cf86e4b3413 @@ -150,7 +150,7 @@ https://conda.anaconda.org/conda-forge/osx-arm64/fiona-1.10.1-py314h7afc4b4_7.co https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda#afb7146667be7c8121f5f8337be60680 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda#72cd17b6f8016221faaa96123711f8c9 diff --git a/workflow/envs/module.win-64.pin.txt b/workflow/envs/module.win-64.pin.txt index d4c1830..b7bd9ef 100644 --- a/workflow/envs/module.win-64.pin.txt +++ b/workflow/envs/module.win-64.pin.txt @@ -26,8 +26,8 @@ https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.4-h3cfd58e_0.conda https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.4-he095d88_0.conda#3ce54d0f8f24dfab99d82414411640aa https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5 https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda#6a01c986e30292c715038d2788aa1385 -https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hdcfe883_3.conda#632effc5cd23068512f7661821764807 -https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.0-h49e36cd_0.conda#79055ec79e1b43749763122cead30976 +https://conda.anaconda.org/conda-forge/win-64/tbb-2023.1.0-hdcfe883_0.conda#8b1275f06705cdc2ab67f3bfdf4df843 +https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-23.1.1-h49e36cd_0.conda#f895db3df946e742c3ce07cfce271bdd https://conda.anaconda.org/conda-forge/win-64/mkl-2026.1.0-hac47afa_235.conda#ace316c48c178d7faa403dee51e30c7b https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-10_h8455456_mkl.conda#0d13dde4466d62a488e58f12aedacc1c https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-10_hf9ab0e9_mkl.conda#3a442b27a0ca4153d07b3ceb12fd486f @@ -50,7 +50,7 @@ https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.5-py314hf700ef7_1.conda#a519b50147401cf6bad2fd188028ad38 https://conda.anaconda.org/conda-forge/noarch/pandera-0.32.1-ha00cc4c_0.conda#f6b96a51cda3cf9a068fd748c8155ced -https://conda.anaconda.org/ddahawkins/label/dev/noarch/tclean-0.2.1-pyh129be3f_0.conda#94b960989bc00859320a84abe9f5df2d +https://conda.anaconda.org/conda-forge/noarch/tclean-0.2.1-pyhc364b38_0.conda#7933a68c93a95ad39e5942946ccb7789 https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda#62ac906f1cd582c6c264c95625cb9d6f https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223 https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.8-py314h5a2d7ad_1.conda#a92f6eff943133d045db4d017ac4297c @@ -94,7 +94,7 @@ https://conda.anaconda.org/conda-forge/win-64/libgomp-16.2.0-h8ee18e1_4.conda#91 https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-20_gnu.conda#1626967b574d1784b578b52eaeb071e7 https://conda.anaconda.org/conda-forge/win-64/libgcc-16.2.0-h110b43a_4.conda#02793cf48b68687fee158a48c92850a6 https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-hba3369d_2.conda#0e21a45f1bb429b6e35e82631e60554a -https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hba3369d_2.conda#87aee04978ab77bf4c0f42b983c216cc +https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-hfa92662_2.conda#68403004c805ce03b71a5889a4c47fa1 https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-hfa92662_1004.conda#fda2f9fedee1b85ba855ea26a9b6bbf8 https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h874e120_2.conda#3658b0201b32da9607ea5e3c1c463c1f https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_2.conda#2bd9052d2e496ea54f47d9db8ae4f881 @@ -126,7 +126,7 @@ https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h6a83c73_2.conda#3e06 https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.4-he095d88_0.conda#d50e00d25a326eb487f9f5540e09b77b https://conda.anaconda.org/conda-forge/win-64/geos-3.14.1-h62f7316_0.conda#f3405ff0366fe7ee04580acf8e422d16 https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-haa95264_20.conda#7eeb5aed49853f8b3e1ca0463ef55a8e -https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-h8fa244a_1.conda#124e35907d5fb36b1e0cb3b74c20bc9d +https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.2-hdc83b33_1.conda#08101c2ed7f91f83798aa142b964d338 https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-h0f64aeb_3.conda#9f0212e4c4bdf5f8f52a1f9793d86311 https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h2fc3b25_120.conda#effdac4d017409986c5861d19775eef1 https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda#28b4cf9065681f43cc567410edf8243d @@ -154,7 +154,7 @@ https://conda.anaconda.org/conda-forge/win-64/fiona-1.10.1-py314hcb00e3b_7.conda https://conda.anaconda.org/conda-forge/noarch/rasterstats-0.21.0-pyhcf101f3_0.conda#290f97c3fcfab8323023b10cbb4f6cc2 https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.9.1-pyhd8ed1ab_0.conda#5d7c149ba9c728d4951f3fe6d3ce0f84 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.25.0-pyhcf101f3_0.conda#0117bf65e45c951a9b0004172cfaeef6 +https://conda.anaconda.org/conda-forge/noarch/narwhals-2.26.0-pyh5ded981_0.conda#afb7146667be7c8121f5f8337be60680 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda#61b8078a0905b12529abc622406cb62c https://conda.anaconda.org/conda-forge/noarch/joblib-1.6.0-pyhcf101f3_0.conda#4ebc70ef1af2e21cdc8edb1bfa1ec28d https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda#74bafde39f688cb95c111e74bfad6669 diff --git a/workflow/envs/module.yaml b/workflow/envs/module.yaml index f05d858..558e20c 100644 --- a/workflow/envs/module.yaml +++ b/workflow/envs/module.yaml @@ -1,6 +1,5 @@ name: module channels: -- https://conda.anaconda.org/ddahawkins/label/dev - conda-forge - bioconda - nodefaults From a12db679cacd7a774ff3bd2122899431e6e9ca5a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 21:19:34 +0000 Subject: [PATCH 241/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- config/config.yaml | 2 +- tests/local/local_test_config.yaml | 4 ++-- workflow/internal/config.schema.yaml | 2 +- workflow/rules/data_quality.smk | 13 ++++--------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 3809ecc..ddf897b 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -182,4 +182,4 @@ data_quality: method: range minimum: value_mode: fixed - value: 0 \ No newline at end of file + value: 0 diff --git a/tests/local/local_test_config.yaml b/tests/local/local_test_config.yaml index fb3c37e..4ab4736 100644 --- a/tests/local/local_test_config.yaml +++ b/tests/local/local_test_config.yaml @@ -202,7 +202,7 @@ integration_test: - name: flat_half_day method: flatline minimum_duration: 12h - + - name: unusual_level method: contextual_level reference_orders: @@ -228,4 +228,4 @@ integration_test: difference_mode: fixed threshold: value_mode: fixed - value: 0.001 \ No newline at end of file + value: 0.001 diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 3b7a18a..b8f24dc 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -91,7 +91,7 @@ $defs: description: > Optional electricity-demand sources to which this test applies. - contexts: + contexts: not: {} required: diff --git a/workflow/rules/data_quality.smk b/workflow/rules/data_quality.smk index ca7073c..c207eb8 100644 --- a/workflow/rules/data_quality.smk +++ b/workflow/rules/data_quality.smk @@ -21,19 +21,14 @@ rule evaluate_data_quality: "Evaluate electricity-demand data quality." script: "../scripts/evaluate_data_quality.py" - + rule plot_data_quality_timeline: input: - demand=( - "/automatic/{shape}/load_cleaned.parquet" - ), + demand=("/automatic/{shape}/load_cleaned.parquet"), failures=rules.evaluate_data_quality.output.failures, output: - plot=( - "/{shape}/" - "load_data_quality_timeline.pdf" - ), + plot=("/{shape}/" "load_data_quality_timeline.pdf"), log: "/{shape}/plot_data_quality_timeline.log", conda: @@ -41,4 +36,4 @@ rule plot_data_quality_timeline: message: "Plot electricity-demand data-quality failures." script: - "../scripts/plot_data_quality_timeline.py" \ No newline at end of file + "../scripts/plot_data_quality_timeline.py"

L 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 literal 0 HcmV?d00001 diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py new file mode 100644 index 0000000..1d1f709 --- /dev/null +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -0,0 +1,236 @@ +"""Create a curated cleaning-timeline figure for the README. + +Temporary development utility. Run from tests/integration/. +""" + +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd +from cleaning.plot_timeline import ( + _add_mean_load_labels, + _add_normalised_demand_traces, + _build_cleaning_method_metadata, + _build_legend_handles, + _build_rank_colours, + _encode_rank_background, + _plot_cleaning_background, + _validate_alignment, + _validate_provenance_metadata, +) + +DEMAND_PATH = Path( + "resources/module/resources/automatic/load_cleaned.parquet" +) + +CLEANING_METHOD_PATH = Path( + "resources/module/resources/automatic/" + "load_final_cleaning_method.parquet" +) + +CLEANING_METHOD_RANK_PATH = Path( + "resources/module/resources/automatic/" + "load_final_cleaning_method_rank.parquet" +) + +OUTPUT_PATH = Path( + "tmp/readme_cleaning_timeline.png" +) + +# Curated subset for the README figure. +COUNTRIES = [ + "ALB", + "GBR", + "IRL", + "MKD", + "DEU", +] + +START = "2021-05-01" +END = "2021-11-01" + +# Must correspond to the configuration used for the long run. +SOURCE_NAMES = [ + "entsoe_api", + "neso", + "opsd_api", +] + +GAP_FILLING_CONFIG = { + "mode": "basic", + "basic": { + "rules": [ + { + "name": "interpolate_short_gaps", + "method": "linear_interpolation", + "max_gap": "3h", + }, + { + "name": "average_adjacent_weeks", + "method": "average_periods", + "max_gap": "326h", + "source_offsets": ["-7d", "7d"], + }, + { + "name": "copy_previous_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "-168h", + }, + { + "name": "copy_following_week", + "method": "copy_period", + "max_gap": "168h", + "source_offset": "168h", + }, + ], + }, +} + + +def main() -> None: + demand = pd.read_parquet(DEMAND_PATH) + cleaning_method = pd.read_parquet( + CLEANING_METHOD_PATH + ) + cleaning_method_rank = pd.read_parquet( + CLEANING_METHOD_RANK_PATH + ) + + print( + "Available period:", + demand.index.min(), + "to", + demand.index.max(), + ) + print( + "Available countries:", + ", ".join(demand.columns), + ) + + missing_countries = [ + country + for country in COUNTRIES + if country not in demand.columns + ] + + if missing_countries: + raise ValueError( + "Requested README countries are unavailable: " + + ", ".join(missing_countries) + ) + + start = pd.Timestamp(START, tz="UTC") + end = pd.Timestamp(END, tz="UTC") + + mask = ( + (demand.index >= start) + & (demand.index < end) + ) + + demand = demand.loc[mask, COUNTRIES] + cleaning_method = cleaning_method.loc[ + mask, + COUNTRIES, + ] + cleaning_method_rank = cleaning_method_rank.loc[ + mask, + COUNTRIES, + ] + + if len(demand) < 2: + raise ValueError( + f"No usable data found between {START} and {END}." + ) + + print( + f"Plotting {len(demand):,} hourly timestamps " + f"for {len(COUNTRIES)} countries." + ) + + # Useful while choosing the README window/countries. + print("\nCleaning-method counts:") + for country in COUNTRIES: + counts = ( + cleaning_method[country] + .value_counts(dropna=False) + ) + print(f"\n{country}") + print(counts.to_string()) + + _validate_alignment( + demand=demand, + cleaning_method=cleaning_method, + cleaning_method_rank=cleaning_method_rank, + ) + + metadata = _build_cleaning_method_metadata( + source_names=SOURCE_NAMES, + gap_filling_config=GAP_FILLING_CONFIG, + ) + + _validate_provenance_metadata( + cleaning_method=cleaning_method, + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + ) + + rank_colours = _build_rank_colours(metadata) + + background, background_cmap = ( + _encode_rank_background( + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + rank_colours=rank_colours, + ) + ) + + figure, axis = _plot_cleaning_background( + demand=demand, + background=background, + background_cmap=background_cmap, + ) + + mean_load_gw = _add_normalised_demand_traces( + axis=axis, + demand=demand, + ) + + _add_mean_load_labels( + axis=axis, + mean_load_gw=mean_load_gw, + countries=demand.columns, + ) + + legend_handles = _build_legend_handles( + metadata, + rank_colours, + ) + + figure.legend( + handles=legend_handles, + loc="center left", + bbox_to_anchor=(1.01, 0.5), + frameon=False, + ) + + OUTPUT_PATH.parent.mkdir( + parents=True, + exist_ok=True, + ) + + figure.savefig( + OUTPUT_PATH, + dpi=200, + bbox_inches="tight", + ) + + plt.close(figure) + + print( + f"\nSaved README figure to: {OUTPUT_PATH.resolve()}" + ) + + +if __name__ == "__main__": + main() \ No newline at end of file From 3add354ca42df67f98681d49559e8ed380b204f0 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 13:54:08 +0200 Subject: [PATCH 077/241] Reverted test_config to original temporal_scope --- tests/integration/test_config.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 67e6320..661f073 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -1,7 +1,7 @@ integration_test: temporal_scope: - start: "2016-01-01" - end: "2026-01-01" + start: "2017-01-01" + end: "2017-01-03" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api @@ -67,23 +67,23 @@ integration_test: end: "2024-02-01" weight: 1 - build_alb_from_gbr_summer: + build_alb_from_alb_2024: country: ALB - start: "2022-05-01" - end: "2022-06-01" - scope: fill_gaps + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite method: construct_from_sources sources: - - country: GBR - start: "2022-05-01" - end: "2022-06-01" + - country: ALB + start: "2024-01-01" + end: "2024-01-03" weight: 1 scaling: method: match_energy target_sources: - country: ALB - start: "2024-05-01" - end: "2024-06-01" + start: "2024-01-01" + end: "2024-01-03" weight: 1 example_inactive_mne_override: From 83fc72e35ffa9fc469d32f7c2ff3dd2e3de91522 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:46:25 +0000 Subject: [PATCH 078/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .gitignore | 2 +- INTERFACE.yaml | 2 +- config/README.md | 10 +- config/config.yaml | 10 +- tests/conftest.py | 6 +- tests/integration/test_config.yaml | 8 +- tests/unit/test_advanced_apply.py | 431 +++------------- tests/unit/test_advanced_combine_auxiliary.py | 73 +-- .../test_advanced_construct_from_sources.py | 217 ++------ tests/unit/test_advanced_plan.py | 263 ++-------- tests/unit/test_advanced_requirements.py | 190 ++----- tests/unit/test_advanced_source_requests.py | 307 ++--------- tests/unit/test_cleaning_pipeline.py | 484 ++++-------------- tests/unit/test_config_schema.py | 82 +-- tests/unit/test_external_profile.py | 89 +--- tests/unit/test_plan_auxiliary_data.py | 174 ++----- tests/unit/test_provenance.py | 40 +- .../test_workflow_environment_boundary.py | 13 +- workflow/internal/config.schema.yaml | 6 +- workflow/internal/settings.yaml | 2 +- workflow/rules/advanced.smk | 230 ++------- workflow/rules/automatic.smk | 51 +- workflow/rules/clean.smk | 121 ++--- workflow/scripts/apply_advanced_overrides.py | 31 +- workflow/scripts/clean_auxiliary_data.py | 18 +- workflow/scripts/clean_demand.py | 112 +--- workflow/scripts/cleaning/advanced/apply.py | 91 +--- .../scripts/cleaning/advanced/gap_report.py | 30 +- .../methods/construct_from_sources.py | 122 ++--- .../advanced/methods/external_profile.py | 37 +- .../cleaning/advanced/planning/execution.py | 87 +--- .../cleaning/advanced/planning/manifest.py | 92 +--- .../cleaning/advanced/planning/plan.py | 225 ++------ .../advanced/planning/requirements.py | 130 ++--- .../advanced/planning/source_requests.py | 119 +---- workflow/scripts/cleaning/basic/apply.py | 163 ++---- .../cleaning/basic/rules/average_periods.py | 64 +-- .../cleaning/basic/rules/copy_period.py | 26 +- .../basic/rules/linear_interpolation.py | 5 +- workflow/scripts/cleaning/combine_sources.py | 93 +--- workflow/scripts/cleaning/pipeline.py | 39 +- workflow/scripts/cleaning/plot_timeline.py | 414 ++++----------- workflow/scripts/cleaning/provenance.py | 73 +-- workflow/scripts/cleaning/sources/neso.py | 50 +- workflow/scripts/cleaning/validation.py | 30 +- workflow/scripts/combine_auxiliary_sources.py | 24 +- workflow/scripts/common/time.py | 10 +- .../scripts/construct_auxiliary_profile.py | 29 +- workflow/scripts/download_load_entsoe_api.py | 129 +---- workflow/scripts/download_load_neso.py | 178 ++----- workflow/scripts/finalise_clean_demand.py | 27 +- workflow/scripts/plan_auxiliary_data.py | 9 +- workflow/scripts/plot_cleaning_timeline.py | 15 +- .../scripts/plot_readme_cleaning_example.py | 131 ++--- workflow/scripts/prepare_load_neso.py | 167 ++---- workflow/scripts/prepare_load_opsd.py | 89 +--- 56 files changed, 1213 insertions(+), 4457 deletions(-) diff --git a/.gitignore b/.gitignore index 7fde88f..68f0b69 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ gurobi.log **/tmp/* #ignore ENTSOE token -resources/user/token_entsoe.txt \ No newline at end of file +resources/user/token_entsoe.txt diff --git a/INTERFACE.yaml b/INTERFACE.yaml index 229d494..f32b4e5 100644 --- a/INTERFACE.yaml +++ b/INTERFACE.yaml @@ -31,4 +31,4 @@ pathvars: description: "Hourly electricity demand per custom shape, in MW." wildcards: - shape: "Name of the custom target shape set. This wildcard allows multiple outputs for different shape definitions." \ No newline at end of file + shape: "Name of the custom target shape set. This wildcard allows multiple outputs for different shape definitions." diff --git a/config/README.md b/config/README.md index 806262d..7f7bd76 100644 --- a/config/README.md +++ b/config/README.md @@ -257,7 +257,7 @@ example_rule_construct_from_sources: The source period must describe the same number of hourly timestamps as the target period. -Multiple sources may be supplied. Their weight values determine their relative contribution to the constructed profile. +Multiple sources may be supplied. Their weight values determine their relative contribution to the constructed profile. > [!IMPORTANT] > Weighting is relative and values provided are normalised such that all weights sum to 1, i.e. in the example above, `GRC` has a relative contribution of `0.25` and `MKD` has a relative contribution of `0.75`. @@ -336,7 +336,7 @@ advanced: basic_cleaning: enabled: true ``` -When enabled, the same basic gap-filling logic is applied to auxiliary demand before it is used to construct an advanced profile. +When enabled, the same basic gap-filling logic is applied to auxiliary demand before it is used to construct an advanced profile. ## Complete example @@ -374,7 +374,7 @@ gap_filling: # This rule constructs a synthetic profile from GRC # and MKD Jan 2022, rescales to ALB Jan 2024 average - # energy levels, and fill gaps in ALB Jan 2022. It + # energy levels, and fill gaps in ALB Jan 2022. It # does not overwrite existing values. build_alb_winter: country: ALB @@ -410,7 +410,7 @@ gap_filling: method: external_profile path: inputs/external_profiles/alb_external.csv - # This rule intentionally leaves missing values. + # This rule intentionally leaves missing values. # This rule is inactive because its target period lies # outside the configured temporal_scope. leave_alb_gap: @@ -419,4 +419,4 @@ gap_filling: end: "2017-01-01" scope: fill_gaps method: leave_missing -``` \ No newline at end of file +``` diff --git a/config/config.yaml b/config/config.yaml index f781128..a1d3851 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -2,9 +2,9 @@ temporal_scope: start: "2016-01-01" end: "2026-01-01" -# provide a list of sources and set their priority order. +# provide a list of sources and set their priority order. # Options include: entsoe_api, neso, opsd_api -load_sources: +load_sources: - entsoe_api - neso - opsd_api @@ -88,7 +88,7 @@ gap_filling: start: "2024-05-01" end: "2024-06-01" weight: 1 - + example_inactive_mne_override: country: MNE start: "2020-03-01" @@ -100,11 +100,11 @@ gap_filling: start: "2022-03-01" end: "2022-04-01" weight: 1 - + test_external_profile_overwrite: country: ALB start: "2022-01-01 00:00" end: "2022-01-08 00:00" scope: overwrite method: external_profile - path: inputs/external_profiles/alb_external_test.csv \ No newline at end of file + path: inputs/external_profiles/alb_external_test.csv diff --git a/tests/conftest.py b/tests/conftest.py index 43f5843..1c77757 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,14 +7,10 @@ import pytest - MODULE_PATH = Path(__file__).resolve().parent.parent WORKFLOW_SCRIPTS = MODULE_PATH / "workflow" / "scripts" -sys.path.insert( - 0, - str(WORKFLOW_SCRIPTS), -) +sys.path.insert(0, str(WORKFLOW_SCRIPTS)) TEST_FILES = { diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 661f073..0bf1058 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -3,7 +3,7 @@ integration_test: start: "2017-01-01" end: "2017-01-03" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api - load_sources: + load_sources: - entsoe_api - neso - opsd_api @@ -85,7 +85,7 @@ integration_test: start: "2024-01-01" end: "2024-01-03" weight: 1 - + example_inactive_mne_override: country: MNE start: "2020-03-01" @@ -97,11 +97,11 @@ integration_test: start: "2022-03-01" end: "2022-04-01" weight: 1 - + test_external_profile_overwrite: country: ALB start: "2022-01-01 00:00" end: "2022-01-08 00:00" scope: overwrite method: external_profile - path: inputs/external_profiles/alb_external_test.csv \ No newline at end of file + path: inputs/external_profiles/alb_external_test.csv diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 6867e35..e95265e 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -9,60 +9,25 @@ def _load() -> pd.DataFrame: - index = pd.date_range( - "2021-01-01", - periods=3, - freq="h", - tz="UTC", - ) + index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") - return pd.DataFrame( - { - "ALB": [ - 1.0, - pd.NA, - 3.0, - ], - }, - index=index, - dtype="Float64", - ) + return pd.DataFrame({"ALB": [1.0, pd.NA, 3.0]}, index=index, dtype="Float64") def _cleaning_method() -> pd.DataFrame: - index = pd.date_range( - "2021-01-01", - periods=3, - freq="h", - tz="UTC", - ) + index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") return pd.DataFrame( - { - "ALB": [ - "observed_entsoe_api", - "missing", - "observed_entsoe_api", - ], - }, - index=index, + {"ALB": ["observed_entsoe_api", "missing", "observed_entsoe_api"]}, index=index ) def test_construct_from_sources_requires_profile() -> None: - rule = { - "method": "construct_from_sources", - } + rule = {"method": "construct_from_sources"} - with pytest.raises( - ValueError, - match="requires a constructed auxiliary profile", - ): + with pytest.raises(ValueError, match="requires a constructed auxiliary profile"): apply_auxiliary_fill_rule( - _load(), - _cleaning_method(), - rule_name="construct_albania", - rule=rule, + _load(), _cleaning_method(), rule_name="construct_albania", rule=rule ) @@ -70,14 +35,7 @@ def test_construct_from_sources_fills_gaps() -> None: load = _load() cleaning_method = _cleaning_method() - profile = pd.Series( - [ - 10.0, - 20.0, - 30.0, - ], - index=load.index, - ) + profile = pd.Series([10.0, 20.0, 30.0], index=load.index) rule = { "method": "construct_from_sources", @@ -88,18 +46,10 @@ def test_construct_from_sources_fills_gaps() -> None: } filled, methods = apply_auxiliary_fill_rule( - load, - cleaning_method, - rule_name="construct_albania", - rule=rule, - profile=profile, + load, cleaning_method, rule_name="construct_albania", rule=rule, profile=profile ) - assert filled["ALB"].tolist() == [ - 1.0, - 20.0, - 3.0, - ] + assert filled["ALB"].tolist() == [1.0, 20.0, 3.0] assert methods["ALB"].tolist() == [ "observed_entsoe_api", @@ -112,14 +62,7 @@ def test_construct_from_sources_overwrites_entire_period() -> None: load = _load() cleaning_method = _cleaning_method() - profile = pd.Series( - [ - 10.0, - 20.0, - 30.0, - ], - index=load.index, - ) + profile = pd.Series([10.0, 20.0, 30.0], index=load.index) rule = { "method": "construct_from_sources", @@ -130,18 +73,10 @@ def test_construct_from_sources_overwrites_entire_period() -> None: } filled, methods = apply_auxiliary_fill_rule( - load, - cleaning_method, - rule_name="construct_albania", - rule=rule, - profile=profile, + load, cleaning_method, rule_name="construct_albania", rule=rule, profile=profile ) - assert filled["ALB"].tolist() == [ - 10.0, - 20.0, - 30.0, - ] + assert filled["ALB"].tolist() == [10.0, 20.0, 30.0] assert methods["ALB"].tolist() == [ "construct_albania", @@ -158,36 +93,23 @@ def test_leave_missing_returns_unchanged_copies() -> None: load, cleaning_method, rule_name="leave_albania_missing", - rule={ - "method": "leave_missing", - }, + rule={"method": "leave_missing"}, ) - pd.testing.assert_frame_equal( - result_load, - load, - ) - pd.testing.assert_frame_equal( - result_method, - cleaning_method, - ) + pd.testing.assert_frame_equal(result_load, load) + pd.testing.assert_frame_equal(result_method, cleaning_method) assert result_load is not load assert result_method is not cleaning_method def test_rejects_unsupported_method() -> None: - with pytest.raises( - ValueError, - match="Unsupported advanced-fill method", - ): + with pytest.raises(ValueError, match="Unsupported advanced-fill method"): apply_auxiliary_fill_rule( _load(), _cleaning_method(), rule_name="invalid_rule", - rule={ - "method": "unknown", - }, + rule={"method": "unknown"}, ) @@ -195,23 +117,9 @@ def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: load = _load() cleaning_method = _cleaning_method() - first_profile = pd.Series( - [ - 10.0, - 20.0, - 30.0, - ], - index=load.index, - ) + first_profile = pd.Series([10.0, 20.0, 30.0], index=load.index) - second_profile = pd.Series( - [ - 100.0, - 200.0, - 300.0, - ], - index=load.index, - ) + second_profile = pd.Series([100.0, 200.0, 300.0], index=load.index) overrides = { "fill_gaps": { @@ -230,30 +138,20 @@ def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: }, } - profiles = { - "fill_gaps": first_profile, - "overwrite": second_profile, - } + profiles = {"fill_gaps": first_profile, "overwrite": second_profile} filled, methods = apply_auxiliary_fill_rules( load, cleaning_method, overrides=overrides, constructed_profiles=profiles, - external_profiles={} + external_profiles={}, ) - assert filled["ALB"].tolist() == [ - 100.0, - 200.0, - 300.0, - ] + assert filled["ALB"].tolist() == [100.0, 200.0, 300.0] + + assert methods["ALB"].tolist() == ["overwrite", "overwrite", "overwrite"] - assert methods["ALB"].tolist() == [ - "overwrite", - "overwrite", - "overwrite", - ] def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: load = _load() @@ -267,46 +165,21 @@ def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: external_profiles={}, ) - pd.testing.assert_frame_equal( - filled, - load, - ) - pd.testing.assert_frame_equal( - methods, - cleaning_method, - ) + pd.testing.assert_frame_equal(filled, load) + pd.testing.assert_frame_equal(methods, cleaning_method) assert filled is not load assert methods is not cleaning_method def test_overwrite_replaces_existing_values() -> None: - index = pd.date_range( - "2022-01-01", - periods=4, - freq="h", - tz="UTC", - ) + index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") - load = pd.DataFrame( - { - "ALB": [10.0, 20.0, 30.0, 40.0], - }, - index=index, - ) + load = pd.DataFrame({"ALB": [10.0, 20.0, 30.0, 40.0]}, index=index) - cleaning_method = pd.DataFrame( - { - "ALB": ["observed_entsoe_api"] * 4, - }, - index=index, - ) + cleaning_method = pd.DataFrame({"ALB": ["observed_entsoe_api"] * 4}, index=index) - profile = pd.Series( - [100.0, 200.0], - index=index[1:3], - name="ALB", - ) + profile = pd.Series([100.0, 200.0], index=index[1:3], name="ALB") overrides = { "replace_albania": { @@ -330,18 +203,11 @@ def test_overwrite_replaces_existing_values() -> None: load, cleaning_method, overrides=overrides, - constructed_profiles={ - "replace_albania": profile, - }, - external_profiles={} + constructed_profiles={"replace_albania": profile}, + external_profiles={}, ) - assert filled["ALB"].tolist() == [ - 10.0, - 100.0, - 200.0, - 40.0, - ] + assert filled["ALB"].tolist() == [10.0, 100.0, 200.0, 40.0] assert methods["ALB"].tolist() == [ "observed_entsoe_api", @@ -350,20 +216,11 @@ def test_overwrite_replaces_existing_values() -> None: "observed_entsoe_api", ] + def test_fill_gaps_preserves_existing_values() -> None: - index = pd.date_range( - "2022-01-01", - periods=4, - freq="h", - tz="UTC", - ) + index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") - load = pd.DataFrame( - { - "ALB": [10.0, float("nan"), 30.0, 40.0], - }, - index=index, - ) + load = pd.DataFrame({"ALB": [10.0, float("nan"), 30.0, 40.0]}, index=index) cleaning_method = pd.DataFrame( { @@ -372,16 +229,12 @@ def test_fill_gaps_preserves_existing_values() -> None: "missing", "observed_entsoe_api", "observed_entsoe_api", - ], + ] }, index=index, ) - profile = pd.Series( - [100.0, 200.0], - index=index[1:3], - name="ALB", - ) + profile = pd.Series([100.0, 200.0], index=index[1:3], name="ALB") overrides = { "fill_albania": { @@ -405,18 +258,11 @@ def test_fill_gaps_preserves_existing_values() -> None: load, cleaning_method, overrides=overrides, - constructed_profiles={ - "fill_albania": profile, - }, + constructed_profiles={"fill_albania": profile}, external_profiles={}, ) - assert filled["ALB"].tolist() == [ - 10.0, - 100.0, - 30.0, - 40.0, - ] + assert filled["ALB"].tolist() == [10.0, 100.0, 30.0, 40.0] assert methods["ALB"].tolist() == [ "observed_entsoe_api", @@ -427,31 +273,13 @@ def test_fill_gaps_preserves_existing_values() -> None: def test_external_profile_fill_gaps_only_replaces_missing_values(): - index = pd.date_range( - "2025-01-01", - periods=4, - freq="h", - tz="UTC", - ) + index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") - load = pd.DataFrame( - { - "ALB": [100.0, None, 300.0, None], - }, - index=index, - ) + load = pd.DataFrame({"ALB": [100.0, None, 300.0, None]}, index=index) - cleaning_method = pd.DataFrame( - None, - index=index, - columns=["ALB"], - dtype=object, - ) + cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - profile = pd.Series( - [110.0, 220.0, 330.0, 440.0], - index=index, - ) + profile = pd.Series([110.0, 220.0, 330.0, 440.0], index=index) overrides = { "external_albania": { @@ -468,53 +296,27 @@ def test_external_profile_fill_gaps_only_replaces_missing_values(): cleaning_method, overrides=overrides, constructed_profiles={}, - external_profiles={ - "external_albania": profile, - }, + external_profiles={"external_albania": profile}, ) - expected = pd.Series( - [100.0, 220.0, 300.0, 440.0], - index=index, - name="ALB", - ) + expected = pd.Series([100.0, 220.0, 300.0, 440.0], index=index, name="ALB") - pd.testing.assert_series_equal( - filled["ALB"], - expected, - ) + pd.testing.assert_series_equal(filled["ALB"], expected) assert pd.isna(methods.loc[index[0], "ALB"]) assert methods.loc[index[1], "ALB"] == "external_albania" assert pd.isna(methods.loc[index[2], "ALB"]) assert methods.loc[index[3], "ALB"] == "external_albania" + def test_external_profile_overwrite_replaces_supplied_values(): - index = pd.date_range( - "2025-01-01", - periods=4, - freq="h", - tz="UTC", - ) + index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") - load = pd.DataFrame( - { - "ALB": [100.0, 200.0, 300.0, 400.0], - }, - index=index, - ) + load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0]}, index=index) - cleaning_method = pd.DataFrame( - None, - index=index, - columns=["ALB"], - dtype=object, - ) + cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - profile = pd.Series( - [110.0, 220.0, 330.0, 440.0], - index=index, - ) + profile = pd.Series([110.0, 220.0, 330.0, 440.0], index=index) overrides = { "external_albania": { @@ -531,57 +333,24 @@ def test_external_profile_overwrite_replaces_supplied_values(): cleaning_method, overrides=overrides, constructed_profiles={}, - external_profiles={ - "external_albania": profile, - }, + external_profiles={"external_albania": profile}, ) - expected = pd.Series( - [110.0, 220.0, 330.0, 440.0], - index=index, - name="ALB", - ) + expected = pd.Series([110.0, 220.0, 330.0, 440.0], index=index, name="ALB") - pd.testing.assert_series_equal( - filled["ALB"], - expected, - ) + pd.testing.assert_series_equal(filled["ALB"], expected) - assert ( - methods["ALB"] - == "external_albania" - ).all() + assert (methods["ALB"] == "external_albania").all() def test_external_profile_overwrite_only_replaces_supplied_timestamps(): - index = pd.date_range( - "2025-01-01", - periods=5, - freq="h", - tz="UTC", - ) + index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") - load = pd.DataFrame( - { - "ALB": [100.0, 200.0, 300.0, 400.0, 500.0], - }, - index=index, - ) + load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) - cleaning_method = pd.DataFrame( - None, - index=index, - columns=["ALB"], - dtype=object, - ) + cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - profile = pd.Series( - [2200.0, 4400.0], - index=[ - index[1], - index[3], - ], - ) + profile = pd.Series([2200.0, 4400.0], index=[index[1], index[3]]) overrides = { "external_albania": { @@ -598,21 +367,12 @@ def test_external_profile_overwrite_only_replaces_supplied_timestamps(): cleaning_method, overrides=overrides, constructed_profiles={}, - external_profiles={ - "external_albania": profile, - }, + external_profiles={"external_albania": profile}, ) - expected = pd.Series( - [100.0, 2200.0, 300.0, 4400.0, 500.0], - index=index, - name="ALB", - ) + expected = pd.Series([100.0, 2200.0, 300.0, 4400.0, 500.0], index=index, name="ALB") - pd.testing.assert_series_equal( - filled["ALB"], - expected, - ) + pd.testing.assert_series_equal(filled["ALB"], expected) assert methods.loc[index[1], "ALB"] == "external_albania" assert methods.loc[index[3], "ALB"] == "external_albania" @@ -623,29 +383,13 @@ def test_external_profile_overwrite_only_replaces_supplied_timestamps(): def test_external_profile_ignores_values_outside_rule_period(): - index = pd.date_range( - "2025-01-01", - periods=5, - freq="h", - tz="UTC", - ) + index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") - load = pd.DataFrame( - {"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, - index=index, - ) + load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) - cleaning_method = pd.DataFrame( - None, - index=index, - columns=["ALB"], - dtype=object, - ) + cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - profile = pd.Series( - [1000.0, 2000.0, 3000.0, 4000.0, 5000.0], - index=index, - ) + profile = pd.Series([1000.0, 2000.0, 3000.0, 4000.0, 5000.0], index=index) overrides = { "external_albania": { @@ -662,42 +406,22 @@ def test_external_profile_ignores_values_outside_rule_period(): cleaning_method, overrides=overrides, constructed_profiles={}, - external_profiles={ - "external_albania": profile, - }, + external_profiles={"external_albania": profile}, ) expected = pd.Series( - [100.0, 2000.0, 3000.0, 4000.0, 500.0], - index=index, - name="ALB", + [100.0, 2000.0, 3000.0, 4000.0, 500.0], index=index, name="ALB" ) - pd.testing.assert_series_equal( - filled["ALB"], - expected, - ) + pd.testing.assert_series_equal(filled["ALB"], expected) def test_external_profile_requires_profile(): - index = pd.date_range( - "2025-01-01", - periods=2, - freq="h", - tz="UTC", - ) + index = pd.date_range("2025-01-01", periods=2, freq="h", tz="UTC") - load = pd.DataFrame( - {"ALB": [None, None]}, - index=index, - ) + load = pd.DataFrame({"ALB": [None, None]}, index=index) - cleaning_method = pd.DataFrame( - None, - index=index, - columns=["ALB"], - dtype=object, - ) + cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) overrides = { "external_albania": { @@ -709,10 +433,7 @@ def test_external_profile_requires_profile(): } } - with pytest.raises( - ValueError, - match="requires an external profile", - ): + with pytest.raises(ValueError, match="requires an external profile"): apply_auxiliary_fill_rules( load, cleaning_method, @@ -720,5 +441,3 @@ def test_external_profile_requires_profile(): constructed_profiles={}, external_profiles={}, ) - - diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py index b311ce6..c5152e2 100644 --- a/tests/unit/test_advanced_combine_auxiliary.py +++ b/tests/unit/test_advanced_combine_auxiliary.py @@ -3,83 +3,34 @@ def test_combine_auxiliary_sources_aligns_country_columns() -> None: - index = pd.date_range( - "2020-01-01", - periods=2, - freq="h", - tz="UTC", - ) + index = pd.date_range("2020-01-01", periods=2, freq="h", tz="UTC") - entsoe = pd.DataFrame( - { - "GBR": [10.0, 11.0], - "GRC": [20.0, 21.0], - }, - index=index, - ) + entsoe = pd.DataFrame({"GBR": [10.0, 11.0], "GRC": [20.0, 21.0]}, index=index) - neso = pd.DataFrame( - { - "GBR": [12.0, 13.0], - }, - index=index, - ) + neso = pd.DataFrame({"GBR": [12.0, 13.0]}, index=index) - combined, data_source, cleaning_method = ( - combine_auxiliary_sources( - { - "entsoe_api": entsoe, - "neso": neso, - }, - priority=[ - "neso", - "entsoe_api", - ], - ) + combined, data_source, cleaning_method = combine_auxiliary_sources( + {"entsoe_api": entsoe, "neso": neso}, priority=["neso", "entsoe_api"] ) - assert list(combined.columns) == [ - "GBR", - "GRC", - ] + assert list(combined.columns) == ["GBR", "GRC"] - assert combined["GBR"].tolist() == [ - 12.0, - 13.0, - ] + assert combined["GBR"].tolist() == [12.0, 13.0] - assert combined["GRC"].tolist() == [ - 20.0, - 21.0, - ] + assert combined["GRC"].tolist() == [20.0, 21.0] - assert data_source["GBR"].tolist() == [ - "neso", - "neso", - ] + assert data_source["GBR"].tolist() == ["neso", "neso"] - assert data_source["GRC"].tolist() == [ - "entsoe_api", - "entsoe_api", - ] + assert data_source["GRC"].tolist() == ["entsoe_api", "entsoe_api"] assert cleaning_method.shape == combined.shape def test_combine_auxiliary_sources_handles_empty_input() -> None: - combined, data_source, cleaning_method = ( - combine_auxiliary_sources( - {}, - priority=[ - "entsoe_api", - "neso", - "opsd_api", - ], - ) + combined, data_source, cleaning_method = combine_auxiliary_sources( + {}, priority=["entsoe_api", "neso", "opsd_api"] ) assert combined.empty assert data_source.empty assert cleaning_method.empty - - diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py index bdbb450..c8e84a1 100644 --- a/tests/unit/test_advanced_construct_from_sources.py +++ b/tests/unit/test_advanced_construct_from_sources.py @@ -5,30 +5,11 @@ def test_construct_from_single_source() -> None: - auxiliary_index = pd.date_range( - "2019-01-01", - periods=3, - freq="h", - tz="UTC", - ) + auxiliary_index = pd.date_range("2019-01-01", periods=3, freq="h", tz="UTC") - auxiliary = pd.DataFrame( - { - "GRC": [ - 10.0, - 20.0, - 30.0, - ] - }, - index=auxiliary_index, - ) + auxiliary = pd.DataFrame({"GRC": [10.0, 20.0, 30.0]}, index=auxiliary_index) - target_index = pd.date_range( - "2020-01-01", - periods=3, - freq="h", - tz="UTC", - ) + target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") result = construct_from_sources( auxiliary, @@ -43,51 +24,19 @@ def test_construct_from_single_source() -> None: ], ) - expected = pd.Series( - [ - 10.0, - 20.0, - 30.0, - ], - index=target_index, - dtype=float, - ) + expected = pd.Series([10.0, 20.0, 30.0], index=target_index, dtype=float) + + pd.testing.assert_series_equal(result, expected) - pd.testing.assert_series_equal( - result, - expected, - ) def test_construct_from_sources_uses_weighted_mean() -> None: - auxiliary_index = pd.date_range( - "2019-01-01", - periods=3, - freq="h", - tz="UTC", - ) + auxiliary_index = pd.date_range("2019-01-01", periods=3, freq="h", tz="UTC") auxiliary = pd.DataFrame( - { - "GRC": [ - 10.0, - 20.0, - 30.0, - ], - "GBR": [ - 30.0, - 40.0, - 50.0, - ], - }, - index=auxiliary_index, + {"GRC": [10.0, 20.0, 30.0], "GBR": [30.0, 40.0, 50.0]}, index=auxiliary_index ) - target_index = pd.date_range( - "2020-01-01", - periods=3, - freq="h", - tz="UTC", - ) + target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") result = construct_from_sources( auxiliary, @@ -108,61 +57,30 @@ def test_construct_from_sources_uses_weighted_mean() -> None: ], ) - expected = pd.Series( - [ - 25.0, - 35.0, - 45.0, - ], - index=target_index, - dtype=float, - ) + expected = pd.Series([25.0, 35.0, 45.0], index=target_index, dtype=float) - pd.testing.assert_series_equal( - result, - expected, - ) + pd.testing.assert_series_equal(result, expected) def test_construct_from_sources_drops_february_29() -> None: source_index = pd.date_range( - "2020-02-01", - "2020-03-01", - freq="h", - inclusive="left", - tz="UTC", + "2020-02-01", "2020-03-01", freq="h", inclusive="left", tz="UTC" ) - values = pd.Series( - 1.0, - index=source_index, - ) + values = pd.Series(1.0, index=source_index) - values.loc[ - (values.index.month == 2) - & (values.index.day == 29) - ] = 999.0 + values.loc[(values.index.month == 2) & (values.index.day == 29)] = 999.0 auxiliary = values.to_frame("GBR") target_index = pd.date_range( - "2021-02-01", - "2021-03-01", - freq="h", - inclusive="left", - tz="UTC", + "2021-02-01", "2021-03-01", freq="h", inclusive="left", tz="UTC" ) result = construct_from_sources( auxiliary, target_index=target_index, - sources=[ - { - "country": "GBR", - "start": "2020-02-01", - "end": "2020-03-01", - } - ], + sources=[{"country": "GBR", "start": "2020-02-01", "end": "2020-03-01"}], ) assert len(result) == 672 @@ -171,94 +89,45 @@ def test_construct_from_sources_drops_february_29() -> None: def test_construct_from_sources_interpolates_february_29() -> None: auxiliary_index = pd.date_range( - "2021-02-01", - "2021-03-02", - freq="h", - inclusive="left", - tz="UTC", + "2021-02-01", "2021-03-02", freq="h", inclusive="left", tz="UTC" ) - values = pd.Series( - 10.0, - index=auxiliary_index, - ) + values = pd.Series(10.0, index=auxiliary_index) - values.loc[ - (values.index.month == 2) - & (values.index.day == 28) - ] = 20.0 + values.loc[(values.index.month == 2) & (values.index.day == 28)] = 20.0 - values.loc[ - (values.index.month == 3) - & (values.index.day == 1) - ] = 40.0 + values.loc[(values.index.month == 3) & (values.index.day == 1)] = 40.0 auxiliary = values.to_frame("GBR") target_index = pd.date_range( - "2020-02-01", - "2020-03-01", - freq="h", - inclusive="left", - tz="UTC", + "2020-02-01", "2020-03-01", freq="h", inclusive="left", tz="UTC" ) result = construct_from_sources( auxiliary, target_index=target_index, - sources=[ - { - "country": "GBR", - "start": "2021-02-01", - "end": "2021-03-01", - } - ], + sources=[{"country": "GBR", "start": "2021-02-01", "end": "2021-03-01"}], ) - target_feb_29 = ( - (result.index.month == 2) - & (result.index.day == 29) - ) + target_feb_29 = (result.index.month == 2) & (result.index.day == 29) assert len(result) == 696 assert (result.loc[target_feb_29] == 30.0).all() + def test_construct_from_sources_matches_reference_energy() -> None: - auxiliary_index = pd.date_range( - "2019-01-01", - periods=6, - freq="h", - tz="UTC", - ) + auxiliary_index = pd.date_range("2019-01-01", periods=6, freq="h", tz="UTC") auxiliary = pd.DataFrame( { - "GBR": [ - 10.0, - 20.0, - 30.0, - 100.0, - 100.0, - 100.0, - ], - "ALB": [ - 0.0, - 0.0, - 0.0, - 20.0, - 40.0, - 60.0, - ], + "GBR": [10.0, 20.0, 30.0, 100.0, 100.0, 100.0], + "ALB": [0.0, 0.0, 0.0, 20.0, 40.0, 60.0], }, index=auxiliary_index, ) - target_index = pd.date_range( - "2020-01-01", - periods=3, - freq="h", - tz="UTC", - ) + target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") result = construct_from_sources( auxiliary, @@ -282,28 +151,13 @@ def test_construct_from_sources_matches_reference_energy() -> None: }, ) - expected = pd.Series( - [ - 20.0, - 40.0, - 60.0, - ], - index=target_index, - dtype=float, - ) + expected = pd.Series([20.0, 40.0, 60.0], index=target_index, dtype=float) + + pd.testing.assert_series_equal(result, expected) - pd.testing.assert_series_equal( - result, - expected, - ) def test_match_energy_uses_weighted_target_energy() -> None: - auxiliary_index = pd.date_range( - "2019-01-01", - periods=6, - freq="h", - tz="UTC", - ) + auxiliary_index = pd.date_range("2019-01-01", periods=6, freq="h", tz="UTC") auxiliary = pd.DataFrame( { @@ -315,12 +169,7 @@ def test_match_energy_uses_weighted_target_energy() -> None: dtype=float, ) - target_index = pd.date_range( - "2020-01-01", - periods=3, - freq="h", - tz="UTC", - ) + target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") result = construct_from_sources( auxiliary, diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index 5c0625a..d84a672 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -9,15 +9,9 @@ TARGET_COUNTRIES = ["ALB"] -TARGET_START = pd.Timestamp( - "2022-01-01", - tz="UTC", -) +TARGET_START = pd.Timestamp("2022-01-01", tz="UTC") -TARGET_END = pd.Timestamp( - "2025-01-01", - tz="UTC", -) +TARGET_END = pd.Timestamp("2025-01-01", tz="UTC") def test_validate_construct_from_sources_rule() -> None: @@ -28,11 +22,7 @@ def test_validate_construct_from_sources_rule() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MKD", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - }, + {"country": "MKD", "start": "2023-01-01", "end": "2023-12-31 23:00"}, { "country": "MNE", "start": "2023-01-01", @@ -42,10 +32,7 @@ def test_validate_construct_from_sources_rule() -> None: ], } - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_validate_external_profile_rule() -> None: @@ -57,10 +44,7 @@ def test_validate_external_profile_rule() -> None: "method": "external_profile", } - validate_auxiliary_fill_rule( - "external_albania_2023", - rule, - ) + validate_auxiliary_fill_rule("external_albania_2023", rule) def test_construct_from_sources_requires_sources() -> None: @@ -72,14 +56,8 @@ def test_construct_from_sources_requires_sources() -> None: "method": "construct_from_sources", } - with pytest.raises( - ValueError, - match="must define 'sources'", - ): - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + with pytest.raises(ValueError, match="must define 'sources'"): + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_source_weight_must_be_positive() -> None: @@ -99,14 +77,8 @@ def test_source_weight_must_be_positive() -> None: ], } - with pytest.raises( - ValueError, - match="must be greater than zero", - ): - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + with pytest.raises(ValueError, match="must be greater than zero"): + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_validate_construct_from_sources_with_scaling() -> None: @@ -117,20 +89,12 @@ def test_validate_construct_from_sources_with_scaling() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MKD", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - } + {"country": "MKD", "start": "2023-01-01", "end": "2023-12-31 23:00"} ], "scaling": { "method": "match_energy", "target_sources": [ - { - "country": "ALB", - "start": "2022-01-01", - "end": "2022-12-31 23:00", - }, + {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"}, { "country": "ALB", "start": "2024-01-01", @@ -141,10 +105,7 @@ def test_validate_construct_from_sources_with_scaling() -> None: }, } - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_match_energy_scaling_requires_target_sources() -> None: @@ -155,25 +116,13 @@ def test_match_energy_scaling_requires_target_sources() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - } + {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"} ], - "scaling": { - "method": "match_energy", - }, + "scaling": {"method": "match_energy"}, } - with pytest.raises( - ValueError, - match="must define 'target_sources'", - ): - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + with pytest.raises(ValueError, match="must define 'target_sources'"): + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_rejects_unsupported_scaling_method() -> None: @@ -184,32 +133,18 @@ def test_rejects_unsupported_scaling_method() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - } + {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"} ], "scaling": { "method": "unknown", "target_sources": [ - { - "country": "ALB", - "start": "2022-01-01", - "end": "2022-12-31 23:00", - } + {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} ], }, } - with pytest.raises( - ValueError, - match="Unsupported scaling method", - ): - validate_auxiliary_fill_rule( - "replace_albania_2023", - rule, - ) + with pytest.raises(ValueError, match="Unsupported scaling method"): + validate_auxiliary_fill_rule("replace_albania_2023", rule) def test_build_auxiliary_fill_plan_normalizes_rules() -> None: @@ -228,11 +163,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - }, + {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"}, { "country": "MKD", "start": "2023-01-01", @@ -243,11 +174,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scaling": { "method": "match_energy", "target_sources": [ - { - "country": "ALB", - "start": "2022-01-01", - "end": "2022-12-31 23:00", - } + {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} ], }, }, @@ -262,61 +189,25 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: expected = pd.DataFrame( { - "rule_name": [ - "construct_albania", - "external_albania", - ], - "country": [ - "ALB", - "ALB", - ], + "rule_name": ["construct_albania", "external_albania"], + "country": ["ALB", "ALB"], "target_start": [ - pd.Timestamp( - "2023-01-01", - tz="UTC", - ), - pd.Timestamp( - "2024-01-01", - tz="UTC", - ), + pd.Timestamp("2023-01-01", tz="UTC"), + pd.Timestamp("2024-01-01", tz="UTC"), ], "target_end": [ - pd.Timestamp( - "2023-12-31 23:00", - tz="UTC", - ), - pd.Timestamp( - "2024-12-31 23:00", - tz="UTC", - ), - ], - "scope": [ - "overwrite", - "overwrite", - ], - "method": [ - "construct_from_sources", - "external_profile", - ], - "status": [ - "ready", - "ready", - ], - "source_count": [ - 2, - 0, - ], - "scaling_method": [ - "match_energy", - None, + pd.Timestamp("2023-12-31 23:00", tz="UTC"), + pd.Timestamp("2024-12-31 23:00", tz="UTC"), ], + "scope": ["overwrite", "overwrite"], + "method": ["construct_from_sources", "external_profile"], + "status": ["ready", "ready"], + "source_count": [2, 0], + "scaling_method": ["match_energy", None], } ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: @@ -341,10 +232,7 @@ def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: ] ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: @@ -355,13 +243,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2023-01-01", - "end": "2023-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], }, "montenegro": { "country": "MNE", @@ -369,13 +251,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2023-01-01", - "end": "2023-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], }, } @@ -386,9 +262,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: target_end=TARGET_END, ) - assert result["rule_name"].tolist() == [ - "albania" - ] + assert result["rule_name"].tolist() == ["albania"] def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: @@ -399,13 +273,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2020-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2020-01-01", - "end": "2020-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], }, "after": { "country": "ALB", @@ -413,13 +281,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2026-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2026-01-01", - "end": "2026-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2026-01-01", "end": "2026-02-01"}], }, } @@ -441,14 +303,8 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: "end": "2022-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2021-12-01", - "end": "2022-02-01", - } - ], - }, + "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-02-01"}], + } } result = build_auxiliary_fill_plan( @@ -458,9 +314,7 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: target_end=TARGET_END, ) - assert result["rule_name"].tolist() == [ - "partial" - ] + assert result["rule_name"].tolist() == ["partial"] def test_build_auxiliary_fill_plan_validates_inactive_rule() -> None: @@ -471,20 +325,11 @@ def test_build_auxiliary_fill_plan_validates_inactive_rule() -> None: "end": "2020-02-01", "scope": "not_a_scope", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2020-01-01", - "end": "2020-02-01", - } - ], - }, + "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], + } } - with pytest.raises( - ValueError, - match="Unsupported scope", - ): + with pytest.raises(ValueError, match="Unsupported scope"): build_auxiliary_fill_plan( rules, target_countries=TARGET_COUNTRIES, @@ -501,13 +346,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2022-01-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2021-12-01", - "end": "2022-01-01", - } - ], + "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-01-01"}], }, "starts_at_end": { "country": "ALB", @@ -515,13 +354,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2025-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2025-01-01", - "end": "2025-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2025-01-01", "end": "2025-02-01"}], }, } diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py index 55c9936..1a52e57 100644 --- a/tests/unit/test_advanced_requirements.py +++ b/tests/unit/test_advanced_requirements.py @@ -19,11 +19,7 @@ def test_compile_auxiliary_requirements_collects_sources() -> None: "scope": "fill_gaps", "method": "construct_from_sources", "sources": [ - { - "country": "GRC", - "start": "2019-01-01", - "end": "2019-01-03", - }, + {"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"}, { "country": "MNE", "start": "2020-01-01", @@ -31,7 +27,7 @@ def test_compile_auxiliary_requirements_collects_sources() -> None: "weight": 2, }, ], - }, + } } result = compile_auxiliary_requirements(overrides) @@ -39,27 +35,12 @@ def test_compile_auxiliary_requirements_collects_sources() -> None: expected = pd.DataFrame( { "country": ["GRC", "MNE"], - "start": pd.to_datetime( - [ - "2019-01-01", - "2020-01-01", - ], - utc=True, - ), - "end": pd.to_datetime( - [ - "2019-01-03", - "2020-01-03", - ], - utc=True, - ), + "start": pd.to_datetime(["2019-01-01", "2020-01-01"], utc=True), + "end": pd.to_datetime(["2019-01-03", "2020-01-03"], utc=True), } ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) def test_compile_auxiliary_requirements_includes_scaling_sources() -> None: @@ -70,58 +51,31 @@ def test_compile_auxiliary_requirements_includes_scaling_sources() -> None: "end": "2021-01-03", "scope": "fill_gaps", "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2019-01-01", - "end": "2019-01-03", - }, - ], + "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"}], "scaling": { "method": "match_energy", "target_sources": [ - { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-01-03", - }, + {"country": "ALB", "start": "2020-01-01", "end": "2020-01-03"} ], }, - }, + } } result = compile_auxiliary_requirements(overrides) - assert list(result["country"]) == [ - "ALB", - "GRC", - ] + assert list(result["country"]) == ["ALB", "GRC"] - assert result.loc[ - result["country"] == "ALB", - "start", - ].iloc[0] == pd.Timestamp( - "2020-01-01", - tz="UTC", + assert result.loc[result["country"] == "ALB", "start"].iloc[0] == pd.Timestamp( + "2020-01-01", tz="UTC" ) def test_compile_auxiliary_requirements_deduplicates_sources() -> None: - source = { - "country": "GRC", - "start": "2019-01-01", - "end": "2019-01-03", - } + source = {"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"} overrides = { - "first_rule": { - "method": "construct_from_sources", - "sources": [source], - }, - "second_rule": { - "method": "construct_from_sources", - "sources": [source], - }, + "first_rule": {"method": "construct_from_sources", "sources": [source]}, + "second_rule": {"method": "construct_from_sources", "sources": [source]}, } result = compile_auxiliary_requirements(overrides) @@ -131,12 +85,8 @@ def test_compile_auxiliary_requirements_deduplicates_sources() -> None: def test_compile_auxiliary_requirements_ignores_other_methods() -> None: overrides = { - "manual_case": { - "method": "leave_missing", - }, - "leave_case": { - "method": "leave_missing", - }, + "manual_case": {"method": "leave_missing"}, + "leave_case": {"method": "leave_missing"}, } result = compile_auxiliary_requirements(overrides) @@ -151,77 +101,44 @@ def test_compile_auxiliary_requirements_returns_empty_schema() -> None: assert result.empty assert list(result.columns) == REQUIREMENT_COLUMNS + def test_compile_auxiliary_requirements_merges_overlapping_periods() -> None: overrides = { "first_rule": { "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2019-01-01", - "end": "2019-06-01", - }, - ], + "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-06-01"}], }, "second_rule": { "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2019-05-01", - "end": "2019-12-01", - }, - ], + "sources": [{"country": "GRC", "start": "2019-05-01", "end": "2019-12-01"}], }, } result = compile_auxiliary_requirements(overrides) assert len(result) == 1 - assert result.iloc[0]["start"] == pd.Timestamp( - "2019-01-01", - tz="UTC", - ) - assert result.iloc[0]["end"] == pd.Timestamp( - "2019-12-01", - tz="UTC", - ) + assert result.iloc[0]["start"] == pd.Timestamp("2019-01-01", tz="UTC") + assert result.iloc[0]["end"] == pd.Timestamp("2019-12-01", tz="UTC") + def test_compile_auxiliary_requirements_merges_adjacent_periods() -> None: overrides = { "first_rule": { "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2019-01-01", - "end": "2019-02-01", - }, - ], + "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-02-01"}], }, "second_rule": { "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2019-02-01", - "end": "2019-03-01", - }, - ], + "sources": [{"country": "GRC", "start": "2019-02-01", "end": "2019-03-01"}], }, } result = compile_auxiliary_requirements(overrides) assert len(result) == 1 - assert result.iloc[0]["start"] == pd.Timestamp( - "2019-01-01", - tz="UTC", - ) - assert result.iloc[0]["end"] == pd.Timestamp( - "2019-03-01", - tz="UTC", - ) + assert result.iloc[0]["start"] == pd.Timestamp("2019-01-01", tz="UTC") + assert result.iloc[0]["end"] == pd.Timestamp("2019-03-01", tz="UTC") + def test_expand_requirements_without_rules_is_unchanged() -> None: requirements = pd.DataFrame( @@ -232,15 +149,10 @@ def test_expand_requirements_without_rules_is_unchanged() -> None: } ) - result = expand_auxiliary_requirements( - requirements, - rules=[], - ) + result = expand_auxiliary_requirements(requirements, rules=[]) + + pd.testing.assert_frame_equal(result, requirements) - pd.testing.assert_frame_equal( - result, - requirements, - ) def test_expand_requirements_can_be_disabled() -> None: requirements = pd.DataFrame( @@ -260,16 +172,9 @@ def test_expand_requirements_can_be_disabled() -> None: } ] - result = expand_auxiliary_requirements( - requirements, - rules=rules, - enabled=False, - ) + result = expand_auxiliary_requirements(requirements, rules=rules, enabled=False) - pd.testing.assert_frame_equal( - result, - requirements, - ) + pd.testing.assert_frame_equal(result, requirements) def test_basic_context_for_copy_period() -> None: @@ -294,10 +199,7 @@ def test_basic_context_compounds_across_ordered_rules() -> None: "name": "average_adjacent_weeks", "method": "average_periods", "max_gap": "168h", - "source_offsets": [ - "-168h", - "168h", - ], + "source_offsets": ["-168h", "168h"], }, { "name": "copy_previous_week", @@ -317,14 +219,8 @@ def test_build_auxiliary_acquisition_requirements_uses_basic_cleaning_config() - overrides = { "reconstruct_albania": { "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2020-01-01", - "end": "2020-02-01", - }, - ], - }, + "sources": [{"country": "GRC", "start": "2020-01-01", "end": "2020-02-01"}], + } } basic_rules = [ @@ -333,20 +229,12 @@ def test_build_auxiliary_acquisition_requirements_uses_basic_cleaning_config() - "method": "copy_period", "max_gap": "168h", "source_offset": "-168h", - }, + } ] result = build_auxiliary_acquisition_requirements( - overrides=overrides, - basic_rules=basic_rules, - basic_cleaning_enabled=True, + overrides=overrides, basic_rules=basic_rules, basic_cleaning_enabled=True ) - assert result.iloc[0]["start"] == pd.Timestamp( - "2019-12-25", - tz="UTC", - ) - assert result.iloc[0]["end"] == pd.Timestamp( - "2020-02-08", - tz="UTC", - ) + assert result.iloc[0]["start"] == pd.Timestamp("2019-12-25", tz="UTC") + assert result.iloc[0]["end"] == pd.Timestamp("2020-02-08", tz="UTC") diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index 3521d46..803ab23 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -15,38 +15,16 @@ def test_build_source_requests_uses_all_applicable_sources() -> None: requirements = pd.DataFrame( { "country": ["GBR", "GRC"], - "start": pd.to_datetime( - [ - "2020-01-01", - "2021-01-01", - ], - utc=True, - ), - "end": pd.to_datetime( - [ - "2020-02-01", - "2021-02-01", - ], - utc=True, - ), + "start": pd.to_datetime(["2020-01-01", "2021-01-01"], utc=True), + "end": pd.to_datetime(["2020-02-01", "2021-02-01"], utc=True), } ) result = build_auxiliary_source_requests( - requirements, - source_names=[ - "entsoe_api", - "neso", - "opsd_api", - ], + requirements, source_names=["entsoe_api", "neso", "opsd_api"] ) - assert list( - result[["source", "country"]].itertuples( - index=False, - name=None, - ) - ) == [ + assert list(result[["source", "country"]].itertuples(index=False, name=None)) == [ ("entsoe_api", "GBR"), ("entsoe_api", "GRC"), ("neso", "GBR"), @@ -59,46 +37,22 @@ def test_neso_is_only_requested_for_gbr() -> None: requirements = pd.DataFrame( { "country": ["GRC"], - "start": [ - pd.Timestamp( - "2020-01-01", - tz="UTC", - ) - ], - "end": [ - pd.Timestamp( - "2020-02-01", - tz="UTC", - ) - ], + "start": [pd.Timestamp("2020-01-01", tz="UTC")], + "end": [pd.Timestamp("2020-02-01", tz="UTC")], } ) - result = build_auxiliary_source_requests( - requirements, - source_names=["neso"], - ) + result = build_auxiliary_source_requests(requirements, source_names=["neso"]) assert result.empty assert list(result.columns) == SOURCE_REQUEST_COLUMNS def test_empty_requirements_return_empty_source_request_schema() -> None: - requirements = pd.DataFrame( - columns=[ - "country", - "start", - "end", - ] - ) + requirements = pd.DataFrame(columns=["country", "start", "end"]) result = build_auxiliary_source_requests( - requirements, - source_names=[ - "entsoe_api", - "neso", - "opsd_api", - ], + requirements, source_names=["entsoe_api", "neso", "opsd_api"] ) assert result.empty @@ -106,217 +60,97 @@ def test_empty_requirements_return_empty_source_request_schema() -> None: def test_unknown_source_is_rejected() -> None: - requirements = pd.DataFrame( - columns=[ - "country", - "start", - "end", - ] - ) + requirements = pd.DataFrame(columns=["country", "start", "end"]) - with pytest.raises( - ValueError, - match="Unsupported auxiliary load sources", - ): - build_auxiliary_source_requests( - requirements, - source_names=["mystery_source"], - ) + with pytest.raises(ValueError, match="Unsupported auxiliary load sources"): + build_auxiliary_source_requests(requirements, source_names=["mystery_source"]) def test_duplicate_source_names_are_rejected() -> None: - requirements = pd.DataFrame( - columns=[ - "country", - "start", - "end", - ] - ) + requirements = pd.DataFrame(columns=["country", "start", "end"]) - with pytest.raises( - ValueError, - match="must be unique", - ): + with pytest.raises(ValueError, match="must be unique"): build_auxiliary_source_requests( - requirements, - source_names=[ - "entsoe_api", - "entsoe_api", - ], + requirements, source_names=["entsoe_api", "entsoe_api"] ) def test_build_batch_id_is_independent_of_country_order() -> None: first = _build_batch_id( source="entsoe_api", - start=pd.Timestamp( - "2020-01-01", - tz="UTC", - ), - end=pd.Timestamp( - "2020-02-01", - tz="UTC", - ), - countries=[ - "ALB", - "GRC", - ], + start=pd.Timestamp("2020-01-01", tz="UTC"), + end=pd.Timestamp("2020-02-01", tz="UTC"), + countries=["ALB", "GRC"], ) second = _build_batch_id( source="entsoe_api", - start=pd.Timestamp( - "2020-01-01", - tz="UTC", - ), - end=pd.Timestamp( - "2020-02-01", - tz="UTC", - ), - countries=[ - "GRC", - "ALB", - ], + start=pd.Timestamp("2020-01-01", tz="UTC"), + end=pd.Timestamp("2020-02-01", tz="UTC"), + countries=["GRC", "ALB"], ) assert first == second - assert first.startswith( - "entsoe_api__" - "20200101T0000__" - "20200201T0000__" - ) + assert first.startswith("entsoe_api__20200101T0000__20200201T0000__") def test_build_batch_id_changes_for_different_country_sets() -> None: common = { "source": "entsoe_api", - "start": pd.Timestamp( - "2020-01-01", - tz="UTC", - ), - "end": pd.Timestamp( - "2020-02-01", - tz="UTC", - ), + "start": pd.Timestamp("2020-01-01", tz="UTC"), + "end": pd.Timestamp("2020-02-01", tz="UTC"), } - first = _build_batch_id( - **common, - countries=["ALB"], - ) + first = _build_batch_id(**common, countries=["ALB"]) - second = _build_batch_id( - **common, - countries=[ - "ALB", - "GRC", - ], - ) + second = _build_batch_id(**common, countries=["ALB", "GRC"]) assert first != second def test_build_group_id_depends_only_on_period() -> None: - start = pd.Timestamp( - "2020-01-01", - tz="UTC", - ) - end = pd.Timestamp( - "2020-02-01", - tz="UTC", - ) + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") - assert _build_group_id( - start=start, - end=end, - ) == ( - "20200101T0000__" - "20200201T0000" - ) + assert _build_group_id(start=start, end=end) == ("20200101T0000__20200201T0000") def test_build_source_batches_groups_matching_periods() -> None: requests = pd.DataFrame( { - "source": [ - "entsoe_api", - "entsoe_api", - "entsoe_api", - ], - "country": [ - "ALB", - "GRC", - "MNE", - ], + "source": ["entsoe_api", "entsoe_api", "entsoe_api"], + "country": ["ALB", "GRC", "MNE"], "start": pd.to_datetime( - [ - "2020-01-01", - "2020-01-01", - "2021-01-01", - ], - utc=True, - ), - "end": pd.to_datetime( - [ - "2020-02-01", - "2020-02-01", - "2021-02-01", - ], - utc=True, + ["2020-01-01", "2020-01-01", "2021-01-01"], utc=True ), + "end": pd.to_datetime(["2020-02-01", "2020-02-01", "2021-02-01"], utc=True), } ) - result = build_auxiliary_source_batches( - requests - ) + result = build_auxiliary_source_batches(requests) - first_start = pd.Timestamp( - "2020-01-01", - tz="UTC", - ) - first_end = pd.Timestamp( - "2020-02-01", - tz="UTC", - ) - second_start = pd.Timestamp( - "2021-01-01", - tz="UTC", - ) - second_end = pd.Timestamp( - "2021-02-01", - tz="UTC", - ) + first_start = pd.Timestamp("2020-01-01", tz="UTC") + first_end = pd.Timestamp("2020-02-01", tz="UTC") + second_start = pd.Timestamp("2021-01-01", tz="UTC") + second_end = pd.Timestamp("2021-02-01", tz="UTC") assert result == [ { - "group_id": _build_group_id( - start=first_start, - end=first_end, - ), + "group_id": _build_group_id(start=first_start, end=first_end), "batch_id": _build_batch_id( source="entsoe_api", start=first_start, end=first_end, - countries=[ - "ALB", - "GRC", - ], + countries=["ALB", "GRC"], ), "source": "entsoe_api", "start": first_start, "end": first_end, - "countries": [ - "ALB", - "GRC", - ], + "countries": ["ALB", "GRC"], }, { - "group_id": _build_group_id( - start=second_start, - end=second_end, - ), + "group_id": _build_group_id(start=second_start, end=second_end), "batch_id": _build_batch_id( source="entsoe_api", start=second_start, @@ -332,53 +166,27 @@ def test_build_source_batches_groups_matching_periods() -> None: def test_build_source_batches_keeps_sources_separate() -> None: - start = pd.Timestamp( - "2020-01-01", - tz="UTC", - ) - end = pd.Timestamp( - "2020-02-01", - tz="UTC", - ) + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") requests = pd.DataFrame( { - "source": [ - "entsoe_api", - "opsd_api", - ], - "country": [ - "GBR", - "GBR", - ], - "start": [ - start, - start, - ], - "end": [ - end, - end, - ], + "source": ["entsoe_api", "opsd_api"], + "country": ["GBR", "GBR"], + "start": [start, start], + "end": [end, end], } ) - result = build_auxiliary_source_batches( - requests - ) + result = build_auxiliary_source_batches(requests) - group_id = _build_group_id( - start=start, - end=end, - ) + group_id = _build_group_id(start=start, end=end) assert result == [ { "group_id": group_id, "batch_id": _build_batch_id( - source="entsoe_api", - start=start, - end=end, - countries=["GBR"], + source="entsoe_api", start=start, end=end, countries=["GBR"] ), "source": "entsoe_api", "start": start, @@ -388,10 +196,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: { "group_id": group_id, "batch_id": _build_batch_id( - source="opsd_api", - start=start, - end=end, - countries=["GBR"], + source="opsd_api", start=start, end=end, countries=["GBR"] ), "source": "opsd_api", "start": start, @@ -402,10 +207,6 @@ def test_build_source_batches_keeps_sources_separate() -> None: def test_build_source_batches_returns_empty_list() -> None: - requests = pd.DataFrame( - columns=SOURCE_REQUEST_COLUMNS - ) + requests = pd.DataFrame(columns=SOURCE_REQUEST_COLUMNS) - assert build_auxiliary_source_batches( - requests - ) == [] + assert build_auxiliary_source_batches(requests) == [] diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index 51ef161..648116c 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -10,68 +10,32 @@ def test_clean_demand_records_methods_and_ranks() -> None: """Track observed sources, filling rules, and unresolved gaps.""" - index = pd.date_range( - start="2017-01-01", - periods=400, - freq="h", - tz="UTC", - ) + index = pd.date_range(start="2017-01-01", periods=400, freq="h", tz="UTC") - primary = pd.DataFrame( - { - "AAA": np.arange( - len(index), - dtype=float, - ), - }, - index=index, - ) + primary = pd.DataFrame({"AAA": np.arange(len(index), dtype=float)}, index=index) - fallback = pd.DataFrame( - np.nan, - index=index, - columns=["AAA"], - dtype=float, - ) + fallback = pd.DataFrame(np.nan, index=index, columns=["AAA"], dtype=float) # Four missing values at the start cannot be interpolated because # the run exceeds max_gap and cannot be copied from a previous week. unresolved_timestamps = index[0:4] - primary.loc[ - unresolved_timestamps, - "AAA", - ] = np.nan + primary.loc[unresolved_timestamps, "AAA"] = np.nan # One primary-source gap is supplied directly by the fallback source. fallback_timestamp = index[200] - primary.loc[ - fallback_timestamp, - "AAA", - ] = np.nan - fallback.loc[ - fallback_timestamp, - "AAA", - ] = 10_000.0 + primary.loc[fallback_timestamp, "AAA"] = np.nan + fallback.loc[fallback_timestamp, "AAA"] = 10_000.0 # A two-hour gap is filled by the first rule. interpolation_timestamps = index[220:222] - primary.loc[ - interpolation_timestamps, - "AAA", - ] = np.nan + primary.loc[interpolation_timestamps, "AAA"] = np.nan # A four-hour gap exceeds the interpolation limit but can be copied # from the corresponding values seven days earlier. copy_timestamps = index[250:254] - primary.loc[ - copy_timestamps, - "AAA", - ] = np.nan - - sources = { - "primary": primary, - "fallback": fallback, - } + primary.loc[copy_timestamps, "AAA"] = np.nan + + sources = {"primary": primary, "fallback": fallback} gap_filling_config = { "mode": "basic", @@ -89,14 +53,10 @@ def test_clean_demand_records_methods_and_ranks() -> None: "source_offset": "-168h", "require_complete_source": True, }, - ], + ] }, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - }, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": {}, }, } @@ -107,184 +67,85 @@ def test_clean_demand_records_methods_and_ranks() -> None: cleaning_method, cleaning_method_rank, gap_report, - auxiliary_fill_plan + auxiliary_fill_plan, ) = clean_demand( sources, - source_priority=[ - "primary", - "fallback", - ], + source_priority=["primary", "fallback"], gap_filling_config=gap_filling_config, ) assert gap_report.empty # All outputs use the same grid. - for frame in [ - data_source, - cleaning_method, - cleaning_method_rank, - ]: + for frame in [data_source, cleaning_method, cleaning_method_rank]: assert frame.index.equals(cleaned.index) assert frame.columns.equals(cleaned.columns) # Primary observations have rank 0. - primary_mask = ( - primary.notna() - & fallback.isna() - ) + primary_mask = primary.notna() & fallback.isna() - assert ( - data_source.to_numpy()[ - primary_mask.to_numpy() - ] - == "primary" - ).all() + assert (data_source.to_numpy()[primary_mask.to_numpy()] == "primary").all() assert ( - cleaning_method.to_numpy()[ - primary_mask.to_numpy() - ] - == "observed_primary" + cleaning_method.to_numpy()[primary_mask.to_numpy()] == "observed_primary" ).all() - assert ( - cleaning_method_rank.to_numpy()[ - primary_mask.to_numpy() - ] - == 0 - ).all() + assert (cleaning_method_rank.to_numpy()[primary_mask.to_numpy()] == 0).all() # The fallback observation has rank 1. - assert ( - cleaned.loc[ - fallback_timestamp, - "AAA", - ] - == 10_000.0 - ) + assert cleaned.loc[fallback_timestamp, "AAA"] == 10_000.0 - assert ( - data_source.loc[ - fallback_timestamp, - "AAA", - ] - == "fallback" - ) + assert data_source.loc[fallback_timestamp, "AAA"] == "fallback" - assert ( - cleaning_method.loc[ - fallback_timestamp, - "AAA", - ] - == "observed_fallback" - ) + assert cleaning_method.loc[fallback_timestamp, "AAA"] == "observed_fallback" - assert ( - cleaning_method_rank.loc[ - fallback_timestamp, - "AAA", - ] - == 1 - ) + assert cleaning_method_rank.loc[fallback_timestamp, "AAA"] == 1 # The two-hour gap is filled by interpolation at rank 2. - assert cleaned.loc[ - interpolation_timestamps, - "AAA", - ].notna().all() - - assert cleaning_method.loc[ - interpolation_timestamps, - "AAA", - ].eq( - "interpolate_short_gaps" - ).all() + assert cleaned.loc[interpolation_timestamps, "AAA"].notna().all() - assert cleaning_method_rank.loc[ - interpolation_timestamps, - "AAA", - ].eq(2).all() + assert ( + cleaning_method.loc[interpolation_timestamps, "AAA"] + .eq("interpolate_short_gaps") + .all() + ) - left_value = cleaned.loc[ - index[219], - "AAA", - ] - right_value = cleaned.loc[ - index[222], - "AAA", - ] + assert cleaning_method_rank.loc[interpolation_timestamps, "AAA"].eq(2).all() - expected_interpolation = np.linspace( - left_value, - right_value, - 4, - )[1:3] + left_value = cleaned.loc[index[219], "AAA"] + right_value = cleaned.loc[index[222], "AAA"] + + expected_interpolation = np.linspace(left_value, right_value, 4)[1:3] np.testing.assert_allclose( - cleaned.loc[ - interpolation_timestamps, - "AAA", - ].to_numpy(), - expected_interpolation, + cleaned.loc[interpolation_timestamps, "AAA"].to_numpy(), expected_interpolation ) # The four-hour gap is copied from seven days earlier at rank 3. - source_timestamps = ( - copy_timestamps - - pd.Timedelta(hours=168) - ) + source_timestamps = copy_timestamps - pd.Timedelta(hours=168) np.testing.assert_allclose( - cleaned.loc[ - copy_timestamps, - "AAA", - ].to_numpy(), - cleaned.loc[ - source_timestamps, - "AAA", - ].to_numpy(), + cleaned.loc[copy_timestamps, "AAA"].to_numpy(), + cleaned.loc[source_timestamps, "AAA"].to_numpy(), ) - assert cleaning_method.loc[ - copy_timestamps, - "AAA", - ].eq( - "copy_previous_week" - ).all() + assert cleaning_method.loc[copy_timestamps, "AAA"].eq("copy_previous_week").all() - assert cleaning_method_rank.loc[ - copy_timestamps, - "AAA", - ].eq(3).all() + assert cleaning_method_rank.loc[copy_timestamps, "AAA"].eq(3).all() # The initial gap remains unresolved at the final rank. - assert cleaned.loc[ - unresolved_timestamps, - "AAA", - ].isna().all() + assert cleaned.loc[unresolved_timestamps, "AAA"].isna().all() - assert cleaning_method.loc[ - unresolved_timestamps, - "AAA", - ].eq("missing").all() + assert cleaning_method.loc[unresolved_timestamps, "AAA"].eq("missing").all() - assert cleaning_method_rank.loc[ - unresolved_timestamps, - "AAA", - ].eq(4).all() + assert cleaning_method_rank.loc[unresolved_timestamps, "AAA"].eq(4).all() # Derived and unresolved values have no observed data source. - derived_or_missing = ( - interpolation_timestamps - .append(copy_timestamps) - .append(unresolved_timestamps) + derived_or_missing = interpolation_timestamps.append(copy_timestamps).append( + unresolved_timestamps ) - assert data_source.loc[ - derived_or_missing, - "AAA", - ].isna().all() + assert data_source.loc[derived_or_missing, "AAA"].isna().all() expected_method_counts = { "observed_primary": 389, @@ -294,66 +155,28 @@ def test_clean_demand_records_methods_and_ranks() -> None: "missing": 4, } - assert ( - cleaning_method["AAA"] - .value_counts() - .to_dict() - == expected_method_counts - ) + assert cleaning_method["AAA"].value_counts().to_dict() == expected_method_counts - expected_rank_counts = { - 0: 389, - 1: 1, - 2: 2, - 3: 4, - 4: 4, - } + expected_rank_counts = {0: 389, 1: 1, 2: 2, 3: 4, 4: 4} assert ( - cleaning_method_rank["AAA"] - .value_counts() - .sort_index() - .to_dict() + cleaning_method_rank["AAA"].value_counts().sort_index().to_dict() == expected_rank_counts ) def test_advanced_mode_reports_unresolved_gaps() -> None: """Report contiguous gaps remaining after basic gap filling.""" - index = pd.date_range( - start="2017-01-01", - periods=400, - freq="h", - tz="UTC", - ) + index = pd.date_range(start="2017-01-01", periods=400, freq="h", tz="UTC") - primary = pd.DataFrame( - { - "AAA": np.arange( - len(index), - dtype=float, - ), - }, - index=index, - ) + primary = pd.DataFrame({"AAA": np.arange(len(index), dtype=float)}, index=index) - fallback = pd.DataFrame( - np.nan, - index=index, - columns=["AAA"], - dtype=float, - ) + fallback = pd.DataFrame(np.nan, index=index, columns=["AAA"], dtype=float) unresolved_timestamps = index[0:4] - primary.loc[ - unresolved_timestamps, - "AAA", - ] = np.nan - - sources = { - "primary": primary, - "fallback": fallback, - } + primary.loc[unresolved_timestamps, "AAA"] = np.nan + + sources = {"primary": primary, "fallback": fallback} gap_filling_config = { "mode": "advanced", @@ -371,14 +194,10 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: "source_offset": "-168h", "require_complete_source": True, }, - ], + ] }, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - }, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": {}, }, } @@ -389,13 +208,10 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: cleaning_method, _cleaning_method_rank, gap_report, - auxiliary_fill_plan + auxiliary_fill_plan, ) = clean_demand( sources, - source_priority=[ - "primary", - "fallback", - ], + source_priority=["primary", "fallback"], gap_filling_config=gap_filling_config, ) @@ -412,15 +228,9 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: "scaling_method", ] - assert cleaned.loc[ - unresolved_timestamps, - "AAA", - ].isna().all() + assert cleaned.loc[unresolved_timestamps, "AAA"].isna().all() - assert cleaning_method.loc[ - unresolved_timestamps, - "AAA", - ].eq("missing").all() + assert cleaning_method.loc[unresolved_timestamps, "AAA"].eq("missing").all() assert len(gap_report) == 1 @@ -436,107 +246,45 @@ def test_advanced_mode_reports_unresolved_gaps() -> None: def test_build_gap_report_identifies_contiguous_gaps_and_boundaries() -> None: """Test the gap report captures gaps and boundaries as expected.""" - index = pd.date_range( - "2021-01-01 00:00", - periods=8, - freq="h", - tz="UTC", - ) + index = pd.date_range("2021-01-01 00:00", periods=8, freq="h", tz="UTC") load = pd.DataFrame( { "ALB": pd.array( - [ - pd.NA, - 10.0, - 11.0, - pd.NA, - pd.NA, - 14.0, - 15.0, - 16.0, - ], - dtype="Float64", - ), - "TUR": pd.array( - [pd.NA] * 8, - dtype="Float64", + [pd.NA, 10.0, 11.0, pd.NA, pd.NA, 14.0, 15.0, 16.0], dtype="Float64" ), + "TUR": pd.array([pd.NA] * 8, dtype="Float64"), "MNE": pd.array( - [ - 20.0, - 21.0, - 22.0, - 23.0, - 24.0, - 25.0, - pd.NA, - pd.NA, - ], - dtype="Float64", + [20.0, 21.0, 22.0, 23.0, 24.0, 25.0, pd.NA, pd.NA], dtype="Float64" ), }, index=index, ) - result = build_gap_report( - load, - enabled=True, - ) + result = build_gap_report(load, enabled=True) expected = pd.DataFrame( { - "country": [ - "ALB", - "ALB", - "MNE", - "TUR", - ], - "gap_start": [ - index[0], - index[3], - index[6], - index[0], - ], + "country": ["ALB", "ALB", "MNE", "TUR"], + "gap_start": [index[0], index[3], index[6], index[0]], "gap_end": [ index[1], index[5], index[7] + pd.Timedelta(hours=1), index[7] + pd.Timedelta(hours=1), ], - "gap_hours": [ - 1, - 2, - 2, - 8, - ], - "touches_start_boundary": [ - True, - False, - False, - True, - ], - "touches_end_boundary": [ - False, - False, - True, - True, - ], + "gap_hours": [1, 2, 2, 8], + "touches_start_boundary": [True, False, False, True], + "touches_end_boundary": [False, False, True, True], } ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) def test_build_gap_report_returns_empty_report_when_disabled() -> None: """Tests that gap report is empty when disabled.""" - result = build_gap_report( - pd.DataFrame(), - enabled=False, - ) + result = build_gap_report(pd.DataFrame(), enabled=False) expected = pd.DataFrame( columns=[ @@ -549,33 +297,18 @@ def test_build_gap_report_returns_empty_report_when_disabled() -> None: ] ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) def test_build_gap_report_returns_empty_report_when_no_gaps_remain() -> None: """Tests that gap report is empty when there are no gaps.""" - index = pd.date_range( - "2021-01-01 00:00", - periods=3, - freq="h", - tz="UTC", - ) + index = pd.date_range("2021-01-01 00:00", periods=3, freq="h", tz="UTC") load = pd.DataFrame( - { - "ALB": [10.0, 11.0, 12.0], - "TUR": [20.0, 21.0, 22.0], - }, - index=index, + {"ALB": [10.0, 11.0, 12.0], "TUR": [20.0, 21.0, 22.0]}, index=index ) - result = build_gap_report( - load, - enabled=True, - ) + result = build_gap_report(load, enabled=True) expected = pd.DataFrame( columns=[ @@ -588,35 +321,19 @@ def test_build_gap_report_returns_empty_report_when_no_gaps_remain() -> None: ] ) - pd.testing.assert_frame_equal( - result, - expected, - ) + pd.testing.assert_frame_equal(result, expected) + def test_advanced_mode_builds_auxiliary_fill_plan() -> None: """Tests construction of the auxiliary fill plan.""" - index = pd.date_range( - "2021-01-01", - periods=3, - freq="h", - tz="UTC", - ) - source = pd.DataFrame( - {"ALB": [1.0, None, 3.0]}, - index=index, - ) + index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") + source = pd.DataFrame({"ALB": [1.0, None, 3.0]}, index=index) config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - }, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": { "fill_albania_from_greece": { "country": "ALB", @@ -662,35 +379,18 @@ def test_advanced_mode_builds_auxiliary_fill_plan() -> None: def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: index = pd.date_range( - start="2022-01-01", - end="2022-01-03 23:00", - freq="h", - tz="UTC", + start="2022-01-01", end="2022-01-03 23:00", freq="h", tz="UTC" ) - source = pd.DataFrame( - { - "ALB": range(len(index)), - }, - index=index, - dtype=float, - ) + source = pd.DataFrame({"ALB": range(len(index))}, index=index, dtype=float) - sources = { - "primary": source, - } + sources = {"primary": source} gap_filling_config = { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": { "active_albania": { "country": "ALB", @@ -749,11 +449,7 @@ def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: _gap_report, auxiliary_fill_plan, ) = clean_demand( - sources, - source_priority=["primary"], - gap_filling_config=gap_filling_config, + sources, source_priority=["primary"], gap_filling_config=gap_filling_config ) - assert auxiliary_fill_plan["rule_name"].tolist() == [ - "active_albania" - ] + assert auxiliary_fill_plan["rule_name"].tolist() == ["active_albania"] diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 9574411..9fa9294 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -8,12 +8,7 @@ REPOSITORY_ROOT = Path(__file__).resolve().parents[2] CONFIG_PATH = REPOSITORY_ROOT / "config" / "config.yaml" -SCHEMA_PATH = ( - REPOSITORY_ROOT - / "workflow" - / "internal" - / "config.schema.yaml" -) +SCHEMA_PATH = REPOSITORY_ROOT / "workflow" / "internal" / "config.schema.yaml" def test_default_config_matches_schema() -> None: @@ -26,65 +21,42 @@ def test_default_config_matches_schema() -> None: validator = Draft202012Validator(schema) errors = sorted( - validator.iter_errors(config), - key=lambda error: list(error.absolute_path), + validator.iter_errors(config), key=lambda error: list(error.absolute_path) ) - assert not errors, "\n".join( - _format_validation_error(error) - for error in errors - ) + assert not errors, "\n".join(_format_validation_error(error) for error in errors) -def _format_validation_error( - error: ValidationError, -) -> str: +def _format_validation_error(error: ValidationError) -> str: """Format one schema-validation error with its config location.""" - path = ".".join( - str(part) - for part in error.absolute_path - ) + path = ".".join(str(part) for part in error.absolute_path) return f"{path or ''}: {error.message}" + def test_advanced_auxiliary_basic_cleaning_can_be_disabled() -> None: """Allow auxiliary basic cleaning to be disabled.""" with SCHEMA_PATH.open(encoding="utf-8") as schema_file: schema = yaml.safe_load(schema_file) config = { - "temporal_scope": { - "start": "2021-01-01", - "end": "2022-01-01", - }, - "load_sources": [ - "entsoe_api", - ], + "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, + "load_sources": ["entsoe_api"], "gap_filling": { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": False, - }, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": False}}, "overrides": {}, }, }, } validator = Draft202012Validator(schema) - errors = list( - validator.iter_errors(config) - ) + errors = list(validator.iter_errors(config)) + + assert not errors, "\n".join(_format_validation_error(error) for error in errors) - assert not errors, "\n".join( - _format_validation_error(error) - for error in errors - ) def test_advanced_mode_allows_no_rules_or_overrides() -> None: """Allow advanced mode to run for diagnosis only.""" @@ -92,35 +64,19 @@ def test_advanced_mode_allows_no_rules_or_overrides() -> None: schema = yaml.safe_load(schema_file) config = { - "temporal_scope": { - "start": "2021-01-01", - "end": "2022-01-01", - }, - "load_sources": [ - "entsoe_api", - ], + "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, + "load_sources": ["entsoe_api"], "gap_filling": { "mode": "advanced", - "basic": { - "rules": [], - }, + "basic": {"rules": []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": True, - }, - }, + "auxiliary_data": {"basic_cleaning": {"enabled": True}}, "overrides": {}, }, }, } validator = Draft202012Validator(schema) - errors = list( - validator.iter_errors(config) - ) + errors = list(validator.iter_errors(config)) - assert not errors, "\n".join( - _format_validation_error(error) - for error in errors - ) + assert not errors, "\n".join(_format_validation_error(error) for error in errors) diff --git a/tests/unit/test_external_profile.py b/tests/unit/test_external_profile.py index fa74f0f..132366c 100644 --- a/tests/unit/test_external_profile.py +++ b/tests/unit/test_external_profile.py @@ -9,9 +9,7 @@ def test_read_external_profile_reads_valid_csv(tmp_path): path = tmp_path / "profile.csv" path.write_text( - "timestamp,demand\n" - "2025-01-01T00:00:00Z,100.0\n" - "2025-01-01T01:00:00Z,110.0\n", + "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-01T01:00:00Z,110.0\n", encoding="utf-8", ) @@ -20,27 +18,18 @@ def test_read_external_profile_reads_valid_csv(tmp_path): expected = pd.Series( [100.0, 110.0], index=pd.DatetimeIndex( - [ - "2025-01-01T00:00:00Z", - "2025-01-01T01:00:00Z", - ], - name="timestamp", + ["2025-01-01T00:00:00Z", "2025-01-01T01:00:00Z"], name="timestamp" ), ) - pd.testing.assert_series_equal( - result, - expected, - ) + pd.testing.assert_series_equal(result, expected) def test_read_external_profile_allows_sparse_timestamps(tmp_path): path = tmp_path / "profile.csv" path.write_text( - "timestamp,demand\n" - "2025-01-01T00:00:00Z,100.0\n" - "2025-01-03T12:00:00Z,200.0\n", + "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-03T12:00:00Z,200.0\n", encoding="utf-8", ) @@ -48,45 +37,29 @@ def test_read_external_profile_allows_sparse_timestamps(tmp_path): assert len(result) == 2 - assert result.loc[ - pd.Timestamp("2025-01-01T00:00:00Z") - ] == 100.0 + assert result.loc[pd.Timestamp("2025-01-01T00:00:00Z")] == 100.0 - assert result.loc[ - pd.Timestamp("2025-01-03T12:00:00Z") - ] == 200.0 + assert result.loc[pd.Timestamp("2025-01-03T12:00:00Z")] == 200.0 def test_read_external_profile_rejects_duplicate_timestamps(tmp_path): path = tmp_path / "profile.csv" path.write_text( - "timestamp,demand\n" - "2025-01-01T00:00:00Z,100.0\n" - "2025-01-01T00:00:00Z,110.0\n", + "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-01T00:00:00Z,110.0\n", encoding="utf-8", ) - with pytest.raises( - ValueError, - match="timestamps must be unique", - ): + with pytest.raises(ValueError, match="timestamps must be unique"): read_external_profile(path) def test_read_external_profile_rejects_non_hourly_timestamps(tmp_path): path = tmp_path / "profile.csv" - path.write_text( - "timestamp,demand\n" - "2025-01-01T00:30:00Z,100.0\n", - encoding="utf-8", - ) + path.write_text("timestamp,demand\n2025-01-01T00:30:00Z,100.0\n", encoding="utf-8") - with pytest.raises( - ValueError, - match="whole hours", - ): + with pytest.raises(ValueError, match="whole hours"): read_external_profile(path) @@ -94,31 +67,19 @@ def test_read_external_profile_rejects_non_numeric_values(tmp_path): path = tmp_path / "profile.csv" path.write_text( - "timestamp,demand\n" - "2025-01-01T00:00:00Z,not-a-number\n", - encoding="utf-8", + "timestamp,demand\n2025-01-01T00:00:00Z,not-a-number\n", encoding="utf-8" ) - with pytest.raises( - ValueError, - match="Unable to parse string", - ): + with pytest.raises(ValueError, match="Unable to parse string"): read_external_profile(path) def test_read_external_profile_rejects_wrong_columns(tmp_path): path = tmp_path / "profile.csv" - path.write_text( - "datetime,demand\n" - "2025-01-01T00:00:00Z,100.0\n", - encoding="utf-8", - ) + path.write_text("datetime,demand\n2025-01-01T00:00:00Z,100.0\n", encoding="utf-8") - with pytest.raises( - ValueError, - match="must contain exactly the columns", - ): + with pytest.raises(ValueError, match="must contain exactly the columns"): read_external_profile(path) @@ -126,31 +87,17 @@ def test_read_external_profile_rejects_extra_columns(tmp_path): path = tmp_path / "profile.csv" path.write_text( - "timestamp,demand,comment\n" - "2025-01-01T00:00:00Z,100.0,test\n", - encoding="utf-8", + "timestamp,demand,comment\n2025-01-01T00:00:00Z,100.0,test\n", encoding="utf-8" ) - with pytest.raises( - ValueError, - match="must contain exactly the columns", - ): + with pytest.raises(ValueError, match="must contain exactly the columns"): read_external_profile(path) def test_read_external_profile_rejects_missing_values(tmp_path): path = tmp_path / "profile.csv" - path.write_text( - "timestamp,demand\n" - "2025-01-01T00:00:00Z,\n", - encoding="utf-8", - ) + path.write_text("timestamp,demand\n2025-01-01T00:00:00Z,\n", encoding="utf-8") - with pytest.raises( - ValueError, - match="must not be missing", - ): + with pytest.raises(ValueError, match="must not be missing"): read_external_profile(path) - - diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index f5642a2..3f59c3c 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -19,11 +19,7 @@ def _config( "mode": mode, "basic": {"rules": basic_rules or []}, "advanced": { - "auxiliary_data": { - "basic_cleaning": { - "enabled": basic_cleaning_enabled, - } - }, + "auxiliary_data": {"basic_cleaning": {"enabled": basic_cleaning_enabled}}, "overrides": overrides, }, } @@ -53,9 +49,7 @@ def _construct_override( def _external_profile_override( - *, - country: str = "ALB", - path: str = "resources/user/external_profiles/alb.csv", + *, country: str = "ALB", path: str = "resources/user/external_profiles/alb.csv" ) -> dict: return { "country": country, @@ -72,10 +66,7 @@ def _fill_plan(rule_names: list[str]) -> pd.DataFrame: return pd.DataFrame() return pd.DataFrame( - { - "rule_name": rule_names, - "status": ["ready"] * len(rule_names), - } + {"rule_name": rule_names, "status": ["ready"] * len(rule_names)} ) @@ -105,9 +96,7 @@ def test_plan_is_empty_outside_advanced_mode() -> None: def test_advanced_plan_with_empty_fill_plan_is_empty() -> None: result = build_advanced_execution_plan( fill_plan=pd.DataFrame(), - gap_filling_config=_config( - {"fill_albania": _construct_override()} - ), + gap_filling_config=_config({"fill_albania": _construct_override()}), source_names=["entsoe_api"], ) @@ -119,9 +108,7 @@ def test_plan_builds_complete_execution_manifest() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - {"fill_albania": override} - ), + gap_filling_config=_config({"fill_albania": override}), source_names=["entsoe_api", "opsd_api"], ) @@ -129,26 +116,17 @@ def test_plan_builds_complete_execution_manifest() -> None: end = pd.Timestamp("2020-02-01", tz="UTC") group_id = _build_group_id(start=start, end=end) entsoe_batch_id = _build_batch_id( - source="entsoe_api", - start=start, - end=end, - countries=["GRC"], + source="entsoe_api", start=start, end=end, countries=["GRC"] ) opsd_batch_id = _build_batch_id( - source="opsd_api", - start=start, - end=end, - countries=["GRC"], + source="opsd_api", start=start, end=end, countries=["GRC"] ) assert result == { "version": 1, "active_rule_names": ["fill_albania"], "rules": { - "fill_albania": { - "override": override, - "required_group_ids": [group_id], - } + "fill_albania": {"override": override, "required_group_ids": [group_id]} }, "batches": [ { @@ -174,9 +152,7 @@ def test_plan_builds_complete_execution_manifest() -> None: "entsoe_api": [entsoe_batch_id], "opsd_api": [opsd_batch_id], }, - "groups": { - group_id: [entsoe_batch_id, opsd_batch_id], - }, + "groups": {group_id: [entsoe_batch_id, opsd_batch_id]}, "constructed_profile_rule_names": ["fill_albania"], "external_profile_files": {}, } @@ -187,12 +163,8 @@ def test_plan_uses_only_overrides_in_fill_plan() -> None: fill_plan=_fill_plan(["active"]), gap_filling_config=_config( { - "active": _construct_override( - source_country="GRC" - ), - "inactive": _construct_override( - source_country="SRB" - ), + "active": _construct_override(source_country="GRC"), + "inactive": _construct_override(source_country="SRB"), } ), source_names=["entsoe_api"], @@ -201,14 +173,8 @@ def test_plan_uses_only_overrides_in_fill_plan() -> None: assert result["active_rule_names"] == ["active"] assert list(result["rules"]) == ["active"] assert result["constructed_profile_rule_names"] == ["active"] - assert all( - "GRC" in batch["countries"] - for batch in result["batches"] - ) - assert all( - "SRB" not in batch["countries"] - for batch in result["batches"] - ) + assert all("GRC" in batch["countries"] for batch in result["batches"]) + assert all("SRB" not in batch["countries"] for batch in result["batches"]) def test_plan_preserves_configured_override_order() -> None: @@ -216,12 +182,8 @@ def test_plan_preserves_configured_override_order() -> None: fill_plan=_fill_plan(["second", "first"]), gap_filling_config=_config( { - "first": _construct_override( - source_country="GRC" - ), - "second": _construct_override( - source_country="SRB" - ), + "first": _construct_override(source_country="GRC"), + "second": _construct_override(source_country="SRB"), } ), source_names=["entsoe_api"], @@ -229,10 +191,7 @@ def test_plan_preserves_configured_override_order() -> None: assert result["active_rule_names"] == ["first", "second"] assert list(result["rules"]) == ["first", "second"] - assert result["constructed_profile_rule_names"] == [ - "first", - "second", - ] + assert result["constructed_profile_rule_names"] == ["first", "second"] def test_plan_resolves_scaling_target_sources_to_groups() -> None: @@ -240,20 +199,13 @@ def test_plan_resolves_scaling_target_sources_to_groups() -> None: override["scaling"] = { "method": "match_energy", "target_sources": [ - { - "country": "ALB", - "start": "2020-03-01", - "end": "2020-04-01", - "weight": 1, - } + {"country": "ALB", "start": "2020-03-01", "end": "2020-04-01", "weight": 1} ], } result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - {"fill_albania": override} - ), + gap_filling_config=_config({"fill_albania": override}), source_names=["entsoe_api"], ) @@ -270,10 +222,7 @@ def test_plan_resolves_scaling_target_sources_to_groups() -> None: ] ) - assert ( - result["rules"]["fill_albania"]["required_group_ids"] - == expected_group_ids - ) + assert result["rules"]["fill_albania"]["required_group_ids"] == expected_group_ids def test_plan_resolves_rule_to_expanded_basic_cleaning_group() -> None: @@ -302,9 +251,7 @@ def test_plan_resolves_rule_to_expanded_basic_cleaning_group() -> None: assert batch["start"] < "2020-01-01T00:00:00+00:00" assert batch["end"] > "2020-02-01T00:00:00+00:00" - assert result["rules"]["fill_albania"]["required_group_ids"] == [ - batch["group_id"] - ] + assert result["rules"]["fill_albania"]["required_group_ids"] == [batch["group_id"]] def test_non_construct_rule_requires_no_auxiliary_groups() -> None: @@ -318,17 +265,12 @@ def test_non_construct_rule_requires_no_auxiliary_groups() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["leave_albania"]), - gap_filling_config=_config( - {"leave_albania": override} - ), + gap_filling_config=_config({"leave_albania": override}), source_names=["entsoe_api"], ) assert result["rules"] == { - "leave_albania": { - "override": override, - "required_group_ids": [], - } + "leave_albania": {"override": override, "required_group_ids": []} } assert result["batches"] == [] assert result["groups"] == {} @@ -354,10 +296,7 @@ def test_batch_years_use_half_open_period_semantics() -> None: def test_plan_rejects_unknown_fill_plan_rule() -> None: - with pytest.raises( - ValueError, - match="references unknown advanced overrides", - ): + with pytest.raises(ValueError, match="references unknown advanced overrides"): build_advanced_execution_plan( fill_plan=_fill_plan(["missing_rule"]), gap_filling_config=_config({}), @@ -366,31 +305,21 @@ def test_plan_rejects_unknown_fill_plan_rule() -> None: def test_plan_rejects_duplicate_fill_plan_rules() -> None: - with pytest.raises( - ValueError, - match="contains duplicate rule names", - ): + with pytest.raises(ValueError, match="contains duplicate rule names"): build_advanced_execution_plan( - fill_plan=_fill_plan( - ["fill_albania", "fill_albania"] - ), - gap_filling_config=_config( - {"fill_albania": _construct_override()} - ), + fill_plan=_fill_plan(["fill_albania", "fill_albania"]), + gap_filling_config=_config({"fill_albania": _construct_override()}), source_names=["entsoe_api"], ) def test_plan_rejects_unresolvable_auxiliary_group() -> None: with pytest.raises( - ValueError, - match="Expected exactly one auxiliary group covering", + ValueError, match="Expected exactly one auxiliary group covering" ): build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - {"fill_albania": _construct_override()} - ), + gap_filling_config=_config({"fill_albania": _construct_override()}), source_names=[], ) @@ -398,33 +327,26 @@ def test_plan_rejects_unresolvable_auxiliary_group() -> None: def test_plan_is_json_serializable() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - {"fill_albania": _construct_override()} - ), + gap_filling_config=_config({"fill_albania": _construct_override()}), source_names=["entsoe_api"], ) json.dumps(result) + def test_plan_records_active_external_profile_file() -> None: override = _external_profile_override( - path="resources/user/external_profiles/alb_2020.csv", + path="resources/user/external_profiles/alb_2020.csv" ) result = build_advanced_execution_plan( fill_plan=_fill_plan(["external_albania"]), - gap_filling_config=_config( - { - "external_albania": override, - } - ), + gap_filling_config=_config({"external_albania": override}), source_names=["entsoe_api"], ) assert result["external_profile_files"] == { - "external_albania": ( - "resources/user/external_profiles/alb_2020.csv" - ), + "external_albania": ("resources/user/external_profiles/alb_2020.csv") } assert result["constructed_profile_rule_names"] == [] @@ -436,44 +358,30 @@ def test_plan_excludes_inactive_external_profile_file() -> None: fill_plan=_fill_plan(["active"]), gap_filling_config=_config( { - "active": _external_profile_override( - path="resources/user/active.csv", - ), + "active": _external_profile_override(path="resources/user/active.csv"), "inactive": _external_profile_override( - path="resources/user/inactive.csv", + path="resources/user/inactive.csv" ), } ), source_names=["entsoe_api"], ) - assert result["external_profile_files"] == { - "active": "resources/user/active.csv", - } + assert result["external_profile_files"] == {"active": "resources/user/active.csv"} def test_plan_allows_external_profile_file_reuse() -> None: - shared_path = ( - "resources/user/external_profiles/" - "gbr_2000_2025.csv" - ) + shared_path = "resources/user/external_profiles/gbr_2000_2025.csv" result = build_advanced_execution_plan( - fill_plan=_fill_plan( - [ - "gbr_period_one", - "gbr_period_two", - ] - ), + fill_plan=_fill_plan(["gbr_period_one", "gbr_period_two"]), gap_filling_config=_config( { "gbr_period_one": _external_profile_override( - country="GBR", - path=shared_path, + country="GBR", path=shared_path ), "gbr_period_two": _external_profile_override( - country="GBR", - path=shared_path, + country="GBR", path=shared_path ), } ), @@ -484,5 +392,3 @@ def test_plan_allows_external_profile_file_reuse() -> None: "gbr_period_one": shared_path, "gbr_period_two": shared_path, } - - diff --git a/tests/unit/test_provenance.py b/tests/unit/test_provenance.py index 21475df..4a8acee 100644 --- a/tests/unit/test_provenance.py +++ b/tests/unit/test_provenance.py @@ -6,52 +6,26 @@ def test_build_final_cleaning_rules_includes_advanced_overrides(): config = { "mode": "advanced", - "basic": { - "rules": [ - { - "name": "basic_rule", - "method": "linear_interpolation", - } - ] - }, + "basic": {"rules": [{"name": "basic_rule", "method": "linear_interpolation"}]}, "advanced": { - "overrides": { - "advanced_rule": { - "method": "construct_from_sources", - } - } + "overrides": {"advanced_rule": {"method": "construct_from_sources"}} }, } rules = build_final_cleaning_rules(config) - assert [rule["name"] for rule in rules] == [ - "basic_rule", - "advanced_rule", - ] + assert [rule["name"] for rule in rules] == ["basic_rule", "advanced_rule"] + def test_build_final_cleaning_rules_excludes_advanced_overrides_in_basic_mode(): config = { "mode": "basic", - "basic": { - "rules": [ - { - "name": "basic_rule", - "method": "linear_interpolation", - } - ] - }, + "basic": {"rules": [{"name": "basic_rule", "method": "linear_interpolation"}]}, "advanced": { - "overrides": { - "advanced_rule": { - "method": "construct_from_sources", - } - } + "overrides": {"advanced_rule": {"method": "construct_from_sources"}} }, } rules = build_final_cleaning_rules(config) - assert [rule["name"] for rule in rules] == [ - "basic_rule", - ] + assert [rule["name"] for rule in rules] == ["basic_rule"] diff --git a/tests/unit/test_workflow_environment_boundary.py b/tests/unit/test_workflow_environment_boundary.py index 4e9e811..f6a589a 100644 --- a/tests/unit/test_workflow_environment_boundary.py +++ b/tests/unit/test_workflow_environment_boundary.py @@ -1,4 +1,5 @@ """Tests for the Snakemake host/module environment boundary.""" + import re from pathlib import Path @@ -23,10 +24,7 @@ def test_snakemake_host_code_has_no_module_runtime_dependencies(): """Ensure host-side workflow code avoids module runtime packages.""" - workflow_files = [ - Path("workflow/Snakefile"), - *Path("workflow/rules").glob("*.smk"), - ] + workflow_files = [Path("workflow/Snakefile"), *Path("workflow/rules").glob("*.smk")] violations = [] @@ -35,12 +33,9 @@ def test_snakemake_host_code_has_no_module_runtime_dependencies(): for pattern in FORBIDDEN_PATTERNS: if re.search(pattern, text, flags=re.MULTILINE): - violations.append( - f"{path}: {pattern}" - ) + violations.append(f"{path}: {pattern}") assert not violations, ( "Snakemake host code must not depend on module runtime " - "packages:\n" - + "\n".join(violations) + "packages:\n" + "\n".join(violations) ) diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 950f17a..1f26596 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -99,7 +99,7 @@ $defs: - target_sources additionalProperties: false - + auxiliary_fill_rule_base: type: object properties: @@ -197,7 +197,7 @@ $defs: - $ref: "#/$defs/construct_from_sources_rule" - $ref: "#/$defs/external_profile_rule" - $ref: "#/$defs/leave_missing_rule" - + auxiliary_basic_cleaning: type: object properties: @@ -423,4 +423,4 @@ required: - load_sources - gap_filling -additionalProperties: false \ No newline at end of file +additionalProperties: false diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index 85b873d..0fa4113 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -44,4 +44,4 @@ load_entsoe_api: - "SVN" - "SVK" # - "TUR" #Data is unavailable for Turkey - MAX_WORKERS: 3 \ No newline at end of file + MAX_WORKERS: 3 diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 601d6de..b0c266f 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -3,13 +3,7 @@ import json def _read_auxiliary_plan(_wildcards=None): """Read the resolved advanced execution plan.""" - plan_file = ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) + plan_file = checkpoints.plan_auxiliary_data.get().output.plan with plan_file.open() as file: return json.load(file) @@ -17,13 +11,7 @@ def _read_auxiliary_plan(_wildcards=None): def auxiliary_acquisition_plan(_wildcards): """Return the execution plan after the checkpoint completes.""" - return ( - checkpoints - .plan_auxiliary_data - .get() - .output - .plan - ) + return checkpoints.plan_auxiliary_data.get().output.plan def auxiliary_entsoe_outputs(_wildcards): @@ -31,14 +19,8 @@ def auxiliary_entsoe_outputs(_wildcards): plan = _read_auxiliary_plan() return [ - ( - "/automatic/" - "auxiliary/entsoe_api/" - f"{batch_id}.parquet" - ) - for batch_id in plan[ - "batch_ids_by_source" - ].get("entsoe_api", []) + ("/automatic/" "auxiliary/entsoe_api/" f"{batch_id}.parquet") + for batch_id in plan["batch_ids_by_source"].get("entsoe_api", []) ] @@ -47,14 +29,8 @@ def auxiliary_opsd_outputs(_wildcards): plan = _read_auxiliary_plan() return [ - ( - "/automatic/" - "auxiliary/opsd_api/" - f"{batch_id}.parquet" - ) - for batch_id in plan[ - "batch_ids_by_source" - ].get("opsd_api", []) + ("/automatic/" "auxiliary/opsd_api/" f"{batch_id}.parquet") + for batch_id in plan["batch_ids_by_source"].get("opsd_api", []) ] @@ -65,17 +41,11 @@ def auxiliary_neso_raw_files(wildcards): batch = next( batch for batch in plan["batches"] - if ( - batch["batch_id"] == wildcards.batch_id - and batch["source"] == "neso" - ) + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "neso") ) return [ - ( - "/automatic/neso/" - f"historic_demand_{year}.csv" - ) + ("/automatic/neso/" f"historic_demand_{year}.csv") for year in batch["years"] ] @@ -85,14 +55,8 @@ def auxiliary_neso_outputs(_wildcards): plan = _read_auxiliary_plan() return [ - ( - "/automatic/" - "auxiliary/neso/" - f"{batch_id}.parquet" - ) - for batch_id in plan[ - "batch_ids_by_source" - ].get("neso", []) + ("/automatic/" "auxiliary/neso/" f"{batch_id}.parquet") + for batch_id in plan["batch_ids_by_source"].get("neso", []) ] @@ -100,14 +64,9 @@ def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" plan = _read_auxiliary_plan() - batch_ids = plan["groups"][ - wildcards.group_id - ] + batch_ids = plan["groups"][wildcards.group_id] - batches_by_id = { - batch["batch_id"]: batch - for batch in plan["batches"] - } + batches_by_id = {batch["batch_id"]: batch for batch in plan["batches"]} return [ ( @@ -124,11 +83,7 @@ def auxiliary_combined_outputs(_wildcards): plan = _read_auxiliary_plan() return [ - ( - "/automatic/" - "auxiliary/combined/" - f"{group_id}.parquet" - ) + ("/automatic/" "auxiliary/combined/" f"{group_id}.parquet") for group_id in plan["groups"] ] @@ -137,16 +92,10 @@ def auxiliary_rule_cleaned_files(wildcards): """Return cleaned auxiliary files required by one advanced override.""" plan = _read_auxiliary_plan() - group_ids = plan["rules"][ - wildcards.rule_name - ]["required_group_ids"] + group_ids = plan["rules"][wildcards.rule_name]["required_group_ids"] return [ - ( - "/automatic/" - "auxiliary/cleaned/" - f"{group_id}.parquet" - ) + ("/automatic/" "auxiliary/cleaned/" f"{group_id}.parquet") for group_id in group_ids ] @@ -156,24 +105,15 @@ def advanced_constructed_profiles(_wildcards): plan = _read_auxiliary_plan() return [ - ( - "/automatic/" - "auxiliary/constructed/" - f"{rule_name}.parquet" - ) - for rule_name in plan[ - "constructed_profile_rule_names" - ] + ("/automatic/" "auxiliary/constructed/" f"{rule_name}.parquet") + for rule_name in plan["constructed_profile_rule_names"] ] def final_clean_demand_input(_wildcards): """Return the cleaned demand appropriate for the configured mode.""" if config["gap_filling"]["mode"] == "advanced": - return ( - "/automatic/" - "load_advanced_cleaned.parquet" - ) + return "/automatic/" "load_advanced_cleaned.parquet" return rules.clean_demand.output.demand @@ -181,10 +121,7 @@ def final_clean_demand_input(_wildcards): def final_cleaning_method_input(_wildcards): """Return cleaning provenance appropriate for the configured mode.""" if config["gap_filling"]["mode"] == "advanced": - return ( - "/automatic/" - "load_advanced_cleaning_method.parquet" - ) + return "/automatic/" "load_advanced_cleaning_method.parquet" return rules.clean_demand.output.cleaning_method @@ -192,11 +129,7 @@ def final_cleaning_method_input(_wildcards): def advanced_external_profile_files(_wildcards): plan = _read_auxiliary_plan() - return list( - dict.fromkeys( - plan["external_profile_files"].values() - ) - ) + return list(dict.fromkeys(plan["external_profile_files"].values())) def auxiliary_entsoe_threads(wildcards): @@ -206,10 +139,7 @@ def auxiliary_entsoe_threads(wildcards): batch = next( batch for batch in plan["batches"] - if ( - batch["batch_id"] == wildcards.batch_id - and batch["source"] == "entsoe_api" - ) + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe_api") ) return min( @@ -222,15 +152,12 @@ checkpoint plan_auxiliary_data: input: fill_plan=rules.clean_demand.output.auxiliary_fill_plan, output: - plan=( - "/automatic/" - "auxiliary/advanced_execution_plan.json" - ), + plan=("/automatic/" "auxiliary/advanced_execution_plan.json"), + conda: + "../envs/module.yaml" params: gap_filling=config["gap_filling"], source_names=config["load_sources"], - conda: - "../envs/module.yaml" message: "Plan auxiliary electricity-demand acquisition." script: @@ -242,23 +169,16 @@ rule finalise_clean_demand: demand=final_clean_demand_input, cleaning_method=final_cleaning_method_input, output: - demand=( - "/automatic/" - "load_cleaned.parquet" - ), - cleaning_method=( - "/automatic/" - "load_final_cleaning_method.parquet" - ), + demand=("/automatic/" "load_cleaned.parquet"), + cleaning_method=("/automatic/" "load_final_cleaning_method.parquet"), cleaning_method_rank=( - "/automatic/" - "load_final_cleaning_method_rank.parquet" + "/automatic/" "load_final_cleaning_method_rank.parquet" ), + conda: + "../envs/module.yaml" params: source_names=config["load_sources"], gap_filling=config["gap_filling"], - conda: - "../envs/module.yaml" message: "Finalise cleaned electricity demand and provenance." script: @@ -270,21 +190,13 @@ rule download_auxiliary_load_entsoe_api: token_entsoe="", plan=auxiliary_acquisition_plan, output: - load=( - "/automatic/" - "auxiliary/entsoe_api/" - "{batch_id}.parquet" - ), + load=("/automatic/" "auxiliary/entsoe_api/" "{batch_id}.parquet"), log: - ( - "/auxiliary/" - "entsoe_api/{batch_id}.log" - ), + ("/auxiliary/" "entsoe_api/{batch_id}.log"), localrule: True conda: "../envs/module.yaml" - threads: - auxiliary_entsoe_threads + threads: auxiliary_entsoe_threads message: "Download auxiliary electricity load from ENTSO-E." script: @@ -296,16 +208,9 @@ rule prepare_auxiliary_load_opsd: load=rules.download_load_entsoe_opsd.output.load, plan=auxiliary_acquisition_plan, output: - load=( - "/automatic/" - "auxiliary/opsd_api/" - "{batch_id}.parquet" - ), + load=("/automatic/" "auxiliary/opsd_api/" "{batch_id}.parquet"), log: - ( - "/auxiliary/" - "opsd_api/{batch_id}.log" - ), + ("/auxiliary/" "opsd_api/{batch_id}.log"), conda: "../envs/module.yaml" message: @@ -319,16 +224,9 @@ rule prepare_auxiliary_load_neso: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=( - "/automatic/" - "auxiliary/neso/" - "{batch_id}.parquet" - ), + load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), log: - ( - "/auxiliary/" - "neso/{batch_id}.log" - ), + ("/auxiliary/" "neso/{batch_id}.log"), conda: "../envs/module.yaml" message: @@ -342,11 +240,7 @@ rule combine_auxiliary_sources: plan=auxiliary_acquisition_plan, sources=auxiliary_group_source_files, output: - demand=( - "/automatic/" - "auxiliary/combined/" - "{group_id}.parquet" - ), + demand=("/automatic/" "auxiliary/combined/" "{group_id}.parquet"), data_source=( "/automatic/" "auxiliary/combined/" @@ -357,10 +251,10 @@ rule combine_auxiliary_sources: "auxiliary/combined/" "{group_id}_cleaning_method.parquet" ), - params: - source_priority=config["load_sources"], conda: "../envs/module.yaml" + params: + source_priority=config["load_sources"], message: "Combine auxiliary electricity-demand sources." script: @@ -369,38 +263,28 @@ rule combine_auxiliary_sources: rule clean_auxiliary_data: input: - demand=( - "/automatic/" - "auxiliary/combined/" - "{group_id}.parquet" - ), + demand=("/automatic/" "auxiliary/combined/" "{group_id}.parquet"), cleaning_method=( "/automatic/" "auxiliary/combined/" "{group_id}_cleaning_method.parquet" ), output: - demand=( - "/automatic/" - "auxiliary/cleaned/" - "{group_id}.parquet" - ), + demand=("/automatic/" "auxiliary/cleaned/" "{group_id}.parquet"), cleaning_method=( "/automatic/" "auxiliary/cleaned/" "{group_id}_cleaning_method.parquet" ), + conda: + "../envs/module.yaml" params: basic_rules=config["gap_filling"]["basic"]["rules"], enabled=( - config["gap_filling"] - ["advanced"] - ["auxiliary_data"] - ["basic_cleaning"] - ["enabled"] + config["gap_filling"]["advanced"]["auxiliary_data"]["basic_cleaning"][ + "enabled" + ] ), - conda: - "../envs/module.yaml" message: "Apply basic cleaning to auxiliary electricity demand." script: @@ -413,9 +297,7 @@ rule construct_auxiliary_profile: sources=auxiliary_rule_cleaned_files, output: profile=( - "/automatic/" - "auxiliary/constructed/" - "{rule_name}.parquet" + "/automatic/" "auxiliary/constructed/" "{rule_name}.parquet" ), conda: "../envs/module.yaml" @@ -427,25 +309,15 @@ rule construct_auxiliary_profile: rule apply_advanced_overrides: input: - demand=( - "/automatic/" - "load_basic_cleaned.parquet" - ), - cleaning_method=( - "/automatic/" - "load_cleaning_method.parquet" - ), + demand=("/automatic/" "load_basic_cleaned.parquet"), + cleaning_method=("/automatic/" "load_cleaning_method.parquet"), plan=auxiliary_acquisition_plan, constructed_profiles=advanced_constructed_profiles, external_profiles=advanced_external_profile_files, output: - demand=( - "/automatic/" - "load_advanced_cleaned.parquet" - ), + demand=("/automatic/" "load_advanced_cleaned.parquet"), cleaning_method=( - "/automatic/" - "load_advanced_cleaning_method.parquet" + "/automatic/" "load_advanced_cleaning_method.parquet" ), conda: "../envs/module.yaml" diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 24e5dc6..05d4898 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -2,38 +2,35 @@ from datetime import datetime, timedelta + def _years_in_period( start: str, end: str, - ) -> tuple[int, ...]: - """Return calendar years intersecting an end-exclusive period.""" - start = datetime.fromisoformat(start) - end = datetime.fromisoformat(end) - - if end <= start: - raise ValueError( - "Period end must be later than period start." - ) - - final_included_time = end - timedelta( - microseconds=1 - ) +) -> tuple[int, ...]: + """Return calendar years intersecting an end-exclusive period.""" + start = datetime.fromisoformat(start) + end = datetime.fromisoformat(end) + + if end <= start: + raise ValueError("Period end must be later than period start.") - return tuple( - range( - start.year, - final_included_time.year + 1, - ) + final_included_time = end - timedelta(microseconds=1) + + return tuple( + range( + start.year, + final_included_time.year + 1, ) + ) + NESO_YEARS = _years_in_period( - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], ) NESO_RAW_FILES = expand( - "/automatic/neso/" - "historic_demand_{year}.csv", + "/automatic/neso/" "historic_demand_{year}.csv", year=NESO_YEARS, ) @@ -80,19 +77,17 @@ rule download_load_entsoe_opsd: curl -sSLo {output.load:q} {params.url_load:q} """ + rule download_load_neso_year: output: - annual_file=( - "/automatic/neso/" - "historic_demand_{year}.csv" - ), - params: - year=lambda wildcards: int(wildcards.year), + annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), log: "/download_load_neso_{year}.log", localrule: True conda: "../envs/module.yaml" + params: + year=lambda wildcards: int(wildcards.year), message: "Download NESO historic electricity demand for {wildcards.year}." script: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 12f7acd..1edf9f1 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -1,22 +1,15 @@ from datetime import datetime, timedelta + def neso_raw_files(_wildcards): """Return annual NESO input files for the configured period.""" - start = datetime.fromisoformat( - config["temporal_scope"]["start"] - ) - end = datetime.fromisoformat( - config["temporal_scope"]["end"] - ) + start = datetime.fromisoformat(config["temporal_scope"]["start"]) + end = datetime.fromisoformat(config["temporal_scope"]["end"]) if end <= start: - raise ValueError( - "Period end must be later than period start." - ) + raise ValueError("Period end must be later than period start.") - final_included_time = end - timedelta( - microseconds=1 - ) + final_included_time = end - timedelta(microseconds=1) years = range( start.year, @@ -24,11 +17,7 @@ def neso_raw_files(_wildcards): ) return [ - ( - "/automatic/neso/" - f"historic_demand_{year}.csv" - ) - for year in years + ("/automatic/neso/" f"historic_demand_{year}.csv") for year in years ] @@ -37,14 +26,14 @@ rule prepare_load_opsd: load="/automatic/load_entsoe_opsd.csv", output: load="/automatic/load_opsd_api.parquet", - params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe_api"]["countries"], log: "/prepare_load_opsd.log", conda: "../envs/module.yaml" + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe_api"]["countries"], message: "Prepare electricity-demand data from OPSD." script: @@ -56,78 +45,52 @@ rule prepare_load_neso: annual_files=neso_raw_files, output: load="/automatic/load_neso.parquet", - params: - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe_api"]["countries"], log: "/prepare_load_neso.log", conda: "../envs/module.yaml" + params: + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + country_codes=internal["load_entsoe_api"]["countries"], message: "Prepare electricity-demand data from NESO." script: "../scripts/prepare_load_neso.py" - + LOAD_SOURCE_PATHS = { - "entsoe_api": ( - "/automatic/" - "load_entsoe_api.parquet" - ), - "neso": ( - "/automatic/" - "load_neso.parquet" - ), - "opsd_api": ( - "/automatic/" - "load_opsd_api.parquet" - ), + "entsoe_api": ("/automatic/" "load_entsoe_api.parquet"), + "neso": ("/automatic/" "load_neso.parquet"), + "opsd_api": ("/automatic/" "load_opsd_api.parquet"), } def configured_load_inputs(_wildcards): - return [ - LOAD_SOURCE_PATHS[source_name] - for source_name in config["load_sources"] - ] + return [LOAD_SOURCE_PATHS[source_name] for source_name in config["load_sources"]] rule clean_demand: input: - configured_load_inputs + configured_load_inputs, output: - demand=( - "/automatic/" - "load_basic_cleaned.parquet" - ), - data_source=( - "/automatic/" - "load_data_source.parquet" - ), - cleaning_method=( - "/automatic/" - "load_cleaning_method.parquet" - ), + demand=("/automatic/" "load_basic_cleaned.parquet"), + data_source=("/automatic/" "load_data_source.parquet"), + cleaning_method=("/automatic/" "load_cleaning_method.parquet"), cleaning_method_rank=( - "/automatic/" - "load_cleaning_method_rank.parquet" - ), - gap_report=( - "/automatic/" - "load_gap_report.parquet" + "/automatic/" "load_cleaning_method_rank.parquet" ), + gap_report=("/automatic/" "load_gap_report.parquet"), auxiliary_fill_plan=( - "/automatic/" - "load_auxiliary_fill_plan.parquet" + "/automatic/" "load_auxiliary_fill_plan.parquet" ), - params: - source_names=config["load_sources"], - gap_filling=config["gap_filling"], log: "/clean_demand.log", conda: "../envs/module.yaml" + params: + source_names=config["load_sources"], + gap_filling=config["gap_filling"], message: "Combine and clean electricity-demand sources." script: @@ -136,35 +99,26 @@ rule clean_demand: rule plot_cleaning_timeline: input: - demand=( - "/automatic/" - "load_cleaned.parquet" - ), - cleaning_method=( - "/automatic/" - "load_final_cleaning_method.parquet" - ), + demand=("/automatic/" "load_cleaned.parquet"), + cleaning_method=("/automatic/" "load_final_cleaning_method.parquet"), cleaning_method_rank=( - "/automatic/" - "load_final_cleaning_method_rank.parquet" + "/automatic/" "load_final_cleaning_method_rank.parquet" ), output: - plot=( - "/{shape}/" - "load_cleaning_timeline.pdf" - ), - params: - source_names=config["load_sources"], - gap_filling=config["gap_filling"], + plot=("/{shape}/" "load_cleaning_timeline.pdf"), log: "/{shape}/plot_cleaning_timeline.log", conda: "../envs/module.yaml" + params: + source_names=config["load_sources"], + gap_filling=config["gap_filling"], message: "Plot electricity-demand cleaning provenance." script: "../scripts/plot_cleaning_timeline.py" + rule clean_population: input: vector="", @@ -175,4 +129,3 @@ rule clean_population: "/{shape}/clean_population.log", wrapper: "v7.2.0/geo/rasterio/clip-geotiff" - diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 145bb0c..8ffa735 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -10,21 +10,13 @@ load_execution_plan, ) -load = pd.read_parquet( - snakemake.input.demand -) +load = pd.read_parquet(snakemake.input.demand) -cleaning_method = pd.read_parquet( - snakemake.input.cleaning_method -) +cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) -plan = load_execution_plan( - snakemake.input.plan -) +plan = load_execution_plan(snakemake.input.plan) -active_overrides = get_active_overrides( - plan -) +active_overrides = get_active_overrides(plan) constructed_profiles = { @@ -40,11 +32,8 @@ external_profiles = { - rule_name: external_profiles_by_path[ - Path(path) - ] - for rule_name, path - in plan["external_profile_files"].items() + rule_name: external_profiles_by_path[Path(path)] + for rule_name, path in plan["external_profile_files"].items() } @@ -57,10 +46,6 @@ ) -filled.to_parquet( - snakemake.output.demand -) +filled.to_parquet(snakemake.output.demand) -cleaning_method.to_parquet( - snakemake.output.cleaning_method -) +cleaning_method.to_parquet(snakemake.output.cleaning_method) diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py index 25d93fb..6a2831d 100644 --- a/workflow/scripts/clean_auxiliary_data.py +++ b/workflow/scripts/clean_auxiliary_data.py @@ -1,15 +1,9 @@ import pandas as pd - from cleaning.basic.apply import fill_basic_gaps +load = pd.read_parquet(snakemake.input.demand) -load = pd.read_parquet( - snakemake.input.demand -) - -cleaning_method = pd.read_parquet( - snakemake.input.cleaning_method -) +cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) cleaned, cleaning_method = fill_basic_gaps( load, @@ -18,10 +12,6 @@ enabled=snakemake.params.enabled, ) -cleaned.to_parquet( - snakemake.output.demand -) +cleaned.to_parquet(snakemake.output.demand) -cleaning_method.to_parquet( - snakemake.output.cleaning_method -) +cleaning_method.to_parquet(snakemake.output.cleaning_method) diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 03b5cbd..4184693 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -31,11 +31,7 @@ def main( sources = { source_name: _read_prepared_source(path) - for source_name, path in zip( - source_names, - input_paths, - strict=True, - ) + for source_name, path in zip(source_names, input_paths, strict=True) } ( @@ -44,50 +40,33 @@ def main( cleaning_method, cleaning_method_rank, gap_report, - auxiliary_fill_plan + auxiliary_fill_plan, ) = clean_demand( - sources, - source_priority=source_names, - gap_filling_config=gap_filling_config, + sources, source_priority=source_names, gap_filling_config=gap_filling_config ) cleaned.to_parquet(output.demand) data_source.to_parquet(output.data_source) - cleaning_method.to_parquet( - output.cleaning_method - ) + cleaning_method.to_parquet(output.cleaning_method) - cleaning_method_rank.to_parquet( - output.cleaning_method_rank - ) + cleaning_method_rank.to_parquet(output.cleaning_method_rank) - gap_report.to_parquet( - output.gap_report, - index=False, - ) + gap_report.to_parquet(output.gap_report, index=False) - auxiliary_fill_plan.to_parquet( - output.auxiliary_fill_plan, - index=False, - ) + auxiliary_fill_plan.to_parquet(output.auxiliary_fill_plan, index=False) _log_source_counts(data_source) - _log_cleaning_method_counts(cleaning_method,cleaning_method_rank) + _log_cleaning_method_counts(cleaning_method, cleaning_method_rank) _log_gap_report(gap_report) -def _read_prepared_source( - path: str | Path, -) -> pd.DataFrame: +def _read_prepared_source(path: str | Path) -> pd.DataFrame: """Read and validate one prepared demand source.""" demand = pd.read_parquet(path) if not isinstance(demand.index, pd.DatetimeIndex): - demand.index = pd.to_datetime( - demand.index, - utc=True, - ) + demand.index = pd.to_datetime(demand.index, utc=True) elif demand.index.tz is None: demand.index = demand.index.tz_localize("UTC") @@ -96,49 +75,32 @@ def _read_prepared_source( demand.index = demand.index.tz_convert("UTC") if demand.index.has_duplicates: - raise ValueError( - f"Demand source contains duplicate timestamps: {path}" - ) + raise ValueError(f"Demand source contains duplicate timestamps: {path}") if demand.columns.has_duplicates: - raise ValueError( - f"Demand source contains duplicate columns: {path}" - ) + raise ValueError(f"Demand source contains duplicate columns: {path}") return demand.sort_index() -def _log_source_counts( - data_source: pd.DataFrame, -) -> None: +def _log_source_counts(data_source: pd.DataFrame) -> None: """Log the number of cells supplied by each observed source.""" counts = data_source.stack().value_counts() for source_name, count in counts.items(): - logger.info( - "%s supplied %s observed values.", - source_name, - int(count), - ) + logger.info("%s supplied %s observed values.", source_name, int(count)) def _log_cleaning_method_counts( - cleaning_method: pd.DataFrame, - cleaning_method_rank: pd.DataFrame, + cleaning_method: pd.DataFrame, cleaning_method_rank: pd.DataFrame ) -> None: """Log the number of cells assigned to each cleaning method.""" method_counts = cleaning_method.stack().value_counts() for method_name, count in method_counts.items(): - method_mask = ( - cleaning_method.eq(method_name) - .fillna(False) - .to_numpy(dtype=bool) - ) + method_mask = cleaning_method.eq(method_name).fillna(False).to_numpy(dtype=bool) - ranks = cleaning_method_rank.to_numpy()[ - method_mask - ] + ranks = cleaning_method_rank.to_numpy()[method_mask] unique_ranks = pd.unique(ranks) @@ -157,26 +119,16 @@ def _log_cleaning_method_counts( ) -def _log_gap_report( - gap_report: pd.DataFrame, -) -> None: +def _log_gap_report(gap_report: pd.DataFrame) -> None: """Log unresolved-gap counts by country.""" if gap_report.empty: - logger.info( - "No advanced unresolved-gap report was generated." - ) + logger.info("No advanced unresolved-gap report was generated.") return - logger.info( - "Gap report contains %s contiguous unresolved gaps.", - len(gap_report), - ) + logger.info("Gap report contains %s contiguous unresolved gaps.", len(gap_report)) - country_summary = gap_report.groupby( - "country" - ).agg( - gap_count=("country", "size"), - missing_hours=("gap_hours", "sum"), + country_summary = gap_report.groupby("country").agg( + gap_count=("country", "size"), missing_hours=("gap_hours", "sum") ) for country, row in country_summary.iterrows(): @@ -187,25 +139,15 @@ def _log_gap_report( int(row["missing_hours"]), ) + if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main( input_paths=list(snakemake.input), - source_names=list( - snakemake.params.source_names - ), - gap_filling_config=( - snakemake.params.gap_filling - ), + source_names=list(snakemake.params.source_names), + gap_filling_config=(snakemake.params.gap_filling), output=snakemake.output, ) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 74f98b8..cd5d84c 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -46,8 +46,7 @@ def apply_auxiliary_fill_rule( if method == EXTERNAL_PROFILE: if profile is None: raise ValueError( - f"Advanced-fill rule {rule_name!r} requires " - "an external profile." + f"Advanced-fill rule {rule_name!r} requires an external profile." ) return apply_external_profile( @@ -64,10 +63,7 @@ def apply_auxiliary_fill_rule( if method == LEAVE_MISSING: return load.copy(), cleaning_method.copy() - raise ValueError( - f"Unsupported advanced-fill method {method!r}." - ) - + raise ValueError(f"Unsupported advanced-fill method {method!r}.") def apply_constructed_profile( @@ -85,57 +81,34 @@ def apply_constructed_profile( filled = load.copy() methods = cleaning_method.copy() - target_index = filled.index[ - (filled.index >= start) - & (filled.index < end) - ] + target_index = filled.index[(filled.index >= start) & (filled.index < end)] if not profile.index.equals(target_index): raise ValueError( - "Constructed profile index must exactly match " - "the target period." + "Constructed profile index must exactly match the target period." ) if country not in filled.columns: - raise ValueError( - f"Target country {country!r} is not present in load data." - ) + raise ValueError(f"Target country {country!r} is not present in load data.") if scope == "fill_gaps": - replace_mask = filled.loc[ - target_index, - country, - ].isna() + replace_mask = filled.loc[target_index, country].isna() elif scope == "overwrite": - replace_mask = pd.Series( - True, - index=target_index, - ) + replace_mask = pd.Series(True, index=target_index) else: - raise ValueError( - f"Unsupported advanced fill scope: {scope!r}" - ) + raise ValueError(f"Unsupported advanced fill scope: {scope!r}") - replacement_index = target_index[ - replace_mask.to_numpy() - ] + replacement_index = target_index[replace_mask.to_numpy()] - filled.loc[ - replacement_index, - country, - ] = profile.loc[ - replacement_index - ] + filled.loc[replacement_index, country] = profile.loc[replacement_index] - methods.loc[ - replacement_index, - country, - ] = rule_name + methods.loc[replacement_index, country] = rule_name return filled, methods + def apply_auxiliary_fill_rules( load: pd.DataFrame, cleaning_method: pd.DataFrame, @@ -161,11 +134,7 @@ def apply_auxiliary_fill_rules( profile = None filled, methods = apply_auxiliary_fill_rule( - filled, - methods, - rule_name=rule_name, - rule=rule, - profile=profile, + filled, methods, rule_name=rule_name, rule=rule, profile=profile ) return filled, methods @@ -187,43 +156,23 @@ def apply_external_profile( methods = cleaning_method.copy() if country not in filled.columns: - raise ValueError( - f"Target country {country!r} is not present in load data." - ) + raise ValueError(f"Target country {country!r} is not present in load data.") - candidate = profile.loc[ - (profile.index >= start) - & (profile.index < end) - ] + candidate = profile.loc[(profile.index >= start) & (profile.index < end)] - candidate = candidate.loc[ - candidate.index.intersection(filled.index) - ] + candidate = candidate.loc[candidate.index.intersection(filled.index)] if scope == "fill_gaps": - replace_index = candidate.index[ - filled.loc[ - candidate.index, - country, - ].isna() - ] + replace_index = candidate.index[filled.loc[candidate.index, country].isna()] elif scope == "overwrite": replace_index = candidate.index else: - raise ValueError( - f"Unsupported advanced fill scope: {scope!r}" - ) + raise ValueError(f"Unsupported advanced fill scope: {scope!r}") - filled.loc[ - replace_index, - country, - ] = candidate.loc[replace_index] + filled.loc[replace_index, country] = candidate.loc[replace_index] - methods.loc[ - replace_index, - country, - ] = rule_name + methods.loc[replace_index, country] = rule_name return filled, methods diff --git a/workflow/scripts/cleaning/advanced/gap_report.py b/workflow/scripts/cleaning/advanced/gap_report.py index 77f6122..9083c3f 100644 --- a/workflow/scripts/cleaning/advanced/gap_report.py +++ b/workflow/scripts/cleaning/advanced/gap_report.py @@ -9,11 +9,7 @@ from cleaning.validation import validate_load -def build_gap_report( - load: pd.DataFrame, - *, - enabled: bool, -) -> pd.DataFrame: +def build_gap_report(load: pd.DataFrame, *, enabled: bool) -> pd.DataFrame: """Describe contiguous unresolved gaps in cleaned load data. An empty report with the expected columns is returned when reporting @@ -44,9 +40,7 @@ def build_gap_report( if not missing.any(): continue - group_ids = missing.ne( - missing.shift(fill_value=False) - ).cumsum() + group_ids = missing.ne(missing.shift(fill_value=False)).cumsum() for _, group in missing.groupby(group_ids): if not bool(group.iloc[0]): @@ -60,26 +54,14 @@ def build_gap_report( "gap_start": timestamps[0], "gap_end": timestamps[-1] + pd.Timedelta(hours=1), "gap_hours": len(timestamps), - "touches_start_boundary": ( - timestamps[0] == first_timestamp - ), - "touches_end_boundary": ( - timestamps[-1] == last_timestamp - ), + "touches_start_boundary": (timestamps[0] == first_timestamp), + "touches_end_boundary": (timestamps[-1] == last_timestamp), } ) - report = pd.DataFrame.from_records( - records, - columns=columns, - ) + report = pd.DataFrame.from_records(records, columns=columns) if report.empty: return report - return report.sort_values( - [ - "country", - "gap_start", - ] - ).reset_index(drop=True) \ No newline at end of file + return report.sort_values(["country", "gap_start"]).reset_index(drop=True) diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py index e84f7fe..29318a6 100644 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py @@ -8,6 +8,7 @@ METHOD_NAME = "construct_from_sources" + def _align_leap_day( auxiliary: pd.Series, *, @@ -16,26 +17,16 @@ def _align_leap_day( target_index: pd.DatetimeIndex, ) -> pd.Series: """Align source values with target calendar around February 29.""" - source_values = auxiliary.loc[ - (auxiliary.index >= start) - & (auxiliary.index < end) - ] + source_values = auxiliary.loc[(auxiliary.index >= start) & (auxiliary.index < end)] source_has_leap_day = ( - (source_values.index.month == 2) - & (source_values.index.day == 29) + (source_values.index.month == 2) & (source_values.index.day == 29) ).any() - target_has_leap_day = ( - (target_index.month == 2) - & (target_index.day == 29) - ).any() + target_has_leap_day = ((target_index.month == 2) & (target_index.day == 29)).any() if source_has_leap_day and not target_has_leap_day: - leap_day = ( - (source_values.index.month == 2) - & (source_values.index.day == 29) - ) + leap_day = (source_values.index.month == 2) & (source_values.index.day == 29) return source_values.loc[~leap_day] @@ -58,23 +49,14 @@ def _align_leap_day( "February 28 and March 1 source data are required." ) - leap_values = ( - feb_28.to_numpy(dtype=float) - + march_1.to_numpy(dtype=float) - ) / 2 + leap_values = (feb_28.to_numpy(dtype=float) + march_1.to_numpy(dtype=float)) / 2 - insertion_point = ( - source_values.index.month < 3 - ).sum() + insertion_point = (source_values.index.month < 3).sum() values = source_values.to_numpy(dtype=float) aligned = pd.Series( - data=[ - *values[:insertion_point], - *leap_values, - *values[insertion_point:], - ], + data=[*values[:insertion_point], *leap_values, *values[insertion_point:]], dtype=float, ) @@ -95,20 +77,12 @@ def _match_energy( for source in target_sources: country = source["country"] - start = as_utc_timestamp( - source["start"] - ) - end = as_utc_timestamp( - source["end"] - ) - weight = float( - source.get("weight", 1) - ) + start = as_utc_timestamp(source["start"]) + end = as_utc_timestamp(source["end"]) + weight = float(source.get("weight", 1)) source_values = auxiliary.loc[ - (auxiliary.index >= start) - & (auxiliary.index < end), - country, + (auxiliary.index >= start) & (auxiliary.index < end), country ] if source_values.empty: @@ -123,48 +97,34 @@ def _match_energy( f"Source {country!r}: {start} to {end}." ) - weighted_energy += ( - float(source_values.sum()) * weight - ) + weighted_energy += float(source_values.sum()) * weight total_weight += weight if total_weight == 0: - raise ValueError( - "Scaling source weights must sum to more than zero." - ) + raise ValueError("Scaling source weights must sum to more than zero.") target_energy = weighted_energy / total_weight profile_energy = float(profile.sum()) if profile_energy == 0: raise ValueError( - "Cannot match energy for a constructed profile " - "with zero total energy." + "Cannot match energy for a constructed profile with zero total energy." ) - return profile * ( - target_energy / profile_energy - ) + return profile * (target_energy / profile_energy) def _apply_scaling( - profile: pd.Series, - *, - auxiliary: pd.DataFrame, - scaling: Mapping[str, Any], + profile: pd.Series, *, auxiliary: pd.DataFrame, scaling: Mapping[str, Any] ) -> pd.Series: """Scale a constructed profile according to its configured method.""" method = scaling["method"] if method != "match_energy": - raise ValueError( - f"Unsupported auxiliary scaling method: {method!r}." - ) + raise ValueError(f"Unsupported auxiliary scaling method: {method!r}.") return _match_energy( - profile, - auxiliary=auxiliary, - target_sources=scaling["target_sources"], + profile, auxiliary=auxiliary, target_sources=scaling["target_sources"] ) @@ -181,21 +141,12 @@ def construct_from_sources( for source in sources: country = source["country"] - start = as_utc_timestamp( - source["start"] - ) - end = as_utc_timestamp( - source["end"] - ) - weight = float( - source.get("weight", 1) - ) + start = as_utc_timestamp(source["start"]) + end = as_utc_timestamp(source["end"]) + weight = float(source.get("weight", 1)) source_values = _align_leap_day( - auxiliary[country], - start=start, - end=end, - target_index=target_index, + auxiliary[country], start=start, end=end, target_index=target_index ) if len(source_values) != len(target_index): @@ -213,34 +164,19 @@ def construct_from_sources( f"Source {country!r}: {start} to {end}." ) - remapped = pd.Series( - source_values.to_numpy(), - index=target_index, - dtype=float, - ) + remapped = pd.Series(source_values.to_numpy(), index=target_index, dtype=float) - weighted_sources.append( - remapped * weight - ) + weighted_sources.append(remapped * weight) weights.append(weight) if not weighted_sources: - raise ValueError( - "At least one auxiliary source is required." - ) + raise ValueError("At least one auxiliary source is required.") - weighted_sum = sum( - weighted_sources[1:], - weighted_sources[0].copy(), - ) + weighted_sum = sum(weighted_sources[1:], weighted_sources[0].copy()) profile = weighted_sum / sum(weights) if scaling is not None: - profile = _apply_scaling( - profile, - auxiliary=auxiliary, - scaling=scaling, - ) + profile = _apply_scaling(profile, auxiliary=auxiliary, scaling=scaling) - return profile \ No newline at end of file + return profile diff --git a/workflow/scripts/cleaning/advanced/methods/external_profile.py b/workflow/scripts/cleaning/advanced/methods/external_profile.py index da0798a..2d44e74 100644 --- a/workflow/scripts/cleaning/advanced/methods/external_profile.py +++ b/workflow/scripts/cleaning/advanced/methods/external_profile.py @@ -6,15 +6,10 @@ METHOD_NAME = "external_profile" -EXPECTED_COLUMNS = { - "timestamp", - "demand", -} +EXPECTED_COLUMNS = {"timestamp", "demand"} -def read_external_profile( - path: str | Path, -) -> pd.Series: +def read_external_profile(path: str | Path) -> pd.Series: """Read a timestamped external demand series from CSV.""" profile = pd.read_csv(path) @@ -24,41 +19,25 @@ def read_external_profile( "'timestamp' and 'demand'." ) - timestamps = pd.to_datetime( - profile["timestamp"], - utc=True, - errors="raise", - ) + timestamps = pd.to_datetime(profile["timestamp"], utc=True, errors="raise") if timestamps.duplicated().any(): - raise ValueError( - "External profile timestamps must be unique." - ) + raise ValueError("External profile timestamps must be unique.") if ( (timestamps.dt.minute != 0).any() or (timestamps.dt.second != 0).any() or (timestamps.dt.microsecond != 0).any() ): - raise ValueError( - "External profile timestamps must be aligned " - "to whole hours." - ) + raise ValueError("External profile timestamps must be aligned to whole hours.") - values = pd.to_numeric( - profile["demand"], - errors="raise", - ) + values = pd.to_numeric(profile["demand"], errors="raise") if values.isna().any(): - raise ValueError( - "External profile demand values must not be missing." - ) + raise ValueError("External profile demand values must not be missing.") result = pd.Series( - values.to_numpy(), - index=pd.DatetimeIndex(timestamps), - dtype=float, + values.to_numpy(), index=pd.DatetimeIndex(timestamps), dtype=float ) return result.sort_index() diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index 5e4d53e..939621b 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -32,31 +32,24 @@ def build_advanced_execution_plan( overrides = gap_filling_config["advanced"]["overrides"] ordered_active_rule_names = _get_ordered_active_rule_names( - fill_plan, - overrides=overrides, + fill_plan, overrides=overrides ) active_overrides = { - rule_name: overrides[rule_name] - for rule_name in ordered_active_rule_names + rule_name: overrides[rule_name] for rule_name in ordered_active_rule_names } requirements = build_auxiliary_acquisition_requirements( overrides=active_overrides, basic_rules=gap_filling_config["basic"]["rules"], basic_cleaning_enabled=( - gap_filling_config["advanced"] - ["auxiliary_data"] - ["basic_cleaning"] - ["enabled"] + gap_filling_config["advanced"]["auxiliary_data"]["basic_cleaning"][ + "enabled" + ] ), ) - requests = build_auxiliary_source_requests( - requirements, - source_names=source_names, - ) + requests = build_auxiliary_source_requests(requirements, source_names=source_names) batches = [ - _serialize_batch(batch) - for batch in build_auxiliary_source_batches(requests) + _serialize_batch(batch) for batch in build_auxiliary_source_batches(requests) ] rules: dict[str, dict[str, object]] = {} @@ -70,15 +63,12 @@ def build_advanced_execution_plan( if override["method"] == CONSTRUCT_FROM_SOURCES: required_group_ids = _get_required_auxiliary_group_ids( - batch_plan, - override=override, + batch_plan, override=override ) constructed_profile_rule_names.append(rule_name) elif override["method"] == EXTERNAL_PROFILE: - external_profile_files[rule_name] = str( - override["path"] - ) + external_profile_files[rule_name] = str(override["path"]) rules[rule_name] = { "override": override, @@ -112,21 +102,14 @@ def _empty_execution_plan() -> dict[str, object]: def _get_ordered_active_rule_names( - fill_plan: pd.DataFrame, - *, - overrides: Mapping[str, Mapping[str, Any]], + fill_plan: pd.DataFrame, *, overrides: Mapping[str, Mapping[str, Any]] ) -> list[str]: """Return active rule names in configured execution order.""" if "rule_name" not in fill_plan.columns: - raise ValueError( - "Auxiliary fill plan must define a 'rule_name' column." - ) + raise ValueError("Auxiliary fill plan must define a 'rule_name' column.") duplicate_rule_names = sorted( - fill_plan.loc[ - fill_plan["rule_name"].duplicated(), - "rule_name", - ] + fill_plan.loc[fill_plan["rule_name"].duplicated(), "rule_name"] .drop_duplicates() .tolist() ) @@ -144,24 +127,16 @@ def _get_ordered_active_rule_names( f"overrides: {sorted(unknown_rule_names)}." ) - return [ - rule_name - for rule_name in overrides - if rule_name in active_rule_names - ] + return [rule_name for rule_name in overrides if rule_name in active_rule_names] -def _serialize_batch( - batch: Mapping[str, object], -) -> dict[str, object]: +def _serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: """Convert one planned batch to JSON-compatible values.""" start = pd.Timestamp(batch["start"]) end = pd.Timestamp(batch["end"]) if end <= start: - raise ValueError( - "Auxiliary batch end must be later than its start." - ) + raise ValueError("Auxiliary batch end must be later than its start.") final_included_time = end - pd.Timedelta(nanoseconds=1) @@ -169,12 +144,7 @@ def _serialize_batch( **batch, "start": start.isoformat(), "end": end.isoformat(), - "years": list( - range( - start.year, - final_included_time.year + 1, - ) - ), + "years": list(range(start.year, final_included_time.year + 1)), } @@ -185,10 +155,7 @@ def _index_batch_ids_by_source( result: dict[str, list[str]] = {} for batch in batches: - result.setdefault( - str(batch["source"]), - [], - ).append(str(batch["batch_id"])) + result.setdefault(str(batch["source"]), []).append(str(batch["batch_id"])) return result @@ -200,35 +167,23 @@ def _index_batch_ids_by_group( result: dict[str, list[str]] = {} for batch in batches: - result.setdefault( - str(batch["group_id"]), - [], - ).append(str(batch["batch_id"])) + result.setdefault(str(batch["group_id"]), []).append(str(batch["batch_id"])) return result - -def _get_required_auxiliary_sources( - override: Mapping, -) -> list[Mapping]: +def _get_required_auxiliary_sources(override: Mapping) -> list[Mapping]: """Return all auxiliary sources consumed by an override.""" sources = list(override["sources"]) scaling = override.get("scaling") if scaling is not None: - sources.extend( - scaling.get("target_sources", []) - ) + sources.extend(scaling.get("target_sources", [])) return sources -def _get_required_auxiliary_group_ids( - plan: Mapping, - *, - override: Mapping, -) -> list[str]: +def _get_required_auxiliary_group_ids(plan: Mapping, *, override: Mapping) -> list[str]: """Return auxiliary groups required to execute one override.""" group_ids: set[str] = set() diff --git a/workflow/scripts/cleaning/advanced/planning/manifest.py b/workflow/scripts/cleaning/advanced/planning/manifest.py index fa7bc66..f1e8ccf 100644 --- a/workflow/scripts/cleaning/advanced/planning/manifest.py +++ b/workflow/scripts/cleaning/advanced/planning/manifest.py @@ -9,62 +9,34 @@ EXECUTION_PLAN_VERSION = 1 -def write_execution_plan( - plan: Mapping[str, Any], - path: str | Path, -) -> None: + +def write_execution_plan(plan: Mapping[str, Any], path: str | Path) -> None: """Write one compiled advanced execution plan.""" path = Path(path) - path.parent.mkdir( - parents=True, - exist_ok=True, - ) - - with path.open( - "w", - encoding="utf-8", - ) as file: - json.dump( - plan, - file, - indent=2, - ) + path.parent.mkdir(parents=True, exist_ok=True) + + with path.open("w", encoding="utf-8") as file: + json.dump(plan, file, indent=2) def write_advanced_execution_plan( - *, - plan: dict[str, object], - output_path: str | Path, + *, plan: dict[str, object], output_path: str | Path ) -> None: """Write the advanced execution plan as JSON.""" output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) - - with output_path.open( - "w", - encoding="utf-8", - ) as file: - json.dump( - plan, - file, - indent=2, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) + with output_path.open("w", encoding="utf-8") as file: + json.dump(plan, file, indent=2) -def load_execution_plan( - path: str | Path, -) -> dict[str, Any]: + +def load_execution_plan(path: str | Path) -> dict[str, Any]: """Load one compiled advanced execution plan.""" with open(path, encoding="utf-8") as file: plan = json.load(file) if not isinstance(plan, dict): - raise TypeError( - "Advanced execution plan must contain a JSON object." - ) + raise TypeError("Advanced execution plan must contain a JSON object.") version = plan.get("version") @@ -78,10 +50,7 @@ def load_execution_plan( def get_batch( - plan: Mapping[str, Any], - *, - batch_id: str, - source: str | None = None, + plan: Mapping[str, Any], *, batch_id: str, source: str | None = None ) -> Mapping[str, Any]: """Return exactly one compiled auxiliary batch.""" matches = [ @@ -89,19 +58,12 @@ def get_batch( for batch in plan["batches"] if ( batch["batch_id"] == batch_id - and ( - source is None - or batch["source"] == source - ) + and (source is None or batch["source"] == source) ) ] if len(matches) != 1: - source_text = ( - f" for source {source!r}" - if source is not None - else "" - ) + source_text = f" for source {source!r}" if source is not None else "" raise ValueError( "Expected exactly one auxiliary batch " f"{batch_id!r}{source_text}, found {len(matches)}." @@ -110,17 +72,13 @@ def get_batch( return matches[0] -def get_active_overrides( - plan: Mapping[str, Any], -) -> dict[str, Mapping[str, Any]]: +def get_active_overrides(plan: Mapping[str, Any]) -> dict[str, Mapping[str, Any]]: """Return active overrides in compiled execution order.""" rule_names = plan["active_rule_names"] rules = plan["rules"] unknown_rule_names = [ - rule_name - for rule_name in rule_names - if rule_name not in rules + rule_name for rule_name in rule_names if rule_name not in rules ] if unknown_rule_names: @@ -129,24 +87,16 @@ def get_active_overrides( f"rules: {unknown_rule_names}." ) - return { - rule_name: rules[rule_name]["override"] - for rule_name in rule_names - } + return {rule_name: rules[rule_name]["override"] for rule_name in rule_names} -def get_rule_override( - plan: Mapping[str, Any], - *, - rule_name: str, -) -> Mapping[str, Any]: +def get_rule_override(plan: Mapping[str, Any], *, rule_name: str) -> Mapping[str, Any]: """Return one active rule's compiled override definition.""" try: rule = plan["rules"][rule_name] except KeyError as error: raise ValueError( - "Advanced execution plan does not contain active rule " - f"{rule_name!r}." + f"Advanced execution plan does not contain active rule {rule_name!r}." ) from error return rule["override"] diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 1399395..4d5a38c 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -10,46 +10,25 @@ ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE -FILL_GAPS= "fill_gaps" +FILL_GAPS = "fill_gaps" OVERWRITE = "overwrite" LEAVE_MISSING = "leave_missing" -def validate_auxiliary_fill_rule( - rule_name: str, - rule: Mapping[str, Any], -) -> None: +def validate_auxiliary_fill_rule(rule_name: str, rule: Mapping[str, Any]) -> None: """Validate one configured advanced-fill rule.""" if not isinstance(rule_name, str): - raise TypeError( - "Advanced-fill rule name must be a string." - ) + raise TypeError("Advanced-fill rule name must be a string.") if not rule_name: - raise ValueError( - "Advanced-fill rule name must not be empty." - ) + raise ValueError("Advanced-fill rule name must not be empty.") if not isinstance(rule, Mapping): - raise TypeError( - f"Advanced-fill rule {rule_name!r} must be a mapping." - ) + raise TypeError(f"Advanced-fill rule {rule_name!r} must be a mapping.") - country = _get_required_string( - rule, - key="country", - rule_name=rule_name, - ) - start = _get_required_timestamp( - rule, - key="start", - rule_name=rule_name, - ) - end = _get_required_timestamp( - rule, - key="end", - rule_name=rule_name, - ) + country = _get_required_string(rule, key="country", rule_name=rule_name) + start = _get_required_timestamp(rule, key="start", rule_name=rule_name) + end = _get_required_timestamp(rule, key="end", rule_name=rule_name) if end < start: raise ValueError( @@ -57,21 +36,10 @@ def validate_auxiliary_fill_rule( "timestamp before its start timestamp." ) - scope = _get_required_string( - rule, - key="scope", - rule_name=rule_name, - ) - method = _get_required_string( - rule, - key="method", - rule_name=rule_name, - ) + scope = _get_required_string(rule, key="scope", rule_name=rule_name) + method = _get_required_string(rule, key="method", rule_name=rule_name) - supported_scopes = { - FILL_GAPS, - OVERWRITE, - } + supported_scopes = {FILL_GAPS, OVERWRITE} if scope not in supported_scopes: raise ValueError( @@ -80,11 +48,7 @@ def validate_auxiliary_fill_rule( f"{sorted(supported_scopes)}." ) - supported_methods = { - CONSTRUCT_FROM_SOURCES, - EXTERNAL_PROFILE, - LEAVE_MISSING, - } + supported_methods = {CONSTRUCT_FROM_SOURCES, EXTERNAL_PROFILE, LEAVE_MISSING} if method not in supported_methods: raise ValueError( @@ -94,20 +58,12 @@ def validate_auxiliary_fill_rule( ) if method == CONSTRUCT_FROM_SOURCES: - _validate_sources( - rule, - rule_name=rule_name, - ) + _validate_sources(rule, rule_name=rule_name) if "scaling" in rule: - _validate_scaling( - rule["scaling"], - rule_name=rule_name, - ) + _validate_scaling(rule["scaling"], rule_name=rule_name) - elif method in { - LEAVE_MISSING, - }: + elif method in {LEAVE_MISSING}: if "sources" in rule: raise ValueError( f"Advanced-fill rule {rule_name!r} uses method " @@ -115,12 +71,7 @@ def validate_auxiliary_fill_rule( ) - -def _validate_sources( - rule: Mapping[str, Any], - *, - rule_name: str, -) -> None: +def _validate_sources(rule: Mapping[str, Any], *, rule_name: str) -> None: """Validate source references for source-based construction.""" if "sources" not in rule: raise ValueError( @@ -130,10 +81,7 @@ def _validate_sources( sources = rule["sources"] - if not isinstance(sources, Sequence) or isinstance( - sources, - (str, bytes), - ): + if not isinstance(sources, Sequence) or isinstance(sources, (str, bytes)): raise TypeError( f"'sources' in advanced-fill rule {rule_name!r} " "must be an ordered sequence." @@ -146,19 +94,11 @@ def _validate_sources( ) for position, source in enumerate(sources): - _validate_source( - source, - rule_name=rule_name, - position=position, - ) + _validate_source(source, rule_name=rule_name, position=position) def _validate_source( - source: object, - *, - rule_name: str, - position: int, - context: str = "source", + source: object, *, rule_name: str, position: int, context: str = "source" ) -> None: """Validate one country-period source reference.""" if not isinstance(source, Mapping): @@ -168,23 +108,14 @@ def _validate_source( ) _get_required_string( - source, - key="country", - rule_name=rule_name, - context=f"{context} {position}", + source, key="country", rule_name=rule_name, context=f"{context} {position}" ) start = _get_required_timestamp( - source, - key="start", - rule_name=rule_name, - context=f"{context} {position}", + source, key="start", rule_name=rule_name, context=f"{context} {position}" ) end = _get_required_timestamp( - source, - key="end", - rule_name=rule_name, - context=f"{context} {position}", + source, key="end", rule_name=rule_name, context=f"{context} {position}" ) if end < start: @@ -210,17 +141,12 @@ def _validate_source( def _get_required_string( - config: Mapping[str, Any], - *, - key: str, - rule_name: str, - context: str = "rule", + config: Mapping[str, Any], *, key: str, rule_name: str, context: str = "rule" ) -> str: """Return one required non-empty string field.""" if key not in config: raise ValueError( - f"Advanced-fill {context} in rule {rule_name!r} " - f"must define {key!r}." + f"Advanced-fill {context} in rule {rule_name!r} must define {key!r}." ) value = config[key] @@ -241,17 +167,12 @@ def _get_required_string( def _get_required_timestamp( - config: Mapping[str, Any], - *, - key: str, - rule_name: str, - context: str = "rule", + config: Mapping[str, Any], *, key: str, rule_name: str, context: str = "rule" ) -> pd.Timestamp: """Return one required timestamp as a UTC pandas timestamp.""" if key not in config: raise ValueError( - f"Advanced-fill {context} in rule {rule_name!r} " - f"must define {key!r}." + f"Advanced-fill {context} in rule {rule_name!r} must define {key!r}." ) try: @@ -270,28 +191,18 @@ def _get_required_timestamp( return timestamp -def _validate_scaling( - scaling: object, - *, - rule_name: str, -) -> None: +def _validate_scaling(scaling: object, *, rule_name: str) -> None: """Validate optional scaling configuration.""" if not isinstance(scaling, Mapping): raise TypeError( - f"'scaling' in advanced-fill rule {rule_name!r} " - "must be a mapping." + f"'scaling' in advanced-fill rule {rule_name!r} must be a mapping." ) method = _get_required_string( - scaling, - key="method", - rule_name=rule_name, - context="scaling", + scaling, key="method", rule_name=rule_name, context="scaling" ) - supported_methods = { - "match_energy", - } + supported_methods = {"match_energy"} if method not in supported_methods: raise ValueError( @@ -309,8 +220,7 @@ def _validate_scaling( target_sources = scaling["target_sources"] if not isinstance(target_sources, Sequence) or isinstance( - target_sources, - (str, bytes), + target_sources, (str, bytes) ): raise TypeError( f"'target_sources' in advanced-fill rule " @@ -341,16 +251,8 @@ def override_intersects_target_scope( target_end: pd.Timestamp, ) -> bool: """Return whether an advanced override intersects the model scope.""" - rule_start = _get_required_timestamp( - rule, - key="start", - rule_name=rule_name, - ) - rule_end = _get_required_timestamp( - rule, - key="end", - rule_name=rule_name, - ) + rule_start = _get_required_timestamp(rule, key="start", rule_name=rule_name) + rule_end = _get_required_timestamp(rule, key="end", rule_name=rule_name) target_start = pd.Timestamp(target_start) @@ -366,19 +268,11 @@ def override_intersects_target_scope( else: target_end = target_end.tz_convert("UTC") - country_intersects = ( - rule["country"] in target_countries - ) + country_intersects = rule["country"] in target_countries - period_intersects = ( - rule_start < target_end - and rule_end > target_start - ) + period_intersects = rule_start < target_end and rule_end > target_start - return ( - country_intersects - and period_intersects - ) + return country_intersects and period_intersects def build_auxiliary_fill_plan( @@ -390,17 +284,12 @@ def build_auxiliary_fill_plan( ) -> pd.DataFrame: """Validate and normalize configured advanced-fill rules.""" if not isinstance(rules, Mapping): - raise TypeError( - "Advanced-fill rules must be provided as a mapping." - ) + raise TypeError("Advanced-fill rules must be provided as a mapping.") records: list[dict[str, Any]] = [] for rule_name, rule in rules.items(): - validate_auxiliary_fill_rule( - rule_name, - rule, - ) + validate_auxiliary_fill_rule(rule_name, rule) if not override_intersects_target_scope( rule, @@ -420,9 +309,7 @@ def build_auxiliary_fill_plan( elif method == LEAVE_MISSING: status = "leave_missing" else: - raise AssertionError( - f"Unhandled advanced-fill method: {method!r}" - ) + raise AssertionError(f"Unhandled advanced-fill method: {method!r}") scaling = rule.get("scaling") @@ -431,26 +318,16 @@ def build_auxiliary_fill_plan( "rule_name": rule_name, "country": rule["country"], "target_start": _get_required_timestamp( - rule, - key="start", - rule_name=rule_name, + rule, key="start", rule_name=rule_name ), "target_end": _get_required_timestamp( - rule, - key="end", - rule_name=rule_name, + rule, key="end", rule_name=rule_name ), "scope": rule["scope"], "method": method, "status": status, - "source_count": len( - rule.get("sources", []) - ), - "scaling_method": ( - scaling["method"] - if scaling is not None - else None - ), + "source_count": len(rule.get("sources", [])), + "scaling_method": (scaling["method"] if scaling is not None else None), } ) @@ -466,19 +343,11 @@ def build_auxiliary_fill_plan( "scaling_method", ] - plan = pd.DataFrame.from_records( - records, - columns=columns, - ) + plan = pd.DataFrame.from_records(records, columns=columns) if plan.empty: return plan - return plan.sort_values( - [ - "country", - "target_start", - "rule_name", - ] - ).reset_index(drop=True) - + return plan.sort_values(["country", "target_start", "rule_name"]).reset_index( + drop=True + ) diff --git a/workflow/scripts/cleaning/advanced/planning/requirements.py b/workflow/scripts/cleaning/advanced/planning/requirements.py index e38a92f..18d9171 100644 --- a/workflow/scripts/cleaning/advanced/planning/requirements.py +++ b/workflow/scripts/cleaning/advanced/planning/requirements.py @@ -10,11 +10,7 @@ from cleaning.advanced.methods.construct_from_sources import METHOD_NAME -REQUIREMENT_COLUMNS = [ - "country", - "start", - "end", -] +REQUIREMENT_COLUMNS = ["country", "start", "end"] def compile_auxiliary_requirements( @@ -27,41 +23,27 @@ def compile_auxiliary_requirements( if rule["method"] != METHOD_NAME: continue - requirements.extend( - _collect_sources(rule["sources"]) - ) + requirements.extend(_collect_sources(rule["sources"])) scaling = rule.get("scaling") if scaling is not None: - requirements.extend( - _collect_sources( - scaling["target_sources"] - ) - ) + requirements.extend(_collect_sources(scaling["target_sources"])) if not requirements: - return pd.DataFrame( - columns=REQUIREMENT_COLUMNS - ) + return pd.DataFrame(columns=REQUIREMENT_COLUMNS) requirements_frame = ( pd.DataFrame(requirements) .drop_duplicates() - .sort_values( - ["country", "start", "end"] - ) + .sort_values(["country", "start", "end"]) .reset_index(drop=True) ) - return _merge_requirements( - requirements_frame - ) + return _merge_requirements(requirements_frame) -def _collect_sources( - sources: list[Mapping[str, Any]], -) -> list[dict[str, Any]]: +def _collect_sources(sources: list[Mapping[str, Any]]) -> list[dict[str, Any]]: """Extract acquisition-relevant fields from source definitions.""" return [ { @@ -72,57 +54,38 @@ def _collect_sources( for source in sources ] -def _merge_requirements( - requirements: pd.DataFrame, -) -> pd.DataFrame: + +def _merge_requirements(requirements: pd.DataFrame) -> pd.DataFrame: """Merge overlapping or adjacent country-period requirements.""" if requirements.empty: return requirements.copy() merged_rows: list[dict[str, Any]] = [] - for country, country_requirements in requirements.groupby( - "country", - sort=True, - ): - ordered = country_requirements.sort_values( - ["start", "end"] - ) + for country, country_requirements in requirements.groupby("country", sort=True): + ordered = country_requirements.sort_values(["start", "end"]) current_start = ordered.iloc[0]["start"] current_end = ordered.iloc[0]["end"] for row in ordered.iloc[1:].itertuples(index=False): if row.start <= current_end: - current_end = max( - current_end, - row.end, - ) + current_end = max(current_end, row.end) continue merged_rows.append( - { - "country": country, - "start": current_start, - "end": current_end, - } + {"country": country, "start": current_start, "end": current_end} ) current_start = row.start current_end = row.end merged_rows.append( - { - "country": country, - "start": current_start, - "end": current_end, - } + {"country": country, "start": current_start, "end": current_end} ) - return pd.DataFrame( - merged_rows, - columns=REQUIREMENT_COLUMNS, - ) + return pd.DataFrame(merged_rows, columns=REQUIREMENT_COLUMNS) + def get_basic_cleaning_context( rules: Sequence[Mapping[str, Any]], @@ -140,51 +103,30 @@ def get_basic_cleaning_context( rule_right = max_gap if method == "linear_interpolation": - offsets = [ - -pd.Timedelta(hours=1), - pd.Timedelta(hours=1), - ] + offsets = [-pd.Timedelta(hours=1), pd.Timedelta(hours=1)] elif method == "copy_period": - offsets = [ - pd.Timedelta(rule["source_offset"]), - ] + offsets = [pd.Timedelta(rule["source_offset"])] elif method == "average_periods": - offsets = [ - pd.Timedelta(offset) - for offset in rule["source_offsets"] - ] + offsets = [pd.Timedelta(offset) for offset in rule["source_offsets"]] else: - raise ValueError( - f"Unsupported basic gap-filling method: {method!r}" - ) + raise ValueError(f"Unsupported basic gap-filling method: {method!r}") previous_left = left previous_right = right for offset in offsets: - rule_left = min( - rule_left, - offset + previous_left, - ) - rule_right = max( - rule_right, - offset + previous_right, - ) + rule_left = min(rule_left, offset + previous_left) + rule_right = max(rule_right, offset + previous_right) - left = min( - previous_left, - rule_left, - ) - right = max( - previous_right, - rule_right, - ) + left = min(previous_left, rule_left) + right = max(previous_right, rule_right) return -left, right + def expand_auxiliary_requirements( requirements: pd.DataFrame, *, @@ -195,18 +137,12 @@ def expand_auxiliary_requirements( if requirements.empty or not enabled or not rules: return requirements.copy() - left_context, right_context = get_basic_cleaning_context( - rules - ) + left_context, right_context = get_basic_cleaning_context(rules) expanded = requirements.copy() - expanded["start"] = ( - expanded["start"] - left_context - ) - expanded["end"] = ( - expanded["end"] + right_context - ) + expanded["start"] = expanded["start"] - left_context + expanded["end"] = expanded["end"] + right_context return _merge_requirements(expanded) @@ -218,12 +154,8 @@ def build_auxiliary_acquisition_requirements( basic_cleaning_enabled: bool, ) -> pd.DataFrame: """Build expanded auxiliary-data requirements for acquisition.""" - exact_requirements = compile_auxiliary_requirements( - overrides - ) + exact_requirements = compile_auxiliary_requirements(overrides) return expand_auxiliary_requirements( - exact_requirements, - rules=basic_rules, - enabled=basic_cleaning_enabled, - ) \ No newline at end of file + exact_requirements, rules=basic_rules, enabled=basic_cleaning_enabled + ) diff --git a/workflow/scripts/cleaning/advanced/planning/source_requests.py b/workflow/scripts/cleaning/advanced/planning/source_requests.py index a2777b3..efc37bb 100644 --- a/workflow/scripts/cleaning/advanced/planning/source_requests.py +++ b/workflow/scripts/cleaning/advanced/planning/source_requests.py @@ -7,41 +7,25 @@ import pandas as pd -SOURCE_REQUEST_COLUMNS = [ - "source", - "country", - "start", - "end", -] +SOURCE_REQUEST_COLUMNS = ["source", "country", "start", "end"] -SUPPORTED_SOURCES = { - "entsoe_api", - "neso", - "opsd_api", -} +SUPPORTED_SOURCES = {"entsoe_api", "neso", "opsd_api"} def build_auxiliary_source_requests( - requirements: pd.DataFrame, - *, - source_names: Sequence[str], + requirements: pd.DataFrame, *, source_names: Sequence[str] ) -> pd.DataFrame: """Map auxiliary requirements onto applicable configured sources.""" _validate_source_names(source_names) if requirements.empty: - return pd.DataFrame( - columns=SOURCE_REQUEST_COLUMNS - ) + return pd.DataFrame(columns=SOURCE_REQUEST_COLUMNS) requests: list[dict[str, object]] = [] for source_name in source_names: for row in requirements.itertuples(index=False): - if not _source_supports_country( - source_name, - row.country, - ): + if not _source_supports_country(source_name, row.country): continue requests.append( @@ -53,34 +37,21 @@ def build_auxiliary_source_requests( } ) - return pd.DataFrame( - requests, - columns=SOURCE_REQUEST_COLUMNS, - ) + return pd.DataFrame(requests, columns=SOURCE_REQUEST_COLUMNS) -def _source_supports_country( - source_name: str, - country: str, -) -> bool: +def _source_supports_country(source_name: str, country: str) -> bool: """Return whether a source is structurally applicable to a country.""" if source_name == "neso": return country == "GBR" - if source_name in { - "entsoe_api", - "opsd_api", - }: + if source_name in {"entsoe_api", "opsd_api"}: return True - raise ValueError( - f"Unsupported auxiliary load source: {source_name!r}" - ) + raise ValueError(f"Unsupported auxiliary load source: {source_name!r}") -def _validate_source_names( - source_names: Sequence[str], -) -> None: +def _validate_source_names(source_names: Sequence[str]) -> None: """Validate configured sources used for auxiliary acquisition.""" unknown = [ source_name @@ -89,56 +60,29 @@ def _validate_source_names( ] if unknown: - raise ValueError( - "Unsupported auxiliary load sources: " - f"{unknown}" - ) + raise ValueError(f"Unsupported auxiliary load sources: {unknown}") if len(source_names) != len(set(source_names)): - raise ValueError( - "Auxiliary load source names must be unique." - ) + raise ValueError("Auxiliary load source names must be unique.") + -def build_auxiliary_source_batches( - requests: pd.DataFrame, -) -> list[dict[str, object]]: +def build_auxiliary_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: """Group compatible auxiliary source requests into batches.""" if requests.empty: return [] batches: list[dict[str, object]] = [] - grouped = requests.groupby( - [ - "source", - "start", - "end", - ], - sort=False, - ) + grouped = requests.groupby(["source", "start", "end"], sort=False) - for ( - source, - start, - end, - ), group in grouped: - countries = sorted( - group["country"] - .unique() - .tolist() - ) + for (source, start, end), group in grouped: + countries = sorted(group["country"].unique().tolist()) batches.append( { - "group_id": _build_group_id( - start=start, - end=end, - ), + "group_id": _build_group_id(start=start, end=end), "batch_id": _build_batch_id( - source=source, - start=start, - end=end, - countries=countries, + source=source, start=start, end=end, countries=countries ), "source": source, "start": start, @@ -151,20 +95,12 @@ def build_auxiliary_source_batches( def _build_batch_id( - *, - source: str, - start: pd.Timestamp, - end: pd.Timestamp, - countries: list[str], + *, source: str, start: pd.Timestamp, end: pd.Timestamp, countries: list[str] ) -> str: """Build a deterministic identifier for an auxiliary source batch.""" - countries_key = ",".join( - sorted(countries) - ) + countries_key = ",".join(sorted(countries)) - countries_hash = hashlib.sha1( - countries_key.encode("utf-8") - ).hexdigest()[:8] + countries_hash = hashlib.sha1(countries_key.encode("utf-8")).hexdigest()[:8] return ( f"{source}__" @@ -174,13 +110,6 @@ def _build_batch_id( ) -def _build_group_id( - *, - start: pd.Timestamp, - end: pd.Timestamp, -) -> str: +def _build_group_id(*, start: pd.Timestamp, end: pd.Timestamp) -> str: """Build a deterministic identifier for an auxiliary period group.""" - return ( - f"{start.strftime('%Y%m%dT%H%M')}__" - f"{end.strftime('%Y%m%dT%H%M')}" - ) + return f"{start.strftime('%Y%m%dT%H%M')}__{end.strftime('%Y%m%dT%H%M')}" diff --git a/workflow/scripts/cleaning/basic/apply.py b/workflow/scripts/cleaning/basic/apply.py index 3aad56e..faf62b1 100644 --- a/workflow/scripts/cleaning/basic/apply.py +++ b/workflow/scripts/cleaning/basic/apply.py @@ -42,7 +42,7 @@ def fill_basic_gaps( enabled: Whether basic gap filling should be applied. - Returns + Returns: ------- filled: Load after applying the configured rules. If gap filling is disabled, @@ -51,27 +51,19 @@ def fill_basic_gaps( Per-cell provenance containing the observed-source identifier, configured gap-filling rule name, or ``missing``. """ - _validate_settings( - enabled=enabled, - rules=rules - ) + _validate_settings(enabled=enabled, rules=rules) validate_load(load) - _validate_cleaning_method( - load=load, - cleaning_method=cleaning_method, - ) + _validate_cleaning_method(load=load, cleaning_method=cleaning_method) filled = load.copy() cleaning_method = cleaning_method.copy() if not enabled: - logger.info("Basic gap filling is disabled.") - cleaning_method = cleaning_method.fillna("missing") - return filled, cleaning_method + logger.info("Basic gap filling is disabled.") + cleaning_method = cleaning_method.fillna("missing") + return filled, cleaning_method - original_gap_duration = calculate_missing_run_durations( - load - ) + original_gap_duration = calculate_missing_run_durations(load) for rule in rules: method = _get_method(rule) @@ -97,69 +89,40 @@ def fill_basic_gaps( filled, max_gap=rule["max_gap"], source_offset=rule["source_offset"], - require_complete_source=rule.get( - "require_complete_source", - True, - ), + require_complete_source=rule.get("require_complete_source", True), original_gap_duration=original_gap_duration, ) else: - raise ValueError( - f"Unsupported gap-filling method: {method!r}" - ) + raise ValueError(f"Unsupported gap-filling method: {method!r}") - cleaning_method = cleaning_method.mask( - newly_filled, - rule_name, - ) + cleaning_method = cleaning_method.mask(newly_filled, rule_name) - _log_rule_results( - rule_name=rule_name, - method=method, - newly_filled=newly_filled, - ) + _log_rule_results(rule_name=rule_name, method=method, newly_filled=newly_filled) - cleaning_method = cleaning_method.fillna( - "missing" - ) + cleaning_method = cleaning_method.fillna("missing") - unresolved = int( - filled.isna().to_numpy().sum() - ) + unresolved = int(filled.isna().to_numpy().sum()) - logger.info( - "Gap filling completed with %s unresolved values.", - unresolved, - ) + logger.info("Gap filling completed with %s unresolved values.", unresolved) return filled, cleaning_method -def calculate_missing_run_durations( - load: pd.DataFrame, -) -> pd.DataFrame: +def calculate_missing_run_durations(load: pd.DataFrame) -> pd.DataFrame: """Return the original duration of each missing run. Observed values receive a duration of zero. """ timestep = infer_regular_timestep(load.index) - durations = pd.DataFrame( - pd.Timedelta(0), - index=load.index, - columns=load.columns, - ) + durations = pd.DataFrame(pd.Timedelta(0), index=load.index, columns=load.columns) for column in load.columns: missing = load[column].isna() group_ids = missing.ne(missing.shift()).cumsum() - run_lengths = ( - missing.groupby(group_ids) - .transform("sum") - .where(missing, 0) - ) + run_lengths = missing.groupby(group_ids).transform("sum").where(missing, 0) durations[column] = run_lengths * timestep @@ -170,50 +133,33 @@ def _get_method(rule: Mapping[str, Any]) -> str: try: method = rule["method"] except KeyError as error: - raise ValueError( - "Each gap-filling rule must define a 'method'." - ) from error + raise ValueError("Each gap-filling rule must define a 'method'.") from error if not isinstance(method, str): - raise TypeError( - "Gap-filling rule 'method' must be a string." - ) + raise TypeError("Gap-filling rule 'method' must be a string.") return method -def _get_rule_name( - rule: Mapping[str, Any], -) -> str: +def _get_rule_name(rule: Mapping[str, Any]) -> str: try: name = rule["name"] except KeyError as error: - raise ValueError( - "Each gap-filling rule must define a 'name'." - ) from error + raise ValueError("Each gap-filling rule must define a 'name'.") from error if not isinstance(name, str): - raise TypeError( - "Gap-filling rule 'name' must be a string." - ) + raise TypeError("Gap-filling rule 'name' must be a string.") if not name: - raise ValueError( - "Gap-filling rule 'name' must not be empty." - ) + raise ValueError("Gap-filling rule 'name' must not be empty.") return name def _log_rule_results( - *, - rule_name: str, - method: str, - newly_filled: pd.DataFrame, + *, rule_name: str, method: str, newly_filled: pd.DataFrame ) -> None: - total = int( - newly_filled.to_numpy().sum() - ) + total = int(newly_filled.to_numpy().sum()) logger.info( "Gap-filling rule '%s' using method '%s' filled %s values.", @@ -227,79 +173,48 @@ def _log_rule_results( if count: logger.info( - "%s: %s values filled using rule '%s'.", - country, - count, - rule_name, + "%s: %s values filled using rule '%s'.", country, count, rule_name ) -def _validate_settings( - *, - enabled: bool, - rules: Sequence[Mapping[str, Any]], -) -> None: +def _validate_settings(*, enabled: bool, rules: Sequence[Mapping[str, Any]]) -> None: """Validate basic gap-filling settings.""" if not isinstance(enabled, bool): - raise TypeError( - "Basic gap-filling 'enabled' must be a boolean." - ) + raise TypeError("Basic gap-filling 'enabled' must be a boolean.") - if not isinstance(rules, Sequence) or isinstance( - rules, - (str, bytes), - ): - raise TypeError( - "Basic gap-filling 'rules' must be an ordered sequence." - ) + if not isinstance(rules, Sequence) or isinstance(rules, (str, bytes)): + raise TypeError("Basic gap-filling 'rules' must be an ordered sequence.") def _validate_cleaning_method( - *, - load: pd.DataFrame, - cleaning_method: pd.DataFrame, + *, load: pd.DataFrame, cleaning_method: pd.DataFrame ) -> None: if not isinstance(cleaning_method, pd.DataFrame): - raise TypeError( - "Cleaning method must be a pandas DataFrame." - ) + raise TypeError("Cleaning method must be a pandas DataFrame.") if not cleaning_method.index.equals(load.index): raise ValueError( - "Cleaning-method provenance must use the same " - "index as the load data." + "Cleaning-method provenance must use the same index as the load data." ) if not cleaning_method.columns.equals(load.columns): raise ValueError( - "Cleaning-method provenance must use the same " - "columns as the load data." + "Cleaning-method provenance must use the same columns as the load data." ) - missing_observed_provenance = ( - load.notna() - & cleaning_method.isna() - ) + missing_observed_provenance = load.notna() & cleaning_method.isna() if missing_observed_provenance.any().any(): - count = int( - missing_observed_provenance.to_numpy().sum() - ) + count = int(missing_observed_provenance.to_numpy().sum()) raise ValueError( - "Cleaning-method provenance is missing for " - f"{count} observed load values." + f"Cleaning-method provenance is missing for {count} observed load values." ) - provenance_for_missing_values = ( - load.isna() - & cleaning_method.notna() - ) + provenance_for_missing_values = load.isna() & cleaning_method.notna() if provenance_for_missing_values.any().any(): - count = int( - provenance_for_missing_values.to_numpy().sum() - ) + count = int(provenance_for_missing_values.to_numpy().sum()) raise ValueError( "Cleaning-method provenance is already assigned " diff --git a/workflow/scripts/cleaning/basic/rules/average_periods.py b/workflow/scripts/cleaning/basic/rules/average_periods.py index bc32355..cc96d5f 100644 --- a/workflow/scripts/cleaning/basic/rules/average_periods.py +++ b/workflow/scripts/cleaning/basic/rules/average_periods.py @@ -6,7 +6,6 @@ import pandas as pd - METHOD_NAME = "average_periods" @@ -33,30 +32,19 @@ def apply_average_periods( """ max_gap = pd.Timedelta(max_gap) - offsets = tuple( - pd.Timedelta(offset) - for offset in source_offsets - ) + offsets = tuple(pd.Timedelta(offset) for offset in source_offsets) if max_gap <= pd.Timedelta(0): - raise ValueError( - "'max_gap' must be greater than zero." - ) + raise ValueError("'max_gap' must be greater than zero.") if len(offsets) < 2: - raise ValueError( - "'source_offsets' must contain at least two offsets." - ) + raise ValueError("'source_offsets' must contain at least two offsets.") if len(set(offsets)) != len(offsets): - raise ValueError( - "'source_offsets' must not contain duplicates." - ) + raise ValueError("'source_offsets' must not contain duplicates.") if pd.Timedelta(0) in offsets: - raise ValueError( - "'source_offsets' must not contain zero." - ) + raise ValueError("'source_offsets' must not contain zero.") eligible = ( load.isna() @@ -64,55 +52,33 @@ def apply_average_periods( & original_gap_duration.le(max_gap) ) - sources = [ - _values_at_offset( - load, - source_offset=offset, - ) - for offset in offsets - ] + sources = [_values_at_offset(load, source_offset=offset) for offset in offsets] - candidate = _mean_complete_sources( - sources=sources, - ) + candidate = _mean_complete_sources(sources=sources) eligible &= candidate.notna() - filled = load.mask( - eligible, - candidate, - ) + filled = load.mask(eligible, candidate) - newly_filled = ( - load.isna() - & filled.notna() - ) + newly_filled = load.isna() & filled.notna() return filled, newly_filled + def _values_at_offset( - load: pd.DataFrame, - *, - source_offset: pd.Timedelta, + load: pd.DataFrame, *, source_offset: pd.Timedelta ) -> pd.DataFrame: """Align values at timestamp + offset to target timestamps.""" - source_timestamps = ( - load.index + source_offset - ) + source_timestamps = load.index + source_offset - source = load.reindex( - source_timestamps - ) + source = load.reindex(source_timestamps) source.index = load.index return source -def _mean_complete_sources( - *, - sources: Sequence[pd.DataFrame], -) -> pd.DataFrame: +def _mean_complete_sources(*, sources: Sequence[pd.DataFrame]) -> pd.DataFrame: """Calculate the mean only where every source is available.""" source_sum = sources[0].copy() complete = sources[0].notna() @@ -123,4 +89,4 @@ def _mean_complete_sources( candidate = source_sum / len(sources) - return candidate.where(complete) \ No newline at end of file + return candidate.where(complete) diff --git a/workflow/scripts/cleaning/basic/rules/copy_period.py b/workflow/scripts/cleaning/basic/rules/copy_period.py index db9baac..cf78d56 100644 --- a/workflow/scripts/cleaning/basic/rules/copy_period.py +++ b/workflow/scripts/cleaning/basic/rules/copy_period.py @@ -41,15 +41,11 @@ def apply_copy_period( & original_gap_duration.le(max_gap) ) - source = _values_at_offset( - load, - source_offset=source_offset, - ) + source = _values_at_offset(load, source_offset=source_offset) if require_complete_source: eligible = _require_complete_source_for_each_gap( - eligible=eligible, - source=source, + eligible=eligible, source=source ) else: eligible &= source.notna() @@ -61,9 +57,7 @@ def apply_copy_period( def _values_at_offset( - load: pd.DataFrame, - *, - source_offset: pd.Timedelta, + load: pd.DataFrame, *, source_offset: pd.Timedelta ) -> pd.DataFrame: """Align values at ``timestamp + source_offset`` to each target timestamp.""" source_timestamps = load.index + source_offset @@ -75,22 +69,14 @@ def _values_at_offset( def _require_complete_source_for_each_gap( - *, - eligible: pd.DataFrame, - source: pd.DataFrame, + *, eligible: pd.DataFrame, source: pd.DataFrame ) -> pd.DataFrame: """Keep a gap eligible only when every source value for that gap exists.""" - result = pd.DataFrame( - False, - index=eligible.index, - columns=eligible.columns, - ) + result = pd.DataFrame(False, index=eligible.index, columns=eligible.columns) for column in eligible.columns: eligible_column = eligible[column] - gap_ids = eligible_column.ne( - eligible_column.shift(fill_value=False) - ).cumsum() + gap_ids = eligible_column.ne(eligible_column.shift(fill_value=False)).cumsum() for _, gap_mask in eligible_column.groupby(gap_ids): gap_index = gap_mask.index[gap_mask] diff --git a/workflow/scripts/cleaning/basic/rules/linear_interpolation.py b/workflow/scripts/cleaning/basic/rules/linear_interpolation.py index c3a99c8..e8267df 100644 --- a/workflow/scripts/cleaning/basic/rules/linear_interpolation.py +++ b/workflow/scripts/cleaning/basic/rules/linear_interpolation.py @@ -33,10 +33,7 @@ def apply_linear_interpolation( & original_gap_duration.le(max_gap) ) - interpolated = load.interpolate( - method="time", - limit_area="inside", - ) + interpolated = load.interpolate(method="time", limit_area="inside") filled = load.mask(eligible, interpolated) newly_filled = load.isna() & filled.notna() diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py index 8534c36..6daf294 100644 --- a/workflow/scripts/cleaning/combine_sources.py +++ b/workflow/scripts/cleaning/combine_sources.py @@ -8,32 +8,20 @@ def combine_sources( - sources: Mapping[str, pd.DataFrame], - *, - priority: Sequence[str], + sources: Mapping[str, pd.DataFrame], *, priority: Sequence[str] ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: """Combine sources and record source and cleaning-method provenance.""" if not priority: - raise ValueError( - "At least one demand source must be configured." - ) + raise ValueError("At least one demand source must be configured.") - missing_sources = [ - source - for source in priority - if source not in sources - ] + missing_sources = [source for source in priority if source not in sources] if missing_sources: raise ValueError( - "Configured demand sources were not supplied: " - f"{missing_sources}" + f"Configured demand sources were not supplied: {missing_sources}" ) - selected = { - source: sources[source] - for source in priority - } + selected = {source: sources[source] for source in priority} _validate_source_alignment(selected) @@ -41,57 +29,38 @@ def combine_sources( combined = selected[first_source].copy() data_source = pd.DataFrame( - pd.NA, - index=combined.index, - columns=combined.columns, - dtype="string", + pd.NA, index=combined.index, columns=combined.columns, dtype="string" ) cleaning_method = pd.DataFrame( - pd.NA, - index=combined.index, - columns=combined.columns, - dtype="string", + pd.NA, index=combined.index, columns=combined.columns, dtype="string" ) first_source_values = combined.notna() - data_source = data_source.mask( - first_source_values, - first_source, - ) + data_source = data_source.mask(first_source_values, first_source) cleaning_method = cleaning_method.mask( - first_source_values, - f"observed_{first_source}", + first_source_values, f"observed_{first_source}" ) for source_name in priority[1:]: candidate = selected[source_name] - newly_supplied = ( - combined.isna() - & candidate.notna() - ) + newly_supplied = combined.isna() & candidate.notna() combined = combined.combine_first(candidate) - data_source = data_source.mask( - newly_supplied, - source_name, - ) + data_source = data_source.mask(newly_supplied, source_name) cleaning_method = cleaning_method.mask( - newly_supplied, - f"observed_{source_name}", + newly_supplied, f"observed_{source_name}" ) return combined, data_source, cleaning_method -def _validate_source_alignment( - sources: Mapping[str, pd.DataFrame], -) -> None: +def _validate_source_alignment(sources: Mapping[str, pd.DataFrame]) -> None: """Require all prepared sources to use the same target grid.""" source_items = list(sources.items()) @@ -112,14 +81,8 @@ def _validate_source_alignment( def combine_auxiliary_sources( - loads: Mapping[str, pd.DataFrame], - *, - priority: Sequence[str], -) -> tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, -]: + loads: Mapping[str, pd.DataFrame], *, priority: Sequence[str] +) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: """Combine available auxiliary sources using configured source priority.""" if not loads: empty = pd.DataFrame() @@ -133,26 +96,10 @@ def combine_auxiliary_sources( f"source priority: {sorted(unexpected_sources)}." ) - available_priority = [ - source - for source in priority - if source in loads - ] - - columns = sorted( - { - column - for load in loads.values() - for column in load.columns - } - ) + available_priority = [source for source in priority if source in loads] - aligned = { - source: load.reindex(columns=columns) - for source, load in loads.items() - } + columns = sorted({column for load in loads.values() for column in load.columns}) - return combine_sources( - aligned, - priority=available_priority, - ) + aligned = {source: load.reindex(columns=columns) for source, load in loads.items()} + + return combine_sources(aligned, priority=available_priority) diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py index 63ee452..2523d07 100644 --- a/workflow/scripts/cleaning/pipeline.py +++ b/workflow/scripts/cleaning/pipeline.py @@ -1,4 +1,5 @@ """The pipeline coordinates the gap-filling rules and ensures each receives the necessary parameters.""" + from __future__ import annotations import logging @@ -22,21 +23,16 @@ def clean_demand( source_priority: Sequence[str], gap_filling_config: Mapping[str, Any], ) -> tuple[ - pd.DataFrame, #data - pd.DataFrame, #sources - pd.DataFrame, #method - pd.DataFrame, #rank - pd.DataFrame, #gap_report - pd.DataFrame, #auxiliary_fill_plan + pd.DataFrame, # data + pd.DataFrame, # sources + pd.DataFrame, # method + pd.DataFrame, # rank + pd.DataFrame, # gap_report + pd.DataFrame, # auxiliary_fill_plan ]: """Combine observed sources and fill remaining gaps.""" - ( - combined, - data_source, - cleaning_method, - ) = combine_sources( - sources, - priority=source_priority, + (combined, data_source, cleaning_method) = combine_sources( + sources, priority=source_priority ) basic_rules = gap_filling_config["basic"]["rules"] @@ -49,18 +45,15 @@ def clean_demand( ) cleaning_method_ranks = build_cleaning_method_ranks( - source_priority=source_priority, - rules=basic_rules, + source_priority=source_priority, rules=basic_rules ) cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, - ranks=cleaning_method_ranks, + cleaning_method=cleaning_method, ranks=cleaning_method_ranks ) gap_report = build_gap_report( - cleaned, - enabled=gap_filling_config["mode"] == "advanced", + cleaned, enabled=gap_filling_config["mode"] == "advanced" ) target_countries = list(cleaned.columns) @@ -78,15 +71,13 @@ def clean_demand( ) logger.info( - "Advanced gap diagnosis found %s unresolved gaps " - "covering %s values.", + "Advanced gap diagnosis found %s unresolved gaps covering %s values.", len(gap_report), int(gap_report["gap_hours"].sum()), ) logger.info( - "Advanced auxiliary-fill plan contains %s active " - "instructions.", + "Advanced auxiliary-fill plan contains %s active instructions.", len(auxiliary_fill_plan), ) else: @@ -104,4 +95,4 @@ def clean_demand( cleaning_method_rank, gap_report, auxiliary_fill_plan, - ) \ No newline at end of file + ) diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index 381801b..c4e835c 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -29,9 +29,7 @@ def main( """Create the electricity-demand cleaning diagnostic.""" demand = pd.read_parquet(demand_path) cleaning_method = pd.read_parquet(cleaning_method_path) - cleaning_method_rank = pd.read_parquet( - cleaning_method_rank_path - ) + cleaning_method_rank = pd.read_parquet(cleaning_method_rank_path) _validate_alignment( demand=demand, @@ -40,8 +38,7 @@ def main( ) metadata = _build_cleaning_method_metadata( - source_names=source_names, - gap_filling_config=gap_filling_config, + source_names=source_names, gap_filling_config=gap_filling_config ) _validate_provenance_metadata( @@ -52,46 +49,29 @@ def main( rank_colours = _build_rank_colours(metadata) - background, background_cmap = ( - _encode_rank_background( - cleaning_method_rank=cleaning_method_rank, - metadata=metadata, - rank_colours=rank_colours, - ) + background, background_cmap = _encode_rank_background( + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + rank_colours=rank_colours, ) logger.info( - "Loaded %s timestamps for %s countries.", - len(demand), - len(demand.columns), + "Loaded %s timestamps for %s countries.", len(demand), len(demand.columns) ) - logger.info( - "Cleaning-method ranks:\n%s", - metadata.to_string(index=False), - ) + logger.info("Cleaning-method ranks:\n%s", metadata.to_string(index=False)) figure, axis = _plot_cleaning_background( - demand=demand, - background=background, - background_cmap=background_cmap, + demand=demand, background=background, background_cmap=background_cmap ) - mean_load_gw = _add_normalised_demand_traces( - axis=axis, - demand=demand, - ) + mean_load_gw = _add_normalised_demand_traces(axis=axis, demand=demand) _add_mean_load_labels( - axis=axis, - mean_load_gw=mean_load_gw, - countries=demand.columns, + axis=axis, mean_load_gw=mean_load_gw, countries=demand.columns ) - legend_handles = _build_legend_handles( - metadata, - rank_colours, - ) + legend_handles = _build_legend_handles(metadata, rank_colours) figure.legend( handles=legend_handles, @@ -101,37 +81,22 @@ def main( ) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - figure.savefig( - output_path, - bbox_inches="tight", - ) + figure.savefig(output_path, bbox_inches="tight") plt.close(figure) - logger.info( - "Saved cleaning timeline to %s.", - output_path, - ) + logger.info("Saved cleaning timeline to %s.", output_path) def _build_legend_handles( - metadata: pd.DataFrame, - rank_colours: dict[ - int, - tuple[float, float, float, float], - ], + metadata: pd.DataFrame, rank_colours: dict[int, tuple[float, float, float, float]] ) -> list[Patch]: """Create handles for every configured rank.""" handles: list[Patch] = [] - ordered = metadata.sort_values( - "cleaning_method_rank" - ) + ordered = metadata.sort_values("cleaning_method_rank") for row in ordered.itertuples(index=False): rank = int(row.cleaning_method_rank) @@ -160,36 +125,22 @@ def _validate_alignment( "cleaning_method_rank": cleaning_method_rank, }.items(): if not frame.index.equals(demand.index): - raise ValueError( - f"{name} does not use the same time index " - "as demand." - ) + raise ValueError(f"{name} does not use the same time index as demand.") if not frame.columns.equals(demand.columns): - raise ValueError( - f"{name} does not use the same country columns " - "as demand." - ) + raise ValueError(f"{name} does not use the same country columns as demand.") if not isinstance(demand.index, pd.DatetimeIndex): - raise TypeError( - "Demand must use a pandas DatetimeIndex." - ) + raise TypeError("Demand must use a pandas DatetimeIndex.") if demand.index.has_duplicates: - raise ValueError( - "Demand timestamps must not contain duplicates." - ) + raise ValueError("Demand timestamps must not contain duplicates.") if not demand.index.is_monotonic_increasing: - raise ValueError( - "Demand timestamps must be sorted." - ) + raise ValueError("Demand timestamps must be sorted.") if demand.columns.has_duplicates: - raise ValueError( - "Demand countries must not contain duplicates." - ) + raise ValueError("Demand countries must not contain duplicates.") def _validate_provenance_metadata( @@ -199,50 +150,25 @@ def _validate_provenance_metadata( metadata: pd.DataFrame, ) -> None: """Validate observed provenance against configured metadata.""" - methods = cleaning_method.stack( - future_stack=True - ).rename("cleaning_method") - - ranks = cleaning_method_rank.stack( - future_stack=True - ).rename("cleaning_method_rank") - - present = ( - pd.concat( - [methods, ranks], - axis=1, - ) - .dropna() - .drop_duplicates() - ) + methods = cleaning_method.stack(future_stack=True).rename("cleaning_method") - present["cleaning_method_rank"] = present[ - "cleaning_method_rank" - ].astype(int) + ranks = cleaning_method_rank.stack(future_stack=True).rename("cleaning_method_rank") - configured = metadata[ - [ - "cleaning_method", - "cleaning_method_rank", - ] - ] + present = pd.concat([methods, ranks], axis=1).dropna().drop_duplicates() + + present["cleaning_method_rank"] = present["cleaning_method_rank"].astype(int) + + configured = metadata[["cleaning_method", "cleaning_method_rank"]] checked = present.merge( configured, - on=[ - "cleaning_method", - "cleaning_method_rank", - ], + on=["cleaning_method", "cleaning_method_rank"], how="left", indicator=True, ) unknown = checked.loc[ - checked["_merge"] == "left_only", - [ - "cleaning_method", - "cleaning_method_rank", - ], + checked["_merge"] == "left_only", ["cleaning_method", "cleaning_method_rank"] ] if not unknown.empty: @@ -257,71 +183,35 @@ def _build_rank_colours( metadata: pd.DataFrame, ) -> dict[int, tuple[float, float, float, float]]: """Assign colours by provenance category.""" - colours: dict[ - int, - tuple[float, float, float, float], - ] = {} + colours: dict[int, tuple[float, float, float, float]] = {} - ordered = metadata.sort_values( - "cleaning_method_rank" - ) + ordered = metadata.sort_values("cleaning_method_rank") - observed = ordered.loc[ - ordered["category"] == "observed" - ] + observed = ordered.loc[ordered["category"] == "observed"] - imputed = ordered.loc[ - ordered["category"] == "imputed" - ] + imputed = ordered.loc[ordered["category"] == "imputed"] - missing = ordered.loc[ - ordered["category"] == "missing" - ] + missing = ordered.loc[ordered["category"] == "missing"] if observed.empty: - raise ValueError( - "At least one observed source must be configured." - ) + raise ValueError("At least one observed source must be configured.") # Primary source is white. Subsequent observed sources # become gradually darker, but remain very light so that # the black demand trace stays clearly visible. - observed_shades = np.linspace( - 1.0, - 0.60, - len(observed), - ) + observed_shades = np.linspace(1.0, 0.60, len(observed)) - for (_, row), shade in zip( - observed.iterrows(), - observed_shades, - strict=True, - ): + for (_, row), shade in zip(observed.iterrows(), observed_shades, strict=True): rank = int(row["cleaning_method_rank"]) - colours[rank] = ( - float(shade), - float(shade), - float(shade), - 1.0, - ) + colours[rank] = (float(shade), float(shade), float(shade), 1.0) if not imputed.empty: - colourtheme = Colormap( - "bids:viridis" - ).to_mpl() - - positions = np.linspace( - 0.05, - 0.95, - len(imputed), - ) + colourtheme = Colormap("bids:viridis").to_mpl() + + positions = np.linspace(0.05, 0.95, len(imputed)) - for (_, row), position in zip( - imputed.iterrows(), - positions, - strict=True, - ): + for (_, row), position in zip(imputed.iterrows(), positions, strict=True): rank = int(row["cleaning_method_rank"]) colours[rank] = colourtheme(position) @@ -329,17 +219,12 @@ def _build_rank_colours( rank = int(row["cleaning_method_rank"]) colours[rank] = to_rgba("#ff0000") - expected_ranks = set( - metadata["cleaning_method_rank"].astype(int) - ) + expected_ranks = set(metadata["cleaning_method_rank"].astype(int)) missing_colours = expected_ranks - set(colours) if missing_colours: - raise ValueError( - "No colour was assigned to ranks: " - f"{sorted(missing_colours)}" - ) + raise ValueError(f"No colour was assigned to ranks: {sorted(missing_colours)}") return colours @@ -348,40 +233,21 @@ def _encode_rank_background( *, cleaning_method_rank: pd.DataFrame, metadata: pd.DataFrame, - rank_colours: dict[ - int, - tuple[float, float, float, float], - ], + rank_colours: dict[int, tuple[float, float, float, float]], ) -> tuple[np.ndarray, ListedColormap]: """Encode ranks as contiguous plotting codes.""" - rank_order = ( - metadata["cleaning_method_rank"] - .astype(int) - .tolist() - ) + rank_order = metadata["cleaning_method_rank"].astype(int).tolist() - rank_to_code = { - rank: code - for code, rank in enumerate(rank_order) - } + rank_to_code = {rank: code for code, rank in enumerate(rank_order)} - encoded = cleaning_method_rank.apply( - lambda column: column.map(rank_to_code) - ) + encoded = cleaning_method_rank.apply(lambda column: column.map(rank_to_code)) if encoded.isna().any().any(): present_ranks = set( - cleaning_method_rank.stack( - future_stack=True - ) - .dropna() - .astype(int) - .unique() + cleaning_method_rank.stack(future_stack=True).dropna().astype(int).unique() ) - unknown_ranks = sorted( - present_ranks - set(rank_to_code) - ) + unknown_ranks = sorted(present_ranks - set(rank_to_code)) raise ValueError( "Cleaning-method rank matrix contains ranks " @@ -389,65 +255,37 @@ def _encode_rank_background( f"{unknown_ranks}" ) - colour_list = [ - rank_colours[rank] - for rank in rank_order - ] + colour_list = [rank_colours[rank] for rank in rank_order] # Input frames are time × country, whereas imshow expects # country × time for this figure orientation. - background = encoded.to_numpy( - dtype=int - ).T + background = encoded.to_numpy(dtype=int).T - return ( - background, - ListedColormap(colour_list), - ) + return (background, ListedColormap(colour_list)) def _plot_cleaning_background( - *, - demand: pd.DataFrame, - background: np.ndarray, - background_cmap: ListedColormap, + *, demand: pd.DataFrame, background: np.ndarray, background_cmap: ListedColormap ) -> tuple[plt.Figure, plt.Axes]: """Plot cleaning-method ranks over time by country.""" country_count = len(demand.columns) if len(demand.index) < 2: raise ValueError( - "At least two timestamps are required to plot " - "the cleaning timeline." + "At least two timestamps are required to plot the cleaning timeline." ) - time_step = ( - demand.index.to_series() - .diff() - .dropna() - .median() - ) + time_step = demand.index.to_series().diff().dropna().median() - if ( - pd.isna(time_step) - or time_step <= pd.Timedelta(0) - ): - raise ValueError( - "Could not determine a valid temporal resolution." - ) + if pd.isna(time_step) or time_step <= pd.Timedelta(0): + raise ValueError("Could not determine a valid temporal resolution.") start = demand.index[0] end = demand.index[-1] + time_step - figure_height = max( - 6.0, - country_count * 0.3, - ) + figure_height = max(6.0, country_count * 0.3) - figure, axis = plt.subplots( - figsize=(16, figure_height), - constrained_layout=True, - ) + figure, axis = plt.subplots(figsize=(16, figure_height), constrained_layout=True) axis.imshow( background, @@ -470,49 +308,25 @@ def _plot_cleaning_background( axis.set_yticklabels(demand.columns) axis.set_xlim(start, end) - axis.set_ylim( - country_count - 0.5, - -0.5, - ) + axis.set_ylim(country_count - 0.5, -0.5) # Light boundaries make individual country strips clear # without obscuring the provenance colours. - axis.set_yticks( - np.arange(-0.5, country_count, 1), - minor=True, - ) + axis.set_yticks(np.arange(-0.5, country_count, 1), minor=True) - axis.grid( - axis="y", - which="minor", - linewidth=0.4, - alpha=0.35, - ) + axis.grid(axis="y", which="minor", linewidth=0.4, alpha=0.35) - axis.tick_params( - axis="y", - which="minor", - left=False, - ) + axis.tick_params(axis="y", which="minor", left=False) axis.set_xlabel("Time") axis.set_ylabel("Country") - date_locator = mdates.AutoDateLocator( - minticks=4, - maxticks=12, - ) + date_locator = mdates.AutoDateLocator(minticks=4, maxticks=12) axis.xaxis.set_major_locator(date_locator) - axis.xaxis.set_major_formatter( - mdates.ConciseDateFormatter( - date_locator - ) - ) + axis.xaxis.set_major_formatter(mdates.ConciseDateFormatter(date_locator)) - axis.set_title( - "Electricity demand and cleaning provenance" - ) + axis.set_title("Electricity demand and cleaning provenance") return figure, axis @@ -527,9 +341,7 @@ def _add_normalised_demand_traces( """Overlay mean-normalised hourly demand traces.""" mean_load_gw: dict[str, float] = {} - for row_index, country in enumerate( - demand.columns - ): + for row_index, country in enumerate(demand.columns): series = demand[country].astype(float) mean_load = series.mean(skipna=True) @@ -540,56 +352,32 @@ def _add_normalised_demand_traces( relative = (series / mean_load) - 1 - scale = relative.abs().quantile( - quantile - ) + scale = relative.abs().quantile(quantile) if pd.isna(scale) or scale == 0: - plotted_y = pd.Series( - row_index, - index=series.index, - dtype=float, - ) + plotted_y = pd.Series(row_index, index=series.index, dtype=float) else: - scaled = relative.clip( - lower=-scale, - upper=scale, - ) / scale + scaled = relative.clip(lower=-scale, upper=scale) / scale # The y-axis is inverted, so subtracting makes # above-average demand appear visually upward. - plotted_y = ( - row_index - - scaled * half_height - ) + plotted_y = row_index - scaled * half_height axis.plot( - series.index, - plotted_y, - color="black", - linewidth=0.6, - alpha=0.9, - zorder=3, + series.index, plotted_y, color="black", linewidth=0.6, alpha=0.9, zorder=3 ) return mean_load_gw def _add_mean_load_labels( - *, - axis: plt.Axes, - mean_load_gw: dict[str, float], - countries: pd.Index, + *, axis: plt.Axes, mean_load_gw: dict[str, float], countries: pd.Index ) -> None: """Annotate country rows with mean load in GW.""" for row_index, country in enumerate(countries): mean_value = mean_load_gw[country] - label = ( - "—" - if pd.isna(mean_value) - else f"{mean_value:.1f}" - ) + label = "—" if pd.isna(mean_value) else f"{mean_value:.1f}" axis.text( 1.01, @@ -613,17 +401,13 @@ def _add_mean_load_labels( ) -def _format_method_label( - method: str, -) -> str: +def _format_method_label(method: str) -> str: """Convert a method identifier into a legend label.""" if method == "missing": return "Missing" if method.startswith("observed_"): - source = method.removeprefix( - "observed_" - ) + source = method.removeprefix("observed_") source_labels = { "entsoe_api": "Observed: ENTSO-E API", @@ -631,22 +415,14 @@ def _format_method_label( } return source_labels.get( - source, - ( - "Observed: " - + source.replace("_", " ").upper() - ), + source, ("Observed: " + source.replace("_", " ").upper()) ) - return method.replace( - "_", - " ", - ).capitalize() + return method.replace("_", " ").capitalize() + def _build_cleaning_method_metadata( - *, - source_names: list[str], - gap_filling_config: dict[str, Any], + *, source_names: list[str], gap_filling_config: dict[str, Any] ) -> pd.DataFrame: """Build complete method metadata in configured rank order.""" rows: list[dict[str, Any]] = [] @@ -655,22 +431,17 @@ def _build_cleaning_method_metadata( for source_name in source_names: rows.append( { - "cleaning_method": ( - f"observed_{source_name}" - ), + "cleaning_method": (f"observed_{source_name}"), "cleaning_method_rank": rank, "label": ( - f"Rank {rank}: Observed " - f"({_format_source_name(source_name)})" + f"Rank {rank}: Observed ({_format_source_name(source_name)})" ), "category": "observed", } ) rank += 1 - rules = build_final_cleaning_rules( - gap_filling_config - ) + rules = build_final_cleaning_rules(gap_filling_config) for rule in rules: rule_name = rule["name"] @@ -679,10 +450,7 @@ def _build_cleaning_method_metadata( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": ( - f"Rank {rank}: " - f"{_format_rule_name(rule_name)}" - ), + "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), "category": "imputed", } ) @@ -701,11 +469,7 @@ def _build_cleaning_method_metadata( def _format_source_name(source_name: str) -> str: - mapping = { - "entsoe_api": "ENTSO-E", - "neso": "NESO", - "opsd_api": "OPSD", - } + mapping = {"entsoe_api": "ENTSO-E", "neso": "NESO", "opsd_api": "OPSD"} return mapping.get(source_name, source_name) diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py index 5619f65..aff4108 100644 --- a/workflow/scripts/cleaning/provenance.py +++ b/workflow/scripts/cleaning/provenance.py @@ -8,37 +8,25 @@ def validate_rule_names( - *, - rules: Sequence[Mapping[str, Any]], - source_priority: Sequence[str], + *, rules: Sequence[Mapping[str, Any]], source_priority: Sequence[str] ) -> None: """Validate rule-name uniqueness and reserved-name collisions.""" names = [str(rule["name"]) for rule in rules] counts = Counter(names) - duplicates = sorted( - name - for name, count in counts.items() - if count > 1 - ) + duplicates = sorted(name for name, count in counts.items() if count > 1) if duplicates: raise ValueError( - "Gap-filling rule names must be unique. " - f"Duplicate names: {duplicates}" + f"Gap-filling rule names must be unique. Duplicate names: {duplicates}" ) reserved_names = { "missing", - *( - f"observed_{source_name}" - for source_name in source_priority - ), + *(f"observed_{source_name}" for source_name in source_priority), } - collisions = sorted( - set(names) & reserved_names - ) + collisions = sorted(set(names) & reserved_names) if collisions: raise ValueError( @@ -48,15 +36,10 @@ def validate_rule_names( def build_cleaning_method_ranks( - *, - source_priority: Sequence[str], - rules: Sequence[Mapping[str, Any]], + *, source_priority: Sequence[str], rules: Sequence[Mapping[str, Any]] ) -> dict[str, int]: """Build cleaning-method ranks from configured order.""" - validate_rule_names( - rules=rules, - source_priority=source_priority, - ) + validate_rule_names(rules=rules, source_priority=source_priority) ranks: dict[str, int] = {} @@ -66,42 +49,25 @@ def build_cleaning_method_ranks( first_gap_filling_rank = len(source_priority) for rule_position, rule in enumerate(rules): - ranks[str(rule["name"])] = ( - first_gap_filling_rank - + rule_position - ) + ranks[str(rule["name"])] = first_gap_filling_rank + rule_position - ranks["missing"] = ( - len(source_priority) - + len(rules) - ) + ranks["missing"] = len(source_priority) + len(rules) return ranks def derive_cleaning_method_rank( - *, - cleaning_method: pd.DataFrame, - ranks: Mapping[str, int], + *, cleaning_method: pd.DataFrame, ranks: Mapping[str, int] ) -> pd.DataFrame: """Translate cleaning-method names to integer ranks.""" - present_methods = set( - cleaning_method.stack().astype(str).unique() - ) + present_methods = set(cleaning_method.stack().astype(str).unique()) - unknown_methods = sorted( - present_methods - set(ranks) - ) + unknown_methods = sorted(present_methods - set(ranks)) if unknown_methods: - raise ValueError( - "No cleaning-method rank is defined for: " - f"{unknown_methods}" - ) + raise ValueError(f"No cleaning-method rank is defined for: {unknown_methods}") - cleaning_method_rank = cleaning_method.replace( - ranks - ) + cleaning_method_rank = cleaning_method.replace(ranks) return cleaning_method_rank.astype("int16") @@ -110,21 +76,14 @@ def build_final_cleaning_rules( gap_filling_config: Mapping[str, Any], ) -> list[dict[str, Any]]: """Return configured cleaning rules in final provenance order.""" - rules = [ - dict(rule) - for rule in gap_filling_config["basic"]["rules"] - ] + rules = [dict(rule) for rule in gap_filling_config["basic"]["rules"]] if gap_filling_config["mode"] == "advanced": rules.extend( - { - "name": rule_name, - **override, - } + {"name": rule_name, **override} for rule_name, override in ( gap_filling_config["advanced"]["overrides"].items() ) ) return rules - diff --git a/workflow/scripts/cleaning/sources/neso.py b/workflow/scripts/cleaning/sources/neso.py index da9dd88..1293d17 100644 --- a/workflow/scripts/cleaning/sources/neso.py +++ b/workflow/scripts/cleaning/sources/neso.py @@ -3,42 +3,30 @@ import pandas as pd -def add_utc_timestamps( - data: pd.DataFrame, -) -> pd.DataFrame: +def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: """Convert NESO settlement dates and periods to UTC timestamps.""" - required_columns = { - "SETTLEMENT_DATE", - "SETTLEMENT_PERIOD", - "ND", - } + required_columns = {"SETTLEMENT_DATE", "SETTLEMENT_PERIOD", "ND"} missing_columns = required_columns - set(data.columns) if missing_columns: raise ValueError( - "NESO data are missing required columns: " - f"{sorted(missing_columns)}" + f"NESO data are missing required columns: {sorted(missing_columns)}" ) prepared = data.copy() prepared["SETTLEMENT_DATE"] = pd.to_datetime( - prepared["SETTLEMENT_DATE"], - errors="raise", + prepared["SETTLEMENT_DATE"], errors="raise" ).dt.normalize() prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( - prepared["SETTLEMENT_PERIOD"], - errors="raise", + prepared["SETTLEMENT_PERIOD"], errors="raise" ).astype(int) timestamp_parts: list[pd.Series] = [] - for settlement_date, day in prepared.groupby( - "SETTLEMENT_DATE", - sort=True, - ): + for settlement_date, day in prepared.groupby("SETTLEMENT_DATE", sort=True): day = day.sort_values("SETTLEMENT_PERIOD").copy() expected_periods = list(range(1, len(day) + 1)) @@ -50,17 +38,11 @@ def add_utc_timestamps( f"{settlement_date.date()}. Expected 1-{len(day)}." ) - local_start = pd.Timestamp( - settlement_date, - tz="Europe/London", - ) + local_start = pd.Timestamp(settlement_date, tz="Europe/London") local_end = local_start + pd.DateOffset(days=1) expected_index = pd.date_range( - start=local_start, - end=local_end, - freq="30min", - inclusive="left", + start=local_start, end=local_end, freq="30min", inclusive="left" ) if len(day) != len(expected_index): @@ -73,19 +55,11 @@ def add_utc_timestamps( ) timestamp_parts.append( - pd.Series( - expected_index, - index=day.index, - name="timestamp", - ) + pd.Series(expected_index, index=day.index, name="timestamp") ) - prepared["timestamp"] = pd.concat( - timestamp_parts - ).sort_index() + prepared["timestamp"] = pd.concat(timestamp_parts).sort_index() - prepared["timestamp"] = ( - prepared["timestamp"].dt.tz_convert("UTC") - ) + prepared["timestamp"] = prepared["timestamp"].dt.tz_convert("UTC") - return prepared.sort_values("timestamp") \ No newline at end of file + return prepared.sort_values("timestamp") diff --git a/workflow/scripts/cleaning/validation.py b/workflow/scripts/cleaning/validation.py index 76ec32f..7fb9b6b 100644 --- a/workflow/scripts/cleaning/validation.py +++ b/workflow/scripts/cleaning/validation.py @@ -19,45 +19,31 @@ def validate_load(load: pd.DataFrame) -> None: f"Found timestep {timestep}." ) - if not all( - pd.api.types.is_numeric_dtype(dtype) - for dtype in load.dtypes - ): + if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in load.dtypes): raise TypeError("All load columns must be numeric.") -def infer_regular_timestep( - index: pd.Index, -) -> pd.Timedelta: +def infer_regular_timestep(index: pd.Index) -> pd.Timedelta: """Infer and validate the regular timestep of a datetime index.""" if not isinstance(index, pd.DatetimeIndex): - raise TypeError( - "Load data must use a pandas DatetimeIndex." - ) + raise TypeError("Load data must use a pandas DatetimeIndex.") if not index.is_monotonic_increasing: - raise ValueError( - "Load timestamps must be sorted in increasing order." - ) + raise ValueError("Load timestamps must be sorted in increasing order.") if index.has_duplicates: - raise ValueError( - "Load timestamps must not contain duplicates." - ) + raise ValueError("Load timestamps must not contain duplicates.") differences = index.to_series().diff().dropna() if differences.empty: - raise ValueError( - "At least two timestamps are required for gap filling." - ) + raise ValueError("At least two timestamps are required for gap filling.") timestep = differences.iloc[0] if not differences.eq(timestep).all(): raise ValueError( - "Load data must have a complete, regular time index " - "before gap filling." + "Load data must have a complete, regular time index before gap filling." ) - return timestep \ No newline at end of file + return timestep diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py index 72bfeb1..011e003 100644 --- a/workflow/scripts/combine_auxiliary_sources.py +++ b/workflow/scripts/combine_auxiliary_sources.py @@ -9,24 +9,12 @@ def _source_name(path: str | Path) -> str: return Path(path).parent.name -loads = { - _source_name(path): pd.read_parquet(path) - for path in snakemake.input.sources -} +loads = {_source_name(path): pd.read_parquet(path) for path in snakemake.input.sources} -combined, data_source, cleaning_method = ( - combine_auxiliary_sources( - loads, - priority=snakemake.params.source_priority, - ) +combined, data_source, cleaning_method = combine_auxiliary_sources( + loads, priority=snakemake.params.source_priority ) -combined.to_parquet( - snakemake.output.demand -) -data_source.to_parquet( - snakemake.output.data_source -) -cleaning_method.to_parquet( - snakemake.output.cleaning_method -) +combined.to_parquet(snakemake.output.demand) +data_source.to_parquet(snakemake.output.data_source) +cleaning_method.to_parquet(snakemake.output.cleaning_method) diff --git a/workflow/scripts/common/time.py b/workflow/scripts/common/time.py index d1595f6..18a7970 100644 --- a/workflow/scripts/common/time.py +++ b/workflow/scripts/common/time.py @@ -15,19 +15,13 @@ def as_utc_timestamp(value: object) -> pd.Timestamp: return timestamp.tz_convert("UTC") -def build_hourly_index( - *, - start: object, - end: object, -) -> pd.DatetimeIndex: +def build_hourly_index(*, start: object, end: object) -> pd.DatetimeIndex: """Create an end-exclusive hourly UTC index.""" start_timestamp = as_utc_timestamp(start) end_timestamp = as_utc_timestamp(end) if end_timestamp <= start_timestamp: - raise ValueError( - "The temporal end must be later than its start." - ) + raise ValueError("The temporal end must be later than its start.") return pd.date_range( start=start_timestamp, diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index cc80aac..d7cb9ab 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -3,34 +3,21 @@ from cleaning.advanced.planning.manifest import get_rule_override, load_execution_plan from common.time import build_hourly_index -loads = [ - pd.read_parquet(path) - for path in snakemake.input.sources -] +loads = [pd.read_parquet(path) for path in snakemake.input.sources] if not loads: - raise ValueError( - "No cleaned auxiliary data were supplied." - ) + raise ValueError("No cleaned auxiliary data were supplied.") auxiliary = loads[0].copy() for load in loads[1:]: auxiliary = auxiliary.combine_first(load) -plan = load_execution_plan( - snakemake.input.plan -) +plan = load_execution_plan(snakemake.input.plan) -override = get_rule_override( - plan, - rule_name=snakemake.wildcards.rule_name, -) +override = get_rule_override(plan, rule_name=snakemake.wildcards.rule_name) -target_index = build_hourly_index( - start=override["start"], - end=override["end"], -) +target_index = build_hourly_index(start=override["start"], end=override["end"]) profile = construct_from_sources( auxiliary, @@ -39,8 +26,4 @@ scaling=override.get("scaling"), ) -profile.to_frame( - name=override["country"] -).to_parquet( - snakemake.output.profile -) +profile.to_frame(name=override["country"]).to_parquet(snakemake.output.profile) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 992af85..38ed5df 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -24,18 +24,12 @@ def configure_logging(log_path) -> None: logger.setLevel(logging.INFO) logger.handlers.clear() - formatter = logging.Formatter( - "%(asctime)s | %(levelname)s | %(message)s" - ) + formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(formatter) - file_handler = logging.FileHandler( - log_path, - mode="w", - encoding="utf-8", - ) + file_handler = logging.FileHandler(log_path, mode="w", encoding="utf-8") file_handler.setFormatter(formatter) logger.addHandler(console_handler) @@ -51,37 +45,23 @@ def load_txt(filepath): def download_country( - *, - country_alpha_3: str, - start: pd.Timestamp, - end: pd.Timestamp, - token: str, + *, country_alpha_3: str, start: pd.Timestamp, end: pd.Timestamp, token: str ) -> tuple[str, pd.Series, float]: """Download ENTSO-E load for one country.""" - country = pycountry.countries.get( - alpha_3=country_alpha_3 - ) + country = pycountry.countries.get(alpha_3=country_alpha_3) if country is None: - raise ValueError( - "Unknown ISO alpha-3 country code: " - f"{country_alpha_3!r}." - ) + raise ValueError(f"Unknown ISO alpha-3 country code: {country_alpha_3!r}.") country_alpha_2 = country.alpha_2 - client = EntsoePandasClient( - api_key=token, - timeout=60, - ) + client = EntsoePandasClient(api_key=token, timeout=60) country_start = perf_counter() try: df_country = client.query_load( - country_code=country_alpha_2, - start=start, - end=end, + country_code=country_alpha_2, start=start, end=end ) df_country = df_country["Actual Load"] @@ -96,28 +76,14 @@ def download_country( end, ) - df_country = pd.Series( - name=country_alpha_3, - dtype=float, - ) + df_country = pd.Series(name=country_alpha_3, dtype=float) elapsed = perf_counter() - country_start - return ( - country_alpha_3, - df_country, - elapsed, - ) + return (country_alpha_3, df_country, elapsed) -def main( - start, - end, - country_codes, - token, - output_load, - workers, - ): +def main(start, end, country_codes, token, output_load, workers): """Download load in MW via the ENTSO-E API.""" start = as_utc_timestamp(start) end = as_utc_timestamp(end) @@ -139,9 +105,7 @@ def main( data_by_country = {} - with ThreadPoolExecutor( - max_workers=workers - ) as executor: + with ThreadPoolExecutor(max_workers=workers) as executor: futures = { executor.submit( download_country, @@ -153,28 +117,18 @@ def main( for country_alpha_3 in country_codes } - for completed, future in enumerate( - as_completed(futures), - start=1, - ): + for completed, future in enumerate(as_completed(futures), start=1): country_alpha_3 = futures[future] try: - ( - country_alpha_3, - df_country, - elapsed, - ) = future.result() + (country_alpha_3, df_country, elapsed) = future.result() except Exception as exc: raise RuntimeError( - "Failed to download ENTSO-E load for " - f"{country_alpha_3!r}." + f"Failed to download ENTSO-E load for {country_alpha_3!r}." ) from exc - data_by_country[ - country_alpha_3 - ] = df_country + data_by_country[country_alpha_3] = df_country logger.info( "[%s/%s] Finished %s in %.1fs.", @@ -184,46 +138,26 @@ def main( elapsed, ) - logger.info( - "Finished ENTSO-E downloads in %.1fs.", - perf_counter() - download_start, - ) + logger.info("Finished ENTSO-E downloads in %.1fs.", perf_counter() - download_start) # Restore configured country order because futures complete # in arbitrary order. - data = [ - data_by_country[country_alpha_3] - for country_alpha_3 in country_codes - ] + data = [data_by_country[country_alpha_3] for country_alpha_3 in country_codes] processing_start = perf_counter() - df = pd.concat( - data, - axis=1, - ) + df = pd.concat(data, axis=1) - df.index = pd.to_datetime( - df.index, - utc=True, - ) + df.index = pd.to_datetime(df.index, utc=True) df = df.resample("1h").mean() # Reindexing adds security to the ENTSO-E download. - target_index = build_hourly_index( - start=start, - end=end, - ) + target_index = build_hourly_index(start=start, end=end) - df = df.reindex( - index=target_index, - columns=country_codes, - ) + df = df.reindex(index=target_index, columns=country_codes) - non_numeric_columns = df.select_dtypes( - exclude="number" - ).columns + non_numeric_columns = df.select_dtypes(exclude="number").columns invalid = { column: df[column].dropna().head().tolist() @@ -232,10 +166,7 @@ def main( } if invalid: - raise TypeError( - "ENTSO-E load contains non-numeric values: " - f"{invalid}" - ) + raise TypeError(f"ENTSO-E load contains non-numeric values: {invalid}") # Pre-cleaning. Replace empty object columns with NaN # columns to allow data-source combining. @@ -250,22 +181,14 @@ def main( if __name__ == "__main__": - configure_logging( - snakemake.log[0] - ) + configure_logging(snakemake.log[0]) - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="entsoe_api", + plan, batch_id=snakemake.wildcards.batch_id, source="entsoe_api" ) start = batch["start"] end = batch["end"] diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 089feee..93ed05c 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -16,65 +16,41 @@ logger = logging.getLogger(__name__) NESO_API_BASE = "https://api.neso.energy/api/3/action" -USER_AGENT = ( - "modelblocks-module-demand-electricity/" - "NESO historic demand downloader" -) +USER_AGENT = "modelblocks-module-demand-electricity/NESO historic demand downloader" -def _request_json( - endpoint: str, - parameters: dict[str, str | int], -) -> dict[str, Any]: +def _request_json(endpoint: str, parameters: dict[str, str | int]) -> dict[str, Any]: """Request one JSON response from the NESO CKAN API.""" query = urlencode(parameters) url = f"{NESO_API_BASE}/{endpoint}?{query}" - request = Request( - url, - headers={"User-Agent": USER_AGENT}, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) with urlopen(request, timeout=120) as response: payload = json.load(response) if not payload.get("success", False): - raise RuntimeError( - f"NESO API request failed for {endpoint}: {payload}" - ) + raise RuntimeError(f"NESO API request failed for {endpoint}: {payload}") result = payload.get("result") if not isinstance(result, dict): - raise RuntimeError( - f"NESO API returned an unexpected result for {endpoint}." - ) + raise RuntimeError(f"NESO API returned an unexpected result for {endpoint}.") return result def _get_historic_demand_dataset() -> dict[str, Any]: """Return the NESO Historic Demand Data dataset.""" - return _request_json( - "package_show", - { - "id": "historic-demand-data", - }, - ) + return _request_json("package_show", {"id": "historic-demand-data"}) -def _select_csv_resource( - dataset: dict[str, Any], - *, - year: int, -) -> dict[str, Any]: +def _select_csv_resource(dataset: dict[str, Any], *, year: int) -> dict[str, Any]: """Select the annual NESO historic-demand CSV resource.""" resources = dataset.get("resources", []) if not isinstance(resources, list): - raise RuntimeError( - "NESO Historic Demand Data has no valid resource list." - ) + raise RuntimeError("NESO Historic Demand Data has no valid resource list.") expected_filename = f"demanddata_{year}.csv" expected_title = f"historic demand data {year}" @@ -82,29 +58,17 @@ def _select_csv_resource( matching_resources: list[dict[str, Any]] = [] for resource in resources: - name = str( - resource.get("name", "") - ).strip().casefold() + name = str(resource.get("name", "")).strip().casefold() - url = str( - resource.get("url", "") - ).strip() + url = str(resource.get("url", "")).strip() - format_name = str( - resource.get("format", "") - ).strip().casefold() + format_name = str(resource.get("format", "")).strip().casefold() url_lower = url.casefold() - is_csv = ( - format_name == "csv" - or url_lower.endswith(".csv") - ) + is_csv = format_name == "csv" or url_lower.endswith(".csv") - matches_year = ( - expected_title in name - or expected_filename in url_lower - ) + matches_year = expected_title in name or expected_filename in url_lower if is_csv and matches_year and url: matching_resources.append(resource) @@ -128,138 +92,72 @@ def _select_csv_resource( return matching_resources[0] -def _download_file( - *, - url: str, - output_path: Path, -) -> None: + +def _download_file(*, url: str, output_path: Path) -> None: """Download one file atomically.""" - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) - temporary_path = output_path.with_suffix( - output_path.suffix + ".part" - ) + temporary_path = output_path.with_suffix(output_path.suffix + ".part") - temporary_path.unlink( - missing_ok=True - ) + temporary_path.unlink(missing_ok=True) - request = Request( - url, - headers={"User-Agent": USER_AGENT}, - ) + request = Request(url, headers={"User-Agent": USER_AGENT}) try: with ( urlopen(request, timeout=300) as response, temporary_path.open("wb") as output_file, ): - shutil.copyfileobj( - response, - output_file, - ) + shutil.copyfileobj(response, output_file) if temporary_path.stat().st_size == 0: - raise RuntimeError( - f"NESO download produced an empty file: {url}" - ) + raise RuntimeError(f"NESO download produced an empty file: {url}") temporary_path.replace(output_path) except Exception: - temporary_path.unlink( - missing_ok=True - ) + temporary_path.unlink(missing_ok=True) raise -def download_annual_files( - *, - years: list[int], - output_paths: list[str | Path], -) -> None: +def download_annual_files(*, years: list[int], output_paths: list[str | Path]) -> None: """Discover and download the NESO CSV for each requested year.""" if len(years) != len(output_paths): raise ValueError( - "The number of NESO years must match the number " - "of output paths." + "The number of NESO years must match the number of output paths." ) if len(set(years)) != len(years): - raise ValueError( - f"NESO years must be unique: {years}" - ) + raise ValueError(f"NESO years must be unique: {years}") dataset = _get_historic_demand_dataset() - for year, raw_output_path in zip( - years, - output_paths, - strict=True, - ): + for year, raw_output_path in zip(years, output_paths, strict=True): output_path = Path(raw_output_path) - if ( - output_path.exists() - and output_path.stat().st_size > 0 - ): - logger.info( - "Retaining existing NESO file for %s: %s", - year, - output_path, - ) + if output_path.exists() and output_path.stat().st_size > 0: + logger.info("Retaining existing NESO file for %s: %s", year, output_path) continue - logger.info( - "Selecting NESO historic-demand resource for %s.", - year, - ) + logger.info("Selecting NESO historic-demand resource for %s.", year) - resource = _select_csv_resource( - dataset, - year=year, - ) + resource = _select_csv_resource(dataset, year=year) url = str(resource["url"]) - logger.info( - "Downloading NESO historic demand for %s from %s.", - year, - url, - ) + logger.info("Downloading NESO historic demand for %s from %s.", year, url) - _download_file( - url=url, - output_path=output_path, - ) + _download_file(url=url, output_path=output_path) - logger.info( - "Saved NESO historic demand for %s to %s.", - year, - output_path, - ) + logger.info("Saved NESO historic demand for %s to %s.", year, output_path) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") download_annual_files( - years=[ - int(snakemake.params.year) - ], - output_paths=[ - Path(snakemake.output.annual_file) - ], - ) \ No newline at end of file + years=[int(snakemake.params.year)], + output_paths=[Path(snakemake.output.annual_file)], + ) diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 45f7669..8ed3987 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -7,35 +7,22 @@ derive_cleaning_method_rank, ) -shutil.copyfile( - snakemake.input.demand, - snakemake.output.demand, -) +shutil.copyfile(snakemake.input.demand, snakemake.output.demand) -cleaning_method = pd.read_parquet( - snakemake.input.cleaning_method -) +cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) gap_filling = snakemake.params.gap_filling -rules = build_final_cleaning_rules( - gap_filling -) +rules = build_final_cleaning_rules(gap_filling) ranks = build_cleaning_method_ranks( - source_priority=snakemake.params.source_names, - rules=rules, + source_priority=snakemake.params.source_names, rules=rules ) cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, - ranks=ranks, + cleaning_method=cleaning_method, ranks=ranks ) -cleaning_method.to_parquet( - snakemake.output.cleaning_method -) +cleaning_method.to_parquet(snakemake.output.cleaning_method) -cleaning_method_rank.to_parquet( - snakemake.output.cleaning_method_rank -) \ No newline at end of file +cleaning_method_rank.to_parquet(snakemake.output.cleaning_method_rank) diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index a17fe8c..b8bc8ca 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -11,9 +11,7 @@ if __name__ == "__main__": - fill_plan = pd.read_parquet( - snakemake.input.fill_plan - ) + fill_plan = pd.read_parquet(snakemake.input.fill_plan) plan = build_advanced_execution_plan( fill_plan=fill_plan, @@ -21,7 +19,4 @@ source_names=snakemake.params.source_names, ) - write_execution_plan( - plan, - snakemake.output.plan, - ) + write_execution_plan(plan, snakemake.output.plan) diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index af0562a..6baae82 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -3,23 +3,14 @@ from cleaning.plot_timeline import main -sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, -) +sys.stderr = open(snakemake.log[0], "w", buffering=1) -logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", -) +logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") main( demand_path=snakemake.input.demand, cleaning_method_path=snakemake.input.cleaning_method, - cleaning_method_rank_path=( - snakemake.input.cleaning_method_rank - ), + cleaning_method_rank_path=(snakemake.input.cleaning_method_rank), output_path=snakemake.output.plot, source_names=snakemake.params.source_names, gap_filling_config=snakemake.params.gap_filling, diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py index 1d1f709..8b0b9f1 100644 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -19,42 +19,26 @@ _validate_provenance_metadata, ) -DEMAND_PATH = Path( - "resources/module/resources/automatic/load_cleaned.parquet" -) +DEMAND_PATH = Path("resources/module/resources/automatic/load_cleaned.parquet") CLEANING_METHOD_PATH = Path( - "resources/module/resources/automatic/" - "load_final_cleaning_method.parquet" + "resources/module/resources/automatic/load_final_cleaning_method.parquet" ) CLEANING_METHOD_RANK_PATH = Path( - "resources/module/resources/automatic/" - "load_final_cleaning_method_rank.parquet" + "resources/module/resources/automatic/load_final_cleaning_method_rank.parquet" ) -OUTPUT_PATH = Path( - "tmp/readme_cleaning_timeline.png" -) +OUTPUT_PATH = Path("tmp/readme_cleaning_timeline.png") # Curated subset for the README figure. -COUNTRIES = [ - "ALB", - "GBR", - "IRL", - "MKD", - "DEU", -] +COUNTRIES = ["ALB", "GBR", "IRL", "MKD", "DEU"] START = "2021-05-01" END = "2021-11-01" # Must correspond to the configuration used for the long run. -SOURCE_NAMES = [ - "entsoe_api", - "neso", - "opsd_api", -] +SOURCE_NAMES = ["entsoe_api", "neso", "opsd_api"] GAP_FILLING_CONFIG = { "mode": "basic", @@ -83,35 +67,21 @@ "max_gap": "168h", "source_offset": "168h", }, - ], + ] }, } def main() -> None: demand = pd.read_parquet(DEMAND_PATH) - cleaning_method = pd.read_parquet( - CLEANING_METHOD_PATH - ) - cleaning_method_rank = pd.read_parquet( - CLEANING_METHOD_RANK_PATH - ) + cleaning_method = pd.read_parquet(CLEANING_METHOD_PATH) + cleaning_method_rank = pd.read_parquet(CLEANING_METHOD_RANK_PATH) - print( - "Available period:", - demand.index.min(), - "to", - demand.index.max(), - ) - print( - "Available countries:", - ", ".join(demand.columns), - ) + print("Available period:", demand.index.min(), "to", demand.index.max()) + print("Available countries:", ", ".join(demand.columns)) missing_countries = [ - country - for country in COUNTRIES - if country not in demand.columns + country for country in COUNTRIES if country not in demand.columns ] if missing_countries: @@ -123,38 +93,21 @@ def main() -> None: start = pd.Timestamp(START, tz="UTC") end = pd.Timestamp(END, tz="UTC") - mask = ( - (demand.index >= start) - & (demand.index < end) - ) + mask = (demand.index >= start) & (demand.index < end) demand = demand.loc[mask, COUNTRIES] - cleaning_method = cleaning_method.loc[ - mask, - COUNTRIES, - ] - cleaning_method_rank = cleaning_method_rank.loc[ - mask, - COUNTRIES, - ] + cleaning_method = cleaning_method.loc[mask, COUNTRIES] + cleaning_method_rank = cleaning_method_rank.loc[mask, COUNTRIES] if len(demand) < 2: - raise ValueError( - f"No usable data found between {START} and {END}." - ) + raise ValueError(f"No usable data found between {START} and {END}.") - print( - f"Plotting {len(demand):,} hourly timestamps " - f"for {len(COUNTRIES)} countries." - ) + print(f"Plotting {len(demand):,} hourly timestamps for {len(COUNTRIES)} countries.") # Useful while choosing the README window/countries. print("\nCleaning-method counts:") for country in COUNTRIES: - counts = ( - cleaning_method[country] - .value_counts(dropna=False) - ) + counts = cleaning_method[country].value_counts(dropna=False) print(f"\n{country}") print(counts.to_string()) @@ -165,8 +118,7 @@ def main() -> None: ) metadata = _build_cleaning_method_metadata( - source_names=SOURCE_NAMES, - gap_filling_config=GAP_FILLING_CONFIG, + source_names=SOURCE_NAMES, gap_filling_config=GAP_FILLING_CONFIG ) _validate_provenance_metadata( @@ -177,35 +129,23 @@ def main() -> None: rank_colours = _build_rank_colours(metadata) - background, background_cmap = ( - _encode_rank_background( - cleaning_method_rank=cleaning_method_rank, - metadata=metadata, - rank_colours=rank_colours, - ) + background, background_cmap = _encode_rank_background( + cleaning_method_rank=cleaning_method_rank, + metadata=metadata, + rank_colours=rank_colours, ) figure, axis = _plot_cleaning_background( - demand=demand, - background=background, - background_cmap=background_cmap, + demand=demand, background=background, background_cmap=background_cmap ) - mean_load_gw = _add_normalised_demand_traces( - axis=axis, - demand=demand, - ) + mean_load_gw = _add_normalised_demand_traces(axis=axis, demand=demand) _add_mean_load_labels( - axis=axis, - mean_load_gw=mean_load_gw, - countries=demand.columns, + axis=axis, mean_load_gw=mean_load_gw, countries=demand.columns ) - legend_handles = _build_legend_handles( - metadata, - rank_colours, - ) + legend_handles = _build_legend_handles(metadata, rank_colours) figure.legend( handles=legend_handles, @@ -214,23 +154,14 @@ def main() -> None: frameon=False, ) - OUTPUT_PATH.parent.mkdir( - parents=True, - exist_ok=True, - ) + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) - figure.savefig( - OUTPUT_PATH, - dpi=200, - bbox_inches="tight", - ) + figure.savefig(OUTPUT_PATH, dpi=200, bbox_inches="tight") plt.close(figure) - print( - f"\nSaved README figure to: {OUTPUT_PATH.resolve()}" - ) + print(f"\nSaved README figure to: {OUTPUT_PATH.resolve()}") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 9f52c3e..5be5dd9 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -17,16 +17,10 @@ logger = logging.getLogger(__name__) -REQUIRED_COLUMNS = [ - "SETTLEMENT_DATE", - "SETTLEMENT_PERIOD", - "ND", -] +REQUIRED_COLUMNS = ["SETTLEMENT_DATE", "SETTLEMENT_PERIOD", "ND"] -def _read_neso_files( - paths: Iterable[str | Path], -) -> pd.DataFrame: +def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: """Read and combine annual NESO historic-demand files.""" frames: list[pd.DataFrame] = [] @@ -34,25 +28,14 @@ def _read_neso_files( path = Path(raw_path) if not path.exists(): - raise FileNotFoundError( - f"NESO input file does not exist: {path}" - ) + raise FileNotFoundError(f"NESO input file does not exist: {path}") - logger.info( - "Reading NESO historic demand from %s.", - path, - ) + logger.info("Reading NESO historic demand from %s.", path) try: - frame = pd.read_csv( - path, - usecols=REQUIRED_COLUMNS, - ) + frame = pd.read_csv(path, usecols=REQUIRED_COLUMNS) except ValueError as error: - available_columns = pd.read_csv( - path, - nrows=0, - ).columns.tolist() + available_columns = pd.read_csv(path, nrows=0).columns.tolist() raise ValueError( f"NESO file {path} does not contain the required " @@ -63,96 +46,56 @@ def _read_neso_files( frames.append(frame) if not frames: - raise ValueError( - "At least one NESO input file is required." - ) + raise ValueError("At least one NESO input file is required.") - return pd.concat( - frames, - ignore_index=True, - ) + return pd.concat(frames, ignore_index=True) -def _prepare_half_hourly_demand( - raw: pd.DataFrame, -) -> pd.Series: +def _prepare_half_hourly_demand(raw: pd.DataFrame) -> pd.Series: """Convert raw NESO records to a UTC half-hourly demand series.""" prepared = add_utc_timestamps(raw) - prepared["ND"] = pd.to_numeric( - prepared["ND"], - errors="coerce", - ) + prepared["ND"] = pd.to_numeric(prepared["ND"], errors="coerce") - invalid_demand_count = int( - prepared["ND"].isna().sum() - ) + invalid_demand_count = int(prepared["ND"].isna().sum()) if invalid_demand_count: logger.warning( - "NESO contains %s missing or non-numeric ND values.", - invalid_demand_count, + "NESO contains %s missing or non-numeric ND values.", invalid_demand_count ) - half_hourly = ( - prepared - .set_index("timestamp")["ND"] - .sort_index() - .rename("GBR") - ) + half_hourly = prepared.set_index("timestamp")["ND"].sort_index().rename("GBR") - duplicate_mask = half_hourly.index.duplicated( - keep=False - ) + duplicate_mask = half_hourly.index.duplicated(keep=False) if duplicate_mask.any(): duplicate_timestamps = ( - half_hourly.index[duplicate_mask] - .unique() - .astype(str) - .tolist() + half_hourly.index[duplicate_mask].unique().astype(str).tolist() ) raise ValueError( - "NESO data contain duplicate UTC timestamps: " - f"{duplicate_timestamps[:10]}" + f"NESO data contain duplicate UTC timestamps: {duplicate_timestamps[:10]}" ) if not half_hourly.index.is_monotonic_increasing: - raise ValueError( - "Prepared NESO timestamps are not sorted." - ) + raise ValueError("Prepared NESO timestamps are not sorted.") return half_hourly -def _aggregate_hourly( - half_hourly: pd.Series, -) -> pd.Series: +def _aggregate_hourly(half_hourly: pd.Series) -> pd.Series: """Aggregate half-hourly MW observations to hourly mean MW.""" hourly_counts = half_hourly.resample("1h").count() - incomplete_hours = hourly_counts.loc[ - hourly_counts.between( - 1, - 1, - inclusive="both", - ) - ] + incomplete_hours = hourly_counts.loc[hourly_counts.between(1, 1, inclusive="both")] if not incomplete_hours.empty: logger.warning( - "NESO contains %s hours with only one valid " - "half-hourly ND observation.", + "NESO contains %s hours with only one valid half-hourly ND observation.", len(incomplete_hours), ) - hourly = ( - half_hourly - .resample("1h") - .mean() - .rename("GBR") - ) + hourly = half_hourly.resample("1h").mean().rename("GBR") return hourly @@ -168,23 +111,12 @@ def prepare_load_neso( """Prepare NESO demand on the common time-country target grid.""" target_countries = list(countries) - if len(target_countries) != len( - set(target_countries) - ): - raise ValueError( - "Target country codes must be unique." - ) + if len(target_countries) != len(set(target_countries)): + raise ValueError("Target country codes must be unique.") - target_index = build_hourly_index( - start=temporal_start, - end=temporal_end, - ) + target_index = build_hourly_index(start=temporal_start, end=temporal_end) - result = pd.DataFrame( - index=target_index, - columns=target_countries, - dtype=float, - ) + result = pd.DataFrame(index=target_index, columns=target_countries, dtype=float) if "GBR" not in target_countries: logger.info( @@ -196,17 +128,11 @@ def prepare_load_neso( half_hourly = _prepare_half_hourly_demand(raw) hourly = _aggregate_hourly(half_hourly) - result["GBR"] = hourly.reindex( - target_index - ) + result["GBR"] = hourly.reindex(target_index) - supplied = int( - result["GBR"].notna().sum() - ) + supplied = int(result["GBR"].notna().sum()) - missing = int( - result["GBR"].isna().sum() - ) + missing = int(result["GBR"].isna().sum()) logger.info( "Prepared NESO GBR demand: %s supplied hourly " @@ -216,45 +142,25 @@ def prepare_load_neso( ) output_path = Path(output_path) - output_path.parent.mkdir( - parents=True, - exist_ok=True, - ) + output_path.parent.mkdir(parents=True, exist_ok=True) result.to_parquet(output_path) logger.info( - "Saved prepared NESO demand to %s with shape %s.", - output_path, - result.shape, + "Saved prepared NESO demand to %s with shape %s.", output_path, result.shape ) if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - logging.basicConfig( - level=logging.INFO, - format="%(levelname)s: %(message)s", - ) + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) - batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="neso", - ) + batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="neso") temporal_start = batch["start"] temporal_end = batch["end"] countries = batch["countries"] @@ -264,10 +170,7 @@ def prepare_load_neso( countries = snakemake.params.country_codes prepare_load_neso( - input_paths=[ - Path(path) - for path in snakemake.input.annual_files - ], + input_paths=[Path(path) for path in snakemake.input.annual_files], output_path=snakemake.output.load, temporal_start=temporal_start, temporal_end=temporal_end, diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 13dc062..51bd3cb 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -14,9 +14,7 @@ snakemake: Any -def get_map_alpha2_to_alpha3( - countries_alpha_2, -) -> dict[str, str]: +def get_map_alpha2_to_alpha3(countries_alpha_2) -> dict[str, str]: """Map ISO alpha-2 country codes to alpha-3 codes.""" mapping = {} @@ -26,107 +24,60 @@ def get_map_alpha2_to_alpha3( if country is not None: mapping[alpha2] = country.alpha_3 else: - warn( - f"Country with alpha-2 code '{alpha2}' " - "not found in pycountry." - ) + warn(f"Country with alpha-2 code '{alpha2}' not found in pycountry.") return mapping -def main( - path_raw_load, - output_load, - start, - end, - country_codes, -): +def main(path_raw_load, output_load, start, end, country_codes): """Prepare OPSD demand for the configured scope.""" load = pd.read_csv(path_raw_load) load = OPSDLoad.validate(load) load = load.loc[load["variable"] == "load"] - load = load.loc[ - load["attribute"] - == "actual_entsoe_power_statistics" - ].copy() + load = load.loc[load["attribute"] == "actual_entsoe_power_statistics"].copy() start = as_utc_timestamp(start) end = as_utc_timestamp(end) - load["utc_timestamp"] = pd.to_datetime( - load["utc_timestamp"], - utc=True, - ) + load["utc_timestamp"] = pd.to_datetime(load["utc_timestamp"], utc=True) # Filter the large long-format table before pivoting. load = load.loc[ - (load["utc_timestamp"] >= start) - & (load["utc_timestamp"] < end) + (load["utc_timestamp"] >= start) & (load["utc_timestamp"] < end) ].copy() - country_mapping = get_map_alpha2_to_alpha3( - load["region"].unique() - ) + country_mapping = get_map_alpha2_to_alpha3(load["region"].unique()) - load = load.loc[ - load["region"].isin(country_mapping) - ].copy() + load = load.loc[load["region"].isin(country_mapping)].copy() - load.loc[:, "region"] = load["region"].map( - country_mapping - ) + load.loc[:, "region"] = load["region"].map(country_mapping) # Keep only countries required by this module run. - load = load.loc[ - load["region"].isin(country_codes) - ].copy() + load = load.loc[load["region"].isin(country_codes)].copy() - load.loc[:, "data"] = pd.to_numeric( - load["data"], - errors="raise", - ) + load.loc[:, "data"] = pd.to_numeric(load["data"], errors="raise") - prepared = pd.pivot( - load, - index="utc_timestamp", - columns="region", - values="data", - ) + prepared = pd.pivot(load, index="utc_timestamp", columns="region", values="data") - target_index = build_hourly_index( - start=start, - end=end, - ) + target_index = build_hourly_index(start=start, end=end) - prepared = prepared.reindex( - index=target_index, - columns=country_codes, - ) + prepared = prepared.reindex(index=target_index, columns=country_codes) prepared = prepared.astype(float) prepared.to_parquet(output_load) + if __name__ == "__main__": - sys.stderr = open( - snakemake.log[0], - "w", - buffering=1, - ) + sys.stderr = open(snakemake.log[0], "w", buffering=1) - plan_path = getattr( - snakemake.input, - "plan", - None, - ) + plan_path = getattr(snakemake.input, "plan", None) if plan_path is not None: plan = load_execution_plan(plan_path) batch = get_batch( - plan, - batch_id=snakemake.wildcards.batch_id, - source="opsd_api", + plan, batch_id=snakemake.wildcards.batch_id, source="opsd_api" ) start = batch["start"] end = batch["end"] @@ -134,9 +85,7 @@ def main( else: start = snakemake.params.start end = snakemake.params.end - country_codes = list( - snakemake.params.country_codes - ) + country_codes = list(snakemake.params.country_codes) main( path_raw_load=snakemake.input.load, From 53746b7ae6cba5b3213c4e0265f8205c532452aa Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 16:29:36 +0200 Subject: [PATCH 079/241] Removed dead functions --- workflow/rules/advanced.smk | 40 ------------------- .../cleaning/advanced/planning/manifest.py | 11 ----- workflow/scripts/cleaning/plot_timeline.py | 20 ---------- 3 files changed, 71 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index b0c266f..f0f24a1 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -14,26 +14,6 @@ def auxiliary_acquisition_plan(_wildcards): return checkpoints.plan_auxiliary_data.get().output.plan -def auxiliary_entsoe_outputs(_wildcards): - """Return all ENTSO-E outputs required by the execution plan.""" - plan = _read_auxiliary_plan() - - return [ - ("/automatic/" "auxiliary/entsoe_api/" f"{batch_id}.parquet") - for batch_id in plan["batch_ids_by_source"].get("entsoe_api", []) - ] - - -def auxiliary_opsd_outputs(_wildcards): - """Return all OPSD outputs required by the execution plan.""" - plan = _read_auxiliary_plan() - - return [ - ("/automatic/" "auxiliary/opsd_api/" f"{batch_id}.parquet") - for batch_id in plan["batch_ids_by_source"].get("opsd_api", []) - ] - - def auxiliary_neso_raw_files(wildcards): """Return annual NESO files required by one auxiliary batch.""" plan = _read_auxiliary_plan() @@ -50,16 +30,6 @@ def auxiliary_neso_raw_files(wildcards): ] -def auxiliary_neso_outputs(_wildcards): - """Return all NESO outputs required by the execution plan.""" - plan = _read_auxiliary_plan() - - return [ - ("/automatic/" "auxiliary/neso/" f"{batch_id}.parquet") - for batch_id in plan["batch_ids_by_source"].get("neso", []) - ] - - def auxiliary_group_source_files(wildcards): """Return prepared source files for one auxiliary group.""" plan = _read_auxiliary_plan() @@ -78,16 +48,6 @@ def auxiliary_group_source_files(wildcards): ] -def auxiliary_combined_outputs(_wildcards): - """Return all combined auxiliary group outputs.""" - plan = _read_auxiliary_plan() - - return [ - ("/automatic/" "auxiliary/combined/" f"{group_id}.parquet") - for group_id in plan["groups"] - ] - - def auxiliary_rule_cleaned_files(wildcards): """Return cleaned auxiliary files required by one advanced override.""" plan = _read_auxiliary_plan() diff --git a/workflow/scripts/cleaning/advanced/planning/manifest.py b/workflow/scripts/cleaning/advanced/planning/manifest.py index f1e8ccf..69e0dc5 100644 --- a/workflow/scripts/cleaning/advanced/planning/manifest.py +++ b/workflow/scripts/cleaning/advanced/planning/manifest.py @@ -19,17 +19,6 @@ def write_execution_plan(plan: Mapping[str, Any], path: str | Path) -> None: json.dump(plan, file, indent=2) -def write_advanced_execution_plan( - *, plan: dict[str, object], output_path: str | Path -) -> None: - """Write the advanced execution plan as JSON.""" - output_path = Path(output_path) - output_path.parent.mkdir(parents=True, exist_ok=True) - - with output_path.open("w", encoding="utf-8") as file: - json.dump(plan, file, indent=2) - - def load_execution_plan(path: str | Path) -> dict[str, Any]: """Load one compiled advanced execution plan.""" with open(path, encoding="utf-8") as file: diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index c4e835c..ff6a699 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -401,26 +401,6 @@ def _add_mean_load_labels( ) -def _format_method_label(method: str) -> str: - """Convert a method identifier into a legend label.""" - if method == "missing": - return "Missing" - - if method.startswith("observed_"): - source = method.removeprefix("observed_") - - source_labels = { - "entsoe_api": "Observed: ENTSO-E API", - "opsd_api": "Observed: OPSD API", - } - - return source_labels.get( - source, ("Observed: " + source.replace("_", " ").upper()) - ) - - return method.replace("_", " ").capitalize() - - def _build_cleaning_method_metadata( *, source_names: list[str], gap_filling_config: dict[str, Any] ) -> pd.DataFrame: From d208fee0786a42984d132cc488e7af9d8424f184 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 17:14:06 +0200 Subject: [PATCH 080/241] Update ruff to ignore test formats. --- ruff.toml | 1 + tests/unit/test_advanced_apply.py | 14 ++++++++++++++ tests/unit/test_advanced_combine_auxiliary.py | 1 + 3 files changed, 16 insertions(+) diff --git a/ruff.toml b/ruff.toml index 97d5205..a0d2a0c 100644 --- a/ruff.toml +++ b/ruff.toml @@ -20,6 +20,7 @@ max-complexity = 10 [lint.per-file-ignores] "__init__.py" = ["E402", "F401", "D104"] "*.ipynb" = ["E402"] +"tests/**/*.py" = ["D100", "D103"] [lint.flake8-quotes] docstring-quotes = "double" diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index e95265e..686c87b 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -23,6 +23,7 @@ def _cleaning_method() -> pd.DataFrame: def test_construct_from_sources_requires_profile() -> None: + """Test construct from sources requires profile.""" rule = {"method": "construct_from_sources"} with pytest.raises(ValueError, match="requires a constructed auxiliary profile"): @@ -32,6 +33,7 @@ def test_construct_from_sources_requires_profile() -> None: def test_construct_from_sources_fills_gaps() -> None: + """Test construct from sources fills gaps.""" load = _load() cleaning_method = _cleaning_method() @@ -59,6 +61,7 @@ def test_construct_from_sources_fills_gaps() -> None: def test_construct_from_sources_overwrites_entire_period() -> None: + """Test construct from sources overwrites entire period.""" load = _load() cleaning_method = _cleaning_method() @@ -86,6 +89,7 @@ def test_construct_from_sources_overwrites_entire_period() -> None: def test_leave_missing_returns_unchanged_copies() -> None: + """Test leaving missing does nothing.""" load = _load() cleaning_method = _cleaning_method() @@ -104,6 +108,7 @@ def test_leave_missing_returns_unchanged_copies() -> None: def test_rejects_unsupported_method() -> None: + """Test unsupported methods.""" with pytest.raises(ValueError, match="Unsupported advanced-fill method"): apply_auxiliary_fill_rule( _load(), @@ -114,6 +119,7 @@ def test_rejects_unsupported_method() -> None: def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: + """Test order.""" load = _load() cleaning_method = _cleaning_method() @@ -154,6 +160,7 @@ def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: + """Test fill returns copy without overrides.""" load = _load() cleaning_method = _cleaning_method() @@ -173,6 +180,7 @@ def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: def test_overwrite_replaces_existing_values() -> None: + """Test overwrite.""" index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [10.0, 20.0, 30.0, 40.0]}, index=index) @@ -218,6 +226,7 @@ def test_overwrite_replaces_existing_values() -> None: def test_fill_gaps_preserves_existing_values() -> None: + """Test fill doesnt overwrite.""" index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [10.0, float("nan"), 30.0, 40.0]}, index=index) @@ -273,6 +282,7 @@ def test_fill_gaps_preserves_existing_values() -> None: def test_external_profile_fill_gaps_only_replaces_missing_values(): + """Test external profile fills gaps.""" index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [100.0, None, 300.0, None]}, index=index) @@ -310,6 +320,7 @@ def test_external_profile_fill_gaps_only_replaces_missing_values(): def test_external_profile_overwrite_replaces_supplied_values(): + """Test external overwrites.""" index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0]}, index=index) @@ -344,6 +355,7 @@ def test_external_profile_overwrite_replaces_supplied_values(): def test_external_profile_overwrite_only_replaces_supplied_timestamps(): + """Test overwrite domains.""" index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) @@ -383,6 +395,7 @@ def test_external_profile_overwrite_only_replaces_supplied_timestamps(): def test_external_profile_ignores_values_outside_rule_period(): + """Test overwrite restricts domain.""" index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) @@ -417,6 +430,7 @@ def test_external_profile_ignores_values_outside_rule_period(): def test_external_profile_requires_profile(): + """Test external requires profile.""" index = pd.date_range("2025-01-01", periods=2, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [None, None]}, index=index) diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py index c5152e2..6ece7ad 100644 --- a/tests/unit/test_advanced_combine_auxiliary.py +++ b/tests/unit/test_advanced_combine_auxiliary.py @@ -3,6 +3,7 @@ def test_combine_auxiliary_sources_aligns_country_columns() -> None: + """Test combine auxiliary sources aligns country cols.""" index = pd.date_range("2020-01-01", periods=2, freq="h", tz="UTC") entsoe = pd.DataFrame({"GBR": [10.0, 11.0], "GRC": [20.0, 21.0]}, index=index) From 86d2ff68fbd8c02c3a7a8680f25bf67e6f50e7a3 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Thu, 13 Aug 2026 17:26:36 +0200 Subject: [PATCH 081/241] Added docstrings to pass Ruff tests, exempted the "ND" error and file size check on the figure image. --- .pre-commit-config.yaml | 4 +++- tests/unit/test_advanced_apply.py | 2 +- workflow/scripts/clean_auxiliary_data.py | 1 + workflow/scripts/cleaning/advanced/planning/plan.py | 1 - workflow/scripts/combine_auxiliary_sources.py | 1 + workflow/scripts/common/schemas.py | 4 ++++ workflow/scripts/construct_auxiliary_profile.py | 1 + workflow/scripts/finalise_clean_demand.py | 1 + workflow/scripts/plot_cleaning_timeline.py | 1 + workflow/scripts/plot_readme_cleaning_example.py | 1 + 10 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d639685..13adccb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: # Repo quality - id: check-added-large-files args: [--enforce-all] - exclude: ^pixi\.lock$ + exclude: ^(pixi\.lock|figures/readme_cleaning_timeline\.png)$ - id: forbid-submodules - id: check-case-conflict - id: check-illegal-windows-names @@ -43,6 +43,8 @@ repos: hooks: - id: codespell files: .*\.(py|smk|md)$|^Snakefile$ + args: + - --ignore-words-list=ND ci: # https://pre-commit.ci/ autoupdate_schedule: quarterly diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index 686c87b..cd83db1 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -226,7 +226,7 @@ def test_overwrite_replaces_existing_values() -> None: def test_fill_gaps_preserves_existing_values() -> None: - """Test fill doesnt overwrite.""" + """Test fill doesn't overwrite.""" index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") load = pd.DataFrame({"ALB": [10.0, float("nan"), 30.0, 40.0]}, index=index) diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py index 6a2831d..e563d28 100644 --- a/workflow/scripts/clean_auxiliary_data.py +++ b/workflow/scripts/clean_auxiliary_data.py @@ -1,3 +1,4 @@ +"""Applies basic rules to auxiliary data.""" import pandas as pd from cleaning.basic.apply import fill_basic_gaps diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 4d5a38c..3dd0e2e 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -26,7 +26,6 @@ def validate_auxiliary_fill_rule(rule_name: str, rule: Mapping[str, Any]) -> Non if not isinstance(rule, Mapping): raise TypeError(f"Advanced-fill rule {rule_name!r} must be a mapping.") - country = _get_required_string(rule, key="country", rule_name=rule_name) start = _get_required_timestamp(rule, key="start", rule_name=rule_name) end = _get_required_timestamp(rule, key="end", rule_name=rule_name) diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py index 011e003..627f0c8 100644 --- a/workflow/scripts/combine_auxiliary_sources.py +++ b/workflow/scripts/combine_auxiliary_sources.py @@ -1,3 +1,4 @@ +"""Aggregates auxiliary data into one source.""" from pathlib import Path import pandas as pd diff --git a/workflow/scripts/common/schemas.py b/workflow/scripts/common/schemas.py index c3b2dae..eac3a23 100644 --- a/workflow/scripts/common/schemas.py +++ b/workflow/scripts/common/schemas.py @@ -8,7 +8,9 @@ class OPSDLoad(DataFrameModel): + """OPSD Download Class.""" class Config: + """OPSD Config class.""" coerce = True strict = False @@ -25,7 +27,9 @@ class Config: class Shapes(DataFrameModel): + """Shapes Class.""" class Config: + """Shape config class.""" coerce = True strict = False diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index d7cb9ab..e2cab20 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,3 +1,4 @@ +"""Constructs the auxiliary profile.""" import pandas as pd from cleaning.advanced.methods.construct_from_sources import construct_from_sources from cleaning.advanced.planning.manifest import get_rule_override, load_execution_plan diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 8ed3987..7b45ce4 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -1,3 +1,4 @@ +"""Generates the final cleaned parquet.""" import shutil import pandas as pd diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index 6baae82..82ed2b1 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -1,3 +1,4 @@ +"""Plots the diagnostic figure.""" import logging import sys diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py index 8b0b9f1..2993afe 100644 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -73,6 +73,7 @@ def main() -> None: + """Main function for generating README figure.""" demand = pd.read_parquet(DEMAND_PATH) cleaning_method = pd.read_parquet(CLEANING_METHOD_PATH) cleaning_method_rank = pd.read_parquet(CLEANING_METHOD_RANK_PATH) From 73a1093f3c100a8046694e5e859bdda5648771da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 15:26:52 +0000 Subject: [PATCH 082/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/clean_auxiliary_data.py | 1 + workflow/scripts/combine_auxiliary_sources.py | 1 + workflow/scripts/common/schemas.py | 4 ++++ workflow/scripts/construct_auxiliary_profile.py | 1 + workflow/scripts/finalise_clean_demand.py | 1 + workflow/scripts/plot_cleaning_timeline.py | 1 + 6 files changed, 9 insertions(+) diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py index e563d28..2aaf4a7 100644 --- a/workflow/scripts/clean_auxiliary_data.py +++ b/workflow/scripts/clean_auxiliary_data.py @@ -1,4 +1,5 @@ """Applies basic rules to auxiliary data.""" + import pandas as pd from cleaning.basic.apply import fill_basic_gaps diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py index 627f0c8..9900c2a 100644 --- a/workflow/scripts/combine_auxiliary_sources.py +++ b/workflow/scripts/combine_auxiliary_sources.py @@ -1,4 +1,5 @@ """Aggregates auxiliary data into one source.""" + from pathlib import Path import pandas as pd diff --git a/workflow/scripts/common/schemas.py b/workflow/scripts/common/schemas.py index eac3a23..38a8957 100644 --- a/workflow/scripts/common/schemas.py +++ b/workflow/scripts/common/schemas.py @@ -9,8 +9,10 @@ class OPSDLoad(DataFrameModel): """OPSD Download Class.""" + class Config: """OPSD Config class.""" + coerce = True strict = False @@ -28,8 +30,10 @@ class Config: class Shapes(DataFrameModel): """Shapes Class.""" + class Config: """Shape config class.""" + coerce = True strict = False diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index e2cab20..29e402b 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,4 +1,5 @@ """Constructs the auxiliary profile.""" + import pandas as pd from cleaning.advanced.methods.construct_from_sources import construct_from_sources from cleaning.advanced.planning.manifest import get_rule_override, load_execution_plan diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 7b45ce4..6660000 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -1,4 +1,5 @@ """Generates the final cleaned parquet.""" + import shutil import pandas as pd diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index 82ed2b1..70056ec 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -1,4 +1,5 @@ """Plots the diagnostic figure.""" + import logging import sys From 89ffe136a04e48e241b17b3965eb8d5f2adc57f1 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 10:51:15 +0200 Subject: [PATCH 083/241] Implemented broad validation refactor to remove redundant validation and centralise necessary validation. --- tests/unit/test_advanced_plan.py | 237 ++++---------- tests/unit/test_validate_config.py | 49 +++ workflow/rules/automatic.smk | 46 +-- workflow/rules/clean.smk | 5 +- .../cleaning/advanced/planning/plan.py | 297 ++---------------- workflow/scripts/validate_config.py | 15 + 6 files changed, 169 insertions(+), 480 deletions(-) create mode 100644 tests/unit/test_validate_config.py create mode 100644 workflow/scripts/validate_config.py diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index d84a672..ca7698c 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -1,153 +1,16 @@ """Tests for advanced auxiliary-fill planning.""" import pandas as pd -import pytest -from cleaning.advanced.planning.plan import ( - build_auxiliary_fill_plan, - validate_auxiliary_fill_rule, -) -TARGET_COUNTRIES = ["ALB"] +from cleaning.advanced.planning.plan import build_auxiliary_fill_plan +TARGET_COUNTRIES = ["ALB"] TARGET_START = pd.Timestamp("2022-01-01", tz="UTC") - TARGET_END = pd.Timestamp("2025-01-01", tz="UTC") -def test_validate_construct_from_sources_rule() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - {"country": "MKD", "start": "2023-01-01", "end": "2023-12-31 23:00"}, - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "weight": 2, - }, - ], - } - - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - -def test_validate_external_profile_rule() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "external_profile", - } - - validate_auxiliary_fill_rule("external_albania_2023", rule) - - -def test_construct_from_sources_requires_sources() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - } - - with pytest.raises(ValueError, match="must define 'sources'"): - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - -def test_source_weight_must_be_positive() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "weight": 0, - } - ], - } - - with pytest.raises(ValueError, match="must be greater than zero"): - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - -def test_validate_construct_from_sources_with_scaling() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - {"country": "MKD", "start": "2023-01-01", "end": "2023-12-31 23:00"} - ], - "scaling": { - "method": "match_energy", - "target_sources": [ - {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"}, - { - "country": "ALB", - "start": "2024-01-01", - "end": "2024-12-31 23:00", - "weight": 2, - }, - ], - }, - } - - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - -def test_match_energy_scaling_requires_target_sources() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"} - ], - "scaling": {"method": "match_energy"}, - } - - with pytest.raises(ValueError, match="must define 'target_sources'"): - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - -def test_rejects_unsupported_scaling_method() -> None: - rule = { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"} - ], - "scaling": { - "method": "unknown", - "target_sources": [ - {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} - ], - }, - } - - with pytest.raises(ValueError, match="Unsupported scaling method"): - validate_auxiliary_fill_rule("replace_albania_2023", rule) - - def test_build_auxiliary_fill_plan_normalizes_rules() -> None: + """Build a normalized plan for active overrides.""" rules = { "external_albania": { "country": "ALB", @@ -163,7 +26,11 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"}, + { + "country": "MNE", + "start": "2023-01-01", + "end": "2023-12-31 23:00", + }, { "country": "MKD", "start": "2023-01-01", @@ -174,7 +41,11 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scaling": { "method": "match_energy", "target_sources": [ - {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} + { + "country": "ALB", + "start": "2022-01-01", + "end": "2022-12-31 23:00", + } ], }, }, @@ -211,6 +82,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: + """Return the expected empty plan structure when no overrides are configured.""" result = build_auxiliary_fill_plan( {}, target_countries=TARGET_COUNTRIES, @@ -236,6 +108,7 @@ def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: + """Exclude overrides for countries outside the target scope.""" rules = { "albania": { "country": "ALB", @@ -243,7 +116,13 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2023-01-01", + "end": "2023-02-01", + } + ], }, "montenegro": { "country": "MNE", @@ -251,7 +130,13 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2023-01-01", + "end": "2023-02-01", + } + ], }, } @@ -266,6 +151,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: + """Exclude overrides whose periods do not intersect the target period.""" rules = { "before": { "country": "ALB", @@ -273,7 +159,13 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2020-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2020-01-01", + "end": "2020-02-01", + } + ], }, "after": { "country": "ALB", @@ -281,7 +173,13 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2026-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2026-01-01", "end": "2026-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2026-01-01", + "end": "2026-02-01", + } + ], }, } @@ -296,6 +194,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: + """Keep overrides that partially overlap the target period.""" rules = { "partial": { "country": "ALB", @@ -303,7 +202,13 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: "end": "2022-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2021-12-01", + "end": "2022-02-01", + } + ], } } @@ -317,28 +222,8 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: assert result["rule_name"].tolist() == ["partial"] -def test_build_auxiliary_fill_plan_validates_inactive_rule() -> None: - rules = { - "invalid_montenegro": { - "country": "MNE", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "not_a_scope", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], - } - } - - with pytest.raises(ValueError, match="Unsupported scope"): - build_auxiliary_fill_plan( - rules, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: + """Treat touching half-open periods as non-overlapping.""" rules = { "ends_at_start": { "country": "ALB", @@ -346,7 +231,13 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2022-01-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-01-01"}], + "sources": [ + { + "country": "GBR", + "start": "2021-12-01", + "end": "2022-01-01", + } + ], }, "starts_at_end": { "country": "ALB", @@ -354,7 +245,13 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2025-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2025-01-01", "end": "2025-02-01"}], + "sources": [ + { + "country": "GBR", + "start": "2025-01-01", + "end": "2025-02-01", + } + ], }, } diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py new file mode 100644 index 0000000..f3c0cfd --- /dev/null +++ b/tests/unit/test_validate_config.py @@ -0,0 +1,49 @@ +"""Tests for semantic configuration validation.""" + +import pytest +from common.config_validation import validate_config_semantics + + +def _config() -> dict: + return { + "temporal_scope": { + "start": "2022-01-01", + "end": "2023-01-01", + }, + "gap_filling": { + "advanced": { + "overrides": {}, + } + }, + } + + +def test_accepts_valid_config() -> None: + """Accept a configuration with valid temporal relationships.""" + validate_config_semantics(_config()) + + +def test_rejects_empty_temporal_scope() -> None: + """Reject a temporal scope whose start and end are identical.""" + config = _config() + config["temporal_scope"]["end"] = config["temporal_scope"]["start"] + + with pytest.raises(ValueError, match="Temporal scope"): + validate_config_semantics(config) + + +def test_rejects_reversed_override_period() -> None: + """Reject an advanced override whose end precedes its start.""" + config = _config() + config["gap_filling"]["advanced"]["overrides"] = { + "bad_period": { + "country": "ALB", + "start": "2024-02-01", + "end": "2024-01-01", + "scope": "fill_gaps", + "method": "leave_missing", + } + } + + with pytest.raises(ValueError, match="bad_period"): + validate_config_semantics(config) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 05d4898..8af1c4e 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -1,42 +1,22 @@ """Rules used to download automatic resource files.""" -from datetime import datetime, timedelta - -def _years_in_period( - start: str, - end: str, -) -> tuple[int, ...]: - """Return calendar years intersecting an end-exclusive period.""" - start = datetime.fromisoformat(start) - end = datetime.fromisoformat(end) - - if end <= start: - raise ValueError("Period end must be later than period start.") - - final_included_time = end - timedelta(microseconds=1) - - return tuple( - range( - start.year, - final_included_time.year + 1, - ) - ) - - -NESO_YEARS = _years_in_period( - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], -) - -NESO_RAW_FILES = expand( - "/automatic/neso/" "historic_demand_{year}.csv", - year=NESO_YEARS, -) +rule validate_config_semantics: + output: + "/automatic/config_validation.json", + conda: + "../envs/module.yaml" + params: + validation_config=config + message: + "Validate module configuration semantics." + script: + "../scripts/validate_config.py" rule download_load_entsoe_api: input: + validation="/automatic/config_validation.json", token_entsoe="", output: load="/automatic/load_entsoe_api.parquet", @@ -79,6 +59,8 @@ rule download_load_entsoe_opsd: rule download_load_neso_year: + input: + validation="/automatic/config_validation.json", output: annual_file=("/automatic/neso/" "historic_demand_{year}.csv"), log: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 1edf9f1..0217c78 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -6,9 +6,6 @@ def neso_raw_files(_wildcards): start = datetime.fromisoformat(config["temporal_scope"]["start"]) end = datetime.fromisoformat(config["temporal_scope"]["end"]) - if end <= start: - raise ValueError("Period end must be later than period start.") - final_included_time = end - timedelta(microseconds=1) years = range( @@ -23,6 +20,7 @@ def neso_raw_files(_wildcards): rule prepare_load_opsd: input: + validation="/automatic/config_validation.json", load="/automatic/load_entsoe_opsd.csv", output: load="/automatic/load_opsd_api.parquet", @@ -42,6 +40,7 @@ rule prepare_load_opsd: rule prepare_load_neso: input: + validation="/automatic/config_validation.json", annual_files=neso_raw_files, output: load="/automatic/load_neso.parquet", diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 3dd0e2e..5f91c15 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -1,274 +1,37 @@ -"""Validate and build advanced auxiliary-fill plans.""" +"""Build advanced auxiliary-fill plans.""" from collections.abc import Mapping, Sequence from typing import Any import pandas as pd -from cleaning.advanced.methods.construct_from_sources import ( - METHOD_NAME as CONSTRUCT_FROM_SOURCES, -) -from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE - -FILL_GAPS = "fill_gaps" -OVERWRITE = "overwrite" LEAVE_MISSING = "leave_missing" -def validate_auxiliary_fill_rule(rule_name: str, rule: Mapping[str, Any]) -> None: - """Validate one configured advanced-fill rule.""" - if not isinstance(rule_name, str): - raise TypeError("Advanced-fill rule name must be a string.") - - if not rule_name: - raise ValueError("Advanced-fill rule name must not be empty.") - - if not isinstance(rule, Mapping): - raise TypeError(f"Advanced-fill rule {rule_name!r} must be a mapping.") - - start = _get_required_timestamp(rule, key="start", rule_name=rule_name) - end = _get_required_timestamp(rule, key="end", rule_name=rule_name) - - if end < start: - raise ValueError( - f"Advanced-fill rule {rule_name!r} has an end " - "timestamp before its start timestamp." - ) - - scope = _get_required_string(rule, key="scope", rule_name=rule_name) - method = _get_required_string(rule, key="method", rule_name=rule_name) - - supported_scopes = {FILL_GAPS, OVERWRITE} - - if scope not in supported_scopes: - raise ValueError( - f"Unsupported scope {scope!r} in advanced-fill rule " - f"{rule_name!r}. Expected one of " - f"{sorted(supported_scopes)}." - ) - - supported_methods = {CONSTRUCT_FROM_SOURCES, EXTERNAL_PROFILE, LEAVE_MISSING} - - if method not in supported_methods: - raise ValueError( - f"Unsupported method {method!r} in advanced-fill rule " - f"{rule_name!r}. Expected one of " - f"{sorted(supported_methods)}." - ) - - if method == CONSTRUCT_FROM_SOURCES: - _validate_sources(rule, rule_name=rule_name) - - if "scaling" in rule: - _validate_scaling(rule["scaling"], rule_name=rule_name) - - elif method in {LEAVE_MISSING}: - if "sources" in rule: - raise ValueError( - f"Advanced-fill rule {rule_name!r} uses method " - f"{method!r} and must not define 'sources'." - ) - - -def _validate_sources(rule: Mapping[str, Any], *, rule_name: str) -> None: - """Validate source references for source-based construction.""" - if "sources" not in rule: - raise ValueError( - f"Advanced-fill rule {rule_name!r} using method " - f"{CONSTRUCT_FROM_SOURCES!r} must define 'sources'." - ) - - sources = rule["sources"] - - if not isinstance(sources, Sequence) or isinstance(sources, (str, bytes)): - raise TypeError( - f"'sources' in advanced-fill rule {rule_name!r} " - "must be an ordered sequence." - ) - - if not sources: - raise ValueError( - f"'sources' in advanced-fill rule {rule_name!r} " - "must contain at least one source." - ) - - for position, source in enumerate(sources): - _validate_source(source, rule_name=rule_name, position=position) - - -def _validate_source( - source: object, *, rule_name: str, position: int, context: str = "source" -) -> None: - """Validate one country-period source reference.""" - if not isinstance(source, Mapping): - raise TypeError( - f"{context.capitalize()} {position} in advanced-fill rule " - f"{rule_name!r} must be a mapping." - ) - - _get_required_string( - source, key="country", rule_name=rule_name, context=f"{context} {position}" - ) - - start = _get_required_timestamp( - source, key="start", rule_name=rule_name, context=f"{context} {position}" - ) - end = _get_required_timestamp( - source, key="end", rule_name=rule_name, context=f"{context} {position}" - ) - - if end < start: - raise ValueError( - f"{context.capitalize()} {position} in advanced-fill rule " - f"{rule_name!r} has an end timestamp before its " - "start timestamp." - ) - - weight = source.get("weight", 1.0) - - if not isinstance(weight, int | float): - raise TypeError( - f"Weight for {context} {position} in advanced-fill rule " - f"{rule_name!r} must be numeric." - ) - - if weight <= 0: - raise ValueError( - f"Weight for {context} {position} in advanced-fill rule " - f"{rule_name!r} must be greater than zero." - ) - - -def _get_required_string( - config: Mapping[str, Any], *, key: str, rule_name: str, context: str = "rule" -) -> str: - """Return one required non-empty string field.""" - if key not in config: - raise ValueError( - f"Advanced-fill {context} in rule {rule_name!r} must define {key!r}." - ) - - value = config[key] - - if not isinstance(value, str): - raise TypeError( - f"Advanced-fill {context} field {key!r} in rule " - f"{rule_name!r} must be a string." - ) - - if not value: - raise ValueError( - f"Advanced-fill {context} field {key!r} in rule " - f"{rule_name!r} must not be empty." - ) - - return value - - -def _get_required_timestamp( - config: Mapping[str, Any], *, key: str, rule_name: str, context: str = "rule" -) -> pd.Timestamp: - """Return one required timestamp as a UTC pandas timestamp.""" - if key not in config: - raise ValueError( - f"Advanced-fill {context} in rule {rule_name!r} must define {key!r}." - ) - - try: - timestamp = pd.Timestamp(config[key]) - except (TypeError, ValueError) as error: - raise ValueError( - f"Advanced-fill {context} field {key!r} in rule " - f"{rule_name!r} is not a valid timestamp." - ) from error +def _as_utc_timestamp(value: object) -> pd.Timestamp: + """Return a timestamp normalized to UTC.""" + timestamp = pd.Timestamp(value) if timestamp.tzinfo is None: - timestamp = timestamp.tz_localize("UTC") - else: - timestamp = timestamp.tz_convert("UTC") + return timestamp.tz_localize("UTC") - return timestamp - - -def _validate_scaling(scaling: object, *, rule_name: str) -> None: - """Validate optional scaling configuration.""" - if not isinstance(scaling, Mapping): - raise TypeError( - f"'scaling' in advanced-fill rule {rule_name!r} must be a mapping." - ) - - method = _get_required_string( - scaling, key="method", rule_name=rule_name, context="scaling" - ) - - supported_methods = {"match_energy"} - - if method not in supported_methods: - raise ValueError( - f"Unsupported scaling method {method!r} in " - f"advanced-fill rule {rule_name!r}. Expected one of " - f"{sorted(supported_methods)}." - ) - - if "target_sources" not in scaling: - raise ValueError( - f"Scaling configuration in advanced-fill rule " - f"{rule_name!r} must define 'target_sources'." - ) - - target_sources = scaling["target_sources"] - - if not isinstance(target_sources, Sequence) or isinstance( - target_sources, (str, bytes) - ): - raise TypeError( - f"'target_sources' in advanced-fill rule " - f"{rule_name!r} must be an ordered sequence." - ) - - if not target_sources: - raise ValueError( - f"'target_sources' in advanced-fill rule " - f"{rule_name!r} must contain at least one source." - ) - - for position, source in enumerate(target_sources): - _validate_source( - source, - rule_name=rule_name, - position=position, - context="scaling target source", - ) + return timestamp.tz_convert("UTC") def override_intersects_target_scope( rule: Mapping[str, Any], *, - rule_name: str, target_countries: Sequence[str], target_start: pd.Timestamp, target_end: pd.Timestamp, ) -> bool: """Return whether an advanced override intersects the model scope.""" - rule_start = _get_required_timestamp(rule, key="start", rule_name=rule_name) - rule_end = _get_required_timestamp(rule, key="end", rule_name=rule_name) - - target_start = pd.Timestamp(target_start) - - if target_start.tzinfo is None: - target_start = target_start.tz_localize("UTC") - else: - target_start = target_start.tz_convert("UTC") - - target_end = pd.Timestamp(target_end) - - if target_end.tzinfo is None: - target_end = target_end.tz_localize("UTC") - else: - target_end = target_end.tz_convert("UTC") + rule_start = _as_utc_timestamp(rule["start"]) + rule_end = _as_utc_timestamp(rule["end"]) + target_start = _as_utc_timestamp(target_start) + target_end = _as_utc_timestamp(target_end) country_intersects = rule["country"] in target_countries - period_intersects = rule_start < target_end and rule_end > target_start return country_intersects and period_intersects @@ -281,18 +44,12 @@ def build_auxiliary_fill_plan( target_start: pd.Timestamp, target_end: pd.Timestamp, ) -> pd.DataFrame: - """Validate and normalize configured advanced-fill rules.""" - if not isinstance(rules, Mapping): - raise TypeError("Advanced-fill rules must be provided as a mapping.") - + """Build the advanced-fill plan for overrides intersecting the model scope.""" records: list[dict[str, Any]] = [] for rule_name, rule in rules.items(): - validate_auxiliary_fill_rule(rule_name, rule) - if not override_intersects_target_scope( rule, - rule_name=rule_name, target_countries=target_countries, target_start=target_start, target_end=target_end, @@ -300,33 +57,23 @@ def build_auxiliary_fill_plan( continue method = rule["method"] - - if method == CONSTRUCT_FROM_SOURCES: - status = "ready" - elif method == EXTERNAL_PROFILE: - status = "ready" - elif method == LEAVE_MISSING: - status = "leave_missing" - else: - raise AssertionError(f"Unhandled advanced-fill method: {method!r}") - scaling = rule.get("scaling") records.append( { "rule_name": rule_name, "country": rule["country"], - "target_start": _get_required_timestamp( - rule, key="start", rule_name=rule_name - ), - "target_end": _get_required_timestamp( - rule, key="end", rule_name=rule_name - ), + "target_start": _as_utc_timestamp(rule["start"]), + "target_end": _as_utc_timestamp(rule["end"]), "scope": rule["scope"], "method": method, - "status": status, + "status": ( + "leave_missing" if method == LEAVE_MISSING else "ready" + ), "source_count": len(rule.get("sources", [])), - "scaling_method": (scaling["method"] if scaling is not None else None), + "scaling_method": ( + scaling["method"] if scaling is not None else None + ), } ) @@ -347,6 +94,6 @@ def build_auxiliary_fill_plan( if plan.empty: return plan - return plan.sort_values(["country", "target_start", "rule_name"]).reset_index( - drop=True - ) + return plan.sort_values( + ["country", "target_start", "rule_name"] + ).reset_index(drop=True) diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py new file mode 100644 index 0000000..e4b3043 --- /dev/null +++ b/workflow/scripts/validate_config.py @@ -0,0 +1,15 @@ +"""Validate semantic constraints in the module configuration.""" + +import json +from pathlib import Path + +from common.config_validation import validate_config_semantics + +validation_config = snakemake.params.validation_config + +validate_config_semantics(validation_config) + +Path(snakemake.output[0]).write_text( + json.dumps({"valid": True}, indent=2) + "\n", + encoding="utf-8", +) \ No newline at end of file From d566e83a3a64e860383d3f6ca6491b840c11e874 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 10:51:41 +0200 Subject: [PATCH 084/241] Removed redundant validation from basic gap-filling functions --- workflow/scripts/cleaning/basic/apply.py | 64 +----------------- .../cleaning/basic/rules/average_periods.py | 12 ---- .../cleaning/basic/rules/copy_period.py | 9 --- workflow/scripts/common/config_validation.py | 67 +++++++++++++++++++ 4 files changed, 70 insertions(+), 82 deletions(-) create mode 100644 workflow/scripts/common/config_validation.py diff --git a/workflow/scripts/cleaning/basic/apply.py b/workflow/scripts/cleaning/basic/apply.py index faf62b1..26a3dd3 100644 --- a/workflow/scripts/cleaning/basic/apply.py +++ b/workflow/scripts/cleaning/basic/apply.py @@ -51,7 +51,6 @@ def fill_basic_gaps( Per-cell provenance containing the observed-source identifier, configured gap-filling rule name, or ``missing``. """ - _validate_settings(enabled=enabled, rules=rules) validate_load(load) _validate_cleaning_method(load=load, cleaning_method=cleaning_method) @@ -66,8 +65,8 @@ def fill_basic_gaps( original_gap_duration = calculate_missing_run_durations(load) for rule in rules: - method = _get_method(rule) - rule_name = _get_rule_name(rule) + method = rule["method"] + rule_name = rule["name"] if method == LINEAR_INTERPOLATION: filled, newly_filled = apply_linear_interpolation( @@ -94,6 +93,7 @@ def fill_basic_gaps( ) else: + #Code should never reach this, but its convenient final catch. raise ValueError(f"Unsupported gap-filling method: {method!r}") cleaning_method = cleaning_method.mask(newly_filled, rule_name) @@ -129,33 +129,6 @@ def calculate_missing_run_durations(load: pd.DataFrame) -> pd.DataFrame: return durations -def _get_method(rule: Mapping[str, Any]) -> str: - try: - method = rule["method"] - except KeyError as error: - raise ValueError("Each gap-filling rule must define a 'method'.") from error - - if not isinstance(method, str): - raise TypeError("Gap-filling rule 'method' must be a string.") - - return method - - -def _get_rule_name(rule: Mapping[str, Any]) -> str: - try: - name = rule["name"] - except KeyError as error: - raise ValueError("Each gap-filling rule must define a 'name'.") from error - - if not isinstance(name, str): - raise TypeError("Gap-filling rule 'name' must be a string.") - - if not name: - raise ValueError("Gap-filling rule 'name' must not be empty.") - - return name - - def _log_rule_results( *, rule_name: str, method: str, newly_filled: pd.DataFrame ) -> None: @@ -177,21 +150,9 @@ def _log_rule_results( ) -def _validate_settings(*, enabled: bool, rules: Sequence[Mapping[str, Any]]) -> None: - """Validate basic gap-filling settings.""" - if not isinstance(enabled, bool): - raise TypeError("Basic gap-filling 'enabled' must be a boolean.") - - if not isinstance(rules, Sequence) or isinstance(rules, (str, bytes)): - raise TypeError("Basic gap-filling 'rules' must be an ordered sequence.") - - def _validate_cleaning_method( *, load: pd.DataFrame, cleaning_method: pd.DataFrame ) -> None: - if not isinstance(cleaning_method, pd.DataFrame): - raise TypeError("Cleaning method must be a pandas DataFrame.") - if not cleaning_method.index.equals(load.index): raise ValueError( "Cleaning-method provenance must use the same index as the load data." @@ -201,22 +162,3 @@ def _validate_cleaning_method( raise ValueError( "Cleaning-method provenance must use the same columns as the load data." ) - - missing_observed_provenance = load.notna() & cleaning_method.isna() - - if missing_observed_provenance.any().any(): - count = int(missing_observed_provenance.to_numpy().sum()) - - raise ValueError( - f"Cleaning-method provenance is missing for {count} observed load values." - ) - - provenance_for_missing_values = load.isna() & cleaning_method.notna() - - if provenance_for_missing_values.any().any(): - count = int(provenance_for_missing_values.to_numpy().sum()) - - raise ValueError( - "Cleaning-method provenance is already assigned " - f"to {count} missing load values." - ) diff --git a/workflow/scripts/cleaning/basic/rules/average_periods.py b/workflow/scripts/cleaning/basic/rules/average_periods.py index cc96d5f..0925a2c 100644 --- a/workflow/scripts/cleaning/basic/rules/average_periods.py +++ b/workflow/scripts/cleaning/basic/rules/average_periods.py @@ -34,18 +34,6 @@ def apply_average_periods( offsets = tuple(pd.Timedelta(offset) for offset in source_offsets) - if max_gap <= pd.Timedelta(0): - raise ValueError("'max_gap' must be greater than zero.") - - if len(offsets) < 2: - raise ValueError("'source_offsets' must contain at least two offsets.") - - if len(set(offsets)) != len(offsets): - raise ValueError("'source_offsets' must not contain duplicates.") - - if pd.Timedelta(0) in offsets: - raise ValueError("'source_offsets' must not contain zero.") - eligible = ( load.isna() & original_gap_duration.gt(pd.Timedelta(0)) diff --git a/workflow/scripts/cleaning/basic/rules/copy_period.py b/workflow/scripts/cleaning/basic/rules/copy_period.py index cf78d56..f9bb550 100644 --- a/workflow/scripts/cleaning/basic/rules/copy_period.py +++ b/workflow/scripts/cleaning/basic/rules/copy_period.py @@ -26,15 +26,6 @@ def apply_copy_period( max_gap = pd.Timedelta(max_gap) source_offset = pd.Timedelta(source_offset) - if max_gap <= pd.Timedelta(0): - raise ValueError("'max_gap' must be greater than zero.") - - if source_offset == pd.Timedelta(0): - raise ValueError("'source_offset' must not be zero.") - - if not isinstance(require_complete_source, bool): - raise TypeError("'require_complete_source' must be a boolean.") - eligible = ( load.isna() & original_gap_duration.gt(pd.Timedelta(0)) diff --git a/workflow/scripts/common/config_validation.py b/workflow/scripts/common/config_validation.py new file mode 100644 index 0000000..1bccb7e --- /dev/null +++ b/workflow/scripts/common/config_validation.py @@ -0,0 +1,67 @@ +"""Validate semantic relationships in module configuration.""" + +from collections.abc import Mapping +from typing import Any + +import pandas as pd + + +def validate_config_semantics(config: Mapping[str, Any]) -> None: + """Validate configuration constraints not covered by the JSON schema.""" + _validate_temporal_scope(config["temporal_scope"]) + + advanced = config["gap_filling"]["advanced"] + _validate_advanced_overrides(advanced["overrides"]) + + +def _validate_temporal_scope(scope: Mapping[str, Any]) -> None: + _validate_period( + scope["start"], + scope["end"], + context="Temporal scope", + ) + + +def _validate_advanced_overrides( + overrides: Mapping[str, Mapping[str, Any]], +) -> None: + for rule_name, rule in overrides.items(): + _validate_period( + rule["start"], + rule["end"], + context=f"Advanced-fill rule {rule_name!r}", + ) + + if rule["method"] != "construct_from_sources": + continue + + for position, source in enumerate(rule["sources"]): + _validate_period( + source["start"], + source["end"], + context=f"Source {position} in advanced-fill rule {rule_name!r}", + ) + + scaling = rule.get("scaling") + if scaling is None: + continue + + for position, source in enumerate(scaling["target_sources"]): + _validate_period( + source["start"], + source["end"], + context=( + f"Scaling target source {position} " + f"in advanced-fill rule {rule_name!r}" + ), + ) + + +def _validate_period(start: object, end: object, *, context: str) -> None: + start_timestamp = pd.Timestamp(start) + end_timestamp = pd.Timestamp(end) + + if end_timestamp <= start_timestamp: + raise ValueError( + f"{context} must have an end timestamp after its start timestamp." + ) From 18c8874413b55e23df9faaf9c30604bdec23aec1 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:05:49 +0200 Subject: [PATCH 085/241] Removed redundant validation from advanced gap-filling --- workflow/scripts/cleaning/advanced/apply.py | 15 +++-------- .../methods/construct_from_sources.py | 27 ++++++------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index cd5d84c..da4b130 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -26,12 +26,6 @@ def apply_auxiliary_fill_rule( method = rule["method"] if method == CONSTRUCT_FROM_SOURCES: - if profile is None: - raise ValueError( - f"Advanced-fill rule {rule_name!r} requires " - "a constructed auxiliary profile." - ) - return apply_constructed_profile( load, cleaning_method, @@ -60,9 +54,7 @@ def apply_auxiliary_fill_rule( rule_name=rule_name, ) - if method == LEAVE_MISSING: - return load.copy(), cleaning_method.copy() - + # Cautionary incase of edge cases making is this far. raise ValueError(f"Unsupported advanced-fill method {method!r}.") @@ -88,9 +80,6 @@ def apply_constructed_profile( "Constructed profile index must exactly match the target period." ) - if country not in filled.columns: - raise ValueError(f"Target country {country!r} is not present in load data.") - if scope == "fill_gaps": replace_mask = filled.loc[target_index, country].isna() @@ -98,6 +87,7 @@ def apply_constructed_profile( replace_mask = pd.Series(True, index=target_index) else: + # Redundant but elifs are preferred so this stays. raise ValueError(f"Unsupported advanced fill scope: {scope!r}") replacement_index = target_index[replace_mask.to_numpy()] @@ -169,6 +159,7 @@ def apply_external_profile( replace_index = candidate.index else: + # Redundant but elifs are preferred so this stays. raise ValueError(f"Unsupported advanced fill scope: {scope!r}") filled.loc[replace_index, country] = candidate.loc[replace_index] diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py index 29318a6..53d0583 100644 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py @@ -43,12 +43,6 @@ def _align_leap_day( & (auxiliary.index.day == 1) ] - if len(feb_28) != 24 or len(march_1) != 24: - raise ValueError( - "Cannot construct February 29 because complete " - "February 28 and March 1 source data are required." - ) - leap_values = (feb_28.to_numpy(dtype=float) + march_1.to_numpy(dtype=float)) / 2 insertion_point = (source_values.index.month < 3).sum() @@ -100,9 +94,6 @@ def _match_energy( weighted_energy += float(source_values.sum()) * weight total_weight += weight - if total_weight == 0: - raise ValueError("Scaling source weights must sum to more than zero.") - target_energy = weighted_energy / total_weight profile_energy = float(profile.sum()) @@ -115,16 +106,16 @@ def _match_energy( def _apply_scaling( - profile: pd.Series, *, auxiliary: pd.DataFrame, scaling: Mapping[str, Any] + profile: pd.Series, + *, + auxiliary: pd.DataFrame, + scaling: Mapping[str, Any], ) -> pd.Series: - """Scale a constructed profile according to its configured method.""" - method = scaling["method"] - - if method != "match_energy": - raise ValueError(f"Unsupported auxiliary scaling method: {method!r}.") - + """Scale a constructed profile to configured reference energy.""" return _match_energy( - profile, auxiliary=auxiliary, target_sources=scaling["target_sources"] + profile, + auxiliary=auxiliary, + target_sources=scaling["target_sources"], ) @@ -169,8 +160,6 @@ def construct_from_sources( weighted_sources.append(remapped * weight) weights.append(weight) - if not weighted_sources: - raise ValueError("At least one auxiliary source is required.") weighted_sum = sum(weighted_sources[1:], weighted_sources[0].copy()) From 49f7aeb7f63aa7aa9480b430de9eb7a8325a0610 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:06:08 +0000 Subject: [PATCH 086/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/test_advanced_plan.py | 69 +++---------------- tests/unit/test_validate_config.py | 11 +-- workflow/rules/automatic.smk | 2 +- .../methods/construct_from_sources.py | 10 +-- .../cleaning/advanced/planning/plan.py | 14 ++-- workflow/scripts/cleaning/basic/apply.py | 2 +- workflow/scripts/common/config_validation.py | 14 +--- workflow/scripts/validate_config.py | 5 +- 8 files changed, 25 insertions(+), 102 deletions(-) diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py index ca7698c..0d4ead6 100644 --- a/tests/unit/test_advanced_plan.py +++ b/tests/unit/test_advanced_plan.py @@ -1,7 +1,6 @@ """Tests for advanced auxiliary-fill planning.""" import pandas as pd - from cleaning.advanced.planning.plan import build_auxiliary_fill_plan TARGET_COUNTRIES = ["ALB"] @@ -26,11 +25,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scope": "overwrite", "method": "construct_from_sources", "sources": [ - { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - }, + {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"}, { "country": "MKD", "start": "2023-01-01", @@ -41,11 +36,7 @@ def test_build_auxiliary_fill_plan_normalizes_rules() -> None: "scaling": { "method": "match_energy", "target_sources": [ - { - "country": "ALB", - "start": "2022-01-01", - "end": "2022-12-31 23:00", - } + {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} ], }, }, @@ -116,13 +107,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2023-01-01", - "end": "2023-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], }, "montenegro": { "country": "MNE", @@ -130,13 +115,7 @@ def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: "end": "2023-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2023-01-01", - "end": "2023-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], }, } @@ -159,13 +138,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2020-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2020-01-01", - "end": "2020-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], }, "after": { "country": "ALB", @@ -173,13 +146,7 @@ def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: "end": "2026-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2026-01-01", - "end": "2026-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2026-01-01", "end": "2026-02-01"}], }, } @@ -202,13 +169,7 @@ def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: "end": "2022-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2021-12-01", - "end": "2022-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-02-01"}], } } @@ -231,13 +192,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2022-01-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2021-12-01", - "end": "2022-01-01", - } - ], + "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-01-01"}], }, "starts_at_end": { "country": "ALB", @@ -245,13 +200,7 @@ def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: "end": "2025-02-01", "scope": "overwrite", "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2025-01-01", - "end": "2025-02-01", - } - ], + "sources": [{"country": "GBR", "start": "2025-01-01", "end": "2025-02-01"}], }, } diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py index f3c0cfd..3c3c7d6 100644 --- a/tests/unit/test_validate_config.py +++ b/tests/unit/test_validate_config.py @@ -6,15 +6,8 @@ def _config() -> dict: return { - "temporal_scope": { - "start": "2022-01-01", - "end": "2023-01-01", - }, - "gap_filling": { - "advanced": { - "overrides": {}, - } - }, + "temporal_scope": {"start": "2022-01-01", "end": "2023-01-01"}, + "gap_filling": {"advanced": {"overrides": {}}}, } diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 8af1c4e..691c0c2 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -7,7 +7,7 @@ rule validate_config_semantics: conda: "../envs/module.yaml" params: - validation_config=config + validation_config=config, message: "Validate module configuration semantics." script: diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py index 53d0583..3d1bac9 100644 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py @@ -106,16 +106,11 @@ def _match_energy( def _apply_scaling( - profile: pd.Series, - *, - auxiliary: pd.DataFrame, - scaling: Mapping[str, Any], + profile: pd.Series, *, auxiliary: pd.DataFrame, scaling: Mapping[str, Any] ) -> pd.Series: """Scale a constructed profile to configured reference energy.""" return _match_energy( - profile, - auxiliary=auxiliary, - target_sources=scaling["target_sources"], + profile, auxiliary=auxiliary, target_sources=scaling["target_sources"] ) @@ -160,7 +155,6 @@ def construct_from_sources( weighted_sources.append(remapped * weight) weights.append(weight) - weighted_sum = sum(weighted_sources[1:], weighted_sources[0].copy()) profile = weighted_sum / sum(weights) diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py index 5f91c15..477ffe4 100644 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ b/workflow/scripts/cleaning/advanced/planning/plan.py @@ -67,13 +67,9 @@ def build_auxiliary_fill_plan( "target_end": _as_utc_timestamp(rule["end"]), "scope": rule["scope"], "method": method, - "status": ( - "leave_missing" if method == LEAVE_MISSING else "ready" - ), + "status": ("leave_missing" if method == LEAVE_MISSING else "ready"), "source_count": len(rule.get("sources", [])), - "scaling_method": ( - scaling["method"] if scaling is not None else None - ), + "scaling_method": (scaling["method"] if scaling is not None else None), } ) @@ -94,6 +90,6 @@ def build_auxiliary_fill_plan( if plan.empty: return plan - return plan.sort_values( - ["country", "target_start", "rule_name"] - ).reset_index(drop=True) + return plan.sort_values(["country", "target_start", "rule_name"]).reset_index( + drop=True + ) diff --git a/workflow/scripts/cleaning/basic/apply.py b/workflow/scripts/cleaning/basic/apply.py index 26a3dd3..ef4c8b8 100644 --- a/workflow/scripts/cleaning/basic/apply.py +++ b/workflow/scripts/cleaning/basic/apply.py @@ -93,7 +93,7 @@ def fill_basic_gaps( ) else: - #Code should never reach this, but its convenient final catch. + # Code should never reach this, but its convenient final catch. raise ValueError(f"Unsupported gap-filling method: {method!r}") cleaning_method = cleaning_method.mask(newly_filled, rule_name) diff --git a/workflow/scripts/common/config_validation.py b/workflow/scripts/common/config_validation.py index 1bccb7e..d17a0b3 100644 --- a/workflow/scripts/common/config_validation.py +++ b/workflow/scripts/common/config_validation.py @@ -15,21 +15,13 @@ def validate_config_semantics(config: Mapping[str, Any]) -> None: def _validate_temporal_scope(scope: Mapping[str, Any]) -> None: - _validate_period( - scope["start"], - scope["end"], - context="Temporal scope", - ) + _validate_period(scope["start"], scope["end"], context="Temporal scope") -def _validate_advanced_overrides( - overrides: Mapping[str, Mapping[str, Any]], -) -> None: +def _validate_advanced_overrides(overrides: Mapping[str, Mapping[str, Any]]) -> None: for rule_name, rule in overrides.items(): _validate_period( - rule["start"], - rule["end"], - context=f"Advanced-fill rule {rule_name!r}", + rule["start"], rule["end"], context=f"Advanced-fill rule {rule_name!r}" ) if rule["method"] != "construct_from_sources": diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index e4b3043..771f0e8 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -10,6 +10,5 @@ validate_config_semantics(validation_config) Path(snakemake.output[0]).write_text( - json.dumps({"valid": True}, indent=2) + "\n", - encoding="utf-8", -) \ No newline at end of file + json.dumps({"valid": True}, indent=2) + "\n", encoding="utf-8" +) From 436ccf3b51550faf8ace9b8374f70aa775911328 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:08:20 +0200 Subject: [PATCH 087/241] Advanced methods bug fixes --- workflow/scripts/cleaning/advanced/apply.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index da4b130..a88530a 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -26,6 +26,13 @@ def apply_auxiliary_fill_rule( method = rule["method"] if method == CONSTRUCT_FROM_SOURCES: + + if profile is None: + raise ValueError( + f"Advanced-fill rule {rule_name!r} requires " + "a constructed auxiliary profile." + ) + return apply_constructed_profile( load, cleaning_method, @@ -54,6 +61,9 @@ def apply_auxiliary_fill_rule( rule_name=rule_name, ) + if method == LEAVE_MISSING: + return load.copy(), cleaning_method.copy() + # Cautionary incase of edge cases making is this far. raise ValueError(f"Unsupported advanced-fill method {method!r}.") From 38c696d6ba273a88dd35877719055f19904224cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:08:59 +0000 Subject: [PATCH 088/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/cleaning/advanced/apply.py | 1 - 1 file changed, 1 deletion(-) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index a88530a..6f7a9b2 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -26,7 +26,6 @@ def apply_auxiliary_fill_rule( method = rule["method"] if method == CONSTRUCT_FROM_SOURCES: - if profile is None: raise ValueError( f"Advanced-fill rule {rule_name!r} requires " From 1789e1de61a25f0fa169b8cc92212bd80bdcabec Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:23:18 +0200 Subject: [PATCH 089/241] Removed redundant validation from cleaning functions. --- workflow/scripts/cleaning/combine_sources.py | 17 ----- workflow/scripts/cleaning/plot_timeline.py | 74 +------------------- workflow/scripts/cleaning/provenance.py | 30 -------- 3 files changed, 3 insertions(+), 118 deletions(-) diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py index 6daf294..e90f534 100644 --- a/workflow/scripts/cleaning/combine_sources.py +++ b/workflow/scripts/cleaning/combine_sources.py @@ -11,15 +11,6 @@ def combine_sources( sources: Mapping[str, pd.DataFrame], *, priority: Sequence[str] ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: """Combine sources and record source and cleaning-method provenance.""" - if not priority: - raise ValueError("At least one demand source must be configured.") - - missing_sources = [source for source in priority if source not in sources] - - if missing_sources: - raise ValueError( - f"Configured demand sources were not supplied: {missing_sources}" - ) selected = {source: sources[source] for source in priority} @@ -88,14 +79,6 @@ def combine_auxiliary_sources( empty = pd.DataFrame() return empty, empty.copy(), empty.copy() - unexpected_sources = set(loads) - set(priority) - - if unexpected_sources: - raise ValueError( - "Auxiliary sources were supplied but are not configured in " - f"source priority: {sorted(unexpected_sources)}." - ) - available_priority = [source for source in priority if source in loads] columns = sorted({column for load in loads.values() for column in load.columns}) diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index ff6a699..22fa06d 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -41,12 +41,6 @@ def main( source_names=source_names, gap_filling_config=gap_filling_config ) - _validate_provenance_metadata( - cleaning_method=cleaning_method, - cleaning_method_rank=cleaning_method_rank, - metadata=metadata, - ) - rank_colours = _build_rank_colours(metadata) background, background_cmap = _encode_rank_background( @@ -128,55 +122,9 @@ def _validate_alignment( raise ValueError(f"{name} does not use the same time index as demand.") if not frame.columns.equals(demand.columns): - raise ValueError(f"{name} does not use the same country columns as demand.") - - if not isinstance(demand.index, pd.DatetimeIndex): - raise TypeError("Demand must use a pandas DatetimeIndex.") - - if demand.index.has_duplicates: - raise ValueError("Demand timestamps must not contain duplicates.") - - if not demand.index.is_monotonic_increasing: - raise ValueError("Demand timestamps must be sorted.") - - if demand.columns.has_duplicates: - raise ValueError("Demand countries must not contain duplicates.") - - -def _validate_provenance_metadata( - *, - cleaning_method: pd.DataFrame, - cleaning_method_rank: pd.DataFrame, - metadata: pd.DataFrame, -) -> None: - """Validate observed provenance against configured metadata.""" - methods = cleaning_method.stack(future_stack=True).rename("cleaning_method") - - ranks = cleaning_method_rank.stack(future_stack=True).rename("cleaning_method_rank") - - present = pd.concat([methods, ranks], axis=1).dropna().drop_duplicates() - - present["cleaning_method_rank"] = present["cleaning_method_rank"].astype(int) - - configured = metadata[["cleaning_method", "cleaning_method_rank"]] - - checked = present.merge( - configured, - on=["cleaning_method", "cleaning_method_rank"], - how="left", - indicator=True, - ) - - unknown = checked.loc[ - checked["_merge"] == "left_only", ["cleaning_method", "cleaning_method_rank"] - ] - - if not unknown.empty: - raise ValueError( - "Cleaning provenance contains method/rank pairs " - "that are not defined by the configuration:\n" - f"{unknown.to_string(index=False)}" - ) + raise ValueError( + f"{name} does not use the same country columns as demand." + ) def _build_rank_colours( @@ -193,9 +141,6 @@ def _build_rank_colours( missing = ordered.loc[ordered["category"] == "missing"] - if observed.empty: - raise ValueError("At least one observed source must be configured.") - # Primary source is white. Subsequent observed sources # become gradually darker, but remain very light so that # the black demand trace stays clearly visible. @@ -242,19 +187,6 @@ def _encode_rank_background( encoded = cleaning_method_rank.apply(lambda column: column.map(rank_to_code)) - if encoded.isna().any().any(): - present_ranks = set( - cleaning_method_rank.stack(future_stack=True).dropna().astype(int).unique() - ) - - unknown_ranks = sorted(present_ranks - set(rank_to_code)) - - raise ValueError( - "Cleaning-method rank matrix contains ranks " - "without metadata: " - f"{unknown_ranks}" - ) - colour_list = [rank_colours[rank] for rank in rank_order] # Input frames are time × country, whereas imshow expects diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py index aff4108..7541fc5 100644 --- a/workflow/scripts/cleaning/provenance.py +++ b/workflow/scripts/cleaning/provenance.py @@ -7,40 +7,10 @@ import pandas as pd -def validate_rule_names( - *, rules: Sequence[Mapping[str, Any]], source_priority: Sequence[str] -) -> None: - """Validate rule-name uniqueness and reserved-name collisions.""" - names = [str(rule["name"]) for rule in rules] - - counts = Counter(names) - duplicates = sorted(name for name, count in counts.items() if count > 1) - - if duplicates: - raise ValueError( - f"Gap-filling rule names must be unique. Duplicate names: {duplicates}" - ) - - reserved_names = { - "missing", - *(f"observed_{source_name}" for source_name in source_priority), - } - - collisions = sorted(set(names) & reserved_names) - - if collisions: - raise ValueError( - "Gap-filling rule names conflict with reserved " - f"cleaning-method names: {collisions}" - ) - - def build_cleaning_method_ranks( *, source_priority: Sequence[str], rules: Sequence[Mapping[str, Any]] ) -> dict[str, int]: """Build cleaning-method ranks from configured order.""" - validate_rule_names(rules=rules, source_priority=source_priority) - ranks: dict[str, int] = {} for rank, source_name in enumerate(source_priority): From bf51c9e16b2a0f346bfdc7f1a20269c9c0d5b5cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:24:27 +0000 Subject: [PATCH 090/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/cleaning/combine_sources.py | 1 - workflow/scripts/cleaning/plot_timeline.py | 4 +--- workflow/scripts/cleaning/provenance.py | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py index e90f534..b338545 100644 --- a/workflow/scripts/cleaning/combine_sources.py +++ b/workflow/scripts/cleaning/combine_sources.py @@ -11,7 +11,6 @@ def combine_sources( sources: Mapping[str, pd.DataFrame], *, priority: Sequence[str] ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: """Combine sources and record source and cleaning-method provenance.""" - selected = {source: sources[source] for source in priority} _validate_source_alignment(selected) diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/cleaning/plot_timeline.py index 22fa06d..3c70e2e 100644 --- a/workflow/scripts/cleaning/plot_timeline.py +++ b/workflow/scripts/cleaning/plot_timeline.py @@ -122,9 +122,7 @@ def _validate_alignment( raise ValueError(f"{name} does not use the same time index as demand.") if not frame.columns.equals(demand.columns): - raise ValueError( - f"{name} does not use the same country columns as demand." - ) + raise ValueError(f"{name} does not use the same country columns as demand.") def _build_rank_colours( diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py index 7541fc5..b1a04b2 100644 --- a/workflow/scripts/cleaning/provenance.py +++ b/workflow/scripts/cleaning/provenance.py @@ -1,6 +1,5 @@ """Cleaning-method provenance and ranking helpers.""" -from collections import Counter from collections.abc import Mapping, Sequence from typing import Any From f19e011e02af9aca3fe0bde17b455969d1b80df4 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:31:24 +0200 Subject: [PATCH 091/241] Added rule-name collissions to validator --- tests/unit/test_validate_config.py | 54 ++++++++++++++++++-- workflow/scripts/common/config_validation.py | 27 +++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py index 3c3c7d6..1ae3d7b 100644 --- a/tests/unit/test_validate_config.py +++ b/tests/unit/test_validate_config.py @@ -1,18 +1,34 @@ """Tests for semantic configuration validation.""" import pytest + from common.config_validation import validate_config_semantics def _config() -> dict: + """Return a structurally valid configuration for semantic validation.""" return { - "temporal_scope": {"start": "2022-01-01", "end": "2023-01-01"}, - "gap_filling": {"advanced": {"overrides": {}}}, + "temporal_scope": { + "start": "2022-01-01", + "end": "2023-01-01", + }, + "load_sources": [ + "entsoe_api", + "neso", + ], + "gap_filling": { + "basic": { + "rules": [], + }, + "advanced": { + "overrides": {}, + }, + }, } def test_accepts_valid_config() -> None: - """Accept a configuration with valid temporal relationships.""" + """Accept a configuration with valid semantic relationships.""" validate_config_semantics(_config()) @@ -40,3 +56,35 @@ def test_rejects_reversed_override_period() -> None: with pytest.raises(ValueError, match="bad_period"): validate_config_semantics(config) + + +def test_rejects_basic_rule_name_reserved_for_observed_source() -> None: + """Reject a basic rule name reserved for observed-source provenance.""" + config = _config() + config["gap_filling"]["basic"]["rules"] = [ + { + "name": "observed_entsoe_api", + "method": "linear_interpolation", + "max_gap": "3h", + } + ] + + with pytest.raises(ValueError, match="conflict with reserved"): + validate_config_semantics(config) + + +def test_rejects_advanced_rule_name_missing() -> None: + """Reject an advanced override using the reserved missing provenance name.""" + config = _config() + config["gap_filling"]["advanced"]["overrides"] = { + "missing": { + "country": "ALB", + "start": "2022-03-01", + "end": "2022-04-01", + "scope": "fill_gaps", + "method": "leave_missing", + } + } + + with pytest.raises(ValueError, match="conflict with reserved"): + validate_config_semantics(config) diff --git a/workflow/scripts/common/config_validation.py b/workflow/scripts/common/config_validation.py index d17a0b3..ccde5e9 100644 --- a/workflow/scripts/common/config_validation.py +++ b/workflow/scripts/common/config_validation.py @@ -9,9 +9,9 @@ def validate_config_semantics(config: Mapping[str, Any]) -> None: """Validate configuration constraints not covered by the JSON schema.""" _validate_temporal_scope(config["temporal_scope"]) - advanced = config["gap_filling"]["advanced"] _validate_advanced_overrides(advanced["overrides"]) + _validate_cleaning_method_names(config) def _validate_temporal_scope(scope: Mapping[str, Any]) -> None: @@ -57,3 +57,28 @@ def _validate_period(start: object, end: object, *, context: str) -> None: raise ValueError( f"{context} must have an end timestamp after its start timestamp." ) + + +def _validate_cleaning_method_names(config: Mapping[str, Any]) -> None: + """Reject configured rule names reserved for cleaning provenance.""" + source_names = config["load_sources"] + basic_rules = config["gap_filling"]["basic"]["rules"] + advanced_overrides = config["gap_filling"]["advanced"]["overrides"] + + reserved_names = { + "missing", + *(f"observed_{source_name}" for source_name in source_names), + } + + rule_names = [ + *(rule["name"] for rule in basic_rules), + *advanced_overrides.keys(), + ] + + collisions = sorted(set(rule_names) & reserved_names) + + if collisions: + raise ValueError( + "Gap-filling rule names conflict with reserved " + f"cleaning-method names: {collisions}" + ) From 434281ace2ffe433db7509a0dbd85857630103e8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:32:01 +0000 Subject: [PATCH 092/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/test_validate_config.py | 20 +++----------------- workflow/scripts/common/config_validation.py | 5 +---- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py index 1ae3d7b..b452198 100644 --- a/tests/unit/test_validate_config.py +++ b/tests/unit/test_validate_config.py @@ -1,29 +1,15 @@ """Tests for semantic configuration validation.""" import pytest - from common.config_validation import validate_config_semantics def _config() -> dict: """Return a structurally valid configuration for semantic validation.""" return { - "temporal_scope": { - "start": "2022-01-01", - "end": "2023-01-01", - }, - "load_sources": [ - "entsoe_api", - "neso", - ], - "gap_filling": { - "basic": { - "rules": [], - }, - "advanced": { - "overrides": {}, - }, - }, + "temporal_scope": {"start": "2022-01-01", "end": "2023-01-01"}, + "load_sources": ["entsoe_api", "neso"], + "gap_filling": {"basic": {"rules": []}, "advanced": {"overrides": {}}}, } diff --git a/workflow/scripts/common/config_validation.py b/workflow/scripts/common/config_validation.py index ccde5e9..766fd19 100644 --- a/workflow/scripts/common/config_validation.py +++ b/workflow/scripts/common/config_validation.py @@ -70,10 +70,7 @@ def _validate_cleaning_method_names(config: Mapping[str, Any]) -> None: *(f"observed_{source_name}" for source_name in source_names), } - rule_names = [ - *(rule["name"] for rule in basic_rules), - *advanced_overrides.keys(), - ] + rule_names = [*(rule["name"] for rule in basic_rules), *advanced_overrides.keys()] collisions = sorted(set(rule_names) & reserved_names) From 39161ef87edb4423ada6860c4aa2040ece2ff409 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:40:34 +0200 Subject: [PATCH 093/241] Removed redundant validation. --- .../scripts/construct_auxiliary_profile.py | 3 -- workflow/scripts/download_load_entsoe_api.py | 13 +------ workflow/scripts/download_load_neso.py | 38 ++++++------------- .../scripts/plot_readme_cleaning_example.py | 7 ---- workflow/scripts/prepare_load_neso.py | 12 ------ 5 files changed, 13 insertions(+), 60 deletions(-) diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 29e402b..4491155 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -7,9 +7,6 @@ loads = [pd.read_parquet(path) for path in snakemake.input.sources] -if not loads: - raise ValueError("No cleaned auxiliary data were supplied.") - auxiliary = loads[0].copy() for load in loads[1:]: diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py index 38ed5df..ca9f0a5 100644 --- a/workflow/scripts/download_load_entsoe_api.py +++ b/workflow/scripts/download_load_entsoe_api.py @@ -152,22 +152,11 @@ def main(start, end, country_codes, token, output_load, workers): df = df.resample("1h").mean() - # Reindexing adds security to the ENTSO-E download. + # Align downloaded data to the requested hourly target grid. target_index = build_hourly_index(start=start, end=end) df = df.reindex(index=target_index, columns=country_codes) - non_numeric_columns = df.select_dtypes(exclude="number").columns - - invalid = { - column: df[column].dropna().head().tolist() - for column in non_numeric_columns - if not df[column].dropna().empty - } - - if invalid: - raise TypeError(f"ENTSO-E load contains non-numeric values: {invalid}") - # Pre-cleaning. Replace empty object columns with NaN # columns to allow data-source combining. df = df.astype(float) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 93ed05c..9ac5bf0 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -120,36 +120,22 @@ def _download_file(*, url: str, output_path: Path) -> None: raise -def download_annual_files(*, years: list[int], output_paths: list[str | Path]) -> None: - """Discover and download the NESO CSV for each requested year.""" - if len(years) != len(output_paths): - raise ValueError( - "The number of NESO years must match the number of output paths." - ) - - if len(set(years)) != len(years): - raise ValueError(f"NESO years must be unique: {years}") +def download_annual_file(*, year: int, output_path: str | Path) -> None: + """Discover and download one annual NESO historic-demand CSV.""" + output_path = Path(output_path) dataset = _get_historic_demand_dataset() - for year, raw_output_path in zip(years, output_paths, strict=True): - output_path = Path(raw_output_path) - - if output_path.exists() and output_path.stat().st_size > 0: - logger.info("Retaining existing NESO file for %s: %s", year, output_path) - continue - - logger.info("Selecting NESO historic-demand resource for %s.", year) - - resource = _select_csv_resource(dataset, year=year) + logger.info("Selecting NESO historic-demand resource for %s.", year) - url = str(resource["url"]) + resource = _select_csv_resource(dataset, year=year) + url = str(resource["url"]) - logger.info("Downloading NESO historic demand for %s from %s.", year, url) + logger.info("Downloading NESO historic demand for %s from %s.", year, url) - _download_file(url=url, output_path=output_path) + _download_file(url=url, output_path=output_path) - logger.info("Saved NESO historic demand for %s to %s.", year, output_path) + logger.info("Saved NESO historic demand for %s to %s.", year, output_path) if __name__ == "__main__": @@ -157,7 +143,7 @@ def download_annual_files(*, years: list[int], output_paths: list[str | Path]) - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - download_annual_files( - years=[int(snakemake.params.year)], - output_paths=[Path(snakemake.output.annual_file)], + download_annual_file( + year=int(snakemake.params.year), + output_path=snakemake.output.annual_file, ) diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py index 2993afe..e993f90 100644 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -16,7 +16,6 @@ _encode_rank_background, _plot_cleaning_background, _validate_alignment, - _validate_provenance_metadata, ) DEMAND_PATH = Path("resources/module/resources/automatic/load_cleaned.parquet") @@ -122,12 +121,6 @@ def main() -> None: source_names=SOURCE_NAMES, gap_filling_config=GAP_FILLING_CONFIG ) - _validate_provenance_metadata( - cleaning_method=cleaning_method, - cleaning_method_rank=cleaning_method_rank, - metadata=metadata, - ) - rank_colours = _build_rank_colours(metadata) background, background_cmap = _encode_rank_background( diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 5be5dd9..04608b3 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -27,9 +27,6 @@ def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: for raw_path in paths: path = Path(raw_path) - if not path.exists(): - raise FileNotFoundError(f"NESO input file does not exist: {path}") - logger.info("Reading NESO historic demand from %s.", path) try: @@ -45,9 +42,6 @@ def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: frames.append(frame) - if not frames: - raise ValueError("At least one NESO input file is required.") - return pd.concat(frames, ignore_index=True) @@ -77,9 +71,6 @@ def _prepare_half_hourly_demand(raw: pd.DataFrame) -> pd.Series: f"NESO data contain duplicate UTC timestamps: {duplicate_timestamps[:10]}" ) - if not half_hourly.index.is_monotonic_increasing: - raise ValueError("Prepared NESO timestamps are not sorted.") - return half_hourly @@ -111,9 +102,6 @@ def prepare_load_neso( """Prepare NESO demand on the common time-country target grid.""" target_countries = list(countries) - if len(target_countries) != len(set(target_countries)): - raise ValueError("Target country codes must be unique.") - target_index = build_hourly_index(start=temporal_start, end=temporal_end) result = pd.DataFrame(index=target_index, columns=target_countries, dtype=float) From fc29043fc7f186bffac5628d3073b283b746b210 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:40:54 +0000 Subject: [PATCH 094/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/download_load_neso.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 9ac5bf0..04fac8b 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -144,6 +144,5 @@ def download_annual_file(*, year: int, output_path: str | Path) -> None: logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") download_annual_file( - year=int(snakemake.params.year), - output_path=snakemake.output.annual_file, + year=int(snakemake.params.year), output_path=snakemake.output.annual_file ) From 9a4099516aa76004a60834e8112a6337bbd0c6eb Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 11:51:35 +0200 Subject: [PATCH 095/241] More redundant validation removal. --- tests/unit/test_advanced_source_requests.py | 23 ++++++------- tests/unit/test_plan_auxiliary_data.py | 18 ---------- .../cleaning/advanced/planning/execution.py | 33 +++++-------------- .../advanced/planning/source_requests.py | 20 ----------- workflow/scripts/cleaning/sources/neso.py | 9 ----- 5 files changed, 20 insertions(+), 83 deletions(-) diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index 803ab23..49e4f93 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -59,19 +59,20 @@ def test_empty_requirements_return_empty_source_request_schema() -> None: assert list(result.columns) == SOURCE_REQUEST_COLUMNS -def test_unknown_source_is_rejected() -> None: - requirements = pd.DataFrame(columns=["country", "start", "end"]) - - with pytest.raises(ValueError, match="Unsupported auxiliary load sources"): - build_auxiliary_source_requests(requirements, source_names=["mystery_source"]) - - -def test_duplicate_source_names_are_rejected() -> None: - requirements = pd.DataFrame(columns=["country", "start", "end"]) +def test_unknown_source_is_rejected_when_planning_request() -> None: + """Reject a source without defined country applicability.""" + requirements = pd.DataFrame( + { + "country": ["ALB"], + "start": [pd.Timestamp("2020-01-01", tz="UTC")], + "end": [pd.Timestamp("2020-02-01", tz="UTC")], + } + ) - with pytest.raises(ValueError, match="must be unique"): + with pytest.raises(ValueError, match="Unsupported auxiliary load source"): build_auxiliary_source_requests( - requirements, source_names=["entsoe_api", "entsoe_api"] + requirements, + source_names=["mystery_source"], ) diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 3f59c3c..69466c8 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -295,24 +295,6 @@ def test_batch_years_use_half_open_period_semantics() -> None: assert result["batches"][0]["years"] == [2020] -def test_plan_rejects_unknown_fill_plan_rule() -> None: - with pytest.raises(ValueError, match="references unknown advanced overrides"): - build_advanced_execution_plan( - fill_plan=_fill_plan(["missing_rule"]), - gap_filling_config=_config({}), - source_names=["entsoe_api"], - ) - - -def test_plan_rejects_duplicate_fill_plan_rules() -> None: - with pytest.raises(ValueError, match="contains duplicate rule names"): - build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania", "fill_albania"]), - gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=["entsoe_api"], - ) - - def test_plan_rejects_unresolvable_auxiliary_group() -> None: with pytest.raises( ValueError, match="Expected exactly one auxiliary group covering" diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index 939621b..ffe5ae1 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -102,32 +102,18 @@ def _empty_execution_plan() -> dict[str, object]: def _get_ordered_active_rule_names( - fill_plan: pd.DataFrame, *, overrides: Mapping[str, Mapping[str, Any]] + fill_plan: pd.DataFrame, + *, + overrides: Mapping[str, Mapping[str, Any]], ) -> list[str]: """Return active rule names in configured execution order.""" - if "rule_name" not in fill_plan.columns: - raise ValueError("Auxiliary fill plan must define a 'rule_name' column.") - - duplicate_rule_names = sorted( - fill_plan.loc[fill_plan["rule_name"].duplicated(), "rule_name"] - .drop_duplicates() - .tolist() - ) - if duplicate_rule_names: - raise ValueError( - "Auxiliary fill plan contains duplicate rule names: " - f"{duplicate_rule_names}." - ) - active_rule_names = set(fill_plan["rule_name"]) - unknown_rule_names = active_rule_names - set(overrides) - if unknown_rule_names: - raise ValueError( - "Auxiliary fill plan references unknown advanced " - f"overrides: {sorted(unknown_rule_names)}." - ) - return [rule_name for rule_name in overrides if rule_name in active_rule_names] + return [ + rule_name + for rule_name in overrides + if rule_name in active_rule_names + ] def _serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: @@ -135,9 +121,6 @@ def _serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: start = pd.Timestamp(batch["start"]) end = pd.Timestamp(batch["end"]) - if end <= start: - raise ValueError("Auxiliary batch end must be later than its start.") - final_included_time = end - pd.Timedelta(nanoseconds=1) return { diff --git a/workflow/scripts/cleaning/advanced/planning/source_requests.py b/workflow/scripts/cleaning/advanced/planning/source_requests.py index efc37bb..705e1bd 100644 --- a/workflow/scripts/cleaning/advanced/planning/source_requests.py +++ b/workflow/scripts/cleaning/advanced/planning/source_requests.py @@ -9,15 +9,10 @@ SOURCE_REQUEST_COLUMNS = ["source", "country", "start", "end"] -SUPPORTED_SOURCES = {"entsoe_api", "neso", "opsd_api"} - - def build_auxiliary_source_requests( requirements: pd.DataFrame, *, source_names: Sequence[str] ) -> pd.DataFrame: """Map auxiliary requirements onto applicable configured sources.""" - _validate_source_names(source_names) - if requirements.empty: return pd.DataFrame(columns=SOURCE_REQUEST_COLUMNS) @@ -51,21 +46,6 @@ def _source_supports_country(source_name: str, country: str) -> bool: raise ValueError(f"Unsupported auxiliary load source: {source_name!r}") -def _validate_source_names(source_names: Sequence[str]) -> None: - """Validate configured sources used for auxiliary acquisition.""" - unknown = [ - source_name - for source_name in source_names - if source_name not in SUPPORTED_SOURCES - ] - - if unknown: - raise ValueError(f"Unsupported auxiliary load sources: {unknown}") - - if len(source_names) != len(set(source_names)): - raise ValueError("Auxiliary load source names must be unique.") - - def build_auxiliary_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: """Group compatible auxiliary source requests into batches.""" if requests.empty: diff --git a/workflow/scripts/cleaning/sources/neso.py b/workflow/scripts/cleaning/sources/neso.py index 1293d17..30490bb 100644 --- a/workflow/scripts/cleaning/sources/neso.py +++ b/workflow/scripts/cleaning/sources/neso.py @@ -5,15 +5,6 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: """Convert NESO settlement dates and periods to UTC timestamps.""" - required_columns = {"SETTLEMENT_DATE", "SETTLEMENT_PERIOD", "ND"} - - missing_columns = required_columns - set(data.columns) - - if missing_columns: - raise ValueError( - f"NESO data are missing required columns: {sorted(missing_columns)}" - ) - prepared = data.copy() prepared["SETTLEMENT_DATE"] = pd.to_datetime( From 12ce7d847b504ab779638cfcd8dce9e0850d4ff9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:52:10 +0000 Subject: [PATCH 096/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/test_advanced_source_requests.py | 5 +---- .../scripts/cleaning/advanced/planning/execution.py | 10 ++-------- .../cleaning/advanced/planning/source_requests.py | 1 + 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index 49e4f93..44cf8e1 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -70,10 +70,7 @@ def test_unknown_source_is_rejected_when_planning_request() -> None: ) with pytest.raises(ValueError, match="Unsupported auxiliary load source"): - build_auxiliary_source_requests( - requirements, - source_names=["mystery_source"], - ) + build_auxiliary_source_requests(requirements, source_names=["mystery_source"]) def test_build_batch_id_is_independent_of_country_order() -> None: diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index ffe5ae1..3517c21 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -102,18 +102,12 @@ def _empty_execution_plan() -> dict[str, object]: def _get_ordered_active_rule_names( - fill_plan: pd.DataFrame, - *, - overrides: Mapping[str, Mapping[str, Any]], + fill_plan: pd.DataFrame, *, overrides: Mapping[str, Mapping[str, Any]] ) -> list[str]: """Return active rule names in configured execution order.""" active_rule_names = set(fill_plan["rule_name"]) - return [ - rule_name - for rule_name in overrides - if rule_name in active_rule_names - ] + return [rule_name for rule_name in overrides if rule_name in active_rule_names] def _serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: diff --git a/workflow/scripts/cleaning/advanced/planning/source_requests.py b/workflow/scripts/cleaning/advanced/planning/source_requests.py index 705e1bd..b6bf330 100644 --- a/workflow/scripts/cleaning/advanced/planning/source_requests.py +++ b/workflow/scripts/cleaning/advanced/planning/source_requests.py @@ -9,6 +9,7 @@ SOURCE_REQUEST_COLUMNS = ["source", "country", "start", "end"] + def build_auxiliary_source_requests( requirements: pd.DataFrame, *, source_names: Sequence[str] ) -> pd.DataFrame: From 073f1dd06f32d15de96a5ac1d5d99dc921469314 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 12:32:15 +0200 Subject: [PATCH 097/241] Added local caching capability for OPSD download to minimise OPSD downloads given 1.35GB file. --- workflow/internal/settings.yaml | 2 +- workflow/rules/advanced.smk | 2 +- workflow/rules/automatic.smk | 14 ++-- workflow/rules/clean.smk | 2 +- workflow/scripts/download_load_opsd.py | 100 +++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 workflow/scripts/download_load_opsd.py diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index 0fa4113..2849f4d 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -2,7 +2,7 @@ resources: automatic: # Links for automatically downloaded files - load_entsoe_opsd: "https://data.open-power-system-data.org/time_series/2019-06-05/time_series_60min_stacked.csv" + load_opsd: "https://data.open-power-system-data.org/time_series/2019-06-05/time_series_60min_stacked.csv" population: "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/GHSL/GHS_POP_GLOBE_R2023A/GHS_POP_E2020_GLOBE_R2023A_54009_1000/V1-0/GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.zip" population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index f0f24a1..4bddd41 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -165,7 +165,7 @@ rule download_auxiliary_load_entsoe_api: rule prepare_auxiliary_load_opsd: input: - load=rules.download_load_entsoe_opsd.output.load, + load=rules.download_load_opsd.output.load, plan=auxiliary_acquisition_plan, output: load=("/automatic/" "auxiliary/opsd_api/" "{batch_id}.parquet"), diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 691c0c2..0e60227 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -40,22 +40,20 @@ rule download_load_entsoe_api: "../scripts/download_load_entsoe_api.py" -rule download_load_entsoe_opsd: +rule download_load_opsd: output: - load="/automatic/load_entsoe_opsd.csv", + load="/automatic/load_opsd.csv", log: - "/download_load_entsoe_opsd.log", + "/download_load_opsd.log", localrule: True conda: "../envs/module.yaml" params: - url_load=internal["resources"]["automatic"]["load_entsoe_opsd"], + url=internal["resources"]["automatic"]["load_opsd"], message: "Download load profiles from Open Power System Data (OPSD)." - shell: - """ - curl -sSLo {output.load:q} {params.url_load:q} - """ + script: + "../scripts/download_load_opsd.py" rule download_load_neso_year: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 0217c78..af7c808 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -21,7 +21,7 @@ def neso_raw_files(_wildcards): rule prepare_load_opsd: input: validation="/automatic/config_validation.json", - load="/automatic/load_entsoe_opsd.csv", + load="/automatic/load_opsd.csv", output: load="/automatic/load_opsd_api.parquet", log: diff --git a/workflow/scripts/download_load_opsd.py b/workflow/scripts/download_load_opsd.py new file mode 100644 index 0000000..e06b893 --- /dev/null +++ b/workflow/scripts/download_load_opsd.py @@ -0,0 +1,100 @@ +"""Download and cache the fixed OPSD electricity-demand snapshot.""" + +import logging +import shutil +import sys +from pathlib import Path +from urllib.request import Request, urlopen + +import pandas as pd + +logger = logging.getLogger(__name__) + +USER_AGENT = "modelblocks-module-demand-electricity/OPSD downloader" + +REQUIRED_COLUMNS = { + "utc_timestamp", + "region", + "variable", + "attribute", + "data", +} + + +def _is_valid_cached_snapshot(path: Path) -> bool: + """Return whether an existing OPSD snapshot is suitable for reuse.""" + if not path.exists() or path.stat().st_size == 0: + return False + + try: + columns = set(pd.read_csv(path, nrows=0).columns) + except (OSError, ValueError): + return False + + return REQUIRED_COLUMNS.issubset(columns) + + +def download_opsd(*, url: str, output_path: str | Path) -> None: + """Download the OPSD snapshot unless a valid cached copy exists.""" + output_path = Path(output_path) + + if _is_valid_cached_snapshot(output_path): + logger.info("Using cached OPSD snapshot: %s", output_path) + return + + if output_path.exists(): + logger.warning("Cached OPSD snapshot is invalid; downloading a replacement.") + output_path.unlink() + + output_path.parent.mkdir(parents=True, exist_ok=True) + + temporary_path = output_path.with_suffix(output_path.suffix + ".part") + temporary_path.unlink(missing_ok=True) + + logger.info("Downloading OPSD snapshot from %s.", url) + + request = Request(url, headers={"User-Agent": USER_AGENT}) + + try: + with ( + urlopen(request, timeout=300) as response, + temporary_path.open("wb") as output_file, + ): + content_length = response.headers.get("Content-Length") + shutil.copyfileobj(response, output_file) + + downloaded_size = temporary_path.stat().st_size + + if content_length is not None and downloaded_size != int(content_length): + raise RuntimeError( + "OPSD download is incomplete: " + f"received {downloaded_size} of {content_length} bytes." + ) + + if not _is_valid_cached_snapshot(temporary_path): + raise RuntimeError( + "Downloaded OPSD snapshot does not contain the expected CSV structure." + ) + + temporary_path.replace(output_path) + + except Exception: + temporary_path.unlink(missing_ok=True) + raise + + logger.info( + "Saved OPSD snapshot to %s (%s bytes).", + output_path, + output_path.stat().st_size, + ) + + +if __name__ == "__main__": + sys.stderr = open(snakemake.log[0], "w", buffering=1) + + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + download_opsd( + url=snakemake.params.url, + output_path=snakemake.output.load, + ) From d601ef1401aa2ca4584c4c171c9ba1bdace930fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:33:10 +0000 Subject: [PATCH 098/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/download_load_opsd.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/workflow/scripts/download_load_opsd.py b/workflow/scripts/download_load_opsd.py index e06b893..24814e0 100644 --- a/workflow/scripts/download_load_opsd.py +++ b/workflow/scripts/download_load_opsd.py @@ -12,13 +12,7 @@ USER_AGENT = "modelblocks-module-demand-electricity/OPSD downloader" -REQUIRED_COLUMNS = { - "utc_timestamp", - "region", - "variable", - "attribute", - "data", -} +REQUIRED_COLUMNS = {"utc_timestamp", "region", "variable", "attribute", "data"} def _is_valid_cached_snapshot(path: Path) -> bool: @@ -83,9 +77,7 @@ def download_opsd(*, url: str, output_path: str | Path) -> None: raise logger.info( - "Saved OPSD snapshot to %s (%s bytes).", - output_path, - output_path.stat().st_size, + "Saved OPSD snapshot to %s (%s bytes).", output_path, output_path.stat().st_size ) @@ -94,7 +86,4 @@ def download_opsd(*, url: str, output_path: str | Path) -> None: logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - download_opsd( - url=snakemake.params.url, - output_path=snakemake.output.load, - ) + download_opsd(url=snakemake.params.url, output_path=snakemake.output.load) From cc59aadb8c664f8d011b3fe1ec9a41f22c667896 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 12:41:37 +0200 Subject: [PATCH 099/241] Added update flag to OPSD download to enable caching --- workflow/rules/automatic.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 0e60227..18c1941 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -42,7 +42,7 @@ rule download_load_entsoe_api: rule download_load_opsd: output: - load="/automatic/load_opsd.csv", + load=update("/automatic/load_opsd.csv"), log: "/download_load_opsd.log", localrule: True From b5372b09ecb1db4b2a144e574e1b1dfc715f7bf1 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 13:28:22 +0200 Subject: [PATCH 100/241] Provided caching capability to the population download. --- workflow/rules/automatic.smk | 11 ++- workflow/scripts/download_population.py | 112 ++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 workflow/scripts/download_population.py diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 18c1941..3b7927b 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -76,20 +76,19 @@ rule download_load_neso_year: rule download_population: output: - population="/automatic/population.zip", + population=update("/automatic/population.zip"), log: "/download_population.log", localrule: True conda: "../envs/module.yaml" params: - url_population=internal["resources"]["automatic"]["population"], + url=internal["resources"]["automatic"]["population"], + expected_member=internal["resources"]["automatic"]["population_tif"], message: "Download population data." - shell: - """ - curl -sSLo {output.population:q} {params.url_population:q} - """ + script: + "../scripts/download_population.py" rule unzip_population: diff --git a/workflow/scripts/download_population.py b/workflow/scripts/download_population.py new file mode 100644 index 0000000..9419f5d --- /dev/null +++ b/workflow/scripts/download_population.py @@ -0,0 +1,112 @@ +"""Download and cache the population-data archive.""" + +import logging +import shutil +import sys +from pathlib import Path +from urllib.request import Request, urlopen +from zipfile import BadZipFile, ZipFile, is_zipfile + +logger = logging.getLogger(__name__) + +USER_AGENT = "modelblocks-module-demand-electricity/population downloader" + + +def _is_valid_cached_archive(path: Path, *, expected_member: str) -> bool: + """Return whether an existing population archive is suitable for reuse.""" + if not path.exists() or path.stat().st_size == 0: + return False + + if not is_zipfile(path): + return False + + try: + with ZipFile(path) as archive: + members = {name.replace("\\", "/") for name in archive.namelist()} + except (OSError, BadZipFile): + return False + + expected_member = expected_member.replace("\\", "/") + + return expected_member in members + + +def download_population( + *, + url: str, + output_path: str | Path, + expected_member: str, +) -> None: + """Download the population archive unless a valid cached copy exists.""" + output_path = Path(output_path) + + if _is_valid_cached_archive( + output_path, + expected_member=expected_member, + ): + logger.info("Using cached population archive: %s", output_path) + return + + if output_path.exists(): + logger.warning( + "Cached population archive is invalid; downloading a replacement." + ) + output_path.unlink() + + output_path.parent.mkdir(parents=True, exist_ok=True) + + temporary_path = output_path.with_suffix(output_path.suffix + ".part") + temporary_path.unlink(missing_ok=True) + + logger.info("Downloading population archive from %s.", url) + + request = Request(url, headers={"User-Agent": USER_AGENT}) + + try: + with ( + urlopen(request, timeout=300) as response, + temporary_path.open("wb") as output_file, + ): + content_length = response.headers.get("Content-Length") + shutil.copyfileobj(response, output_file) + + downloaded_size = temporary_path.stat().st_size + + if content_length is not None and downloaded_size != int(content_length): + raise RuntimeError( + "Population download is incomplete: " + f"received {downloaded_size} of {content_length} bytes." + ) + + if not _is_valid_cached_archive( + temporary_path, + expected_member=expected_member, + ): + raise RuntimeError( + "Downloaded population archive does not contain " + f"the expected file {expected_member!r}." + ) + + temporary_path.replace(output_path) + + except Exception: + temporary_path.unlink(missing_ok=True) + raise + + logger.info( + "Saved population archive to %s (%s bytes).", + output_path, + output_path.stat().st_size, + ) + + +if __name__ == "__main__": + sys.stderr = open(snakemake.log[0], "w", buffering=1) + + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + download_population( + url=snakemake.params.url, + output_path=snakemake.output.population, + expected_member=snakemake.params.expected_member, + ) From 18e6bd5e357ee3e3629fc0ad5e48c534a7b7f830 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:28:45 +0000 Subject: [PATCH 101/241] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/scripts/download_population.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/workflow/scripts/download_population.py b/workflow/scripts/download_population.py index 9419f5d..2182ab5 100644 --- a/workflow/scripts/download_population.py +++ b/workflow/scripts/download_population.py @@ -32,18 +32,12 @@ def _is_valid_cached_archive(path: Path, *, expected_member: str) -> bool: def download_population( - *, - url: str, - output_path: str | Path, - expected_member: str, + *, url: str, output_path: str | Path, expected_member: str ) -> None: """Download the population archive unless a valid cached copy exists.""" output_path = Path(output_path) - if _is_valid_cached_archive( - output_path, - expected_member=expected_member, - ): + if _is_valid_cached_archive(output_path, expected_member=expected_member): logger.info("Using cached population archive: %s", output_path) return @@ -79,8 +73,7 @@ def download_population( ) if not _is_valid_cached_archive( - temporary_path, - expected_member=expected_member, + temporary_path, expected_member=expected_member ): raise RuntimeError( "Downloaded population archive does not contain " From e447219c6849a4dd85272ab4a83c8d3146b8489f Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Fri, 14 Aug 2026 13:38:04 +0200 Subject: [PATCH 102/241] Changed "incase" to "in case" because Pre-Commit is super whiney. --- workflow/scripts/cleaning/advanced/apply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py index 6f7a9b2..5dc03c5 100644 --- a/workflow/scripts/cleaning/advanced/apply.py +++ b/workflow/scripts/cleaning/advanced/apply.py @@ -63,7 +63,7 @@ def apply_auxiliary_fill_rule( if method == LEAVE_MISSING: return load.copy(), cleaning_method.copy() - # Cautionary incase of edge cases making is this far. + # Cautionary in case of edge cases making is this far. raise ValueError(f"Unsupported advanced-fill method {method!r}.") From 66aa069810f95b706477417500ee3a8e3b5b01e3 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 10:40:59 +0200 Subject: [PATCH 103/241] Added tclean (VIA EDITABLE PIP) - MUST UNDO FOR RELEASE --- pixi.lock | 1910 +++++++++++++++++++++++++++++++++++++++++++++++++++++ pixi.toml | 3 + 2 files changed, 1913 insertions(+) diff --git a/pixi.lock b/pixi.lock index f603ea6..c4f192e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -837,6 +837,8 @@ environments: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ + indexes: + - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda @@ -1158,6 +1160,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - pypi: ../../tclean osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1426,6 +1429,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: ../../tclean win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda @@ -1693,10 +1697,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py312he5662c2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda + - pypi: ../../tclean test: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ + indexes: + - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda @@ -2154,6 +2161,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/yte-1.9.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - pypi: ../../tclean osx-arm64: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda @@ -2554,6 +2562,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: ../../tclean win-64: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.3-pyhdfd78af_1.conda - conda: https://conda.anaconda.org/bioconda/noarch/snakemake-interface-common-1.23.0-pyhdfd78af_1.conda @@ -2958,6 +2967,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstandard-0.25.0-py314hc5dbbe4_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda + - pypi: ../../tclean packages: - conda: https://conda.anaconda.org/bioconda/noarch/snakefmt-2.0.2-pyhdfd78af_0.conda sha256: 71fd599b42cd1352bb8a7d278eef9b681b8de5efe210f358101ce47de5a4d699 @@ -2983,6 +2993,8 @@ packages: - python >=3.11,<4.0 license: MIT license_family: MIT + purls: + - pkg:pypi/snakefmt?source=hash-mapping run_exports: weak: - snakefmt >=2.0.3,<2.1.0a0 @@ -2998,6 +3010,8 @@ packages: - python >=3.8 license: MIT license_family: MIT + purls: + - pkg:pypi/snakemake-interface-common?source=hash-mapping run_exports: weak: - snakemake-interface-common >=1.23.0,<2.0a0 @@ -3013,6 +3027,8 @@ packages: - throttler >=1.2.2,<2.0.0 license: MIT license_family: MIT + purls: + - pkg:pypi/snakemake-interface-executor-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-executor-plugins >=9.4.0,<10.0a0 @@ -3025,6 +3041,8 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.17.4,<2.0.0 license: MIT + purls: + - pkg:pypi/snakemake-interface-logger-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-logger-plugins >=2.1.0,<3.0a0 @@ -3037,6 +3055,8 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.16.0,<2.0.0 license: MIT + purls: + - pkg:pypi/snakemake-interface-report-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-report-plugins >=1.3.0,<2.0a0 @@ -3049,6 +3069,8 @@ packages: - python >=3.11.0,<4.0.0 - snakemake-interface-common >=1.20.1,<2.0.0 license: MIT + purls: + - pkg:pypi/snakemake-interface-scheduler-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-scheduler-plugins >=2.0.2,<3.0a0 @@ -3066,6 +3088,8 @@ packages: - wrapt >=1.15.0,<2.0.0 license: MIT license_family: MIT + purls: + - pkg:pypi/snakemake-interface-storage-plugins?source=hash-mapping run_exports: weak: - snakemake-interface-storage-plugins >=4.4.1,<5.0a0 @@ -3149,6 +3173,8 @@ packages: - yte >=1.5.5,<2.0 license: MIT license_family: MIT + purls: + - pkg:pypi/snakemake?source=hash-mapping run_exports: weak: - snakemake-minimal >=9.25.1,<10.0a0 @@ -3165,6 +3191,7 @@ packages: - openmp_impl <0.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -3178,6 +3205,7 @@ packages: - libgcc >=14 license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - alsa-lib >=1.2.16.1,<1.3.0a0 @@ -3213,6 +3241,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/ast-serialize?source=compressed-mapping run_exports: {} size: 1116952 timestamp: 1786328964477 @@ -3227,6 +3257,7 @@ packages: - libglib >=2.68.1,<3.0a0 license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - at-spi2-atk >=2.38.0,<3.0a0 @@ -3244,6 +3275,7 @@ packages: - xorg-libxtst license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - at-spi2-core >=2.40.3,<2.41.0a0 @@ -3260,6 +3292,7 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - atk-1.0 >=2.38.0 @@ -3278,6 +3311,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 @@ -3296,6 +3330,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-auth >=0.7.30,<0.7.31.0a0 @@ -3311,6 +3346,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.7.4,<0.7.5.0a0 @@ -3326,6 +3362,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -3339,6 +3376,7 @@ packages: - libgcc >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 @@ -3352,6 +3390,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.9.28,<0.9.29.0a0 @@ -3366,6 +3405,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-compression >=0.2.19,<0.2.20.0a0 @@ -3380,6 +3420,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 @@ -3397,6 +3438,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 @@ -3414,6 +3456,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -3431,6 +3474,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 @@ -3448,6 +3492,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-http >=0.8.9,<0.8.10.0a0 @@ -3464,6 +3509,7 @@ packages: - s2n >=1.5.5,<1.5.6.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-io >=0.14.18,<0.14.19.0a0 @@ -3480,6 +3526,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 @@ -3496,6 +3543,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 @@ -3512,6 +3560,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -3532,6 +3581,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 @@ -3552,6 +3602,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-s3 >=0.6.5,<0.6.6.0a0 @@ -3566,6 +3617,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 @@ -3580,6 +3632,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 @@ -3594,6 +3647,7 @@ packages: - libgcc >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-checksums >=0.1.18,<0.1.19.0a0 @@ -3608,6 +3662,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 @@ -3631,6 +3686,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 @@ -3654,6 +3710,7 @@ packages: - aws-c-http >=0.11.0,<0.11.1.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 @@ -3675,6 +3732,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 @@ -3694,6 +3752,7 @@ packages: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 @@ -3710,6 +3769,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.13.0,<1.13.1.0a0 @@ -3726,6 +3786,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -3742,6 +3803,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 @@ -3758,6 +3820,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.8.0,<1.8.1.0a0 @@ -3774,6 +3837,7 @@ packages: - libstdcxx-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 @@ -3790,6 +3854,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -3808,6 +3873,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 @@ -3825,6 +3891,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 @@ -3842,6 +3909,7 @@ packages: - libstdcxx-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 @@ -3859,6 +3927,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 @@ -3877,6 +3946,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -3894,6 +3964,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -3910,6 +3981,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -3928,6 +4000,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -3945,6 +4018,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: {} size: 19615 timestamp: 1756599385418 @@ -3958,6 +4032,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: {} size: 21021 timestamp: 1764017221344 @@ -3974,6 +4049,8 @@ packages: - libbrotlicommon 1.1.0 hb03c661_4 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 354149 timestamp: 1756599553574 @@ -3990,6 +4067,8 @@ packages: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 367376 timestamp: 1764017265553 @@ -4001,6 +4080,7 @@ packages: - libgcc >=14 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -4014,6 +4094,7 @@ packages: - libgcc >=14 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -4027,6 +4108,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.6,<2.0a0 @@ -4042,6 +4124,7 @@ packages: - c-ares-static <0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 @@ -4070,6 +4153,7 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -4099,6 +4183,7 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -4116,6 +4201,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping run_exports: {} size: 295716 timestamp: 1761202958833 @@ -4144,6 +4231,8 @@ packages: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT + purls: + - pkg:pypi/cffi?source=compressed-mapping run_exports: {} size: 305586 timestamp: 1786528481627 @@ -4158,6 +4247,7 @@ packages: - libgfortran5 >=12.3.0 - libzlib >=1.3.1,<2.0a0 license: LicenseRef-fitsio + purls: [] run_exports: weak: - cfitsio >=4.4.1,<4.4.2.0a0 @@ -4186,6 +4276,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -4213,6 +4304,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -4239,6 +4331,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -4264,6 +4357,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -4286,6 +4380,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 @@ -4362,6 +4457,8 @@ packages: - conda-env >=2.6 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda?source=hash-mapping run_exports: {} size: 1464668 timestamp: 1785769797579 @@ -4402,6 +4499,8 @@ packages: - conda >=26.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/conda-pypi?source=hash-mapping run_exports: {} size: 323834 timestamp: 1784203010834 @@ -4417,6 +4516,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 320386 timestamp: 1769155979897 @@ -4432,6 +4533,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 324013 timestamp: 1769155968691 @@ -4453,6 +4556,7 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: CC0-1.0 + purls: [] run_exports: {} size: 24388 timestamp: 1785920465106 @@ -4470,6 +4574,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1817713 timestamp: 1763019879546 @@ -4486,6 +4592,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1817375 timestamp: 1763019875032 @@ -4504,6 +4612,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 195067 timestamp: 1785500114646 @@ -4521,6 +4630,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 169240 timestamp: 1722439933267 @@ -4537,6 +4647,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD + purls: [] run_exports: weak: - cyrus-sasl >=2.1.28,<3.0a0 @@ -4555,6 +4666,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD + purls: [] run_exports: weak: - cyrus-sasl >=2.1.28,<3.0a0 @@ -4571,6 +4683,8 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 623770 timestamp: 1771855837505 @@ -4585,6 +4699,8 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 641304 timestamp: 1771855845410 @@ -4597,6 +4713,7 @@ packages: - libglib >=2.70.2,<3.0a0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - dbus >=1.13.6,<2.0a0 @@ -4613,6 +4730,7 @@ packages: - libglib >=2.86.2,<3.0a0 - libexpat >=2.7.3,<3.0a0 license: AFL-2.1 OR GPL-2.0-or-later + purls: [] run_exports: weak: - dbus >=1.16.2,<2.0a0 @@ -4629,6 +4747,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 2842115 timestamp: 1780390153580 @@ -4641,6 +4761,7 @@ packages: - libstdcxx >=13 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - double-conversion >=3.3.1,<3.4.0a0 @@ -4655,6 +4776,7 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - double-conversion >=3.4.0,<3.5.0a0 @@ -4680,6 +4802,7 @@ packages: - xorg-libxxf86vm >=1.1.6,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - epoxy >=1.5.10,<1.6.0a0 @@ -4694,6 +4817,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libexpat >=2.8.1,<3.0a0 @@ -4714,6 +4838,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 570988 timestamp: 1778864779103 @@ -4732,6 +4858,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 581290 timestamp: 1778864786750 @@ -4755,6 +4883,8 @@ packages: - shapely license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 1171712 timestamp: 1726664552119 @@ -4776,6 +4906,8 @@ packages: - shapely license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 1203455 timestamp: 1767051530201 @@ -4788,6 +4920,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=11.0.2,<11.1.0a0 @@ -4802,6 +4935,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=12.1.0,<12.2.0a0 @@ -4831,6 +4965,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.1,<3.0a0 @@ -4850,6 +4985,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 @@ -4869,6 +5005,8 @@ packages: - unicodedata2 >=15.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 3007892 timestamp: 1778770568019 @@ -4879,6 +5017,7 @@ packages: - libfreetype 2.14.3 ha770c72_0 - libfreetype6 2.14.3 h73754d4_0 license: GPL-2.0-only OR FTL + purls: [] run_exports: weak: - libfreetype >=2.14.3 @@ -4892,6 +5031,7 @@ packages: - libfreetype 2.14.3 ha770c72_1 - libfreetype6 2.14.3 h73754d4_1 license: GPL-2.0-only OR FTL + purls: [] run_exports: weak: - libfreetype >=2.14.3 @@ -4909,6 +5049,7 @@ packages: - minizip >=4.0.7,<5.0a0 license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 @@ -4930,6 +5071,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 @@ -4950,6 +5092,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/gdal?source=hash-mapping run_exports: {} size: 1698682 timestamp: 1728293498040 @@ -4981,6 +5125,7 @@ packages: - libtiff >=4.7.1,<4.8.0a0 license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - gdk-pixbuf >=2.44.7,<3.0a0 @@ -4994,6 +5139,7 @@ packages: - libgcc-ng >=12 - libstdcxx-ng >=12 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.12.2,<3.12.3.0a0 @@ -5007,6 +5153,7 @@ packages: - libgcc >=14 - libstdcxx >=14 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 @@ -5026,6 +5173,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - geotiff >=1.7.3,<1.8.0a0 @@ -5040,6 +5188,7 @@ packages: - libstdcxx >=13 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - gflags >=2.2.2,<2.3.0a0 @@ -5054,6 +5203,7 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - gflags >=2.3.1,<2.4.0a0 @@ -5067,6 +5217,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - giflib >=5.2.2,<5.3.0a0 @@ -5079,6 +5230,7 @@ packages: - libgcc-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - giflib >=5.2.2,<5.3.0a0 @@ -5104,6 +5256,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-or-later + purls: [] run_exports: {} size: 238159 timestamp: 1786457663614 @@ -5117,6 +5270,7 @@ packages: - libstdcxx >=14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 @@ -5131,6 +5285,7 @@ packages: - libstdcxx-ng >=12 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 @@ -5141,6 +5296,7 @@ packages: md5: 1e152fa4fe2a7b77d4b86c76a6a85b50 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 2008937 timestamp: 1775514521503 @@ -5153,6 +5309,7 @@ packages: - libgcc >=14 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 @@ -5191,6 +5348,7 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other + purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 @@ -5220,6 +5378,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/greenlet?source=compressed-mapping run_exports: {} size: 278368 timestamp: 1786384047792 @@ -5263,6 +5423,7 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - gtk3 >=3.24.52,<4.0a0 @@ -5278,6 +5439,7 @@ packages: - libstdcxx-ng >=12 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - gts >=0.7.6,<0.8.0a0 @@ -5299,6 +5461,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - harfbuzz >=11.0.0,<12.0a0 @@ -5330,6 +5493,7 @@ packages: - libharfbuzz-devel 14.3.0 h17a8019_0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -5345,6 +5509,7 @@ packages: - libzlib >=1.2.13,<2.0.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf4 >=4.2.15,<4.2.16.0a0 @@ -5364,6 +5529,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf5 >=1.14.3,<1.14.4.0a0 @@ -5374,6 +5540,7 @@ packages: md5: 129e404c5b001f3ef5581316971e3ea0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: {} size: 17625 timestamp: 1771539597968 @@ -5386,6 +5553,7 @@ packages: - libstdcxx-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - icu >=75.1,<76.0a0 @@ -5410,6 +5578,7 @@ packages: - libstdcxx >=14 - libgcc >=14 license: MIT + purls: [] run_exports: weak: - icu >=78.3,<79.0a0 @@ -5425,6 +5594,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 55407 timestamp: 1757685419776 @@ -5436,6 +5607,7 @@ packages: - libgcc-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - json-c >=0.17,<0.18.0a0 @@ -5449,6 +5621,7 @@ packages: - libgcc >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - json-c >=0.18,<0.19.0a0 @@ -5464,6 +5637,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - kealib >=1.5.3,<1.6.0a0 @@ -5476,6 +5650,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - keyutils >=1.6.3,<2.0a0 @@ -5492,6 +5667,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 77120 timestamp: 1773067050308 @@ -5506,6 +5683,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 77363 timestamp: 1773067048780 @@ -5521,6 +5700,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.21.3,<1.22.0a0 @@ -5539,6 +5719,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 @@ -5554,6 +5735,7 @@ packages: - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 @@ -5569,6 +5751,7 @@ packages: - binutils_impl_linux-64 2.45.1 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: {} size: 728002 timestamp: 1774197446916 @@ -5582,6 +5765,7 @@ packages: - binutils_impl_linux-64 2.46.1 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: {} size: 745303 timestamp: 1784214507189 @@ -5594,6 +5778,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.1.0,<5.0a0 @@ -5608,6 +5793,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 @@ -5625,6 +5811,7 @@ packages: - libabseil-static =20240116.2=cxx17* license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20240116.2,<20240117.0a0 @@ -5643,6 +5830,7 @@ packages: - libabseil-static =20260526.0=cxx17* license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 @@ -5658,6 +5846,7 @@ packages: - libstdcxx >=14 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libaec >=1.1.5,<2.0a0 @@ -5679,6 +5868,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.7.7,<3.8.0a0 @@ -5720,6 +5910,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 @@ -5763,6 +5954,7 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=17.0.0,<17.1.0a0 @@ -5803,6 +5995,7 @@ packages: - parquet-cpp <0.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 @@ -5819,6 +6012,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=17.0.0,<17.1.0a0 @@ -5836,6 +6030,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -5855,6 +6050,7 @@ packages: - re2 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 @@ -5873,6 +6069,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=17.0.0,<17.1.0a0 @@ -5892,6 +6089,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 @@ -5913,6 +6111,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=17.0.0,<17.1.0a0 @@ -5934,6 +6133,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 @@ -5954,6 +6154,7 @@ packages: - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -5974,6 +6175,7 @@ packages: - mkl <2027 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -5987,6 +6189,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -6000,6 +6203,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -6014,6 +6218,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.1.0,<1.2.0a0 @@ -6028,6 +6233,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 @@ -6042,6 +6248,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.1.0,<1.2.0a0 @@ -6056,6 +6263,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 @@ -6073,6 +6281,7 @@ packages: - liblapack 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -6090,6 +6299,7 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -6105,6 +6315,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libclang-cpp19.1 >=19.1.7,<19.2.0a0 @@ -6123,6 +6334,7 @@ packages: - libxml2-16 >=2.14.6 - libllvm22 >=22.1.8,<22.2.0a0 license: Apache-2.0 WITH LLVM-exception + purls: [] run_exports: weak: - libclang-cpp22.1 >=22.1.8,<22.2.0a0 @@ -6138,6 +6350,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libclang13 >=21.1.0 @@ -6157,6 +6370,7 @@ packages: - libxml2-16 >=2.14.6 - libllvm22 >=22.1.8,<22.2.0a0 license: Apache-2.0 WITH LLVM-exception + purls: [] run_exports: weak: - libclang13 >=22.1.8 @@ -6170,6 +6384,7 @@ packages: - libstdcxx-ng >=9.4.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 @@ -6186,6 +6401,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libcups >=2.3.3,<2.4.0a0 @@ -6202,6 +6418,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libcups >=2.3.3,<2.4.0a0 @@ -6238,6 +6455,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 @@ -6256,6 +6474,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.9.1,<9.0a0 @@ -6269,6 +6488,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -6282,6 +6502,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -6307,6 +6528,7 @@ packages: - libpciaccess >=0.19,<0.20.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdrm >=2.4.127,<2.5.0a0 @@ -6322,6 +6544,7 @@ packages: - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 @@ -6334,6 +6557,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + purls: [] run_exports: {} size: 46500 timestamp: 1779728188901 @@ -6346,6 +6570,7 @@ packages: - libgl-devel 1.7.0 ha4b6fd6_3 - xorg-libx11 license: LicenseRef-libglvnd + purls: [] run_exports: weak: - libegl >=1.7.0,<2.0a0 @@ -6359,6 +6584,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 @@ -6371,6 +6597,7 @@ packages: - libgcc-ng >=12 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 @@ -6384,6 +6611,7 @@ packages: - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -6399,6 +6627,7 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT + purls: [] run_exports: {} size: 77856 timestamp: 1781203599810 @@ -6410,6 +6639,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.5.2,<3.6.0a0 @@ -6423,6 +6653,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 @@ -6434,6 +6665,7 @@ packages: depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 8049 timestamp: 1774298163029 @@ -6443,6 +6675,7 @@ packages: depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 8419 timestamp: 1785641173212 @@ -6457,6 +6690,7 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 384575 timestamp: 1774298162622 @@ -6471,6 +6705,7 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 386042 timestamp: 1785641172605 @@ -6485,6 +6720,7 @@ packages: - libgomp 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 1041084 timestamp: 1778269013026 @@ -6499,6 +6735,7 @@ packages: - libgcc-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 1057877 timestamp: 1785375436766 @@ -6509,6 +6746,7 @@ packages: - libgcc 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libgcc @@ -6521,6 +6759,7 @@ packages: - libgcc 16.1.0 ha9f2e26_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libgcc @@ -6545,6 +6784,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD + purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 @@ -6569,6 +6809,7 @@ packages: - libgdal-xls 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -6614,6 +6855,7 @@ packages: - libgdal 3.12.4.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 @@ -6658,6 +6900,7 @@ packages: - libgdal 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -6675,6 +6918,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 478024 timestamp: 1728294286914 @@ -6690,6 +6934,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 722657 timestamp: 1728294356817 @@ -6706,6 +6951,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 579075 timestamp: 1728294420920 @@ -6721,6 +6967,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 643208 timestamp: 1728294499558 @@ -6736,6 +6983,7 @@ packages: - openjpeg >=2.5.2,<3.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 469287 timestamp: 1728294554655 @@ -6753,6 +7001,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 480532 timestamp: 1728294987957 @@ -6772,6 +7021,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 738251 timestamp: 1728295070799 @@ -6787,6 +7037,7 @@ packages: - poppler license: MIT license_family: MIT + purls: [] run_exports: {} size: 668085 timestamp: 1728294642230 @@ -6803,6 +7054,7 @@ packages: - postgresql license: MIT license_family: MIT + purls: [] run_exports: {} size: 527072 timestamp: 1728294709895 @@ -6819,6 +7071,7 @@ packages: - postgresql license: MIT license_family: MIT + purls: [] run_exports: {} size: 480524 timestamp: 1728294772712 @@ -6834,6 +7087,7 @@ packages: - tiledb >=2.26.0,<2.27.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 681765 timestamp: 1726093490312 @@ -6849,6 +7103,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 434813 timestamp: 1728294922029 @@ -6861,6 +7116,7 @@ packages: - libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 27655 timestamp: 1778269042954 @@ -6873,6 +7129,7 @@ packages: - libgfortran-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 28134 timestamp: 1785375470055 @@ -6883,6 +7140,7 @@ packages: - libgfortran 15.2.0 h69a702a_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libgfortran @@ -6898,6 +7156,7 @@ packages: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 2483673 timestamp: 1778269025089 @@ -6911,6 +7170,7 @@ packages: - libgfortran 16.1.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 2538696 timestamp: 1785375448623 @@ -6922,6 +7182,7 @@ packages: - libglvnd 1.7.0 ha4b6fd6_3 - libglx 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + purls: [] run_exports: {} size: 133469 timestamp: 1779728207669 @@ -6933,6 +7194,7 @@ packages: - libgl 1.7.0 ha4b6fd6_3 - libglx-devel 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + purls: [] run_exports: weak: - libgl >=1.7.0,<2.0a0 @@ -6951,6 +7213,7 @@ packages: constrains: - glib 2.84.0 *_0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.84.0,<3.0a0 @@ -6984,6 +7247,7 @@ packages: constrains: - glib >2.66 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 @@ -6995,6 +7259,7 @@ packages: depends: - __glibc >=2.17,<3.0.a0 license: LicenseRef-libglvnd + purls: [] run_exports: {} size: 133586 timestamp: 1779728183422 @@ -7006,6 +7271,7 @@ packages: - libglvnd 1.7.0 ha4b6fd6_3 - xorg-libx11 >=1.8.13,<2.0a0 license: LicenseRef-libglvnd + purls: [] run_exports: {} size: 76586 timestamp: 1779728199059 @@ -7018,6 +7284,7 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 - xorg-xorgproto license: LicenseRef-libglvnd + purls: [] run_exports: weak: - libglx >=1.7.0,<2.0a0 @@ -7030,6 +7297,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -7042,6 +7310,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -7064,6 +7333,7 @@ packages: - libgoogle-cloud 2.28.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=2.28.0,<2.29.0a0 @@ -7087,6 +7357,7 @@ packages: - libgoogle-cloud 3.8.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=3.8.0,<3.9.0a0 @@ -7107,6 +7378,7 @@ packages: - openssl license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 @@ -7127,6 +7399,7 @@ packages: - openssl license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 @@ -7150,6 +7423,7 @@ packages: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.62.2,<1.63.0a0 @@ -7174,6 +7448,7 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 @@ -7196,6 +7471,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 1333436 timestamp: 1785770053613 @@ -7218,6 +7494,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -7231,6 +7508,7 @@ packages: - libgcc >=14 - libstdcxx >=14 license: Apache-2.0 OR BSD-3-Clause + purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 @@ -7243,6 +7521,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-only + purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 @@ -7257,6 +7536,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.1.4.1,<4.0a0 @@ -7271,6 +7551,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 @@ -7288,6 +7569,7 @@ packages: - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 @@ -7305,6 +7587,7 @@ packages: - uriparser >=0.9.8,<1.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 412514 timestamp: 1777026799177 @@ -7320,6 +7603,7 @@ packages: - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -7337,6 +7621,7 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -7368,6 +7653,7 @@ packages: - blas 2.309 openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapacke >=3.11.0,<3.12.0a0 @@ -7385,6 +7671,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libllvm19 >=19.1.7,<19.2.0a0 @@ -7402,6 +7689,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libllvm21 >=21.1.0,<21.2.0a0 @@ -7420,6 +7708,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libllvm22 >=22.1.8,<22.2.0a0 @@ -7434,6 +7723,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -7448,6 +7738,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -7461,6 +7752,7 @@ packages: - libgcc >=14 - liblzma 5.8.3 hb03c661_0 license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -7514,6 +7806,7 @@ packages: - libsolv >=0.7.39,<0.8.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libmamba >=2.9.0,<2.10.0a0 @@ -7541,6 +7834,7 @@ packages: - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 20529 timestamp: 1786125663902 @@ -7588,6 +7882,8 @@ packages: - pybind11-abi ==11 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/libmambapy?source=hash-mapping run_exports: weak: - libmambapy >=2.9.0,<2.10.0a0 @@ -7601,6 +7897,7 @@ packages: - libgcc >=14 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: {} size: 92400 timestamp: 1769482286018 @@ -7626,6 +7923,7 @@ packages: constrains: - msgpack-c ==6.1.0 *_7 license: BSL-1.0 + purls: [] run_exports: weak: - libmsgpack-c >=6.1.0,<7.0a0 @@ -7651,6 +7949,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnetcdf >=4.9.2,<4.9.3.0a0 @@ -7670,6 +7969,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 @@ -7683,6 +7983,7 @@ packages: - libgcc >=13 license: LGPL-2.1-only license_family: GPL + purls: [] run_exports: weak: - libnsl >=2.0.1,<2.1.0a0 @@ -7695,6 +7996,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libntlm >=1.8,<2.0a0 @@ -7712,6 +8014,7 @@ packages: - openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libopenblas >=0.3.33,<1.0a0 @@ -7729,6 +8032,7 @@ packages: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libopenblas >=0.3.34,<1.0a0 @@ -7741,6 +8045,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd + purls: [] run_exports: {} size: 51767 timestamp: 1779728204026 @@ -7761,6 +8066,7 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -7771,6 +8077,7 @@ packages: md5: 6cc68cbbe88746be8beaf027d6c64ad3 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 394726 timestamp: 1783133038109 @@ -7787,6 +8094,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=17.0.0,<17.1.0a0 @@ -7805,6 +8113,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 @@ -7828,6 +8137,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libpciaccess >=0.19,<0.20.0a0 @@ -7841,6 +8151,7 @@ packages: - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement + purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 @@ -7857,6 +8168,7 @@ packages: - openldap >=2.6.8,<2.7.0a0 - openssl >=3.4.0,<4.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -7873,6 +8185,7 @@ packages: - openldap >=2.6.13,<2.7.0a0 - openssl >=3.5.7,<4.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=18.4,<19.0a0 @@ -7890,6 +8203,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=4.25.3,<4.25.4.0a0 @@ -7907,6 +8221,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 @@ -7922,6 +8237,7 @@ packages: - icu >=78.3,<79.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 @@ -7939,6 +8255,7 @@ packages: - libharfbuzz >=14.2.1 license: MIT license_family: MIT + purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 @@ -7956,6 +8273,7 @@ packages: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -7974,6 +8292,7 @@ packages: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -7996,6 +8315,7 @@ packages: constrains: - __glibc >=2.17 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - librsvg >=2.62.3,<3.0a0 @@ -8011,6 +8331,7 @@ packages: - libstdcxx >=14 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -8026,6 +8347,7 @@ packages: - libstdcxx-ng >=12 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -8038,6 +8360,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: ISC + purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 @@ -8053,6 +8376,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 @@ -8079,6 +8403,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -8103,6 +8428,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -8126,6 +8452,7 @@ packages: - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -8140,6 +8467,7 @@ packages: - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -8155,6 +8483,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 @@ -8170,6 +8499,7 @@ packages: - libstdcxx-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 5852044 timestamp: 1778269036376 @@ -8183,6 +8513,7 @@ packages: - libstdcxx-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 6631744 timestamp: 1785375462643 @@ -8193,6 +8524,7 @@ packages: - libstdcxx 15.2.0 h934c35e_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libstdcxx @@ -8205,6 +8537,7 @@ packages: - libstdcxx 16.1.0 h934c35e_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libstdcxx @@ -8222,6 +8555,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.20.0,<0.20.1.0a0 @@ -8239,6 +8573,7 @@ packages: - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 @@ -8259,6 +8594,7 @@ packages: - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.1,<4.8.0a0 @@ -8279,6 +8615,7 @@ packages: - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 @@ -8292,6 +8629,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 @@ -8305,6 +8643,7 @@ packages: - libgcc >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.8.0,<2.9.0a0 @@ -8318,6 +8657,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libuuid >=2.42.2,<3.0a0 @@ -8336,6 +8676,7 @@ packages: - libvulkan-headers 1.4.357.0.* license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libvulkan-loader >=1.4.357.0,<2.0a0 @@ -8351,6 +8692,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -8366,6 +8708,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -8382,6 +8725,7 @@ packages: - xorg-libxdmcp license: MIT license_family: MIT + purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 @@ -8393,6 +8737,7 @@ packages: depends: - libgcc-ng >=12 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libxcrypt >=4.4.36 @@ -8405,6 +8750,7 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libxcrypt >=4.4.38 @@ -8423,6 +8769,7 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 license: MIT/X11 Derivative license_family: MIT + purls: [] run_exports: weak: - libxkbcommon >=1.11.0,<2.0a0 @@ -8442,6 +8789,7 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 license: MIT/X11 Derivative license_family: MIT + purls: [] run_exports: weak: - libxkbcommon >=1.13.2,<2.0a0 @@ -8461,6 +8809,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + purls: [] run_exports: {} size: 559775 timestamp: 1776376739004 @@ -8476,6 +8825,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 >=2.13.9,<2.14.0a0 @@ -8494,6 +8844,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -8514,6 +8865,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -8530,6 +8882,7 @@ packages: - libxml2-16 >=2.14.6 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 @@ -8544,6 +8897,7 @@ packages: - libxml2 >=2.13.8,<2.14.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 @@ -8560,6 +8914,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libzip >=1.11.2,<2.0a0 @@ -8574,6 +8929,7 @@ packages: - zlib 1.3.2 *_2 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -8588,6 +8944,7 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -8604,6 +8961,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 39432 timestamp: 1725089587134 @@ -8619,6 +8978,8 @@ packages: - lz4-c >=1.10.0,<1.11.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 45224 timestamp: 1765026391393 @@ -8631,6 +8992,7 @@ packages: - libstdcxx >=13 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 @@ -8644,6 +9006,7 @@ packages: - libstdcxx-ng >=12 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.9.4,<1.10.0a0 @@ -8657,6 +9020,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 @@ -8674,6 +9038,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 26057 timestamp: 1772445297924 @@ -8689,6 +9055,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 27424 timestamp: 1772445227915 @@ -8703,6 +9071,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 17831 timestamp: 1777000588302 @@ -8717,6 +9086,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 15007 timestamp: 1785211130117 @@ -8746,6 +9116,8 @@ packages: - tk >=8.6.13,<8.7.0a0 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8336056 timestamp: 1777000573501 @@ -8776,6 +9148,8 @@ packages: - tk >=8.6.13,<8.7.0a0 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 9068707 timestamp: 1785211110030 @@ -8797,6 +9171,8 @@ packages: - tomli-w - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT + purls: + - pkg:pypi/menuinst?source=hash-mapping run_exports: {} size: 207824 timestamp: 1786008811631 @@ -8815,6 +9191,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.1,<5.0a0 @@ -8835,6 +9212,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 @@ -8851,6 +9229,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 112800 timestamp: 1782460774570 @@ -8865,6 +9245,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 113343 timestamp: 1782460776477 @@ -8890,6 +9272,7 @@ packages: - libstdcxx >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 @@ -8929,6 +9312,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/mypy?source=hash-mapping run_exports: {} size: 23233304 timestamp: 1783986175097 @@ -8942,6 +9327,7 @@ packages: - openssl >=3.4.1,<4.0a0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: {} size: 616215 timestamp: 1744124836761 @@ -8958,6 +9344,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: {} size: 1369369 timestamp: 1744124916632 @@ -8968,6 +9355,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: X11 AND BSD-3-Clause + purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -8980,6 +9368,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: X11 AND BSD-3-Clause + purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -8992,6 +9381,7 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 136372 timestamp: 1785920438981 @@ -9004,6 +9394,7 @@ packages: - libstdcxx >=14 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - nspr >=4.38,<5.0a0 @@ -9021,6 +9412,7 @@ packages: - nspr >=4.38,<5.0a0 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - nss >=3.118,<4.0a0 @@ -9041,6 +9433,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.26.4,<2.0a0 @@ -9080,6 +9474,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.25,<3 @@ -9097,6 +9493,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - openjpeg >=2.5.4,<3.0a0 @@ -9114,6 +9511,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: OLDAP-2.8 license_family: BSD + purls: [] run_exports: weak: - openldap >=2.6.10,<2.7.0a0 @@ -9131,6 +9529,7 @@ packages: - openssl >=3.5.6,<4.0a0 license: OLDAP-2.8 license_family: BSD + purls: [] run_exports: weak: - openldap >=2.6.13,<2.7.0a0 @@ -9145,6 +9544,7 @@ packages: - libgcc >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -9159,6 +9559,7 @@ packages: - libgcc >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -9179,6 +9580,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - orc >=2.0.2,<2.0.3.0a0 @@ -9201,6 +9603,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 @@ -9259,6 +9662,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 14872605 timestamp: 1778602625175 @@ -9370,6 +9775,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 15360863 timestamp: 1785276230218 @@ -9411,6 +9818,7 @@ packages: - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - pango >=1.58.2,<2.0a0 @@ -9426,6 +9834,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.44,<10.45.0a0 @@ -9441,6 +9850,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 @@ -9465,6 +9875,8 @@ packages: - libfreetype6 >=2.14.3 - zlib-ng >=2.3.3,<2.4.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 1039561 timestamp: 1775060059882 @@ -9487,6 +9899,8 @@ packages: - lcms2 >=2.19.1,<3.0a0 - tk >=8.6.13,<8.7.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 1108174 timestamp: 1782912080163 @@ -9500,6 +9914,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -9514,6 +9929,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -9544,6 +9960,7 @@ packages: - poppler-data license: GPL-2.0-only license_family: GPL + purls: [] run_exports: {} size: 1907007 timestamp: 1724659640508 @@ -9567,6 +9984,7 @@ packages: - tzdata - zstd >=1.5.6,<1.6.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -9587,6 +10005,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.4.1,<9.5.0a0 @@ -9609,6 +10028,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -9626,6 +10046,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 @@ -9641,6 +10062,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 225545 timestamp: 1769678155334 @@ -9654,6 +10077,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 231303 timestamp: 1769678156552 @@ -9665,6 +10090,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: {} size: 9115 timestamp: 1786067714761 @@ -9676,6 +10102,7 @@ packages: - libgcc >=13 license: MIT license_family: MIT + purls: [] run_exports: {} size: 8252 timestamp: 1726802366959 @@ -9689,6 +10116,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 230470 timestamp: 1757853295064 @@ -9724,6 +10153,8 @@ packages: - __glibc >=2.17 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/py-rattler?source=hash-mapping run_exports: {} size: 12065493 timestamp: 1784707900361 @@ -9741,6 +10172,7 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 25538 timestamp: 1730169714708 @@ -9757,6 +10189,7 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 26040 timestamp: 1784258391200 @@ -9777,6 +10210,8 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 4607408 timestamp: 1730169265797 @@ -9797,6 +10232,8 @@ packages: - apache-arrow-proc * cpu license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 5466581 timestamp: 1784258329989 @@ -9822,6 +10259,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pycosat?source=hash-mapping run_exports: {} size: 88896 timestamp: 1784146233775 @@ -9838,6 +10277,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1895409 timestamp: 1778084226169 @@ -9854,6 +10295,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1895020 timestamp: 1778084229247 @@ -9871,6 +10314,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 639343 timestamp: 1727771812389 @@ -9888,6 +10333,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 667940 timestamp: 1764402531595 @@ -9903,6 +10350,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=hash-mapping run_exports: {} size: 547934 timestamp: 1725436149519 @@ -9919,6 +10368,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} size: 573847 timestamp: 1786351881422 @@ -9943,6 +10394,9 @@ packages: - libxml2-16 >=2.14.6 license: LGPL-3.0-only license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} size: 13821776 timestamp: 1778933872780 @@ -9965,6 +10419,9 @@ packages: - qt6-main >=6.8.2,<6.9.0a0 license: LGPL-3.0-only license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} size: 10898802 timestamp: 1740757230072 @@ -9992,6 +10449,7 @@ packages: constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.12.* *_cp312 @@ -10023,6 +10481,7 @@ packages: - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 @@ -10080,6 +10539,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/librt?source=compressed-mapping run_exports: {} size: 162846 timestamp: 1786328851615 @@ -10093,6 +10554,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 291078 timestamp: 1778688806850 @@ -10107,6 +10570,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 198293 timestamp: 1770223620706 @@ -10121,6 +10586,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 202391 timestamp: 1770223462836 @@ -10138,6 +10605,8 @@ packages: - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} size: 210896 timestamp: 1779483879367 @@ -10149,6 +10618,7 @@ packages: - libgcc-ng >=12 - libstdcxx-ng >=12 license: LicenseRef-Qhull + purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 @@ -10224,6 +10694,7 @@ packages: - qt ==6.11.1 license: LGPL-3.0-only license_family: LGPL + purls: [] run_exports: weak: - qt6-main >=6.11.1,<7.0a0 @@ -10288,6 +10759,7 @@ packages: - qt 6.8.2 license: LGPL-3.0-only license_family: LGPL + purls: [] run_exports: weak: - qt6-main >=6.8.2,<6.9.0a0 @@ -10316,6 +10788,8 @@ packages: - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7430252 timestamp: 1726176608886 @@ -10341,6 +10815,8 @@ packages: - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7777425 timestamp: 1785964760305 @@ -10351,6 +10827,7 @@ packages: - libre2-11 2023.09.01 h5a48ba9_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -10364,6 +10841,7 @@ packages: - libre2-11 2025.11.05 h60473fc_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -10379,6 +10857,7 @@ packages: - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: weak: - readline >=8.3,<9.0a0 @@ -10392,6 +10871,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc >=14.2.7.post0,<14.3.0a0 @@ -10418,6 +10898,7 @@ packages: - reproc >=14.2.7.post0,<14.3.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc-cpp >=14.2.7.post0,<14.3.0a0 @@ -10461,6 +10942,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} size: 300129 timestamp: 1782831350283 @@ -10475,6 +10958,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 308487 timestamp: 1766175778417 @@ -10488,6 +10973,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} size: 150041 timestamp: 1766159514023 @@ -10517,6 +11004,8 @@ packages: - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/ruff?source=hash-mapping run_exports: {} size: 9481279 timestamp: 1786151870074 @@ -10529,6 +11018,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - s2n >=1.5.5,<1.5.6.0a0 @@ -10543,6 +11033,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - s2n >=1.7.5,<1.7.6.0a0 @@ -10566,6 +11057,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 10038167 timestamp: 1780401052981 @@ -10587,6 +11080,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 10311253 timestamp: 1780401051520 @@ -10609,6 +11104,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 16828243 timestamp: 1779874781187 @@ -10631,6 +11128,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 17260022 timestamp: 1781912924009 @@ -10646,6 +11145,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 571255 timestamp: 1725394110104 @@ -10661,6 +11162,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 652785 timestamp: 1762523657698 @@ -10683,6 +11186,7 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: Apache-2.0 + purls: [] run_exports: weak: - simdjson >=4.6.6,<4.7.0a0 @@ -10698,6 +11202,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 160254 timestamp: 1777112097179 @@ -10711,6 +11217,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 163682 timestamp: 1777112100084 @@ -10724,6 +11232,7 @@ packages: - libgcc >=14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 @@ -10739,6 +11248,7 @@ packages: - libstdcxx-ng >=12 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.14.1,<1.15.0a0 @@ -10766,6 +11276,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.17.0,<1.18.0a0 @@ -10796,6 +11307,8 @@ packages: - __glibc >=2.17,<3.0.a0 - python_abi 3.14.* *_cp314 license: MIT + purls: + - pkg:pypi/sqlalchemy?source=compressed-mapping run_exports: {} size: 4047549 timestamp: 1786535232010 @@ -10810,6 +11323,7 @@ packages: - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -10827,6 +11341,7 @@ packages: - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -10860,6 +11375,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - tiledb >=2.26.0,<2.27.0a0 @@ -10876,6 +11392,7 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: TCL license_family: BSD + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -10892,6 +11409,7 @@ packages: constrains: - xorg-libx11 >=1.8.13,<2.0a0 license: TCL + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -10907,6 +11425,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 864705 timestamp: 1781006801632 @@ -10932,6 +11452,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=compressed-mapping run_exports: {} size: 923237 timestamp: 1786226770518 @@ -10943,6 +11465,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 71525 timestamp: 1776960300375 @@ -10956,6 +11479,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 410641 timestamp: 1770909099497 @@ -10969,6 +11494,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 409562 timestamp: 1770909102180 @@ -10980,6 +11507,7 @@ packages: - libstdcxx-ng >=12 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 @@ -11009,6 +11537,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - wayland >=1.26.0,<2.0a0 @@ -11025,6 +11554,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - wayland >=1.26.0,<2.0a0 @@ -11040,6 +11570,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 64997 timestamp: 1756851739706 @@ -11052,6 +11584,7 @@ packages: - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util >=0.4.1,<0.5.0a0 @@ -11069,6 +11602,7 @@ packages: - xcb-util-renderutil >=0.3.10,<0.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util-cursor >=0.1.6,<0.2.0a0 @@ -11083,6 +11617,7 @@ packages: - xcb-util >=0.4.1,<0.5.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util-image >=0.4.0,<0.5.0a0 @@ -11096,6 +11631,7 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util-keysyms >=0.4.1,<0.5.0a0 @@ -11109,6 +11645,7 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util-renderutil >=0.3.10,<0.4.0a0 @@ -11122,6 +11659,7 @@ packages: - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xcb-util-wm >=0.4.2,<0.5.0a0 @@ -11138,6 +11676,7 @@ packages: - libstdcxx >=13 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.2.5,<3.3.0a0 @@ -11154,6 +11693,7 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 @@ -11168,6 +11708,7 @@ packages: - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 441670 timestamp: 1782027360439 @@ -11178,6 +11719,7 @@ packages: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: MIT + purls: [] run_exports: weak: - xorg-libice >=1.1.2,<2.0a0 @@ -11191,6 +11733,7 @@ packages: - libgcc >=13 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libice >=1.1.2,<2.0a0 @@ -11205,6 +11748,7 @@ packages: - xorg-libice >=1.1.2,<2.0a0 - libuuid >=2.42.2,<3.0a0 license: MIT + purls: [] run_exports: weak: - xorg-libsm >=1.2.6,<2.0a0 @@ -11220,6 +11764,7 @@ packages: - xorg-libice >=1.1.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libsm >=1.2.6,<2.0a0 @@ -11234,6 +11779,7 @@ packages: - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libx11 >=1.8.13,<2.0a0 @@ -11247,6 +11793,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -11260,6 +11807,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -11275,6 +11823,7 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxcomposite >=0.4.7,<1.0a0 @@ -11291,6 +11840,7 @@ packages: - xorg-libxrender >=0.9.11,<0.10.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxcursor >=1.2.3,<2.0a0 @@ -11307,6 +11857,7 @@ packages: - xorg-libxfixes >=6.0.1,<7.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdamage >=1.1.6,<2.0a0 @@ -11320,6 +11871,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -11333,6 +11885,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -11347,6 +11900,7 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxext >=1.3.7,<2.0a0 @@ -11361,6 +11915,7 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxfixes >=6.0.2,<7.0a0 @@ -11377,6 +11932,7 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxi >=1.8.3,<2.0a0 @@ -11393,6 +11949,7 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxinerama >=1.1.6,<1.2.0a0 @@ -11409,6 +11966,7 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxrandr >=1.5.5,<2.0a0 @@ -11423,6 +11981,7 @@ packages: - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxrender >=0.9.12,<0.10.0a0 @@ -11439,6 +11998,7 @@ packages: - xorg-libxi >=1.7.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxtst >=1.2.5,<2.0a0 @@ -11454,6 +12014,7 @@ packages: - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxxf86vm >=1.1.7,<2.0a0 @@ -11467,6 +12028,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: {} size: 594844 timestamp: 1786114408394 @@ -11491,6 +12053,7 @@ packages: - xz-gpl-tools 5.8.3 ha02ee65_0 - xz-tools 5.8.3 hb03c661_0 license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -11506,6 +12069,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -11521,6 +12085,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD AND LGPL-2.1-or-later + purls: [] run_exports: {} size: 95955 timestamp: 1775825530484 @@ -11532,6 +12097,7 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml >=0.2.5,<0.3.0a0 @@ -11558,6 +12124,7 @@ packages: - libstdcxx >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml-cpp >=0.8.0,<0.9.0a0 @@ -11574,6 +12141,7 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - zeromq >=4.3.5,<4.4.0a0 @@ -11587,6 +12155,7 @@ packages: - libzlib 1.3.2 h25fd6f3_2 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -11600,6 +12169,7 @@ packages: - libzlib 1.3.2 h25fd6f3_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -11614,6 +12184,7 @@ packages: - libstdcxx >=14 license: Zlib license_family: Other + purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 @@ -11632,6 +12203,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 467133 timestamp: 1762512686069 @@ -11648,6 +12221,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 473605 timestamp: 1762512687493 @@ -11659,6 +12234,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 @@ -11682,6 +12258,7 @@ packages: - python-gil license: MIT license_family: MIT + purls: [] run_exports: {} size: 8144 timestamp: 1784221492234 @@ -11694,6 +12271,7 @@ packages: - librsvg license: LGPL-3.0-or-later OR CC-BY-SA-3.0 license_family: LGPL + purls: [] run_exports: {} size: 631452 timestamp: 1758743294412 @@ -11704,6 +12282,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/affine?source=hash-mapping run_exports: {} size: 19164 timestamp: 1733762153202 @@ -11715,6 +12295,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/affine?source=compressed-mapping run_exports: {} size: 16762 timestamp: 1786226684740 @@ -11726,6 +12308,8 @@ packages: - pyparsing - python >=3.10 license: EPL-2.0 + purls: + - pkg:pypi/amply?source=hash-mapping run_exports: {} size: 26885 timestamp: 1782349642228 @@ -11737,6 +12321,8 @@ packages: - typing-extensions >=4.0.0 license: MIT license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping run_exports: {} size: 18074 timestamp: 1733247158254 @@ -11748,6 +12334,8 @@ packages: - typing-extensions >=4.0.0 license: MIT license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping run_exports: {} size: 19461 timestamp: 1784935220549 @@ -11782,6 +12370,8 @@ packages: - winloop >=0.2.3 license: MIT license_family: MIT + purls: + - pkg:pypi/anyio?source=hash-mapping run_exports: {} size: 164465 timestamp: 1783889660383 @@ -11792,6 +12382,8 @@ packages: - python >=3.9 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/appnope?source=hash-mapping run_exports: {} size: 10076 timestamp: 1733332433806 @@ -11801,6 +12393,8 @@ packages: depends: - python >=3.9 license: MIT OR Apache-2.0 + purls: + - pkg:pypi/archspec?source=hash-mapping run_exports: {} size: 50894 timestamp: 1737352715041 @@ -11811,6 +12405,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/argparse-dataclass?source=hash-mapping run_exports: {} size: 12806 timestamp: 1764079623900 @@ -11834,6 +12430,8 @@ packages: - astroid >=2,<5 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/asttokens?source=hash-mapping run_exports: {} size: 34639 timestamp: 1783975742052 @@ -11845,6 +12443,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/attrs?source=hash-mapping run_exports: {} size: 64927 timestamp: 1773935801332 @@ -11855,6 +12455,7 @@ packages: depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 + purls: [] run_exports: {} size: 7526 timestamp: 1781450817767 @@ -11867,6 +12468,8 @@ packages: - typing-extensions license: MIT license_family: MIT + purls: + - pkg:pypi/beautifulsoup4?source=hash-mapping run_exports: {} size: 92704 timestamp: 1780853175566 @@ -11883,6 +12486,8 @@ packages: - pytokens >=0.4 license: MIT license_family: MIT + purls: + - pkg:pypi/black?source=hash-mapping run_exports: {} size: 176838 timestamp: 1779460936776 @@ -11904,6 +12509,8 @@ packages: - panel >=1.8.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/bokeh?source=hash-mapping run_exports: {} size: 4526315 timestamp: 1781002115296 @@ -11925,6 +12532,8 @@ packages: - panel >=1.8.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/bokeh?source=hash-mapping run_exports: {} size: 4292921 timestamp: 1785249803504 @@ -11944,6 +12553,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/boltons?source=hash-mapping run_exports: {} size: 309453 timestamp: 1784706150046 @@ -11955,6 +12566,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/branca?source=hash-mapping run_exports: {} size: 30176 timestamp: 1759755695447 @@ -11964,6 +12577,7 @@ packages: depends: - __win license: ISC + purls: [] run_exports: {} size: 129352 timestamp: 1781709016515 @@ -11973,6 +12587,7 @@ packages: depends: - __unix license: ISC + purls: [] run_exports: {} size: 128866 timestamp: 1781708962055 @@ -11982,6 +12597,7 @@ packages: depends: - __win license: ISC + purls: [] run_exports: {} size: 132136 timestamp: 1784754918886 @@ -11991,6 +12607,7 @@ packages: depends: - __unix license: ISC + purls: [] run_exports: {} size: 131780 timestamp: 1784754889428 @@ -12012,6 +12629,7 @@ packages: - cached_property >=1.5.2,<1.5.3.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 6836 timestamp: 1783242914545 @@ -12031,6 +12649,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cached-property?source=hash-mapping run_exports: {} size: 14752 timestamp: 1783242913845 @@ -12040,6 +12660,8 @@ packages: depends: - python >=3.10 license: ISC + purls: + - pkg:pypi/certifi?source=hash-mapping run_exports: {} size: 133877 timestamp: 1781719949728 @@ -12049,6 +12671,8 @@ packages: depends: - python >=3.10 license: ISC + purls: + - pkg:pypi/certifi?source=hash-mapping run_exports: {} size: 137015 timestamp: 1784717699092 @@ -12059,6 +12683,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping run_exports: {} size: 58872 timestamp: 1775127203018 @@ -12069,6 +12695,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping run_exports: {} size: 61418 timestamp: 1783505332569 @@ -12082,6 +12710,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping run_exports: {} size: 104080 timestamp: 1779900586237 @@ -12094,6 +12724,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping run_exports: {} size: 104999 timestamp: 1779900548735 @@ -12107,6 +12739,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping run_exports: {} size: 106227 timestamp: 1783085395110 @@ -12119,6 +12753,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping run_exports: {} size: 107155 timestamp: 1783085363526 @@ -12130,6 +12766,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/click-plugins?source=hash-mapping run_exports: {} size: 12683 timestamp: 1750848314962 @@ -12141,6 +12779,8 @@ packages: - python >=3.9,<4.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cligj?source=hash-mapping run_exports: {} size: 12521 timestamp: 1733750069604 @@ -12158,6 +12798,8 @@ packages: - pyyaml >=6.0.2 license: MIT license_family: MIT + purls: + - pkg:pypi/clio-tools?source=hash-mapping run_exports: {} size: 23713 timestamp: 1782229072527 @@ -12169,6 +12811,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cloudpickle?source=hash-mapping run_exports: {} size: 27353 timestamp: 1765303462831 @@ -12180,6 +12824,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cmap?source=hash-mapping run_exports: {} size: 678920 timestamp: 1775317331599 @@ -12190,6 +12836,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping run_exports: {} size: 27011 timestamp: 1733218222191 @@ -12201,6 +12849,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/comm?source=hash-mapping run_exports: {} size: 14690 timestamp: 1753453984907 @@ -12218,6 +12868,8 @@ packages: - zstandard license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-index?source=hash-mapping run_exports: {} size: 210538 timestamp: 1782239750810 @@ -12229,6 +12881,8 @@ packages: - pyyaml >=6.0.0,<7.0.0 license: MIT license_family: MIT + purls: + - pkg:pypi/conda-inject?source=hash-mapping run_exports: {} size: 10327 timestamp: 1717043667069 @@ -12260,6 +12914,8 @@ packages: - conda >=26.5 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-libmamba-solver?source=hash-mapping run_exports: {} size: 46828 timestamp: 1784200590190 @@ -12285,6 +12941,8 @@ packages: - ruamel.yaml license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-lockfiles?source=hash-mapping run_exports: {} size: 20633 timestamp: 1783438353470 @@ -12299,6 +12957,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-package-handling?source=hash-mapping run_exports: {} size: 256182 timestamp: 1781015592350 @@ -12310,6 +12970,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-package-streaming?source=hash-mapping run_exports: {} size: 23954 timestamp: 1781293054998 @@ -12323,6 +12985,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-rattler-solver?source=hash-mapping run_exports: {} size: 37334 timestamp: 1781212602267 @@ -12344,6 +13008,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda-self?source=hash-mapping run_exports: {} size: 22510 timestamp: 1784724984642 @@ -12356,6 +13022,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/configargparse?source=hash-mapping run_exports: {} size: 45817 timestamp: 1773233306055 @@ -12366,6 +13034,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/connection-pool?source=hash-mapping run_exports: {} size: 8331 timestamp: 1608581999360 @@ -12387,6 +13057,7 @@ packages: - python >=3.14,<3.15.0a0 - python_abi * *_cp314 license: Python-2.0 + purls: [] run_exports: {} size: 49916 timestamp: 1786444134021 @@ -12398,6 +13069,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cycler?source=hash-mapping run_exports: {} size: 14778 timestamp: 1764466758386 @@ -12420,6 +13093,7 @@ packages: - openssl !=1.1.1e license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 11216 timestamp: 1781272553165 @@ -12442,6 +13116,7 @@ packages: - openssl !=1.1.1e license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 10399 timestamp: 1784056179014 @@ -12461,6 +13136,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/dask?source=hash-mapping run_exports: {} size: 1069053 timestamp: 1781221472758 @@ -12480,6 +13157,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/dask?source=hash-mapping run_exports: {} size: 1074755 timestamp: 1784031128995 @@ -12490,6 +13169,8 @@ packages: - python >=3.10 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/decorator?source=hash-mapping run_exports: {} size: 16102 timestamp: 1779115228886 @@ -12518,6 +13199,8 @@ packages: - openssl !=1.1.1e license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/distributed?source=hash-mapping run_exports: {} size: 838296 timestamp: 1781563082788 @@ -12546,6 +13229,8 @@ packages: - openssl !=1.1.1e license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/distributed?source=hash-mapping run_exports: {} size: 843078 timestamp: 1784043304773 @@ -12556,6 +13241,8 @@ packages: - python >=3.9 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/distro?source=hash-mapping run_exports: {} size: 41773 timestamp: 1734729953882 @@ -12565,6 +13252,8 @@ packages: depends: - python >=3.10 license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + purls: + - pkg:pypi/docutils?source=hash-mapping run_exports: {} size: 438002 timestamp: 1766092633160 @@ -12575,6 +13264,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/dpath?source=hash-mapping run_exports: {} size: 21853 timestamp: 1762165431693 @@ -12589,6 +13280,8 @@ packages: - requests license: MIT license_family: MIT + purls: + - pkg:pypi/entsoe-py?source=hash-mapping run_exports: {} size: 952643 timestamp: 1776204877247 @@ -12599,6 +13292,8 @@ packages: - python >=3.10 - typing_extensions >=4.6.0 license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=hash-mapping run_exports: {} size: 21333 timestamp: 1763918099466 @@ -12609,6 +13304,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/executing?source=hash-mapping run_exports: {} size: 30753 timestamp: 1756729456476 @@ -12626,6 +13323,8 @@ packages: depends: - python >=3.10 license: Unlicense + purls: + - pkg:pypi/filelock?source=hash-mapping run_exports: {} size: 77939 timestamp: 1785376409053 @@ -12641,6 +13340,8 @@ packages: - xyzservices license: MIT license_family: MIT + purls: + - pkg:pypi/folium?source=hash-mapping run_exports: {} size: 82665 timestamp: 1750113928159 @@ -12649,6 +13350,7 @@ packages: md5: 0c96522c6bdaed4b1566d11387caaf45 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 397370 timestamp: 1566932522327 @@ -12657,6 +13359,7 @@ packages: md5: 34893075a5c9e55cdafac56607368fc6 license: OFL-1.1 license_family: Other + purls: [] run_exports: {} size: 96530 timestamp: 1620479909603 @@ -12665,6 +13368,7 @@ packages: md5: 4d59c254e01d9cde7957100457e2d5fb license: OFL-1.1 license_family: Other + purls: [] run_exports: {} size: 700814 timestamp: 1620479612257 @@ -12673,6 +13377,7 @@ packages: md5: 49023d73832ef61042f6a237cb2687e7 license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other + purls: [] run_exports: {} size: 1620504 timestamp: 1727511233259 @@ -12683,6 +13388,7 @@ packages: - fonts-conda-forge license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 3667 timestamp: 1566974674465 @@ -12696,6 +13402,7 @@ packages: - font-ttf-source-code-pro license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 4059 timestamp: 1762351264405 @@ -12711,6 +13418,8 @@ packages: - fonttools_no_compile license: MIT license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 846038 timestamp: 1778770337113 @@ -12722,6 +13431,8 @@ packages: - python license: LGPL-3.0-only license_family: LGPL + purls: + - pkg:pypi/frozendict?source=hash-mapping run_exports: {} size: 34300 timestamp: 1781203600051 @@ -12732,6 +13443,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fsspec?source=hash-mapping run_exports: {} size: 149709 timestamp: 1781615868173 @@ -12742,6 +13455,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fsspec?source=hash-mapping run_exports: {} size: 151868 timestamp: 1785325238671 @@ -12759,6 +13474,7 @@ packages: - xyzservices license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 8267 timestamp: 1782507446937 @@ -12773,6 +13489,8 @@ packages: - shapely >=2.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/geopandas?source=hash-mapping run_exports: {} size: 255909 timestamp: 1782507445933 @@ -12784,6 +13502,8 @@ packages: - smmap >=3.0.1,<6 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/gitdb?source=hash-mapping run_exports: {} size: 53136 timestamp: 1735887290843 @@ -12808,6 +13528,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/gitpython?source=compressed-mapping run_exports: {} size: 177303 timestamp: 1786443183227 @@ -12829,6 +13551,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/gregor?source=hash-mapping run_exports: {} size: 16487 timestamp: 1782753092500 @@ -12841,6 +13565,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/h11?source=hash-mapping run_exports: {} size: 39069 timestamp: 1767729720872 @@ -12854,6 +13580,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/h2?source=hash-mapping run_exports: {} size: 95967 timestamp: 1756364871835 @@ -12867,6 +13595,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/h2?source=hash-mapping run_exports: {} size: 100789 timestamp: 1785796355216 @@ -12877,6 +13607,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/hpack?source=hash-mapping run_exports: {} size: 32884 timestamp: 1782283986153 @@ -12893,6 +13625,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/httpcore?source=hash-mapping run_exports: {} size: 49483 timestamp: 1745602916758 @@ -12907,6 +13641,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/httpx?source=hash-mapping run_exports: {} size: 63082 timestamp: 1733663449209 @@ -12918,6 +13654,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/humanfriendly?source=hash-mapping run_exports: {} size: 73563 timestamp: 1733928021866 @@ -12930,6 +13668,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/humanfriendly?source=hash-mapping run_exports: {} size: 74084 timestamp: 1733928364561 @@ -12940,6 +13680,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/hyperframe?source=hash-mapping run_exports: {} size: 17397 timestamp: 1737618427549 @@ -12951,6 +13693,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/idna?source=hash-mapping run_exports: {} size: 163869 timestamp: 1781620148226 @@ -12963,6 +13707,8 @@ packages: - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/importlib-metadata?source=hash-mapping run_exports: {} size: 34766 timestamp: 1779714582554 @@ -12973,6 +13719,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/iniconfig?source=hash-mapping run_exports: {} size: 13387 timestamp: 1760831448842 @@ -12986,6 +13734,8 @@ packages: - toml >=0.10.2 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipdb?source=hash-mapping run_exports: {} size: 18713 timestamp: 1734884952029 @@ -13013,6 +13763,8 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 137725 timestamp: 1781101860049 @@ -13039,6 +13791,8 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 138046 timestamp: 1781101760172 @@ -13065,6 +13819,8 @@ packages: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipykernel?source=hash-mapping run_exports: {} size: 138635 timestamp: 1781101665847 @@ -13131,6 +13887,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipython?source=hash-mapping run_exports: {} size: 716078 timestamp: 1785754203352 @@ -13153,6 +13911,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipython?source=hash-mapping run_exports: {} size: 715162 timestamp: 1785754256301 @@ -13164,6 +13924,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/ipython-pygments-lexers?source=hash-mapping run_exports: {} size: 13993 timestamp: 1737123723464 @@ -13175,6 +13937,8 @@ packages: - parso >=0.8.6,<0.9.0 - python license: Apache-2.0 AND MIT + purls: + - pkg:pypi/jedi?source=hash-mapping run_exports: {} size: 2715215 timestamp: 1782251948616 @@ -13187,6 +13951,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jinja2?source=hash-mapping run_exports: {} size: 120685 timestamp: 1764517220861 @@ -13198,6 +13964,8 @@ packages: - setuptools license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/joblib?source=hash-mapping run_exports: {} size: 226448 timestamp: 1765794135253 @@ -13209,6 +13977,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jsonpatch?source=hash-mapping run_exports: {} size: 17311 timestamp: 1733814664790 @@ -13220,6 +13990,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jsonpointer?source=hash-mapping run_exports: {} size: 14190 timestamp: 1774311356147 @@ -13235,6 +14007,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/jsonschema?source=hash-mapping run_exports: {} size: 82356 timestamp: 1767839954256 @@ -13247,6 +14021,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/jsonschema-specifications?source=hash-mapping run_exports: {} size: 19236 timestamp: 1757335715225 @@ -13264,6 +14040,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jupyter-client?source=hash-mapping run_exports: {} size: 117954 timestamp: 1781019994076 @@ -13281,6 +14059,8 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jupyter-core?source=hash-mapping run_exports: {} size: 64679 timestamp: 1760643889625 @@ -13298,6 +14078,8 @@ packages: - pywin32 >=300 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/jupyter-core?source=hash-mapping run_exports: {} size: 65503 timestamp: 1760643864586 @@ -13308,6 +14090,8 @@ packages: - python >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/locket?source=hash-mapping run_exports: {} size: 8250 timestamp: 1650660473123 @@ -13323,6 +14107,8 @@ packages: - scipy >=1.12 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/mapclassify?source=hash-mapping run_exports: {} size: 810830 timestamp: 1752271625200 @@ -13338,6 +14124,8 @@ packages: - scipy >=1.14 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/mapclassify?source=hash-mapping run_exports: {} size: 448635 timestamp: 1786480374013 @@ -13349,6 +14137,8 @@ packages: - traitlets license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/matplotlib-inline?source=hash-mapping run_exports: {} size: 15725 timestamp: 1778264403247 @@ -13359,6 +14149,8 @@ packages: - python >=3.9 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/munkres?source=hash-mapping run_exports: {} size: 15851 timestamp: 1749895533014 @@ -13369,6 +14161,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/mypy-extensions?source=hash-mapping run_exports: {} size: 11766 timestamp: 1745776666688 @@ -13380,6 +14174,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/narwhals?source=hash-mapping run_exports: {} size: 285532 timestamp: 1780672242196 @@ -13391,6 +14187,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/narwhals?source=hash-mapping run_exports: {} size: 289946 timestamp: 1783943716915 @@ -13419,6 +14217,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/nbformat?source=compressed-mapping run_exports: {} size: 107583 timestamp: 1786372859684 @@ -13430,6 +14230,8 @@ packages: - python license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/nest-asyncio2?source=hash-mapping run_exports: {} size: 15903 timestamp: 1770973502283 @@ -13446,6 +14248,8 @@ packages: - pandas >=2.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/networkx?source=hash-mapping run_exports: {} size: 1587439 timestamp: 1765215107045 @@ -13461,6 +14265,7 @@ packages: md5: 849b8852621992c3f3a1901c939a0399 license: MIT license_family: MIT + purls: [] run_exports: weak: - nlohmann_json-abi ==3.12.0 @@ -13474,6 +14279,8 @@ packages: - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping run_exports: {} size: 62477 timestamp: 1745345660407 @@ -13485,6 +14292,8 @@ packages: - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping run_exports: {} size: 91574 timestamp: 1777103621679 @@ -13508,6 +14317,7 @@ packages: - numpy >=1.24.4 license: MIT license_family: MIT + purls: [] run_exports: {} size: 4464 timestamp: 1782916642380 @@ -13539,6 +14349,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pandera?source=hash-mapping run_exports: {} size: 268608 timestamp: 1782916642380 @@ -13550,6 +14362,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/parso?source=hash-mapping run_exports: {} size: 82472 timestamp: 1777722955579 @@ -13562,6 +14376,8 @@ packages: - toolz license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/partd?source=hash-mapping run_exports: {} size: 20884 timestamp: 1715026639309 @@ -13572,6 +14388,8 @@ packages: - python >=3.10 license: MPL-2.0 license_family: MOZILLA + purls: + - pkg:pypi/pathspec?source=hash-mapping run_exports: {} size: 56559 timestamp: 1777271601895 @@ -13582,6 +14400,8 @@ packages: - ptyprocess >=0.5 - python >=3.9 license: ISC + purls: + - pkg:pypi/pexpect?source=hash-mapping run_exports: {} size: 53561 timestamp: 1733302019362 @@ -13601,6 +14421,8 @@ packages: - python >=3.13.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pip?source=hash-mapping run_exports: {} size: 1198163 timestamp: 1785914482439 @@ -13622,6 +14444,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/platformdirs?source=compressed-mapping run_exports: {} size: 26869 timestamp: 1786390868223 @@ -13633,6 +14457,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pluggy?source=hash-mapping run_exports: {} size: 25877 timestamp: 1764896838868 @@ -13641,6 +14467,7 @@ packages: md5: d8d7293c5b37f39b2ac32940621c6592 license: BSD-3-Clause AND (GPL-2.0-only OR GPL-3.0-only) license_family: OTHER + purls: [] run_exports: {} size: 2348171 timestamp: 1675353652214 @@ -13666,6 +14493,8 @@ packages: - prompt_toolkit 3.0.53 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/prompt-toolkit?source=hash-mapping run_exports: {} size: 276081 timestamp: 1785160613307 @@ -13675,6 +14504,8 @@ packages: depends: - python >=3.9 license: ISC + purls: + - pkg:pypi/ptyprocess?source=hash-mapping run_exports: {} size: 19457 timestamp: 1733302371990 @@ -13685,6 +14516,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/pure-eval?source=hash-mapping run_exports: {} size: 16668 timestamp: 1733569518868 @@ -13693,6 +14526,7 @@ packages: md5: f0599959a2447c1e544e216bddf393fa license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pybind11-abi ==11 @@ -13706,6 +14540,8 @@ packages: - setuptools license: LGPL-2.1-or-later license_family: LGPL + purls: + - pkg:pypi/pycountry?source=hash-mapping run_exports: {} size: 3105570 timestamp: 1718094617616 @@ -13717,6 +14553,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pycparser?source=hash-mapping run_exports: {} size: 55886 timestamp: 1779293633166 @@ -13732,6 +14570,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic?source=hash-mapping run_exports: {} size: 346511 timestamp: 1778103405862 @@ -13745,6 +14585,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pydot?source=hash-mapping run_exports: {} size: 150656 timestamp: 1766345630713 @@ -13755,6 +14597,8 @@ packages: - python >=3.10 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping run_exports: {} size: 893031 timestamp: 1774796815820 @@ -13766,6 +14610,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pyparsing?source=hash-mapping run_exports: {} size: 110893 timestamp: 1769003998136 @@ -13777,6 +14623,8 @@ packages: - tomli >=1.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproject-hooks?source=hash-mapping run_exports: {} size: 15528 timestamp: 1733710122949 @@ -13789,6 +14637,8 @@ packages: - win_inet_pton license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping run_exports: {} size: 21784 timestamp: 1733217448189 @@ -13800,6 +14650,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping run_exports: {} size: 21085 timestamp: 1733217331982 @@ -13820,6 +14672,8 @@ packages: - pytest-faulthandler >=2 license: MIT license_family: MIT + purls: + - pkg:pypi/pytest?source=hash-mapping run_exports: {} size: 306724 timestamp: 1782127176429 @@ -13838,6 +14692,8 @@ packages: - build <0 license: MIT license_family: MIT + purls: + - pkg:pypi/build?source=hash-mapping run_exports: {} size: 28946 timestamp: 1778048948266 @@ -13850,6 +14706,8 @@ packages: - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/python-dateutil?source=hash-mapping run_exports: {} size: 233310 timestamp: 1751104122689 @@ -13871,6 +14729,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastjsonschema?source=hash-mapping run_exports: {} size: 252180 timestamp: 1785242896823 @@ -13890,6 +14750,7 @@ packages: - cpython 3.14.6.* - python_abi * *_cp314 license: Python-2.0 + purls: [] run_exports: {} size: 49897 timestamp: 1786444152084 @@ -13900,6 +14761,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/installer?source=hash-mapping run_exports: {} size: 239330 timestamp: 1778554210561 @@ -13910,6 +14773,8 @@ packages: - python >=3.10 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/tzdata?source=hash-mapping run_exports: {} size: 146639 timestamp: 1777068997932 @@ -13920,6 +14785,8 @@ packages: - python >=3.10 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/tzdata?source=hash-mapping run_exports: {} size: 146862 timestamp: 1783704822814 @@ -13931,6 +14798,7 @@ packages: - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 6958 timestamp: 1752805918820 @@ -13942,6 +14810,7 @@ packages: - python 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 6989 timestamp: 1752805904792 @@ -13953,6 +14822,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pytz?source=hash-mapping run_exports: {} size: 201747 timestamp: 1777892201250 @@ -13964,6 +14835,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/pytz?source=hash-mapping run_exports: {} size: 201126 timestamp: 1785077087428 @@ -13984,6 +14857,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterstats?source=hash-mapping run_exports: {} size: 24082 timestamp: 1779718549710 @@ -13998,6 +14873,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/referencing?source=hash-mapping run_exports: {} size: 51788 timestamp: 1760379115194 @@ -14015,6 +14892,8 @@ packages: - chardet >=3.0.2,<8 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/requests?source=hash-mapping run_exports: {} size: 68709 timestamp: 1778851103479 @@ -14031,6 +14910,8 @@ packages: - xarray >=2024.7.0 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/rioxarray?source=hash-mapping run_exports: {} size: 53405 timestamp: 1761337252983 @@ -14047,6 +14928,8 @@ packages: - python license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/rioxarray?source=hash-mapping run_exports: {} size: 65705 timestamp: 1785242371731 @@ -14057,6 +14940,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/setuptools?source=hash-mapping run_exports: {} size: 639697 timestamp: 1773074868565 @@ -14067,6 +14952,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/setuptools?source=compressed-mapping run_exports: {} size: 524488 timestamp: 1786282924579 @@ -14078,6 +14965,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/six?source=hash-mapping run_exports: {} size: 18455 timestamp: 1753199211006 @@ -14101,6 +14990,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/smart-open?source=hash-mapping run_exports: {} size: 57158 timestamp: 1782573421136 @@ -14112,6 +15003,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/smmap?source=hash-mapping run_exports: {} size: 27262 timestamp: 1781021238494 @@ -14122,6 +15015,8 @@ packages: - python >=3.10 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/sniffio?source=hash-mapping run_exports: {} size: 15698 timestamp: 1762941572482 @@ -14134,6 +15029,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/snuggs?source=hash-mapping run_exports: {} size: 11313 timestamp: 1733818738919 @@ -14144,6 +15041,8 @@ packages: - python >=3.9 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/sortedcontainers?source=hash-mapping run_exports: {} size: 28657 timestamp: 1738440459037 @@ -14154,6 +15053,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/soupsieve?source=hash-mapping run_exports: {} size: 38802 timestamp: 1779635534390 @@ -14164,6 +15065,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/soupsieve?source=compressed-mapping run_exports: {} size: 39439 timestamp: 1786202135509 @@ -14177,6 +15080,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/sqlmodel?source=hash-mapping run_exports: {} size: 30854 timestamp: 1771872849343 @@ -14190,6 +15095,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/stack-data?source=hash-mapping run_exports: {} size: 26988 timestamp: 1733569565672 @@ -14201,6 +15108,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/tabulate?source=hash-mapping run_exports: {} size: 43964 timestamp: 1772732795746 @@ -14212,6 +15121,8 @@ packages: - python license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/tblib?source=hash-mapping run_exports: {} size: 19397 timestamp: 1762956379123 @@ -14223,6 +15134,8 @@ packages: - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/tenacity?source=hash-mapping run_exports: {} size: 31404 timestamp: 1770510172846 @@ -14233,6 +15146,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/threadpoolctl?source=hash-mapping run_exports: {} size: 23869 timestamp: 1741878358548 @@ -14243,6 +15158,8 @@ packages: - python >=3.6 license: MIT license_family: MIT + purls: + - pkg:pypi/throttler?source=hash-mapping run_exports: {} size: 12341 timestamp: 1691135604942 @@ -14254,6 +15171,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/toml?source=hash-mapping run_exports: {} size: 24017 timestamp: 1764486833072 @@ -14265,6 +15184,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/tomli?source=hash-mapping run_exports: {} size: 21561 timestamp: 1774492402955 @@ -14275,6 +15196,8 @@ packages: - python >=3.9 license: MIT license_family: MIT + purls: + - pkg:pypi/tomli-w?source=hash-mapping run_exports: {} size: 12680 timestamp: 1736962345843 @@ -14285,6 +15208,8 @@ packages: - python >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/toolz?source=hash-mapping run_exports: {} size: 53978 timestamp: 1760707830681 @@ -14326,6 +15251,8 @@ packages: - envwrap >=0.2 - ipywidgets >=6.0 license: MPL-2.0 and MIT + purls: + - pkg:pypi/tqdm?source=hash-mapping run_exports: {} size: 98184 timestamp: 1785172528905 @@ -14341,6 +15268,8 @@ packages: - envwrap >=0.2 - ipywidgets >=6.0 license: MPL-2.0 and MIT + purls: + - pkg:pypi/tqdm?source=hash-mapping run_exports: {} size: 97602 timestamp: 1785172567718 @@ -14362,6 +15291,8 @@ packages: - python license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/traitlets?source=hash-mapping run_exports: {} size: 116935 timestamp: 1785761789772 @@ -14373,6 +15304,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/truststore?source=hash-mapping run_exports: {} size: 24279 timestamp: 1766494826559 @@ -14401,6 +15334,8 @@ packages: - pytest >=7 license: MIT license_family: MIT + purls: + - pkg:pypi/typeguard?source=hash-mapping run_exports: {} size: 38525 timestamp: 1785075519409 @@ -14411,6 +15346,7 @@ packages: - typing_extensions ==4.15.0 pyhcf101f3_0 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 91383 timestamp: 1756220668932 @@ -14421,6 +15357,7 @@ packages: - typing_extensions ==4.16.0 pyhcf101f3_0 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 94080 timestamp: 1783002732887 @@ -14433,6 +15370,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=hash-mapping run_exports: {} size: 20935 timestamp: 1777105465795 @@ -14445,6 +15384,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=compressed-mapping run_exports: {} size: 20940 timestamp: 1786372593154 @@ -14456,6 +15397,8 @@ packages: - python license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping run_exports: {} size: 51692 timestamp: 1756220668932 @@ -14467,6 +15410,8 @@ packages: - python license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping run_exports: {} size: 52631 timestamp: 1783002732887 @@ -14479,6 +15424,8 @@ packages: - typing_extensions >=3.7.4 license: MIT license_family: MIT + purls: + - pkg:pypi/typing-inspect?source=hash-mapping run_exports: {} size: 14919 timestamp: 1733845966415 @@ -14486,6 +15433,7 @@ packages: sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c md5: ad659d0a2b3e47e38d829aa8cad2d610 license: LicenseRef-Public-Domain + purls: [] run_exports: {} size: 119135 timestamp: 1767016325805 @@ -14493,6 +15441,7 @@ packages: sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 md5: fcb489df604d100968b737f2cb6076c6 license: LicenseRef-Public-Domain + purls: [] run_exports: {} size: 118849 timestamp: 1784250406640 @@ -14507,6 +15456,8 @@ packages: - requests >=2.25 license: MIT license_family: MIT + purls: + - pkg:pypi/unearth?source=hash-mapping run_exports: {} size: 286235 timestamp: 1766474788879 @@ -14521,6 +15472,8 @@ packages: - zstandard >=0.18.0 license: MIT license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping run_exports: {} size: 101735 timestamp: 1750271478254 @@ -14535,6 +15488,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping run_exports: {} size: 103560 timestamp: 1778188657149 @@ -14554,6 +15509,8 @@ packages: - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/wcwidth?source=hash-mapping run_exports: {} size: 132415 timestamp: 1782771807703 @@ -14564,6 +15521,8 @@ packages: - __win - python >=3.9 license: LicenseRef-Public-Domain + purls: + - pkg:pypi/win-inet-pton?source=hash-mapping run_exports: {} size: 9555 timestamp: 1733130678956 @@ -14601,6 +15560,8 @@ packages: - zarr >=2.18 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/xarray?source=hash-mapping run_exports: {} size: 1017999 timestamp: 1776122774298 @@ -14638,6 +15599,8 @@ packages: - zarr >=2.18 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/xarray?source=hash-mapping run_exports: {} size: 1027069 timestamp: 1783633473025 @@ -14648,6 +15611,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/xyzservices?source=hash-mapping run_exports: {} size: 51732 timestamp: 1774900074457 @@ -14661,6 +15626,8 @@ packages: - pyyaml >=6.0,<7.0 license: MIT license_family: MIT + purls: + - pkg:pypi/yte?source=hash-mapping run_exports: {} size: 16215 timestamp: 1764250734338 @@ -14671,6 +15638,8 @@ packages: - python >=3.9 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zict?source=hash-mapping run_exports: {} size: 36341 timestamp: 1733261642963 @@ -14682,6 +15651,8 @@ packages: - python license: MIT license_family: MIT + purls: + - pkg:pypi/zipp?source=hash-mapping run_exports: {} size: 24190 timestamp: 1779159948016 @@ -14693,6 +15664,7 @@ packages: - llvm-openmp >=9.0.1 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - _openmp_mutex >=4.5 @@ -14726,6 +15698,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/ast-serialize?source=compressed-mapping run_exports: {} size: 1085485 timestamp: 1786328966627 @@ -14741,6 +15715,7 @@ packages: - atk-1.0 2.38.0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - atk-1.0 >=2.38.0 @@ -14758,6 +15733,7 @@ packages: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 @@ -14775,6 +15751,7 @@ packages: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-auth >=0.7.30,<0.7.31.0a0 @@ -14789,6 +15766,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.7.4,<0.7.5.0a0 @@ -14802,6 +15780,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -14814,6 +15793,7 @@ packages: - __osx >=11.0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 @@ -14826,6 +15806,7 @@ packages: - __osx >=11.0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.9.28,<0.9.29.0a0 @@ -14839,6 +15820,7 @@ packages: - aws-c-common >=0.9.28,<0.9.29.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-compression >=0.2.19,<0.2.20.0a0 @@ -14852,6 +15834,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 @@ -14868,6 +15851,7 @@ packages: - libcxx >=17 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 @@ -14884,6 +15868,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -14900,6 +15885,7 @@ packages: - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 @@ -14916,6 +15902,7 @@ packages: - aws-c-io >=0.14.18,<0.14.19.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-http >=0.8.9,<0.8.10.0a0 @@ -14930,6 +15917,7 @@ packages: - aws-c-common >=0.9.28,<0.9.29.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-io >=0.14.18,<0.14.19.0a0 @@ -14945,6 +15933,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 @@ -14960,6 +15949,7 @@ packages: - aws-c-io >=0.14.18,<0.14.19.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 @@ -14975,6 +15965,7 @@ packages: - aws-c-http >=0.11.0,<0.11.1.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -14993,6 +15984,7 @@ packages: - aws-checksums >=0.2.10,<0.2.11.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 @@ -15011,6 +16003,7 @@ packages: - aws-checksums >=0.1.18,<0.1.19.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-s3 >=0.6.5,<0.6.6.0a0 @@ -15024,6 +16017,7 @@ packages: - aws-c-common >=0.9.28,<0.9.29.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 @@ -15037,6 +16031,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 @@ -15050,6 +16045,7 @@ packages: - aws-c-common >=0.9.28,<0.9.29.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-checksums >=0.1.18,<0.1.19.0a0 @@ -15063,6 +16059,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 @@ -15085,6 +16082,7 @@ packages: - libcxx >=17 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 @@ -15107,6 +16105,7 @@ packages: - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 @@ -15127,6 +16126,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 @@ -15145,6 +16145,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 @@ -15160,6 +16161,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.13.0,<1.13.1.0a0 @@ -15175,6 +16177,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -15190,6 +16193,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 @@ -15205,6 +16209,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.8.0,<1.8.1.0a0 @@ -15220,6 +16225,7 @@ packages: - libcxx >=16 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 @@ -15235,6 +16241,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -15252,6 +16259,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 @@ -15268,6 +16276,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 @@ -15284,6 +16293,7 @@ packages: - libcxx >=16 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.11.0,<12.11.1.0a0 @@ -15300,6 +16310,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 @@ -15317,6 +16328,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -15334,6 +16346,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -15349,6 +16362,7 @@ packages: - libbrotlienc 1.1.0 h6caf38d_4 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -15366,6 +16380,7 @@ packages: - libbrotlienc 1.2.0 hc919400_1 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -15382,6 +16397,7 @@ packages: - libbrotlienc 1.1.0 h6caf38d_4 license: MIT license_family: MIT + purls: [] run_exports: {} size: 17373 timestamp: 1756599741779 @@ -15394,6 +16410,7 @@ packages: - libbrotlienc 1.2.0 hc919400_1 license: MIT license_family: MIT + purls: [] run_exports: {} size: 18628 timestamp: 1764018033635 @@ -15410,6 +16427,8 @@ packages: - libbrotlicommon 1.1.0 h6caf38d_4 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 341750 timestamp: 1756600036931 @@ -15426,6 +16445,8 @@ packages: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 359854 timestamp: 1764018178608 @@ -15436,6 +16457,7 @@ packages: - __osx >=11.0 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -15448,6 +16470,7 @@ packages: - __osx >=11.0 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -15460,6 +16483,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.6,<2.0a0 @@ -15474,6 +16498,7 @@ packages: - c-ares-static <0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 @@ -15495,6 +16520,7 @@ packages: - libzlib >=1.3.1,<2.0a0 - pixman >=0.44.2,<1.0a0 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -15517,6 +16543,7 @@ packages: - libzlib >=1.3.1,<2.0a0 - pixman >=0.46.4,<1.0a0 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -15534,6 +16561,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping run_exports: {} size: 288080 timestamp: 1761203317419 @@ -15561,6 +16590,8 @@ packages: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: MIT + purls: + - pkg:pypi/cffi?source=compressed-mapping run_exports: {} size: 297356 timestamp: 1786528517794 @@ -15576,6 +16607,7 @@ packages: - libgfortran5 >=13.2.0 - libzlib >=1.3.1,<2.0a0 license: LicenseRef-fitsio + purls: [] run_exports: weak: - cfitsio >=4.4.1,<4.4.2.0a0 @@ -15603,6 +16635,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -15629,6 +16662,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -15654,6 +16688,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -15678,6 +16713,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -15699,6 +16735,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 @@ -15777,6 +16814,8 @@ packages: - conda-env >=2.6 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda?source=hash-mapping run_exports: {} size: 1468859 timestamp: 1785769918540 @@ -15819,6 +16858,8 @@ packages: - conda >=26.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/conda-pypi?source=hash-mapping run_exports: {} size: 328032 timestamp: 1784203150047 @@ -15834,6 +16875,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 286084 timestamp: 1769156157865 @@ -15849,6 +16892,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 290405 timestamp: 1769156069514 @@ -15859,6 +16904,7 @@ packages: - libcxx >=19 - __osx >=11.0 license: CC0-1.0 + purls: [] run_exports: {} size: 25909 timestamp: 1785920661291 @@ -15884,6 +16930,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1638054 timestamp: 1763019931024 @@ -15900,6 +16948,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1639272 timestamp: 1763019908165 @@ -15917,6 +16967,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 181023 timestamp: 1785500142429 @@ -15933,6 +16984,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 153833 timestamp: 1722440571650 @@ -15947,6 +16999,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD + purls: [] run_exports: weak: - cyrus-sasl >=2.1.28,<3.0a0 @@ -15963,6 +17016,8 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 591797 timestamp: 1771856474133 @@ -15977,6 +17032,8 @@ packages: - toolz >=0.10.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 615096 timestamp: 1771856205834 @@ -15991,6 +17048,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 2776045 timestamp: 1780390212997 @@ -16001,6 +17060,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - epoxy >=1.5.10,<1.6.0a0 @@ -16021,6 +17081,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 524384 timestamp: 1778865060800 @@ -16039,6 +17101,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 535738 timestamp: 1778865753466 @@ -16062,6 +17126,8 @@ packages: - shapely license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 1025443 timestamp: 1726664909376 @@ -16083,6 +17149,8 @@ packages: - shapely license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 1059210 timestamp: 1767051558277 @@ -16094,6 +17162,7 @@ packages: - libcxx >=18 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=11.0.2,<11.1.0a0 @@ -16117,6 +17186,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=12.1.0,<12.2.0a0 @@ -16134,6 +17204,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.1,<3.0a0 @@ -16152,6 +17223,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 @@ -16171,6 +17243,8 @@ packages: - unicodedata2 >=15.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 2904377 timestamp: 1778771054844 @@ -16181,6 +17255,7 @@ packages: - libfreetype 2.14.3 hce30654_1 - libfreetype6 2.14.3 hdfa99f5_1 license: GPL-2.0-only OR FTL + purls: [] run_exports: weak: - libfreetype >=2.14.3 @@ -16197,6 +17272,7 @@ packages: - minizip >=4.0.7,<5.0a0 license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 @@ -16208,6 +17284,7 @@ packages: depends: - __osx >=11.0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 @@ -16236,6 +17313,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/gdal?source=hash-mapping run_exports: {} size: 1662506 timestamp: 1728293877630 @@ -16267,6 +17346,7 @@ packages: - libtiff >=4.7.1,<4.8.0a0 license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - gdk-pixbuf >=2.44.7,<3.0a0 @@ -16279,6 +17359,7 @@ packages: - __osx >=11.0 - libcxx >=16 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.12.2,<3.12.3.0a0 @@ -16291,6 +17372,7 @@ packages: - __osx >=11.0 - libcxx >=19 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 @@ -16309,6 +17391,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - geotiff >=1.7.3,<1.8.0a0 @@ -16322,6 +17405,7 @@ packages: - libcxx >=17 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - gflags >=2.2.2,<2.3.0a0 @@ -16335,6 +17419,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - gflags >=2.3.1,<2.4.0a0 @@ -16345,6 +17430,7 @@ packages: md5: 95fa1486c77505330c20f7202492b913 license: MIT license_family: MIT + purls: [] run_exports: weak: - giflib >=5.2.2,<5.3.0a0 @@ -16357,6 +17443,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - giflib >=5.2.2,<5.3.0a0 @@ -16382,6 +17469,7 @@ packages: - __osx >=11.0 - libintl >=0.25.1,<1.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: {} size: 204965 timestamp: 1786457780347 @@ -16394,6 +17482,7 @@ packages: - libcxx >=19 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 @@ -16408,6 +17497,7 @@ packages: - libcxx >=16 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - glog >=0.7.1,<0.8.0a0 @@ -16418,6 +17508,7 @@ packages: md5: 55beafb01e44f9527026dbffcea260ee license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 1829675 timestamp: 1775514584688 @@ -16429,6 +17520,7 @@ packages: - __osx >=11.0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 @@ -16465,6 +17557,7 @@ packages: - pango >=1.56.4,<2.0a0 license: EPL-1.0 license_family: Other + purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 @@ -16494,6 +17587,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/greenlet?source=compressed-mapping run_exports: {} size: 274201 timestamp: 1786384265137 @@ -16520,6 +17615,7 @@ packages: - pango >=1.56.4,<2.0a0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - gtk3 >=3.24.52,<4.0a0 @@ -16534,6 +17630,7 @@ packages: - libglib >=2.76.3,<3.0a0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - gts >=0.7.6,<0.8.0a0 @@ -16564,6 +17661,7 @@ packages: - libharfbuzz-devel 14.3.0 h5a65909_0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -16578,6 +17676,7 @@ packages: - libzlib >=1.2.13,<2.0.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf4 >=4.2.15,<4.2.16.0a0 @@ -16598,6 +17697,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf5 >=1.14.3,<1.14.4.0a0 @@ -16608,6 +17708,7 @@ packages: md5: cfb39109ac5fa8601eb595d66d5bf156 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: {} size: 17616 timestamp: 1771539622983 @@ -16618,6 +17719,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - icu >=75.1,<76.0a0 @@ -16638,6 +17740,7 @@ packages: depends: - __osx >=11.0 license: MIT + purls: [] run_exports: weak: - icu >=78.3,<79.0a0 @@ -16653,6 +17756,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 52567 timestamp: 1757685575891 @@ -16663,6 +17768,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - json-c >=0.17,<0.18.0a0 @@ -16675,6 +17781,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - json-c >=0.18,<0.19.0a0 @@ -16689,6 +17796,7 @@ packages: - libcxx >=17 license: MIT license_family: MIT + purls: [] run_exports: weak: - kealib >=1.5.3,<1.6.0a0 @@ -16705,6 +17813,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 68490 timestamp: 1773067215781 @@ -16719,6 +17829,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 69284 timestamp: 1773067285911 @@ -16733,6 +17845,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.21.3,<1.22.0a0 @@ -16749,6 +17862,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 @@ -16763,6 +17877,7 @@ packages: - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 @@ -16776,6 +17891,7 @@ packages: - libcxx >=19 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.1.0,<5.0a0 @@ -16789,6 +17905,7 @@ packages: - libcxx >=19 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 @@ -16805,6 +17922,7 @@ packages: - libabseil-static =20240116.2=cxx17* license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20240116.2,<20240117.0a0 @@ -16822,6 +17940,7 @@ packages: - abseil-cpp =20260526.0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 @@ -16836,6 +17955,7 @@ packages: - libcxx >=19 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libaec >=1.1.5,<2.0a0 @@ -16857,6 +17977,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.7.7,<3.8.0a0 @@ -16898,6 +18019,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 @@ -16939,6 +18061,7 @@ packages: - arrow-cpp <0.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=17.0.0,<17.1.0a0 @@ -16978,6 +18101,7 @@ packages: - parquet-cpp <0.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 @@ -16993,6 +18117,7 @@ packages: - libcxx >=17 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=17.0.0,<17.1.0a0 @@ -17013,6 +18138,7 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -17035,6 +18161,7 @@ packages: - re2 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 @@ -17052,6 +18179,7 @@ packages: - libparquet 17.0.0 hf0ba9ef_13_cpu license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=17.0.0,<17.1.0a0 @@ -17074,6 +18202,7 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 @@ -17094,6 +18223,7 @@ packages: - libprotobuf >=4.25.3,<4.25.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=17.0.0,<17.1.0a0 @@ -17114,6 +18244,7 @@ packages: - libprotobuf >=7.35.1,<7.35.2.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 @@ -17134,6 +18265,7 @@ packages: - mkl <2027 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -17154,6 +18286,7 @@ packages: - mkl <2027 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -17166,6 +18299,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -17178,6 +18312,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -17191,6 +18326,7 @@ packages: - libbrotlicommon 1.1.0 h6caf38d_4 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.1.0,<1.2.0a0 @@ -17204,6 +18340,7 @@ packages: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 @@ -17217,6 +18354,7 @@ packages: - libbrotlicommon 1.1.0 h6caf38d_4 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.1.0,<1.2.0a0 @@ -17230,6 +18368,7 @@ packages: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 @@ -17247,6 +18386,7 @@ packages: - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -17264,6 +18404,7 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -17276,6 +18417,7 @@ packages: - libcxx >=11.1.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 @@ -17310,6 +18452,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 @@ -17327,6 +18470,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.9.1,<9.0a0 @@ -17339,6 +18483,7 @@ packages: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: {} size: 569349 timestamp: 1781670209146 @@ -17349,6 +18494,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -17361,6 +18507,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -17375,6 +18522,7 @@ packages: - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 @@ -17387,6 +18535,7 @@ packages: - __osx >=11.0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 @@ -17397,6 +18546,7 @@ packages: md5: 36d33e440c31857372a72137f78bacf5 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 @@ -17409,6 +18559,7 @@ packages: - openssl >=3.1.1,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -17423,6 +18574,7 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT + purls: [] run_exports: {} size: 69362 timestamp: 1781203631990 @@ -17433,6 +18585,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.5.2,<3.6.0a0 @@ -17445,6 +18598,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 @@ -17456,6 +18610,7 @@ packages: depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 8365 timestamp: 1780933612390 @@ -17469,6 +18624,7 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 338442 timestamp: 1780933611662 @@ -17482,6 +18638,7 @@ packages: - libgomp 15.2.0 19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 404080 timestamp: 1778273064154 @@ -17495,6 +18652,7 @@ packages: - libgcc-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 364162 timestamp: 1785374452947 @@ -17517,6 +18675,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: GD license_family: BSD + purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 @@ -17541,6 +18700,7 @@ packages: - libgdal-xls 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -17585,6 +18745,7 @@ packages: - libgdal 3.12.4.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 @@ -17627,6 +18788,7 @@ packages: - libgdal 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -17643,6 +18805,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 466138 timestamp: 1728294964843 @@ -17657,6 +18820,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 652171 timestamp: 1728295096895 @@ -17672,6 +18836,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 578331 timestamp: 1728295222220 @@ -17686,6 +18851,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 591844 timestamp: 1728295364313 @@ -17700,6 +18866,7 @@ packages: - openjpeg >=2.5.2,<3.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 462891 timestamp: 1728295482655 @@ -17716,6 +18883,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 469587 timestamp: 1728296301261 @@ -17734,6 +18902,7 @@ packages: - libnetcdf >=4.9.2,<4.9.3.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 668071 timestamp: 1728296444763 @@ -17748,6 +18917,7 @@ packages: - poppler license: MIT license_family: MIT + purls: [] run_exports: {} size: 598629 timestamp: 1728295629149 @@ -17763,6 +18933,7 @@ packages: - postgresql license: MIT license_family: MIT + purls: [] run_exports: {} size: 503494 timestamp: 1728295758903 @@ -17778,6 +18949,7 @@ packages: - postgresql license: MIT license_family: MIT + purls: [] run_exports: {} size: 468214 timestamp: 1728295893350 @@ -17792,6 +18964,7 @@ packages: - tiledb >=2.26.0,<2.27.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 616363 timestamp: 1726096931827 @@ -17806,6 +18979,7 @@ packages: - libkml >=1.3.0,<1.4.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 432445 timestamp: 1728296164752 @@ -17818,6 +18992,7 @@ packages: - libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 139675 timestamp: 1778273280875 @@ -17830,6 +19005,7 @@ packages: - libgfortran-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 98528 timestamp: 1785374566402 @@ -17842,6 +19018,7 @@ packages: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 599691 timestamp: 1778273075448 @@ -17854,6 +19031,7 @@ packages: - libgfortran 16.1.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 556657 timestamp: 1785374459225 @@ -17870,6 +19048,7 @@ packages: constrains: - glib 2.84.0 *_0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.84.0,<3.0a0 @@ -17903,6 +19082,7 @@ packages: constrains: - glib >2.66 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 @@ -17924,6 +19104,7 @@ packages: - libgoogle-cloud 2.28.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=2.28.0,<2.29.0a0 @@ -17946,6 +19127,7 @@ packages: - libgoogle-cloud 3.8.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=3.8.0,<3.9.0a0 @@ -17965,6 +19147,7 @@ packages: - openssl license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 @@ -17984,6 +19167,7 @@ packages: - openssl license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 @@ -18006,6 +19190,7 @@ packages: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.62.2,<1.63.0a0 @@ -18029,6 +19214,7 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 @@ -18050,6 +19236,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 942277 timestamp: 1785770026085 @@ -18071,6 +19258,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -18083,6 +19271,7 @@ packages: - __osx >=11.0 - libcxx >=19 license: Apache-2.0 OR BSD-3-Clause + purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 @@ -18094,6 +19283,7 @@ packages: depends: - __osx >=11.0 license: LGPL-2.1-only + purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 @@ -18106,6 +19296,7 @@ packages: - __osx >=11.0 - libiconv >=1.18,<2.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libintl >=0.25.1,<1.0a0 @@ -18119,6 +19310,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.1.4.1,<4.0a0 @@ -18132,6 +19324,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 @@ -18148,6 +19341,7 @@ packages: - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 @@ -18164,6 +19358,7 @@ packages: - uriparser >=0.9.8,<1.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 283804 timestamp: 1777027670481 @@ -18179,6 +19374,7 @@ packages: - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -18196,6 +19392,7 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -18227,6 +19424,7 @@ packages: - blas 2.309 openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapacke >=3.11.0,<3.12.0a0 @@ -18240,6 +19438,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -18253,6 +19452,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -18265,6 +19465,7 @@ packages: - __osx >=11.0 - liblzma 5.8.3 h8088a28_0 license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -18316,6 +19517,7 @@ packages: - reproc-cpp >=14.2.7.post0,<14.3.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libmamba >=2.9.0,<2.10.0a0 @@ -18341,6 +19543,7 @@ packages: - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 23751 timestamp: 1786125740624 @@ -18388,6 +19591,8 @@ packages: - nlohmann_json-abi ==3.12.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/libmambapy?source=hash-mapping run_exports: weak: - libmambapy >=2.9.0,<2.10.0a0 @@ -18400,6 +19605,7 @@ packages: - __osx >=11.0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: {} size: 73690 timestamp: 1769482560514 @@ -18423,6 +19629,7 @@ packages: constrains: - msgpack-c ==6.1.0 *_7 license: BSL-1.0 + purls: [] run_exports: weak: - libmsgpack-c >=6.1.0,<7.0a0 @@ -18448,6 +19655,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnetcdf >=4.9.2,<4.9.3.0a0 @@ -18466,6 +19674,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 @@ -18477,6 +19686,7 @@ packages: depends: - __osx >=11.0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libntlm >=1.8,<2.0a0 @@ -18494,6 +19704,7 @@ packages: - openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libopenblas >=0.3.33,<1.0a0 @@ -18511,6 +19722,7 @@ packages: - openblas >=0.3.34,<0.3.35.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libopenblas >=0.3.34,<1.0a0 @@ -18533,6 +19745,7 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -18543,6 +19756,7 @@ packages: md5: 7f61001d82fd50385d4f9fb57f936e95 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 394664 timestamp: 1783133038717 @@ -18558,6 +19772,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=17.0.0,<17.1.0a0 @@ -18579,6 +19794,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 @@ -18591,6 +19807,7 @@ packages: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement + purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 @@ -18606,6 +19823,7 @@ packages: - openldap >=2.6.8,<2.7.0a0 - openssl >=3.4.0,<4.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -18622,6 +19840,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=4.25.3,<4.25.4.0a0 @@ -18638,6 +19857,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 @@ -18652,6 +19872,7 @@ packages: - icu >=78.3,<79.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 @@ -18668,6 +19889,7 @@ packages: - fribidi >=1.0.16,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 @@ -18684,6 +19906,7 @@ packages: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -18701,6 +19924,7 @@ packages: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -18722,6 +19946,7 @@ packages: constrains: - __osx >=11.0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - librsvg >=2.62.3,<3.0a0 @@ -18736,6 +19961,7 @@ packages: - libcxx >=16 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -18750,6 +19976,7 @@ packages: - libcxx >=19 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -18761,6 +19988,7 @@ packages: depends: - __osx >=11.0 license: ISC + purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 @@ -18775,6 +20003,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 @@ -18801,6 +20030,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -18825,6 +20055,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -18847,6 +20078,7 @@ packages: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -18860,6 +20092,7 @@ packages: - icu >=78.3,<79.0a0 - libzlib >=1.3.2,<2.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -18873,6 +20106,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 @@ -18889,6 +20123,7 @@ packages: - openssl >=3.3.1,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.20.0,<0.20.1.0a0 @@ -18905,6 +20140,7 @@ packages: - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 @@ -18924,6 +20160,7 @@ packages: - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.1,<4.8.0a0 @@ -18943,6 +20180,7 @@ packages: - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 @@ -18955,6 +20193,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 @@ -18967,6 +20206,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.8.0,<2.9.0a0 @@ -18981,6 +20221,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -18995,6 +20236,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -19010,6 +20252,7 @@ packages: - xorg-libxdmcp license: MIT license_family: MIT + purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 @@ -19028,6 +20271,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + purls: [] run_exports: {} size: 466360 timestamp: 1776377102261 @@ -19042,6 +20286,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 >=2.13.9,<2.14.0a0 @@ -19059,6 +20304,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -19078,6 +20324,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -19092,6 +20339,7 @@ packages: - libxml2 >=2.13.8,<2.14.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 @@ -19107,6 +20355,7 @@ packages: - openssl >=3.3.2,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libzip >=1.11.2,<2.0a0 @@ -19121,6 +20370,7 @@ packages: - zlib 1.3.2 *_2 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -19135,6 +20385,7 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -19150,6 +20401,7 @@ packages: - openmp 22.1.8|22.1.8.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE + purls: [] run_exports: strong: - llvm-openmp >=22.1.8 @@ -19166,6 +20418,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 108178 timestamp: 1725089672204 @@ -19181,6 +20435,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 127471 timestamp: 1765026415723 @@ -19192,6 +20448,7 @@ packages: - libcxx >=18 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 @@ -19204,6 +20461,7 @@ packages: - libcxx >=14.0.6 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.9.4,<1.10.0a0 @@ -19216,6 +20474,7 @@ packages: - __osx >=11.0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 @@ -19233,6 +20492,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 25564 timestamp: 1772445846939 @@ -19248,6 +20509,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 27256 timestamp: 1772445397216 @@ -19261,6 +20524,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 17770 timestamp: 1777001080046 @@ -19274,6 +20538,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 14958 timestamp: 1785211126869 @@ -19302,6 +20567,8 @@ packages: - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8191891 timestamp: 1777001043842 @@ -19330,6 +20597,8 @@ packages: - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8776291 timestamp: 1785211103855 @@ -19352,6 +20621,8 @@ packages: - tomli-w - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT + purls: + - pkg:pypi/menuinst?source=hash-mapping run_exports: {} size: 207074 timestamp: 1786008880338 @@ -19369,6 +20640,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.1,<5.0a0 @@ -19388,6 +20660,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 @@ -19404,6 +20677,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 104400 timestamp: 1782460860576 @@ -19430,6 +20705,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 103113 timestamp: 1786217418606 @@ -19442,6 +20719,7 @@ packages: - llvm-openmp >=18.1.8 license: MIT license_family: MIT + purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 @@ -19479,6 +20757,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/mypy?source=hash-mapping run_exports: {} size: 13696727 timestamp: 1783986170854 @@ -19488,6 +20768,7 @@ packages: depends: - __osx >=11.0 license: X11 AND BSD-3-Clause + purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -19499,6 +20780,7 @@ packages: depends: - __osx >=11.0 license: X11 AND BSD-3-Clause + purls: [] run_exports: weak: - ncurses >=6.6,<7.0a0 @@ -19511,6 +20793,7 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 137984 timestamp: 1785920576349 @@ -19522,6 +20805,7 @@ packages: - libcxx >=19 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - nspr >=4.38,<5.0a0 @@ -19538,6 +20822,7 @@ packages: - nspr >=4.38,<5.0a0 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - nss >=3.118,<4.0a0 @@ -19558,6 +20843,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.26.4,<2.0a0 @@ -19595,6 +20882,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.25,<3 @@ -19611,6 +20900,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - openjpeg >=2.5.4,<3.0a0 @@ -19627,6 +20917,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: OLDAP-2.8 license_family: BSD + purls: [] run_exports: weak: - openldap >=2.6.10,<2.7.0a0 @@ -19640,6 +20931,7 @@ packages: - ca-certificates license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -19653,6 +20945,7 @@ packages: - ca-certificates license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -19672,6 +20965,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - orc >=2.0.2,<2.0.3.0a0 @@ -19693,6 +20987,7 @@ packages: - libabseil * cxx17* license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 @@ -19751,6 +21046,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 13926263 timestamp: 1778602825408 @@ -19862,6 +21159,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 14402418 timestamp: 1784821943837 @@ -19901,6 +21200,7 @@ packages: - libpng >=1.6.58,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - pango >=1.58.2,<2.0a0 @@ -19915,6 +21215,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.44,<10.45.0a0 @@ -19929,6 +21230,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 @@ -19953,6 +21255,8 @@ packages: - openjpeg >=2.5.4,<3.0a0 - libwebp-base >=1.6.0,<2.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 965082 timestamp: 1775060469004 @@ -19975,6 +21279,8 @@ packages: - tk >=8.6.13,<8.7.0a0 - lcms2 >=2.19.1,<3.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 1019767 timestamp: 1782912213683 @@ -19986,6 +21292,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -19999,6 +21306,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -20029,6 +21337,7 @@ packages: - poppler-data license: GPL-2.0-only license_family: GPL + purls: [] run_exports: {} size: 1514138 timestamp: 1724660307740 @@ -20051,6 +21360,7 @@ packages: - tzdata - zstd >=1.5.6,<1.6.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -20070,6 +21380,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.4.1,<9.5.0a0 @@ -20091,6 +21402,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -20107,6 +21419,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 @@ -20122,6 +21435,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 239031 timestamp: 1769678393511 @@ -20135,6 +21450,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 245502 timestamp: 1769678303655 @@ -20145,6 +21462,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 9238 timestamp: 1786068031100 @@ -20155,6 +21473,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 8381 timestamp: 1726802424786 @@ -20169,6 +21488,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 229818 timestamp: 1757853438761 @@ -20202,6 +21523,8 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/py-rattler?source=hash-mapping run_exports: {} size: 10605428 timestamp: 1784707901992 @@ -20219,6 +21542,7 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 25811 timestamp: 1730169125041 @@ -20235,6 +21559,7 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 25915 timestamp: 1784258318884 @@ -20255,6 +21580,8 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 3990396 timestamp: 1730169098217 @@ -20274,6 +21601,8 @@ packages: - numpy >=1.23,<3 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 4030783 timestamp: 1784258302127 @@ -20298,6 +21627,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pycosat?source=hash-mapping run_exports: {} size: 93498 timestamp: 1784146274338 @@ -20314,6 +21645,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1720774 timestamp: 1778084314791 @@ -20330,6 +21663,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1722694 timestamp: 1778084354332 @@ -20347,6 +21682,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 562964 timestamp: 1727771822945 @@ -20364,6 +21701,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 599877 timestamp: 1764402722213 @@ -20379,6 +21718,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=hash-mapping run_exports: {} size: 485985 timestamp: 1725436209323 @@ -20394,6 +21735,8 @@ packages: - proj >=9.8.1,<9.9.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} size: 533238 timestamp: 1786351900726 @@ -20416,6 +21759,7 @@ packages: constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.12.* *_cp312 @@ -20468,6 +21812,7 @@ packages: - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 @@ -20498,6 +21843,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/librt?source=compressed-mapping run_exports: {} size: 134968 timestamp: 1786328898613 @@ -20511,6 +21858,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 175381 timestamp: 1778689065100 @@ -20525,6 +21874,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 187278 timestamp: 1770223990452 @@ -20539,6 +21890,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 189475 timestamp: 1770223788648 @@ -20555,6 +21908,8 @@ packages: - cpython >=3.12 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} size: 191432 timestamp: 1779484184540 @@ -20565,6 +21920,7 @@ packages: - __osx >=11.0 - libcxx >=16 license: LicenseRef-Qhull + purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 @@ -20593,6 +21949,8 @@ packages: - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7233883 timestamp: 1726176805073 @@ -20618,6 +21976,8 @@ packages: - snuggs >=1.4.1 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7369572 timestamp: 1785965949045 @@ -20628,6 +21988,7 @@ packages: - libre2-11 2023.09.01 h7b2c953_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -20641,6 +22002,7 @@ packages: - libre2-11 2025.11.05 h5d15848_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -20655,6 +22017,7 @@ packages: - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: weak: - readline >=8.3,<9.0a0 @@ -20667,6 +22030,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc >=14.2.7.post0,<14.3.0a0 @@ -20691,6 +22055,7 @@ packages: - reproc >=14.2.7.post0,<14.3.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc-cpp >=14.2.7.post0,<14.3.0a0 @@ -20731,6 +22096,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} size: 285627 timestamp: 1782831308617 @@ -20745,6 +22112,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 311922 timestamp: 1766175797575 @@ -20758,6 +22127,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} size: 133016 timestamp: 1766159585543 @@ -20785,6 +22156,8 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: + - pkg:pypi/ruff?source=hash-mapping run_exports: {} size: 8732289 timestamp: 1786152041950 @@ -20796,6 +22169,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - s2n >=1.7.5,<1.7.6.0a0 @@ -20819,6 +22193,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9400478 timestamp: 1780401354221 @@ -20840,6 +22216,8 @@ packages: - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9667030 timestamp: 1780401292916 @@ -20861,6 +22239,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 13936510 timestamp: 1779874824714 @@ -20882,6 +22262,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 14122215 timestamp: 1781912992503 @@ -20897,6 +22279,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 531604 timestamp: 1725394221848 @@ -20912,6 +22296,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 618928 timestamp: 1762524178138 @@ -20932,6 +22318,7 @@ packages: - __osx >=11.0 - libcxx >=19 license: Apache-2.0 + purls: [] run_exports: weak: - simdjson >=4.6.6,<4.7.0a0 @@ -20947,6 +22334,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 160192 timestamp: 1777112397342 @@ -20960,6 +22349,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 163732 timestamp: 1777112456003 @@ -20971,6 +22362,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 @@ -20985,6 +22377,7 @@ packages: - libcxx >=16 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.14.1,<1.15.0a0 @@ -20999,6 +22392,7 @@ packages: - libcxx >=19 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.17.0,<1.18.0a0 @@ -21039,6 +22433,8 @@ packages: - __osx >=11.0 - python_abi 3.14.* *_cp314 license: MIT + purls: + - pkg:pypi/sqlalchemy?source=compressed-mapping run_exports: {} size: 4044830 timestamp: 1786535242452 @@ -21052,6 +22448,7 @@ packages: - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -21068,6 +22465,7 @@ packages: - ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -21100,6 +22498,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - tiledb >=2.26.0,<2.27.0a0 @@ -21113,6 +22512,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -21125,6 +22525,7 @@ packages: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: TCL + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -21140,6 +22541,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 863360 timestamp: 1781007464047 @@ -21165,6 +22568,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 922067 timestamp: 1786227125229 @@ -21175,6 +22580,7 @@ packages: - __osx >=11.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 66995 timestamp: 1776960443135 @@ -21188,6 +22594,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 415828 timestamp: 1770909782683 @@ -21201,6 +22609,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 416130 timestamp: 1770909728445 @@ -21212,6 +22622,7 @@ packages: - libcxx >=16 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 @@ -21227,6 +22638,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 61800 timestamp: 1756851815321 @@ -21239,6 +22652,7 @@ packages: - libcxx >=17 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.2.5,<3.3.0a0 @@ -21253,6 +22667,7 @@ packages: - libcxx >=19 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 @@ -21265,6 +22680,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -21277,6 +22693,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -21289,6 +22706,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -21301,6 +22719,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -21316,6 +22735,7 @@ packages: - xz-gpl-tools 5.8.3 hd0f0c4f_0 - xz-tools 5.8.3 h8088a28_0 license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -21330,6 +22750,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -21344,6 +22765,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD AND LGPL-2.1-or-later + purls: [] run_exports: {} size: 85931 timestamp: 1775825857949 @@ -21354,6 +22776,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml >=0.2.5,<0.3.0a0 @@ -21367,6 +22790,7 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml-cpp >=0.8.0,<0.9.0a0 @@ -21392,6 +22816,7 @@ packages: - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - zeromq >=4.3.5,<4.4.0a0 @@ -21405,6 +22830,7 @@ packages: - libzlib 1.3.2 h8088a28_2 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -21418,6 +22844,7 @@ packages: - libzlib 1.3.2 h8088a28_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -21431,6 +22858,7 @@ packages: - libcxx >=19 license: Zlib license_family: Other + purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 @@ -21449,6 +22877,8 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 390920 timestamp: 1762512713481 @@ -21465,6 +22895,8 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 397786 timestamp: 1762512730914 @@ -21476,6 +22908,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 @@ -21493,6 +22926,7 @@ packages: - msys2-conda-epoch <0.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -21526,6 +22960,8 @@ packages: - cpython >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/ast-serialize?source=compressed-mapping run_exports: {} size: 1071375 timestamp: 1786328955132 @@ -21543,6 +22979,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-auth >=0.10.4,<0.10.5.0a0 @@ -21562,6 +22999,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-auth >=0.7.30,<0.7.31.0a0 @@ -21578,6 +23016,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.7.4,<0.7.5.0a0 @@ -21593,6 +23032,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-cal >=0.9.14,<0.9.15.0a0 @@ -21607,6 +23047,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.14.2,<0.14.3.0a0 @@ -21621,6 +23062,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-common >=0.9.28,<0.9.29.0a0 @@ -21636,6 +23078,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-compression >=0.2.19,<0.2.20.0a0 @@ -21651,6 +23094,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-compression >=0.3.2,<0.3.3.0a0 @@ -21668,6 +23112,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-event-stream >=0.4.3,<0.4.4.0a0 @@ -21685,6 +23130,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-event-stream >=0.7.1,<0.7.2.0a0 @@ -21703,6 +23149,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-http >=0.11.0,<0.11.1.0a0 @@ -21721,6 +23168,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-http >=0.8.9,<0.8.10.0a0 @@ -21737,6 +23185,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-io >=0.14.18,<0.14.19.0a0 @@ -21753,6 +23202,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-io >=0.27.3,<0.27.4.0a0 @@ -21770,6 +23220,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-mqtt >=0.10.5,<0.10.6.0a0 @@ -21787,6 +23238,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 @@ -21807,6 +23259,7 @@ packages: - aws-c-io >=0.27.3,<0.27.4.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-s3 >=0.12.8,<0.12.9.0a0 @@ -21827,6 +23280,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-s3 >=0.6.5,<0.6.6.0a0 @@ -21842,6 +23296,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-c-sdkutils >=0.1.19,<0.1.20.0a0 @@ -21857,6 +23312,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 @@ -21872,6 +23328,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-checksums >=0.1.20,<0.1.21.0a0 @@ -21887,6 +23344,7 @@ packages: - aws-c-common >=0.14.2,<0.14.3.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-checksums >=0.2.10,<0.2.11.0a0 @@ -21910,6 +23368,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-crt-cpp >=0.28.2,<0.28.3.0a0 @@ -21933,6 +23392,7 @@ packages: - aws-c-mqtt >=0.16.0,<0.16.1.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 @@ -21952,6 +23412,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.379,<1.11.380.0a0 @@ -21970,6 +23431,7 @@ packages: - aws-crt-cpp >=0.40.1,<0.40.2.0a0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 @@ -21985,6 +23447,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.13.0,<1.13.1.0a0 @@ -21999,6 +23462,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-core-cpp >=1.16.3,<1.16.4.0a0 @@ -22014,6 +23478,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.13.3,<1.13.4.0a0 @@ -22029,6 +23494,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-identity-cpp >=1.8.0,<1.8.1.0a0 @@ -22045,6 +23511,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.12.0,<12.12.1.0a0 @@ -22061,6 +23528,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 @@ -22076,6 +23544,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 @@ -22091,6 +23560,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-common-cpp >=12.7.0,<12.7.1.0a0 @@ -22108,6 +23578,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 @@ -22126,6 +23597,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -22144,6 +23616,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - blosc >=1.21.6,<2.0a0 @@ -22161,6 +23634,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -22180,6 +23654,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -22198,6 +23673,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: {} size: 21425 timestamp: 1756599802301 @@ -22212,6 +23688,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: {} size: 22714 timestamp: 1764017952449 @@ -22228,6 +23705,8 @@ packages: - libbrotlicommon 1.1.0 hfd05255_4 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 323090 timestamp: 1756599941278 @@ -22244,6 +23723,8 @@ packages: - libbrotlicommon 1.2.0 hfd05255_1 license: MIT license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping run_exports: {} size: 335782 timestamp: 1764018443683 @@ -22256,6 +23737,7 @@ packages: - vc14_runtime >=14.44.35208 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -22270,6 +23752,7 @@ packages: - vc14_runtime >=14.44.35208 license: bzip2-1.0.6 license_family: BSD + purls: [] run_exports: weak: - bzip2 >=1.0.8,<2.0a0 @@ -22284,6 +23767,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.6,<2.0a0 @@ -22300,6 +23784,7 @@ packages: - c-ares-static <0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - c-ares >=1.34.8,<2.0a0 @@ -22323,6 +23808,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -22345,6 +23831,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LGPL-2.1-only or MPL-1.1 + purls: [] run_exports: weak: - cairo >=1.18.4,<2.0a0 @@ -22362,6 +23849,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping run_exports: {} size: 291324 timestamp: 1761203195397 @@ -22390,6 +23879,8 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT + purls: + - pkg:pypi/cffi?source=compressed-mapping run_exports: {} size: 347503 timestamp: 1786528551392 @@ -22403,6 +23894,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LicenseRef-fitsio + purls: [] run_exports: weak: - cfitsio >=4.4.1,<4.4.2.0a0 @@ -22426,6 +23918,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cbc >=2.10.13,<2.11.0a0 @@ -22448,6 +23941,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-cgl >=0.60,<0.61.0a0 @@ -22469,6 +23963,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-clp >=1.17,<1.18.0a0 @@ -22489,6 +23984,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-osi >=0.108,<0.109.0a0 @@ -22508,6 +24004,7 @@ packages: - coincbc * *_metapackage license: EPL-2.0 license_family: OTHER + purls: [] run_exports: weak: - coin-or-utils >=2.11,<2.12.0a0 @@ -22584,6 +24081,8 @@ packages: - conda-env >=2.6 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/conda?source=hash-mapping run_exports: {} size: 1472250 timestamp: 1785769861157 @@ -22624,6 +24123,8 @@ packages: - conda >=26.1.0 license: MIT license_family: MIT + purls: + - pkg:pypi/conda-pypi?source=hash-mapping run_exports: {} size: 323375 timestamp: 1784203061870 @@ -22639,6 +24140,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 244035 timestamp: 1769155978578 @@ -22654,6 +24157,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping run_exports: {} size: 247437 timestamp: 1769155978556 @@ -22678,6 +24183,7 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: CC0-1.0 + purls: [] run_exports: {} size: 22027 timestamp: 1785920525392 @@ -22695,6 +24201,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1645185 timestamp: 1763019902333 @@ -22712,6 +24220,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/cramjam?source=hash-mapping run_exports: {} size: 1644554 timestamp: 1763019901149 @@ -22729,6 +24239,7 @@ packages: - vc14_runtime >=14.44.35208 license: curl license_family: MIT + purls: [] run_exports: {} size: 189174 timestamp: 1785500250348 @@ -22745,6 +24256,7 @@ packages: - vc14_runtime >=14.29.30139 license: curl license_family: MIT + purls: [] run_exports: {} size: 157453 timestamp: 1722440281830 @@ -22760,6 +24272,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 563651 timestamp: 1771855915942 @@ -22775,6 +24289,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping run_exports: {} size: 582082 timestamp: 1771855937157 @@ -22789,6 +24305,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/debugpy?source=hash-mapping run_exports: {} size: 4022782 timestamp: 1780390190830 @@ -22801,6 +24319,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - double-conversion >=3.3.1,<3.4.0a0 @@ -22815,6 +24334,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - double-conversion >=3.4.0,<3.5.0a0 @@ -22836,6 +24356,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 511190 timestamp: 1778864902674 @@ -22855,6 +24377,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fastparquet?source=hash-mapping run_exports: {} size: 523153 timestamp: 1778864931353 @@ -22878,6 +24402,8 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 976102 timestamp: 1726664869512 @@ -22899,6 +24425,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/fiona?source=hash-mapping run_exports: {} size: 985443 timestamp: 1767051440390 @@ -22911,6 +24439,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=11.0.2,<11.1.0a0 @@ -22925,6 +24454,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - fmt >=12.1.0,<12.2.0a0 @@ -22956,6 +24486,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.1,<3.0a0 @@ -22977,6 +24508,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 @@ -22997,6 +24529,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping run_exports: {} size: 2533681 timestamp: 1778770493020 @@ -23008,6 +24542,7 @@ packages: - libfreetype6 2.14.3 hdbac1cb_1 - zlib license: GPL-2.0-only OR FTL + purls: [] run_exports: weak: - libfreetype >=2.14.3 @@ -23026,6 +24561,7 @@ packages: - vc14_runtime >=14.29.30139 license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - freexl >=2.0.0,<3.0a0 @@ -23049,6 +24585,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - fribidi >=1.0.16,<2.0a0 @@ -23069,6 +24606,8 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: + - pkg:pypi/gdal?source=hash-mapping run_exports: {} size: 1634719 timestamp: 1728294392653 @@ -23080,6 +24619,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.12.2,<3.12.3.0a0 @@ -23093,6 +24633,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only + purls: [] run_exports: weak: - geos >=3.14.1,<3.14.2.0a0 @@ -23112,6 +24653,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - geotiff >=1.7.3,<1.8.0a0 @@ -23129,6 +24671,7 @@ packages: - ucrt >=10.0.20348.0 license: LGPL-3.0-only license_family: LGPL + purls: [] run_exports: weak: - getopt-win32 >=0.1,<0.1.1.0a0 @@ -23148,6 +24691,7 @@ packages: - ucrt >=10.0.20348.0 - libintl >=0.22.5,<1.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 @@ -23164,6 +24708,7 @@ packages: - ucrt >=10.0.20348.0 - libintl >=0.22.5,<1.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: {} size: 251656 timestamp: 1786457672418 @@ -23172,6 +24717,7 @@ packages: md5: 354dc95ae9872158dc0f56a9b696d841 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 2017538 timestamp: 1775514603704 @@ -23184,6 +24730,7 @@ packages: - ucrt >=10.0.20348.0 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 @@ -23218,6 +24765,7 @@ packages: - vc14_runtime >=14.44.35208 license: EPL-1.0 license_family: Other + purls: [] run_exports: weak: - graphviz >=14.1.2,<15.0a0 @@ -23247,6 +24795,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/greenlet?source=compressed-mapping run_exports: {} size: 259251 timestamp: 1786384106710 @@ -23260,6 +24810,7 @@ packages: - vc14_runtime >=14.29.30139 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - gts >=0.7.6,<0.8.0a0 @@ -23281,6 +24832,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - harfbuzz >=11.0.0,<12.0a0 @@ -23312,6 +24864,7 @@ packages: - libharfbuzz-devel 14.3.0 h03b5201_0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -23328,6 +24881,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf4 >=4.2.15,<4.2.16.0a0 @@ -23346,6 +24900,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - hdf5 >=1.14.3,<1.14.4.0a0 @@ -23360,6 +24915,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - icu >=75.1,<76.0a0 @@ -23374,6 +24930,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - icu >=78.3,<79.0a0 @@ -23401,6 +24958,8 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/immutables?source=hash-mapping run_exports: {} size: 56350 timestamp: 1757685649329 @@ -23414,6 +24973,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - kealib >=1.5.3,<1.6.0a0 @@ -23430,6 +24990,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 73357 timestamp: 1773067062006 @@ -23444,6 +25006,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping run_exports: {} size: 73330 timestamp: 1773067062280 @@ -23457,6 +25021,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.21.3,<1.22.0a0 @@ -23472,6 +25037,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 @@ -23488,6 +25054,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 @@ -23502,6 +25069,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.1.0,<5.0a0 @@ -23516,6 +25084,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 @@ -23533,6 +25102,7 @@ packages: - abseil-cpp =20240116.2 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20240116.2,<20240117.0a0 @@ -23551,6 +25121,7 @@ packages: - abseil-cpp =20260526.0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libabseil >=20260526.0,<20260527.0a0 @@ -23566,6 +25137,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libaec >=1.1.5,<2.0a0 @@ -23588,6 +25160,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.7.7,<3.8.0a0 @@ -23631,6 +25204,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libarchive >=3.8.9,<3.9.0a0 @@ -23670,6 +25244,7 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=17.0.0,<17.1.0a0 @@ -23710,6 +25285,7 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow >=25.0.0,<25.1.0a0 @@ -23726,6 +25302,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=17.0.0,<17.1.0a0 @@ -23743,6 +25320,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-acero >=25.0.0,<25.1.0a0 @@ -23762,6 +25340,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-compute >=25.0.0,<25.1.0a0 @@ -23780,6 +25359,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=17.0.0,<17.1.0a0 @@ -23799,6 +25379,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-dataset >=25.0.0,<25.1.0a0 @@ -23820,6 +25401,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=17.0.0,<17.1.0a0 @@ -23841,6 +25423,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libarrow-substrait >=25.0.0,<25.1.0a0 @@ -23859,6 +25442,7 @@ packages: - libcblas 3.11.0 6*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -23892,6 +25476,7 @@ packages: - liblapacke 3.11.0 9*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 @@ -23906,6 +25491,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.1.0,<1.2.0a0 @@ -23920,6 +25506,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlicommon >=1.2.0,<1.3.0a0 @@ -23935,6 +25522,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.1.0,<1.2.0a0 @@ -23950,6 +25538,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlidec >=1.2.0,<1.3.0a0 @@ -23965,6 +25554,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.1.0,<1.2.0a0 @@ -23980,6 +25570,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libbrotlienc >=1.2.0,<1.3.0a0 @@ -23997,6 +25588,7 @@ packages: - liblapack 3.11.0 6*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -24028,6 +25620,7 @@ packages: - liblapacke 3.11.0 9*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 @@ -24046,6 +25639,7 @@ packages: - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception + purls: [] run_exports: weak: - libclang13 >=22.1.8 @@ -24062,6 +25656,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libclang13 >=22.1.8 @@ -24075,6 +25670,7 @@ packages: - vs2015_runtime >=14.16.27012 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcrc32c >=1.1.2,<1.2.0a0 @@ -24107,6 +25703,7 @@ packages: - vc14_runtime >=14.44.35208 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 @@ -24124,6 +25721,7 @@ packages: - vc14_runtime >=14.29.30139 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.9.1,<9.0a0 @@ -24138,6 +25736,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -24152,6 +25751,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -24167,6 +25767,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libevent >=2.1.12,<2.1.13.0a0 @@ -24183,6 +25784,7 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT + purls: [] run_exports: {} size: 71631 timestamp: 1781203724164 @@ -24195,6 +25797,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.5.2,<3.6.0a0 @@ -24209,6 +25812,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 @@ -24220,6 +25824,7 @@ packages: depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 8711 timestamp: 1780934891782 @@ -24235,6 +25840,7 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 340411 timestamp: 1780934813224 @@ -24250,6 +25856,7 @@ packages: - libgomp 15.2.0 h8ee18e1_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 821854 timestamp: 1778273037795 @@ -24265,6 +25872,7 @@ packages: - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 819817 timestamp: 1785377219855 @@ -24289,6 +25897,7 @@ packages: - xorg-libxpm >=3.5.17,<4.0a0 license: GD license_family: BSD + purls: [] run_exports: weak: - libgd >=2.3.3,<2.4.0a0 @@ -24313,6 +25922,7 @@ packages: - libgdal-xls 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -24356,6 +25966,7 @@ packages: - libgdal 3.12.4.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.12.4,<3.13.0a0 @@ -24397,6 +26008,7 @@ packages: - libgdal 3.9.2.* license: MIT license_family: MIT + purls: [] run_exports: weak: - libgdal-core >=3.9.2,<3.10.0a0 @@ -24414,6 +26026,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 497868 timestamp: 1728296632048 @@ -24429,6 +26042,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 678215 timestamp: 1728296811437 @@ -24445,6 +26059,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 562915 timestamp: 1728296979645 @@ -24460,6 +26075,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 613800 timestamp: 1728297167598 @@ -24475,6 +26091,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 498132 timestamp: 1728297319442 @@ -24492,6 +26109,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 518891 timestamp: 1728298457098 @@ -24511,6 +26129,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 667045 timestamp: 1728298642403 @@ -24526,6 +26145,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 626278 timestamp: 1728297526707 @@ -24542,6 +26162,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 532532 timestamp: 1728297702019 @@ -24558,6 +26179,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 505346 timestamp: 1728297893312 @@ -24573,6 +26195,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 628075 timestamp: 1726098183036 @@ -24588,6 +26211,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: {} size: 466632 timestamp: 1728298290371 @@ -24606,6 +26230,7 @@ packages: constrains: - glib 2.84.0 *_0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.84.0,<3.0a0 @@ -24643,6 +26268,7 @@ packages: constrains: - glib >2.66 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 @@ -24657,6 +26283,7 @@ packages: - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -24672,6 +26299,7 @@ packages: - msys2-conda-epoch <0.0a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -24694,6 +26322,7 @@ packages: - libgoogle-cloud 2.28.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=2.28.0,<2.29.0a0 @@ -24716,6 +26345,7 @@ packages: - libgoogle-cloud 3.7.0 *_0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud >=3.7.0,<3.8.0a0 @@ -24735,6 +26365,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=2.28.0,<2.29.0a0 @@ -24754,6 +26385,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libgoogle-cloud-storage >=3.7.0,<3.8.0a0 @@ -24778,6 +26410,7 @@ packages: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.62.2,<1.63.0a0 @@ -24802,6 +26435,7 @@ packages: - grpc-cpp =1.82.1 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libgrpc >=1.82.1,<1.83.0a0 @@ -24825,6 +26459,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: {} size: 1041066 timestamp: 1785770469568 @@ -24849,6 +26484,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -24865,6 +26501,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libhwloc >=2.12.1,<2.12.2.0a0 @@ -24882,6 +26519,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libhwloc >=2.13.0,<2.13.1.0a0 @@ -24895,6 +26533,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 OR BSD-3-Clause + purls: [] run_exports: weak: - libhwy >=1.4.0,<1.5.0a0 @@ -24908,6 +26547,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only + purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 @@ -24919,6 +26559,7 @@ packages: depends: - libiconv >=1.17,<2.0a0 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libintl >=0.22.5,<1.0a0 @@ -24931,6 +26572,7 @@ packages: - libiconv >=1.17,<2.0a0 - libintl 0.22.5 h5728263_3 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libintl >=0.22.5,<1.0a0 @@ -24946,6 +26588,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.1.4.1,<4.0a0 @@ -24961,6 +26604,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 @@ -24978,6 +26622,7 @@ packages: - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libjxl >=0.12.0,<0.13.0a0 @@ -24995,6 +26640,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 1668156 timestamp: 1777026660593 @@ -25010,6 +26656,7 @@ packages: - libcblas 3.11.0 6*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -25041,6 +26688,7 @@ packages: - liblapacke 3.11.0 9*_mkl license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -25056,6 +26704,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -25071,6 +26720,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -25085,6 +26735,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -25138,6 +26789,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libmamba >=2.9.0,<2.10.0a0 @@ -25165,6 +26817,7 @@ packages: - libmamba >=2.9.0,<2.10.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 18480 timestamp: 1786125710299 @@ -25212,6 +26865,8 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/libmambapy?source=compressed-mapping run_exports: weak: - libmambapy >=2.9.0,<2.10.0a0 @@ -25226,6 +26881,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: {} size: 89411 timestamp: 1769482314283 @@ -25251,6 +26907,7 @@ packages: constrains: - msgpack-c ==6.1.0 *_7 license: BSL-1.0 + purls: [] run_exports: weak: - libmsgpack-c >=6.1.0,<7.0a0 @@ -25276,6 +26933,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnetcdf >=4.9.2,<4.9.3.0a0 @@ -25298,6 +26956,7 @@ packages: - cpp-opentelemetry-sdk =1.27.0 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libopentelemetry-cpp >=1.27.0,<1.28.0a0 @@ -25308,6 +26967,7 @@ packages: md5: 85e4342d7e798c99a828d59303909d09 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 436532 timestamp: 1783133530257 @@ -25324,6 +26984,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=17.0.0,<17.1.0a0 @@ -25342,6 +27003,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libparquet >=25.0.0,<25.1.0a0 @@ -25356,6 +27018,7 @@ packages: - ucrt >=10.0.20348.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement + purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 @@ -25372,6 +27035,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -25389,6 +27053,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=4.25.3,<4.25.4.0a0 @@ -25406,6 +27071,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libprotobuf >=7.35.1,<7.35.2.0a0 @@ -25421,6 +27087,7 @@ packages: - icu >=78.3,<79.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 @@ -25439,6 +27106,7 @@ packages: - fribidi >=1.0.16,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libraqm >=0.11.0,<0.12.0a0 @@ -25457,6 +27125,7 @@ packages: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -25475,6 +27144,7 @@ packages: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -25490,6 +27160,7 @@ packages: - vc14_runtime >=14.29.30139 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -25505,6 +27176,7 @@ packages: - vc14_runtime >=14.44.35208 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - librttopo >=1.1.0,<1.2.0a0 @@ -25518,6 +27190,7 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: ISC + purls: [] run_exports: weak: - libsodium >=1.0.22,<1.0.23.0a0 @@ -25533,6 +27206,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libsolv >=0.7.39,<0.8.0a0 @@ -25559,6 +27233,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -25583,6 +27258,7 @@ packages: - zlib license: MPL-1.1 license_family: MOZILLA + purls: [] run_exports: weak: - libspatialite >=5.1.0,<5.2.0a0 @@ -25606,6 +27282,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -25619,6 +27296,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -25635,6 +27313,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 @@ -25652,6 +27331,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.20.0,<0.20.1.0a0 @@ -25669,6 +27349,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libthrift >=0.22.0,<0.22.1.0a0 @@ -25688,6 +27369,7 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.1,<4.8.0a0 @@ -25707,6 +27389,7 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 @@ -25721,6 +27404,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.11.3,<2.12.0a0 @@ -25735,6 +27419,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - libutf8proc >=2.8.0,<2.9.0a0 @@ -25751,6 +27436,7 @@ packages: - libvulkan-headers 1.4.357.0.* license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - libvulkan-loader >=1.4.357.0,<2.0a0 @@ -25767,6 +27453,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -25783,6 +27470,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -25797,6 +27485,7 @@ packages: - pthreads-win32 <0.0a0 - msys2-conda-epoch <0.0a0 license: MIT AND BSD-3-Clause-Clear + purls: [] run_exports: {} size: 36621 timestamp: 1759768399557 @@ -25812,6 +27501,7 @@ packages: - xorg-libxdmcp license: MIT license_family: MIT + purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 @@ -25832,6 +27522,7 @@ packages: - libxml2 2.15.3 license: MIT license_family: MIT + purls: [] run_exports: {} size: 519871 timestamp: 1776376969852 @@ -25846,6 +27537,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 >=2.13.9,<2.14.0a0 @@ -25865,6 +27557,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -25886,6 +27579,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxml2 @@ -25903,6 +27597,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 @@ -25918,6 +27613,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - libxslt >=1.1.43,<2.0a0 @@ -25935,6 +27631,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libzip >=1.11.2,<2.0a0 @@ -25951,6 +27648,7 @@ packages: - zlib 1.3.2 *_2 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -25967,6 +27665,7 @@ packages: - zlib 1.3.2 *_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -25984,6 +27683,7 @@ packages: - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE + purls: [] run_exports: strong: - llvm-openmp >=22.1.8 @@ -26001,6 +27701,8 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 76980 timestamp: 1725090008004 @@ -26017,6 +27719,8 @@ packages: - lz4-c >=1.10.0,<1.11.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping run_exports: {} size: 47054 timestamp: 1765026429237 @@ -26029,6 +27733,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.10.0,<1.11.0a0 @@ -26043,6 +27748,7 @@ packages: - vs2015_runtime >=14.29.30139 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - lz4-c >=1.9.4,<1.10.0a0 @@ -26060,6 +27766,7 @@ packages: - ucrt >=10.0.20348.0 license: GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - lzo >=2.10,<3.0a0 @@ -26078,6 +27785,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 28510 timestamp: 1772445175216 @@ -26094,6 +27803,8 @@ packages: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping run_exports: {} size: 30022 timestamp: 1772445159549 @@ -26108,6 +27819,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 18066 timestamp: 1777000842638 @@ -26122,6 +27834,7 @@ packages: - tornado >=5 license: PSF-2.0 license_family: PSF + purls: [] run_exports: {} size: 15333 timestamp: 1785211311397 @@ -26150,6 +27863,8 @@ packages: - vc14_runtime >=14.44.35208 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8033620 timestamp: 1777000825433 @@ -26179,6 +27894,8 @@ packages: - vc14_runtime >=14.44.35208 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping run_exports: {} size: 8810362 timestamp: 1785211292854 @@ -26206,6 +27923,8 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause AND MIT + purls: + - pkg:pypi/menuinst?source=hash-mapping run_exports: {} size: 199350 timestamp: 1786008821495 @@ -26222,6 +27941,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.1,<5.0a0 @@ -26240,6 +27960,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Zlib license_family: Other + purls: [] run_exports: weak: - minizip >=4.2.2,<5.0a0 @@ -26257,6 +27978,7 @@ packages: - vc14_runtime >=14.44.35208 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary + purls: [] run_exports: {} size: 99617963 timestamp: 1778105275590 @@ -26285,6 +28007,7 @@ packages: - vc14_runtime >=14.44.35208 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary + purls: [] run_exports: {} size: 114429478 timestamp: 1786086389935 @@ -26299,6 +28022,8 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 89054 timestamp: 1782460807428 @@ -26326,6 +28051,8 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} size: 89771 timestamp: 1782460807585 @@ -26338,6 +28065,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - muparser >=2.3.5,<2.4.0a0 @@ -26379,6 +28107,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/mypy?source=hash-mapping run_exports: {} size: 10913265 timestamp: 1783986254963 @@ -26389,6 +28119,7 @@ packages: - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 135270 timestamp: 1785920493388 @@ -26408,6 +28139,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.26.4,<2.0a0 @@ -26447,6 +28180,8 @@ packages: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping run_exports: weak: - numpy >=1.25,<3 @@ -26457,6 +28192,7 @@ packages: md5: eded4ef8a94852a2e3e4c779b3b6fdda license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary + purls: [] run_exports: {} size: 41377 timestamp: 1778104928629 @@ -26479,6 +28215,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - openjpeg >=2.5.4,<3.0a0 @@ -26494,6 +28231,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -26509,6 +28247,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 @@ -26529,6 +28268,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - orc >=2.0.2,<2.0.3.0a0 @@ -26551,6 +28291,7 @@ packages: - libabseil * cxx17* license: Apache-2.0 license_family: APACHE + purls: [] run_exports: weak: - orc >=2.3.1,<2.3.2.0a0 @@ -26610,6 +28351,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 13644207 timestamp: 1778602674307 @@ -26723,6 +28466,8 @@ packages: - zstandard >=0.23.0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pandas?source=hash-mapping run_exports: {} size: 14103773 timestamp: 1785276287020 @@ -26766,6 +28511,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - pango >=1.58.2,<2.0a0 @@ -26782,6 +28528,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.44,<10.45.0a0 @@ -26798,6 +28545,7 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 @@ -26823,6 +28571,8 @@ packages: - openjpeg >=2.5.4,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 944998 timestamp: 1775060119774 @@ -26846,6 +28596,8 @@ packages: - tk >=8.6.13,<8.7.0a0 - zlib-ng >=2.3.3,<2.4.0a0 license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping run_exports: {} size: 989058 timestamp: 1782912129930 @@ -26861,6 +28613,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -26875,6 +28628,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 @@ -26902,6 +28656,7 @@ packages: - vc14_runtime >=14.29.30139 license: GPL-2.0-only license_family: GPL + purls: [] run_exports: {} size: 2355995 timestamp: 1724660655649 @@ -26922,6 +28677,7 @@ packages: - vc14_runtime >=14.29.30139 - zstd >=1.5.6,<1.6.0a0 license: PostgreSQL + purls: [] run_exports: weak: - libpq >=17.2,<18.0a0 @@ -26942,6 +28698,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.4.1,<9.5.0a0 @@ -26964,6 +28721,7 @@ packages: - proj4 ==999999999999 license: MIT license_family: MIT + purls: [] run_exports: weak: - proj >=9.8.1,<9.9.0a0 @@ -26981,6 +28739,7 @@ packages: - zlib license: MIT license_family: MIT + purls: [] run_exports: weak: - prometheus-cpp >=1.3.0,<1.4.0a0 @@ -26997,6 +28756,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 242769 timestamp: 1769678170631 @@ -27011,6 +28772,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping run_exports: {} size: 249950 timestamp: 1769678167309 @@ -27023,6 +28786,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 9389 timestamp: 1726802555076 @@ -27035,6 +28799,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 10120 timestamp: 1786067833280 @@ -27048,6 +28813,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pulp?source=hash-mapping run_exports: {} size: 14442928 timestamp: 1757853584977 @@ -27079,6 +28846,8 @@ packages: - cpython >=3.10 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/py-rattler?source=hash-mapping run_exports: {} size: 13161702 timestamp: 1784707929736 @@ -27096,6 +28865,7 @@ packages: - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 26129 timestamp: 1730169358866 @@ -27112,6 +28882,7 @@ packages: - python_abi 3.14.* *_cp314 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 26584 timestamp: 1784258906758 @@ -27132,6 +28903,8 @@ packages: - apache-arrow-proc =*=cpu license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 3551041 timestamp: 1730169341038 @@ -27153,6 +28926,8 @@ packages: - libprotobuf >=6.33.5 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} size: 3689897 timestamp: 1784258892556 @@ -27180,6 +28955,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/pycosat?source=hash-mapping run_exports: {} size: 80121 timestamp: 1784146283080 @@ -27195,6 +28972,8 @@ packages: - python_abi 3.12.* *_cp312 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1888918 timestamp: 1778084258226 @@ -27210,6 +28989,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping run_exports: {} size: 1885001 timestamp: 1778084256232 @@ -27227,6 +29008,8 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 806834 timestamp: 1727772248889 @@ -27244,6 +29027,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/pyogrio?source=hash-mapping run_exports: {} size: 899343 timestamp: 1764402737921 @@ -27260,6 +29045,8 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=hash-mapping run_exports: {} size: 740323 timestamp: 1725436440016 @@ -27277,6 +29064,8 @@ packages: - proj >=9.8.1,<9.9.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyproj?source=compressed-mapping run_exports: {} size: 893897 timestamp: 1786351867522 @@ -27288,6 +29077,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pyreadline3?source=hash-mapping run_exports: {} size: 175241 timestamp: 1778848456926 @@ -27309,6 +29100,9 @@ packages: - libclang13 >=22.1.5 license: LGPL-3.0-only license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} size: 11579652 timestamp: 1778933912020 @@ -27328,6 +29122,9 @@ packages: - vc14_runtime >=14.42.34438 license: LGPL-3.0-only license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping run_exports: {} size: 8918147 timestamp: 1743761403797 @@ -27350,6 +29147,7 @@ packages: constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.12.* *_cp312 @@ -27402,6 +29200,7 @@ packages: - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 + purls: [] run_exports: weak: - python_abi 3.14.* *_cp314 @@ -27434,6 +29233,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/librt?source=compressed-mapping run_exports: {} size: 108739 timestamp: 1786328843266 @@ -27448,6 +29249,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/pytokens?source=hash-mapping run_exports: {} size: 120434 timestamp: 1778688861070 @@ -27462,6 +29265,8 @@ packages: - python_abi 3.14.* *_cp314 license: PSF-2.0 license_family: PSF + purls: + - pkg:pypi/pywin32?source=hash-mapping run_exports: {} size: 4472831 timestamp: 1781362876741 @@ -27477,6 +29282,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 179738 timestamp: 1770223468771 @@ -27492,6 +29299,8 @@ packages: - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} size: 181257 timestamp: 1770223460931 @@ -27509,6 +29318,8 @@ packages: - zeromq >=4.3.5,<4.3.6.0a0 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/pyzmq?source=hash-mapping run_exports: {} size: 182831 timestamp: 1779483925948 @@ -27520,6 +29331,7 @@ packages: - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: LicenseRef-Qhull + purls: [] run_exports: weak: - qhull >=2020.2,<2020.3.0a0 @@ -27556,6 +29368,7 @@ packages: - qt ==6.11.1 license: LGPL-3.0-only license_family: LGPL + purls: [] run_exports: weak: - qt6-main >=6.11.1,<7.0a0 @@ -27587,6 +29400,7 @@ packages: - qt 6.9.0 license: LGPL-3.0-only license_family: LGPL + purls: [] run_exports: weak: - qt6-main >=6.9.0,<6.10.0a0 @@ -27615,6 +29429,8 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 7448190 timestamp: 1726177323954 @@ -27640,6 +29456,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/rasterio?source=hash-mapping run_exports: {} size: 8483184 timestamp: 1785964794642 @@ -27650,6 +29468,7 @@ packages: - libre2-11 2023.09.01 hf8d8778_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2023.9.1,<2024.0a0 @@ -27663,6 +29482,7 @@ packages: - libre2-11 2025.11.05 h45f70d9_2 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libre2-11 >=2025.11.5 @@ -27678,6 +29498,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc >=14.2.7.post0,<14.3.0a0 @@ -27705,6 +29526,7 @@ packages: - reproc >=14.2.7.post0,<14.3.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - reproc-cpp >=14.2.7.post0,<14.3.0a0 @@ -27746,6 +29568,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/rpds-py?source=hash-mapping run_exports: {} size: 217789 timestamp: 1782831449604 @@ -27761,6 +29585,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml?source=hash-mapping run_exports: {} size: 306951 timestamp: 1766175833786 @@ -27775,6 +29601,8 @@ packages: - python_abi 3.14.* *_cp314 license: MIT license_family: MIT + purls: + - pkg:pypi/ruamel-yaml-clib?source=hash-mapping run_exports: {} size: 105668 timestamp: 1766159584330 @@ -27802,6 +29630,8 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: + - pkg:pypi/ruff?source=hash-mapping run_exports: {} size: 9898546 timestamp: 1786151875428 @@ -27822,6 +29652,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9153589 timestamp: 1780413519467 @@ -27842,6 +29674,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping run_exports: {} size: 9411356 timestamp: 1780401101875 @@ -27862,6 +29696,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 15078490 timestamp: 1779876221509 @@ -27882,6 +29718,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping run_exports: {} size: 15353018 timestamp: 1781914001107 @@ -27898,6 +29736,8 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 534109 timestamp: 1725394700590 @@ -27914,6 +29754,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping run_exports: {} size: 614463 timestamp: 1762523809485 @@ -27936,6 +29778,7 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: Apache-2.0 + purls: [] run_exports: weak: - simdjson >=4.6.6,<4.7.0a0 @@ -27952,6 +29795,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 158103 timestamp: 1777112188130 @@ -27966,6 +29811,8 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: + - pkg:pypi/simplejson?source=hash-mapping run_exports: {} size: 161975 timestamp: 1777112198099 @@ -27981,6 +29828,7 @@ packages: - ucrt >=10.0.20348.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - snappy >=1.2.2,<1.3.0a0 @@ -27996,6 +29844,7 @@ packages: - vc14_runtime >=14.29.30139 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.14.1,<1.15.0a0 @@ -28023,6 +29872,7 @@ packages: - vc14_runtime >=14.44.35208 license: MIT license_family: MIT + purls: [] run_exports: weak: - spdlog >=1.17.0,<1.18.0a0 @@ -28055,6 +29905,8 @@ packages: - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 license: MIT + purls: + - pkg:pypi/sqlalchemy?source=hash-mapping run_exports: {} size: 3998837 timestamp: 1786535220662 @@ -28067,6 +29919,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.3,<4.0a0 @@ -28081,6 +29934,7 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing + purls: [] run_exports: weak: - libsqlite >=3.53.4,<4.0a0 @@ -28096,6 +29950,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 155714 timestamp: 1762510341121 @@ -28109,6 +29964,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE + purls: [] run_exports: {} size: 156515 timestamp: 1778673901757 @@ -28141,6 +29997,7 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - tiledb >=2.26.0,<2.27.0a0 @@ -28155,6 +30012,7 @@ packages: - vc14_runtime >=14.44.35208 license: TCL license_family: BSD + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -28168,6 +30026,7 @@ packages: - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: TCL + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 @@ -28184,6 +30043,8 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 865801 timestamp: 1781006895319 @@ -28211,6 +30072,8 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping run_exports: {} size: 925990 timestamp: 1786226866739 @@ -28221,6 +30084,7 @@ packages: - vc14_runtime >=14.29.30037 - vs2015_runtime >=14.29.30037 license: LicenseRef-MicrosoftWindowsSDK10 + purls: [] run_exports: {} size: 694692 timestamp: 1756385147981 @@ -28235,6 +30099,8 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 405896 timestamp: 1770909182518 @@ -28249,6 +30115,8 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping run_exports: {} size: 406126 timestamp: 1770909191618 @@ -28261,6 +30129,7 @@ packages: - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - uriparser >=0.9.8,<1.0a0 @@ -28275,6 +30144,7 @@ packages: - vc14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 20362 timestamp: 1781320968457 @@ -28287,6 +30157,7 @@ packages: - vc14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 21383 timestamp: 1785359368566 @@ -28300,6 +30171,7 @@ packages: - vs2015_runtime 14.51.36231.* *_39 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary + purls: [] run_exports: {} size: 737434 timestamp: 1781320964561 @@ -28313,6 +30185,7 @@ packages: - vs2015_runtime 14.51.36247.* *_41 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary + purls: [] run_exports: {} size: 767955 timestamp: 1785359364369 @@ -28325,6 +30198,7 @@ packages: - vs2015_runtime 14.51.36231.* *_39 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary + purls: [] run_exports: strong: - vcomp14 >=14.51.36231 @@ -28339,6 +30213,7 @@ packages: - vs2015_runtime 14.51.36247.* *_41 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary + purls: [] run_exports: strong: - vcomp14 >=14.51.36247 @@ -28351,6 +30226,7 @@ packages: - vc14_runtime >=14.51.36231 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 20355 timestamp: 1781320968804 @@ -28361,6 +30237,7 @@ packages: - vc14_runtime >=14.51.36247 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 21386 timestamp: 1785359368990 @@ -28375,6 +30252,8 @@ packages: - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD + purls: + - pkg:pypi/wrapt?source=hash-mapping run_exports: {} size: 63873 timestamp: 1756852097390 @@ -28387,6 +30266,7 @@ packages: - vc14_runtime >=14.29.30139 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.2.5,<3.3.0a0 @@ -28401,6 +30281,7 @@ packages: - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - xerces-c >=3.3.0,<3.4.0a0 @@ -28427,6 +30308,7 @@ packages: - ucrt >=10.0.20348.0 - xorg-libx11 >=1.8.13,<2.0a0 license: MIT + purls: [] run_exports: weak: - xorg-libice >=1.1.2,<2.0a0 @@ -28453,6 +30335,7 @@ packages: - libwinpthread >=12.0.0.r4.gg4f2fc60ca - xorg-libice >=1.1.2,<2.0a0 license: MIT + purls: [] run_exports: weak: - xorg-libsm >=1.2.6,<2.0a0 @@ -28468,6 +30351,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libx11 >=1.8.13,<2.0a0 @@ -28482,6 +30366,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -28496,6 +30381,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxau >=1.0.12,<2.0a0 @@ -28510,6 +30396,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -28524,6 +30411,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxdmcp >=1.1.5,<2.0a0 @@ -28539,6 +30427,7 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxext >=1.3.7,<2.0a0 @@ -28556,6 +30445,7 @@ packages: - xorg-libxt >=1.3.1,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxpm >=3.5.19,<4.0a0 @@ -28573,6 +30463,7 @@ packages: - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxt >=1.3.1,<2.0a0 @@ -28589,6 +30480,7 @@ packages: - vc14_runtime >=14.44.35208 - xz-tools 5.8.3 hfd05255_0 license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -28605,6 +30497,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD AND LGPL-2.1-or-later + purls: [] run_exports: {} size: 68149 timestamp: 1775825719953 @@ -28620,6 +30513,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml >=0.2.5,<0.3.0a0 @@ -28634,6 +30528,7 @@ packages: - ucrt >=10.0.20348.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - yaml-cpp >=0.8.0,<0.9.0a0 @@ -28664,6 +30559,7 @@ packages: - krb5 >=1.22.2,<1.23.0a0 license: MPL-2.0 license_family: MOZILLA + purls: [] run_exports: weak: - zeromq >=4.3.5,<4.3.6.0a0 @@ -28679,6 +30575,7 @@ packages: - vc14_runtime >=14.44.35208 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -28694,6 +30591,7 @@ packages: - vc14_runtime >=14.44.35208 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -28708,6 +30606,7 @@ packages: - vc14_runtime >=14.44.35208 license: Zlib license_family: Other + purls: [] run_exports: weak: - zlib-ng >=2.3.3,<2.4.0a0 @@ -28730,6 +30629,8 @@ packages: - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 374949 timestamp: 1762512770373 @@ -28750,6 +30651,8 @@ packages: - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping run_exports: {} size: 381179 timestamp: 1762512709971 @@ -28763,8 +30666,15 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 size: 388453 timestamp: 1764777142545 +- pypi: ../../tclean + name: tclean + requires_dist: + - pandas>=3.0.3 + - pandera>=0.31.1 + requires_python: '>=3.12' diff --git a/pixi.toml b/pixi.toml index ebcd924..3c6997b 100644 --- a/pixi.toml +++ b/pixi.toml @@ -40,6 +40,9 @@ rioxarray = ">=0.20.0" pandera = ">=0.31.1" pyyaml = ">=6.0.3" +[feature.module.pypi-dependencies] +tclean = { path = "../../tclean", editable = true } + [environments] module = { features = ["module"], no-default-feature = true } test = { features = ["module"] } From f995dd45cbbf44f77bc6c03b7a92ffabfc15d7b6 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 10:41:10 +0200 Subject: [PATCH 104/241] Refactored source download and preparation scripts. --- workflow/scripts/cleaning/sources/neso.py | 56 ----- workflow/scripts/download_load_entsoe.py | 64 ++++++ workflow/scripts/download_load_entsoe_api.py | 197 ------------------ workflow/scripts/download_load_neso.py | 153 ++------------ workflow/scripts/download_load_opsd.py | 96 ++------- workflow/scripts/prepapre_load_entsoe.py | 56 +++++ workflow/scripts/prepare_load_neso.py | 186 ++++------------- workflow/scripts/prepare_load_opsd.py | 107 ++++------ workflow/scripts/sources/entsoe/download.py | 168 +++++++++++++++ workflow/scripts/sources/entsoe/prepare.py | 39 ++++ workflow/scripts/sources/neso/download.py | 203 +++++++++++++++++++ workflow/scripts/sources/neso/prepare.py | 168 +++++++++++++++ workflow/scripts/sources/opsd/download.py | 118 +++++++++++ workflow/scripts/sources/opsd/prepare.py | 108 ++++++++++ 14 files changed, 1045 insertions(+), 674 deletions(-) delete mode 100644 workflow/scripts/cleaning/sources/neso.py create mode 100644 workflow/scripts/download_load_entsoe.py delete mode 100644 workflow/scripts/download_load_entsoe_api.py create mode 100644 workflow/scripts/prepapre_load_entsoe.py create mode 100644 workflow/scripts/sources/entsoe/download.py create mode 100644 workflow/scripts/sources/entsoe/prepare.py create mode 100644 workflow/scripts/sources/neso/download.py create mode 100644 workflow/scripts/sources/neso/prepare.py create mode 100644 workflow/scripts/sources/opsd/download.py create mode 100644 workflow/scripts/sources/opsd/prepare.py diff --git a/workflow/scripts/cleaning/sources/neso.py b/workflow/scripts/cleaning/sources/neso.py deleted file mode 100644 index 30490bb..0000000 --- a/workflow/scripts/cleaning/sources/neso.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Prepare NESO settlement-period data.""" - -import pandas as pd - - -def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: - """Convert NESO settlement dates and periods to UTC timestamps.""" - prepared = data.copy() - - prepared["SETTLEMENT_DATE"] = pd.to_datetime( - prepared["SETTLEMENT_DATE"], errors="raise" - ).dt.normalize() - - prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( - prepared["SETTLEMENT_PERIOD"], errors="raise" - ).astype(int) - - timestamp_parts: list[pd.Series] = [] - - for settlement_date, day in prepared.groupby("SETTLEMENT_DATE", sort=True): - day = day.sort_values("SETTLEMENT_PERIOD").copy() - - expected_periods = list(range(1, len(day) + 1)) - observed_periods = day["SETTLEMENT_PERIOD"].tolist() - - if observed_periods != expected_periods: - raise ValueError( - "NESO settlement periods are not consecutive for " - f"{settlement_date.date()}. Expected 1-{len(day)}." - ) - - local_start = pd.Timestamp(settlement_date, tz="Europe/London") - local_end = local_start + pd.DateOffset(days=1) - - expected_index = pd.date_range( - start=local_start, end=local_end, freq="30min", inclusive="left" - ) - - if len(day) != len(expected_index): - raise ValueError( - "NESO settlement-period count does not match the " - "Europe/London clock for " - f"{settlement_date.date()}: " - f"{len(day)} records versus " - f"{len(expected_index)} expected." - ) - - timestamp_parts.append( - pd.Series(expected_index, index=day.index, name="timestamp") - ) - - prepared["timestamp"] = pd.concat(timestamp_parts).sort_index() - - prepared["timestamp"] = prepared["timestamp"].dt.tz_convert("UTC") - - return prepared.sort_values("timestamp") diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py new file mode 100644 index 0000000..bec2ab5 --- /dev/null +++ b/workflow/scripts/download_load_entsoe.py @@ -0,0 +1,64 @@ +"""Snakemake entry point for downloading ENTSO-E load data.""" + +import logging +from typing import TYPE_CHECKING, Any + +from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from sources.entsoe.download import download_entsoe +from tclean import TimeGrid + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Download ENTSO-E data for the requested workflow period.""" + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="entsoe_api", + ) + + start = batch["start"] + end = batch["end"] + country_codes = list(batch["countries"]) + + else: + start = snakemake.params.temporal_start + end = snakemake.params.temporal_end + country_codes = list( + snakemake.params.country_codes + ) + + grid = TimeGrid( + start=start, + end=end, + frequency=snakemake.params.frequency, + ) + + download_entsoe( + start=grid.start, + end=grid.end, + country_codes=country_codes, + token_path=snakemake.input.token_entsoe, + output_path=snakemake.output.raw_load, + workers=snakemake.threads, + ) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + main(snakemake) diff --git a/workflow/scripts/download_load_entsoe_api.py b/workflow/scripts/download_load_entsoe_api.py deleted file mode 100644 index ca9f0a5..0000000 --- a/workflow/scripts/download_load_entsoe_api.py +++ /dev/null @@ -1,197 +0,0 @@ -"""Download electricity load data from ENTSO-E using the entsoe-py library.""" - -import logging -import sys -from concurrent.futures import ThreadPoolExecutor, as_completed -from time import perf_counter -from typing import TYPE_CHECKING, Any - -import pandas as pd -import pycountry -from cleaning.advanced.planning.manifest import get_batch, load_execution_plan -from common.time import as_utc_timestamp, build_hourly_index -from entsoe.entsoe import EntsoePandasClient -from entsoe.exceptions import NoMatchingDataError - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - snakemake: Any - - -def configure_logging(log_path) -> None: - """Log downloader progress to both the console and rule log.""" - logger.setLevel(logging.INFO) - logger.handlers.clear() - - formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") - - console_handler = logging.StreamHandler(sys.stdout) - console_handler.setFormatter(formatter) - - file_handler = logging.FileHandler(log_path, mode="w", encoding="utf-8") - file_handler.setFormatter(formatter) - - logger.addHandler(console_handler) - logger.addHandler(file_handler) - - -def load_txt(filepath): - """Load text file.""" - with open(filepath) as file: - data = file.read() - - return data - - -def download_country( - *, country_alpha_3: str, start: pd.Timestamp, end: pd.Timestamp, token: str -) -> tuple[str, pd.Series, float]: - """Download ENTSO-E load for one country.""" - country = pycountry.countries.get(alpha_3=country_alpha_3) - - if country is None: - raise ValueError(f"Unknown ISO alpha-3 country code: {country_alpha_3!r}.") - - country_alpha_2 = country.alpha_2 - - client = EntsoePandasClient(api_key=token, timeout=60) - - country_start = perf_counter() - - try: - df_country = client.query_load( - country_code=country_alpha_2, start=start, end=end - ) - - df_country = df_country["Actual Load"] - df_country.name = country_alpha_3 - - except NoMatchingDataError: - logger.warning( - "No data found for %s/%s in the given period: %s to %s.", - country_alpha_2, - country_alpha_3, - start, - end, - ) - - df_country = pd.Series(name=country_alpha_3, dtype=float) - - elapsed = perf_counter() - country_start - - return (country_alpha_3, df_country, elapsed) - - -def main(start, end, country_codes, token, output_load, workers): - """Download load in MW via the ENTSO-E API.""" - start = as_utc_timestamp(start) - end = as_utc_timestamp(end) - token = load_txt(token).strip() - - country_codes = list(country_codes) - total_countries = len(country_codes) - - download_start = perf_counter() - - logger.info( - "Downloading ENTSO-E load for %s countries " - "from %s to %s using %s parallel workers.", - total_countries, - start, - end, - workers, - ) - - data_by_country = {} - - with ThreadPoolExecutor(max_workers=workers) as executor: - futures = { - executor.submit( - download_country, - country_alpha_3=country_alpha_3, - start=start, - end=end, - token=token, - ): country_alpha_3 - for country_alpha_3 in country_codes - } - - for completed, future in enumerate(as_completed(futures), start=1): - country_alpha_3 = futures[future] - - try: - (country_alpha_3, df_country, elapsed) = future.result() - - except Exception as exc: - raise RuntimeError( - f"Failed to download ENTSO-E load for {country_alpha_3!r}." - ) from exc - - data_by_country[country_alpha_3] = df_country - - logger.info( - "[%s/%s] Finished %s in %.1fs.", - completed, - total_countries, - country_alpha_3, - elapsed, - ) - - logger.info("Finished ENTSO-E downloads in %.1fs.", perf_counter() - download_start) - - # Restore configured country order because futures complete - # in arbitrary order. - data = [data_by_country[country_alpha_3] for country_alpha_3 in country_codes] - - processing_start = perf_counter() - - df = pd.concat(data, axis=1) - - df.index = pd.to_datetime(df.index, utc=True) - - df = df.resample("1h").mean() - - # Align downloaded data to the requested hourly target grid. - target_index = build_hourly_index(start=start, end=end) - - df = df.reindex(index=target_index, columns=country_codes) - - # Pre-cleaning. Replace empty object columns with NaN - # columns to allow data-source combining. - df = df.astype(float) - - df.to_parquet(output_load) - - logger.info( - "ENTSO-E processing and write completed in %.1fs.", - perf_counter() - processing_start, - ) - - -if __name__ == "__main__": - configure_logging(snakemake.log[0]) - - plan_path = getattr(snakemake.input, "plan", None) - - if plan_path is not None: - plan = load_execution_plan(plan_path) - batch = get_batch( - plan, batch_id=snakemake.wildcards.batch_id, source="entsoe_api" - ) - start = batch["start"] - end = batch["end"] - country_codes = batch["countries"] - else: - start = snakemake.params.temporal_start - end = snakemake.params.temporal_end - country_codes = snakemake.params.country_codes - - main( - start=start, - end=end, - country_codes=country_codes, - token=snakemake.input.token_entsoe, - output_load=snakemake.output.load, - workers=snakemake.threads, - ) diff --git a/workflow/scripts/download_load_neso.py b/workflow/scripts/download_load_neso.py index 04fac8b..cd07170 100644 --- a/workflow/scripts/download_load_neso.py +++ b/workflow/scripts/download_load_neso.py @@ -1,148 +1,33 @@ -"""Download annual historic electricity-demand data from NESO.""" +"""Snakemake entry point for downloading NESO historic demand.""" -import json import logging -import shutil import sys -from pathlib import Path from typing import TYPE_CHECKING, Any -from urllib.parse import urlencode -from urllib.request import Request, urlopen + +from sources.neso.download import download_annual_file if TYPE_CHECKING: snakemake: Any -logger = logging.getLogger(__name__) - -NESO_API_BASE = "https://api.neso.energy/api/3/action" -USER_AGENT = "modelblocks-module-demand-electricity/NESO historic demand downloader" - - -def _request_json(endpoint: str, parameters: dict[str, str | int]) -> dict[str, Any]: - """Request one JSON response from the NESO CKAN API.""" - query = urlencode(parameters) - url = f"{NESO_API_BASE}/{endpoint}?{query}" - - request = Request(url, headers={"User-Agent": USER_AGENT}) - - with urlopen(request, timeout=120) as response: - payload = json.load(response) - - if not payload.get("success", False): - raise RuntimeError(f"NESO API request failed for {endpoint}: {payload}") - - result = payload.get("result") - - if not isinstance(result, dict): - raise RuntimeError(f"NESO API returned an unexpected result for {endpoint}.") - - return result - - -def _get_historic_demand_dataset() -> dict[str, Any]: - """Return the NESO Historic Demand Data dataset.""" - return _request_json("package_show", {"id": "historic-demand-data"}) - - -def _select_csv_resource(dataset: dict[str, Any], *, year: int) -> dict[str, Any]: - """Select the annual NESO historic-demand CSV resource.""" - resources = dataset.get("resources", []) - - if not isinstance(resources, list): - raise RuntimeError("NESO Historic Demand Data has no valid resource list.") - - expected_filename = f"demanddata_{year}.csv" - expected_title = f"historic demand data {year}" - - matching_resources: list[dict[str, Any]] = [] - - for resource in resources: - name = str(resource.get("name", "")).strip().casefold() - - url = str(resource.get("url", "")).strip() - - format_name = str(resource.get("format", "")).strip().casefold() - - url_lower = url.casefold() - - is_csv = format_name == "csv" or url_lower.endswith(".csv") - - matches_year = expected_title in name or expected_filename in url_lower - - if is_csv and matches_year and url: - matching_resources.append(resource) - - if len(matching_resources) != 1: - available_resources = [ - { - "name": resource.get("name"), - "format": resource.get("format"), - "url": resource.get("url"), - } - for resource in resources - ] - - raise RuntimeError( - "Could not identify exactly one NESO historic-demand " - f"CSV resource for {year}. " - f"Matches: {len(matching_resources)}. " - f"Resources: {available_resources}" - ) - - return matching_resources[0] - - -def _download_file(*, url: str, output_path: Path) -> None: - """Download one file atomically.""" - output_path.parent.mkdir(parents=True, exist_ok=True) - - temporary_path = output_path.with_suffix(output_path.suffix + ".part") - - temporary_path.unlink(missing_ok=True) - - request = Request(url, headers={"User-Agent": USER_AGENT}) - - try: - with ( - urlopen(request, timeout=300) as response, - temporary_path.open("wb") as output_file, - ): - shutil.copyfileobj(response, output_file) - - if temporary_path.stat().st_size == 0: - raise RuntimeError(f"NESO download produced an empty file: {url}") - - temporary_path.replace(output_path) - - except Exception: - temporary_path.unlink(missing_ok=True) - raise - - -def download_annual_file(*, year: int, output_path: str | Path) -> None: - """Discover and download one annual NESO historic-demand CSV.""" - output_path = Path(output_path) - - dataset = _get_historic_demand_dataset() - - logger.info("Selecting NESO historic-demand resource for %s.", year) - - resource = _select_csv_resource(dataset, year=year) - url = str(resource["url"]) - - logger.info("Downloading NESO historic demand for %s from %s.", year, url) - - _download_file(url=url, output_path=output_path) - - logger.info("Saved NESO historic demand for %s to %s.", year, output_path) +def main(snakemake: Any) -> None: + """Download one annual NESO historic-demand file.""" + download_annual_file( + year=int(snakemake.params.year), + output_path=snakemake.output.annual_file, + ) if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) - download_annual_file( - year=int(snakemake.params.year), output_path=snakemake.output.annual_file + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", ) + + main(snakemake) diff --git a/workflow/scripts/download_load_opsd.py b/workflow/scripts/download_load_opsd.py index 24814e0..c756076 100644 --- a/workflow/scripts/download_load_opsd.py +++ b/workflow/scripts/download_load_opsd.py @@ -1,89 +1,33 @@ -"""Download and cache the fixed OPSD electricity-demand snapshot.""" +"""Snakemake entry point for downloading OPSD demand data.""" import logging -import shutil import sys -from pathlib import Path -from urllib.request import Request, urlopen +from typing import TYPE_CHECKING, Any -import pandas as pd +from sources.opsd.download import download_opsd -logger = logging.getLogger(__name__) +if TYPE_CHECKING: + snakemake: Any -USER_AGENT = "modelblocks-module-demand-electricity/OPSD downloader" -REQUIRED_COLUMNS = {"utc_timestamp", "region", "variable", "attribute", "data"} - - -def _is_valid_cached_snapshot(path: Path) -> bool: - """Return whether an existing OPSD snapshot is suitable for reuse.""" - if not path.exists() or path.stat().st_size == 0: - return False - - try: - columns = set(pd.read_csv(path, nrows=0).columns) - except (OSError, ValueError): - return False - - return REQUIRED_COLUMNS.issubset(columns) - - -def download_opsd(*, url: str, output_path: str | Path) -> None: - """Download the OPSD snapshot unless a valid cached copy exists.""" - output_path = Path(output_path) - - if _is_valid_cached_snapshot(output_path): - logger.info("Using cached OPSD snapshot: %s", output_path) - return - - if output_path.exists(): - logger.warning("Cached OPSD snapshot is invalid; downloading a replacement.") - output_path.unlink() - - output_path.parent.mkdir(parents=True, exist_ok=True) - - temporary_path = output_path.with_suffix(output_path.suffix + ".part") - temporary_path.unlink(missing_ok=True) - - logger.info("Downloading OPSD snapshot from %s.", url) - - request = Request(url, headers={"User-Agent": USER_AGENT}) - - try: - with ( - urlopen(request, timeout=300) as response, - temporary_path.open("wb") as output_file, - ): - content_length = response.headers.get("Content-Length") - shutil.copyfileobj(response, output_file) - - downloaded_size = temporary_path.stat().st_size - - if content_length is not None and downloaded_size != int(content_length): - raise RuntimeError( - "OPSD download is incomplete: " - f"received {downloaded_size} of {content_length} bytes." - ) - - if not _is_valid_cached_snapshot(temporary_path): - raise RuntimeError( - "Downloaded OPSD snapshot does not contain the expected CSV structure." - ) - - temporary_path.replace(output_path) - - except Exception: - temporary_path.unlink(missing_ok=True) - raise - - logger.info( - "Saved OPSD snapshot to %s (%s bytes).", output_path, output_path.stat().st_size +def main(snakemake: Any) -> None: + """Download the configured OPSD snapshot.""" + download_opsd( + url=snakemake.params.url, + output_path=snakemake.output.load, ) if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) - download_opsd(url=snakemake.params.url, output_path=snakemake.output.load) + main(snakemake) diff --git a/workflow/scripts/prepapre_load_entsoe.py b/workflow/scripts/prepapre_load_entsoe.py new file mode 100644 index 0000000..2e6d737 --- /dev/null +++ b/workflow/scripts/prepapre_load_entsoe.py @@ -0,0 +1,56 @@ +"""Snakemake entry point for preparing ENTSO-E load data.""" + +from typing import TYPE_CHECKING, Any + +from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from sources.entsoe.prepare import prepare_entsoe +from tclean import TimeGrid + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Prepare ENTSO-E demand for the requested workflow period.""" + plan_path = getattr( + snakemake.input, + "plan", + None, + ) + + if plan_path is not None: + plan = load_execution_plan(plan_path) + + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="entsoe_api", + ) + + start = batch["start"] + end = batch["end"] + country_codes = list(batch["countries"]) + + else: + start = snakemake.params.temporal_start + end = snakemake.params.temporal_end + country_codes = list( + snakemake.params.country_codes + ) + + grid = TimeGrid( + start=start, + end=end, + frequency=snakemake.params.frequency, + ) + + prepare_entsoe( + input_path=snakemake.input.raw_load, + output_path=snakemake.output.load, + grid=grid, + country_codes=country_codes, + ) + + +if __name__ == "__main__": + main(snakemake) diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index 04608b3..e4b8fb8 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -1,166 +1,70 @@ -"""Prepare NESO historic demand on the configured hourly target grid.""" +"""Snakemake entry point for NESO demand preparation.""" import logging import sys -from collections.abc import Iterable from pathlib import Path from typing import TYPE_CHECKING, Any -import pandas as pd from cleaning.advanced.planning.manifest import get_batch, load_execution_plan -from cleaning.sources.neso import add_utc_timestamps -from common.time import build_hourly_index +from sources.neso.prepare import prepare_neso +from tclean import TimeGrid if TYPE_CHECKING: snakemake: Any -logger = logging.getLogger(__name__) - -REQUIRED_COLUMNS = ["SETTLEMENT_DATE", "SETTLEMENT_PERIOD", "ND"] - - -def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: - """Read and combine annual NESO historic-demand files.""" - frames: list[pd.DataFrame] = [] - - for raw_path in paths: - path = Path(raw_path) - - logger.info("Reading NESO historic demand from %s.", path) - - try: - frame = pd.read_csv(path, usecols=REQUIRED_COLUMNS) - except ValueError as error: - available_columns = pd.read_csv(path, nrows=0).columns.tolist() - - raise ValueError( - f"NESO file {path} does not contain the required " - f"columns {REQUIRED_COLUMNS}. " - f"Available columns: {available_columns}" - ) from error - - frames.append(frame) - - return pd.concat(frames, ignore_index=True) - - -def _prepare_half_hourly_demand(raw: pd.DataFrame) -> pd.Series: - """Convert raw NESO records to a UTC half-hourly demand series.""" - prepared = add_utc_timestamps(raw) - - prepared["ND"] = pd.to_numeric(prepared["ND"], errors="coerce") - - invalid_demand_count = int(prepared["ND"].isna().sum()) - - if invalid_demand_count: - logger.warning( - "NESO contains %s missing or non-numeric ND values.", invalid_demand_count - ) - - half_hourly = prepared.set_index("timestamp")["ND"].sort_index().rename("GBR") - - duplicate_mask = half_hourly.index.duplicated(keep=False) - - if duplicate_mask.any(): - duplicate_timestamps = ( - half_hourly.index[duplicate_mask].unique().astype(str).tolist() - ) - - raise ValueError( - f"NESO data contain duplicate UTC timestamps: {duplicate_timestamps[:10]}" - ) - - return half_hourly - - -def _aggregate_hourly(half_hourly: pd.Series) -> pd.Series: - """Aggregate half-hourly MW observations to hourly mean MW.""" - hourly_counts = half_hourly.resample("1h").count() +def main(snakemake: Any) -> None: + """Prepare NESO demand for the requested workflow period.""" + plan_path = getattr( + snakemake.input, + "plan", + None, + ) - incomplete_hours = hourly_counts.loc[hourly_counts.between(1, 1, inclusive="both")] + if plan_path is not None: + plan = load_execution_plan(plan_path) - if not incomplete_hours.empty: - logger.warning( - "NESO contains %s hours with only one valid half-hourly ND observation.", - len(incomplete_hours), + batch = get_batch( + plan, + batch_id=snakemake.wildcards.batch_id, + source="neso", ) - hourly = half_hourly.resample("1h").mean().rename("GBR") - - return hourly - - -def prepare_load_neso( - *, - input_paths: Iterable[str | Path], - output_path: str | Path, - temporal_start: str, - temporal_end: str, - countries: Iterable[str], -) -> None: - """Prepare NESO demand on the common time-country target grid.""" - target_countries = list(countries) - - target_index = build_hourly_index(start=temporal_start, end=temporal_end) - - result = pd.DataFrame(index=target_index, columns=target_countries, dtype=float) - - if "GBR" not in target_countries: - logger.info( - "GBR is not part of the configured country scope. " - "Writing an empty NESO target-grid dataframe." - ) + start = batch["start"] + end = batch["end"] + countries = batch["countries"] else: - raw = _read_neso_files(input_paths) - half_hourly = _prepare_half_hourly_demand(raw) - hourly = _aggregate_hourly(half_hourly) - - result["GBR"] = hourly.reindex(target_index) - - supplied = int(result["GBR"].notna().sum()) - - missing = int(result["GBR"].isna().sum()) - - logger.info( - "Prepared NESO GBR demand: %s supplied hourly " - "values and %s missing values.", - supplied, - missing, - ) - - output_path = Path(output_path) - output_path.parent.mkdir(parents=True, exist_ok=True) + start = snakemake.params.start + end = snakemake.params.end + countries = snakemake.params.country_codes - result.to_parquet(output_path) + grid = TimeGrid( + start=start, + end=end, + frequency=snakemake.params.frequency, + ) - logger.info( - "Saved prepared NESO demand to %s with shape %s.", output_path, result.shape + prepare_neso( + input_paths=[ + Path(path) + for path in snakemake.input.annual_files + ], + output_path=snakemake.output.load, + target_index=grid.target_index, + countries=countries, ) if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - - plan_path = getattr(snakemake.input, "plan", None) - - if plan_path is not None: - plan = load_execution_plan(plan_path) - batch = get_batch(plan, batch_id=snakemake.wildcards.batch_id, source="neso") - temporal_start = batch["start"] - temporal_end = batch["end"] - countries = batch["countries"] - else: - temporal_start = snakemake.params.start - temporal_end = snakemake.params.end - countries = snakemake.params.country_codes + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) - prepare_load_neso( - input_paths=[Path(path) for path in snakemake.input.annual_files], - output_path=snakemake.output.load, - temporal_start=temporal_start, - temporal_end=temporal_end, - countries=countries, + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", ) + + main(snakemake) diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 51bd3cb..00325f8 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -1,96 +1,63 @@ -"""Prepare electricity-demand data downloaded from OPSD.""" +"""Snakemake entry point for preparing OPSD demand data.""" import sys from typing import TYPE_CHECKING, Any -from warnings import warn -import pandas as pd -import pycountry from cleaning.advanced.planning.manifest import get_batch, load_execution_plan -from common.schemas import OPSDLoad -from common.time import as_utc_timestamp, build_hourly_index +from sources.opsd.prepare import prepare_opsd +from tclean import TimeGrid if TYPE_CHECKING: snakemake: Any -def get_map_alpha2_to_alpha3(countries_alpha_2) -> dict[str, str]: - """Map ISO alpha-2 country codes to alpha-3 codes.""" - mapping = {} - - for alpha2 in countries_alpha_2: - country = pycountry.countries.get(alpha_2=alpha2) - - if country is not None: - mapping[alpha2] = country.alpha_3 - else: - warn(f"Country with alpha-2 code '{alpha2}' not found in pycountry.") - - return mapping - - -def main(path_raw_load, output_load, start, end, country_codes): - """Prepare OPSD demand for the configured scope.""" - load = pd.read_csv(path_raw_load) - load = OPSDLoad.validate(load) - - load = load.loc[load["variable"] == "load"] - load = load.loc[load["attribute"] == "actual_entsoe_power_statistics"].copy() - - start = as_utc_timestamp(start) - end = as_utc_timestamp(end) - - load["utc_timestamp"] = pd.to_datetime(load["utc_timestamp"], utc=True) - - # Filter the large long-format table before pivoting. - load = load.loc[ - (load["utc_timestamp"] >= start) & (load["utc_timestamp"] < end) - ].copy() - - country_mapping = get_map_alpha2_to_alpha3(load["region"].unique()) - - load = load.loc[load["region"].isin(country_mapping)].copy() - - load.loc[:, "region"] = load["region"].map(country_mapping) - - # Keep only countries required by this module run. - load = load.loc[load["region"].isin(country_codes)].copy() - - load.loc[:, "data"] = pd.to_numeric(load["data"], errors="raise") - - prepared = pd.pivot(load, index="utc_timestamp", columns="region", values="data") - - target_index = build_hourly_index(start=start, end=end) - - prepared = prepared.reindex(index=target_index, columns=country_codes) - - prepared = prepared.astype(float) - - prepared.to_parquet(output_load) - - -if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - - plan_path = getattr(snakemake.input, "plan", None) +def main(snakemake: Any) -> None: + """Prepare OPSD demand for the requested workflow period.""" + plan_path = getattr( + snakemake.input, + "plan", + None, + ) if plan_path is not None: plan = load_execution_plan(plan_path) + batch = get_batch( - plan, batch_id=snakemake.wildcards.batch_id, source="opsd_api" + plan, + batch_id=snakemake.wildcards.batch_id, + source="opsd_api", ) + start = batch["start"] end = batch["end"] country_codes = batch["countries"] + else: start = snakemake.params.start end = snakemake.params.end - country_codes = list(snakemake.params.country_codes) + country_codes = list( + snakemake.params.country_codes + ) - main( - path_raw_load=snakemake.input.load, - output_load=snakemake.output.load, + grid = TimeGrid( start=start, end=end, + frequency=snakemake.params.frequency, + ) + + prepare_opsd( + input_path=snakemake.input.load, + output_path=snakemake.output.load, + grid=grid, country_codes=country_codes, ) + + +if __name__ == "__main__": + sys.stderr = open( + snakemake.log[0], + "w", + buffering=1, + ) + + main(snakemake) diff --git a/workflow/scripts/sources/entsoe/download.py b/workflow/scripts/sources/entsoe/download.py new file mode 100644 index 0000000..5668f9a --- /dev/null +++ b/workflow/scripts/sources/entsoe/download.py @@ -0,0 +1,168 @@ +"""Download electricity load data from ENTSO-E.""" + +import logging +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path +from time import perf_counter + +import pandas as pd +import pycountry +from entsoe.entsoe import EntsoePandasClient +from entsoe.exceptions import NoMatchingDataError + +logger = logging.getLogger(__name__) + + +def load_token(filepath: str | Path) -> str: + """Load an ENTSO-E API token from a text file.""" + return Path(filepath).read_text().strip() + + +def download_country( + *, + country_alpha_3: str, + start: pd.Timestamp, + end: pd.Timestamp, + token: str, +) -> tuple[str, pd.Series, float]: + """Download ENTSO-E load for one country.""" + country = pycountry.countries.get( + alpha_3=country_alpha_3 + ) + + if country is None: + raise ValueError( + f"Unknown ISO alpha-3 country code: " + f"{country_alpha_3!r}." + ) + + country_alpha_2 = country.alpha_2 + + client = EntsoePandasClient( + api_key=token, + timeout=60, + ) + + country_start = perf_counter() + + try: + data = client.query_load( + country_code=country_alpha_2, + start=start, + end=end, + ) + + data = data["Actual Load"] + data.name = country_alpha_3 + + except NoMatchingDataError: + logger.warning( + "No data found for %s/%s in the given period: %s to %s.", + country_alpha_2, + country_alpha_3, + start, + end, + ) + + data = pd.Series( + name=country_alpha_3, + dtype=float, + ) + + elapsed = perf_counter() - country_start + + return country_alpha_3, data, elapsed + + +def download_entsoe( + *, + start: pd.Timestamp, + end: pd.Timestamp, + country_codes: list[str], + token_path: str | Path, + output_path: str | Path, + workers: int, +) -> None: + """Download raw ENTSO-E load data for the requested countries.""" + token = load_token(token_path) + output_path = Path(output_path) + + total_countries = len(country_codes) + download_start = perf_counter() + + logger.info( + "Downloading ENTSO-E load for %s countries " + "from %s to %s using %s parallel workers.", + total_countries, + start, + end, + workers, + ) + + data_by_country: dict[str, pd.Series] = {} + + with ThreadPoolExecutor( + max_workers=workers + ) as executor: + futures = { + executor.submit( + download_country, + country_alpha_3=country_code, + start=start, + end=end, + token=token, + ): country_code + for country_code in country_codes + } + + for completed, future in enumerate( + as_completed(futures), + start=1, + ): + country_code = futures[future] + + try: + ( + country_code, + country_data, + elapsed, + ) = future.result() + + except Exception as exc: + raise RuntimeError( + "Failed to download ENTSO-E load for " + f"{country_code!r}." + ) from exc + + data_by_country[country_code] = ( + country_data + ) + + logger.info( + "[%s/%s] Finished %s in %.1fs.", + completed, + total_countries, + country_code, + elapsed, + ) + + data = [ + data_by_country[country_code] + for country_code in country_codes + ] + + raw = pd.concat(data, axis=1) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + raw.to_parquet(output_path) + + logger.info( + "Finished ENTSO-E downloads in %.1fs. " + "Saved raw data to %s.", + perf_counter() - download_start, + output_path, + ) diff --git a/workflow/scripts/sources/entsoe/prepare.py b/workflow/scripts/sources/entsoe/prepare.py new file mode 100644 index 0000000..e912cfc --- /dev/null +++ b/workflow/scripts/sources/entsoe/prepare.py @@ -0,0 +1,39 @@ +"""Prepare downloaded ENTSO-E electricity-demand data.""" + +from pathlib import Path + +import pandas as pd +from tclean import TimeGrid + + +def prepare_entsoe( + *, + input_path: str | Path, + output_path: str | Path, + grid: TimeGrid, + country_codes: list[str], +) -> None: + """Prepare ENTSO-E demand on the configured target grid.""" + data = pd.read_parquet(input_path) + + data.index = pd.to_datetime( + data.index, + utc=True, + ) + + data = data.resample("1h").mean() + + data = data.reindex( + index=grid.target_index, + columns=country_codes, + ) + + data = data.astype(float) + + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + data.to_parquet(output_path) diff --git a/workflow/scripts/sources/neso/download.py b/workflow/scripts/sources/neso/download.py new file mode 100644 index 0000000..8f83460 --- /dev/null +++ b/workflow/scripts/sources/neso/download.py @@ -0,0 +1,203 @@ +"""Download annual historic electricity-demand data from NESO.""" + +import json +import logging +import shutil +from pathlib import Path +from typing import Any +from urllib.parse import urlencode +from urllib.request import Request, urlopen + +logger = logging.getLogger(__name__) + +NESO_API_BASE = "https://api.neso.energy/api/3/action" +USER_AGENT = "modelblocks-module-demand-electricity/NESO historic demand downloader" + + +def _request_json( + endpoint: str, + parameters: dict[str, str | int], +) -> dict[str, Any]: + """Request one JSON response from the NESO CKAN API.""" + query = urlencode(parameters) + url = f"{NESO_API_BASE}/{endpoint}?{query}" + + request = Request( + url, + headers={"User-Agent": USER_AGENT}, + ) + + with urlopen(request, timeout=120) as response: + payload = json.load(response) + + if not payload.get("success", False): + raise RuntimeError( + f"NESO API request failed for {endpoint}: {payload}" + ) + + result = payload.get("result") + + if not isinstance(result, dict): + raise RuntimeError( + f"NESO API returned an unexpected result for {endpoint}." + ) + + return result + + +def _get_historic_demand_dataset() -> dict[str, Any]: + """Return the NESO Historic Demand Data dataset.""" + return _request_json( + "package_show", + {"id": "historic-demand-data"}, + ) + + +def _select_csv_resource( + dataset: dict[str, Any], + *, + year: int, +) -> dict[str, Any]: + """Select the annual NESO historic-demand CSV resource.""" + resources = dataset.get("resources", []) + + if not isinstance(resources, list): + raise RuntimeError( + "NESO Historic Demand Data has no valid resource list." + ) + + expected_filename = f"demanddata_{year}.csv" + expected_title = f"historic demand data {year}" + + matching_resources: list[dict[str, Any]] = [] + + for resource in resources: + name = str( + resource.get("name", "") + ).strip().casefold() + + url = str( + resource.get("url", "") + ).strip() + + format_name = str( + resource.get("format", "") + ).strip().casefold() + + url_lower = url.casefold() + + is_csv = ( + format_name == "csv" + or url_lower.endswith(".csv") + ) + + matches_year = ( + expected_title in name + or expected_filename in url_lower + ) + + if is_csv and matches_year and url: + matching_resources.append(resource) + + if len(matching_resources) != 1: + available_resources = [ + { + "name": resource.get("name"), + "format": resource.get("format"), + "url": resource.get("url"), + } + for resource in resources + ] + + raise RuntimeError( + "Could not identify exactly one NESO historic-demand " + f"CSV resource for {year}. " + f"Matches: {len(matching_resources)}. " + f"Resources: {available_resources}" + ) + + return matching_resources[0] + + +def _download_file( + *, + url: str, + output_path: Path, +) -> None: + """Download one file atomically.""" + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + temporary_path = output_path.with_suffix( + output_path.suffix + ".part" + ) + + temporary_path.unlink(missing_ok=True) + + request = Request( + url, + headers={"User-Agent": USER_AGENT}, + ) + + try: + with ( + urlopen(request, timeout=300) as response, + temporary_path.open("wb") as output_file, + ): + shutil.copyfileobj( + response, + output_file, + ) + + if temporary_path.stat().st_size == 0: + raise RuntimeError( + f"NESO download produced an empty file: {url}" + ) + + temporary_path.replace(output_path) + + except Exception: + temporary_path.unlink(missing_ok=True) + raise + + +def download_annual_file( + *, + year: int, + output_path: str | Path, +) -> None: + """Discover and download one annual NESO historic-demand CSV.""" + output_path = Path(output_path) + + dataset = _get_historic_demand_dataset() + + logger.info( + "Selecting NESO historic-demand resource for %s.", + year, + ) + + resource = _select_csv_resource( + dataset, + year=year, + ) + + url = str(resource["url"]) + + logger.info( + "Downloading NESO historic demand for %s from %s.", + year, + url, + ) + + _download_file( + url=url, + output_path=output_path, + ) + + logger.info( + "Saved NESO historic demand for %s to %s.", + year, + output_path, + ) diff --git a/workflow/scripts/sources/neso/prepare.py b/workflow/scripts/sources/neso/prepare.py new file mode 100644 index 0000000..8b8f25a --- /dev/null +++ b/workflow/scripts/sources/neso/prepare.py @@ -0,0 +1,168 @@ +"""Prepare NESO historic demand for Modelblocks.""" + +import logging +from collections.abc import Iterable +from pathlib import Path + +import pandas as pd + +logger = logging.getLogger(__name__) + +REQUIRED_COLUMNS = ["SETTLEMENT_DATE", "SETTLEMENT_PERIOD", "ND"] + + +def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: + """Add UTC timestamps derived from NESO settlement dates and periods.""" + # Keep your existing implementation here. + ... + + +def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: + """Read and combine annual NESO historic-demand files.""" + frames: list[pd.DataFrame] = [] + + for raw_path in paths: + path = Path(raw_path) + + logger.info("Reading NESO historic demand from %s.", path) + + try: + frame = pd.read_csv(path, usecols=REQUIRED_COLUMNS) + except ValueError as error: + available_columns = pd.read_csv(path, nrows=0).columns.tolist() + + raise ValueError( + f"NESO file {path} does not contain the required " + f"columns {REQUIRED_COLUMNS}. " + f"Available columns: {available_columns}" + ) from error + + frames.append(frame) + + return pd.concat(frames, ignore_index=True) + + +def _prepare_half_hourly_demand(raw: pd.DataFrame) -> pd.Series: + """Convert raw NESO records to a UTC half-hourly demand series.""" + prepared = add_utc_timestamps(raw) + + prepared["ND"] = pd.to_numeric( + prepared["ND"], + errors="coerce", + ) + + invalid_demand_count = int( + prepared["ND"].isna().sum() + ) + + if invalid_demand_count: + logger.warning( + "NESO contains %s missing or non-numeric ND values.", + invalid_demand_count, + ) + + half_hourly = ( + prepared.set_index("timestamp")["ND"] + .sort_index() + .rename("GBR") + ) + + duplicate_mask = half_hourly.index.duplicated( + keep=False + ) + + if duplicate_mask.any(): + duplicate_timestamps = ( + half_hourly.index[duplicate_mask] + .unique() + .astype(str) + .tolist() + ) + + raise ValueError( + "NESO data contain duplicate UTC timestamps: " + f"{duplicate_timestamps[:10]}" + ) + + return half_hourly + + +def _aggregate_hourly( + half_hourly: pd.Series, +) -> pd.Series: + """Aggregate half-hourly MW observations to hourly mean MW.""" + hourly_counts = half_hourly.resample("1h").count() + + incomplete_hours = hourly_counts.loc[ + hourly_counts.between( + 1, + 1, + inclusive="both", + ) + ] + + if not incomplete_hours.empty: + logger.warning( + "NESO contains %s hours with only one valid " + "half-hourly ND observation.", + len(incomplete_hours), + ) + + return ( + half_hourly.resample("1h") + .mean() + .rename("GBR") + ) + + +def prepare_neso( + *, + input_paths: Iterable[str | Path], + output_path: str | Path, + target_index: pd.DatetimeIndex, + countries: Iterable[str], +) -> None: + """Prepare NESO demand on the requested canonical target index.""" + target_countries = list(countries) + + result = pd.DataFrame( + index=target_index, + columns=target_countries, + dtype=float, + ) + + if "GBR" not in target_countries: + logger.info( + "GBR is not part of the configured country scope. " + "Writing an empty NESO target-grid dataframe." + ) + else: + raw = _read_neso_files(input_paths) + half_hourly = _prepare_half_hourly_demand(raw) + hourly = _aggregate_hourly(half_hourly) + + result["GBR"] = hourly.reindex(target_index) + + supplied = int(result["GBR"].notna().sum()) + missing = int(result["GBR"].isna().sum()) + + logger.info( + "Prepared NESO GBR demand: %s supplied hourly " + "values and %s missing values.", + supplied, + missing, + ) + + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + result.to_parquet(output_path) + + logger.info( + "Saved prepared NESO demand to %s with shape %s.", + output_path, + result.shape, + ) diff --git a/workflow/scripts/sources/opsd/download.py b/workflow/scripts/sources/opsd/download.py new file mode 100644 index 0000000..2cc3471 --- /dev/null +++ b/workflow/scripts/sources/opsd/download.py @@ -0,0 +1,118 @@ +"""Download and cache the fixed OPSD electricity-demand snapshot.""" + +import logging +import shutil +from pathlib import Path +from urllib.request import Request, urlopen + +import pandas as pd + +logger = logging.getLogger(__name__) + +USER_AGENT = "modelblocks-module-demand-electricity/OPSD downloader" + +REQUIRED_COLUMNS = { + "utc_timestamp", + "region", + "variable", + "attribute", + "data", +} + + +def _is_valid_cached_snapshot(path: Path) -> bool: + """Return whether an existing OPSD snapshot is suitable for reuse.""" + if not path.exists() or path.stat().st_size == 0: + return False + + try: + columns = set(pd.read_csv(path, nrows=0).columns) + except (OSError, ValueError): + return False + + return REQUIRED_COLUMNS.issubset(columns) + + +def download_opsd( + *, + url: str, + output_path: str | Path, +) -> None: + """Download the OPSD snapshot unless a valid cached copy exists.""" + output_path = Path(output_path) + + if _is_valid_cached_snapshot(output_path): + logger.info("Using cached OPSD snapshot: %s", output_path) + return + + if output_path.exists(): + logger.warning( + "Cached OPSD snapshot is invalid; downloading a replacement." + ) + output_path.unlink() + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + temporary_path = output_path.with_suffix( + output_path.suffix + ".part" + ) + temporary_path.unlink(missing_ok=True) + + logger.info( + "Downloading OPSD snapshot from %s.", + url, + ) + + request = Request( + url, + headers={"User-Agent": USER_AGENT}, + ) + + try: + with ( + urlopen(request, timeout=300) as response, + temporary_path.open("wb") as output_file, + ): + content_length = response.headers.get( + "Content-Length" + ) + + shutil.copyfileobj( + response, + output_file, + ) + + downloaded_size = temporary_path.stat().st_size + + if ( + content_length is not None + and downloaded_size != int(content_length) + ): + raise RuntimeError( + "OPSD download is incomplete: " + f"received {downloaded_size} " + f"of {content_length} bytes." + ) + + if not _is_valid_cached_snapshot( + temporary_path + ): + raise RuntimeError( + "Downloaded OPSD snapshot does not contain " + "the expected CSV structure." + ) + + temporary_path.replace(output_path) + + except Exception: + temporary_path.unlink(missing_ok=True) + raise + + logger.info( + "Saved OPSD snapshot to %s (%s bytes).", + output_path, + output_path.stat().st_size, + ) diff --git a/workflow/scripts/sources/opsd/prepare.py b/workflow/scripts/sources/opsd/prepare.py new file mode 100644 index 0000000..035df24 --- /dev/null +++ b/workflow/scripts/sources/opsd/prepare.py @@ -0,0 +1,108 @@ +"""Prepare electricity-demand data downloaded from OPSD.""" + +from pathlib import Path +from warnings import warn + +import pandas as pd +import pycountry +from common.schemas import OPSDLoad +from tclean import TimeGrid + + +def get_map_alpha2_to_alpha3( + countries_alpha_2, +) -> dict[str, str]: + """Map ISO alpha-2 country codes to alpha-3 codes.""" + mapping = {} + + for alpha2 in countries_alpha_2: + country = pycountry.countries.get( + alpha_2=alpha2 + ) + + if country is not None: + mapping[alpha2] = country.alpha_3 + else: + warn( + f"Country with alpha-2 code " + f"'{alpha2}' not found in pycountry." + ) + + return mapping + + +def prepare_opsd( + *, + input_path: str | Path, + output_path: str | Path, + grid: TimeGrid, + country_codes: list[str], +) -> None: + """Prepare OPSD demand on the requested canonical target index.""" + load = pd.read_csv(input_path) + load = OPSDLoad.validate(load) + + load = load.loc[ + load["variable"] == "load" + ] + + load = load.loc[ + load["attribute"] + == "actual_entsoe_power_statistics" + ].copy() + + load["utc_timestamp"] = pd.to_datetime( + load["utc_timestamp"], + utc=True, + ) + + # target_index is end-exclusive in conceptual terms, so + # derive the exclusive bound from its frequency externally + # or simply filter to timestamps represented in the index. + load = load.loc[ + (load["utc_timestamp"] >= grid.start) + & (load["utc_timestamp"] < grid.end) + ].copy() + + country_mapping = get_map_alpha2_to_alpha3( + load["region"].unique() + ) + + load = load.loc[ + load["region"].isin(country_mapping) + ].copy() + + load.loc[:, "region"] = load[ + "region" + ].map(country_mapping) + + load = load.loc[ + load["region"].isin(country_codes) + ].copy() + + load.loc[:, "data"] = pd.to_numeric( + load["data"], + errors="raise", + ) + + prepared = pd.pivot( + load, + index="utc_timestamp", + columns="region", + values="data", + ) + + prepared = prepared.reindex( + index=grid.target_index, + columns=country_codes, + ) + + prepared = prepared.astype(float) + + output_path = Path(output_path) + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + prepared.to_parquet(output_path) From c8f340590abba20213b46d590493de38699faf46 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 11:10:22 +0200 Subject: [PATCH 105/241] Updated config schema to require frequency. --- tests/integration/test_config.yaml | 1 + workflow/internal/config.schema.yaml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index 0bf1058..e16bb24 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -2,6 +2,7 @@ integration_test: temporal_scope: start: "2017-01-01" end: "2017-01-03" + frequency: "1h" # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api load_sources: - entsoe_api diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 1f26596..bbd6767 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -327,10 +327,14 @@ properties: end: $ref: "#/$defs/iso_date" + + frequency: + $ref: "#/$defs/duration" required: - start - end + - frequency additionalProperties: false From 344aab676c016484b15c6bbe9b2f7269c975b52b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 11:35:40 +0200 Subject: [PATCH 106/241] Fixed naming bugs --- ..._load_entsoe.py => prepare_load_entsoe.py} | 0 workflow/scripts/sources/neso/prepare.py | 81 ++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) rename workflow/scripts/{prepapre_load_entsoe.py => prepare_load_entsoe.py} (100%) diff --git a/workflow/scripts/prepapre_load_entsoe.py b/workflow/scripts/prepare_load_entsoe.py similarity index 100% rename from workflow/scripts/prepapre_load_entsoe.py rename to workflow/scripts/prepare_load_entsoe.py diff --git a/workflow/scripts/sources/neso/prepare.py b/workflow/scripts/sources/neso/prepare.py index 8b8f25a..b73d6e4 100644 --- a/workflow/scripts/sources/neso/prepare.py +++ b/workflow/scripts/sources/neso/prepare.py @@ -12,9 +12,84 @@ def add_utc_timestamps(data: pd.DataFrame) -> pd.DataFrame: - """Add UTC timestamps derived from NESO settlement dates and periods.""" - # Keep your existing implementation here. - ... + """Convert NESO settlement dates and periods to UTC timestamps.""" + prepared = data.copy() + + prepared["SETTLEMENT_DATE"] = pd.to_datetime( + prepared["SETTLEMENT_DATE"], + errors="raise", + ).dt.normalize() + + prepared["SETTLEMENT_PERIOD"] = pd.to_numeric( + prepared["SETTLEMENT_PERIOD"], + errors="raise", + ).astype(int) + + timestamp_parts: list[pd.Series] = [] + + for settlement_date, day in prepared.groupby( + "SETTLEMENT_DATE", + sort=True, + ): + day = day.sort_values("SETTLEMENT_PERIOD").copy() + + expected_periods = list( + range(1, len(day) + 1) + ) + observed_periods = day[ + "SETTLEMENT_PERIOD" + ].tolist() + + if observed_periods != expected_periods: + raise ValueError( + "NESO settlement periods are not consecutive for " + f"{settlement_date.date()}. " + f"Expected 1-{len(day)}." + ) + + local_start = pd.Timestamp( + settlement_date, + tz="Europe/London", + ) + local_end = ( + local_start + pd.DateOffset(days=1) + ) + + expected_index = pd.date_range( + start=local_start, + end=local_end, + freq="30min", + inclusive="left", + ) + + if len(day) != len(expected_index): + raise ValueError( + "NESO settlement-period count does not match the " + "Europe/London clock for " + f"{settlement_date.date()}: " + f"{len(day)} records versus " + f"{len(expected_index)} expected." + ) + + timestamp_parts.append( + pd.Series( + expected_index, + index=day.index, + name="timestamp", + ) + ) + + prepared["timestamp"] = ( + pd.concat(timestamp_parts) + .sort_index() + ) + + prepared["timestamp"] = ( + prepared["timestamp"] + .dt.tz_convert("UTC") + ) + + return prepared.sort_values("timestamp") def _read_neso_files(paths: Iterable[str | Path]) -> pd.DataFrame: From f3b6dfd30038964147cffa0d0c59e3f9f0f49a47 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 11:37:47 +0200 Subject: [PATCH 107/241] Updated snakemake rules to use newly refactored module directories --- workflow/internal/settings.yaml | 2 +- workflow/rules/advanced.smk | 65 +++++++++++++++++++++++++++------ workflow/rules/automatic.smk | 18 ++++----- workflow/rules/clean.smk | 31 +++++++++++++--- 4 files changed, 90 insertions(+), 26 deletions(-) diff --git a/workflow/internal/settings.yaml b/workflow/internal/settings.yaml index 2849f4d..9a6f9ef 100644 --- a/workflow/internal/settings.yaml +++ b/workflow/internal/settings.yaml @@ -6,7 +6,7 @@ resources: population: "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/GHSL/GHS_POP_GLOBE_R2023A/GHS_POP_E2020_GLOBE_R2023A_54009_1000/V1-0/GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.zip" population_tif: "GHS_POP_E2020_GLOBE_R2023A_54009_1000_V1_0.tif" -load_entsoe_api: +load_entsoe: countries: # Download all ENTSO-E countries - "ALB" diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 4bddd41..8f6ab77 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -99,11 +99,11 @@ def auxiliary_entsoe_threads(wildcards): batch = next( batch for batch in plan["batches"] - if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe_api") + if (batch["batch_id"] == wildcards.batch_id and batch["source"] == "entsoe") ) return min( - internal["load_entsoe_api"]["MAX_WORKERS"], + internal["load_entsoe"]["MAX_WORKERS"], len(batch["countries"]), ) @@ -145,22 +145,57 @@ rule finalise_clean_demand: "../scripts/finalise_clean_demand.py" -rule download_auxiliary_load_entsoe_api: +rule download_auxiliary_load_entsoe: input: token_entsoe="", plan=auxiliary_acquisition_plan, output: - load=("/automatic/" "auxiliary/entsoe_api/" "{batch_id}.parquet"), + raw_load=( + "/automatic/" + "auxiliary/entsoe/raw/" + "{batch_id}.parquet" + ), log: - ("/auxiliary/" "entsoe_api/{batch_id}.log"), + ( + "/auxiliary/" + "entsoe/download_{batch_id}.log" + ), localrule: True conda: "../envs/module.yaml" - threads: auxiliary_entsoe_threads + threads: + auxiliary_entsoe_threads + params: + frequency=config["temporal_scope"]["frequency"], message: "Download auxiliary electricity load from ENTSO-E." script: - "../scripts/download_load_entsoe_api.py" + "../scripts/download_load_entsoe.py" + + +rule prepare_auxiliary_load_entsoe: + input: + plan=auxiliary_acquisition_plan, + raw_load=rules.download_auxiliary_load_entsoe.output.raw_load, + output: + load=( + "/automatic/" + "auxiliary/entsoe/" + "{batch_id}.parquet" + ), + log: + ( + "/auxiliary/" + "entsoe/prepare_{batch_id}.log" + ), + conda: + "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], + message: + "Prepare auxiliary electricity-demand data from ENTSO-E." + script: + "../scripts/prepare_load_entsoe.py" rule prepare_auxiliary_load_opsd: @@ -168,11 +203,13 @@ rule prepare_auxiliary_load_opsd: load=rules.download_load_opsd.output.load, plan=auxiliary_acquisition_plan, output: - load=("/automatic/" "auxiliary/opsd_api/" "{batch_id}.parquet"), + load=("/automatic/" "auxiliary/opsd/" "{batch_id}.parquet"), log: - ("/auxiliary/" "opsd_api/{batch_id}.log"), + ("/auxiliary/" "opsd/{batch_id}.log"), conda: "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], message: "Prepare auxiliary electricity-demand data from OPSD." script: @@ -184,11 +221,17 @@ rule prepare_auxiliary_load_neso: plan=auxiliary_acquisition_plan, annual_files=auxiliary_neso_raw_files, output: - load=("/automatic/" "auxiliary/neso/" "{batch_id}.parquet"), + load=( + "/automatic/" + "auxiliary/neso/" + "{batch_id}.parquet" + ), log: - ("/auxiliary/" "neso/{batch_id}.log"), + "/auxiliary/neso/{batch_id}.log", conda: "../envs/module.yaml" + params: + frequency=config["temporal_scope"]["frequency"], message: "Prepare auxiliary electricity-demand data from NESO." script: diff --git a/workflow/rules/automatic.smk b/workflow/rules/automatic.smk index 3b7927b..fa3babe 100644 --- a/workflow/rules/automatic.smk +++ b/workflow/rules/automatic.smk @@ -14,31 +14,31 @@ rule validate_config_semantics: "../scripts/validate_config.py" -rule download_load_entsoe_api: +rule download_load_entsoe: input: validation="/automatic/config_validation.json", token_entsoe="", output: - load="/automatic/load_entsoe_api.parquet", + raw_load="/automatic/entsoe/raw_load.parquet", log: - "/download_load_entsoe_api.log", + "/download_load_entsoe.log", localrule: True conda: "../envs/module.yaml" threads: min( - internal["load_entsoe_api"]["MAX_WORKERS"], - len(internal["load_entsoe_api"]["countries"]), - ) + internal["load_entsoe"]["MAX_WORKERS"], + len(internal["load_entsoe"]["countries"]), + ), params: - country_codes=internal["load_entsoe_api"]["countries"], temporal_start=config["temporal_scope"]["start"], temporal_end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], message: "Download electricity load from ENTSOE." script: - "../scripts/download_load_entsoe_api.py" - + "../scripts/download_load_entsoe.py" rule download_load_opsd: output: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index af7c808..41ad13b 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -17,13 +17,32 @@ def neso_raw_files(_wildcards): ("/automatic/neso/" f"historic_demand_{year}.csv") for year in years ] +rule prepare_load_entsoe: + input: + raw_load=rules.download_load_entsoe.output.raw_load, + output: + load="/automatic/load_entsoe.parquet", + localrule: True + conda: + "../envs/module.yaml" + log: + "/prepare_load_entsoe.log", + params: + temporal_start=config["temporal_scope"]["start"], + temporal_end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], + message: + "Prepare electricity load from ENTSOE." + script: + "../scripts/prepare_load_entsoe.py" rule prepare_load_opsd: input: validation="/automatic/config_validation.json", load="/automatic/load_opsd.csv", output: - load="/automatic/load_opsd_api.parquet", + load="/automatic/load_opsd.parquet", log: "/prepare_load_opsd.log", conda: @@ -31,7 +50,8 @@ rule prepare_load_opsd: params: start=config["temporal_scope"]["start"], end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe_api"]["countries"], + frequency=config["temporal_scope"]["frequency"], + country_codes=internal["load_entsoe"]["countries"], message: "Prepare electricity-demand data from OPSD." script: @@ -51,7 +71,8 @@ rule prepare_load_neso: params: start=config["temporal_scope"]["start"], end=config["temporal_scope"]["end"], - country_codes=internal["load_entsoe_api"]["countries"], + country_codes=internal["load_entsoe"]["countries"], + frequency=config["temporal_scope"]["frequency"], message: "Prepare electricity-demand data from NESO." script: @@ -59,9 +80,9 @@ rule prepare_load_neso: LOAD_SOURCE_PATHS = { - "entsoe_api": ("/automatic/" "load_entsoe_api.parquet"), + "entsoe": ("/automatic/" "load_entsoe.parquet"), "neso": ("/automatic/" "load_neso.parquet"), - "opsd_api": ("/automatic/" "load_opsd_api.parquet"), + "opsd": ("/automatic/" "load_opsd.parquet"), } From f8e47e954ae35f89be9282940aa853809e6da702 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 11:52:24 +0200 Subject: [PATCH 108/241] Fixed config schema issues. --- tests/integration/test_config.yaml | 8 ++++---- workflow/internal/config.schema.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index e16bb24..b39bcc4 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -3,15 +3,15 @@ integration_test: start: "2017-01-01" end: "2017-01-03" frequency: "1h" - # provide a list of sources and set their priority order. Options include: entsoe_api, opsd_api + # provide a list of sources and set their priority order. Options include: entsoe, neso, opsd load_sources: - - entsoe_api + - entsoe - neso - - opsd_api + - opsd gap_filling: # Sets the data-cleaning mode. Options: off, basic, advanced. - mode: basic + mode: advanced basic: # Basic gap-filling rules are applied in the listed order. diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index bbd6767..9a570e3 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -350,9 +350,9 @@ properties: items: type: string enum: - - entsoe_api + - entsoe - neso - - opsd_api + - opsd gap_filling: type: object From b4e386a8205aff87420ac15daba995ed2a9ff07b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 12:05:09 +0200 Subject: [PATCH 109/241] Started transfer to TClean --- tests/integration/test_config.yaml | 4 +- tests/unit/test_advanced_apply.py | 24 ++++----- tests/unit/test_advanced_combine_auxiliary.py | 6 +-- tests/unit/test_advanced_source_requests.py | 40 +++++++------- tests/unit/test_cleaning_pipeline.py | 4 +- tests/unit/test_config_schema.py | 4 +- tests/unit/test_plan_auxiliary_data.py | 36 ++++++------- tests/unit/test_validate_config.py | 4 +- workflow/internal/config.schema.yaml | 6 +-- workflow/rules/advanced.smk | 3 ++ .../cleaning/advanced/planning/execution.py | 53 ++++++++++++++++--- workflow/scripts/plan_auxiliary_data.py | 13 ++++- 12 files changed, 125 insertions(+), 72 deletions(-) diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index b39bcc4..cfe7478 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -31,13 +31,13 @@ integration_test: # Copies from the previous week for gaps of up to one week. - name: copy_previous_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: -168h # Copies from the following week for gaps of up to one week. - name: copy_following_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: 168h diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py index cd83db1..3caebd3 100644 --- a/tests/unit/test_advanced_apply.py +++ b/tests/unit/test_advanced_apply.py @@ -18,7 +18,7 @@ def _cleaning_method() -> pd.DataFrame: index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") return pd.DataFrame( - {"ALB": ["observed_entsoe_api", "missing", "observed_entsoe_api"]}, index=index + {"ALB": ["observed_entsoe", "missing", "observed_entsoe"]}, index=index ) @@ -54,9 +54,9 @@ def test_construct_from_sources_fills_gaps() -> None: assert filled["ALB"].tolist() == [1.0, 20.0, 3.0] assert methods["ALB"].tolist() == [ - "observed_entsoe_api", + "observed_entsoe", "construct_albania", - "observed_entsoe_api", + "observed_entsoe", ] @@ -185,7 +185,7 @@ def test_overwrite_replaces_existing_values() -> None: load = pd.DataFrame({"ALB": [10.0, 20.0, 30.0, 40.0]}, index=index) - cleaning_method = pd.DataFrame({"ALB": ["observed_entsoe_api"] * 4}, index=index) + cleaning_method = pd.DataFrame({"ALB": ["observed_entsoe"] * 4}, index=index) profile = pd.Series([100.0, 200.0], index=index[1:3], name="ALB") @@ -218,10 +218,10 @@ def test_overwrite_replaces_existing_values() -> None: assert filled["ALB"].tolist() == [10.0, 100.0, 200.0, 40.0] assert methods["ALB"].tolist() == [ - "observed_entsoe_api", + "observed_entsoe", "replace_albania", "replace_albania", - "observed_entsoe_api", + "observed_entsoe", ] @@ -234,10 +234,10 @@ def test_fill_gaps_preserves_existing_values() -> None: cleaning_method = pd.DataFrame( { "ALB": [ - "observed_entsoe_api", + "observed_entsoe", "missing", - "observed_entsoe_api", - "observed_entsoe_api", + "observed_entsoe", + "observed_entsoe", ] }, index=index, @@ -274,10 +274,10 @@ def test_fill_gaps_preserves_existing_values() -> None: assert filled["ALB"].tolist() == [10.0, 100.0, 30.0, 40.0] assert methods["ALB"].tolist() == [ - "observed_entsoe_api", + "observed_entsoe", "fill_albania", - "observed_entsoe_api", - "observed_entsoe_api", + "observed_entsoe", + "observed_entsoe", ] diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py index 6ece7ad..20cd8ed 100644 --- a/tests/unit/test_advanced_combine_auxiliary.py +++ b/tests/unit/test_advanced_combine_auxiliary.py @@ -11,7 +11,7 @@ def test_combine_auxiliary_sources_aligns_country_columns() -> None: neso = pd.DataFrame({"GBR": [12.0, 13.0]}, index=index) combined, data_source, cleaning_method = combine_auxiliary_sources( - {"entsoe_api": entsoe, "neso": neso}, priority=["neso", "entsoe_api"] + {"entsoe": entsoe, "neso": neso}, priority=["neso", "entsoe"] ) assert list(combined.columns) == ["GBR", "GRC"] @@ -22,14 +22,14 @@ def test_combine_auxiliary_sources_aligns_country_columns() -> None: assert data_source["GBR"].tolist() == ["neso", "neso"] - assert data_source["GRC"].tolist() == ["entsoe_api", "entsoe_api"] + assert data_source["GRC"].tolist() == ["entsoe", "entsoe"] assert cleaning_method.shape == combined.shape def test_combine_auxiliary_sources_handles_empty_input() -> None: combined, data_source, cleaning_method = combine_auxiliary_sources( - {}, priority=["entsoe_api", "neso", "opsd_api"] + {}, priority=["entsoe", "neso", "opsd"] ) assert combined.empty diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py index 44cf8e1..9807a01 100644 --- a/tests/unit/test_advanced_source_requests.py +++ b/tests/unit/test_advanced_source_requests.py @@ -21,15 +21,15 @@ def test_build_source_requests_uses_all_applicable_sources() -> None: ) result = build_auxiliary_source_requests( - requirements, source_names=["entsoe_api", "neso", "opsd_api"] + requirements, source_names=["entsoe", "neso", "opsd"] ) assert list(result[["source", "country"]].itertuples(index=False, name=None)) == [ - ("entsoe_api", "GBR"), - ("entsoe_api", "GRC"), + ("entsoe", "GBR"), + ("entsoe", "GRC"), ("neso", "GBR"), - ("opsd_api", "GBR"), - ("opsd_api", "GRC"), + ("opsd", "GBR"), + ("opsd", "GRC"), ] @@ -52,7 +52,7 @@ def test_empty_requirements_return_empty_source_request_schema() -> None: requirements = pd.DataFrame(columns=["country", "start", "end"]) result = build_auxiliary_source_requests( - requirements, source_names=["entsoe_api", "neso", "opsd_api"] + requirements, source_names=["entsoe", "neso", "opsd"] ) assert result.empty @@ -75,14 +75,14 @@ def test_unknown_source_is_rejected_when_planning_request() -> None: def test_build_batch_id_is_independent_of_country_order() -> None: first = _build_batch_id( - source="entsoe_api", + source="entsoe", start=pd.Timestamp("2020-01-01", tz="UTC"), end=pd.Timestamp("2020-02-01", tz="UTC"), countries=["ALB", "GRC"], ) second = _build_batch_id( - source="entsoe_api", + source="entsoe", start=pd.Timestamp("2020-01-01", tz="UTC"), end=pd.Timestamp("2020-02-01", tz="UTC"), countries=["GRC", "ALB"], @@ -90,12 +90,12 @@ def test_build_batch_id_is_independent_of_country_order() -> None: assert first == second - assert first.startswith("entsoe_api__20200101T0000__20200201T0000__") + assert first.startswith("entsoe__20200101T0000__20200201T0000__") def test_build_batch_id_changes_for_different_country_sets() -> None: common = { - "source": "entsoe_api", + "source": "entsoe", "start": pd.Timestamp("2020-01-01", tz="UTC"), "end": pd.Timestamp("2020-02-01", tz="UTC"), } @@ -117,7 +117,7 @@ def test_build_group_id_depends_only_on_period() -> None: def test_build_source_batches_groups_matching_periods() -> None: requests = pd.DataFrame( { - "source": ["entsoe_api", "entsoe_api", "entsoe_api"], + "source": ["entsoe", "entsoe", "entsoe"], "country": ["ALB", "GRC", "MNE"], "start": pd.to_datetime( ["2020-01-01", "2020-01-01", "2021-01-01"], utc=True @@ -137,12 +137,12 @@ def test_build_source_batches_groups_matching_periods() -> None: { "group_id": _build_group_id(start=first_start, end=first_end), "batch_id": _build_batch_id( - source="entsoe_api", + source="entsoe", start=first_start, end=first_end, countries=["ALB", "GRC"], ), - "source": "entsoe_api", + "source": "entsoe", "start": first_start, "end": first_end, "countries": ["ALB", "GRC"], @@ -150,12 +150,12 @@ def test_build_source_batches_groups_matching_periods() -> None: { "group_id": _build_group_id(start=second_start, end=second_end), "batch_id": _build_batch_id( - source="entsoe_api", + source="entsoe", start=second_start, end=second_end, countries=["MNE"], ), - "source": "entsoe_api", + "source": "entsoe", "start": second_start, "end": second_end, "countries": ["MNE"], @@ -169,7 +169,7 @@ def test_build_source_batches_keeps_sources_separate() -> None: requests = pd.DataFrame( { - "source": ["entsoe_api", "opsd_api"], + "source": ["entsoe", "opsd"], "country": ["GBR", "GBR"], "start": [start, start], "end": [end, end], @@ -184,9 +184,9 @@ def test_build_source_batches_keeps_sources_separate() -> None: { "group_id": group_id, "batch_id": _build_batch_id( - source="entsoe_api", start=start, end=end, countries=["GBR"] + source="entsoe", start=start, end=end, countries=["GBR"] ), - "source": "entsoe_api", + "source": "entsoe", "start": start, "end": end, "countries": ["GBR"], @@ -194,9 +194,9 @@ def test_build_source_batches_keeps_sources_separate() -> None: { "group_id": group_id, "batch_id": _build_batch_id( - source="opsd_api", start=start, end=end, countries=["GBR"] + source="opsd", start=start, end=end, countries=["GBR"] ), - "source": "opsd_api", + "source": "opsd", "start": start, "end": end, "countries": ["GBR"], diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py index 648116c..0d3f0f2 100644 --- a/tests/unit/test_cleaning_pipeline.py +++ b/tests/unit/test_cleaning_pipeline.py @@ -361,8 +361,8 @@ def test_advanced_mode_builds_auxiliary_fill_plan() -> None: _gap_report, auxiliary_fill_plan, ) = clean_demand( - {"entsoe_api": source}, - source_priority=["entsoe_api"], + {"entsoe": source}, + source_priority=["entsoe"], gap_filling_config=config, ) diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 9fa9294..54e0577 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -41,7 +41,7 @@ def test_advanced_auxiliary_basic_cleaning_can_be_disabled() -> None: config = { "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, - "load_sources": ["entsoe_api"], + "load_sources": ["entsoe"], "gap_filling": { "mode": "advanced", "basic": {"rules": []}, @@ -65,7 +65,7 @@ def test_advanced_mode_allows_no_rules_or_overrides() -> None: config = { "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, - "load_sources": ["entsoe_api"], + "load_sources": ["entsoe"], "gap_filling": { "mode": "advanced", "basic": {"rules": []}, diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py index 69466c8..7b562e1 100644 --- a/tests/unit/test_plan_auxiliary_data.py +++ b/tests/unit/test_plan_auxiliary_data.py @@ -87,7 +87,7 @@ def test_plan_is_empty_outside_advanced_mode() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["example"]), gap_filling_config=_config({}, mode="basic"), - source_names=["entsoe_api", "neso", "opsd_api"], + source_names=["entsoe", "neso", "opsd"], ) assert result == _empty_execution_plan() @@ -97,7 +97,7 @@ def test_advanced_plan_with_empty_fill_plan_is_empty() -> None: result = build_advanced_execution_plan( fill_plan=pd.DataFrame(), gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result == _empty_execution_plan() @@ -109,17 +109,17 @@ def test_plan_builds_complete_execution_manifest() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), gap_filling_config=_config({"fill_albania": override}), - source_names=["entsoe_api", "opsd_api"], + source_names=["entsoe", "opsd"], ) start = pd.Timestamp("2020-01-01", tz="UTC") end = pd.Timestamp("2020-02-01", tz="UTC") group_id = _build_group_id(start=start, end=end) entsoe_batch_id = _build_batch_id( - source="entsoe_api", start=start, end=end, countries=["GRC"] + source="entsoe", start=start, end=end, countries=["GRC"] ) opsd_batch_id = _build_batch_id( - source="opsd_api", start=start, end=end, countries=["GRC"] + source="opsd", start=start, end=end, countries=["GRC"] ) assert result == { @@ -132,7 +132,7 @@ def test_plan_builds_complete_execution_manifest() -> None: { "group_id": group_id, "batch_id": entsoe_batch_id, - "source": "entsoe_api", + "source": "entsoe", "start": "2020-01-01T00:00:00+00:00", "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], @@ -141,7 +141,7 @@ def test_plan_builds_complete_execution_manifest() -> None: { "group_id": group_id, "batch_id": opsd_batch_id, - "source": "opsd_api", + "source": "opsd", "start": "2020-01-01T00:00:00+00:00", "end": "2020-02-01T00:00:00+00:00", "countries": ["GRC"], @@ -149,8 +149,8 @@ def test_plan_builds_complete_execution_manifest() -> None: }, ], "batch_ids_by_source": { - "entsoe_api": [entsoe_batch_id], - "opsd_api": [opsd_batch_id], + "entsoe": [entsoe_batch_id], + "opsd": [opsd_batch_id], }, "groups": {group_id: [entsoe_batch_id, opsd_batch_id]}, "constructed_profile_rule_names": ["fill_albania"], @@ -167,7 +167,7 @@ def test_plan_uses_only_overrides_in_fill_plan() -> None: "inactive": _construct_override(source_country="SRB"), } ), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["active_rule_names"] == ["active"] @@ -186,7 +186,7 @@ def test_plan_preserves_configured_override_order() -> None: "second": _construct_override(source_country="SRB"), } ), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["active_rule_names"] == ["first", "second"] @@ -206,7 +206,7 @@ def test_plan_resolves_scaling_target_sources_to_groups() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), gap_filling_config=_config({"fill_albania": override}), - source_names=["entsoe_api"], + source_names=["entsoe"], ) expected_group_ids = sorted( @@ -244,7 +244,7 @@ def test_plan_resolves_rule_to_expanded_basic_cleaning_group() -> None: basic_cleaning_enabled=True, basic_rules=basic_rules, ), - source_names=["entsoe_api"], + source_names=["entsoe"], ) batch = result["batches"][0] @@ -266,7 +266,7 @@ def test_non_construct_rule_requires_no_auxiliary_groups() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["leave_albania"]), gap_filling_config=_config({"leave_albania": override}), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["rules"] == { @@ -310,7 +310,7 @@ def test_plan_is_json_serializable() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["fill_albania"]), gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=["entsoe_api"], + source_names=["entsoe"], ) json.dumps(result) @@ -324,7 +324,7 @@ def test_plan_records_active_external_profile_file() -> None: result = build_advanced_execution_plan( fill_plan=_fill_plan(["external_albania"]), gap_filling_config=_config({"external_albania": override}), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["external_profile_files"] == { @@ -346,7 +346,7 @@ def test_plan_excludes_inactive_external_profile_file() -> None: ), } ), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["external_profile_files"] == {"active": "resources/user/active.csv"} @@ -367,7 +367,7 @@ def test_plan_allows_external_profile_file_reuse() -> None: ), } ), - source_names=["entsoe_api"], + source_names=["entsoe"], ) assert result["external_profile_files"] == { diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py index b452198..81ea4ee 100644 --- a/tests/unit/test_validate_config.py +++ b/tests/unit/test_validate_config.py @@ -8,7 +8,7 @@ def _config() -> dict: """Return a structurally valid configuration for semantic validation.""" return { "temporal_scope": {"start": "2022-01-01", "end": "2023-01-01"}, - "load_sources": ["entsoe_api", "neso"], + "load_sources": ["entsoe", "neso"], "gap_filling": {"basic": {"rules": []}, "advanced": {"overrides": {}}}, } @@ -49,7 +49,7 @@ def test_rejects_basic_rule_name_reserved_for_observed_source() -> None: config = _config() config["gap_filling"]["basic"]["rules"] = [ { - "name": "observed_entsoe_api", + "name": "observed_entsoe", "method": "linear_interpolation", "max_gap": "3h", } diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 9a570e3..2bf36e1 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -280,14 +280,14 @@ $defs: - source_offsets unevaluatedProperties: false - copy_period_rule: + copy_periods_rule: allOf: - $ref: "#/$defs/gap_filling_rule_base" - type: object properties: method: - const: copy_period + const: copy_periods max_gap: $ref: "#/$defs/duration" @@ -312,7 +312,7 @@ $defs: oneOf: - $ref: "#/$defs/linear_interpolation_rule" - $ref: "#/$defs/average_periods_rule" - - $ref: "#/$defs/copy_period_rule" + - $ref: "#/$defs/copy_periods_rule" properties: diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 8f6ab77..818753f 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -118,6 +118,9 @@ checkpoint plan_auxiliary_data: params: gap_filling=config["gap_filling"], source_names=config["load_sources"], + start=config["temporal_scope"]["start"], + end=config["temporal_scope"]["end"], + frequency=config["temporal_scope"]["frequency"], message: "Plan auxiliary electricity-demand acquisition." script: diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py index 3517c21..756e926 100644 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ b/workflow/scripts/cleaning/advanced/planning/execution.py @@ -5,19 +5,18 @@ import pandas as pd from common.time import as_utc_timestamp +from tclean import TimeGrid +from tclean.advanced import ( + build_auxiliary_acquisition_requirements, + build_auxiliary_source_requests, +) from cleaning.advanced.methods.construct_from_sources import ( METHOD_NAME as CONSTRUCT_FROM_SOURCES, ) from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE from cleaning.advanced.planning.manifest import EXECUTION_PLAN_VERSION -from cleaning.advanced.planning.requirements import ( - build_auxiliary_acquisition_requirements, -) -from cleaning.advanced.planning.source_requests import ( - build_auxiliary_source_batches, - build_auxiliary_source_requests, -) +from cleaning.advanced.planning.source_requests import build_auxiliary_source_batches def build_advanced_execution_plan( @@ -25,6 +24,7 @@ def build_advanced_execution_plan( fill_plan: pd.DataFrame, gap_filling_config: Mapping[str, Any], source_names: Sequence[str], + grid: TimeGrid, ) -> dict[str, object]: """Compile all domain-aware information needed by the advanced DAG.""" if gap_filling_config["mode"] != "advanced" or fill_plan.empty: @@ -87,6 +87,45 @@ def build_advanced_execution_plan( } +def _build_source_periods( + overrides: Mapping[str, Mapping[str, Any]], +) -> list[pd.DataFrame]: + """Convert Modelblocks auxiliary source definitions to T-Clean tables.""" + source_periods: list[pd.DataFrame] = [] + + for override in overrides.values(): + if override["method"] != CONSTRUCT_FROM_SOURCES: + continue + + periods = list(override["sources"]) + + scaling = override.get("scaling") + + if scaling is not None: + periods.extend( + scaling.get("target_sources", []) + ) + + if not periods: + continue + + source_periods.append( + pd.DataFrame( + [ + { + "context": period["country"], + "start": period["start"], + "end": period["end"], + "weight": period["weight"], + } + for period in periods + ] + ) + ) + + return source_periods + + def _empty_execution_plan() -> dict[str, object]: """Return an empty plan with the complete manifest schema.""" return { diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index b8bc8ca..db57397 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -5,6 +5,7 @@ import pandas as pd from cleaning.advanced.planning.execution import build_advanced_execution_plan from cleaning.advanced.planning.manifest import write_execution_plan +from tclean import TimeGrid if TYPE_CHECKING: snakemake: Any @@ -13,10 +14,20 @@ if __name__ == "__main__": fill_plan = pd.read_parquet(snakemake.input.fill_plan) + grid = TimeGrid( + start=snakemake.params.start, + end=snakemake.params.end, + frequency=snakemake.params.frequency, + ) + plan = build_advanced_execution_plan( fill_plan=fill_plan, gap_filling_config=snakemake.params.gap_filling, source_names=snakemake.params.source_names, + grid=grid, ) - write_execution_plan(plan, snakemake.output.plan) + write_execution_plan( + plan, + snakemake.output.plan, + ) From a981a157ca03254b7245572798a0d02879def761 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 12:51:14 +0200 Subject: [PATCH 110/241] refactor demand cleaning to use tclean --- tests/integration/test_config.yaml | 71 +-- workflow/internal/config.schema.yaml | 439 +++++++++++-------- workflow/rules/advanced.smk | 15 +- workflow/rules/clean.smk | 16 +- workflow/scripts/_tclean_config.py | 257 +++++++++++ workflow/scripts/clean_demand.py | 251 +++++++---- workflow/scripts/common/config_validation.py | 81 ---- workflow/scripts/validate_config.py | 326 +++++++++++++- 8 files changed, 1045 insertions(+), 411 deletions(-) create mode 100644 workflow/scripts/_tclean_config.py delete mode 100644 workflow/scripts/common/config_validation.py diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml index cfe7478..b3828b5 100644 --- a/tests/integration/test_config.yaml +++ b/tests/integration/test_config.yaml @@ -10,18 +10,14 @@ integration_test: - opsd gap_filling: - # Sets the data-cleaning mode. Options: off, basic, advanced. mode: advanced basic: - # Basic gap-filling rules are applied in the listed order. rules: - # Interpolates across tiny gaps where no more than 3 hours are missing. - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h - # Takes the average of the same hour one week earlier and later. - name: average_adjacent_weeks method: average_periods max_gap: 326h @@ -29,80 +25,91 @@ integration_test: - -7d - 7d - # Copies from the previous week for gaps of up to one week. - name: copy_previous_week method: copy_periods max_gap: 168h source_offset: -168h + require_complete_source: true - # Copies from the following week for gaps of up to one week. - name: copy_following_week method: copy_periods max_gap: 168h source_offset: 168h + require_complete_source: true advanced: auxiliary_data: basic_cleaning: enabled: true - # Explicit country-period instructions for unresolved cases. - overrides: - - build_alb_from_gbr_alb_winter: - country: ALB - start: "2022-01-01" - end: "2022-02-01" - scope: fill_gaps + sources: + alb_from_gbr_alb_winter: method: construct_from_sources - sources: + periods: - country: GBR start: "2024-01-01" end: "2024-02-01" weight: 1 scaling: method: match_energy - target_sources: + periods: - country: ALB start: "2024-01-01" end: "2024-02-01" weight: 1 - build_alb_from_alb_2024: - country: ALB - start: "2017-01-01" - end: "2017-01-03" - scope: overwrite + alb_from_alb_2024: method: construct_from_sources - sources: + periods: - country: ALB start: "2024-01-01" end: "2024-01-03" weight: 1 scaling: method: match_energy - target_sources: + periods: - country: ALB start: "2024-01-01" end: "2024-01-03" weight: 1 - example_inactive_mne_override: - country: MNE - start: "2020-03-01" - end: "2020-04-01" - scope: fill_gaps + mne_from_srb: method: construct_from_sources - sources: + periods: - country: SRB start: "2022-03-01" end: "2022-04-01" weight: 1 - test_external_profile_overwrite: + alb_external_test: + method: external_profile + file: inputs/external_profiles/alb_external_test.csv + + rules: + - name: build_alb_from_gbr_alb_winter + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + source: alb_from_gbr_alb_winter + + - name: build_alb_from_alb_2024 + country: ALB + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024 + + - name: example_inactive_mne_override + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps + source: mne_from_srb + + - name: test_external_profile_overwrite country: ALB start: "2022-01-01 00:00" end: "2022-01-08 00:00" scope: overwrite - method: external_profile - path: inputs/external_profiles/alb_external_test.csv + source: alb_external_test \ No newline at end of file diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml index 2bf36e1..eaf9cf5 100644 --- a/workflow/internal/config.schema.yaml +++ b/workflow/internal/config.schema.yaml @@ -7,12 +7,10 @@ description: > type: object $defs: - iso_date: - type: string - format: date - pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$' - description: > - Date in ISO 8601 format (YYYY-MM-DD). + + # --------------------------------------------------------------------------- + # General temporal values + # --------------------------------------------------------------------------- duration: type: string @@ -37,167 +35,305 @@ $defs: Two or more unique, non-zero signed durations identifying the source periods to average. - hourly_timestamp: + timestamp: type: string - pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])([ T]([01]\d|2[0-3]):00)?$' + pattern: '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])([ T]([01]\d|2[0-3]):[0-5]\d)?$' description: > - Hourly timestamp interpreted as UTC. May be written as YYYY-MM-DD - for midnight or as YYYY-MM-DD HH:00. + Timestamp interpreted as UTC. May be written as YYYY-MM-DD for + midnight or as YYYY-MM-DD HH:MM. Temporal alignment with the + configured time grid is validated semantically. - auxiliary_source: + country_code: + type: string + pattern: '^[A-Z]{3}$' + description: > + ISO alpha-3 country code. + + + # --------------------------------------------------------------------------- + # Basic gap-filling rules + # --------------------------------------------------------------------------- + + gap_filling_rule_base: type: object properties: - country: + name: + type: string + pattern: '^[a-z0-9_]+$' + description: > + Unique identifier for this configured gap-filling rule. Used for + provenance reporting and cleaning-method ranking. + + method: type: string - pattern: '^[A-Z]{3}$' description: > - ISO alpha-3 country code supplying the source profile. + Gap-filling method applied by this rule. + + required: + - name + - method + + linear_interpolation_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + + - type: object + properties: + method: + const: linear_interpolation + + max_gap: + $ref: "#/$defs/duration" + + required: + - max_gap + + unevaluatedProperties: false + + average_periods_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + + - type: object + properties: + method: + const: average_periods + + max_gap: + $ref: "#/$defs/duration" + + source_offsets: + $ref: "#/$defs/signed_durations" + + required: + - max_gap + - source_offsets + + unevaluatedProperties: false + + copy_periods_rule: + allOf: + - $ref: "#/$defs/gap_filling_rule_base" + + - type: object + properties: + method: + const: copy_periods + + max_gap: + $ref: "#/$defs/duration" + + source_offset: + $ref: "#/$defs/signed_duration" + + require_complete_source: + type: boolean + description: > + Require all source-period values needed for a gap to be + available before filling that gap. + + required: + - max_gap + - source_offset + + unevaluatedProperties: false + + gap_filling_rule: + oneOf: + - $ref: "#/$defs/linear_interpolation_rule" + - $ref: "#/$defs/average_periods_rule" + - $ref: "#/$defs/copy_periods_rule" + + + # --------------------------------------------------------------------------- + # Advanced source definitions + # + # These describe HOW an advanced replacement profile is obtained. + # They do not describe where or when that profile is applied. + # --------------------------------------------------------------------------- + + source_period: + type: object + properties: + country: + $ref: "#/$defs/country_code" start: - $ref: "#/$defs/hourly_timestamp" + $ref: "#/$defs/timestamp" end: - $ref: "#/$defs/hourly_timestamp" + $ref: "#/$defs/timestamp" weight: type: number exclusiveMinimum: 0 - default: 1 description: > - Relative weight assigned to this source when source profiles - are combined using a weighted mean. + Relative weight assigned to this source period when multiple + profiles are combined using a weighted mean. required: - country - start - end + - weight additionalProperties: false - auxiliary_sources: + source_periods: type: array minItems: 1 items: - $ref: "#/$defs/auxiliary_source" + $ref: "#/$defs/source_period" description: > - One or more country-period profiles combined using a weighted mean. + One or more country-period profiles used to construct an advanced + source. - auxiliary_scaling: + constructed_source_scaling: type: object properties: method: const: match_energy - target_sources: - $ref: "#/$defs/auxiliary_sources" + periods: + $ref: "#/$defs/source_periods" description: > Reference periods whose weighted-mean energy determines the - energy total of the constructed target profile. + energy total of the constructed profile. required: - method - - target_sources + - periods additionalProperties: false - auxiliary_fill_rule_base: + construct_from_sources_definition: type: object properties: - country: - type: string - pattern: '^[A-Z]{3}$' + method: + const: construct_from_sources + + periods: + $ref: "#/$defs/source_periods" description: > - ISO alpha-3 code of the country whose demand values are targeted. + Country-period profiles used to construct the source profile. - start: - $ref: "#/$defs/hourly_timestamp" + scaling: + $ref: "#/$defs/constructed_source_scaling" - end: - $ref: "#/$defs/hourly_timestamp" + required: + - method + - periods - scope: - type: string - enum: - - fill_gaps - - overwrite - description: > - Whether the instruction fills only missing values within the - target period (fill_gaps) or replaces all values in that period - (overwrite). + additionalProperties: false + external_profile_definition: + type: object + properties: method: + const: external_profile + + file: type: string + minLength: 1 description: > - Method used to obtain or handle the target demand values. + Path to a local CSV containing timestamp and value columns for + the external profile. required: - - country - - start - - end - - scope - method + - file + additionalProperties: false - construct_from_sources_rule: - allOf: - - $ref: "#/$defs/auxiliary_fill_rule_base" + advanced_source_definition: + oneOf: + - $ref: "#/$defs/construct_from_sources_definition" + - $ref: "#/$defs/external_profile_definition" - - type: object - properties: - method: - const: construct_from_sources - sources: - $ref: "#/$defs/auxiliary_sources" + # --------------------------------------------------------------------------- + # Advanced application rules + # + # These describe WHERE and WHEN an advanced source is applied. + # --------------------------------------------------------------------------- + + advanced_rule_base: + type: object + properties: + name: + type: string + pattern: '^[a-z0-9_]+$' + description: > + Unique identifier for this advanced cleaning rule. - scaling: - $ref: "#/$defs/auxiliary_scaling" + country: + $ref: "#/$defs/country_code" - required: - - sources + start: + $ref: "#/$defs/timestamp" - unevaluatedProperties: false + end: + $ref: "#/$defs/timestamp" + scope: + type: string + enum: + - fill_gaps + - overwrite + description: > + Whether the rule fills only missing values within the target + period or replaces all values in that period. + + required: + - name + - country + - start + - end + - scope - external_profile_rule: + sourced_advanced_rule: allOf: - - $ref: "#/$defs/auxiliary_fill_rule_base" + - $ref: "#/$defs/advanced_rule_base" - type: object properties: - method: - const: external_profile - - path: + source: type: string - minLength: 1 + pattern: '^[a-z0-9_]+$' description: > - Path to a local CSV containing timestamp and value - columns for the external series. + Name of the advanced source definition used by this rule. required: - - path + - source unevaluatedProperties: false - leave_missing_rule: allOf: - - $ref: "#/$defs/auxiliary_fill_rule_base" + - $ref: "#/$defs/advanced_rule_base" - type: object properties: method: const: leave_missing + description: > + Explicitly leave matching missing values unresolved. + + required: + - method unevaluatedProperties: false - auxiliary_fill_rule: + advanced_rule: oneOf: - - $ref: "#/$defs/construct_from_sources_rule" - - $ref: "#/$defs/external_profile_rule" + - $ref: "#/$defs/sourced_advanced_rule" - $ref: "#/$defs/leave_missing_rule" + + # --------------------------------------------------------------------------- + # Auxiliary-data handling + # --------------------------------------------------------------------------- + auxiliary_basic_cleaning: type: object properties: @@ -205,15 +341,15 @@ $defs: type: boolean default: true description: > - Apply the configured basic gap-filling rules to auxiliary - demand data before it is used for advanced profile construction. + Apply the configured basic gap-filling rules to acquired + auxiliary data before it is used to construct an advanced + profile. required: - enabled additionalProperties: false - auxiliary_data: type: object properties: @@ -225,109 +361,26 @@ $defs: additionalProperties: false - gap_filling_rule_base: - type: object - properties: - name: - type: string - pattern: '^[a-z0-9_]+$' - description: > - Unique identifier for this configured gap-filling rule. Used for - provenance reporting and cleaning-method ranking. - - method: - type: string - description: > - Gap-filling method applied by this rule. - - required: - - name - - method - - linear_interpolation_rule: - allOf: - - $ref: "#/$defs/gap_filling_rule_base" - - - type: object - properties: - method: - const: linear_interpolation - - max_gap: - $ref: "#/$defs/duration" - - required: - - max_gap - unevaluatedProperties: false - - average_periods_rule: - allOf: - - $ref: "#/$defs/gap_filling_rule_base" - - - type: object - properties: - method: - const: average_periods - - max_gap: - $ref: "#/$defs/duration" - - source_offsets: - $ref: "#/$defs/signed_durations" - - required: - - max_gap - - source_offsets - unevaluatedProperties: false - - copy_periods_rule: - allOf: - - $ref: "#/$defs/gap_filling_rule_base" - - - type: object - properties: - method: - const: copy_periods - - max_gap: - $ref: "#/$defs/duration" - - source_offset: - $ref: "#/$defs/signed_duration" - - require_complete_source: - type: boolean - default: true - description: > - Require all source-period values needed for a gap to be - available before filling that gap. - - required: - - max_gap - - source_offset - - unevaluatedProperties: false - - gap_filling_rule: - oneOf: - - $ref: "#/$defs/linear_interpolation_rule" - - $ref: "#/$defs/average_periods_rule" - - $ref: "#/$defs/copy_periods_rule" +# ============================================================================= +# Top-level module configuration +# ============================================================================= properties: + temporal_scope: type: object description: > - End-exclusive temporal scope for the electricity-demand time series. + End-exclusive temporal scope and frequency of the electricity-demand + time series. The start timestamp anchors the phase of the time grid. properties: start: - $ref: "#/$defs/iso_date" + $ref: "#/$defs/timestamp" end: - $ref: "#/$defs/iso_date" - + $ref: "#/$defs/timestamp" + frequency: $ref: "#/$defs/duration" @@ -338,6 +391,7 @@ properties: additionalProperties: false + load_sources: type: array description: > @@ -354,32 +408,33 @@ properties: - neso - opsd + gap_filling: type: object description: > - Gap-filling configuration. Basic rules are applied in order using - data within the requested temporal scope. Advanced mode additionally - supports auxiliary-data planning and filling. + Gap-filling configuration. Basic rules are applied in listed order. + Advanced mode additionally permits named constructed or external + sources to be applied using explicit country-period rules. properties: + mode: type: string enum: - off - basic - advanced - default: basic description: > Gap-filling mode. 'off' disables gap filling, 'basic' applies configured deterministic rules, and 'advanced' additionally - produces and applies auxiliary-fill instructions. + enables advanced source planning and application. + basic: type: object properties: rules: type: array - default: [] items: $ref: "#/$defs/gap_filling_rule" description: > @@ -390,26 +445,38 @@ properties: additionalProperties: false + advanced: type: object properties: + auxiliary_data: $ref: "#/$defs/auxiliary_data" - overrides: + sources: type: object - default: {} description: > - Named country-period instructions that override general - advanced gap-handling decisions. + Named definitions of profiles that can be used by advanced + cleaning rules. + propertyNames: pattern: '^[a-z0-9_]+$' + additionalProperties: - $ref: "#/$defs/auxiliary_fill_rule" + $ref: "#/$defs/advanced_source_definition" + + rules: + type: array + description: > + Ordered advanced country-period cleaning rules. + + items: + $ref: "#/$defs/advanced_rule" required: - auxiliary_data - - overrides + - sources + - rules additionalProperties: false diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 818753f..f33354b 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -110,17 +110,18 @@ def auxiliary_entsoe_threads(wildcards): checkpoint plan_auxiliary_data: input: - fill_plan=rules.clean_demand.output.auxiliary_fill_plan, + gap_report=rules.clean_demand.output.gap_report, output: - plan=("/automatic/" "auxiliary/advanced_execution_plan.json"), - conda: - "../envs/module.yaml" + plan=( + "/automatic/" + "auxiliary/advanced_execution_plan.json" + ), params: + temporal_scope=config["temporal_scope"], gap_filling=config["gap_filling"], source_names=config["load_sources"], - start=config["temporal_scope"]["start"], - end=config["temporal_scope"]["end"], - frequency=config["temporal_scope"]["frequency"], + conda: + "../envs/module.yaml" message: "Plan auxiliary electricity-demand acquisition." script: diff --git a/workflow/rules/clean.smk b/workflow/rules/clean.smk index 41ad13b..c515fe3 100644 --- a/workflow/rules/clean.smk +++ b/workflow/rules/clean.smk @@ -94,22 +94,18 @@ rule clean_demand: input: configured_load_inputs, output: - demand=("/automatic/" "load_basic_cleaned.parquet"), - data_source=("/automatic/" "load_data_source.parquet"), - cleaning_method=("/automatic/" "load_cleaning_method.parquet"), - cleaning_method_rank=( - "/automatic/" "load_cleaning_method_rank.parquet" - ), - gap_report=("/automatic/" "load_gap_report.parquet"), - auxiliary_fill_plan=( - "/automatic/" "load_auxiliary_fill_plan.parquet" - ), + demand=("/automatic/load_basic_cleaned.parquet"), + data_source=("/automatic/load_data_source.parquet"), + cleaning_method=("/automatic/load_cleaning_method.parquet"), + cleaning_method_rank=("/automatic/load_cleaning_method_rank.parquet"), + gap_report=("/automatic/load_gap_report.parquet"), log: "/clean_demand.log", conda: "../envs/module.yaml" params: source_names=config["load_sources"], + temporal_scope=config["temporal_scope"], gap_filling=config["gap_filling"], message: "Combine and clean electricity-demand sources." diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py new file mode 100644 index 0000000..da54196 --- /dev/null +++ b/workflow/scripts/_tclean_config.py @@ -0,0 +1,257 @@ +"""Translate Modelblocks electricity-demand configuration to T-Clean inputs.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd +from tclean import TCleanConfig, TimeGrid + + +def build_time_grid( + temporal_scope: Mapping[str, Any], +) -> TimeGrid: + """Build the canonical T-Clean time grid.""" + return TimeGrid( + start=temporal_scope["start"], + end=temporal_scope["end"], + frequency=temporal_scope["frequency"], + ) + + +def build_tclean_config( + temporal_scope: Mapping[str, Any], +) -> TCleanConfig: + """Build the T-Clean configuration used for demand cleaning.""" + return TCleanConfig( + grid=build_time_grid(temporal_scope) + ) + + +def build_basic_rules( + gap_filling_config: Mapping[str, Any], +) -> list[dict[str, Any]]: + """Return configured basic-cleaning rules for T-Clean.""" + if gap_filling_config["mode"] == "off": + return [] + + return [ + dict(rule) + for rule in gap_filling_config["basic"]["rules"] + ] + + +def build_advanced_rules( + gap_filling_config: Mapping[str, Any], +) -> pd.DataFrame: + """Build the canonical T-Clean advanced-rule table.""" + columns = [ + "rule_name", + "method", + "source", + "context", + "start", + "end", + "scope", + ] + + if gap_filling_config["mode"] != "advanced": + return pd.DataFrame(columns=columns) + + advanced_config = gap_filling_config["advanced"] + source_definitions = advanced_config["sources"] + + rows: list[dict[str, object]] = [] + + for rule in advanced_config["rules"]: + source_name = rule.get("source") + + if source_name is None: + method = "leave_missing" + else: + if source_name not in source_definitions: + raise ValueError( + "Advanced rule " + f"{rule['name']!r} references unknown source " + f"{source_name!r}." + ) + + method = source_definitions[source_name]["method"] + + rows.append( + { + "rule_name": rule["name"], + "method": method, + "source": source_name, + "context": rule["country"], + "start": rule["start"], + "end": rule["end"], + "scope": rule["scope"], + } + ) + + return pd.DataFrame( + rows, + columns=columns, + ) + + +def build_constructed_source_periods( + source_definition: Mapping[str, Any], +) -> pd.DataFrame: + """Build T-Clean source periods for one constructed source.""" + if source_definition["method"] != "construct_from_sources": + raise ValueError( + "Source definition is not a construct_from_sources source." + ) + + return _build_source_periods( + source_definition["periods"] + ) + + +def build_scaling_source_periods( + source_definition: Mapping[str, Any], +) -> pd.DataFrame | None: + """Build optional T-Clean scaling periods for a constructed source.""" + scaling = source_definition.get("scaling") + + if scaling is None: + return None + + if scaling["method"] != "match_energy": + raise ValueError( + "Unsupported scaling method: " + f"{scaling['method']!r}." + ) + + return _build_source_periods( + scaling["periods"] + ) + + +def build_all_constructed_source_periods( + gap_filling_config: Mapping[str, Any], + *, + source_names: Sequence[str] | None = None, +) -> dict[str, pd.DataFrame]: + """Build source-period tables for configured constructed sources.""" + if gap_filling_config["mode"] != "advanced": + return {} + + source_definitions = ( + gap_filling_config["advanced"]["sources"] + ) + + if source_names is None: + selected_names = list(source_definitions) + else: + selected_names = list(source_names) + + result: dict[str, pd.DataFrame] = {} + + for source_name in selected_names: + if source_name not in source_definitions: + raise ValueError( + f"Unknown advanced source {source_name!r}." + ) + + definition = source_definitions[source_name] + + if definition["method"] != "construct_from_sources": + continue + + frames = [ + build_constructed_source_periods(definition) + ] + + scaling_periods = ( + build_scaling_source_periods(definition) + ) + + if scaling_periods is not None: + frames.append(scaling_periods) + + result[source_name] = pd.concat( + frames, + ignore_index=True, + ) + + return result + + +def build_source_capabilities( + source_names: Sequence[str], +) -> pd.DataFrame: + """Describe which contexts configured providers can supply.""" + if len(source_names) != len(set(source_names)): + raise ValueError( + "Configured load source names must be unique." + ) + + capabilities: list[dict[str, object]] = [] + + for source_name in source_names: + if source_name == "neso": + capabilities.append( + { + "source": "neso", + "context": "GBR", + } + ) + + elif source_name in {"entsoe", "opsd"}: + capabilities.append( + { + "source": source_name, + "context": None, + } + ) + + else: + raise ValueError( + "Unsupported electricity-demand source: " + f"{source_name!r}." + ) + + return pd.DataFrame( + capabilities, + columns=[ + "source", + "context", + ], + ) + + +def get_advanced_source_definitions( + gap_filling_config: Mapping[str, Any], +) -> Mapping[str, Any]: + """Return configured advanced source definitions.""" + if gap_filling_config["mode"] != "advanced": + return {} + + return gap_filling_config["advanced"]["sources"] + + +def _build_source_periods( + periods: Sequence[Mapping[str, Any]], +) -> pd.DataFrame: + """Convert Modelblocks country periods to generic T-Clean periods.""" + return pd.DataFrame( + [ + { + "context": period["country"], + "start": period["start"], + "end": period["end"], + "weight": period["weight"], + } + for period in periods + ], + columns=[ + "context", + "start", + "end", + "weight", + ], + ) diff --git a/workflow/scripts/clean_demand.py b/workflow/scripts/clean_demand.py index 4184693..974d911 100644 --- a/workflow/scripts/clean_demand.py +++ b/workflow/scripts/clean_demand.py @@ -1,153 +1,230 @@ -"""Combine and clean prepared electricity-demand sources.""" +"""Combine and basic-clean prepared electricity-demand sources.""" import logging -import sys -from collections.abc import Mapping, Sequence from pathlib import Path from typing import TYPE_CHECKING, Any import pandas as pd -from cleaning.pipeline import clean_demand +from _tclean_config import build_basic_rules, build_tclean_config +from tclean import clean +from tclean.advanced import build_gap_report +from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank if TYPE_CHECKING: snakemake: Any + logger = logging.getLogger(__name__) -def main( - *, - input_paths: Sequence[str | Path], - source_names: Sequence[str], - gap_filling_config: Mapping[str, Any], - output: Any, -) -> None: - """Read prepared sources, clean demand, and write provenance.""" +def main(snakemake: Any) -> None: + """Run the main electricity-demand cleaning stage.""" + source_names = list( + snakemake.params.source_names + ) + + input_paths = list( + snakemake.input + ) + if len(input_paths) != len(source_names): raise ValueError( - "The number of input files must equal the number " - "of configured load sources." + "The number of prepared load inputs must match the " + "number of configured load sources." ) sources = { source_name: _read_prepared_source(path) - for source_name, path in zip(source_names, input_paths, strict=True) + for source_name, path in zip( + source_names, + input_paths, + strict=True, + ) } + config = build_tclean_config( + snakemake.params.temporal_scope + ) + + basic_rules = build_basic_rules( + snakemake.params.gap_filling + ) + ( cleaned, data_source, cleaning_method, - cleaning_method_rank, - gap_report, - auxiliary_fill_plan, - ) = clean_demand( - sources, source_priority=source_names, gap_filling_config=gap_filling_config + ) = clean( + sources, + config=config, + basic_rules=basic_rules, ) - cleaned.to_parquet(output.demand) + basic_rule_names = [ + rule["name"] + for rule in basic_rules + ] - data_source.to_parquet(output.data_source) - cleaning_method.to_parquet(output.cleaning_method) + cleaning_method_ranks = build_cleaning_method_ranks( + source_names, + basic_rule_names=basic_rule_names, + ) - cleaning_method_rank.to_parquet(output.cleaning_method_rank) + cleaning_method_rank = derive_cleaning_method_rank( + cleaning_method=cleaning_method, + ranks=cleaning_method_ranks, + ) - gap_report.to_parquet(output.gap_report, index=False) + gap_report = build_gap_report( + cleaned, + grid=config.grid, + enabled=( + snakemake.params.gap_filling["mode"] + == "advanced" + ), + ) - auxiliary_fill_plan.to_parquet(output.auxiliary_fill_plan, index=False) + cleaned.to_parquet( + snakemake.output.demand + ) - _log_source_counts(data_source) - _log_cleaning_method_counts(cleaning_method, cleaning_method_rank) - _log_gap_report(gap_report) + data_source.to_parquet( + snakemake.output.data_source + ) + cleaning_method.to_parquet( + snakemake.output.cleaning_method + ) -def _read_prepared_source(path: str | Path) -> pd.DataFrame: - """Read and validate one prepared demand source.""" - demand = pd.read_parquet(path) + cleaning_method_rank.to_parquet( + snakemake.output.cleaning_method_rank + ) - if not isinstance(demand.index, pd.DatetimeIndex): - demand.index = pd.to_datetime(demand.index, utc=True) + gap_report.to_parquet( + snakemake.output.gap_report, + index=False, + ) - elif demand.index.tz is None: - demand.index = demand.index.tz_localize("UTC") + _log_source_counts( + data_source + ) - else: - demand.index = demand.index.tz_convert("UTC") + _log_cleaning_method_counts( + cleaning_method + ) - if demand.index.has_duplicates: - raise ValueError(f"Demand source contains duplicate timestamps: {path}") + _log_gap_report( + gap_report + ) - if demand.columns.has_duplicates: - raise ValueError(f"Demand source contains duplicate columns: {path}") - return demand.sort_index() +def _read_prepared_source( + path: str | Path, +) -> pd.DataFrame: + """Read one prepared electricity-demand source.""" + data = pd.read_parquet(path) + if not isinstance( + data.index, + pd.DatetimeIndex, + ): + data.index = pd.to_datetime( + data.index, + utc=True, + ) -def _log_source_counts(data_source: pd.DataFrame) -> None: - """Log the number of cells supplied by each observed source.""" - counts = data_source.stack().value_counts() + elif data.index.tz is None: + data.index = data.index.tz_localize( + "UTC" + ) - for source_name, count in counts.items(): - logger.info("%s supplied %s observed values.", source_name, int(count)) + else: + data.index = data.index.tz_convert( + "UTC" + ) + data.index.name = "timestamp" -def _log_cleaning_method_counts( - cleaning_method: pd.DataFrame, cleaning_method_rank: pd.DataFrame + return data + + +def _log_source_counts( + data_source: pd.DataFrame, ) -> None: - """Log the number of cells assigned to each cleaning method.""" - method_counts = cleaning_method.stack().value_counts() + """Log observed-value counts by source.""" + counts = ( + data_source + .stack() + .value_counts() + ) + + if counts.empty: + logger.info( + "No observed source values were recorded." + ) + return - for method_name, count in method_counts.items(): - method_mask = cleaning_method.eq(method_name).fillna(False).to_numpy(dtype=bool) + for source_name, count in counts.items(): + logger.info( + "%s supplied %s values.", + source_name, + int(count), + ) - ranks = cleaning_method_rank.to_numpy()[method_mask] - unique_ranks = pd.unique(ranks) +def _log_cleaning_method_counts( + cleaning_method: pd.DataFrame, +) -> None: + """Log value counts by cleaning method.""" + counts = ( + cleaning_method + .stack() + .value_counts() + ) - if len(unique_ranks) != 1: - raise ValueError( - "Cleaning method " - f"{method_name!r} has multiple ranks: " - f"{unique_ranks.tolist()}" - ) + if counts.empty: + logger.info( + "No cleaning methods were recorded." + ) + return + for method, count in counts.items(): logger.info( - "Cleaning method '%s' at rank %s supplied %s values.", - method_name, - int(unique_ranks[0]), + "%s supplied %s values.", + method, int(count), ) -def _log_gap_report(gap_report: pd.DataFrame) -> None: - """Log unresolved-gap counts by country.""" +def _log_gap_report( + gap_report: pd.DataFrame, +) -> None: + """Log unresolved-gap counts by context.""" if gap_report.empty: - logger.info("No advanced unresolved-gap report was generated.") + logger.info( + "No unresolved gaps remain after basic cleaning." + ) return - logger.info("Gap report contains %s contiguous unresolved gaps.", len(gap_report)) - - country_summary = gap_report.groupby("country").agg( - gap_count=("country", "size"), missing_hours=("gap_hours", "sum") + logger.info( + "Gap report contains %s contiguous unresolved gaps.", + len(gap_report), ) - for country, row in country_summary.iterrows(): + for context, context_gaps in gap_report.groupby( + "context" + ): + total_duration = ( + context_gaps["gap_duration"].sum() + ) + logger.info( - "%s: %s unresolved gaps covering %s hours.", - country, - int(row["gap_count"]), - int(row["missing_hours"]), + "%s has %s unresolved gaps covering %s.", + context, + len(context_gaps), + total_duration, ) if __name__ == "__main__": - sys.stderr = open(snakemake.log[0], "w", buffering=1) - - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - - main( - input_paths=list(snakemake.input), - source_names=list(snakemake.params.source_names), - gap_filling_config=(snakemake.params.gap_filling), - output=snakemake.output, - ) + main(snakemake) diff --git a/workflow/scripts/common/config_validation.py b/workflow/scripts/common/config_validation.py deleted file mode 100644 index 766fd19..0000000 --- a/workflow/scripts/common/config_validation.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Validate semantic relationships in module configuration.""" - -from collections.abc import Mapping -from typing import Any - -import pandas as pd - - -def validate_config_semantics(config: Mapping[str, Any]) -> None: - """Validate configuration constraints not covered by the JSON schema.""" - _validate_temporal_scope(config["temporal_scope"]) - advanced = config["gap_filling"]["advanced"] - _validate_advanced_overrides(advanced["overrides"]) - _validate_cleaning_method_names(config) - - -def _validate_temporal_scope(scope: Mapping[str, Any]) -> None: - _validate_period(scope["start"], scope["end"], context="Temporal scope") - - -def _validate_advanced_overrides(overrides: Mapping[str, Mapping[str, Any]]) -> None: - for rule_name, rule in overrides.items(): - _validate_period( - rule["start"], rule["end"], context=f"Advanced-fill rule {rule_name!r}" - ) - - if rule["method"] != "construct_from_sources": - continue - - for position, source in enumerate(rule["sources"]): - _validate_period( - source["start"], - source["end"], - context=f"Source {position} in advanced-fill rule {rule_name!r}", - ) - - scaling = rule.get("scaling") - if scaling is None: - continue - - for position, source in enumerate(scaling["target_sources"]): - _validate_period( - source["start"], - source["end"], - context=( - f"Scaling target source {position} " - f"in advanced-fill rule {rule_name!r}" - ), - ) - - -def _validate_period(start: object, end: object, *, context: str) -> None: - start_timestamp = pd.Timestamp(start) - end_timestamp = pd.Timestamp(end) - - if end_timestamp <= start_timestamp: - raise ValueError( - f"{context} must have an end timestamp after its start timestamp." - ) - - -def _validate_cleaning_method_names(config: Mapping[str, Any]) -> None: - """Reject configured rule names reserved for cleaning provenance.""" - source_names = config["load_sources"] - basic_rules = config["gap_filling"]["basic"]["rules"] - advanced_overrides = config["gap_filling"]["advanced"]["overrides"] - - reserved_names = { - "missing", - *(f"observed_{source_name}" for source_name in source_names), - } - - rule_names = [*(rule["name"] for rule in basic_rules), *advanced_overrides.keys()] - - collisions = sorted(set(rule_names) & reserved_names) - - if collisions: - raise ValueError( - "Gap-filling rule names conflict with reserved " - f"cleaning-method names: {collisions}" - ) diff --git a/workflow/scripts/validate_config.py b/workflow/scripts/validate_config.py index 771f0e8..516fa72 100644 --- a/workflow/scripts/validate_config.py +++ b/workflow/scripts/validate_config.py @@ -1,14 +1,324 @@ -"""Validate semantic constraints in the module configuration.""" - +"""Validate semantic constraints of the module configuration.""" import json +from collections import Counter +from collections.abc import Mapping, Sequence from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pandas as pd +from _tclean_config import ( + build_advanced_rules, + build_basic_rules, + build_constructed_source_periods, + build_scaling_source_periods, + build_time_grid, +) +from tclean import TimeGrid +from tclean.basic import validate_basic_rules -from common.config_validation import validate_config_semantics +if TYPE_CHECKING: + snakemake: Any -validation_config = snakemake.params.validation_config -validate_config_semantics(validation_config) +def validate_config_semantics( + config: Mapping[str, Any], +) -> None: + """Validate module configuration semantics.""" + gap_filling = config["gap_filling"] -Path(snakemake.output[0]).write_text( - json.dumps({"valid": True}, indent=2) + "\n", encoding="utf-8" -) + grid = build_time_grid( + config["temporal_scope"] + ) + + _validate_basic_config( + gap_filling, + grid=grid, + ) + + _validate_advanced_config( + gap_filling, + grid=grid, + ) + + +def _validate_basic_config( + gap_filling: Mapping[str, Any], + *, + grid: TimeGrid, +) -> None: + """Validate the configured basic-cleaning rules.""" + mode = gap_filling["mode"] + + if mode == "off": + return + + rules = build_basic_rules(gap_filling) + + if not rules: + raise ValueError( + f"Gap-filling mode is {mode!r}, but no basic " + "cleaning rules are configured." + ) + + validate_basic_rules( + rules, + grid=grid, + ) + + +def _validate_advanced_config( + gap_filling: Mapping[str, Any], + *, + grid: TimeGrid, +) -> None: + """Validate advanced source definitions and application rules.""" + if gap_filling["mode"] != "advanced": + return + + advanced = gap_filling["advanced"] + + source_definitions = advanced["sources"] + rules = advanced["rules"] + + _validate_unique_rule_names(rules) + + _validate_advanced_rule_sources( + rules, + source_definitions=source_definitions, + ) + + _validate_advanced_rule_periods( + rules, + grid=grid, + ) + + _validate_advanced_source_definitions( + source_definitions, + grid=grid, + ) + + # Ensure that the Modelblocks configuration can be represented by the + # canonical T-Clean advanced-rule contract. + build_advanced_rules(gap_filling) + + +def _validate_unique_rule_names( + rules: Sequence[Mapping[str, Any]], +) -> None: + """Require advanced rule names to be unique.""" + counts = Counter( + rule["name"] + for rule in rules + ) + + duplicates = sorted( + name + for name, count in counts.items() + if count > 1 + ) + + if duplicates: + raise ValueError( + "Advanced cleaning rule names must be unique. " + f"Duplicate names: {duplicates}." + ) + + +def _validate_advanced_rule_sources( + rules: Sequence[Mapping[str, Any]], + *, + source_definitions: Mapping[str, Mapping[str, Any]], +) -> None: + """Validate references from advanced rules to advanced sources.""" + for rule in rules: + rule_name = rule["name"] + + if rule.get("method") == "leave_missing": + if "source" in rule: + raise ValueError( + f"Advanced rule {rule_name!r} uses " + "'leave_missing' and must not define a source." + ) + + continue + + source_name = rule.get("source") + + if source_name is None: + raise ValueError( + f"Advanced rule {rule_name!r} must reference " + "an advanced source." + ) + + if source_name not in source_definitions: + raise ValueError( + f"Advanced rule {rule_name!r} references unknown " + f"advanced source {source_name!r}." + ) + + +def _validate_advanced_rule_periods( + rules: Sequence[Mapping[str, Any]], + *, + grid: TimeGrid, +) -> None: + """Validate advanced target periods against the configured grid.""" + for rule in rules: + try: + grid.validate_period( + start=pd.Timestamp(rule["start"]), + end=pd.Timestamp(rule["end"]), + ) + + except (TypeError, ValueError) as error: + raise ValueError( + f"Invalid target period for advanced rule " + f"{rule['name']!r}: {error}" + ) from error + + +def _validate_advanced_source_definitions( + source_definitions: Mapping[str, Mapping[str, Any]], + *, + grid: TimeGrid, +) -> None: + """Validate every configured advanced source.""" + for source_name, definition in source_definitions.items(): + method = definition["method"] + + if method == "construct_from_sources": + _validate_constructed_source( + source_name, + definition, + grid=grid, + ) + + elif method == "external_profile": + continue + + else: + raise ValueError( + f"Advanced source {source_name!r} uses " + f"unsupported method {method!r}." + ) + + +def _validate_constructed_source( + source_name: str, + definition: Mapping[str, Any], + *, + grid: TimeGrid, +) -> None: + """Validate one constructed advanced source.""" + source_periods = build_constructed_source_periods( + definition + ) + + _validate_source_periods( + source_name, + source_periods, + period_kind="construction", + grid=grid, + ) + + _validate_equal_period_lengths( + source_name, + source_periods, + period_kind="construction", + grid=grid, + ) + + scaling_periods = build_scaling_source_periods( + definition + ) + + if scaling_periods is None: + return + + _validate_source_periods( + source_name, + scaling_periods, + period_kind="scaling", + grid=grid, + ) + + +def _validate_source_periods( + source_name: str, + periods: pd.DataFrame, + *, + period_kind: str, + grid: TimeGrid, +) -> None: + """Validate advanced source periods against the configured grid.""" + for period in periods.itertuples(index=False): + try: + grid.validate_period( + start=period.start, + end=period.end, + ) + + except (TypeError, ValueError) as error: + raise ValueError( + f"Invalid {period_kind} period in advanced source " + f"{source_name!r} for country {period.context!r}: " + f"{error}" + ) from error + + +def _validate_equal_period_lengths( + source_name: str, + periods: pd.DataFrame, + *, + period_kind: str, + grid: TimeGrid, +) -> None: + """Require construction periods to contain equal numbers of values.""" + lengths = { + len( + grid.index_for_period( + start=period.start, + end=period.end, + ) + ) + for period in periods.itertuples(index=False) + } + + if len(lengths) > 1: + raise ValueError( + f"All {period_kind} periods in advanced source " + f"{source_name!r} must contain the same number of " + "configured time steps." + ) + + +def write_validation_marker( + output_path: str | Path, +) -> None: + """Write a marker file when semantic validation succeeds.""" + output_path = Path(output_path) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with output_path.open( + "w", + encoding="utf-8", + ) as file: + json.dump( + {"valid": True}, + file, + indent=2, + ) + + +if __name__ == "__main__": + validate_config_semantics( + snakemake.params.validation_config + ) + + write_validation_marker( + snakemake.output[0] + ) From 5d634a4b2b05e8ae733df27105dc5616ab72d955 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 13:15:04 +0200 Subject: [PATCH 111/241] refactor advanced planning to use tclean --- workflow/rules/advanced.smk | 2 +- workflow/scripts/_advanced_execution.py | 272 ++++++++++++++++++ workflow/scripts/_tclean_config.py | 20 +- workflow/scripts/plan_auxiliary_data.py | 368 ++++++++++++++++++++++-- 4 files changed, 641 insertions(+), 21 deletions(-) create mode 100644 workflow/scripts/_advanced_execution.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index f33354b..cff9279 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -110,7 +110,7 @@ def auxiliary_entsoe_threads(wildcards): checkpoint plan_auxiliary_data: input: - gap_report=rules.clean_demand.output.gap_report, + demand=rules.clean_demand.output.demand, output: plan=( "/automatic/" diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py new file mode 100644 index 0000000..e09247e --- /dev/null +++ b/workflow/scripts/_advanced_execution.py @@ -0,0 +1,272 @@ +"""Build Snakemake execution metadata for advanced demand cleaning.""" + +from __future__ import annotations + +import hashlib +from collections.abc import Mapping, Sequence +from typing import Any + +import pandas as pd + +EXECUTION_PLAN_VERSION = 1 + + +def build_source_batches( + requests: pd.DataFrame, +) -> list[dict[str, object]]: + """Group T-Clean source requests into executable provider batches.""" + if requests.empty: + return [] + + required_columns = { + "source", + "context", + "start", + "end", + } + + missing_columns = ( + required_columns - set(requests.columns) + ) + + if missing_columns: + raise ValueError( + "Auxiliary source requests are missing required " + f"columns: {sorted(missing_columns)}." + ) + + batches: list[dict[str, object]] = [] + + grouped = requests.groupby( + [ + "source", + "start", + "end", + ], + sort=False, + ) + + for ( + source, + start, + end, + ), group in grouped: + start = pd.Timestamp(start) + end = pd.Timestamp(end) + + if end <= start: + raise ValueError( + "Auxiliary batch end must be later than " + "its start." + ) + + countries = sorted( + group["context"] + .drop_duplicates() + .tolist() + ) + + group_id = build_group_id( + start=start, + end=end, + ) + + batch_id = build_batch_id( + source=str(source), + start=start, + end=end, + countries=countries, + ) + + batches.append( + { + "group_id": group_id, + "batch_id": batch_id, + "source": str(source), + "start": start, + "end": end, + "countries": countries, + } + ) + + return batches + + +def serialize_batch( + batch: Mapping[str, object], +) -> dict[str, object]: + """Convert one auxiliary batch to JSON-compatible values.""" + start = pd.Timestamp(batch["start"]) + end = pd.Timestamp(batch["end"]) + + if end <= start: + raise ValueError( + "Auxiliary batch end must be later than " + "its start." + ) + + final_included_time = ( + end - pd.Timedelta(nanoseconds=1) + ) + + return { + **batch, + "start": start.isoformat(), + "end": end.isoformat(), + "years": list( + range( + start.year, + final_included_time.year + 1, + ) + ), + } + + +def build_group_id( + *, + start: pd.Timestamp, + end: pd.Timestamp, +) -> str: + """Build a deterministic identifier for one auxiliary period.""" + return ( + f"{start.strftime('%Y%m%dT%H%M')}__" + f"{end.strftime('%Y%m%dT%H%M')}" + ) + + +def build_batch_id( + *, + source: str, + start: pd.Timestamp, + end: pd.Timestamp, + countries: Sequence[str], +) -> str: + """Build a deterministic identifier for one provider batch.""" + countries_key = ",".join( + sorted(countries) + ) + + countries_hash = hashlib.sha1( + countries_key.encode("utf-8") + ).hexdigest()[:8] + + return ( + f"{source}__" + f"{start.strftime('%Y%m%dT%H%M')}__" + f"{end.strftime('%Y%m%dT%H%M')}__" + f"{countries_hash}" + ) + + +def index_batch_ids_by_source( + batches: Sequence[Mapping[str, object]], +) -> dict[str, list[str]]: + """Index planned batch identifiers by provider source.""" + result: dict[str, list[str]] = {} + + for batch in batches: + source = str(batch["source"]) + batch_id = str(batch["batch_id"]) + + result.setdefault( + source, + [], + ).append(batch_id) + + return result + + +def index_batch_ids_by_group( + batches: Sequence[Mapping[str, object]], +) -> dict[str, list[str]]: + """Index planned batch identifiers by auxiliary period group.""" + result: dict[str, list[str]] = {} + + for batch in batches: + group_id = str(batch["group_id"]) + batch_id = str(batch["batch_id"]) + + result.setdefault( + group_id, + [], + ).append(batch_id) + + return result + + +def resolve_required_group_ids( + batches: Sequence[Mapping[str, object]], + *, + source_periods: pd.DataFrame, +) -> list[str]: + """Resolve source periods to acquired auxiliary period groups.""" + if source_periods.empty: + return [] + + required_columns = { + "context", + "start", + "end", + } + + missing_columns = ( + required_columns - set(source_periods.columns) + ) + + if missing_columns: + raise ValueError( + "Advanced source periods are missing required " + f"columns: {sorted(missing_columns)}." + ) + + group_ids: list[str] = [] + + for period in source_periods.itertuples( + index=False + ): + start = pd.Timestamp(period.start) + end = pd.Timestamp(period.end) + + matching_group_ids = { + str(batch["group_id"]) + for batch in batches + if ( + period.context + in batch["countries"] + and pd.Timestamp(batch["start"]) + <= start + and pd.Timestamp(batch["end"]) + >= end + ) + } + + if len(matching_group_ids) != 1: + raise ValueError( + "Expected exactly one auxiliary group " + f"covering context {period.context!r} " + f"from {start} to {end}, found " + f"{sorted(matching_group_ids)}." + ) + + group_id = next( + iter(matching_group_ids) + ) + + if group_id not in group_ids: + group_ids.append(group_id) + + return group_ids + + +def empty_execution_plan() -> dict[str, object]: + """Return an empty advanced execution manifest.""" + return { + "version": EXECUTION_PLAN_VERSION, + "active_rule_names": [], + "rules": {}, + "batches": [], + "batch_ids_by_source": {}, + "groups": {}, + "constructed_profile_rule_names": [], + "external_profile_files": {}, + } diff --git a/workflow/scripts/_tclean_config.py b/workflow/scripts/_tclean_config.py index da54196..1667fec 100644 --- a/workflow/scripts/_tclean_config.py +++ b/workflow/scripts/_tclean_config.py @@ -85,8 +85,14 @@ def build_advanced_rules( "method": method, "source": source_name, "context": rule["country"], - "start": rule["start"], - "end": rule["end"], + "start": pd.to_datetime( + rule["start"], + utc=True, + ), + "end": pd.to_datetime( + rule["end"], + utc=True, + ), "scope": rule["scope"], } ) @@ -242,8 +248,14 @@ def _build_source_periods( [ { "context": period["country"], - "start": period["start"], - "end": period["end"], + "start": pd.to_datetime( + period["start"], + utc=True, + ), + "end": pd.to_datetime( + period["end"], + utc=True, + ), "weight": period["weight"], } for period in periods diff --git a/workflow/scripts/plan_auxiliary_data.py b/workflow/scripts/plan_auxiliary_data.py index db57397..ba5eeb7 100644 --- a/workflow/scripts/plan_auxiliary_data.py +++ b/workflow/scripts/plan_auxiliary_data.py @@ -1,33 +1,369 @@ -"""Write the compiled advanced electricity-demand execution plan.""" +"""Plan advanced electricity-demand execution.""" +import json +from collections.abc import Mapping, Sequence +from pathlib import Path from typing import TYPE_CHECKING, Any import pandas as pd -from cleaning.advanced.planning.execution import build_advanced_execution_plan -from cleaning.advanced.planning.manifest import write_execution_plan -from tclean import TimeGrid +from _advanced_execution import ( + EXECUTION_PLAN_VERSION, + build_source_batches, + empty_execution_plan, + index_batch_ids_by_group, + index_batch_ids_by_source, + resolve_required_group_ids, + serialize_batch, +) +from _tclean_config import ( + build_advanced_rules, + build_all_constructed_source_periods, + build_basic_rules, + build_source_capabilities, + build_time_grid, + get_advanced_source_definitions, +) +from tclean.advanced import ( + build_auxiliary_acquisition_requirements, + build_auxiliary_source_requests, + select_active_advanced_rules, +) if TYPE_CHECKING: snakemake: Any -if __name__ == "__main__": - fill_plan = pd.read_parquet(snakemake.input.fill_plan) +def build_advanced_execution_plan( + *, + target_contexts: Sequence[str], + temporal_scope: Mapping[str, Any], + gap_filling_config: Mapping[str, Any], + source_names: Sequence[str], +) -> dict[str, object]: + """Build the advanced Snakemake execution manifest.""" + if gap_filling_config["mode"] != "advanced": + return empty_execution_plan() - grid = TimeGrid( - start=snakemake.params.start, - end=snakemake.params.end, - frequency=snakemake.params.frequency, + grid = build_time_grid( + temporal_scope ) - plan = build_advanced_execution_plan( - fill_plan=fill_plan, - gap_filling_config=snakemake.params.gap_filling, - source_names=snakemake.params.source_names, + advanced_rules = build_advanced_rules( + gap_filling_config + ) + + active_rules = select_active_advanced_rules( + advanced_rules, + target_contexts=target_contexts, + grid=grid, + ) + + if active_rules.empty: + return empty_execution_plan() + + source_definitions = ( + get_advanced_source_definitions( + gap_filling_config + ) + ) + + active_source_names = _get_active_source_names( + active_rules + ) + + constructed_source_periods = ( + build_all_constructed_source_periods( + gap_filling_config, + source_names=active_source_names, + ) + ) + + requirements = ( + build_auxiliary_acquisition_requirements( + list( + constructed_source_periods.values() + ), + basic_rules=build_basic_rules( + gap_filling_config + ), + grid=grid, + basic_cleaning_enabled=( + gap_filling_config[ + "advanced" + ][ + "auxiliary_data" + ][ + "basic_cleaning" + ][ + "enabled" + ] + ), + ) + ) + + source_capabilities = ( + build_source_capabilities( + source_names + ) + ) + + requests = build_auxiliary_source_requests( + requirements, + source_capabilities=source_capabilities, grid=grid, ) + raw_batches = build_source_batches( + requests + ) + + batches = [ + serialize_batch(batch) + for batch in raw_batches + ] + + rules = _build_rule_manifest( + active_rules, + source_definitions=source_definitions, + constructed_source_periods=( + constructed_source_periods + ), + batches=raw_batches, + ) + + constructed_profile_rule_names = [ + rule_name + for rule_name, rule in rules.items() + if rule["method"] + == "construct_from_sources" + ] + + external_profile_files = ( + _build_external_profile_files( + active_rules, + source_definitions=source_definitions, + ) + ) + + return { + "version": EXECUTION_PLAN_VERSION, + "active_rule_names": ( + active_rules["rule_name"].tolist() + ), + "rules": rules, + "batches": batches, + "batch_ids_by_source": ( + index_batch_ids_by_source( + batches + ) + ), + "groups": ( + index_batch_ids_by_group( + batches + ) + ), + "constructed_profile_rule_names": ( + constructed_profile_rule_names + ), + "external_profile_files": ( + external_profile_files + ), + } + + +def _get_active_source_names( + active_rules: pd.DataFrame, +) -> list[str]: + """Return referenced advanced sources in rule order.""" + source_names: list[str] = [] + + for source_name in active_rules["source"]: + if pd.isna(source_name): + continue + + source_name = str(source_name) + + if source_name not in source_names: + source_names.append(source_name) + + return source_names + + +def _build_rule_manifest( + active_rules: pd.DataFrame, + *, + source_definitions: Mapping[ + str, + Mapping[str, Any], + ], + constructed_source_periods: Mapping[ + str, + pd.DataFrame, + ], + batches: Sequence[Mapping[str, object]], +) -> dict[str, dict[str, object]]: + """Build manifest metadata for active advanced rules.""" + rules: dict[str, dict[str, object]] = {} + + for rule in active_rules.itertuples( + index=False + ): + source_name = ( + None + if pd.isna(rule.source) + else str(rule.source) + ) + + required_group_ids: list[str] = [] + + if ( + source_name is not None + and rule.method + == "construct_from_sources" + ): + if ( + source_name + not in constructed_source_periods + ): + raise ValueError( + "No constructed-source periods were " + f"planned for advanced source " + f"{source_name!r}." + ) + + required_group_ids = ( + resolve_required_group_ids( + batches, + source_periods=( + constructed_source_periods[ + source_name + ] + ), + ) + ) + + rules[str(rule.rule_name)] = { + "method": str(rule.method), + "source": source_name, + "context": str(rule.context), + "start": pd.Timestamp( + rule.start + ).isoformat(), + "end": pd.Timestamp( + rule.end + ).isoformat(), + "scope": str(rule.scope), + "required_group_ids": ( + required_group_ids + ), + } + + return rules + + +def _build_external_profile_files( + active_rules: pd.DataFrame, + *, + source_definitions: Mapping[ + str, + Mapping[str, Any], + ], +) -> dict[str, str]: + """Map active external-profile rules to their files.""" + result: dict[str, str] = {} + + for rule in active_rules.itertuples( + index=False + ): + if rule.method != "external_profile": + continue + + if pd.isna(rule.source): + raise ValueError( + "External-profile rule " + f"{rule.rule_name!r} has no source." + ) + + source_name = str(rule.source) + + if source_name not in source_definitions: + raise ValueError( + f"Unknown advanced source " + f"{source_name!r}." + ) + + definition = ( + source_definitions[source_name] + ) + + if ( + definition["method"] + != "external_profile" + ): + raise ValueError( + f"Advanced source {source_name!r} " + "is not an external-profile source." + ) + + result[str(rule.rule_name)] = str( + definition["file"] + ) + + return result + + +def write_execution_plan( + *, + plan: Mapping[str, object], + output_path: str | Path, +) -> None: + """Write the execution manifest as JSON.""" + output_path = Path(output_path) + + output_path.parent.mkdir( + parents=True, + exist_ok=True, + ) + + with output_path.open( + "w", + encoding="utf-8", + ) as file: + json.dump( + plan, + file, + indent=2, + ) + + +def main(snakemake: Any) -> None: + """Build and write the advanced execution manifest.""" + demand = pd.read_parquet( + snakemake.input.demand + ) + + target_contexts = list( + demand.columns + ) + + plan = build_advanced_execution_plan( + target_contexts=target_contexts, + temporal_scope=( + snakemake.params.temporal_scope + ), + gap_filling_config=( + snakemake.params.gap_filling + ), + source_names=( + snakemake.params.source_names + ), + ) + write_execution_plan( - plan, - snakemake.output.plan, + plan=plan, + output_path=snakemake.output.plan, ) + + +if __name__ == "__main__": + main(snakemake) From b993152f4ac459c60d8f6e1a72075b430186bd01 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:16:55 +0200 Subject: [PATCH 112/241] refactor auxiliary group cleaning to use tclean --- workflow/rules/advanced.smk | 54 +++---- workflow/scripts/clean_auxiliary_group.py | 176 ++++++++++++++++++++++ workflow/scripts/download_load_entsoe.py | 2 +- workflow/scripts/prepare_load_entsoe.py | 2 +- workflow/scripts/prepare_load_opsd.py | 2 +- 5 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 workflow/scripts/clean_auxiliary_group.py diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index cff9279..69005be 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -242,60 +242,42 @@ rule prepare_auxiliary_load_neso: "../scripts/prepare_load_neso.py" -rule combine_auxiliary_sources: +rule clean_auxiliary_group: input: plan=auxiliary_acquisition_plan, sources=auxiliary_group_source_files, output: - demand=("/automatic/" "auxiliary/combined/" "{group_id}.parquet"), - data_source=( + demand=( "/automatic/" - "auxiliary/combined/" - "{group_id}_data_source.parquet" - ), - cleaning_method=( - "/automatic/" - "auxiliary/combined/" - "{group_id}_cleaning_method.parquet" + "auxiliary/cleaned/" + "{group_id}.parquet" ), - conda: - "../envs/module.yaml" - params: - source_priority=config["load_sources"], - message: - "Combine auxiliary electricity-demand sources." - script: - "../scripts/combine_auxiliary_sources.py" - - -rule clean_auxiliary_data: - input: - demand=("/automatic/" "auxiliary/combined/" "{group_id}.parquet"), - cleaning_method=( + data_source=( "/automatic/" - "auxiliary/combined/" - "{group_id}_cleaning_method.parquet" + "auxiliary/cleaned/" + "{group_id}_data_source.parquet" ), - output: - demand=("/automatic/" "auxiliary/cleaned/" "{group_id}.parquet"), cleaning_method=( "/automatic/" "auxiliary/cleaned/" "{group_id}_cleaning_method.parquet" ), - conda: - "../envs/module.yaml" params: + frequency=config["temporal_scope"]["frequency"], basic_rules=config["gap_filling"]["basic"]["rules"], - enabled=( - config["gap_filling"]["advanced"]["auxiliary_data"]["basic_cleaning"][ - "enabled" - ] + basic_cleaning_enabled=( + config["gap_filling"] + ["advanced"] + ["auxiliary_data"] + ["basic_cleaning"] + ["enabled"] ), + conda: + "../envs/module.yaml" message: - "Apply basic cleaning to auxiliary electricity demand." + "Combine and clean auxiliary electricity-demand sources." script: - "../scripts/clean_auxiliary_data.py" + "../scripts/clean_auxiliary_group.py" rule construct_auxiliary_profile: diff --git a/workflow/scripts/clean_auxiliary_group.py b/workflow/scripts/clean_auxiliary_group.py new file mode 100644 index 0000000..130aef4 --- /dev/null +++ b/workflow/scripts/clean_auxiliary_group.py @@ -0,0 +1,176 @@ +"""Combine and clean one auxiliary electricity-demand group.""" + +import json +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pandas as pd +from tclean import TCleanConfig, TimeGrid, clean + +if TYPE_CHECKING: + snakemake: Any + + +def main(snakemake: Any) -> None: + """Combine and basic-clean one auxiliary source group.""" + plan = _read_plan( + snakemake.input.plan + ) + + group_id = str( + snakemake.wildcards.group_id + ) + + batch_ids = plan["groups"][group_id] + + batches_by_id = { + batch["batch_id"]: batch + for batch in plan["batches"] + } + + batches = [ + batches_by_id[batch_id] + for batch_id in batch_ids + ] + + if not batches: + raise ValueError( + f"Auxiliary group {group_id!r} " + "contains no source batches." + ) + + starts = { + pd.Timestamp(batch["start"]) + for batch in batches + } + + ends = { + pd.Timestamp(batch["end"]) + for batch in batches + } + + if len(starts) != 1 or len(ends) != 1: + raise ValueError( + f"Auxiliary group {group_id!r} " + "contains inconsistent batch periods." + ) + + group_start = next(iter(starts)) + group_end = next(iter(ends)) + + grid = TimeGrid( + start=group_start, + end=group_end, + frequency=( + snakemake.params.frequency + ), + ) + + config = TCleanConfig( + grid=grid + ) + + source_paths = list( + snakemake.input.sources + ) + + if len(source_paths) != len(batches): + raise ValueError( + f"Auxiliary group {group_id!r} has " + f"{len(batches)} planned batches but " + f"{len(source_paths)} prepared inputs." + ) + + sources: dict[str, pd.DataFrame] = {} + + for batch, path in zip( + batches, + source_paths, + strict=True, + ): + source_name = str( + batch["source"] + ) + + if source_name in sources: + raise ValueError( + f"Auxiliary group {group_id!r} " + f"contains duplicate source " + f"{source_name!r}." + ) + + sources[source_name] = ( + _read_prepared_source(path) + ) + + basic_rules = ( + list(snakemake.params.basic_rules) + if snakemake.params.basic_cleaning_enabled + else [] + ) + + ( + cleaned, + data_source, + cleaning_method, + ) = clean( + sources, + config=config, + basic_rules=basic_rules, + ) + + cleaned.to_parquet( + snakemake.output.demand + ) + + data_source.to_parquet( + snakemake.output.data_source + ) + + cleaning_method.to_parquet( + snakemake.output.cleaning_method + ) + + +def _read_plan( + path: str | Path, +) -> dict[str, Any]: + """Read the advanced execution manifest.""" + with Path(path).open( + encoding="utf-8" + ) as file: + return json.load(file) + + +def _read_prepared_source( + path: str | Path, +) -> pd.DataFrame: + """Read one prepared auxiliary provider frame.""" + data = pd.read_parquet(path) + + if not isinstance( + data.index, + pd.DatetimeIndex, + ): + data.index = pd.to_datetime( + data.index, + utc=True, + ) + + elif data.index.tz is None: + data.index = ( + data.index.tz_localize("UTC") + ) + + else: + data.index = ( + data.index.tz_convert("UTC") + ) + + data.index.name = "timestamp" + + return data + + +if __name__ == "__main__": + main(snakemake) diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index bec2ab5..5765eb4 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -25,7 +25,7 @@ def main(snakemake: Any) -> None: batch = get_batch( plan, batch_id=snakemake.wildcards.batch_id, - source="entsoe_api", + source="entsoe", ) start = batch["start"] diff --git a/workflow/scripts/prepare_load_entsoe.py b/workflow/scripts/prepare_load_entsoe.py index 2e6d737..d92bd1e 100644 --- a/workflow/scripts/prepare_load_entsoe.py +++ b/workflow/scripts/prepare_load_entsoe.py @@ -24,7 +24,7 @@ def main(snakemake: Any) -> None: batch = get_batch( plan, batch_id=snakemake.wildcards.batch_id, - source="entsoe_api", + source="entsoe", ) start = batch["start"] diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 00325f8..536a288 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -25,7 +25,7 @@ def main(snakemake: Any) -> None: batch = get_batch( plan, batch_id=snakemake.wildcards.batch_id, - source="opsd_api", + source="opsd", ) start = batch["start"] From 807a03fa5bbf8b21ccd05d08e9ee65bfe3334de8 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:24:34 +0200 Subject: [PATCH 113/241] refactor auxiliary profile construction to use tclean --- workflow/rules/advanced.smk | 11 ++- .../scripts/construct_auxiliary_profile.py | 77 +++++++++++++++---- 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index 69005be..ef7ba87 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -286,7 +286,16 @@ rule construct_auxiliary_profile: sources=auxiliary_rule_cleaned_files, output: profile=( - "/automatic/" "auxiliary/constructed/" "{rule_name}.parquet" + "/automatic/" + "auxiliary/constructed/" + "{rule_name}.parquet" + ), + params: + frequency=config["temporal_scope"]["frequency"], + advanced_sources=( + config["gap_filling"] + ["advanced"] + ["sources"] ), conda: "../envs/module.yaml" diff --git a/workflow/scripts/construct_auxiliary_profile.py b/workflow/scripts/construct_auxiliary_profile.py index 4491155..bcc3fe3 100644 --- a/workflow/scripts/construct_auxiliary_profile.py +++ b/workflow/scripts/construct_auxiliary_profile.py @@ -1,28 +1,73 @@ -"""Constructs the auxiliary profile.""" +"""Construct an auxiliary profile from cleaned source data.""" + +import json import pandas as pd -from cleaning.advanced.methods.construct_from_sources import construct_from_sources -from cleaning.advanced.planning.manifest import get_rule_override, load_execution_plan -from common.time import build_hourly_index +from _tclean_config import ( + build_constructed_source_periods, + build_scaling_source_periods, +) +from tclean import TimeGrid +from tclean.advanced import construct_from_sources -loads = [pd.read_parquet(path) for path in snakemake.input.sources] +with open(snakemake.input.plan, encoding="utf-8") as file: + plan = json.load(file) -auxiliary = loads[0].copy() +rule_name = snakemake.wildcards.rule_name +rule = plan["rules"][rule_name] -for load in loads[1:]: - auxiliary = auxiliary.combine_first(load) +if rule["method"] != "construct_from_sources": + raise ValueError( + f"Rule {rule_name!r} is not a construct_from_sources rule." + ) + +source_name = rule["source"] +source_definition = snakemake.params.advanced_sources[source_name] + +sources = build_constructed_source_periods(source_definition) +scaling_sources = build_scaling_source_periods(source_definition) + +grid = TimeGrid( + start=rule["start"], + end=rule["end"], + frequency=snakemake.params.frequency, +) -plan = load_execution_plan(snakemake.input.plan) +loads = [ + pd.read_parquet(path) + for path in snakemake.input.sources +] -override = get_rule_override(plan, rule_name=snakemake.wildcards.rule_name) +if not loads: + raise ValueError( + f"No cleaned auxiliary data were supplied for rule {rule_name!r}." + ) -target_index = build_hourly_index(start=override["start"], end=override["end"]) +source_data = loads[0].copy() + +for load in loads[1:]: + source_data = source_data.combine_first(load) + +source_start = source_data.index.min() +source_end = source_data.index.max() + grid.frequency + +source_data = source_data.reindex( + grid.index_for_period( + start=source_start, + end=source_end, + ) +) profile = construct_from_sources( - auxiliary, - target_index=target_index, - sources=override["sources"], - scaling=override.get("scaling"), + source_data, + target_index=grid.target_index, + sources=sources, + scaling_sources=scaling_sources, + grid=grid, ) -profile.to_frame(name=override["country"]).to_parquet(snakemake.output.profile) +profile.to_frame( + name=rule["context"] +).to_parquet( + snakemake.output.profile +) From 4ee27494db2b37f3e962bb1809716fca81d5e785 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:30:06 +0200 Subject: [PATCH 114/241] refactor advanced override application to use tclean --- workflow/rules/advanced.smk | 3 + workflow/scripts/apply_advanced_overrides.py | 83 ++++++++++++-------- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/workflow/rules/advanced.smk b/workflow/rules/advanced.smk index ef7ba87..4112f98 100644 --- a/workflow/rules/advanced.smk +++ b/workflow/rules/advanced.smk @@ -308,6 +308,7 @@ rule construct_auxiliary_profile: rule apply_advanced_overrides: input: demand=("/automatic/" "load_basic_cleaned.parquet"), + data_source=rules.clean_demand.output.data_source, cleaning_method=("/automatic/" "load_cleaning_method.parquet"), plan=auxiliary_acquisition_plan, constructed_profiles=advanced_constructed_profiles, @@ -317,6 +318,8 @@ rule apply_advanced_overrides: cleaning_method=( "/automatic/" "load_advanced_cleaning_method.parquet" ), + params: + temporal_scope=config["temporal_scope"], conda: "../envs/module.yaml" message: diff --git a/workflow/scripts/apply_advanced_overrides.py b/workflow/scripts/apply_advanced_overrides.py index 8ffa735..957b56c 100644 --- a/workflow/scripts/apply_advanced_overrides.py +++ b/workflow/scripts/apply_advanced_overrides.py @@ -1,51 +1,72 @@ -"""Apply configured advanced electricity-demand overrides.""" +"""Apply advanced T-Clean rules to basic-cleaned demand.""" +import json from pathlib import Path import pandas as pd -from cleaning.advanced.apply import apply_auxiliary_fill_rules -from cleaning.advanced.methods.external_profile import read_external_profile -from cleaning.advanced.planning.manifest import ( - get_active_overrides, - load_execution_plan, -) - -load = pd.read_parquet(snakemake.input.demand) +from tclean import TimeGrid +from tclean.advanced import read_external_profile +from tclean.advanced.apply import apply_advanced_rules -cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) +with open(snakemake.input.plan, encoding="utf-8") as file: + plan = json.load(file) -plan = load_execution_plan(snakemake.input.plan) +grid = TimeGrid( + start=snakemake.params.temporal_scope["start"], + end=snakemake.params.temporal_scope["end"], + frequency=snakemake.params.temporal_scope["frequency"], +) -active_overrides = get_active_overrides(plan) +data = pd.read_parquet(snakemake.input.demand) +data_source = pd.read_parquet(snakemake.input.data_source) +cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) +rules = pd.DataFrame( + [ + { + "rule_name": rule_name, + "method": plan["rules"][rule_name]["method"], + "source": plan["rules"][rule_name]["source"], + "context": plan["rules"][rule_name]["context"], + "start": plan["rules"][rule_name]["start"], + "end": plan["rules"][rule_name]["end"], + "scope": plan["rules"][rule_name]["scope"], + } + for rule_name in plan["active_rule_names"] + ] +) -constructed_profiles = { - Path(path).stem: pd.read_parquet(path).iloc[:, 0] - for path in snakemake.input.constructed_profiles -} +advanced_sources = {} +for path in snakemake.input.constructed_profiles: + rule_name = Path(path).stem + rule = plan["rules"][rule_name] + source_name = rule["source"] -external_profiles_by_path = { - Path(path): read_external_profile(path) - for path in snakemake.input.external_profiles -} + profile = pd.read_parquet(path) + if profile.shape[1] != 1: + raise ValueError( + f"Constructed profile for rule {rule_name!r} " + "must contain exactly one column." + ) -external_profiles = { - rule_name: external_profiles_by_path[Path(path)] - for rule_name, path in plan["external_profile_files"].items() -} + advanced_sources[source_name] = profile.iloc[:, 0] +for source_name, path in plan["external_profile_files"].items(): + advanced_sources[source_name] = read_external_profile( + path, + grid=grid, + ) -filled, cleaning_method = apply_auxiliary_fill_rules( - load, +filled, _, cleaning_method = apply_advanced_rules( + data, + data_source, cleaning_method, - overrides=active_overrides, - constructed_profiles=constructed_profiles, - external_profiles=external_profiles, + rules=rules, + advanced_sources=advanced_sources, + grid=grid, ) - filled.to_parquet(snakemake.output.demand) - cleaning_method.to_parquet(snakemake.output.cleaning_method) From a5658a73c8b285a8b17f042710f1b6d127447c90 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:33:17 +0200 Subject: [PATCH 115/241] refactor final provenance ranking to use tclean --- workflow/scripts/finalise_clean_demand.py | 47 ++++++++++++++++------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/workflow/scripts/finalise_clean_demand.py b/workflow/scripts/finalise_clean_demand.py index 6660000..90f765c 100644 --- a/workflow/scripts/finalise_clean_demand.py +++ b/workflow/scripts/finalise_clean_demand.py @@ -1,30 +1,51 @@ -"""Generates the final cleaned parquet.""" +"""Generate the final cleaned demand and provenance outputs.""" import shutil import pandas as pd -from cleaning.provenance import ( - build_cleaning_method_ranks, - build_final_cleaning_rules, - derive_cleaning_method_rank, -) +from _tclean_config import build_advanced_rules, build_basic_rules +from tclean.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank -shutil.copyfile(snakemake.input.demand, snakemake.output.demand) +shutil.copyfile( + snakemake.input.demand, + snakemake.output.demand, +) -cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) +cleaning_method = pd.read_parquet( + snakemake.input.cleaning_method +) gap_filling = snakemake.params.gap_filling -rules = build_final_cleaning_rules(gap_filling) +basic_rules = build_basic_rules(gap_filling) +advanced_rules = build_advanced_rules(gap_filling) + +basic_rule_names = [ + rule["name"] + for rule in basic_rules +] + +advanced_rule_names = ( + advanced_rules["rule_name"].tolist() + if not advanced_rules.empty + else [] +) ranks = build_cleaning_method_ranks( - source_priority=snakemake.params.source_names, rules=rules + snakemake.params.source_names, + basic_rule_names=basic_rule_names, + advanced_rule_names=advanced_rule_names, ) cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, ranks=ranks + cleaning_method=cleaning_method, + ranks=ranks, ) -cleaning_method.to_parquet(snakemake.output.cleaning_method) +cleaning_method.to_parquet( + snakemake.output.cleaning_method +) -cleaning_method_rank.to_parquet(snakemake.output.cleaning_method_rank) +cleaning_method_rank.to_parquet( + snakemake.output.cleaning_method_rank +) From c3105482ba75bfb606ba52a9ccf5451b07b37cc1 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:45:42 +0200 Subject: [PATCH 116/241] remove legacy cleaning package after tclean cutover --- .../plot_timeline.py => _plot_timeline.py} | 0 workflow/scripts/clean_auxiliary_data.py | 19 -- workflow/scripts/cleaning/__init__.py | 0 .../scripts/cleaning/advanced/__init__.py | 0 workflow/scripts/cleaning/advanced/apply.py | 178 -------------- .../scripts/cleaning/advanced/gap_report.py | 67 ----- .../cleaning/advanced/methods/__init__.py | 0 .../methods/construct_from_sources.py | 165 ------------- .../advanced/methods/external_profile.py | 43 ---- .../cleaning/advanced/planning/__init__.py | 0 .../cleaning/advanced/planning/execution.py | 230 ------------------ .../cleaning/advanced/planning/manifest.py | 91 ------- .../cleaning/advanced/planning/plan.py | 95 -------- .../advanced/planning/requirements.py | 161 ------------ .../advanced/planning/source_requests.py | 96 -------- workflow/scripts/cleaning/basic/__init__.py | 0 workflow/scripts/cleaning/basic/apply.py | 164 ------------- .../scripts/cleaning/basic/rules/__init__.py | 0 .../cleaning/basic/rules/average_periods.py | 80 ------ .../cleaning/basic/rules/copy_period.py | 81 ------ .../basic/rules/linear_interpolation.py | 41 ---- workflow/scripts/cleaning/combine_sources.py | 87 ------- workflow/scripts/cleaning/pipeline.py | 98 -------- workflow/scripts/cleaning/provenance.py | 58 ----- workflow/scripts/cleaning/sources/__init__.py | 0 workflow/scripts/cleaning/validation.py | 49 ---- workflow/scripts/combine_auxiliary_sources.py | 22 -- 27 files changed, 1825 deletions(-) rename workflow/scripts/{cleaning/plot_timeline.py => _plot_timeline.py} (100%) delete mode 100644 workflow/scripts/clean_auxiliary_data.py delete mode 100644 workflow/scripts/cleaning/__init__.py delete mode 100644 workflow/scripts/cleaning/advanced/__init__.py delete mode 100644 workflow/scripts/cleaning/advanced/apply.py delete mode 100644 workflow/scripts/cleaning/advanced/gap_report.py delete mode 100644 workflow/scripts/cleaning/advanced/methods/__init__.py delete mode 100644 workflow/scripts/cleaning/advanced/methods/construct_from_sources.py delete mode 100644 workflow/scripts/cleaning/advanced/methods/external_profile.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/__init__.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/execution.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/manifest.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/plan.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/requirements.py delete mode 100644 workflow/scripts/cleaning/advanced/planning/source_requests.py delete mode 100644 workflow/scripts/cleaning/basic/__init__.py delete mode 100644 workflow/scripts/cleaning/basic/apply.py delete mode 100644 workflow/scripts/cleaning/basic/rules/__init__.py delete mode 100644 workflow/scripts/cleaning/basic/rules/average_periods.py delete mode 100644 workflow/scripts/cleaning/basic/rules/copy_period.py delete mode 100644 workflow/scripts/cleaning/basic/rules/linear_interpolation.py delete mode 100644 workflow/scripts/cleaning/combine_sources.py delete mode 100644 workflow/scripts/cleaning/pipeline.py delete mode 100644 workflow/scripts/cleaning/provenance.py delete mode 100644 workflow/scripts/cleaning/sources/__init__.py delete mode 100644 workflow/scripts/cleaning/validation.py delete mode 100644 workflow/scripts/combine_auxiliary_sources.py diff --git a/workflow/scripts/cleaning/plot_timeline.py b/workflow/scripts/_plot_timeline.py similarity index 100% rename from workflow/scripts/cleaning/plot_timeline.py rename to workflow/scripts/_plot_timeline.py diff --git a/workflow/scripts/clean_auxiliary_data.py b/workflow/scripts/clean_auxiliary_data.py deleted file mode 100644 index 2aaf4a7..0000000 --- a/workflow/scripts/clean_auxiliary_data.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Applies basic rules to auxiliary data.""" - -import pandas as pd -from cleaning.basic.apply import fill_basic_gaps - -load = pd.read_parquet(snakemake.input.demand) - -cleaning_method = pd.read_parquet(snakemake.input.cleaning_method) - -cleaned, cleaning_method = fill_basic_gaps( - load, - cleaning_method=cleaning_method, - rules=snakemake.params.basic_rules, - enabled=snakemake.params.enabled, -) - -cleaned.to_parquet(snakemake.output.demand) - -cleaning_method.to_parquet(snakemake.output.cleaning_method) diff --git a/workflow/scripts/cleaning/__init__.py b/workflow/scripts/cleaning/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/advanced/__init__.py b/workflow/scripts/cleaning/advanced/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/advanced/apply.py b/workflow/scripts/cleaning/advanced/apply.py deleted file mode 100644 index 5dc03c5..0000000 --- a/workflow/scripts/cleaning/advanced/apply.py +++ /dev/null @@ -1,178 +0,0 @@ -"""Apply validated advanced auxiliary-fill rules.""" - -from collections.abc import Mapping -from typing import Any - -import pandas as pd -from common.time import as_utc_timestamp - -from cleaning.advanced.methods.construct_from_sources import ( - METHOD_NAME as CONSTRUCT_FROM_SOURCES, -) -from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE - -LEAVE_MISSING = "leave_missing" - - -def apply_auxiliary_fill_rule( - load: pd.DataFrame, - cleaning_method: pd.DataFrame, - *, - rule_name: str, - rule: Mapping[str, Any], - profile: pd.Series | None = None, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply one validated advanced-fill rule.""" - method = rule["method"] - - if method == CONSTRUCT_FROM_SOURCES: - if profile is None: - raise ValueError( - f"Advanced-fill rule {rule_name!r} requires " - "a constructed auxiliary profile." - ) - - return apply_constructed_profile( - load, - cleaning_method, - profile, - country=rule["country"], - start=as_utc_timestamp(rule["start"]), - end=as_utc_timestamp(rule["end"]), - scope=rule["scope"], - rule_name=rule_name, - ) - - if method == EXTERNAL_PROFILE: - if profile is None: - raise ValueError( - f"Advanced-fill rule {rule_name!r} requires an external profile." - ) - - return apply_external_profile( - load, - cleaning_method, - profile, - country=rule["country"], - start=as_utc_timestamp(rule["start"]), - end=as_utc_timestamp(rule["end"]), - scope=rule["scope"], - rule_name=rule_name, - ) - - if method == LEAVE_MISSING: - return load.copy(), cleaning_method.copy() - - # Cautionary in case of edge cases making is this far. - raise ValueError(f"Unsupported advanced-fill method {method!r}.") - - -def apply_constructed_profile( - load: pd.DataFrame, - cleaning_method: pd.DataFrame, - profile: pd.Series, - *, - country: str, - start: pd.Timestamp, - end: pd.Timestamp, - scope: str, - rule_name: str, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply a constructed auxiliary profile to the target demand.""" - filled = load.copy() - methods = cleaning_method.copy() - - target_index = filled.index[(filled.index >= start) & (filled.index < end)] - - if not profile.index.equals(target_index): - raise ValueError( - "Constructed profile index must exactly match the target period." - ) - - if scope == "fill_gaps": - replace_mask = filled.loc[target_index, country].isna() - - elif scope == "overwrite": - replace_mask = pd.Series(True, index=target_index) - - else: - # Redundant but elifs are preferred so this stays. - raise ValueError(f"Unsupported advanced fill scope: {scope!r}") - - replacement_index = target_index[replace_mask.to_numpy()] - - filled.loc[replacement_index, country] = profile.loc[replacement_index] - - methods.loc[replacement_index, country] = rule_name - - return filled, methods - - -def apply_auxiliary_fill_rules( - load: pd.DataFrame, - cleaning_method: pd.DataFrame, - *, - overrides: Mapping[str, Mapping[str, Any]], - constructed_profiles: Mapping[str, pd.Series], - external_profiles: Mapping[str, pd.Series], -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply validated advanced-fill rules in configuration order.""" - filled = load.copy() - methods = cleaning_method.copy() - - for rule_name, rule in overrides.items(): - method = rule["method"] - - if method == CONSTRUCT_FROM_SOURCES: - profile = constructed_profiles.get(rule_name) - - elif method == EXTERNAL_PROFILE: - profile = external_profiles.get(rule_name) - - else: - profile = None - - filled, methods = apply_auxiliary_fill_rule( - filled, methods, rule_name=rule_name, rule=rule, profile=profile - ) - - return filled, methods - - -def apply_external_profile( - load: pd.DataFrame, - cleaning_method: pd.DataFrame, - profile: pd.Series, - *, - country: str, - start: pd.Timestamp, - end: pd.Timestamp, - scope: str, - rule_name: str, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply supplied external values to target demand.""" - filled = load.copy() - methods = cleaning_method.copy() - - if country not in filled.columns: - raise ValueError(f"Target country {country!r} is not present in load data.") - - candidate = profile.loc[(profile.index >= start) & (profile.index < end)] - - candidate = candidate.loc[candidate.index.intersection(filled.index)] - - if scope == "fill_gaps": - replace_index = candidate.index[filled.loc[candidate.index, country].isna()] - - elif scope == "overwrite": - replace_index = candidate.index - - else: - # Redundant but elifs are preferred so this stays. - raise ValueError(f"Unsupported advanced fill scope: {scope!r}") - - filled.loc[replace_index, country] = candidate.loc[replace_index] - - methods.loc[replace_index, country] = rule_name - - return filled, methods diff --git a/workflow/scripts/cleaning/advanced/gap_report.py b/workflow/scripts/cleaning/advanced/gap_report.py deleted file mode 100644 index 9083c3f..0000000 --- a/workflow/scripts/cleaning/advanced/gap_report.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Build reports for unresolved gaps after basic cleaning.""" - -from __future__ import annotations - -from typing import Any - -import pandas as pd - -from cleaning.validation import validate_load - - -def build_gap_report(load: pd.DataFrame, *, enabled: bool) -> pd.DataFrame: - """Describe contiguous unresolved gaps in cleaned load data. - - An empty report with the expected columns is returned when reporting - is disabled or when no unresolved gaps remain. - """ - columns = [ - "country", - "gap_start", - "gap_end", - "gap_hours", - "touches_start_boundary", - "touches_end_boundary", - ] - - if not enabled: - return pd.DataFrame(columns=columns) - - validate_load(load) - - records: list[dict[str, Any]] = [] - - first_timestamp = load.index[0] - last_timestamp = load.index[-1] - - for country in load.columns: - missing = load[country].isna() - - if not missing.any(): - continue - - group_ids = missing.ne(missing.shift(fill_value=False)).cumsum() - - for _, group in missing.groupby(group_ids): - if not bool(group.iloc[0]): - continue - - timestamps = group.index - - records.append( - { - "country": country, - "gap_start": timestamps[0], - "gap_end": timestamps[-1] + pd.Timedelta(hours=1), - "gap_hours": len(timestamps), - "touches_start_boundary": (timestamps[0] == first_timestamp), - "touches_end_boundary": (timestamps[-1] == last_timestamp), - } - ) - - report = pd.DataFrame.from_records(records, columns=columns) - - if report.empty: - return report - - return report.sort_values(["country", "gap_start"]).reset_index(drop=True) diff --git a/workflow/scripts/cleaning/advanced/methods/__init__.py b/workflow/scripts/cleaning/advanced/methods/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py b/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py deleted file mode 100644 index 3d1bac9..0000000 --- a/workflow/scripts/cleaning/advanced/methods/construct_from_sources.py +++ /dev/null @@ -1,165 +0,0 @@ -"""Construct an auxiliary demand profile from configured source periods.""" - -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd -from common.time import as_utc_timestamp - -METHOD_NAME = "construct_from_sources" - - -def _align_leap_day( - auxiliary: pd.Series, - *, - start: pd.Timestamp, - end: pd.Timestamp, - target_index: pd.DatetimeIndex, -) -> pd.Series: - """Align source values with target calendar around February 29.""" - source_values = auxiliary.loc[(auxiliary.index >= start) & (auxiliary.index < end)] - - source_has_leap_day = ( - (source_values.index.month == 2) & (source_values.index.day == 29) - ).any() - - target_has_leap_day = ((target_index.month == 2) & (target_index.day == 29)).any() - - if source_has_leap_day and not target_has_leap_day: - leap_day = (source_values.index.month == 2) & (source_values.index.day == 29) - - return source_values.loc[~leap_day] - - if target_has_leap_day and not source_has_leap_day: - feb_28 = auxiliary.loc[ - (auxiliary.index.year == start.year) - & (auxiliary.index.month == 2) - & (auxiliary.index.day == 28) - ] - - march_1 = auxiliary.loc[ - (auxiliary.index.year == start.year) - & (auxiliary.index.month == 3) - & (auxiliary.index.day == 1) - ] - - leap_values = (feb_28.to_numpy(dtype=float) + march_1.to_numpy(dtype=float)) / 2 - - insertion_point = (source_values.index.month < 3).sum() - - values = source_values.to_numpy(dtype=float) - - aligned = pd.Series( - data=[*values[:insertion_point], *leap_values, *values[insertion_point:]], - dtype=float, - ) - - return aligned - - return source_values - - -def _match_energy( - profile: pd.Series, - *, - auxiliary: pd.DataFrame, - target_sources: Sequence[Mapping[str, Any]], -) -> pd.Series: - """Scale a profile to the weighted-mean energy of reference periods.""" - weighted_energy = 0.0 - total_weight = 0.0 - - for source in target_sources: - country = source["country"] - start = as_utc_timestamp(source["start"]) - end = as_utc_timestamp(source["end"]) - weight = float(source.get("weight", 1)) - - source_values = auxiliary.loc[ - (auxiliary.index >= start) & (auxiliary.index < end), country - ] - - if source_values.empty: - raise ValueError( - "Scaling source period contains no values. " - f"Source {country!r}: {start} to {end}." - ) - - if source_values.isna().any(): - raise ValueError( - "Scaling source period contains missing values. " - f"Source {country!r}: {start} to {end}." - ) - - weighted_energy += float(source_values.sum()) * weight - total_weight += weight - - target_energy = weighted_energy / total_weight - profile_energy = float(profile.sum()) - - if profile_energy == 0: - raise ValueError( - "Cannot match energy for a constructed profile with zero total energy." - ) - - return profile * (target_energy / profile_energy) - - -def _apply_scaling( - profile: pd.Series, *, auxiliary: pd.DataFrame, scaling: Mapping[str, Any] -) -> pd.Series: - """Scale a constructed profile to configured reference energy.""" - return _match_energy( - profile, auxiliary=auxiliary, target_sources=scaling["target_sources"] - ) - - -def construct_from_sources( - auxiliary: pd.DataFrame, - *, - target_index: pd.DatetimeIndex, - sources: Sequence[Mapping[str, Any]], - scaling: Mapping[str, Any] | None = None, -) -> pd.Series: - """Construct a target demand profile from weighted auxiliary sources.""" - weighted_sources: list[pd.Series] = [] - weights: list[float] = [] - - for source in sources: - country = source["country"] - start = as_utc_timestamp(source["start"]) - end = as_utc_timestamp(source["end"]) - weight = float(source.get("weight", 1)) - - source_values = _align_leap_day( - auxiliary[country], start=start, end=end, target_index=target_index - ) - - if len(source_values) != len(target_index): - raise ValueError( - "Auxiliary source period must contain " - "the same number of values as the target " - f"period. Source {country!r} contains " - f"{len(source_values)} values; target " - f"contains {len(target_index)}." - ) - - if source_values.isna().any(): - raise ValueError( - "Auxiliary source period contains missing values. " - f"Source {country!r}: {start} to {end}." - ) - - remapped = pd.Series(source_values.to_numpy(), index=target_index, dtype=float) - - weighted_sources.append(remapped * weight) - weights.append(weight) - - weighted_sum = sum(weighted_sources[1:], weighted_sources[0].copy()) - - profile = weighted_sum / sum(weights) - - if scaling is not None: - profile = _apply_scaling(profile, auxiliary=auxiliary, scaling=scaling) - - return profile diff --git a/workflow/scripts/cleaning/advanced/methods/external_profile.py b/workflow/scripts/cleaning/advanced/methods/external_profile.py deleted file mode 100644 index 2d44e74..0000000 --- a/workflow/scripts/cleaning/advanced/methods/external_profile.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Load and validate locally supplied external demand profiles.""" - -from pathlib import Path - -import pandas as pd - -METHOD_NAME = "external_profile" - -EXPECTED_COLUMNS = {"timestamp", "demand"} - - -def read_external_profile(path: str | Path) -> pd.Series: - """Read a timestamped external demand series from CSV.""" - profile = pd.read_csv(path) - - if set(profile.columns) != EXPECTED_COLUMNS: - raise ValueError( - "External profile must contain exactly the columns " - "'timestamp' and 'demand'." - ) - - timestamps = pd.to_datetime(profile["timestamp"], utc=True, errors="raise") - - if timestamps.duplicated().any(): - raise ValueError("External profile timestamps must be unique.") - - if ( - (timestamps.dt.minute != 0).any() - or (timestamps.dt.second != 0).any() - or (timestamps.dt.microsecond != 0).any() - ): - raise ValueError("External profile timestamps must be aligned to whole hours.") - - values = pd.to_numeric(profile["demand"], errors="raise") - - if values.isna().any(): - raise ValueError("External profile demand values must not be missing.") - - result = pd.Series( - values.to_numpy(), index=pd.DatetimeIndex(timestamps), dtype=float - ) - - return result.sort_index() diff --git a/workflow/scripts/cleaning/advanced/planning/__init__.py b/workflow/scripts/cleaning/advanced/planning/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/advanced/planning/execution.py b/workflow/scripts/cleaning/advanced/planning/execution.py deleted file mode 100644 index 756e926..0000000 --- a/workflow/scripts/cleaning/advanced/planning/execution.py +++ /dev/null @@ -1,230 +0,0 @@ -"""Compile the execution manifest for advanced gap filling.""" - -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd -from common.time import as_utc_timestamp -from tclean import TimeGrid -from tclean.advanced import ( - build_auxiliary_acquisition_requirements, - build_auxiliary_source_requests, -) - -from cleaning.advanced.methods.construct_from_sources import ( - METHOD_NAME as CONSTRUCT_FROM_SOURCES, -) -from cleaning.advanced.methods.external_profile import METHOD_NAME as EXTERNAL_PROFILE -from cleaning.advanced.planning.manifest import EXECUTION_PLAN_VERSION -from cleaning.advanced.planning.source_requests import build_auxiliary_source_batches - - -def build_advanced_execution_plan( - *, - fill_plan: pd.DataFrame, - gap_filling_config: Mapping[str, Any], - source_names: Sequence[str], - grid: TimeGrid, -) -> dict[str, object]: - """Compile all domain-aware information needed by the advanced DAG.""" - if gap_filling_config["mode"] != "advanced" or fill_plan.empty: - return _empty_execution_plan() - - overrides = gap_filling_config["advanced"]["overrides"] - ordered_active_rule_names = _get_ordered_active_rule_names( - fill_plan, overrides=overrides - ) - active_overrides = { - rule_name: overrides[rule_name] for rule_name in ordered_active_rule_names - } - - requirements = build_auxiliary_acquisition_requirements( - overrides=active_overrides, - basic_rules=gap_filling_config["basic"]["rules"], - basic_cleaning_enabled=( - gap_filling_config["advanced"]["auxiliary_data"]["basic_cleaning"][ - "enabled" - ] - ), - ) - requests = build_auxiliary_source_requests(requirements, source_names=source_names) - batches = [ - _serialize_batch(batch) for batch in build_auxiliary_source_batches(requests) - ] - - rules: dict[str, dict[str, object]] = {} - constructed_profile_rule_names: list[str] = [] - external_profile_files: dict[str, str] = {} - batch_plan = {"batches": batches} - - for rule_name in ordered_active_rule_names: - override = active_overrides[rule_name] - required_group_ids: list[str] = [] - - if override["method"] == CONSTRUCT_FROM_SOURCES: - required_group_ids = _get_required_auxiliary_group_ids( - batch_plan, override=override - ) - constructed_profile_rule_names.append(rule_name) - - elif override["method"] == EXTERNAL_PROFILE: - external_profile_files[rule_name] = str(override["path"]) - - rules[rule_name] = { - "override": override, - "required_group_ids": required_group_ids, - } - - return { - "version": EXECUTION_PLAN_VERSION, - "active_rule_names": ordered_active_rule_names, - "rules": rules, - "batches": batches, - "batch_ids_by_source": _index_batch_ids_by_source(batches), - "groups": _index_batch_ids_by_group(batches), - "constructed_profile_rule_names": constructed_profile_rule_names, - "external_profile_files": external_profile_files, - } - - -def _build_source_periods( - overrides: Mapping[str, Mapping[str, Any]], -) -> list[pd.DataFrame]: - """Convert Modelblocks auxiliary source definitions to T-Clean tables.""" - source_periods: list[pd.DataFrame] = [] - - for override in overrides.values(): - if override["method"] != CONSTRUCT_FROM_SOURCES: - continue - - periods = list(override["sources"]) - - scaling = override.get("scaling") - - if scaling is not None: - periods.extend( - scaling.get("target_sources", []) - ) - - if not periods: - continue - - source_periods.append( - pd.DataFrame( - [ - { - "context": period["country"], - "start": period["start"], - "end": period["end"], - "weight": period["weight"], - } - for period in periods - ] - ) - ) - - return source_periods - - -def _empty_execution_plan() -> dict[str, object]: - """Return an empty plan with the complete manifest schema.""" - return { - "version": EXECUTION_PLAN_VERSION, - "active_rule_names": [], - "rules": {}, - "batches": [], - "batch_ids_by_source": {}, - "groups": {}, - "constructed_profile_rule_names": [], - "external_profile_files": {}, - } - - -def _get_ordered_active_rule_names( - fill_plan: pd.DataFrame, *, overrides: Mapping[str, Mapping[str, Any]] -) -> list[str]: - """Return active rule names in configured execution order.""" - active_rule_names = set(fill_plan["rule_name"]) - - return [rule_name for rule_name in overrides if rule_name in active_rule_names] - - -def _serialize_batch(batch: Mapping[str, object]) -> dict[str, object]: - """Convert one planned batch to JSON-compatible values.""" - start = pd.Timestamp(batch["start"]) - end = pd.Timestamp(batch["end"]) - - final_included_time = end - pd.Timedelta(nanoseconds=1) - - return { - **batch, - "start": start.isoformat(), - "end": end.isoformat(), - "years": list(range(start.year, final_included_time.year + 1)), - } - - -def _index_batch_ids_by_source( - batches: Sequence[Mapping[str, object]], -) -> dict[str, list[str]]: - """Index planned batch identifiers by source.""" - result: dict[str, list[str]] = {} - - for batch in batches: - result.setdefault(str(batch["source"]), []).append(str(batch["batch_id"])) - - return result - - -def _index_batch_ids_by_group( - batches: Sequence[Mapping[str, object]], -) -> dict[str, list[str]]: - """Index planned batch identifiers by period group.""" - result: dict[str, list[str]] = {} - - for batch in batches: - result.setdefault(str(batch["group_id"]), []).append(str(batch["batch_id"])) - - return result - - -def _get_required_auxiliary_sources(override: Mapping) -> list[Mapping]: - """Return all auxiliary sources consumed by an override.""" - sources = list(override["sources"]) - - scaling = override.get("scaling") - if scaling is not None: - sources.extend(scaling.get("target_sources", [])) - - return sources - - -def _get_required_auxiliary_group_ids(plan: Mapping, *, override: Mapping) -> list[str]: - """Return auxiliary groups required to execute one override.""" - group_ids: set[str] = set() - - for source in _get_required_auxiliary_sources(override): - country = source["country"] - start = as_utc_timestamp(source["start"]) - end = as_utc_timestamp(source["end"]) - - matching_groups = { - batch["group_id"] - for batch in plan["batches"] - if ( - country in batch["countries"] - and as_utc_timestamp(batch["start"]) <= start - and as_utc_timestamp(batch["end"]) >= end - ) - } - - if len(matching_groups) != 1: - raise ValueError( - "Expected exactly one auxiliary group covering " - f"{country!r} from {start} to {end}, " - f"found {sorted(matching_groups)}." - ) - - group_ids.update(matching_groups) - - return sorted(group_ids) diff --git a/workflow/scripts/cleaning/advanced/planning/manifest.py b/workflow/scripts/cleaning/advanced/planning/manifest.py deleted file mode 100644 index 69e0dc5..0000000 --- a/workflow/scripts/cleaning/advanced/planning/manifest.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Read and query compiled advanced execution manifests.""" - -from __future__ import annotations - -import json -from collections.abc import Mapping -from pathlib import Path -from typing import Any - -EXECUTION_PLAN_VERSION = 1 - - -def write_execution_plan(plan: Mapping[str, Any], path: str | Path) -> None: - """Write one compiled advanced execution plan.""" - path = Path(path) - path.parent.mkdir(parents=True, exist_ok=True) - - with path.open("w", encoding="utf-8") as file: - json.dump(plan, file, indent=2) - - -def load_execution_plan(path: str | Path) -> dict[str, Any]: - """Load one compiled advanced execution plan.""" - with open(path, encoding="utf-8") as file: - plan = json.load(file) - - if not isinstance(plan, dict): - raise TypeError("Advanced execution plan must contain a JSON object.") - - version = plan.get("version") - - if version != EXECUTION_PLAN_VERSION: - raise ValueError( - "Unsupported advanced execution plan version: " - f"{version!r}. Expected {EXECUTION_PLAN_VERSION}." - ) - - return plan - - -def get_batch( - plan: Mapping[str, Any], *, batch_id: str, source: str | None = None -) -> Mapping[str, Any]: - """Return exactly one compiled auxiliary batch.""" - matches = [ - batch - for batch in plan["batches"] - if ( - batch["batch_id"] == batch_id - and (source is None or batch["source"] == source) - ) - ] - - if len(matches) != 1: - source_text = f" for source {source!r}" if source is not None else "" - raise ValueError( - "Expected exactly one auxiliary batch " - f"{batch_id!r}{source_text}, found {len(matches)}." - ) - - return matches[0] - - -def get_active_overrides(plan: Mapping[str, Any]) -> dict[str, Mapping[str, Any]]: - """Return active overrides in compiled execution order.""" - rule_names = plan["active_rule_names"] - rules = plan["rules"] - - unknown_rule_names = [ - rule_name for rule_name in rule_names if rule_name not in rules - ] - - if unknown_rule_names: - raise ValueError( - "Advanced execution plan references unknown compiled " - f"rules: {unknown_rule_names}." - ) - - return {rule_name: rules[rule_name]["override"] for rule_name in rule_names} - - -def get_rule_override(plan: Mapping[str, Any], *, rule_name: str) -> Mapping[str, Any]: - """Return one active rule's compiled override definition.""" - try: - rule = plan["rules"][rule_name] - except KeyError as error: - raise ValueError( - f"Advanced execution plan does not contain active rule {rule_name!r}." - ) from error - - return rule["override"] diff --git a/workflow/scripts/cleaning/advanced/planning/plan.py b/workflow/scripts/cleaning/advanced/planning/plan.py deleted file mode 100644 index 477ffe4..0000000 --- a/workflow/scripts/cleaning/advanced/planning/plan.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Build advanced auxiliary-fill plans.""" - -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd - -LEAVE_MISSING = "leave_missing" - - -def _as_utc_timestamp(value: object) -> pd.Timestamp: - """Return a timestamp normalized to UTC.""" - timestamp = pd.Timestamp(value) - - if timestamp.tzinfo is None: - return timestamp.tz_localize("UTC") - - return timestamp.tz_convert("UTC") - - -def override_intersects_target_scope( - rule: Mapping[str, Any], - *, - target_countries: Sequence[str], - target_start: pd.Timestamp, - target_end: pd.Timestamp, -) -> bool: - """Return whether an advanced override intersects the model scope.""" - rule_start = _as_utc_timestamp(rule["start"]) - rule_end = _as_utc_timestamp(rule["end"]) - target_start = _as_utc_timestamp(target_start) - target_end = _as_utc_timestamp(target_end) - - country_intersects = rule["country"] in target_countries - period_intersects = rule_start < target_end and rule_end > target_start - - return country_intersects and period_intersects - - -def build_auxiliary_fill_plan( - rules: Mapping[str, Mapping[str, Any]], - *, - target_countries: Sequence[str], - target_start: pd.Timestamp, - target_end: pd.Timestamp, -) -> pd.DataFrame: - """Build the advanced-fill plan for overrides intersecting the model scope.""" - records: list[dict[str, Any]] = [] - - for rule_name, rule in rules.items(): - if not override_intersects_target_scope( - rule, - target_countries=target_countries, - target_start=target_start, - target_end=target_end, - ): - continue - - method = rule["method"] - scaling = rule.get("scaling") - - records.append( - { - "rule_name": rule_name, - "country": rule["country"], - "target_start": _as_utc_timestamp(rule["start"]), - "target_end": _as_utc_timestamp(rule["end"]), - "scope": rule["scope"], - "method": method, - "status": ("leave_missing" if method == LEAVE_MISSING else "ready"), - "source_count": len(rule.get("sources", [])), - "scaling_method": (scaling["method"] if scaling is not None else None), - } - ) - - columns = [ - "rule_name", - "country", - "target_start", - "target_end", - "scope", - "method", - "status", - "source_count", - "scaling_method", - ] - - plan = pd.DataFrame.from_records(records, columns=columns) - - if plan.empty: - return plan - - return plan.sort_values(["country", "target_start", "rule_name"]).reset_index( - drop=True - ) diff --git a/workflow/scripts/cleaning/advanced/planning/requirements.py b/workflow/scripts/cleaning/advanced/planning/requirements.py deleted file mode 100644 index 18d9171..0000000 --- a/workflow/scripts/cleaning/advanced/planning/requirements.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Compile auxiliary-data requirements for advanced gap filling.""" - -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd -from common.time import as_utc_timestamp - -from cleaning.advanced.methods.construct_from_sources import METHOD_NAME - -REQUIREMENT_COLUMNS = ["country", "start", "end"] - - -def compile_auxiliary_requirements( - overrides: Mapping[str, Mapping[str, Any]], -) -> pd.DataFrame: - """Compile country-period data required by advanced overrides.""" - requirements: list[dict[str, Any]] = [] - - for rule in overrides.values(): - if rule["method"] != METHOD_NAME: - continue - - requirements.extend(_collect_sources(rule["sources"])) - - scaling = rule.get("scaling") - - if scaling is not None: - requirements.extend(_collect_sources(scaling["target_sources"])) - - if not requirements: - return pd.DataFrame(columns=REQUIREMENT_COLUMNS) - - requirements_frame = ( - pd.DataFrame(requirements) - .drop_duplicates() - .sort_values(["country", "start", "end"]) - .reset_index(drop=True) - ) - - return _merge_requirements(requirements_frame) - - -def _collect_sources(sources: list[Mapping[str, Any]]) -> list[dict[str, Any]]: - """Extract acquisition-relevant fields from source definitions.""" - return [ - { - "country": source["country"], - "start": as_utc_timestamp(source["start"]), - "end": as_utc_timestamp(source["end"]), - } - for source in sources - ] - - -def _merge_requirements(requirements: pd.DataFrame) -> pd.DataFrame: - """Merge overlapping or adjacent country-period requirements.""" - if requirements.empty: - return requirements.copy() - - merged_rows: list[dict[str, Any]] = [] - - for country, country_requirements in requirements.groupby("country", sort=True): - ordered = country_requirements.sort_values(["start", "end"]) - - current_start = ordered.iloc[0]["start"] - current_end = ordered.iloc[0]["end"] - - for row in ordered.iloc[1:].itertuples(index=False): - if row.start <= current_end: - current_end = max(current_end, row.end) - continue - - merged_rows.append( - {"country": country, "start": current_start, "end": current_end} - ) - - current_start = row.start - current_end = row.end - - merged_rows.append( - {"country": country, "start": current_start, "end": current_end} - ) - - return pd.DataFrame(merged_rows, columns=REQUIREMENT_COLUMNS) - - -def get_basic_cleaning_context( - rules: Sequence[Mapping[str, Any]], -) -> tuple[pd.Timedelta, pd.Timedelta]: - """Return required left and right context for ordered basic rules.""" - left = pd.Timedelta(0) - right = pd.Timedelta(0) - - for rule in rules: - method = rule["method"] - max_gap = pd.Timedelta(rule["max_gap"]) - - # Context is also needed to classify gaps correctly at boundaries. - rule_left = -max_gap - rule_right = max_gap - - if method == "linear_interpolation": - offsets = [-pd.Timedelta(hours=1), pd.Timedelta(hours=1)] - - elif method == "copy_period": - offsets = [pd.Timedelta(rule["source_offset"])] - - elif method == "average_periods": - offsets = [pd.Timedelta(offset) for offset in rule["source_offsets"]] - - else: - raise ValueError(f"Unsupported basic gap-filling method: {method!r}") - - previous_left = left - previous_right = right - - for offset in offsets: - rule_left = min(rule_left, offset + previous_left) - rule_right = max(rule_right, offset + previous_right) - - left = min(previous_left, rule_left) - right = max(previous_right, rule_right) - - return -left, right - - -def expand_auxiliary_requirements( - requirements: pd.DataFrame, - *, - rules: Sequence[Mapping[str, Any]], - enabled: bool = True, -) -> pd.DataFrame: - """Expand auxiliary periods with context needed for basic cleaning.""" - if requirements.empty or not enabled or not rules: - return requirements.copy() - - left_context, right_context = get_basic_cleaning_context(rules) - - expanded = requirements.copy() - - expanded["start"] = expanded["start"] - left_context - expanded["end"] = expanded["end"] + right_context - - return _merge_requirements(expanded) - - -def build_auxiliary_acquisition_requirements( - *, - overrides: Mapping[str, Mapping[str, Any]], - basic_rules: Sequence[Mapping[str, Any]], - basic_cleaning_enabled: bool, -) -> pd.DataFrame: - """Build expanded auxiliary-data requirements for acquisition.""" - exact_requirements = compile_auxiliary_requirements(overrides) - - return expand_auxiliary_requirements( - exact_requirements, rules=basic_rules, enabled=basic_cleaning_enabled - ) diff --git a/workflow/scripts/cleaning/advanced/planning/source_requests.py b/workflow/scripts/cleaning/advanced/planning/source_requests.py deleted file mode 100644 index b6bf330..0000000 --- a/workflow/scripts/cleaning/advanced/planning/source_requests.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Plan source requests for auxiliary electricity-demand data.""" - -from __future__ import annotations - -import hashlib -from collections.abc import Sequence - -import pandas as pd - -SOURCE_REQUEST_COLUMNS = ["source", "country", "start", "end"] - - -def build_auxiliary_source_requests( - requirements: pd.DataFrame, *, source_names: Sequence[str] -) -> pd.DataFrame: - """Map auxiliary requirements onto applicable configured sources.""" - if requirements.empty: - return pd.DataFrame(columns=SOURCE_REQUEST_COLUMNS) - - requests: list[dict[str, object]] = [] - - for source_name in source_names: - for row in requirements.itertuples(index=False): - if not _source_supports_country(source_name, row.country): - continue - - requests.append( - { - "source": source_name, - "country": row.country, - "start": row.start, - "end": row.end, - } - ) - - return pd.DataFrame(requests, columns=SOURCE_REQUEST_COLUMNS) - - -def _source_supports_country(source_name: str, country: str) -> bool: - """Return whether a source is structurally applicable to a country.""" - if source_name == "neso": - return country == "GBR" - - if source_name in {"entsoe_api", "opsd_api"}: - return True - - raise ValueError(f"Unsupported auxiliary load source: {source_name!r}") - - -def build_auxiliary_source_batches(requests: pd.DataFrame) -> list[dict[str, object]]: - """Group compatible auxiliary source requests into batches.""" - if requests.empty: - return [] - - batches: list[dict[str, object]] = [] - - grouped = requests.groupby(["source", "start", "end"], sort=False) - - for (source, start, end), group in grouped: - countries = sorted(group["country"].unique().tolist()) - - batches.append( - { - "group_id": _build_group_id(start=start, end=end), - "batch_id": _build_batch_id( - source=source, start=start, end=end, countries=countries - ), - "source": source, - "start": start, - "end": end, - "countries": countries, - } - ) - - return batches - - -def _build_batch_id( - *, source: str, start: pd.Timestamp, end: pd.Timestamp, countries: list[str] -) -> str: - """Build a deterministic identifier for an auxiliary source batch.""" - countries_key = ",".join(sorted(countries)) - - countries_hash = hashlib.sha1(countries_key.encode("utf-8")).hexdigest()[:8] - - return ( - f"{source}__" - f"{start.strftime('%Y%m%dT%H%M')}__" - f"{end.strftime('%Y%m%dT%H%M')}__" - f"{countries_hash}" - ) - - -def _build_group_id(*, start: pd.Timestamp, end: pd.Timestamp) -> str: - """Build a deterministic identifier for an auxiliary period group.""" - return f"{start.strftime('%Y%m%dT%H%M')}__{end.strftime('%Y%m%dT%H%M')}" diff --git a/workflow/scripts/cleaning/basic/__init__.py b/workflow/scripts/cleaning/basic/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/basic/apply.py b/workflow/scripts/cleaning/basic/apply.py deleted file mode 100644 index ef4c8b8..0000000 --- a/workflow/scripts/cleaning/basic/apply.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Coordinate deterministic basic gap-filling rules.""" - -from __future__ import annotations - -import logging -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd - -from cleaning.basic.rules.average_periods import METHOD_NAME as AVERAGE_PERIODS -from cleaning.basic.rules.average_periods import apply_average_periods -from cleaning.basic.rules.copy_period import METHOD_NAME as COPY_PERIOD -from cleaning.basic.rules.copy_period import apply_copy_period -from cleaning.basic.rules.linear_interpolation import ( - METHOD_NAME as LINEAR_INTERPOLATION, -) -from cleaning.basic.rules.linear_interpolation import apply_linear_interpolation -from cleaning.validation import infer_regular_timestep, validate_load - -logger = logging.getLogger(__name__) - - -def fill_basic_gaps( - load: pd.DataFrame, - *, - cleaning_method: pd.DataFrame, - rules: Sequence[Mapping[str, Any]], - enabled: bool = True, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply configured gap-filling rules and record method provenance. - - Parameters - ---------- - load: - Hourly demand data indexed by timestamp, with one column per country. - cleaning_method: - Per-cell cleaning-method provenance for the observed input values. - Missing input values should contain ``pd.NA``. - rules: - Ordered basic gap-filling rules. - enabled: - Whether basic gap filling should be applied. - - Returns: - ------- - filled: - Load after applying the configured rules. If gap filling is disabled, - this is an unchanged copy of ``load``. - cleaning_method: - Per-cell provenance containing the observed-source identifier, - configured gap-filling rule name, or ``missing``. - """ - validate_load(load) - _validate_cleaning_method(load=load, cleaning_method=cleaning_method) - - filled = load.copy() - cleaning_method = cleaning_method.copy() - - if not enabled: - logger.info("Basic gap filling is disabled.") - cleaning_method = cleaning_method.fillna("missing") - return filled, cleaning_method - - original_gap_duration = calculate_missing_run_durations(load) - - for rule in rules: - method = rule["method"] - rule_name = rule["name"] - - if method == LINEAR_INTERPOLATION: - filled, newly_filled = apply_linear_interpolation( - filled, - max_gap=rule["max_gap"], - original_gap_duration=original_gap_duration, - ) - - elif method == AVERAGE_PERIODS: - filled, newly_filled = apply_average_periods( - filled, - max_gap=rule["max_gap"], - source_offsets=rule["source_offsets"], - original_gap_duration=original_gap_duration, - ) - - elif method == COPY_PERIOD: - filled, newly_filled = apply_copy_period( - filled, - max_gap=rule["max_gap"], - source_offset=rule["source_offset"], - require_complete_source=rule.get("require_complete_source", True), - original_gap_duration=original_gap_duration, - ) - - else: - # Code should never reach this, but its convenient final catch. - raise ValueError(f"Unsupported gap-filling method: {method!r}") - - cleaning_method = cleaning_method.mask(newly_filled, rule_name) - - _log_rule_results(rule_name=rule_name, method=method, newly_filled=newly_filled) - - cleaning_method = cleaning_method.fillna("missing") - - unresolved = int(filled.isna().to_numpy().sum()) - - logger.info("Gap filling completed with %s unresolved values.", unresolved) - - return filled, cleaning_method - - -def calculate_missing_run_durations(load: pd.DataFrame) -> pd.DataFrame: - """Return the original duration of each missing run. - - Observed values receive a duration of zero. - """ - timestep = infer_regular_timestep(load.index) - - durations = pd.DataFrame(pd.Timedelta(0), index=load.index, columns=load.columns) - - for column in load.columns: - missing = load[column].isna() - group_ids = missing.ne(missing.shift()).cumsum() - - run_lengths = missing.groupby(group_ids).transform("sum").where(missing, 0) - - durations[column] = run_lengths * timestep - - return durations - - -def _log_rule_results( - *, rule_name: str, method: str, newly_filled: pd.DataFrame -) -> None: - total = int(newly_filled.to_numpy().sum()) - - logger.info( - "Gap-filling rule '%s' using method '%s' filled %s values.", - rule_name, - method, - total, - ) - - for country, count in newly_filled.sum().items(): - count = int(count) - - if count: - logger.info( - "%s: %s values filled using rule '%s'.", country, count, rule_name - ) - - -def _validate_cleaning_method( - *, load: pd.DataFrame, cleaning_method: pd.DataFrame -) -> None: - if not cleaning_method.index.equals(load.index): - raise ValueError( - "Cleaning-method provenance must use the same index as the load data." - ) - - if not cleaning_method.columns.equals(load.columns): - raise ValueError( - "Cleaning-method provenance must use the same columns as the load data." - ) diff --git a/workflow/scripts/cleaning/basic/rules/__init__.py b/workflow/scripts/cleaning/basic/rules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/basic/rules/average_periods.py b/workflow/scripts/cleaning/basic/rules/average_periods.py deleted file mode 100644 index 0925a2c..0000000 --- a/workflow/scripts/cleaning/basic/rules/average_periods.py +++ /dev/null @@ -1,80 +0,0 @@ -"""A gap-filling method: average corresponding values from other periods.""" - -from __future__ import annotations - -from collections.abc import Sequence - -import pandas as pd - -METHOD_NAME = "average_periods" - - -def apply_average_periods( - load: pd.DataFrame, - *, - max_gap: str | pd.Timedelta, - source_offsets: Sequence[str | pd.Timedelta], - original_gap_duration: pd.DataFrame, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Fill missing runs using the mean of complete source periods. - - For a target timestamp ``t``, source values are taken from - ``t + source_offset`` for each configured offset. - - For example, offsets ``-7D`` and ``7D`` average the same hour - from the previous and following weeks. - - A gap is filled only when: - - - it was missing in the original data; - - its original duration does not exceed ``max_gap``; - - every configured source period is complete for the whole gap. - """ - max_gap = pd.Timedelta(max_gap) - - offsets = tuple(pd.Timedelta(offset) for offset in source_offsets) - - eligible = ( - load.isna() - & original_gap_duration.gt(pd.Timedelta(0)) - & original_gap_duration.le(max_gap) - ) - - sources = [_values_at_offset(load, source_offset=offset) for offset in offsets] - - candidate = _mean_complete_sources(sources=sources) - - eligible &= candidate.notna() - - filled = load.mask(eligible, candidate) - - newly_filled = load.isna() & filled.notna() - - return filled, newly_filled - - -def _values_at_offset( - load: pd.DataFrame, *, source_offset: pd.Timedelta -) -> pd.DataFrame: - """Align values at timestamp + offset to target timestamps.""" - source_timestamps = load.index + source_offset - - source = load.reindex(source_timestamps) - - source.index = load.index - - return source - - -def _mean_complete_sources(*, sources: Sequence[pd.DataFrame]) -> pd.DataFrame: - """Calculate the mean only where every source is available.""" - source_sum = sources[0].copy() - complete = sources[0].notna() - - for source in sources[1:]: - source_sum = source_sum + source - complete &= source.notna() - - candidate = source_sum / len(sources) - - return candidate.where(complete) diff --git a/workflow/scripts/cleaning/basic/rules/copy_period.py b/workflow/scripts/cleaning/basic/rules/copy_period.py deleted file mode 100644 index f9bb550..0000000 --- a/workflow/scripts/cleaning/basic/rules/copy_period.py +++ /dev/null @@ -1,81 +0,0 @@ -"""A gap filling method: copy period.""" - -from __future__ import annotations - -import pandas as pd - -METHOD_NAME = "copy_period" - - -def apply_copy_period( - load: pd.DataFrame, - *, - max_gap: str | pd.Timedelta, - source_offset: str | pd.Timedelta, - original_gap_duration: pd.DataFrame, - require_complete_source: bool = True, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Fill eligible missing runs from another period in the same time series. - - For a target timestamp ``t``, the source value is taken from - ``t + source_offset``. A negative offset therefore copies from an - earlier period. - - Only original missing runs no longer than ``max_gap`` are eligible. - """ - max_gap = pd.Timedelta(max_gap) - source_offset = pd.Timedelta(source_offset) - - eligible = ( - load.isna() - & original_gap_duration.gt(pd.Timedelta(0)) - & original_gap_duration.le(max_gap) - ) - - source = _values_at_offset(load, source_offset=source_offset) - - if require_complete_source: - eligible = _require_complete_source_for_each_gap( - eligible=eligible, source=source - ) - else: - eligible &= source.notna() - - filled = load.mask(eligible, source) - newly_filled = load.isna() & filled.notna() - - return filled, newly_filled - - -def _values_at_offset( - load: pd.DataFrame, *, source_offset: pd.Timedelta -) -> pd.DataFrame: - """Align values at ``timestamp + source_offset`` to each target timestamp.""" - source_timestamps = load.index + source_offset - - source = load.reindex(source_timestamps) - source.index = load.index - - return source - - -def _require_complete_source_for_each_gap( - *, eligible: pd.DataFrame, source: pd.DataFrame -) -> pd.DataFrame: - """Keep a gap eligible only when every source value for that gap exists.""" - result = pd.DataFrame(False, index=eligible.index, columns=eligible.columns) - - for column in eligible.columns: - eligible_column = eligible[column] - gap_ids = eligible_column.ne(eligible_column.shift(fill_value=False)).cumsum() - - for _, gap_mask in eligible_column.groupby(gap_ids): - gap_index = gap_mask.index[gap_mask] - - if gap_index.empty: - continue - - if source.loc[gap_index, column].notna().all(): - result.loc[gap_index, column] = True - - return result diff --git a/workflow/scripts/cleaning/basic/rules/linear_interpolation.py b/workflow/scripts/cleaning/basic/rules/linear_interpolation.py deleted file mode 100644 index e8267df..0000000 --- a/workflow/scripts/cleaning/basic/rules/linear_interpolation.py +++ /dev/null @@ -1,41 +0,0 @@ -"""A gap filling method: linear interpolation.""" - -from __future__ import annotations - -import pandas as pd - -METHOD_NAME = "linear_interpolation" - - -def apply_linear_interpolation( - load: pd.DataFrame, - *, - max_gap: str | pd.Timedelta, - original_gap_duration: pd.DataFrame, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Fill bounded missing runs using linear interpolation. - - Only complete original gaps whose duration is less than or equal to - ``max_gap`` are eligible. - - Returns: - ------- - filled: - Load dataframe after applying this rule. - newly_filled: - Boolean dataframe identifying values filled by this rule. - """ - max_gap = pd.Timedelta(max_gap) - - eligible = ( - load.isna() - & original_gap_duration.gt(pd.Timedelta(0)) - & original_gap_duration.le(max_gap) - ) - - interpolated = load.interpolate(method="time", limit_area="inside") - - filled = load.mask(eligible, interpolated) - newly_filled = load.isna() & filled.notna() - - return filled, newly_filled diff --git a/workflow/scripts/cleaning/combine_sources.py b/workflow/scripts/cleaning/combine_sources.py deleted file mode 100644 index b338545..0000000 --- a/workflow/scripts/cleaning/combine_sources.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Combine prepared demand sources in priority order.""" - -from __future__ import annotations - -from collections.abc import Mapping, Sequence - -import pandas as pd - - -def combine_sources( - sources: Mapping[str, pd.DataFrame], *, priority: Sequence[str] -) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Combine sources and record source and cleaning-method provenance.""" - selected = {source: sources[source] for source in priority} - - _validate_source_alignment(selected) - - first_source = priority[0] - combined = selected[first_source].copy() - - data_source = pd.DataFrame( - pd.NA, index=combined.index, columns=combined.columns, dtype="string" - ) - - cleaning_method = pd.DataFrame( - pd.NA, index=combined.index, columns=combined.columns, dtype="string" - ) - - first_source_values = combined.notna() - - data_source = data_source.mask(first_source_values, first_source) - - cleaning_method = cleaning_method.mask( - first_source_values, f"observed_{first_source}" - ) - - for source_name in priority[1:]: - candidate = selected[source_name] - - newly_supplied = combined.isna() & candidate.notna() - - combined = combined.combine_first(candidate) - - data_source = data_source.mask(newly_supplied, source_name) - - cleaning_method = cleaning_method.mask( - newly_supplied, f"observed_{source_name}" - ) - - return combined, data_source, cleaning_method - - -def _validate_source_alignment(sources: Mapping[str, pd.DataFrame]) -> None: - """Require all prepared sources to use the same target grid.""" - source_items = list(sources.items()) - - reference_name, reference = source_items[0] - - for source_name, source in source_items[1:]: - if not source.index.equals(reference.index): - raise ValueError( - f"Demand source {source_name!r} does not use the " - f"same time index as {reference_name!r}." - ) - - if not source.columns.equals(reference.columns): - raise ValueError( - f"Demand source {source_name!r} does not use the " - f"same country columns as {reference_name!r}." - ) - - -def combine_auxiliary_sources( - loads: Mapping[str, pd.DataFrame], *, priority: Sequence[str] -) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Combine available auxiliary sources using configured source priority.""" - if not loads: - empty = pd.DataFrame() - return empty, empty.copy(), empty.copy() - - available_priority = [source for source in priority if source in loads] - - columns = sorted({column for load in loads.values() for column in load.columns}) - - aligned = {source: load.reindex(columns=columns) for source, load in loads.items()} - - return combine_sources(aligned, priority=available_priority) diff --git a/workflow/scripts/cleaning/pipeline.py b/workflow/scripts/cleaning/pipeline.py deleted file mode 100644 index 2523d07..0000000 --- a/workflow/scripts/cleaning/pipeline.py +++ /dev/null @@ -1,98 +0,0 @@ -"""The pipeline coordinates the gap-filling rules and ensures each receives the necessary parameters.""" - -from __future__ import annotations - -import logging -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd - -from cleaning.advanced.gap_report import build_gap_report -from cleaning.advanced.planning.plan import build_auxiliary_fill_plan -from cleaning.basic.apply import fill_basic_gaps -from cleaning.combine_sources import combine_sources -from cleaning.provenance import build_cleaning_method_ranks, derive_cleaning_method_rank - -logger = logging.getLogger(__name__) - - -def clean_demand( - sources: Mapping[str, pd.DataFrame], - *, - source_priority: Sequence[str], - gap_filling_config: Mapping[str, Any], -) -> tuple[ - pd.DataFrame, # data - pd.DataFrame, # sources - pd.DataFrame, # method - pd.DataFrame, # rank - pd.DataFrame, # gap_report - pd.DataFrame, # auxiliary_fill_plan -]: - """Combine observed sources and fill remaining gaps.""" - (combined, data_source, cleaning_method) = combine_sources( - sources, priority=source_priority - ) - - basic_rules = gap_filling_config["basic"]["rules"] - - cleaned, cleaning_method = fill_basic_gaps( - load=combined, - cleaning_method=cleaning_method, - rules=basic_rules, - enabled=gap_filling_config["mode"] != "off", - ) - - cleaning_method_ranks = build_cleaning_method_ranks( - source_priority=source_priority, rules=basic_rules - ) - - cleaning_method_rank = derive_cleaning_method_rank( - cleaning_method=cleaning_method, ranks=cleaning_method_ranks - ) - - gap_report = build_gap_report( - cleaned, enabled=gap_filling_config["mode"] == "advanced" - ) - - target_countries = list(cleaned.columns) - target_start = cleaned.index.min() - target_end = cleaned.index.max() + pd.Timedelta(hours=1) - - if gap_filling_config["mode"] == "advanced": - advanced_overrides = gap_filling_config["advanced"]["overrides"] - - auxiliary_fill_plan = build_auxiliary_fill_plan( - advanced_overrides, - target_countries=target_countries, - target_start=target_start, - target_end=target_end, - ) - - logger.info( - "Advanced gap diagnosis found %s unresolved gaps covering %s values.", - len(gap_report), - int(gap_report["gap_hours"].sum()), - ) - - logger.info( - "Advanced auxiliary-fill plan contains %s active instructions.", - len(auxiliary_fill_plan), - ) - else: - auxiliary_fill_plan = build_auxiliary_fill_plan( - {}, - target_countries=target_countries, - target_start=target_start, - target_end=target_end, - ) - - return ( - cleaned, - data_source, - cleaning_method, - cleaning_method_rank, - gap_report, - auxiliary_fill_plan, - ) diff --git a/workflow/scripts/cleaning/provenance.py b/workflow/scripts/cleaning/provenance.py deleted file mode 100644 index b1a04b2..0000000 --- a/workflow/scripts/cleaning/provenance.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Cleaning-method provenance and ranking helpers.""" - -from collections.abc import Mapping, Sequence -from typing import Any - -import pandas as pd - - -def build_cleaning_method_ranks( - *, source_priority: Sequence[str], rules: Sequence[Mapping[str, Any]] -) -> dict[str, int]: - """Build cleaning-method ranks from configured order.""" - ranks: dict[str, int] = {} - - for rank, source_name in enumerate(source_priority): - ranks[f"observed_{source_name}"] = rank - - first_gap_filling_rank = len(source_priority) - - for rule_position, rule in enumerate(rules): - ranks[str(rule["name"])] = first_gap_filling_rank + rule_position - - ranks["missing"] = len(source_priority) + len(rules) - - return ranks - - -def derive_cleaning_method_rank( - *, cleaning_method: pd.DataFrame, ranks: Mapping[str, int] -) -> pd.DataFrame: - """Translate cleaning-method names to integer ranks.""" - present_methods = set(cleaning_method.stack().astype(str).unique()) - - unknown_methods = sorted(present_methods - set(ranks)) - - if unknown_methods: - raise ValueError(f"No cleaning-method rank is defined for: {unknown_methods}") - - cleaning_method_rank = cleaning_method.replace(ranks) - - return cleaning_method_rank.astype("int16") - - -def build_final_cleaning_rules( - gap_filling_config: Mapping[str, Any], -) -> list[dict[str, Any]]: - """Return configured cleaning rules in final provenance order.""" - rules = [dict(rule) for rule in gap_filling_config["basic"]["rules"]] - - if gap_filling_config["mode"] == "advanced": - rules.extend( - {"name": rule_name, **override} - for rule_name, override in ( - gap_filling_config["advanced"]["overrides"].items() - ) - ) - - return rules diff --git a/workflow/scripts/cleaning/sources/__init__.py b/workflow/scripts/cleaning/sources/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/cleaning/validation.py b/workflow/scripts/cleaning/validation.py deleted file mode 100644 index 7fb9b6b..0000000 --- a/workflow/scripts/cleaning/validation.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Shared validation for cleaned electricity-demand data.""" - -import pandas as pd - - -def validate_load(load: pd.DataFrame) -> None: - """Validate the structure of hourly electricity-demand data.""" - if not isinstance(load, pd.DataFrame): - raise TypeError("Load must be a pandas DataFrame.") - - if load.empty: - raise ValueError("Load dataframe is empty.") - - timestep = infer_regular_timestep(load.index) - - if timestep != pd.Timedelta(hours=1): - raise ValueError( - "Gap filling currently expects hourly load data. " - f"Found timestep {timestep}." - ) - - if not all(pd.api.types.is_numeric_dtype(dtype) for dtype in load.dtypes): - raise TypeError("All load columns must be numeric.") - - -def infer_regular_timestep(index: pd.Index) -> pd.Timedelta: - """Infer and validate the regular timestep of a datetime index.""" - if not isinstance(index, pd.DatetimeIndex): - raise TypeError("Load data must use a pandas DatetimeIndex.") - - if not index.is_monotonic_increasing: - raise ValueError("Load timestamps must be sorted in increasing order.") - - if index.has_duplicates: - raise ValueError("Load timestamps must not contain duplicates.") - - differences = index.to_series().diff().dropna() - - if differences.empty: - raise ValueError("At least two timestamps are required for gap filling.") - - timestep = differences.iloc[0] - - if not differences.eq(timestep).all(): - raise ValueError( - "Load data must have a complete, regular time index before gap filling." - ) - - return timestep diff --git a/workflow/scripts/combine_auxiliary_sources.py b/workflow/scripts/combine_auxiliary_sources.py deleted file mode 100644 index 9900c2a..0000000 --- a/workflow/scripts/combine_auxiliary_sources.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Aggregates auxiliary data into one source.""" - -from pathlib import Path - -import pandas as pd -from cleaning.combine_sources import combine_auxiliary_sources - - -def _source_name(path: str | Path) -> str: - """Return source name from auxiliary source path.""" - return Path(path).parent.name - - -loads = {_source_name(path): pd.read_parquet(path) for path in snakemake.input.sources} - -combined, data_source, cleaning_method = combine_auxiliary_sources( - loads, priority=snakemake.params.source_priority -) - -combined.to_parquet(snakemake.output.demand) -data_source.to_parquet(snakemake.output.data_source) -cleaning_method.to_parquet(snakemake.output.cleaning_method) From 62200afbe1746fe4a2675e5d4819b39b838f8c19 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:45:48 +0200 Subject: [PATCH 117/241] remove legacy cleaning package after tclean cutover --- workflow/scripts/_advanced_execution.py | 59 +++++++++++++++++++ workflow/scripts/_plot_timeline.py | 22 +++++-- workflow/scripts/download_load_entsoe.py | 2 +- workflow/scripts/plot_cleaning_timeline.py | 2 +- .../scripts/plot_readme_cleaning_example.py | 4 +- workflow/scripts/prepare_load_entsoe.py | 2 +- workflow/scripts/prepare_load_neso.py | 2 +- workflow/scripts/prepare_load_opsd.py | 2 +- 8 files changed, 82 insertions(+), 13 deletions(-) diff --git a/workflow/scripts/_advanced_execution.py b/workflow/scripts/_advanced_execution.py index e09247e..da8b23c 100644 --- a/workflow/scripts/_advanced_execution.py +++ b/workflow/scripts/_advanced_execution.py @@ -3,7 +3,9 @@ from __future__ import annotations import hashlib +import json from collections.abc import Mapping, Sequence +from pathlib import Path from typing import Any import pandas as pd @@ -270,3 +272,60 @@ def empty_execution_plan() -> dict[str, object]: "constructed_profile_rule_names": [], "external_profile_files": {}, } + + +def load_execution_plan( + path: str | Path, +) -> dict[str, Any]: + """Load one compiled advanced execution plan.""" + with open(path, encoding="utf-8") as file: + plan = json.load(file) + + if not isinstance(plan, dict): + raise TypeError( + "Advanced execution plan must contain a JSON object." + ) + + if plan.get("version") != EXECUTION_PLAN_VERSION: + raise ValueError( + "Unsupported advanced execution plan version: " + f"{plan.get('version')!r}. " + f"Expected {EXECUTION_PLAN_VERSION}." + ) + + return plan + + +def get_batch( + plan: Mapping[str, Any], + *, + batch_id: str, + source: str | None = None, +) -> Mapping[str, Any]: + """Return exactly one compiled auxiliary batch.""" + matches = [ + batch + for batch in plan["batches"] + if ( + batch["batch_id"] == batch_id + and ( + source is None + or batch["source"] == source + ) + ) + ] + + if len(matches) != 1: + source_text = ( + f" for source {source!r}" + if source is not None + else "" + ) + + raise ValueError( + "Expected exactly one auxiliary batch " + f"{batch_id!r}{source_text}, " + f"found {len(matches)}." + ) + + return matches[0] \ No newline at end of file diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 3c70e2e..075faea 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -8,12 +8,11 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd +from _tclean_config import build_advanced_rules, build_basic_rules from cmap import Colormap from matplotlib.colors import ListedColormap, to_rgba from matplotlib.patches import Patch -from cleaning.provenance import build_final_cleaning_rules - logger = logging.getLogger(__name__) @@ -351,16 +350,27 @@ def _build_cleaning_method_metadata( ) rank += 1 - rules = build_final_cleaning_rules(gap_filling_config) + basic_rules = build_basic_rules(gap_filling_config) + advanced_rules = build_advanced_rules(gap_filling_config) + + rule_names = [ + rule["name"] + for rule in basic_rules + ] - for rule in rules: - rule_name = rule["name"] + if not advanced_rules.empty: + rule_names.extend( + advanced_rules["rule_name"].tolist() + ) + for rule_name in rule_names: rows.append( { "cleaning_method": rule_name, "cleaning_method_rank": rank, - "label": (f"Rank {rank}: {_format_rule_name(rule_name)}"), + "label": ( + f"Rank {rank}: {_format_rule_name(rule_name)}" + ), "category": "imputed", } ) diff --git a/workflow/scripts/download_load_entsoe.py b/workflow/scripts/download_load_entsoe.py index 5765eb4..43e41a3 100644 --- a/workflow/scripts/download_load_entsoe.py +++ b/workflow/scripts/download_load_entsoe.py @@ -3,7 +3,7 @@ import logging from typing import TYPE_CHECKING, Any -from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from _advanced_execution import get_batch, load_execution_plan from sources.entsoe.download import download_entsoe from tclean import TimeGrid diff --git a/workflow/scripts/plot_cleaning_timeline.py b/workflow/scripts/plot_cleaning_timeline.py index 70056ec..bad95a5 100644 --- a/workflow/scripts/plot_cleaning_timeline.py +++ b/workflow/scripts/plot_cleaning_timeline.py @@ -3,7 +3,7 @@ import logging import sys -from cleaning.plot_timeline import main +from _plot_timeline import main sys.stderr = open(snakemake.log[0], "w", buffering=1) diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py index e993f90..3cb995f 100644 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -7,7 +7,7 @@ import matplotlib.pyplot as plt import pandas as pd -from cleaning.plot_timeline import ( +from _plot_timeline import ( _add_mean_load_labels, _add_normalised_demand_traces, _build_cleaning_method_metadata, @@ -37,7 +37,7 @@ END = "2021-11-01" # Must correspond to the configuration used for the long run. -SOURCE_NAMES = ["entsoe_api", "neso", "opsd_api"] +SOURCE_NAMES = ["entsoe", "neso", "opsd"] GAP_FILLING_CONFIG = { "mode": "basic", diff --git a/workflow/scripts/prepare_load_entsoe.py b/workflow/scripts/prepare_load_entsoe.py index d92bd1e..fb77fca 100644 --- a/workflow/scripts/prepare_load_entsoe.py +++ b/workflow/scripts/prepare_load_entsoe.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any -from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from _advanced_execution import get_batch, load_execution_plan from sources.entsoe.prepare import prepare_entsoe from tclean import TimeGrid diff --git a/workflow/scripts/prepare_load_neso.py b/workflow/scripts/prepare_load_neso.py index e4b8fb8..bf0932e 100644 --- a/workflow/scripts/prepare_load_neso.py +++ b/workflow/scripts/prepare_load_neso.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from _advanced_execution import get_batch, load_execution_plan from sources.neso.prepare import prepare_neso from tclean import TimeGrid diff --git a/workflow/scripts/prepare_load_opsd.py b/workflow/scripts/prepare_load_opsd.py index 536a288..677a889 100644 --- a/workflow/scripts/prepare_load_opsd.py +++ b/workflow/scripts/prepare_load_opsd.py @@ -3,7 +3,7 @@ import sys from typing import TYPE_CHECKING, Any -from cleaning.advanced.planning.manifest import get_batch, load_execution_plan +from _advanced_execution import get_batch, load_execution_plan from sources.opsd.prepare import prepare_opsd from tclean import TimeGrid From 001a5263fe5abe5d71c420b595e7224a3b507b51 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 14:55:00 +0200 Subject: [PATCH 118/241] flatten common workflow helpers --- workflow/Snakefile | 4 +-- .../scripts/{common/plots.py => _plots.py} | 0 .../{common/schemas.py => _schemas.py} | 0 workflow/scripts/common/__init__.py | 0 workflow/scripts/common/time.py | 32 ------------------- .../scripts/demand_electricity_polygon.py | 4 +-- workflow/scripts/demand_electricity_raster.py | 4 +-- workflow/scripts/sources/opsd/prepare.py | 2 +- 8 files changed, 7 insertions(+), 39 deletions(-) rename workflow/scripts/{common/plots.py => _plots.py} (100%) rename workflow/scripts/{common/schemas.py => _schemas.py} (100%) delete mode 100644 workflow/scripts/common/__init__.py delete mode 100644 workflow/scripts/common/time.py diff --git a/workflow/Snakefile b/workflow/Snakefile index 2210789..33345b2 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -30,8 +30,8 @@ with open(workflow.source_path("internal/settings.yaml"), "r") as f: # Load python files in scripts that are used in other scripts, but not run itself. # This is necessary for integrating the snakemake module via reference to GitHub. -workflow.source_path("scripts/common/plots.py") -workflow.source_path("scripts/common/schemas.py") +workflow.source_path("scripts/_plots.py") +workflow.source_path("scripts/_schemas.py") # Add all your includes here. diff --git a/workflow/scripts/common/plots.py b/workflow/scripts/_plots.py similarity index 100% rename from workflow/scripts/common/plots.py rename to workflow/scripts/_plots.py diff --git a/workflow/scripts/common/schemas.py b/workflow/scripts/_schemas.py similarity index 100% rename from workflow/scripts/common/schemas.py rename to workflow/scripts/_schemas.py diff --git a/workflow/scripts/common/__init__.py b/workflow/scripts/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/workflow/scripts/common/time.py b/workflow/scripts/common/time.py deleted file mode 100644 index 18a7970..0000000 --- a/workflow/scripts/common/time.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Shared time-index utilities.""" - -from __future__ import annotations - -import pandas as pd - - -def as_utc_timestamp(value: object) -> pd.Timestamp: - """Convert a timestamp-like value to UTC.""" - timestamp = pd.Timestamp(value) - - if timestamp.tzinfo is None: - return timestamp.tz_localize("UTC") - - return timestamp.tz_convert("UTC") - - -def build_hourly_index(*, start: object, end: object) -> pd.DatetimeIndex: - """Create an end-exclusive hourly UTC index.""" - start_timestamp = as_utc_timestamp(start) - end_timestamp = as_utc_timestamp(end) - - if end_timestamp <= start_timestamp: - raise ValueError("The temporal end must be later than its start.") - - return pd.date_range( - start=start_timestamp, - end=end_timestamp, - freq="h", - inclusive="left", - name="time", - ) diff --git a/workflow/scripts/demand_electricity_polygon.py b/workflow/scripts/demand_electricity_polygon.py index d91f99b..721358c 100644 --- a/workflow/scripts/demand_electricity_polygon.py +++ b/workflow/scripts/demand_electricity_polygon.py @@ -9,8 +9,8 @@ import matplotlib.pyplot as plt import pandas as pd import rioxarray as rxr -from common.plots import map_polygon -from common.schemas import Shapes +from _plots import map_polygon +from _schemas import Shapes if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/demand_electricity_raster.py b/workflow/scripts/demand_electricity_raster.py index 9654f07..31664f9 100644 --- a/workflow/scripts/demand_electricity_raster.py +++ b/workflow/scripts/demand_electricity_raster.py @@ -10,8 +10,8 @@ import numpy as np import pandas as pd import rioxarray as rxr -from common.plots import map_raster, plot_national_profiles -from common.schemas import Shapes +from _plots import map_raster, plot_national_profiles +from _schemas import Shapes if TYPE_CHECKING: snakemake: Any diff --git a/workflow/scripts/sources/opsd/prepare.py b/workflow/scripts/sources/opsd/prepare.py index 035df24..00c0ff2 100644 --- a/workflow/scripts/sources/opsd/prepare.py +++ b/workflow/scripts/sources/opsd/prepare.py @@ -5,7 +5,7 @@ import pandas as pd import pycountry -from common.schemas import OPSDLoad +from _schemas import OPSDLoad from tclean import TimeGrid From 350b275cb7a84550fc37db33a63a0ff6b7a569d5 Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 15:09:56 +0200 Subject: [PATCH 119/241] rebuild unit tests around modelblocks tclean boundary --- config/config.yaml | 102 ++-- tests/unit/README.md | 22 + tests/unit/test_advanced_apply.py | 457 ------------------ tests/unit/test_advanced_combine_auxiliary.py | 37 -- .../test_advanced_construct_from_sources.py | 203 -------- tests/unit/test_advanced_execution.py | 110 +++++ tests/unit/test_advanced_plan.py | 214 -------- tests/unit/test_advanced_requirements.py | 240 --------- tests/unit/test_advanced_source_requests.py | 210 -------- tests/unit/test_cleaning_pipeline.py | 455 ----------------- tests/unit/test_config_schema.py | 61 +-- tests/unit/test_external_profile.py | 103 ---- tests/unit/test_plan_auxiliary_data.py | 376 -------------- tests/unit/test_provenance.py | 31 -- tests/unit/test_schemas.py | 33 ++ tests/unit/test_tclean_config.py | 57 +++ tests/unit/test_validate_config.py | 76 --- .../test_workflow_environment_boundary.py | 37 +- 18 files changed, 312 insertions(+), 2512 deletions(-) create mode 100644 tests/unit/README.md delete mode 100644 tests/unit/test_advanced_apply.py delete mode 100644 tests/unit/test_advanced_combine_auxiliary.py delete mode 100644 tests/unit/test_advanced_construct_from_sources.py create mode 100644 tests/unit/test_advanced_execution.py delete mode 100644 tests/unit/test_advanced_plan.py delete mode 100644 tests/unit/test_advanced_requirements.py delete mode 100644 tests/unit/test_advanced_source_requests.py delete mode 100644 tests/unit/test_cleaning_pipeline.py delete mode 100644 tests/unit/test_external_profile.py delete mode 100644 tests/unit/test_plan_auxiliary_data.py delete mode 100644 tests/unit/test_provenance.py create mode 100644 tests/unit/test_schemas.py create mode 100644 tests/unit/test_tclean_config.py delete mode 100644 tests/unit/test_validate_config.py diff --git a/config/config.yaml b/config/config.yaml index a1d3851..21ec320 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,28 +1,22 @@ -# A minimal example of how to configure this module temporal_scope: - start: "2016-01-01" - end: "2026-01-01" -# provide a list of sources and set their priority order. -# Options include: entsoe_api, neso, opsd_api + start: "2017-01-01" + end: "2017-01-03" + frequency: "1h" +# provide a list of sources and set their priority order. Options include: entsoe, neso, opsd load_sources: - - entsoe_api + - entsoe - neso - - opsd_api + - opsd gap_filling: - # Sets the data-cleaning mode. Options: off, basic, advanced. - mode: basic + mode: advanced - # Basic gap-filling configuration. basic: - # Basic gap-filling rules are applied in the listed order. rules: - # Interpolates across tiny gaps where no more than 3 hours are missing. - name: interpolate_short_gaps method: linear_interpolation max_gap: 3h - # Takes the average of the same hour one week earlier and later. - name: average_adjacent_weeks method: average_periods max_gap: 326h @@ -30,81 +24,91 @@ gap_filling: - -7d - 7d - # Copies from the previous week for gaps of up to one week. - name: copy_previous_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: -168h + require_complete_source: true - # Copies from the following week for gaps of up to one week. - name: copy_following_week - method: copy_period + method: copy_periods max_gap: 168h source_offset: 168h + require_complete_source: true - # Advanced gap-filling configuration. advanced: auxiliary_data: basic_cleaning: enabled: true - # Explicit country-period instructions for unresolved cases. - overrides: - - build_alb_from_gbr_alb_winter: - country: ALB - start: "2022-01-01" - end: "2022-02-01" - scope: fill_gaps + sources: + alb_from_gbr_alb_winter: method: construct_from_sources - sources: + periods: - country: GBR start: "2024-01-01" end: "2024-02-01" weight: 1 scaling: method: match_energy - target_sources: + periods: - country: ALB start: "2024-01-01" end: "2024-02-01" weight: 1 - build_alb_from_gbr_summer: - country: ALB - start: "2022-05-01" - end: "2022-06-01" - scope: fill_gaps + alb_from_alb_2024: method: construct_from_sources - sources: - - country: GBR - start: "2022-05-01" - end: "2022-06-01" + periods: + - country: ALB + start: "2024-01-01" + end: "2024-01-03" weight: 1 scaling: method: match_energy - target_sources: + periods: - country: ALB - start: "2024-05-01" - end: "2024-06-01" + start: "2024-01-01" + end: "2024-01-03" weight: 1 - example_inactive_mne_override: - country: MNE - start: "2020-03-01" - end: "2020-04-01" - scope: fill_gaps + mne_from_srb: method: construct_from_sources - sources: + periods: - country: SRB start: "2022-03-01" end: "2022-04-01" weight: 1 - test_external_profile_overwrite: + alb_external_test: + method: external_profile + file: inputs/external_profiles/alb_external_test.csv + + rules: + - name: build_alb_from_gbr_alb_winter + country: ALB + start: "2022-01-01" + end: "2022-02-01" + scope: fill_gaps + source: alb_from_gbr_alb_winter + + - name: build_alb_from_alb_2024 + country: ALB + start: "2017-01-01" + end: "2017-01-03" + scope: overwrite + source: alb_from_alb_2024 + + - name: example_inactive_mne_override + country: MNE + start: "2020-03-01" + end: "2020-04-01" + scope: fill_gaps + source: mne_from_srb + + - name: test_external_profile_overwrite country: ALB start: "2022-01-01 00:00" end: "2022-01-08 00:00" scope: overwrite - method: external_profile - path: inputs/external_profiles/alb_external_test.csv + source: alb_external_test \ No newline at end of file diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 0000000..f2f9468 --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,22 @@ +# Replacement unit-test suite + +This suite is intentionally scoped to **module_demand_electricity responsibilities** after the T-Clean extraction. + +It does not re-test generic T-Clean behavior such as gap filling, gap reports, advanced-rule application, external-profile parsing, source construction, generic auxiliary requirements, or provenance algorithms. Those belong in T-Clean. + +Covered here: +- Modelblocks execution-plan/batch metadata and JSON loading +- Modelblocks-to-T-Clean configuration translation at the adapter boundary +- module configuration schema +- module/provider schemas +- architecture/environment boundaries + +The old cleaning-focused unit tests can be removed rather than ported. + + +## Current migration signal + +`test_default_config_matches_schema` is intentionally retained. If it fails, the +repository's shipped `config/config.yaml` is out of sync with the current +`workflow/internal/config.schema.yaml`; fix the configuration rather than +weakening or removing this test. diff --git a/tests/unit/test_advanced_apply.py b/tests/unit/test_advanced_apply.py deleted file mode 100644 index 3caebd3..0000000 --- a/tests/unit/test_advanced_apply.py +++ /dev/null @@ -1,457 +0,0 @@ -"""Tests for applying advanced auxiliary-fill rules.""" - -import pandas as pd -import pytest -from cleaning.advanced.apply import ( - apply_auxiliary_fill_rule, - apply_auxiliary_fill_rules, -) - - -def _load() -> pd.DataFrame: - index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") - - return pd.DataFrame({"ALB": [1.0, pd.NA, 3.0]}, index=index, dtype="Float64") - - -def _cleaning_method() -> pd.DataFrame: - index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") - - return pd.DataFrame( - {"ALB": ["observed_entsoe", "missing", "observed_entsoe"]}, index=index - ) - - -def test_construct_from_sources_requires_profile() -> None: - """Test construct from sources requires profile.""" - rule = {"method": "construct_from_sources"} - - with pytest.raises(ValueError, match="requires a constructed auxiliary profile"): - apply_auxiliary_fill_rule( - _load(), _cleaning_method(), rule_name="construct_albania", rule=rule - ) - - -def test_construct_from_sources_fills_gaps() -> None: - """Test construct from sources fills gaps.""" - load = _load() - cleaning_method = _cleaning_method() - - profile = pd.Series([10.0, 20.0, 30.0], index=load.index) - - rule = { - "method": "construct_from_sources", - "country": "ALB", - "start": "2021-01-01T00:00:00+00:00", - "end": "2021-01-01T03:00:00+00:00", - "scope": "fill_gaps", - } - - filled, methods = apply_auxiliary_fill_rule( - load, cleaning_method, rule_name="construct_albania", rule=rule, profile=profile - ) - - assert filled["ALB"].tolist() == [1.0, 20.0, 3.0] - - assert methods["ALB"].tolist() == [ - "observed_entsoe", - "construct_albania", - "observed_entsoe", - ] - - -def test_construct_from_sources_overwrites_entire_period() -> None: - """Test construct from sources overwrites entire period.""" - load = _load() - cleaning_method = _cleaning_method() - - profile = pd.Series([10.0, 20.0, 30.0], index=load.index) - - rule = { - "method": "construct_from_sources", - "country": "ALB", - "start": "2021-01-01T00:00:00+00:00", - "end": "2021-01-01T03:00:00+00:00", - "scope": "overwrite", - } - - filled, methods = apply_auxiliary_fill_rule( - load, cleaning_method, rule_name="construct_albania", rule=rule, profile=profile - ) - - assert filled["ALB"].tolist() == [10.0, 20.0, 30.0] - - assert methods["ALB"].tolist() == [ - "construct_albania", - "construct_albania", - "construct_albania", - ] - - -def test_leave_missing_returns_unchanged_copies() -> None: - """Test leaving missing does nothing.""" - load = _load() - cleaning_method = _cleaning_method() - - result_load, result_method = apply_auxiliary_fill_rule( - load, - cleaning_method, - rule_name="leave_albania_missing", - rule={"method": "leave_missing"}, - ) - - pd.testing.assert_frame_equal(result_load, load) - pd.testing.assert_frame_equal(result_method, cleaning_method) - - assert result_load is not load - assert result_method is not cleaning_method - - -def test_rejects_unsupported_method() -> None: - """Test unsupported methods.""" - with pytest.raises(ValueError, match="Unsupported advanced-fill method"): - apply_auxiliary_fill_rule( - _load(), - _cleaning_method(), - rule_name="invalid_rule", - rule={"method": "unknown"}, - ) - - -def test_apply_auxiliary_fill_rules_applies_rules_in_order() -> None: - """Test order.""" - load = _load() - cleaning_method = _cleaning_method() - - first_profile = pd.Series([10.0, 20.0, 30.0], index=load.index) - - second_profile = pd.Series([100.0, 200.0, 300.0], index=load.index) - - overrides = { - "fill_gaps": { - "method": "construct_from_sources", - "country": "ALB", - "start": "2021-01-01T00:00:00+00:00", - "end": "2021-01-01T03:00:00+00:00", - "scope": "fill_gaps", - }, - "overwrite": { - "method": "construct_from_sources", - "country": "ALB", - "start": "2021-01-01T00:00:00+00:00", - "end": "2021-01-01T03:00:00+00:00", - "scope": "overwrite", - }, - } - - profiles = {"fill_gaps": first_profile, "overwrite": second_profile} - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles=profiles, - external_profiles={}, - ) - - assert filled["ALB"].tolist() == [100.0, 200.0, 300.0] - - assert methods["ALB"].tolist() == ["overwrite", "overwrite", "overwrite"] - - -def test_apply_auxiliary_fill_rules_with_no_overrides_returns_copies() -> None: - """Test fill returns copy without overrides.""" - load = _load() - cleaning_method = _cleaning_method() - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides={}, - constructed_profiles={}, - external_profiles={}, - ) - - pd.testing.assert_frame_equal(filled, load) - pd.testing.assert_frame_equal(methods, cleaning_method) - - assert filled is not load - assert methods is not cleaning_method - - -def test_overwrite_replaces_existing_values() -> None: - """Test overwrite.""" - index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [10.0, 20.0, 30.0, 40.0]}, index=index) - - cleaning_method = pd.DataFrame({"ALB": ["observed_entsoe"] * 4}, index=index) - - profile = pd.Series([100.0, 200.0], index=index[1:3], name="ALB") - - overrides = { - "replace_albania": { - "country": "ALB", - "start": index[1], - "end": index[3], - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2024-01-01", - "end": "2024-01-01 02:00", - "weight": 1, - } - ], - } - } - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={"replace_albania": profile}, - external_profiles={}, - ) - - assert filled["ALB"].tolist() == [10.0, 100.0, 200.0, 40.0] - - assert methods["ALB"].tolist() == [ - "observed_entsoe", - "replace_albania", - "replace_albania", - "observed_entsoe", - ] - - -def test_fill_gaps_preserves_existing_values() -> None: - """Test fill doesn't overwrite.""" - index = pd.date_range("2022-01-01", periods=4, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [10.0, float("nan"), 30.0, 40.0]}, index=index) - - cleaning_method = pd.DataFrame( - { - "ALB": [ - "observed_entsoe", - "missing", - "observed_entsoe", - "observed_entsoe", - ] - }, - index=index, - ) - - profile = pd.Series([100.0, 200.0], index=index[1:3], name="ALB") - - overrides = { - "fill_albania": { - "country": "ALB", - "start": index[1], - "end": index[3], - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2024-01-01", - "end": "2024-01-01 02:00", - "weight": 1, - } - ], - } - } - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={"fill_albania": profile}, - external_profiles={}, - ) - - assert filled["ALB"].tolist() == [10.0, 100.0, 30.0, 40.0] - - assert methods["ALB"].tolist() == [ - "observed_entsoe", - "fill_albania", - "observed_entsoe", - "observed_entsoe", - ] - - -def test_external_profile_fill_gaps_only_replaces_missing_values(): - """Test external profile fills gaps.""" - index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [100.0, None, 300.0, None]}, index=index) - - cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - - profile = pd.Series([110.0, 220.0, 330.0, 440.0], index=index) - - overrides = { - "external_albania": { - "method": "external_profile", - "country": "ALB", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T04:00:00Z", - "scope": "fill_gaps", - } - } - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={}, - external_profiles={"external_albania": profile}, - ) - - expected = pd.Series([100.0, 220.0, 300.0, 440.0], index=index, name="ALB") - - pd.testing.assert_series_equal(filled["ALB"], expected) - - assert pd.isna(methods.loc[index[0], "ALB"]) - assert methods.loc[index[1], "ALB"] == "external_albania" - assert pd.isna(methods.loc[index[2], "ALB"]) - assert methods.loc[index[3], "ALB"] == "external_albania" - - -def test_external_profile_overwrite_replaces_supplied_values(): - """Test external overwrites.""" - index = pd.date_range("2025-01-01", periods=4, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0]}, index=index) - - cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - - profile = pd.Series([110.0, 220.0, 330.0, 440.0], index=index) - - overrides = { - "external_albania": { - "method": "external_profile", - "country": "ALB", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T04:00:00Z", - "scope": "overwrite", - } - } - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={}, - external_profiles={"external_albania": profile}, - ) - - expected = pd.Series([110.0, 220.0, 330.0, 440.0], index=index, name="ALB") - - pd.testing.assert_series_equal(filled["ALB"], expected) - - assert (methods["ALB"] == "external_albania").all() - - -def test_external_profile_overwrite_only_replaces_supplied_timestamps(): - """Test overwrite domains.""" - index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) - - cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - - profile = pd.Series([2200.0, 4400.0], index=[index[1], index[3]]) - - overrides = { - "external_albania": { - "method": "external_profile", - "country": "ALB", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T05:00:00Z", - "scope": "overwrite", - } - } - - filled, methods = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={}, - external_profiles={"external_albania": profile}, - ) - - expected = pd.Series([100.0, 2200.0, 300.0, 4400.0, 500.0], index=index, name="ALB") - - pd.testing.assert_series_equal(filled["ALB"], expected) - - assert methods.loc[index[1], "ALB"] == "external_albania" - assert methods.loc[index[3], "ALB"] == "external_albania" - - assert pd.isna(methods.loc[index[0], "ALB"]) - assert pd.isna(methods.loc[index[2], "ALB"]) - assert pd.isna(methods.loc[index[4], "ALB"]) - - -def test_external_profile_ignores_values_outside_rule_period(): - """Test overwrite restricts domain.""" - index = pd.date_range("2025-01-01", periods=5, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [100.0, 200.0, 300.0, 400.0, 500.0]}, index=index) - - cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - - profile = pd.Series([1000.0, 2000.0, 3000.0, 4000.0, 5000.0], index=index) - - overrides = { - "external_albania": { - "method": "external_profile", - "country": "ALB", - "start": "2025-01-01T01:00:00Z", - "end": "2025-01-01T04:00:00Z", - "scope": "overwrite", - } - } - - filled, _ = apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={}, - external_profiles={"external_albania": profile}, - ) - - expected = pd.Series( - [100.0, 2000.0, 3000.0, 4000.0, 500.0], index=index, name="ALB" - ) - - pd.testing.assert_series_equal(filled["ALB"], expected) - - -def test_external_profile_requires_profile(): - """Test external requires profile.""" - index = pd.date_range("2025-01-01", periods=2, freq="h", tz="UTC") - - load = pd.DataFrame({"ALB": [None, None]}, index=index) - - cleaning_method = pd.DataFrame(None, index=index, columns=["ALB"], dtype=object) - - overrides = { - "external_albania": { - "method": "external_profile", - "country": "ALB", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T02:00:00Z", - "scope": "fill_gaps", - } - } - - with pytest.raises(ValueError, match="requires an external profile"): - apply_auxiliary_fill_rules( - load, - cleaning_method, - overrides=overrides, - constructed_profiles={}, - external_profiles={}, - ) diff --git a/tests/unit/test_advanced_combine_auxiliary.py b/tests/unit/test_advanced_combine_auxiliary.py deleted file mode 100644 index 20cd8ed..0000000 --- a/tests/unit/test_advanced_combine_auxiliary.py +++ /dev/null @@ -1,37 +0,0 @@ -import pandas as pd -from cleaning.combine_sources import combine_auxiliary_sources - - -def test_combine_auxiliary_sources_aligns_country_columns() -> None: - """Test combine auxiliary sources aligns country cols.""" - index = pd.date_range("2020-01-01", periods=2, freq="h", tz="UTC") - - entsoe = pd.DataFrame({"GBR": [10.0, 11.0], "GRC": [20.0, 21.0]}, index=index) - - neso = pd.DataFrame({"GBR": [12.0, 13.0]}, index=index) - - combined, data_source, cleaning_method = combine_auxiliary_sources( - {"entsoe": entsoe, "neso": neso}, priority=["neso", "entsoe"] - ) - - assert list(combined.columns) == ["GBR", "GRC"] - - assert combined["GBR"].tolist() == [12.0, 13.0] - - assert combined["GRC"].tolist() == [20.0, 21.0] - - assert data_source["GBR"].tolist() == ["neso", "neso"] - - assert data_source["GRC"].tolist() == ["entsoe", "entsoe"] - - assert cleaning_method.shape == combined.shape - - -def test_combine_auxiliary_sources_handles_empty_input() -> None: - combined, data_source, cleaning_method = combine_auxiliary_sources( - {}, priority=["entsoe", "neso", "opsd"] - ) - - assert combined.empty - assert data_source.empty - assert cleaning_method.empty diff --git a/tests/unit/test_advanced_construct_from_sources.py b/tests/unit/test_advanced_construct_from_sources.py deleted file mode 100644 index c8e84a1..0000000 --- a/tests/unit/test_advanced_construct_from_sources.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Tests for source-based advanced profile construction.""" - -import pandas as pd -from cleaning.advanced.methods.construct_from_sources import construct_from_sources - - -def test_construct_from_single_source() -> None: - auxiliary_index = pd.date_range("2019-01-01", periods=3, freq="h", tz="UTC") - - auxiliary = pd.DataFrame({"GRC": [10.0, 20.0, 30.0]}, index=auxiliary_index) - - target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[ - { - "country": "GRC", - "start": "2019-01-01T00:00:00+00:00", - "end": "2019-01-01T03:00:00+00:00", - "weight": 1, - } - ], - ) - - expected = pd.Series([10.0, 20.0, 30.0], index=target_index, dtype=float) - - pd.testing.assert_series_equal(result, expected) - - -def test_construct_from_sources_uses_weighted_mean() -> None: - auxiliary_index = pd.date_range("2019-01-01", periods=3, freq="h", tz="UTC") - - auxiliary = pd.DataFrame( - {"GRC": [10.0, 20.0, 30.0], "GBR": [30.0, 40.0, 50.0]}, index=auxiliary_index - ) - - target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[ - { - "country": "GRC", - "start": "2019-01-01T00:00:00+00:00", - "end": "2019-01-01T03:00:00+00:00", - "weight": 1, - }, - { - "country": "GBR", - "start": "2019-01-01T00:00:00+00:00", - "end": "2019-01-01T03:00:00+00:00", - "weight": 3, - }, - ], - ) - - expected = pd.Series([25.0, 35.0, 45.0], index=target_index, dtype=float) - - pd.testing.assert_series_equal(result, expected) - - -def test_construct_from_sources_drops_february_29() -> None: - source_index = pd.date_range( - "2020-02-01", "2020-03-01", freq="h", inclusive="left", tz="UTC" - ) - - values = pd.Series(1.0, index=source_index) - - values.loc[(values.index.month == 2) & (values.index.day == 29)] = 999.0 - - auxiliary = values.to_frame("GBR") - - target_index = pd.date_range( - "2021-02-01", "2021-03-01", freq="h", inclusive="left", tz="UTC" - ) - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[{"country": "GBR", "start": "2020-02-01", "end": "2020-03-01"}], - ) - - assert len(result) == 672 - assert not (result == 999.0).any() - - -def test_construct_from_sources_interpolates_february_29() -> None: - auxiliary_index = pd.date_range( - "2021-02-01", "2021-03-02", freq="h", inclusive="left", tz="UTC" - ) - - values = pd.Series(10.0, index=auxiliary_index) - - values.loc[(values.index.month == 2) & (values.index.day == 28)] = 20.0 - - values.loc[(values.index.month == 3) & (values.index.day == 1)] = 40.0 - - auxiliary = values.to_frame("GBR") - - target_index = pd.date_range( - "2020-02-01", "2020-03-01", freq="h", inclusive="left", tz="UTC" - ) - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[{"country": "GBR", "start": "2021-02-01", "end": "2021-03-01"}], - ) - - target_feb_29 = (result.index.month == 2) & (result.index.day == 29) - - assert len(result) == 696 - assert (result.loc[target_feb_29] == 30.0).all() - - -def test_construct_from_sources_matches_reference_energy() -> None: - auxiliary_index = pd.date_range("2019-01-01", periods=6, freq="h", tz="UTC") - - auxiliary = pd.DataFrame( - { - "GBR": [10.0, 20.0, 30.0, 100.0, 100.0, 100.0], - "ALB": [0.0, 0.0, 0.0, 20.0, 40.0, 60.0], - }, - index=auxiliary_index, - ) - - target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[ - { - "country": "GBR", - "start": "2019-01-01T00:00:00+00:00", - "end": "2019-01-01T03:00:00+00:00", - } - ], - scaling={ - "method": "match_energy", - "target_sources": [ - { - "country": "ALB", - "start": "2019-01-01T03:00:00+00:00", - "end": "2019-01-01T06:00:00+00:00", - } - ], - }, - ) - - expected = pd.Series([20.0, 40.0, 60.0], index=target_index, dtype=float) - - pd.testing.assert_series_equal(result, expected) - - -def test_match_energy_uses_weighted_target_energy() -> None: - auxiliary_index = pd.date_range("2019-01-01", periods=6, freq="h", tz="UTC") - - auxiliary = pd.DataFrame( - { - "GBR": [10, 20, 30, 0, 0, 0], - "ALB": [0, 0, 0, 20, 20, 20], - "GRC": [0, 0, 0, 60, 60, 60], - }, - index=auxiliary_index, - dtype=float, - ) - - target_index = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") - - result = construct_from_sources( - auxiliary, - target_index=target_index, - sources=[ - { - "country": "GBR", - "start": "2019-01-01T00:00:00+00:00", - "end": "2019-01-01T03:00:00+00:00", - } - ], - scaling={ - "method": "match_energy", - "target_sources": [ - { - "country": "ALB", - "start": "2019-01-01T03:00:00+00:00", - "end": "2019-01-01T06:00:00+00:00", - "weight": 1, - }, - { - "country": "GRC", - "start": "2019-01-01T03:00:00+00:00", - "end": "2019-01-01T06:00:00+00:00", - "weight": 3, - }, - ], - }, - ) - - assert result.sum() == 150.0 diff --git a/tests/unit/test_advanced_execution.py b/tests/unit/test_advanced_execution.py new file mode 100644 index 0000000..590c1a8 --- /dev/null +++ b/tests/unit/test_advanced_execution.py @@ -0,0 +1,110 @@ +"""Tests for Modelblocks-specific advanced execution metadata.""" + +import json + +import pandas as pd +import pytest +from _advanced_execution import ( + EXECUTION_PLAN_VERSION, + build_batch_id, + build_group_id, + build_source_batches, + empty_execution_plan, + get_batch, + index_batch_ids_by_group, + index_batch_ids_by_source, + load_execution_plan, + serialize_batch, +) + + +def _requests() -> pd.DataFrame: + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + return pd.DataFrame( + { + "source": ["entsoe", "entsoe", "opsd"], + "context": ["ALB", "GRC", "ALB"], + "start": [start, start, start], + "end": [end, end, end], + } + ) + + +def test_group_id_depends_only_on_period() -> None: + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + assert build_group_id(start=start, end=end) == "20200101T0000__20200201T0000" + + +def test_batch_id_is_independent_of_country_order() -> None: + start = pd.Timestamp("2020-01-01", tz="UTC") + end = pd.Timestamp("2020-02-01", tz="UTC") + first = build_batch_id( + source="entsoe", start=start, end=end, countries=["ALB", "GRC"] + ) + second = build_batch_id( + source="entsoe", start=start, end=end, countries=["GRC", "ALB"] + ) + assert first == second + assert first.startswith("entsoe__20200101T0000__20200201T0000__") + + +def test_source_batches_group_countries_by_source_and_period() -> None: + batches = build_source_batches(_requests()) + assert len(batches) == 2 + assert batches[0]["source"] == "entsoe" + assert batches[0]["countries"] == ["ALB", "GRC"] + assert batches[1]["source"] == "opsd" + assert batches[1]["countries"] == ["ALB"] + assert batches[0]["group_id"] == batches[1]["group_id"] + + +def test_batch_indexes_preserve_compiled_ids() -> None: + batches = build_source_batches(_requests()) + by_source = index_batch_ids_by_source(batches) + by_group = index_batch_ids_by_group(batches) + assert set(by_source) == {"entsoe", "opsd"} + assert len(by_source["entsoe"]) == 1 + assert len(by_source["opsd"]) == 1 + assert list(by_group.values())[0] == [batch["batch_id"] for batch in batches] + + +def test_serialize_batch_produces_json_safe_values() -> None: + batch = build_source_batches(_requests())[0] + serialized = serialize_batch(batch) + json.dumps(serialized) + assert isinstance(serialized["start"], str) + assert isinstance(serialized["end"], str) + + +def test_empty_execution_plan_has_stable_contract() -> None: + assert empty_execution_plan() == { + "version": EXECUTION_PLAN_VERSION, + "active_rule_names": [], + "rules": {}, + "batches": [], + "batch_ids_by_source": {}, + "groups": {}, + "constructed_profile_rule_names": [], + "external_profile_files": {}, + } + + +def test_load_execution_plan_and_get_batch(tmp_path) -> None: + batch = serialize_batch(build_source_batches(_requests())[0]) + plan = empty_execution_plan() + plan["batches"] = [batch] + path = tmp_path / "plan.json" + path.write_text(json.dumps(plan), encoding="utf-8") + + loaded = load_execution_plan(path) + selected = get_batch( + loaded, batch_id=batch["batch_id"], source=batch["source"] + ) + assert selected == batch + + +def test_get_batch_requires_exactly_one_match() -> None: + with pytest.raises(ValueError, match="Expected exactly one auxiliary batch"): + get_batch(empty_execution_plan(), batch_id="missing") diff --git a/tests/unit/test_advanced_plan.py b/tests/unit/test_advanced_plan.py deleted file mode 100644 index 0d4ead6..0000000 --- a/tests/unit/test_advanced_plan.py +++ /dev/null @@ -1,214 +0,0 @@ -"""Tests for advanced auxiliary-fill planning.""" - -import pandas as pd -from cleaning.advanced.planning.plan import build_auxiliary_fill_plan - -TARGET_COUNTRIES = ["ALB"] -TARGET_START = pd.Timestamp("2022-01-01", tz="UTC") -TARGET_END = pd.Timestamp("2025-01-01", tz="UTC") - - -def test_build_auxiliary_fill_plan_normalizes_rules() -> None: - """Build a normalized plan for active overrides.""" - rules = { - "external_albania": { - "country": "ALB", - "start": "2024-01-01", - "end": "2024-12-31 23:00", - "scope": "overwrite", - "method": "external_profile", - }, - "construct_albania": { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [ - {"country": "MNE", "start": "2023-01-01", "end": "2023-12-31 23:00"}, - { - "country": "MKD", - "start": "2023-01-01", - "end": "2023-12-31 23:00", - "weight": 2, - }, - ], - "scaling": { - "method": "match_energy", - "target_sources": [ - {"country": "ALB", "start": "2022-01-01", "end": "2022-12-31 23:00"} - ], - }, - }, - } - - result = build_auxiliary_fill_plan( - rules, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - expected = pd.DataFrame( - { - "rule_name": ["construct_albania", "external_albania"], - "country": ["ALB", "ALB"], - "target_start": [ - pd.Timestamp("2023-01-01", tz="UTC"), - pd.Timestamp("2024-01-01", tz="UTC"), - ], - "target_end": [ - pd.Timestamp("2023-12-31 23:00", tz="UTC"), - pd.Timestamp("2024-12-31 23:00", tz="UTC"), - ], - "scope": ["overwrite", "overwrite"], - "method": ["construct_from_sources", "external_profile"], - "status": ["ready", "ready"], - "source_count": [2, 0], - "scaling_method": ["match_energy", None], - } - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_build_auxiliary_fill_plan_returns_empty_schema() -> None: - """Return the expected empty plan structure when no overrides are configured.""" - result = build_auxiliary_fill_plan( - {}, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - expected = pd.DataFrame( - columns=[ - "rule_name", - "country", - "target_start", - "target_end", - "scope", - "method", - "status", - "source_count", - "scaling_method", - ] - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_build_auxiliary_fill_plan_ignores_wrong_country() -> None: - """Exclude overrides for countries outside the target scope.""" - rules = { - "albania": { - "country": "ALB", - "start": "2023-01-01", - "end": "2023-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], - }, - "montenegro": { - "country": "MNE", - "start": "2023-01-01", - "end": "2023-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2023-01-01", "end": "2023-02-01"}], - }, - } - - result = build_auxiliary_fill_plan( - rules, - target_countries=["ALB"], - target_start=TARGET_START, - target_end=TARGET_END, - ) - - assert result["rule_name"].tolist() == ["albania"] - - -def test_build_auxiliary_fill_plan_ignores_non_overlapping_periods() -> None: - """Exclude overrides whose periods do not intersect the target period.""" - rules = { - "before": { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2020-01-01", "end": "2020-02-01"}], - }, - "after": { - "country": "ALB", - "start": "2026-01-01", - "end": "2026-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2026-01-01", "end": "2026-02-01"}], - }, - } - - result = build_auxiliary_fill_plan( - rules, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - assert result.empty - - -def test_build_auxiliary_fill_plan_keeps_partial_overlap() -> None: - """Keep overrides that partially overlap the target period.""" - rules = { - "partial": { - "country": "ALB", - "start": "2021-12-01", - "end": "2022-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-02-01"}], - } - } - - result = build_auxiliary_fill_plan( - rules, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - assert result["rule_name"].tolist() == ["partial"] - - -def test_build_auxiliary_fill_plan_excludes_touching_periods() -> None: - """Treat touching half-open periods as non-overlapping.""" - rules = { - "ends_at_start": { - "country": "ALB", - "start": "2021-12-01", - "end": "2022-01-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2021-12-01", "end": "2022-01-01"}], - }, - "starts_at_end": { - "country": "ALB", - "start": "2025-01-01", - "end": "2025-02-01", - "scope": "overwrite", - "method": "construct_from_sources", - "sources": [{"country": "GBR", "start": "2025-01-01", "end": "2025-02-01"}], - }, - } - - result = build_auxiliary_fill_plan( - rules, - target_countries=TARGET_COUNTRIES, - target_start=TARGET_START, - target_end=TARGET_END, - ) - - assert result.empty diff --git a/tests/unit/test_advanced_requirements.py b/tests/unit/test_advanced_requirements.py deleted file mode 100644 index 1a52e57..0000000 --- a/tests/unit/test_advanced_requirements.py +++ /dev/null @@ -1,240 +0,0 @@ -"""Tests for compiling auxiliary-data requirements.""" - -import pandas as pd -from cleaning.advanced.planning.requirements import ( - REQUIREMENT_COLUMNS, - build_auxiliary_acquisition_requirements, - compile_auxiliary_requirements, - expand_auxiliary_requirements, - get_basic_cleaning_context, -) - - -def test_compile_auxiliary_requirements_collects_sources() -> None: - overrides = { - "reconstruct_albania": { - "country": "ALB", - "start": "2021-01-01", - "end": "2021-01-03", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - {"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"}, - { - "country": "MNE", - "start": "2020-01-01", - "end": "2020-01-03", - "weight": 2, - }, - ], - } - } - - result = compile_auxiliary_requirements(overrides) - - expected = pd.DataFrame( - { - "country": ["GRC", "MNE"], - "start": pd.to_datetime(["2019-01-01", "2020-01-01"], utc=True), - "end": pd.to_datetime(["2019-01-03", "2020-01-03"], utc=True), - } - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_compile_auxiliary_requirements_includes_scaling_sources() -> None: - overrides = { - "reconstruct_albania": { - "country": "ALB", - "start": "2021-01-01", - "end": "2021-01-03", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"}], - "scaling": { - "method": "match_energy", - "target_sources": [ - {"country": "ALB", "start": "2020-01-01", "end": "2020-01-03"} - ], - }, - } - } - - result = compile_auxiliary_requirements(overrides) - - assert list(result["country"]) == ["ALB", "GRC"] - - assert result.loc[result["country"] == "ALB", "start"].iloc[0] == pd.Timestamp( - "2020-01-01", tz="UTC" - ) - - -def test_compile_auxiliary_requirements_deduplicates_sources() -> None: - source = {"country": "GRC", "start": "2019-01-01", "end": "2019-01-03"} - - overrides = { - "first_rule": {"method": "construct_from_sources", "sources": [source]}, - "second_rule": {"method": "construct_from_sources", "sources": [source]}, - } - - result = compile_auxiliary_requirements(overrides) - - assert len(result) == 1 - - -def test_compile_auxiliary_requirements_ignores_other_methods() -> None: - overrides = { - "manual_case": {"method": "leave_missing"}, - "leave_case": {"method": "leave_missing"}, - } - - result = compile_auxiliary_requirements(overrides) - - assert result.empty - assert list(result.columns) == REQUIREMENT_COLUMNS - - -def test_compile_auxiliary_requirements_returns_empty_schema() -> None: - result = compile_auxiliary_requirements({}) - - assert result.empty - assert list(result.columns) == REQUIREMENT_COLUMNS - - -def test_compile_auxiliary_requirements_merges_overlapping_periods() -> None: - overrides = { - "first_rule": { - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-06-01"}], - }, - "second_rule": { - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2019-05-01", "end": "2019-12-01"}], - }, - } - - result = compile_auxiliary_requirements(overrides) - - assert len(result) == 1 - assert result.iloc[0]["start"] == pd.Timestamp("2019-01-01", tz="UTC") - assert result.iloc[0]["end"] == pd.Timestamp("2019-12-01", tz="UTC") - - -def test_compile_auxiliary_requirements_merges_adjacent_periods() -> None: - overrides = { - "first_rule": { - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2019-01-01", "end": "2019-02-01"}], - }, - "second_rule": { - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2019-02-01", "end": "2019-03-01"}], - }, - } - - result = compile_auxiliary_requirements(overrides) - - assert len(result) == 1 - assert result.iloc[0]["start"] == pd.Timestamp("2019-01-01", tz="UTC") - assert result.iloc[0]["end"] == pd.Timestamp("2019-03-01", tz="UTC") - - -def test_expand_requirements_without_rules_is_unchanged() -> None: - requirements = pd.DataFrame( - { - "country": ["GRC"], - "start": [pd.Timestamp("2020-01-01", tz="UTC")], - "end": [pd.Timestamp("2020-02-01", tz="UTC")], - } - ) - - result = expand_auxiliary_requirements(requirements, rules=[]) - - pd.testing.assert_frame_equal(result, requirements) - - -def test_expand_requirements_can_be_disabled() -> None: - requirements = pd.DataFrame( - { - "country": ["GRC"], - "start": [pd.Timestamp("2020-01-01", tz="UTC")], - "end": [pd.Timestamp("2020-02-01", tz="UTC")], - } - ) - - rules = [ - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - } - ] - - result = expand_auxiliary_requirements(requirements, rules=rules, enabled=False) - - pd.testing.assert_frame_equal(result, requirements) - - -def test_basic_context_for_copy_period() -> None: - rules = [ - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - } - ] - - left, right = get_basic_cleaning_context(rules) - - assert left == pd.Timedelta("7D") - assert right == pd.Timedelta("7D") - - -def test_basic_context_compounds_across_ordered_rules() -> None: - rules = [ - { - "name": "average_adjacent_weeks", - "method": "average_periods", - "max_gap": "168h", - "source_offsets": ["-168h", "168h"], - }, - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - }, - ] - - left, right = get_basic_cleaning_context(rules) - - assert left == pd.Timedelta("14D") - assert right == pd.Timedelta("7D") - - -def test_build_auxiliary_acquisition_requirements_uses_basic_cleaning_config() -> None: - overrides = { - "reconstruct_albania": { - "method": "construct_from_sources", - "sources": [{"country": "GRC", "start": "2020-01-01", "end": "2020-02-01"}], - } - } - - basic_rules = [ - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - } - ] - - result = build_auxiliary_acquisition_requirements( - overrides=overrides, basic_rules=basic_rules, basic_cleaning_enabled=True - ) - - assert result.iloc[0]["start"] == pd.Timestamp("2019-12-25", tz="UTC") - assert result.iloc[0]["end"] == pd.Timestamp("2020-02-08", tz="UTC") diff --git a/tests/unit/test_advanced_source_requests.py b/tests/unit/test_advanced_source_requests.py deleted file mode 100644 index 9807a01..0000000 --- a/tests/unit/test_advanced_source_requests.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Tests for auxiliary source-request planning.""" - -import pandas as pd -import pytest -from cleaning.advanced.planning.source_requests import ( - SOURCE_REQUEST_COLUMNS, - _build_batch_id, - _build_group_id, - build_auxiliary_source_batches, - build_auxiliary_source_requests, -) - - -def test_build_source_requests_uses_all_applicable_sources() -> None: - requirements = pd.DataFrame( - { - "country": ["GBR", "GRC"], - "start": pd.to_datetime(["2020-01-01", "2021-01-01"], utc=True), - "end": pd.to_datetime(["2020-02-01", "2021-02-01"], utc=True), - } - ) - - result = build_auxiliary_source_requests( - requirements, source_names=["entsoe", "neso", "opsd"] - ) - - assert list(result[["source", "country"]].itertuples(index=False, name=None)) == [ - ("entsoe", "GBR"), - ("entsoe", "GRC"), - ("neso", "GBR"), - ("opsd", "GBR"), - ("opsd", "GRC"), - ] - - -def test_neso_is_only_requested_for_gbr() -> None: - requirements = pd.DataFrame( - { - "country": ["GRC"], - "start": [pd.Timestamp("2020-01-01", tz="UTC")], - "end": [pd.Timestamp("2020-02-01", tz="UTC")], - } - ) - - result = build_auxiliary_source_requests(requirements, source_names=["neso"]) - - assert result.empty - assert list(result.columns) == SOURCE_REQUEST_COLUMNS - - -def test_empty_requirements_return_empty_source_request_schema() -> None: - requirements = pd.DataFrame(columns=["country", "start", "end"]) - - result = build_auxiliary_source_requests( - requirements, source_names=["entsoe", "neso", "opsd"] - ) - - assert result.empty - assert list(result.columns) == SOURCE_REQUEST_COLUMNS - - -def test_unknown_source_is_rejected_when_planning_request() -> None: - """Reject a source without defined country applicability.""" - requirements = pd.DataFrame( - { - "country": ["ALB"], - "start": [pd.Timestamp("2020-01-01", tz="UTC")], - "end": [pd.Timestamp("2020-02-01", tz="UTC")], - } - ) - - with pytest.raises(ValueError, match="Unsupported auxiliary load source"): - build_auxiliary_source_requests(requirements, source_names=["mystery_source"]) - - -def test_build_batch_id_is_independent_of_country_order() -> None: - first = _build_batch_id( - source="entsoe", - start=pd.Timestamp("2020-01-01", tz="UTC"), - end=pd.Timestamp("2020-02-01", tz="UTC"), - countries=["ALB", "GRC"], - ) - - second = _build_batch_id( - source="entsoe", - start=pd.Timestamp("2020-01-01", tz="UTC"), - end=pd.Timestamp("2020-02-01", tz="UTC"), - countries=["GRC", "ALB"], - ) - - assert first == second - - assert first.startswith("entsoe__20200101T0000__20200201T0000__") - - -def test_build_batch_id_changes_for_different_country_sets() -> None: - common = { - "source": "entsoe", - "start": pd.Timestamp("2020-01-01", tz="UTC"), - "end": pd.Timestamp("2020-02-01", tz="UTC"), - } - - first = _build_batch_id(**common, countries=["ALB"]) - - second = _build_batch_id(**common, countries=["ALB", "GRC"]) - - assert first != second - - -def test_build_group_id_depends_only_on_period() -> None: - start = pd.Timestamp("2020-01-01", tz="UTC") - end = pd.Timestamp("2020-02-01", tz="UTC") - - assert _build_group_id(start=start, end=end) == ("20200101T0000__20200201T0000") - - -def test_build_source_batches_groups_matching_periods() -> None: - requests = pd.DataFrame( - { - "source": ["entsoe", "entsoe", "entsoe"], - "country": ["ALB", "GRC", "MNE"], - "start": pd.to_datetime( - ["2020-01-01", "2020-01-01", "2021-01-01"], utc=True - ), - "end": pd.to_datetime(["2020-02-01", "2020-02-01", "2021-02-01"], utc=True), - } - ) - - result = build_auxiliary_source_batches(requests) - - first_start = pd.Timestamp("2020-01-01", tz="UTC") - first_end = pd.Timestamp("2020-02-01", tz="UTC") - second_start = pd.Timestamp("2021-01-01", tz="UTC") - second_end = pd.Timestamp("2021-02-01", tz="UTC") - - assert result == [ - { - "group_id": _build_group_id(start=first_start, end=first_end), - "batch_id": _build_batch_id( - source="entsoe", - start=first_start, - end=first_end, - countries=["ALB", "GRC"], - ), - "source": "entsoe", - "start": first_start, - "end": first_end, - "countries": ["ALB", "GRC"], - }, - { - "group_id": _build_group_id(start=second_start, end=second_end), - "batch_id": _build_batch_id( - source="entsoe", - start=second_start, - end=second_end, - countries=["MNE"], - ), - "source": "entsoe", - "start": second_start, - "end": second_end, - "countries": ["MNE"], - }, - ] - - -def test_build_source_batches_keeps_sources_separate() -> None: - start = pd.Timestamp("2020-01-01", tz="UTC") - end = pd.Timestamp("2020-02-01", tz="UTC") - - requests = pd.DataFrame( - { - "source": ["entsoe", "opsd"], - "country": ["GBR", "GBR"], - "start": [start, start], - "end": [end, end], - } - ) - - result = build_auxiliary_source_batches(requests) - - group_id = _build_group_id(start=start, end=end) - - assert result == [ - { - "group_id": group_id, - "batch_id": _build_batch_id( - source="entsoe", start=start, end=end, countries=["GBR"] - ), - "source": "entsoe", - "start": start, - "end": end, - "countries": ["GBR"], - }, - { - "group_id": group_id, - "batch_id": _build_batch_id( - source="opsd", start=start, end=end, countries=["GBR"] - ), - "source": "opsd", - "start": start, - "end": end, - "countries": ["GBR"], - }, - ] - - -def test_build_source_batches_returns_empty_list() -> None: - requests = pd.DataFrame(columns=SOURCE_REQUEST_COLUMNS) - - assert build_auxiliary_source_batches(requests) == [] diff --git a/tests/unit/test_cleaning_pipeline.py b/tests/unit/test_cleaning_pipeline.py deleted file mode 100644 index 0d3f0f2..0000000 --- a/tests/unit/test_cleaning_pipeline.py +++ /dev/null @@ -1,455 +0,0 @@ -"""Tests for the demand-cleaning pipeline.""" - -from __future__ import annotations - -import numpy as np -import pandas as pd -from cleaning.advanced.gap_report import build_gap_report -from cleaning.pipeline import clean_demand - - -def test_clean_demand_records_methods_and_ranks() -> None: - """Track observed sources, filling rules, and unresolved gaps.""" - index = pd.date_range(start="2017-01-01", periods=400, freq="h", tz="UTC") - - primary = pd.DataFrame({"AAA": np.arange(len(index), dtype=float)}, index=index) - - fallback = pd.DataFrame(np.nan, index=index, columns=["AAA"], dtype=float) - - # Four missing values at the start cannot be interpolated because - # the run exceeds max_gap and cannot be copied from a previous week. - unresolved_timestamps = index[0:4] - primary.loc[unresolved_timestamps, "AAA"] = np.nan - - # One primary-source gap is supplied directly by the fallback source. - fallback_timestamp = index[200] - primary.loc[fallback_timestamp, "AAA"] = np.nan - fallback.loc[fallback_timestamp, "AAA"] = 10_000.0 - - # A two-hour gap is filled by the first rule. - interpolation_timestamps = index[220:222] - primary.loc[interpolation_timestamps, "AAA"] = np.nan - - # A four-hour gap exceeds the interpolation limit but can be copied - # from the corresponding values seven days earlier. - copy_timestamps = index[250:254] - primary.loc[copy_timestamps, "AAA"] = np.nan - - sources = {"primary": primary, "fallback": fallback} - - gap_filling_config = { - "mode": "basic", - "basic": { - "rules": [ - { - "name": "interpolate_short_gaps", - "method": "linear_interpolation", - "max_gap": "3h", - }, - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - "require_complete_source": True, - }, - ] - }, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": {}, - }, - } - - ( - cleaned, - data_source, - cleaning_method, - cleaning_method_rank, - gap_report, - auxiliary_fill_plan, - ) = clean_demand( - sources, - source_priority=["primary", "fallback"], - gap_filling_config=gap_filling_config, - ) - - assert gap_report.empty - - # All outputs use the same grid. - for frame in [data_source, cleaning_method, cleaning_method_rank]: - assert frame.index.equals(cleaned.index) - assert frame.columns.equals(cleaned.columns) - - # Primary observations have rank 0. - primary_mask = primary.notna() & fallback.isna() - - assert (data_source.to_numpy()[primary_mask.to_numpy()] == "primary").all() - - assert ( - cleaning_method.to_numpy()[primary_mask.to_numpy()] == "observed_primary" - ).all() - - assert (cleaning_method_rank.to_numpy()[primary_mask.to_numpy()] == 0).all() - - # The fallback observation has rank 1. - assert cleaned.loc[fallback_timestamp, "AAA"] == 10_000.0 - - assert data_source.loc[fallback_timestamp, "AAA"] == "fallback" - - assert cleaning_method.loc[fallback_timestamp, "AAA"] == "observed_fallback" - - assert cleaning_method_rank.loc[fallback_timestamp, "AAA"] == 1 - - # The two-hour gap is filled by interpolation at rank 2. - assert cleaned.loc[interpolation_timestamps, "AAA"].notna().all() - - assert ( - cleaning_method.loc[interpolation_timestamps, "AAA"] - .eq("interpolate_short_gaps") - .all() - ) - - assert cleaning_method_rank.loc[interpolation_timestamps, "AAA"].eq(2).all() - - left_value = cleaned.loc[index[219], "AAA"] - right_value = cleaned.loc[index[222], "AAA"] - - expected_interpolation = np.linspace(left_value, right_value, 4)[1:3] - - np.testing.assert_allclose( - cleaned.loc[interpolation_timestamps, "AAA"].to_numpy(), expected_interpolation - ) - - # The four-hour gap is copied from seven days earlier at rank 3. - source_timestamps = copy_timestamps - pd.Timedelta(hours=168) - - np.testing.assert_allclose( - cleaned.loc[copy_timestamps, "AAA"].to_numpy(), - cleaned.loc[source_timestamps, "AAA"].to_numpy(), - ) - - assert cleaning_method.loc[copy_timestamps, "AAA"].eq("copy_previous_week").all() - - assert cleaning_method_rank.loc[copy_timestamps, "AAA"].eq(3).all() - - # The initial gap remains unresolved at the final rank. - assert cleaned.loc[unresolved_timestamps, "AAA"].isna().all() - - assert cleaning_method.loc[unresolved_timestamps, "AAA"].eq("missing").all() - - assert cleaning_method_rank.loc[unresolved_timestamps, "AAA"].eq(4).all() - - # Derived and unresolved values have no observed data source. - derived_or_missing = interpolation_timestamps.append(copy_timestamps).append( - unresolved_timestamps - ) - - assert data_source.loc[derived_or_missing, "AAA"].isna().all() - - expected_method_counts = { - "observed_primary": 389, - "observed_fallback": 1, - "interpolate_short_gaps": 2, - "copy_previous_week": 4, - "missing": 4, - } - - assert cleaning_method["AAA"].value_counts().to_dict() == expected_method_counts - - expected_rank_counts = {0: 389, 1: 1, 2: 2, 3: 4, 4: 4} - - assert ( - cleaning_method_rank["AAA"].value_counts().sort_index().to_dict() - == expected_rank_counts - ) - - -def test_advanced_mode_reports_unresolved_gaps() -> None: - """Report contiguous gaps remaining after basic gap filling.""" - index = pd.date_range(start="2017-01-01", periods=400, freq="h", tz="UTC") - - primary = pd.DataFrame({"AAA": np.arange(len(index), dtype=float)}, index=index) - - fallback = pd.DataFrame(np.nan, index=index, columns=["AAA"], dtype=float) - - unresolved_timestamps = index[0:4] - primary.loc[unresolved_timestamps, "AAA"] = np.nan - - sources = {"primary": primary, "fallback": fallback} - - gap_filling_config = { - "mode": "advanced", - "basic": { - "rules": [ - { - "name": "interpolate_short_gaps", - "method": "linear_interpolation", - "max_gap": "3h", - }, - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - "require_complete_source": True, - }, - ] - }, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": {}, - }, - } - - ( - cleaned, - _data_source, - cleaning_method, - _cleaning_method_rank, - gap_report, - auxiliary_fill_plan, - ) = clean_demand( - sources, - source_priority=["primary", "fallback"], - gap_filling_config=gap_filling_config, - ) - - assert auxiliary_fill_plan.empty - assert list(auxiliary_fill_plan.columns) == [ - "rule_name", - "country", - "target_start", - "target_end", - "scope", - "method", - "status", - "source_count", - "scaling_method", - ] - - assert cleaned.loc[unresolved_timestamps, "AAA"].isna().all() - - assert cleaning_method.loc[unresolved_timestamps, "AAA"].eq("missing").all() - - assert len(gap_report) == 1 - - row = gap_report.iloc[0] - - assert row["country"] == "AAA" - assert row["gap_start"] == index[0] - assert row["gap_end"] == index[4] - assert row["gap_hours"] == 4 - assert bool(row["touches_start_boundary"]) - assert not bool(row["touches_end_boundary"]) - - -def test_build_gap_report_identifies_contiguous_gaps_and_boundaries() -> None: - """Test the gap report captures gaps and boundaries as expected.""" - index = pd.date_range("2021-01-01 00:00", periods=8, freq="h", tz="UTC") - - load = pd.DataFrame( - { - "ALB": pd.array( - [pd.NA, 10.0, 11.0, pd.NA, pd.NA, 14.0, 15.0, 16.0], dtype="Float64" - ), - "TUR": pd.array([pd.NA] * 8, dtype="Float64"), - "MNE": pd.array( - [20.0, 21.0, 22.0, 23.0, 24.0, 25.0, pd.NA, pd.NA], dtype="Float64" - ), - }, - index=index, - ) - - result = build_gap_report(load, enabled=True) - - expected = pd.DataFrame( - { - "country": ["ALB", "ALB", "MNE", "TUR"], - "gap_start": [index[0], index[3], index[6], index[0]], - "gap_end": [ - index[1], - index[5], - index[7] + pd.Timedelta(hours=1), - index[7] + pd.Timedelta(hours=1), - ], - "gap_hours": [1, 2, 2, 8], - "touches_start_boundary": [True, False, False, True], - "touches_end_boundary": [False, False, True, True], - } - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_build_gap_report_returns_empty_report_when_disabled() -> None: - """Tests that gap report is empty when disabled.""" - result = build_gap_report(pd.DataFrame(), enabled=False) - - expected = pd.DataFrame( - columns=[ - "country", - "gap_start", - "gap_end", - "gap_hours", - "touches_start_boundary", - "touches_end_boundary", - ] - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_build_gap_report_returns_empty_report_when_no_gaps_remain() -> None: - """Tests that gap report is empty when there are no gaps.""" - index = pd.date_range("2021-01-01 00:00", periods=3, freq="h", tz="UTC") - - load = pd.DataFrame( - {"ALB": [10.0, 11.0, 12.0], "TUR": [20.0, 21.0, 22.0]}, index=index - ) - - result = build_gap_report(load, enabled=True) - - expected = pd.DataFrame( - columns=[ - "country", - "gap_start", - "gap_end", - "gap_hours", - "touches_start_boundary", - "touches_end_boundary", - ] - ) - - pd.testing.assert_frame_equal(result, expected) - - -def test_advanced_mode_builds_auxiliary_fill_plan() -> None: - """Tests construction of the auxiliary fill plan.""" - index = pd.date_range("2021-01-01", periods=3, freq="h", tz="UTC") - source = pd.DataFrame({"ALB": [1.0, None, 3.0]}, index=index) - - config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": { - "fill_albania_from_greece": { - "country": "ALB", - "start": "2021-01-01 01:00", - "end": "2021-01-01 01:00", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": "GRC", - "start": "2021-01-01 01:00", - "end": "2021-01-01 01:00", - } - ], - } - }, - }, - } - - ( - _cleaned, - _data_source, - _cleaning_method, - _cleaning_method_rank, - _gap_report, - auxiliary_fill_plan, - ) = clean_demand( - {"entsoe": source}, - source_priority=["entsoe"], - gap_filling_config=config, - ) - - assert len(auxiliary_fill_plan) == 1 - - row = auxiliary_fill_plan.iloc[0] - - assert row["rule_name"] == "fill_albania_from_greece" - assert row["country"] == "ALB" - assert row["method"] == "construct_from_sources" - assert row["status"] == "ready" - assert row["source_count"] == 1 - - -def test_clean_demand_filters_advanced_overrides_to_model_scope() -> None: - index = pd.date_range( - start="2022-01-01", end="2022-01-03 23:00", freq="h", tz="UTC" - ) - - source = pd.DataFrame({"ALB": range(len(index))}, index=index, dtype=float) - - sources = {"primary": source} - - gap_filling_config = { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": { - "active_albania": { - "country": "ALB", - "start": "2022-01-02", - "end": "2022-01-03", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2022-01-02", - "end": "2022-01-03", - "weight": 1, - } - ], - }, - "inactive_country": { - "country": "MNE", - "start": "2022-01-02", - "end": "2022-01-03", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2022-01-02", - "end": "2022-01-03", - "weight": 1, - } - ], - }, - "inactive_period": { - "country": "ALB", - "start": "2021-01-01", - "end": "2021-02-01", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": "GBR", - "start": "2021-01-01", - "end": "2021-02-01", - "weight": 1, - } - ], - }, - }, - }, - } - - ( - _cleaned, - _data_source, - _cleaning_method, - _cleaning_method_rank, - _gap_report, - auxiliary_fill_plan, - ) = clean_demand( - sources, source_priority=["primary"], gap_filling_config=gap_filling_config - ) - - assert auxiliary_fill_plan["rule_name"].tolist() == ["active_albania"] diff --git a/tests/unit/test_config_schema.py b/tests/unit/test_config_schema.py index 54e0577..646ba19 100644 --- a/tests/unit/test_config_schema.py +++ b/tests/unit/test_config_schema.py @@ -11,72 +11,41 @@ SCHEMA_PATH = REPOSITORY_ROOT / "workflow" / "internal" / "config.schema.yaml" -def test_default_config_matches_schema() -> None: - """Validate the default user configuration against its schema.""" - with CONFIG_PATH.open(encoding="utf-8") as config_file: - config = yaml.safe_load(config_file) - - with SCHEMA_PATH.open(encoding="utf-8") as schema_file: - schema = yaml.safe_load(schema_file) - - validator = Draft202012Validator(schema) - errors = sorted( - validator.iter_errors(config), key=lambda error: list(error.absolute_path) - ) - - assert not errors, "\n".join(_format_validation_error(error) for error in errors) - - def _format_validation_error(error: ValidationError) -> str: - """Format one schema-validation error with its config location.""" path = ".".join(str(part) for part in error.absolute_path) - return f"{path or ''}: {error.message}" -def test_advanced_auxiliary_basic_cleaning_can_be_disabled() -> None: - """Allow auxiliary basic cleaning to be disabled.""" +def _validate(config: dict) -> list[ValidationError]: with SCHEMA_PATH.open(encoding="utf-8") as schema_file: schema = yaml.safe_load(schema_file) + return list(Draft202012Validator(schema).iter_errors(config)) - config = { - "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, - "load_sources": ["entsoe"], - "gap_filling": { - "mode": "advanced", - "basic": {"rules": []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": False}}, - "overrides": {}, - }, - }, - } - - validator = Draft202012Validator(schema) - errors = list(validator.iter_errors(config)) +def test_default_config_matches_schema() -> None: + with CONFIG_PATH.open(encoding="utf-8") as config_file: + config = yaml.safe_load(config_file) + errors = _validate(config) assert not errors, "\n".join(_format_validation_error(error) for error in errors) -def test_advanced_mode_allows_no_rules_or_overrides() -> None: - """Allow advanced mode to run for diagnosis only.""" - with SCHEMA_PATH.open(encoding="utf-8") as schema_file: - schema = yaml.safe_load(schema_file) - +def test_advanced_mode_allows_no_sources_or_rules() -> None: config = { - "temporal_scope": {"start": "2021-01-01", "end": "2022-01-01"}, + "temporal_scope": { + "start": "2021-01-01", + "end": "2022-01-01", + "frequency": "1h", + }, "load_sources": ["entsoe"], "gap_filling": { "mode": "advanced", "basic": {"rules": []}, "advanced": { "auxiliary_data": {"basic_cleaning": {"enabled": True}}, - "overrides": {}, + "sources": {}, + "rules": [], }, }, } - - validator = Draft202012Validator(schema) - errors = list(validator.iter_errors(config)) - + errors = _validate(config) assert not errors, "\n".join(_format_validation_error(error) for error in errors) diff --git a/tests/unit/test_external_profile.py b/tests/unit/test_external_profile.py deleted file mode 100644 index 132366c..0000000 --- a/tests/unit/test_external_profile.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Tests for externally supplied demand profiles.""" - -import pandas as pd -import pytest -from cleaning.advanced.methods.external_profile import read_external_profile - - -def test_read_external_profile_reads_valid_csv(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text( - "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-01T01:00:00Z,110.0\n", - encoding="utf-8", - ) - - result = read_external_profile(path) - - expected = pd.Series( - [100.0, 110.0], - index=pd.DatetimeIndex( - ["2025-01-01T00:00:00Z", "2025-01-01T01:00:00Z"], name="timestamp" - ), - ) - - pd.testing.assert_series_equal(result, expected) - - -def test_read_external_profile_allows_sparse_timestamps(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text( - "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-03T12:00:00Z,200.0\n", - encoding="utf-8", - ) - - result = read_external_profile(path) - - assert len(result) == 2 - - assert result.loc[pd.Timestamp("2025-01-01T00:00:00Z")] == 100.0 - - assert result.loc[pd.Timestamp("2025-01-03T12:00:00Z")] == 200.0 - - -def test_read_external_profile_rejects_duplicate_timestamps(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text( - "timestamp,demand\n2025-01-01T00:00:00Z,100.0\n2025-01-01T00:00:00Z,110.0\n", - encoding="utf-8", - ) - - with pytest.raises(ValueError, match="timestamps must be unique"): - read_external_profile(path) - - -def test_read_external_profile_rejects_non_hourly_timestamps(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text("timestamp,demand\n2025-01-01T00:30:00Z,100.0\n", encoding="utf-8") - - with pytest.raises(ValueError, match="whole hours"): - read_external_profile(path) - - -def test_read_external_profile_rejects_non_numeric_values(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text( - "timestamp,demand\n2025-01-01T00:00:00Z,not-a-number\n", encoding="utf-8" - ) - - with pytest.raises(ValueError, match="Unable to parse string"): - read_external_profile(path) - - -def test_read_external_profile_rejects_wrong_columns(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text("datetime,demand\n2025-01-01T00:00:00Z,100.0\n", encoding="utf-8") - - with pytest.raises(ValueError, match="must contain exactly the columns"): - read_external_profile(path) - - -def test_read_external_profile_rejects_extra_columns(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text( - "timestamp,demand,comment\n2025-01-01T00:00:00Z,100.0,test\n", encoding="utf-8" - ) - - with pytest.raises(ValueError, match="must contain exactly the columns"): - read_external_profile(path) - - -def test_read_external_profile_rejects_missing_values(tmp_path): - path = tmp_path / "profile.csv" - - path.write_text("timestamp,demand\n2025-01-01T00:00:00Z,\n", encoding="utf-8") - - with pytest.raises(ValueError, match="must not be missing"): - read_external_profile(path) diff --git a/tests/unit/test_plan_auxiliary_data.py b/tests/unit/test_plan_auxiliary_data.py deleted file mode 100644 index 7b562e1..0000000 --- a/tests/unit/test_plan_auxiliary_data.py +++ /dev/null @@ -1,376 +0,0 @@ -"""Tests for compiling the advanced execution plan.""" - -import json - -import pandas as pd -import pytest -from cleaning.advanced.planning.execution import build_advanced_execution_plan -from cleaning.advanced.planning.source_requests import _build_batch_id, _build_group_id - - -def _config( - overrides: dict, - *, - mode: str = "advanced", - basic_cleaning_enabled: bool = False, - basic_rules: list[dict] | None = None, -) -> dict: - return { - "mode": mode, - "basic": {"rules": basic_rules or []}, - "advanced": { - "auxiliary_data": {"basic_cleaning": {"enabled": basic_cleaning_enabled}}, - "overrides": overrides, - }, - } - - -def _construct_override( - *, - source_country: str = "GRC", - source_start: str = "2020-01-01", - source_end: str = "2020-02-01", -) -> dict: - return { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps", - "method": "construct_from_sources", - "sources": [ - { - "country": source_country, - "start": source_start, - "end": source_end, - "weight": 1, - } - ], - } - - -def _external_profile_override( - *, country: str = "ALB", path: str = "resources/user/external_profiles/alb.csv" -) -> dict: - return { - "country": country, - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps", - "method": "external_profile", - "path": path, - } - - -def _fill_plan(rule_names: list[str]) -> pd.DataFrame: - if not rule_names: - return pd.DataFrame() - - return pd.DataFrame( - {"rule_name": rule_names, "status": ["ready"] * len(rule_names)} - ) - - -def _empty_execution_plan() -> dict: - return { - "version": 1, - "active_rule_names": [], - "rules": {}, - "batches": [], - "batch_ids_by_source": {}, - "groups": {}, - "constructed_profile_rule_names": [], - "external_profile_files": {}, - } - - -def test_plan_is_empty_outside_advanced_mode() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["example"]), - gap_filling_config=_config({}, mode="basic"), - source_names=["entsoe", "neso", "opsd"], - ) - - assert result == _empty_execution_plan() - - -def test_advanced_plan_with_empty_fill_plan_is_empty() -> None: - result = build_advanced_execution_plan( - fill_plan=pd.DataFrame(), - gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=["entsoe"], - ) - - assert result == _empty_execution_plan() - - -def test_plan_builds_complete_execution_manifest() -> None: - override = _construct_override() - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config({"fill_albania": override}), - source_names=["entsoe", "opsd"], - ) - - start = pd.Timestamp("2020-01-01", tz="UTC") - end = pd.Timestamp("2020-02-01", tz="UTC") - group_id = _build_group_id(start=start, end=end) - entsoe_batch_id = _build_batch_id( - source="entsoe", start=start, end=end, countries=["GRC"] - ) - opsd_batch_id = _build_batch_id( - source="opsd", start=start, end=end, countries=["GRC"] - ) - - assert result == { - "version": 1, - "active_rule_names": ["fill_albania"], - "rules": { - "fill_albania": {"override": override, "required_group_ids": [group_id]} - }, - "batches": [ - { - "group_id": group_id, - "batch_id": entsoe_batch_id, - "source": "entsoe", - "start": "2020-01-01T00:00:00+00:00", - "end": "2020-02-01T00:00:00+00:00", - "countries": ["GRC"], - "years": [2020], - }, - { - "group_id": group_id, - "batch_id": opsd_batch_id, - "source": "opsd", - "start": "2020-01-01T00:00:00+00:00", - "end": "2020-02-01T00:00:00+00:00", - "countries": ["GRC"], - "years": [2020], - }, - ], - "batch_ids_by_source": { - "entsoe": [entsoe_batch_id], - "opsd": [opsd_batch_id], - }, - "groups": {group_id: [entsoe_batch_id, opsd_batch_id]}, - "constructed_profile_rule_names": ["fill_albania"], - "external_profile_files": {}, - } - - -def test_plan_uses_only_overrides_in_fill_plan() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["active"]), - gap_filling_config=_config( - { - "active": _construct_override(source_country="GRC"), - "inactive": _construct_override(source_country="SRB"), - } - ), - source_names=["entsoe"], - ) - - assert result["active_rule_names"] == ["active"] - assert list(result["rules"]) == ["active"] - assert result["constructed_profile_rule_names"] == ["active"] - assert all("GRC" in batch["countries"] for batch in result["batches"]) - assert all("SRB" not in batch["countries"] for batch in result["batches"]) - - -def test_plan_preserves_configured_override_order() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["second", "first"]), - gap_filling_config=_config( - { - "first": _construct_override(source_country="GRC"), - "second": _construct_override(source_country="SRB"), - } - ), - source_names=["entsoe"], - ) - - assert result["active_rule_names"] == ["first", "second"] - assert list(result["rules"]) == ["first", "second"] - assert result["constructed_profile_rule_names"] == ["first", "second"] - - -def test_plan_resolves_scaling_target_sources_to_groups() -> None: - override = _construct_override() - override["scaling"] = { - "method": "match_energy", - "target_sources": [ - {"country": "ALB", "start": "2020-03-01", "end": "2020-04-01", "weight": 1} - ], - } - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config({"fill_albania": override}), - source_names=["entsoe"], - ) - - expected_group_ids = sorted( - [ - _build_group_id( - start=pd.Timestamp("2020-01-01", tz="UTC"), - end=pd.Timestamp("2020-02-01", tz="UTC"), - ), - _build_group_id( - start=pd.Timestamp("2020-03-01", tz="UTC"), - end=pd.Timestamp("2020-04-01", tz="UTC"), - ), - ] - ) - - assert result["rules"]["fill_albania"]["required_group_ids"] == expected_group_ids - - -def test_plan_resolves_rule_to_expanded_basic_cleaning_group() -> None: - override = _construct_override() - basic_rules = [ - { - "name": "copy_previous_week", - "method": "copy_period", - "max_gap": "168h", - "source_offset": "-168h", - "require_complete_source": True, - } - ] - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - {"fill_albania": override}, - basic_cleaning_enabled=True, - basic_rules=basic_rules, - ), - source_names=["entsoe"], - ) - - batch = result["batches"][0] - - assert batch["start"] < "2020-01-01T00:00:00+00:00" - assert batch["end"] > "2020-02-01T00:00:00+00:00" - assert result["rules"]["fill_albania"]["required_group_ids"] == [batch["group_id"]] - - -def test_non_construct_rule_requires_no_auxiliary_groups() -> None: - override = { - "country": "ALB", - "start": "2020-01-01", - "end": "2020-02-01", - "scope": "fill_gaps", - "method": "leave_missing", - } - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["leave_albania"]), - gap_filling_config=_config({"leave_albania": override}), - source_names=["entsoe"], - ) - - assert result["rules"] == { - "leave_albania": {"override": override, "required_group_ids": []} - } - assert result["batches"] == [] - assert result["groups"] == {} - assert result["constructed_profile_rule_names"] == [] - - -def test_batch_years_use_half_open_period_semantics() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config( - { - "fill_albania": _construct_override( - source_country="GBR", - source_start="2020-12-31", - source_end="2021-01-01", - ) - } - ), - source_names=["neso"], - ) - - assert result["batches"][0]["years"] == [2020] - - -def test_plan_rejects_unresolvable_auxiliary_group() -> None: - with pytest.raises( - ValueError, match="Expected exactly one auxiliary group covering" - ): - build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=[], - ) - - -def test_plan_is_json_serializable() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["fill_albania"]), - gap_filling_config=_config({"fill_albania": _construct_override()}), - source_names=["entsoe"], - ) - - json.dumps(result) - - -def test_plan_records_active_external_profile_file() -> None: - override = _external_profile_override( - path="resources/user/external_profiles/alb_2020.csv" - ) - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["external_albania"]), - gap_filling_config=_config({"external_albania": override}), - source_names=["entsoe"], - ) - - assert result["external_profile_files"] == { - "external_albania": ("resources/user/external_profiles/alb_2020.csv") - } - - assert result["constructed_profile_rule_names"] == [] - assert result["batches"] == [] - - -def test_plan_excludes_inactive_external_profile_file() -> None: - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["active"]), - gap_filling_config=_config( - { - "active": _external_profile_override(path="resources/user/active.csv"), - "inactive": _external_profile_override( - path="resources/user/inactive.csv" - ), - } - ), - source_names=["entsoe"], - ) - - assert result["external_profile_files"] == {"active": "resources/user/active.csv"} - - -def test_plan_allows_external_profile_file_reuse() -> None: - shared_path = "resources/user/external_profiles/gbr_2000_2025.csv" - - result = build_advanced_execution_plan( - fill_plan=_fill_plan(["gbr_period_one", "gbr_period_two"]), - gap_filling_config=_config( - { - "gbr_period_one": _external_profile_override( - country="GBR", path=shared_path - ), - "gbr_period_two": _external_profile_override( - country="GBR", path=shared_path - ), - } - ), - source_names=["entsoe"], - ) - - assert result["external_profile_files"] == { - "gbr_period_one": shared_path, - "gbr_period_two": shared_path, - } diff --git a/tests/unit/test_provenance.py b/tests/unit/test_provenance.py deleted file mode 100644 index 4a8acee..0000000 --- a/tests/unit/test_provenance.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests for provenance rules.""" - -from cleaning.provenance import build_final_cleaning_rules - - -def test_build_final_cleaning_rules_includes_advanced_overrides(): - config = { - "mode": "advanced", - "basic": {"rules": [{"name": "basic_rule", "method": "linear_interpolation"}]}, - "advanced": { - "overrides": {"advanced_rule": {"method": "construct_from_sources"}} - }, - } - - rules = build_final_cleaning_rules(config) - - assert [rule["name"] for rule in rules] == ["basic_rule", "advanced_rule"] - - -def test_build_final_cleaning_rules_excludes_advanced_overrides_in_basic_mode(): - config = { - "mode": "basic", - "basic": {"rules": [{"name": "basic_rule", "method": "linear_interpolation"}]}, - "advanced": { - "overrides": {"advanced_rule": {"method": "construct_from_sources"}} - }, - } - - rules = build_final_cleaning_rules(config) - - assert [rule["name"] for rule in rules] == ["basic_rule"] diff --git a/tests/unit/test_schemas.py b/tests/unit/test_schemas.py new file mode 100644 index 0000000..d9fe518 --- /dev/null +++ b/tests/unit/test_schemas.py @@ -0,0 +1,33 @@ +"""Tests for module/provider-specific Pandera schemas.""" + +import pandas as pd +import pytest +from _schemas import OPSDLoad, Shapes +from pandera.errors import SchemaError + + +def test_opsd_schema_accepts_provider_rows() -> None: + frame = pd.DataFrame( + { + "region": ["GB"], + "variable": ["load"], + "attribute": ["actual"], + "utc_timestamp": [pd.Timestamp("2020-01-01T00:00:00Z")], + "data": [42.0], + } + ) + validated = OPSDLoad.validate(frame) + assert validated.loc[0, "data"] == 42.0 + + +def test_shapes_schema_rejects_unknown_shape_class() -> None: + frame = pd.DataFrame( + { + "shape_id": ["x"], + "country_id": ["GBR"], + "shape_class": ["unknown"], + "geometry": [None], + } + ) + with pytest.raises(SchemaError): + Shapes.validate(frame) diff --git a/tests/unit/test_tclean_config.py b/tests/unit/test_tclean_config.py new file mode 100644 index 0000000..4e953c7 --- /dev/null +++ b/tests/unit/test_tclean_config.py @@ -0,0 +1,57 @@ +"""Tests for the Modelblocks-to-T-Clean configuration adapter.""" + +import pandas as pd +from _tclean_config import build_advanced_rules, build_basic_rules, build_time_grid +from tclean import TimeGrid + + +def test_build_time_grid_translates_temporal_scope() -> None: + grid = build_time_grid( + { + "start": "2020-01-01T00:30:00Z", + "end": "2020-01-01T03:30:00Z", + "frequency": "1h", + } + ) + assert isinstance(grid, TimeGrid) + assert grid.start == pd.Timestamp("2020-01-01T00:30:00Z") + assert grid.end == pd.Timestamp("2020-01-01T03:30:00Z") + assert grid.frequency == pd.Timedelta("1h") + + +def test_build_basic_rules_returns_no_rules_when_mode_is_off() -> None: + config = {"mode": "off", "basic": {"rules": []}} + assert build_basic_rules(config) == [] + + +def test_build_basic_rules_preserves_configured_rule_order() -> None: + rules = [ + {"name": "first", "method": "linear_interpolation", "max_gap": "2h"}, + { + "name": "second", + "method": "copy_periods", + "max_gap": "4h", + "source_offsets": ["-24h"], + }, + ] + config = {"mode": "basic", "basic": {"rules": rules}} + assert build_basic_rules(config) == rules + + +def test_build_advanced_rules_returns_canonical_columns_when_empty() -> None: + config = { + "mode": "advanced", + "basic": {"rules": []}, + "advanced": {"sources": {}, "rules": []}, + } + result = build_advanced_rules(config) + assert result.empty + assert list(result.columns) == [ + "rule_name", + "method", + "source", + "context", + "start", + "end", + "scope", + ] diff --git a/tests/unit/test_validate_config.py b/tests/unit/test_validate_config.py deleted file mode 100644 index 81ea4ee..0000000 --- a/tests/unit/test_validate_config.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Tests for semantic configuration validation.""" - -import pytest -from common.config_validation import validate_config_semantics - - -def _config() -> dict: - """Return a structurally valid configuration for semantic validation.""" - return { - "temporal_scope": {"start": "2022-01-01", "end": "2023-01-01"}, - "load_sources": ["entsoe", "neso"], - "gap_filling": {"basic": {"rules": []}, "advanced": {"overrides": {}}}, - } - - -def test_accepts_valid_config() -> None: - """Accept a configuration with valid semantic relationships.""" - validate_config_semantics(_config()) - - -def test_rejects_empty_temporal_scope() -> None: - """Reject a temporal scope whose start and end are identical.""" - config = _config() - config["temporal_scope"]["end"] = config["temporal_scope"]["start"] - - with pytest.raises(ValueError, match="Temporal scope"): - validate_config_semantics(config) - - -def test_rejects_reversed_override_period() -> None: - """Reject an advanced override whose end precedes its start.""" - config = _config() - config["gap_filling"]["advanced"]["overrides"] = { - "bad_period": { - "country": "ALB", - "start": "2024-02-01", - "end": "2024-01-01", - "scope": "fill_gaps", - "method": "leave_missing", - } - } - - with pytest.raises(ValueError, match="bad_period"): - validate_config_semantics(config) - - -def test_rejects_basic_rule_name_reserved_for_observed_source() -> None: - """Reject a basic rule name reserved for observed-source provenance.""" - config = _config() - config["gap_filling"]["basic"]["rules"] = [ - { - "name": "observed_entsoe", - "method": "linear_interpolation", - "max_gap": "3h", - } - ] - - with pytest.raises(ValueError, match="conflict with reserved"): - validate_config_semantics(config) - - -def test_rejects_advanced_rule_name_missing() -> None: - """Reject an advanced override using the reserved missing provenance name.""" - config = _config() - config["gap_filling"]["advanced"]["overrides"] = { - "missing": { - "country": "ALB", - "start": "2022-03-01", - "end": "2022-04-01", - "scope": "fill_gaps", - "method": "leave_missing", - } - } - - with pytest.raises(ValueError, match="conflict with reserved"): - validate_config_semantics(config) diff --git a/tests/unit/test_workflow_environment_boundary.py b/tests/unit/test_workflow_environment_boundary.py index f6a589a..f9fdff3 100644 --- a/tests/unit/test_workflow_environment_boundary.py +++ b/tests/unit/test_workflow_environment_boundary.py @@ -1,14 +1,10 @@ -"""Tests for the Snakemake host/module environment boundary.""" +"""Tests for workflow architecture and Snakemake host boundaries.""" import re from pathlib import Path -FORBIDDEN_PATTERNS = ( +FORBIDDEN_HOST_PATTERNS = ( r"\bsys\.path\b", - r"^\s*from\s+cleaning\b", - r"^\s*import\s+cleaning\b", - r"^\s*from\s+common\b", - r"^\s*import\s+common\b", r"^\s*import\s+pandas\b", r"^\s*from\s+pandas\b", r"^\s*import\s+numpy\b", @@ -19,23 +15,34 @@ r"^\s*from\s+rioxarray\b", r"^\s*import\s+pandera\b", r"^\s*from\s+pandera\b", + r"^\s*import\s+tclean\b", + r"^\s*from\s+tclean\b", ) -def test_snakemake_host_code_has_no_module_runtime_dependencies(): - """Ensure host-side workflow code avoids module runtime packages.""" +def test_snakemake_host_code_has_no_module_runtime_dependencies() -> None: workflow_files = [Path("workflow/Snakefile"), *Path("workflow/rules").glob("*.smk")] - violations = [] - for path in workflow_files: text = path.read_text(encoding="utf-8") - - for pattern in FORBIDDEN_PATTERNS: + for pattern in FORBIDDEN_HOST_PATTERNS: if re.search(pattern, text, flags=re.MULTILINE): violations.append(f"{path}: {pattern}") - assert not violations, ( - "Snakemake host code must not depend on module runtime " - "packages:\n" + "\n".join(violations) + "Snakemake host code must not depend on module runtime packages:\n" + + "\n".join(violations) ) + + +def test_removed_legacy_packages_are_not_reintroduced() -> None: + assert not Path("workflow/scripts/cleaning").exists() + assert not Path("workflow/scripts/common").exists() + + violations = [] + for path in Path("workflow/scripts").rglob("*.py"): + text = path.read_text(encoding="utf-8") + if re.search(r"^\s*(?:from|import)\s+cleaning\b", text, re.MULTILINE): + violations.append(str(path)) + if re.search(r"^\s*(?:from|import)\s+common\b", text, re.MULTILINE): + violations.append(str(path)) + assert not violations, "Legacy package imports remain:\n" + "\n".join(violations) From 0020a6f30281118d5e4af2bc21b73a808c04c50c Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 15:17:57 +0200 Subject: [PATCH 120/241] Plot fixes --- workflow/scripts/_plot_timeline.py | 2 +- workflow/scripts/plot_readme_cleaning_example.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/_plot_timeline.py b/workflow/scripts/_plot_timeline.py index 075faea..fd845c4 100644 --- a/workflow/scripts/_plot_timeline.py +++ b/workflow/scripts/_plot_timeline.py @@ -389,7 +389,7 @@ def _build_cleaning_method_metadata( def _format_source_name(source_name: str) -> str: - mapping = {"entsoe_api": "ENTSO-E", "neso": "NESO", "opsd_api": "OPSD"} + mapping = {"entsoe": "ENTSO-E", "neso": "NESO", "opsd": "OPSD"} return mapping.get(source_name, source_name) diff --git a/workflow/scripts/plot_readme_cleaning_example.py b/workflow/scripts/plot_readme_cleaning_example.py index 3cb995f..436bbad 100644 --- a/workflow/scripts/plot_readme_cleaning_example.py +++ b/workflow/scripts/plot_readme_cleaning_example.py @@ -56,13 +56,13 @@ }, { "name": "copy_previous_week", - "method": "copy_period", + "method": "copy_periods", "max_gap": "168h", "source_offset": "-168h", }, { "name": "copy_following_week", - "method": "copy_period", + "method": "copy_periods", "max_gap": "168h", "source_offset": "168h", }, From fe47121c4f0219f4fe046bb2ab77ba22c708273b Mon Sep 17 00:00:00 2001 From: Dano Hawkins Date: Tue, 25 Aug 2026 15:22:50 +0200 Subject: [PATCH 121/241] Updated READMEs --- README.md | 162 ++++++++++------- config/README.md | 461 ++++++++++++++++++++++++----------------------- 2 files changed, 338 insertions(+), 285 deletions(-) diff --git a/README.md b/README.md index 349faa1..478bf48 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ # European electricity demand -This module prepares hourly electricity-demand time series for European regions at arbitrary spatial resolution. National demand data from multiple sources are combined and cleaned before being spatially disaggregated using population data and aggregated to user-provided target regions. +This Modelblocks module prepares regular electricity-demand time series for European target regions. National demand observations from multiple providers are combined and cleaned on a user-defined time grid, then spatially disaggregated using population data and aggregated to user-provided shapes. -The module supports configurable gap filling, including deterministic cleaning rules and advanced country- and period-specific strategies, whilst retaining provenance for observed and filled demand values. - - - +Demand cleaning is performed with T-Clean, while this module remains responsible for electricity-demand providers, Modelblocks configuration, auxiliary-data acquisition, workflow orchestration, spatial disaggregation, and diagnostic outputs.