From 5c5e28edcd0fdd9fe7c2c0fdd2b50d3f10f3fb6d Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Fri, 3 Jan 2025 13:05:38 -0800 Subject: [PATCH 01/75] fix plot bug --- workflow/scripts/plot_statistics.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/workflow/scripts/plot_statistics.py b/workflow/scripts/plot_statistics.py index d39e5209a..f9763751c 100644 --- a/workflow/scripts/plot_statistics.py +++ b/workflow/scripts/plot_statistics.py @@ -353,7 +353,12 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): # Set up the figure and axes fig, axes = plt.subplots(rows, columns, figsize=(columns * 2.5, rows * 5), sharex=True, sharey=True) - axes = axes.flatten() + + # Ensure axes is a flattened array for consistent indexing + if num_regions == 1: + axes = [axes] # Wrap single Axes object in a list + else: + axes = axes.flatten() for i, region in enumerate(regions): region_data = data.loc[region] @@ -369,9 +374,12 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): axes[i].set_ylabel(ylabel) axes[i].set_xlabel("") - for j in range(i + 1, len(axes)): - fig.delaxes(axes[j]) + # Remove unused subplots + if num_regions > 1: + for j in range(i + 1, len(axes)): + fig.delaxes(axes[j]) + # Create legend handles, labels = [], [] for carrier in data.reset_index().Carrier.unique(): handle = plt.Rectangle((0, 0), 1, 1, color=palette[carrier]) @@ -424,7 +432,12 @@ def plot_regional_emissions_bar( # Set up the figure and axes fig, axes = plt.subplots(rows, columns, figsize=(columns * 2.5, rows * 5), sharex=True, sharey=True) - axes = axes.flatten() + + # Ensure axes is a flattened array for consistent indexing + if num_regions == 1: + axes = [axes] # Wrap single Axes object in a list + else: + axes = axes.flatten() for i, region in enumerate(regions): region_data = regional_emissions.loc[region] From 1a30350f7d759922340dd3632834c4d37ae349cc Mon Sep 17 00:00:00 2001 From: Kamran Date: Mon, 6 Jan 2025 19:16:40 -0800 Subject: [PATCH 02/75] Update mem reqs, fix TCT bug --- workflow/rules/common.smk | 6 +++--- workflow/run_slurm.sh | 2 +- workflow/scripts/cluster_network.py | 2 +- workflow/scripts/solve_network.py | 7 ++++++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index 509a32882..e87888106 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -86,7 +86,7 @@ def solver_threads(w): def memory(w): - factor = 10.0 + factor = 4.0 for o in w.opts.split("-"): m = re.match(r"^(\d+)h$", o, re.IGNORECASE) if m is not None: @@ -98,7 +98,7 @@ def memory(w): factor *= int(m.group(1)) / 8760 break if w.clusters.endswith("m") or w.clusters.endswith("c"): - val = int(factor * (55000 + 100 * int(w.simpl) + 195 * int(w.clusters[:-1]))) + val = int(factor * (50000 + 30 * int(w.simpl) + 195 * int(w.clusters[:-1]))) elif w.clusters == "all": val = int(factor * (18000 + 180 * 4000)) else: @@ -143,7 +143,7 @@ def interconnect_mem_s(w): def interconnect_mem_c(w): - mem = 10000 * len(config_provider("scenario", "planning_horizons")(w)) + mem = 1000 * len(config_provider("scenario", "planning_horizons")(w)) if w.interconnect == "usa": return int(mem * 4) elif w.interconnect == "eastern": diff --git a/workflow/run_slurm.sh b/workflow/run_slurm.sh index 018a238a8..bf8dcb83b 100644 --- a/workflow/run_slurm.sh +++ b/workflow/run_slurm.sh @@ -1,3 +1,3 @@ # SLURM specifications made in default.cluster.yaml & the individual rules # GRB_LICENSE_FILE=/share/software/user/restricted/gurobi/11.0.2/licenses/gurobi.lic⁠ -snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL --mail-user {cluster.email} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/config.egs_study.yaml +snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL --mail-user {cluster.email} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/base_paper/config.base.yaml \ No newline at end of file diff --git a/workflow/scripts/cluster_network.py b/workflow/scripts/cluster_network.py index faf8c4e80..e4d0cde5c 100644 --- a/workflow/scripts/cluster_network.py +++ b/workflow/scripts/cluster_network.py @@ -511,7 +511,7 @@ def convert_to_transport( itl_agg = pd.concat([itl_agg, itls_to_virtual]) itl_agg_costs = None if itl_agg_costs_fn is None else pd.concat([itl_cost, pd.read_csv(itl_agg_costs_fn)]) - add_itls(buses, itl_agg, itl_agg_costs, expansion=False) + add_itls(buses, itl_agg, itl_agg_costs, expansion=True) itls = pd.concat([itls_filt, itl_agg]) else: itls = itls_filt diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index d71109d0a..09921f268 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -183,7 +183,7 @@ def prepare_network( def add_technology_capacity_target_constraints(n, config): """ - Add Technology Capacaity Target (TCT) constraint to the network. + Add Technology Capacity Target (TCT) constraint to the network. Add minimum or maximum levels of generator nominal capacity per carrier for individual regions. Each constraint can be designated for a specified planning horizon in multi-period models. Opts and path for technology_capacity_targets.csv must be defined in config.yaml. Default file is available at config/policy_constraints/technology_capacity_targets.csv. @@ -281,8 +281,13 @@ def add_technology_capacity_target_constraints(n, config): if target["max"] == "existing": target["max"] = lhs_extendable + else: + target["max"] = float(target["max"]) + if target["min"] == "existing": target["min"] = lhs_extendable + else: + target["min"] = float(target["min"]) if not np.isnan(target["min"]): From 610bf0b1eb30896268c8a36d803d1e09bce7dd9b Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 6 Jan 2025 19:19:23 -0800 Subject: [PATCH 03/75] update scenario comparison --- workflow/scripts/scenario_comparison.py | 66 ++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/workflow/scripts/scenario_comparison.py b/workflow/scripts/scenario_comparison.py index 4783c0a58..00e93ad74 100644 --- a/workflow/scripts/scenario_comparison.py +++ b/workflow/scripts/scenario_comparison.py @@ -120,7 +120,18 @@ def process_data(data, alias_dict=None, new_order=None): # Plot comparison -def scenario_comparison(stats, variable, variable_units, carriers, title, figures_path, include_link=False): +def scenario_comparison( + stats, + variable, + variable_units, + carriers, + title, + figures_path, + include_link=False, + as_pct=False, + reference_scenario=None, +): + combined_df = pd.DataFrame(columns=["Scenario", "horizon", "nice_name", "statistics"], index=[]) colors = carriers["color"] planning_horizons = stats[next(iter(stats.keys()))]["statistics"][variable].columns fig, axes = plt.subplots( @@ -129,6 +140,12 @@ def scenario_comparison(stats, variable, variable_units, carriers, title, figure figsize=(8, 4.2 * len(planning_horizons)), sharex=True, ) + if variable_units in ["GW", "GWh"]: + factor_units = 1e3 + elif variable_units in ["%"]: + factor_units = 1 + else: + factor_units = 1e9 if len(planning_horizons) == 1: axes = [axes] @@ -145,11 +162,18 @@ def scenario_comparison(stats, variable, variable_units, carriers, title, figure df = df.loc[df.index.get_level_values(0).isin(["Generator", "StorageUnit"]), variable] df.index = df.index.get_level_values(1) df = df.reindex(carriers.index).dropna() + if as_pct: + df = ((df / df.sum()) * 100).round(2) for i, tech in enumerate(df.index.unique()): - values = df.loc[tech, horizon] / (1e3 if variable_units in ["GW", "GWh"] else 1e9) + values = df.loc[tech, horizon] / factor_units ax.barh(y_positions[j], values, left=bottoms[j], color=colors[tech], label=tech if j == 0 else "") bottoms[j] += values + df[["Scenario", "horizon"]] = scenario, horizon + df = df.reset_index() + df.rename(columns={horizon: "statistics"}, inplace=True) + combined_df = pd.concat([combined_df, df[["Scenario", "nice_name", "statistics", "horizon"]]]) + ax.text(1.01, 0.5, f"{horizon}", transform=ax.transAxes, va="center", rotation="vertical") ax.set_yticks(y_positions) ax.set_yticklabels(stats.keys()) @@ -170,7 +194,27 @@ def scenario_comparison(stats, variable, variable_units, carriers, title, figure fig.suptitle(title, fontsize=12, fontweight="bold") plt.tight_layout() plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") - return fig, axes + + if reference_scenario: + combined_df = combined_df.set_index("Scenario") + combined_df = combined_df.query("horizon == @horizon").drop(columns="horizon") # only plot last horizon + ref = combined_df.loc[reference_scenario].set_index("nice_name") + combined_df = combined_df.reset_index().set_index("nice_name") + for scenario in combined_df.index.unique(): + combined_df.loc[scenario, "statistics"] = ( + (combined_df.loc[scenario, "statistics"] - ref.loc[scenario, "statistics"]) / ref.sum().values * 100 + ) + combined_df = combined_df.reset_index().set_index("Scenario") + stacked_data = combined_df.reset_index().pivot(index="Scenario", columns="nice_name", values="statistics") + stacked_data.plot( + kind="bar", + stacked=True, + figsize=(10, 7), + color=[colors[tech] for tech in stacked_data.columns], + legend=False, + ) + plt.ylabel("∆ Capacity[%]") + plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path, reference_scenario=None): @@ -203,7 +247,7 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path ref = combined_df.loc[reference_scenario] pct_df = (combined_df - ref) / combined_df * 100 pct_df.plot(kind="bar", y="statistics", title="Total System Costs", legend=False) - plt.ylabel(" Reduction in Annualized System Costs [%]") + plt.ylabel("∆ Annualized System Costs [%]") plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") @@ -238,15 +282,23 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path title = "Capacity Evolution Comparison" # Generate plots - scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path) + scenario_comparison( + processed_data, + variable, + variable_units, + carriers, + title, + figures_path, + reference_scenario=reference_scenario, + ) # Example variable and title variable = "Supply" - variable_units = "GWh" + variable_units = "%" title = "Supply Evolution Comparison" # Generate plots - scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path) + scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=True) # Example variable and title variable = "System Costs" From d85e06388241edf84b46c62985c0057454f47319 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Tue, 7 Jan 2025 13:55:48 -0800 Subject: [PATCH 04/75] update get_region_buses to include reeds zone id --- workflow/scripts/scenario_comparison.py | 23 ++++++++++++++++++++--- workflow/scripts/solve_network.py | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/scenario_comparison.py b/workflow/scripts/scenario_comparison.py index 00e93ad74..8997a6967 100644 --- a/workflow/scripts/scenario_comparison.py +++ b/workflow/scripts/scenario_comparison.py @@ -137,7 +137,7 @@ def scenario_comparison( fig, axes = plt.subplots( nrows=len(planning_horizons), ncols=1, - figsize=(8, 4.2 * len(planning_horizons)), + figsize=(8, 3 * len(planning_horizons)), sharex=True, ) if variable_units in ["GW", "GWh"]: @@ -215,6 +215,7 @@ def scenario_comparison( ) plt.ylabel("∆ Capacity[%]") plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + combined_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path, reference_scenario=None): @@ -279,7 +280,7 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path # Example variable and title variable = "Optimal Capacity" variable_units = "GW" - title = "Capacity Evolution Comparison" + title = "Capacity Comparison" # Generate plots scenario_comparison( @@ -295,11 +296,27 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path # Example variable and title variable = "Supply" variable_units = "%" - title = "Supply Evolution Comparison" + title = "Supply Comparison" # Generate plots scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=True) + # Example variable and title + variable = "Capital Expenditure" + variable_units = "$B" + title = "CAPEX Comparison" + + # Generate plots + scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=False) + + # Example variable and title + variable = "Operational Expenditure" + variable_units = "$B" + title = "OPEX Comparison" + + # Generate plots + scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=False) + # Example variable and title variable = "System Costs" variable_units = "$B" diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 09921f268..01fe1dff8 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -50,6 +50,7 @@ def get_region_buses(n, region_list): return n.buses[ ( n.buses.country.isin(region_list) + | n.buses.reeds_zone.isin(region_list) | n.buses.reeds_state.isin(region_list) | n.buses.interconnect.str.lower().isin(region_list) | n.buses.nerc_reg.isin(region_list) From 93ec17dda6ca4a0744e52fdac3954734dee1dd75 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 10 Jan 2025 20:32:54 -0800 Subject: [PATCH 05/75] dr by carrier components --- workflow/scripts/build_electricity_sector.py | 26 +++++----- workflow/scripts/build_heat.py | 50 +++++++++----------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/workflow/scripts/build_electricity_sector.py b/workflow/scripts/build_electricity_sector.py index edbc0b93b..c07105a04 100644 --- a/workflow/scripts/build_electricity_sector.py +++ b/workflow/scripts/build_electricity_sector.py @@ -90,24 +90,24 @@ def add_electricity_dr( Adds stores to the network to use for demand response. """ - shift = dr_config.get("shift", 0) - marginal_cost_storage = dr_config.get("marginal_cost", 0) + by_carrier = dr_config.get("by_carrier", False) - if isinstance(marginal_cost_storage, dict): - try: - mc = marginal_cost_storage["electricity"] - except KeyError: - mc = 0 - else: - mc = marginal_cost_storage + # check if dr is applied at a per-carrier level - if mc == 0: - logger.warning(f"No cost applied to demand response for {sector}") + if by_carrier: + dr_config = dr_config.get("elec", {}) + shift = dr_config.get("shift", 0) if shift == 0: logger.info(f"DR not applied to {sector} as allowable sift is {shift}") return + # assign marginal cost value + + marginal_cost_storage = dr_config.get("marginal_cost", 0) + if marginal_cost_storage == 0: + logger.warning(f"No cost applied to demand response for {sector}") + elec = SecCarriers.ELECTRICITY.value df = n.loads[n.loads.index.str.endswith(f"-{elec}") & n.loads.index.str.contains(f"{sector}-")].copy() @@ -204,7 +204,7 @@ def add_electricity_dr( e_min_pu=0, e_max_pu=1, carrier=df.carrier, - marginal_cost_storage=mc, + marginal_cost_storage=marginal_cost_storage, ) n.madd( @@ -218,7 +218,7 @@ def add_electricity_dr( e_min_pu=-1, e_max_pu=0, carrier=df.carrier, - marginal_cost_storage=mc * (-1), + marginal_cost_storage=marginal_cost_storage * (-1), ) diff --git a/workflow/scripts/build_heat.py b/workflow/scripts/build_heat.py index d0c357893..b0f28f1b9 100644 --- a/workflow/scripts/build_heat.py +++ b/workflow/scripts/build_heat.py @@ -929,50 +929,46 @@ def add_heat_dr( Adds end-use thermal demand response. """ - shift = dr_config.get("shift", 0) - marginal_cost_storage = dr_config.get("marginal_cost", 0) - - if shift == 0: - logger.info(f"DR not applied to {sector} as allowable sift is {shift}") - return + by_carrier = dr_config.get("by_carrier", False) - if marginal_cost_storage == 0: - logger.warning(f"No cost applied to demand response for {sector}") + # check if dr is applied at a per-carrier level if sector in ["res", "com"]: assert heat_system in ("urban", "rural", "total") assert heat_carrier in ("heat", "space-heat", "cool") - if isinstance(marginal_cost_storage, dict): - try: - mc = marginal_cost_storage[heat_carrier] - except KeyError: - mc = 0 - else: - mc = marginal_cost_storage - carrier_name = f"{sector}-{heat_system}-{heat_carrier}" + if by_carrier: + dr_config = dr_config.get(heat_carrier, {}) + elif sector == "ind": carrier_name = f"ind-heat" - if isinstance(marginal_cost_storage, dict): - try: - mc = marginal_cost_storage["heat"] - except KeyError: - mc = 0 - else: - mc = marginal_cost_storage + if by_carrier: + dr_config = dr_config.get("heat", {}) else: raise ValueError(f"{sector} not valid dr option") - if mc == 0: + # check if demand response is applied + + shift = dr_config.get("shift", 0) + if shift == 0: + logger.info(f"DR not applied to {sector} as allowable sift is {shift}") + return + + # assign marginal cost value + + marginal_cost_storage = dr_config.get("marginal_cost", 0) + if marginal_cost_storage == 0: logger.warning(f"No cost applied to demand response for {sector}") - # must be run after rural/urban load split + # get components to add + # MUST BE RUN AFTER URBAN/RURAL SPLIT + buses = n.buses[n.buses.carrier == carrier_name] df = pd.DataFrame(index=buses.index) @@ -1069,7 +1065,7 @@ def add_heat_dr( e_max_pu=1, carrier=df.carrier, standing_loss=standing_loss, - marginal_cost_storage=mc, + marginal_cost_storage=marginal_cost_storage, ) n.madd( @@ -1084,7 +1080,7 @@ def add_heat_dr( e_max_pu=0, carrier=df.carrier, standing_loss=standing_loss, - marginal_cost_storage=mc * (-1), + marginal_cost_storage=marginal_cost_storage * (-1), ) From 1a5fe7afa8368841042add22221e709af1c8f9e2 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 10 Jan 2025 21:52:17 -0800 Subject: [PATCH 06/75] dr constraint update --- workflow/scripts/solve_network.py | 57 +++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 4dc9b570e..dc37074f1 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -25,7 +25,7 @@ import logging import re -from typing import Optional +from typing import Any, Optional import numpy as np import pandas as pd @@ -1324,13 +1324,19 @@ def add_capacity_constraint( n: pypsa.Network, sector: str, shift: float, # as a percentage + carrier: Optional[str] = None, ): """Adds limit on deferable load No need to multiply out snapshot weights here """ - dr_links = n.links[n.links.carrier.str.endswith("-dr") & n.links.carrier.str.startswith(f"{sector}-")] + dr_links = n.links[n.links.carrier.str.endswith("-dr") & n.links.carrier.str.startswith(f"{sector}-")].copy() + constraint_name = f"demand_response_capacity-{sector}" + + if carrier: + dr_links = dr_links[dr_links.carrier.str.contains(f"-{carrier}-")].copy() + constraint_name = f"demand_response_capacity-{sector}-{carrier}" if dr_links.empty: return @@ -1350,7 +1356,7 @@ def add_capacity_constraint( bus_order = lhs.vars.bus1.data rhs = rhs[bus_order] - n.model.add_constraints(lhs <= rhs, name=f"demand_response_capacity-{sector}") + n.model.add_constraints(lhs <= rhs, name=constraint_name) # transport dr is at the aggregation bus # sum all outgoing capacity and apply the capacity limit to that @@ -1369,9 +1375,32 @@ def add_capacity_constraint( n.model.add_constraints( lhs >= rhs, - name=f"demand_response_capacity-trn", + name=constraint_name, ) + # helper to manage capacity constraint between non-carrier and carrier + + def _apply_constraint( + n: pypsa.Network, + sector: str, + cfg: dict[str, Any], + carrier: Optional[str] = None, + ): + + shift = cfg.get("shift", 0) + + if not shift < 0.001: + logger.info(f"Demand response not enabled for {sector}") + return + + if shift == "inf": + pass + elif shift >= 0.001: # for tolerance + add_capacity_constraint(n, sector, shift, carrier) + else: + logger.info(f"Unknown arguement of {shift} for {sector} DR") + raise ValueError(shift) + # demand response addition starts here sectors = ["res", "com", "ind", "trn"] @@ -1387,21 +1416,19 @@ def add_capacity_constraint( else: raise ValueError - shift = dr_config.get("shift", 0) - - if not dr_config or shift < 0.001: - logger.info(f"Demand response not enabled for {sector}") + if not dr_config: continue - # capacity constraint + by_carrier = dr_config.get("by_carrier", False) - if shift == "inf": - pass - elif shift >= 0.001: # for tolerance - add_capacity_constraint(n, sector, shift) + if by_carrier: + for carrier, carrier_config in dr_config.items(): + # hacky check to make sure only carriers get passed in + # the actual constraint should check this as well + if carrier in ("elec", "heat", "space-heat", "water-heat", "cool"): + _apply_constraint(n, sector, carrier_config, carrier) else: - logger.info(f"Unknown arguement of {shift} for {sector} DR") - raise ValueError(shift) + _apply_constraint(n, sector, dr_config) def extra_functionality(n, snapshots): From f5db4d7172a83f9b1e4a28887a7b1823e50fd030 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sat, 11 Jan 2025 16:24:20 -0800 Subject: [PATCH 07/75] correct conditional --- workflow/scripts/solve_network.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index dc37074f1..443a2b5c3 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -1389,7 +1389,7 @@ def _apply_constraint( shift = cfg.get("shift", 0) - if not shift < 0.001: + if shift < 0.001: logger.info(f"Demand response not enabled for {sector}") return @@ -1545,7 +1545,7 @@ def solve_network(n, config, solving, opts="", **kwargs): simpl="70", clusters="4m", ll="v1.0", - opts="3h", + opts="3h-TCT", sector="E-G", planning_horizons="2019", ) From 4222ac38fcce2a76a3fcfc18a26b1ac911a034f8 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sat, 11 Jan 2025 16:42:26 -0800 Subject: [PATCH 08/75] docs update --- docs/source/config-sectors.md | 31 +++++++++++++++++++ .../source/configtables/sector_industrial.csv | 3 +- docs/source/configtables/sector_service.csv | 1 + docs/source/data-sectors.md | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/source/config-sectors.md b/docs/source/config-sectors.md index 9022a732e..04420454e 100644 --- a/docs/source/config-sectors.md +++ b/docs/source/config-sectors.md @@ -60,6 +60,23 @@ Only single-period studies are currently supported when running sector studies. :file: configtables/sector_service.csv ``` +```{info} +If running demand response at a per-carrier level, put each carrier as a key. For example: +```yaml +demand_response: + by_carrier: True + space-heat: + shift: 20 + marginal_cost: 16 + elec: + shift: 30 + marginal_cost: 25 + cool: + shift: 10 + marginal_cost: 30 +``` +``` + ## Transport Sector ```{eval-rst} .. literalinclude:: ../../workflow/repo_data/config/config.sector.yaml @@ -84,3 +101,17 @@ Only single-period studies are currently supported when running sector studies. :widths: 22,7,22,33 :file: configtables/sector_industrial.csv ``` + +```{info} +If running demand response at a per-carrier level, put each carrier as a key. For example: +```yaml +demand_response: + by_carrier: True + elec: + shift: 30 + marginal_cost: 25 + heat: + shift: 10 + marginal_cost: 30 +``` +``` diff --git a/docs/source/configtables/sector_industrial.csv b/docs/source/configtables/sector_industrial.csv index c5a853ef0..2830d0760 100644 --- a/docs/source/configtables/sector_industrial.csv +++ b/docs/source/configtables/sector_industrial.csv @@ -1,7 +1,7 @@ ,Unit,Values,Description industrial_sector ,,,Options when implementing the industrial sector -- brownfield,--,"bool {true, false}",Include brownfield end-use capacity. --- dynamic_costs,--,"bool {true, false}","Apply sector specific time varrying natural gas, oil, and coal costs. " +-- dynamic_costs,--,"bool {true, false}","Apply sector specific time varrying natural gas, oil, and coal costs." -- technologies,,,Energy conversion technologies to model. -- -- gas_furnace,,"bool {true, false}",Include natural gas furnaces -- -- coal_furnace,,"bool {true, false}",Include coal boilers @@ -9,3 +9,4 @@ industrial_sector ,,,Options when implementing the industrial sector -- demnand_response,,,Demand response options for the service sector. -- -- shift,percentage,"float {0 <=, >= 100} or 'inf'",Allowable load to be shifted per snapshot. Set to 0 to turn off demand response. Set to 'inf' to not enforce capacity limits. -- -- marginal_cost,$/MWh,float {0 <=},Cost to store one unit of energy for one hour. +-- -- by_carrier,,"bool {true, false}",Run demand response at a per-carrier level. diff --git a/docs/source/configtables/sector_service.csv b/docs/source/configtables/sector_service.csv index 4afde5b27..44dc1845b 100644 --- a/docs/source/configtables/sector_service.csv +++ b/docs/source/configtables/sector_service.csv @@ -28,3 +28,4 @@ service_sector,,,Options when implementing the service sector (Residential and C -- demnand_response,,,Demand response options for the service sector. -- -- shift,percentage,"float {0 <=, >= 100} or 'inf'",Allowable load to be shifted per snapshot. Set to 0 to turn off demand response. Set to 'inf' to not enforce capacity limits. -- -- marginal_cost,$/MWh,float {0 <=},Cost to store one unit of energy for one hour. +-- -- by_carrier,,"bool {true, false}",Run demand response at a per-carrier level. diff --git a/docs/source/data-sectors.md b/docs/source/data-sectors.md index 128babc23..a731c38dd 100644 --- a/docs/source/data-sectors.md +++ b/docs/source/data-sectors.md @@ -65,7 +65,7 @@ Capacity constraints limit how much energy can be delievered through demand resp &\ \hspace{1cm} s_{n,t} = \text{Allowable shiftable load per unit of } d_{n,t} \\ &\ \hspace{1cm} dr_{n,t} = \text{Discharge of demand response at time } t \text{ and bus } n \\ &\ s.t. \\ - &\ \hspace{1cm} d_{n,t} \times s_{n,t} \leq dr_{n,t} \hspace{0.5cm} \forall_{\text{n,t}}\\ + &\ \hspace{1cm} d_{n,t} \times s_{n,t} \geq dr_{n,t} \hspace{0.5cm} \forall_{\text{n,t}}\\ \end{align*} This is not applied directly to the `Link` object via the `p_nom` paprameter to be consistent with the transport sector. Within the transport sector, demand response is applied to the aggregation bus due to endogenous investment options. Therefore, knowing how much electrical load to be shifted at each timestep is not possible before solving. For the transport sector, the following constraint is added. See the [transportation section](./data-transportation.md) schematics for details on where demand reponse is applied. From dc374cbaa2369427d339be5f905bc998f9e8fa89 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sat, 11 Jan 2025 16:52:19 -0800 Subject: [PATCH 09/75] fix docs --- docs/source/config-sectors.md | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/source/config-sectors.md b/docs/source/config-sectors.md index 04420454e..bd1832be6 100644 --- a/docs/source/config-sectors.md +++ b/docs/source/config-sectors.md @@ -60,22 +60,22 @@ Only single-period studies are currently supported when running sector studies. :file: configtables/sector_service.csv ``` -```{info} +:::{note} If running demand response at a per-carrier level, put each carrier as a key. For example: ```yaml demand_response: - by_carrier: True - space-heat: - shift: 20 - marginal_cost: 16 - elec: - shift: 30 - marginal_cost: 25 - cool: - shift: 10 - marginal_cost: 30 -``` + by_carrier: True + space-heat: + shift: 20 + marginal_cost: 16 + elec: + shift: 30 + marginal_cost: 25 + cool: + shift: 10 + marginal_cost: 30 ``` +::: ## Transport Sector ```{eval-rst} @@ -102,16 +102,16 @@ demand_response: :file: configtables/sector_industrial.csv ``` -```{info} +:::{note} If running demand response at a per-carrier level, put each carrier as a key. For example: ```yaml demand_response: - by_carrier: True - elec: - shift: 30 - marginal_cost: 25 - heat: - shift: 10 - marginal_cost: 30 -``` + by_carrier: True + elec: + shift: 30 + marginal_cost: 25 + heat: + shift: 10 + marginal_cost: 30 ``` +::: From cf1fa2ff35e00026ace35fdd15e7978e9f995667 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sun, 12 Jan 2025 12:51:30 -0800 Subject: [PATCH 10/75] dr sector hotfix --- workflow/scripts/solve_network.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 443a2b5c3..0de31705d 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -1389,14 +1389,17 @@ def _apply_constraint( shift = cfg.get("shift", 0) - if shift < 0.001: - logger.info(f"Demand response not enabled for {sector}") - return - - if shift == "inf": - pass - elif shift >= 0.001: # for tolerance - add_capacity_constraint(n, sector, shift, carrier) + if isinstance(shift, str): + if shift == "inf": + pass + else: + logger.info(f"Unknown arguement of {shift} for {sector} DR") + raise ValueError(shift) + elif isinstance(shift, (int, float)): + if shift < 0.001: + logger.info(f"Demand response not enabled for {sector}") + else: + add_capacity_constraint(n, sector, shift, carrier) else: logger.info(f"Unknown arguement of {shift} for {sector} DR") raise ValueError(shift) From b87ffee11af22e6f8b28b08ea48d98beb1af2839 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 20 Jan 2025 11:37:29 -0800 Subject: [PATCH 11/75] Adds population weighting strategy for simplify and cluster --- docs/source/configtables/clustering.csv | 2 + workflow/scripts/add_electricity.py | 2 +- workflow/scripts/cluster_network.py | 68 ++++++++++++++----------- workflow/scripts/simplify_network.py | 1 + 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/docs/source/configtables/clustering.csv b/docs/source/configtables/clustering.csv index 275bff7a9..b9ab77d67 100644 --- a/docs/source/configtables/clustering.csv +++ b/docs/source/configtables/clustering.csv @@ -1,9 +1,11 @@ ,Unit,Value,Description simplify_network:,,, +weighting_strategy,str,"{population, demand-capacity}","When building multiple networks, Use 'population' if you want to ensure the clusters are assigned constant names." to_substations,bool,"{true, false}",Implementation curerntly overrides to true. Network is simplified to substation nodes with positive or negative power injection. algorithm,str,{'kmeans'}, feature,str," {'solar+onwind-time', 'solar+onwind-cap', 'solar-time', 'solar-cap', 'solar+offwind-cap'}",For HAC clustering. cluster_network:,,, +weighting_strategy,str,"{population, demand-capacity}","When building multiple networks, Use 'population' if you want to ensure the clusters are assigned constant names." algorithm,str,{'kmeans'}, feature,str," {'solar+onwind-time', 'solar+onwind-cap', 'solar-time', 'solar-cap', 'solar+offwind-cap'}",For HAC clustering. aggregation_strategies:,,, diff --git a/workflow/scripts/add_electricity.py b/workflow/scripts/add_electricity.py index cf1eeb5f8..45a7cdfce 100755 --- a/workflow/scripts/add_electricity.py +++ b/workflow/scripts/add_electricity.py @@ -813,7 +813,7 @@ def clean_bus_data(n: pypsa.Network): Drops data from the network that are no longer needed in workflow. """ col_list = [ - "Pd", + # "Pd", "load_dissag", "LAF", "LAF_state", diff --git a/workflow/scripts/cluster_network.py b/workflow/scripts/cluster_network.py index e4d0cde5c..f689f2749 100644 --- a/workflow/scripts/cluster_network.py +++ b/workflow/scripts/cluster_network.py @@ -35,7 +35,9 @@ def normed(x): return (x / x.sum()).fillna(0.0) -def weighting_for_country(n, x): +def weighting_for_region(n, x, weighting_strategy=None): + """Calculate the weighting for internal nodes within a given region.""" + conv_carriers = {"nuclear", "OCGT", "CCGT", "PHS", "hydro", "coal", "biomass"} generators = n.generators generators["carrier_base"] = generators.carrier.str.split().str[0] @@ -57,6 +59,9 @@ def weighting_for_country(n, x): l = normed(load.reindex(b_i, fill_value=0)) w = g + l + if weighting_strategy == "population": + w = normed(n.buses.loc[x.index].Pd) + return (w * (100.0 / w.max())).clip(lower=1.0).astype(int) @@ -100,24 +105,20 @@ def get_feature_for_hac(n, buses_i=None, feature=None): return feature_data -def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc"): +def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc", weighting_strategy=None): """ - Determine the number of clusters per country. + Determine the number of clusters per region. """ - L = ( - n.loads_t.p_set.mean() - .groupby(n.loads.bus) - .sum() - .groupby([n.buses.country, n.buses.sub_network]) - .sum() - .pipe(normed) - ) - - N = n.buses.groupby(["country", "sub_network"]).size() + if weighting_strategy == "population": + bus_distribution_factor = n.buses.Pd + else: + bus_distribution_factor = n.loads_t.p_set.mean().groupby(n.loads.bus).sum() + factors = bus_distribution_factor.groupby([n.buses.country, n.buses.sub_network]).sum().pipe(normed) + n_subnetwork_nodes = n.buses.groupby(["country", "sub_network"]).size() assert ( - n_clusters >= len(N) and n_clusters <= N.sum() - ), f"Number of clusters must be {len(N)} <= n_clusters <= {N.sum()} for this selection of countries." + n_clusters >= len(n_subnetwork_nodes) and n_clusters <= n_subnetwork_nodes.sum() + ), f"Number of clusters must be {len(n_subnetwork_nodes)} <= n_clusters <= {n_subnetwork_nodes.sum()} for this selection of countries." if focus_weights is not None: total_focus = sum(list(focus_weights.values())) @@ -125,28 +126,28 @@ def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc"): assert total_focus <= 1.0, "The sum of focus weights must be less than or equal to 1." for country, weight in focus_weights.items(): - L[country] = weight / len(L[country]) + factors[country] = weight / len(factors[country]) - remainder = [c not in focus_weights.keys() for c in L.index.get_level_values("country")] - L[remainder] = L.loc[remainder].pipe(normed) * (1 - total_focus) + remainder = [c not in focus_weights.keys() for c in factors.index.get_level_values("country")] + factors[remainder] = factors.loc[remainder].pipe(normed) * (1 - total_focus) logger.warning("Using custom focus weights for determining number of clusters.") assert np.isclose( - L.sum(), + factors.sum(), 1.0, rtol=1e-3, - ), f"Country weights L must sum up to 1.0 when distributing clusters. Is {L.sum()}." + ), f"Country weights L must sum up to 1.0 when distributing clusters. Is {factors.sum()}." m = po.ConcreteModel() def n_bounds(model, *n_id): - return (1, N[n_id]) + return (1, n_subnetwork_nodes[n_id]) - m.n = po.Var(list(L.index), bounds=n_bounds, domain=po.Integers) + m.n = po.Var(list(factors.index), bounds=n_bounds, domain=po.Integers) m.tot = po.Constraint(expr=(po.summation(m.n) == n_clusters)) m.objective = po.Objective( - expr=sum((m.n[i] - L.loc[i] * n_clusters) ** 2 for i in L.index), + expr=sum((m.n[i] - factors.loc[i] * n_clusters) ** 2 for i in factors.index), sense=po.minimize, ) @@ -163,7 +164,7 @@ def n_bounds(model, *n_id): results = opt.solve(m) assert results["Solver"][0]["Status"] == "ok", f"Solver returned non-optimally: {results}" - return pd.Series(m.n.get_values(), index=L.index).round().astype(int) + return pd.Series(m.n.get_values(), index=factors.index).round().astype(int) def busmap_for_n_clusters( @@ -173,6 +174,7 @@ def busmap_for_n_clusters( focus_weights=None, algorithm="kmeans", feature=None, + weighting_strategy=None, **algorithm_kwds, ): if algorithm == "kmeans": @@ -232,10 +234,10 @@ def fix_country_assignment_for_hac(n): n, n_clusters, focus_weights=focus_weights, + weighting_strategy=weighting_strategy, solver_name=solver_name, ) - - # Potentiall remove buses and lines from these corner case counties from network + # Remove buses and lines that are not part of the clustering to reconcile TAMU and ReEDS Topologies nc_set = set(n_clusters.index.get_level_values(0).unique()) bus_set = set(n.buses.country.unique()) countries_remove = list(bus_set - nc_set) @@ -260,8 +262,7 @@ def busmap_for_country(x): logger.debug(f"Determining busmap for country {prefix[:-1]}") if len(x) == 1: return pd.Series(prefix + "0", index=x.index) - weight = weighting_for_country(n, x) - + weight = weighting_for_region(n, x, weighting_strategy) if algorithm == "kmeans": return prefix + busmap_by_kmeans( n, @@ -307,6 +308,7 @@ def clustering_for_n_clusters( algorithm="hac", feature=None, focus_weights=None, + weighting_strategy=None, ): if not isinstance(custom_busmap, pd.Series): busmap = busmap_for_n_clusters( @@ -316,14 +318,16 @@ def clustering_for_n_clusters( focus_weights, algorithm, feature, + weighting_strategy, ) + # plot_busmap(n, busmap, 'busmap.png') else: busmap = custom_busmap line_strategies = aggregation_strategies.get("lines", dict()) generator_strategies = aggregation_strategies.get("generators", dict()) one_port_strategies = aggregation_strategies.get("one_ports", dict()) - + bus_strategies = {"Pd": "sum"} clustering = get_clustering_from_busmap( n, busmap, @@ -333,6 +337,7 @@ def clustering_for_n_clusters( line_length_factor=line_length_factor, line_strategies=line_strategies, generator_strategies=generator_strategies, + bus_strategies=bus_strategies, one_port_strategies=one_port_strategies, scale_link_capital_costs=False, ) @@ -556,6 +561,7 @@ def convert_to_transport( def cluster_regions(busmaps, input=None, output=None): + """Create new geojson files for the clustered regions.""" busmap = reduce(lambda x, y: x.map(y), busmaps[1:], busmaps[0]) for which in ("regions_onshore", "regions_offshore"): @@ -567,8 +573,7 @@ def cluster_regions(busmaps, input=None, output=None): regions_c.to_file(getattr(output, which)) -def plot_busmap_for_n_clusters(n, n_clusters, fn=None): - busmap = busmap_for_n_clusters(n, n_clusters) +def plot_busmap(n, busmap, fn=None): cs = busmap.unique() cr = sns.color_palette("hls", len(cs)) n.plot(bus_colors=busmap.map(dict(zip(cs, cr)))) @@ -729,6 +734,7 @@ def plot_busmap_for_n_clusters(n, n_clusters, fn=None): params.cluster_network["algorithm"], params.cluster_network["feature"], params.focus_weights, + weighting_strategy=params.cluster_network.get("weighting_strategy", None), ) if transport_model: diff --git a/workflow/scripts/simplify_network.py b/workflow/scripts/simplify_network.py index d3efc7791..8b6996215 100644 --- a/workflow/scripts/simplify_network.py +++ b/workflow/scripts/simplify_network.py @@ -281,6 +281,7 @@ def assign_line_lengths(n, line_length_factor): algorithm=params.simplify_network["algorithm"], feature=params.simplify_network["feature"], aggregation_strategies=params.aggregation_strategies, + weighting_strategy=params.simplify_network.get("weighting_strategy", None), ) n = clustering.network From be1bafe9131c3499a02d5beac52cc01268c40053 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 20 Jan 2025 11:42:26 -0800 Subject: [PATCH 12/75] update default config --- workflow/repo_data/config/config.default.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workflow/repo_data/config/config.default.yaml b/workflow/repo_data/config/config.default.yaml index 980a9baf7..aefa01efa 100644 --- a/workflow/repo_data/config/config.default.yaml +++ b/workflow/repo_data/config/config.default.yaml @@ -138,10 +138,12 @@ costs: # docs : clustering: simplify_network: + weighting_strategy: demand-capacity # choose from: [population, demand-capacity] to_substations: false # network is simplified to nodes with positive or negative power injection (i.e. substations or offwind connections) algorithm: kmeans # choose from: [hac, kmeans] feature: solar+onwind-time # only for hac. choose from: [solar+onwind-time, solar+onwind-cap, solar-time, solar-cap, solar+offwind-cap] etc. cluster_network: + weighting_strategy: demand-capacity # choose from: [population, demand-capacity] algorithm: kmeans # choose from: [hac, kmeans] feature: solar+onwind-time exclude_carriers: [] From f5f931201a10da580a3b91326b52dc25c5faacd6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:44:44 +0000 Subject: [PATCH 13/75] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/run_slurm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/run_slurm.sh b/workflow/run_slurm.sh index bf8dcb83b..386cfd502 100644 --- a/workflow/run_slurm.sh +++ b/workflow/run_slurm.sh @@ -1,3 +1,3 @@ # SLURM specifications made in default.cluster.yaml & the individual rules # GRB_LICENSE_FILE=/share/software/user/restricted/gurobi/11.0.2/licenses/gurobi.lic⁠ -snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL --mail-user {cluster.email} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/base_paper/config.base.yaml \ No newline at end of file +snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL --mail-user {cluster.email} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/base_paper/config.base.yaml From 416f938481323b3629af6ba38e4c0810a3f59248 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Wed, 22 Jan 2025 16:22:09 -0800 Subject: [PATCH 14/75] Remove virtual buses --- workflow/scripts/cluster_network.py | 104 ++++++++-------------------- 1 file changed, 30 insertions(+), 74 deletions(-) diff --git a/workflow/scripts/cluster_network.py b/workflow/scripts/cluster_network.py index f689f2749..7c15967c7 100644 --- a/workflow/scripts/cluster_network.py +++ b/workflow/scripts/cluster_network.py @@ -455,69 +455,49 @@ def convert_to_transport( add_itls(buses, itls_filt, itl_cost) if itl_agg_fn: + # Aggregating the ITLs to lower resolution topology_aggregation_key = list(topology_aggregation.keys())[0] - itl_agg = pd.read_csv(itl_agg_fn) - itl_agg.columns = itl_agg.columns.str.lower() - itl_agg = itl_agg.rename(columns={"transgrp": "r", "transgrpp": "rr"}) - itl_agg = itl_agg[ - itl_agg.r.isin(clustering.network.buses["country"]) | itl_agg.rr.isin(clustering.network.buses["country"]) + itl_lower_res = pd.read_csv(itl_agg_fn) + itl_lower_res.columns = itl_lower_res.columns.str.lower() + itl_lower_res = itl_lower_res.rename(columns={"transgrp": "r", "transgrpp": "rr"}) + + itl_lower_res = itl_lower_res[ # Filter low-res ITLs to only include those that have an end in the network + itl_lower_res.r.isin(buses["country"]) | itl_lower_res.rr.isin(buses["country"]) ] aggregated_buses = agg_busmap.rename(index=lambda x: x.strip(" 0")) non_agg_buses = buses[~buses.index.isin(agg_busmap.values)] non_agg_buses = non_agg_buses[ - non_agg_buses[topology_aggregation_key].isin(itl_agg.r) - | non_agg_buses[topology_aggregation_key].isin(itl_agg.rr) + non_agg_buses[topology_aggregation_key].isin(itl_lower_res.r) + | non_agg_buses[topology_aggregation_key].isin(itl_lower_res.rr) ] - virtual_buses = non_agg_buses.groupby(topology_aggregation_key)[non_agg_buses.columns].min() - existing_x = non_agg_buses.groupby(topology_aggregation_key).x.mean() - existing_y = non_agg_buses.groupby(topology_aggregation_key).y.mean() - agg_x = buses.loc[agg_busmap.unique()].x.mean() - agg_y = buses.loc[agg_busmap.unique()].y.mean() - virtual_buses["x"] = 0.3 * agg_x + 0.7 * existing_x - virtual_buses["y"] = 0.3 * agg_y + 0.7 * existing_y - - if not virtual_buses.empty: - clustering.network.madd( - "Bus", - virtual_buses[topology_aggregation_key], - country=virtual_buses[topology_aggregation_key], - reeds_zone="na" if topology_aggregation_key == "trans_grp" else virtual_buses["reeds_zone"], - reeds_ba="na" if topology_aggregation_key == "trans_grp" else virtual_buses["reeds_ba"], - interconnect=virtual_buses["interconnect"], - nerc_reg=virtual_buses["nerc_reg"], - trans_reg=virtual_buses["trans_reg"], - trans_grp=virtual_buses["trans_grp"], - reeds_state=virtual_buses["reeds_state"], - x=virtual_buses.x, - y=virtual_buses.y, - ) - itl_agg = itl_agg[ - itl_agg.r.isin(clustering.network.buses["country"]) - & itl_agg.rr.isin(clustering.network.buses["country"]) - & (itl_agg.r.isin(agg_busmap.values) | itl_agg.rr.isin(agg_busmap.values)) + + itl_lower_res = itl_lower_res[ # Filter low-res ITLs to only include those that have both ends in the network + itl_lower_res.r.isin(buses["country"]) + & itl_lower_res.rr.isin(buses["country"]) + & (itl_lower_res.r.isin(agg_busmap.values) | itl_lower_res.rr.isin(agg_busmap.values)) ] - buses = clustering.network.buses.copy() # itls from county to respective virtual bus - itls_to_virtual = itls[ + itls_between = itls[ # Remove ITLs internal to the aggregated buses (itls.r.isin(aggregated_buses.index) | itls.rr.isin(aggregated_buses.index)) & ~(itls.r.isin(aggregated_buses.index) & itls.rr.isin(aggregated_buses.index)) ] - itls_to_virtual = itls_to_virtual[itls_to_virtual.r.isin(buses.index) | itls_to_virtual.rr.isin(buses.index)] - # Update the itls virtual such that the existing bus : virtual bus - itls_to_virtual.loc[itls_to_virtual.r.isin(non_agg_buses.index), "rr"] = non_agg_buses.loc[ - itls_to_virtual.r[itls_to_virtual.r.isin(non_agg_buses.index)], - topology_aggregation_key, - ].values - itls_to_virtual.loc[itls_to_virtual.rr.isin(non_agg_buses.index), "r"] = non_agg_buses.loc[ - itls_to_virtual.rr[itls_to_virtual.rr.isin(non_agg_buses.index)], - topology_aggregation_key, - ].values - - itl_agg = pd.concat([itl_agg, itls_to_virtual]) + itls_between = itls_between[ # Keep only ITLS which have end in network buses + itls_between.r.isin(buses.index) | itls_between.rr.isin(buses.index) + ] + + # Instead replace the itl aggregated bus with the new agg_bus + itls_between.loc[itls_between.r.isin(aggregated_buses.index), "r"] = itls_between.r.map( + aggregated_buses, + ).dropna() + itls_between.loc[itls_between.rr.isin(aggregated_buses.index), "rr"] = itls_between.rr.map( + aggregated_buses, + ).dropna() + + itl_lower_res = pd.concat([itl_lower_res, itls_between]) itl_agg_costs = None if itl_agg_costs_fn is None else pd.concat([itl_cost, pd.read_csv(itl_agg_costs_fn)]) - add_itls(buses, itl_agg, itl_agg_costs, expansion=True) - itls = pd.concat([itls_filt, itl_agg]) + add_itls(buses, itl_lower_res, itl_agg_costs, expansion=True) + itls = pd.concat([itls_filt, itl_lower_res]) else: itls = itls_filt @@ -533,30 +513,6 @@ def convert_to_transport( f"Network configuration contains {len(disconnected_buses)} disconnected buses. ", ) - # Dissolve TX for particular zones according to default reeds configurations - clustering.network.links.loc[ - clustering.network.links.bus0.isin(["p119"]) & clustering.network.links.bus1.isin(["p122"]), - "p_nom", - ] = 1e9 - clustering.network.links.loc[ - clustering.network.links.bus1.isin(["p119"]) & clustering.network.links.bus0.isin(["p122"]), - "p_nom", - ] = 1e9 - # Dissolve p124 and p99 - if "p124" in clustering.network.buses.index and "p99" in clustering.network.buses.index: - clustering.network.add( - "Link", - "p124||p99", - bus0="p124", - bus1="p99", - p_nom=1e9, - p_nom_extendable=False, - length=0, - capital_cost=0, - efficiency=1, - carrier="AC", - ) - return clustering From 0067809ddc458cbfada8ec6cc551de1fd61489c2 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 3 Feb 2025 14:34:02 -0800 Subject: [PATCH 15/75] update tct logic --- workflow/scripts/solve_network.py | 128 ++++++++++++++++-------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 44b44b958..d7774c5ce 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -25,6 +25,7 @@ import logging import re +from math import ceil, floor from typing import Any, Optional import numpy as np @@ -59,7 +60,14 @@ def get_region_buses(n, region_list): ] -def filter_components(n, component_type, planning_horizon, carrier_list, region_buses, extendable): +def filter_components( + n: pypsa.Network, + component_type: str, + planning_horizon: str | int, + carrier_list: list[str], + region_buses: pd.Index, + extendable: bool, +): """ Filter components based on common criteria. @@ -207,13 +215,13 @@ def add_technology_capacity_target_constraints(n, config): if tct_data.empty: return - for idx, target in tct_data.iterrows(): + for _, target in tct_data.iterrows(): planning_horizon = target.planning_horizon region_list = [region_.strip() for region_ in target.region.split(",")] carrier_list = [carrier_.strip() for carrier_ in target.carrier.split(",")] region_buses = get_region_buses(n, region_list) - lhs_gens = filter_components( + lhs_gens_ext = filter_components( n=n, component_type="Generator", planning_horizon=planning_horizon, @@ -221,17 +229,16 @@ def add_technology_capacity_target_constraints(n, config): region_buses=region_buses.index, extendable=True, ) + lhs_gens_existing = filter_components( + n=n, + component_type="Generator", + planning_horizon=planning_horizon, + carrier_list=carrier_list, + region_buses=region_buses.index, + extendable=False, + ) - # rhs_g_non_extendable = filter_components( - # n=n, - # component_type="Generator", - # planning_horizon=planning_horizon, - # carrier_list=carrier_list, - # region_buses=region_buses.index, - # extendable=False, - # ).p_nom.sum() - - lhs_storage = filter_components( + lhs_storage_ext = filter_components( n=n, component_type="StorageUnit", planning_horizon=planning_horizon, @@ -239,32 +246,37 @@ def add_technology_capacity_target_constraints(n, config): region_buses=region_buses.index, extendable=True, ) + lhs_storage_existing = filter_components( + n=n, + component_type="StorageUnit", + planning_horizon=planning_horizon, + carrier_list=carrier_list, + region_buses=region_buses.index, + extendable=False, + ) - # rhs_s_non_extendable = filter_components( - # n=n, - # component_type="StorageUnit", - # planning_horizon=planning_horizon, - # carrier_list=carrier_list, - # region_buses=region_buses.index, - # extendable=False, - # ).p_nom.sum() - - if region_buses.empty or (lhs_gens.empty and lhs_storage.empty): + if region_buses.empty or (lhs_gens_ext.empty and lhs_storage_ext.empty): continue - if not lhs_gens.empty: - grouper_g = pd.concat([lhs_gens.bus.map(n.buses.country), lhs_gens.carrier], axis=1).rename_axis( + if not lhs_gens_ext.empty: + grouper_g = pd.concat( + [lhs_gens_ext.bus.map(n.buses.country), lhs_gens_ext.carrier], + axis=1, + ).rename_axis( "Generator-ext", ) - lhs_g = p_nom.loc[lhs_gens.index].groupby(grouper_g).sum().rename(bus="country") + lhs_g = p_nom.loc[lhs_gens_ext.index].groupby(grouper_g).sum().rename(bus="country") else: lhs_g = None - if not lhs_storage.empty: - grouper_s = pd.concat([lhs_storage.bus.map(n.buses.country), lhs_storage.carrier], axis=1).rename_axis( + if not lhs_storage_ext.empty: + grouper_s = pd.concat( + [lhs_storage_ext.bus.map(n.buses.country), lhs_storage_ext.carrier], + axis=1, + ).rename_axis( "StorageUnit-ext", ) - lhs_s = n.model["StorageUnit-p_nom"].loc[lhs_storage.index].groupby(grouper_s).sum() + lhs_s = n.model["StorageUnit-p_nom"].loc[lhs_storage_ext.index].groupby(grouper_s).sum() else: lhs_s = None @@ -277,56 +289,52 @@ def add_technology_capacity_target_constraints(n, config): else: lhs = (lhs_g + lhs_s).sum() - lhs_extendable = lhs_gens.p_nom.sum() + lhs_storage.p_nom.sum() - # rhs_non_extendable = rhs_g_non_extendable + rhs_s_non_extendable + lhs_existing = lhs_gens_existing.p_nom.sum() + lhs_storage_existing.p_nom.sum() if target["max"] == "existing": - target["max"] = lhs_extendable + target["max"] = ceil(lhs_existing) else: target["max"] = float(target["max"]) if target["min"] == "existing": - target["min"] = lhs_extendable + target["min"] = floor(lhs_existing) else: target["min"] = float(target["min"]) if not np.isnan(target["min"]): + rhs = target["min"] - floor(lhs_existing) + n.model.add_constraints( - lhs >= (target["min"]), - name=f"GlobalConstraint-{target.name}_{target.planning_horizon}_min", + lhs >= rhs, + name=f"{target.name}_{target.planning_horizon}_min", ) + logger.info( "Adding TCT Constraint:\n" - "Name: %s\n" - "Planning Horizon: %s\n" - "Region: %s\n" - "Carrier: %s\n" - "Min Value: %s", - target.name, - target.planning_horizon, - target.region, - target.carrier, - (target["min"]), + f"Name: {target.name}\n" + f"Planning Horizon: {target.planning_horizon}\n" + f"Region: {target.region}\n" + f"Carrier: {target.carrier}\n" + f"Min Value: {target['min']}", ) if not np.isnan(target["max"]): + + rhs = target["max"] - floor(lhs_existing) + n.model.add_constraints( - lhs <= (target["max"]), - name=f"GlobalConstraint-{target.name}_{target.planning_horizon}_max", + lhs <= rhs, + name=f"{target.name}_{target.planning_horizon}_max", ) + logger.info( "Adding TCT Constraint:\n" - "Name: %s\n" - "Planning Horizon: %s\n" - "Region: %s\n" - "Carrier: %s\n" - "Max Value: %s", - target.name, - target.planning_horizon, - target.region, - target.carrier, - (target["max"]), + f"Name: {target.name}\n" + f"Planning Horizon: {target.planning_horizon}\n" + f"Region: {target.region}\n" + f"Carrier: {target.carrier}\n" + f"Max Value: {target['max']}", ) @@ -1546,17 +1554,15 @@ def solve_network(n, config, solving, opts="", **kwargs): if "snakemake" not in globals(): from _helpers import mock_snakemake - print("") - snakemake = mock_snakemake( "solve_network", interconnect="western", simpl="70", clusters="4m", ll="v1.0", - opts="3h-TCT", + opts="1h-TCT", sector="E-G", - planning_horizons="2019", + planning_horizons="2030", ) configure_logging(snakemake) update_config_from_wildcards(snakemake.config, snakemake.wildcards) From 09e8c8cd31eac8bb67ab971635f4f65813afb533 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 3 Feb 2025 14:47:19 -0800 Subject: [PATCH 16/75] tct note in docs --- docs/source/config-wildcards.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/docs/source/config-wildcards.md b/docs/source/config-wildcards.md index cdc455df7..7e8d4ecea 100644 --- a/docs/source/config-wildcards.md +++ b/docs/source/config-wildcards.md @@ -75,6 +75,10 @@ The REM, SAFER, RPS can be defined using either the reeds zone name 'p##" the state code (eg, TX, CA, MT), pypsa-usa interconnect name (western, eastern, texas, usa), or nerc region name. +```{warning} +TCT Targets can only be used with renewable generators and utility scale batteries in sector studies. +``` + There are currently: ```{eval-rst} @@ -84,14 +88,9 @@ There are currently: :file: configtables/opts.csv ``` - (sector)= ## The `{sector}` wildcard -```{warning} -Sector coupling studies are all under active development -``` - The `{sector}` wildcard is used to specify what sectors to include. If `None` is provided, an electrical only study is completed. @@ -100,15 +99,6 @@ is provided, an electrical only study is completed. | Electricity | E | Electrical sector. Will always be run. | Runs | | Natural Gas | G | All sectors added | Development | -(scope)= -## The `{scope}` wildcard - -```{warning} -Sector coupling studies are all under active development -``` - -Takes values `residential`, `urban`, `total`. Used in sector coupling studies to define -population breakdown. (cutout_wc)= ## The `{cutout}` wildcard From 419a13d5b1a142a8c2b287e3935cb6ed7a260b42 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 3 Feb 2025 15:11:25 -0800 Subject: [PATCH 17/75] update comment --- workflow/scripts/solve_network.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index d7774c5ce..42a8e8319 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -196,8 +196,6 @@ def add_technology_capacity_target_constraints(n, config): Add minimum or maximum levels of generator nominal capacity per carrier for individual regions. Each constraint can be designated for a specified planning horizon in multi-period models. Opts and path for technology_capacity_targets.csv must be defined in config.yaml. Default file is available at config/policy_constraints/technology_capacity_targets.csv. - *** Review to subtract the non-extensible capacity from the rhs? *** - Parameters ---------- n : pypsa.Network From 2739bffd5f8bd5f96690a62edbfea1225a5e8b9c Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 4 Feb 2025 10:38:57 -0800 Subject: [PATCH 18/75] add assertion --- workflow/scripts/solve_network.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 42a8e8319..c0b0418ec 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -25,7 +25,6 @@ import logging import re -from math import ceil, floor from typing import Any, Optional import numpy as np @@ -290,18 +289,18 @@ def add_technology_capacity_target_constraints(n, config): lhs_existing = lhs_gens_existing.p_nom.sum() + lhs_storage_existing.p_nom.sum() if target["max"] == "existing": - target["max"] = ceil(lhs_existing) + target["max"] = round(lhs_existing, 2) + 0.01 else: target["max"] = float(target["max"]) if target["min"] == "existing": - target["min"] = floor(lhs_existing) + target["min"] = round(lhs_existing, 2) - 0.01 else: target["min"] = float(target["min"]) if not np.isnan(target["min"]): - rhs = target["min"] - floor(lhs_existing) + rhs = target["min"] - round(lhs_existing, 2) n.model.add_constraints( lhs >= rhs, @@ -319,7 +318,11 @@ def add_technology_capacity_target_constraints(n, config): if not np.isnan(target["max"]): - rhs = target["max"] - floor(lhs_existing) + assert ( + target["max"] >= lhs_existing + ), f"{target['max']} for {target['carrier']} must be at least {lhs_existing}" + + rhs = target["max"] - round(lhs_existing, 2) n.model.add_constraints( lhs <= rhs, From ff7d165777246b487962638f1a54d6fec3e84f70 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 4 Feb 2025 10:43:02 -0800 Subject: [PATCH 19/75] typo --- workflow/scripts/solve_network.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index c0b0418ec..b4f0b7e5b 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -313,14 +313,15 @@ def add_technology_capacity_target_constraints(n, config): f"Planning Horizon: {target.planning_horizon}\n" f"Region: {target.region}\n" f"Carrier: {target.carrier}\n" - f"Min Value: {target['min']}", + f"Min Value: {target['min']}\n", + f"Min Value Adj: {rhs}", ) if not np.isnan(target["max"]): assert ( target["max"] >= lhs_existing - ), f"{target['max']} for {target['carrier']} must be at least {lhs_existing}" + ), f"TCT constraint of {target['max']} MW for {target['carrier']} must be at least {lhs_existing}" rhs = target["max"] - round(lhs_existing, 2) @@ -335,7 +336,8 @@ def add_technology_capacity_target_constraints(n, config): f"Planning Horizon: {target.planning_horizon}\n" f"Region: {target.region}\n" f"Carrier: {target.carrier}\n" - f"Max Value: {target['max']}", + f"Max Value: {target['max']}\n", + f"Max Value Adj: {rhs}", ) From 1772d4a3335efd0191bb15a4bc00fd11adc8360c Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 4 Feb 2025 15:21:30 -0800 Subject: [PATCH 20/75] dr implementation --- workflow/scripts/add_extra_components.py | 146 ++++++++++++++++++++++- 1 file changed, 143 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index 125c606ea..648d89e14 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -627,6 +627,142 @@ def apply_max_annual_growth_rate(n, max_growth): n.carriers.loc[carrier, "max_relative_growth"] = rate**years +def add_demand_response( + n: pypsa.Network, + dr_config: dict[str, str | float], +) -> None: + """Add price based demand response to network.""" + + shift = dr_config.get("shift", 0) + if shift == 0: + logger.info(f"DR not applied as allowable sift is {shift}") + return + + marginal_cost_storage = dr_config.get("marginal_cost", 0) + if marginal_cost_storage == 0: + logger.warning("No cost applied to demand response") + + # attach dr at all load locations + + buses = n.loads.bus + df = n.buses[n.buses.index.isin(buses)].copy() + + # two storageunits for forward and backwards load shifting + + n.madd( + "Bus", + names=df.index, + suffix="-fwd-dr", + x=df.x, + y=df.y, + carrier="demand_response", + unit="MWh", + country=df.country, + reeds_zone=df.reeds_zone, + reeds_ba=df.reeds_ba, + interconnect=df.interconnect, + trans_reg=df.trans_reg, + trans_grp=df.trans_grp, + reeds_state=df.reeds_state, + substation_lv=df.substation_lv, + ) + + n.madd( + "Bus", + names=df.index, + suffix="-bck-dr", + x=df.x, + y=df.y, + carrier="demand_response", + unit="MWh", + country=df.country, + reeds_zone=df.reeds_zone, + reeds_ba=df.reeds_ba, + interconnect=df.interconnect, + trans_reg=df.trans_reg, + trans_grp=df.trans_grp, + reeds_state=df.reeds_state, + substation_lv=df.substation_lv, + ) + + # seperate charging/discharging links for easier constraint generation + + n.madd( + "Link", + names=df.index, + suffix="-fwd-dr-charger", + bus0=df.index, + bus1=df.index + "-fwd-dr", + carrier=df.carrier, + p_nom_extendable=False, + p_nom=np.inf, + ) + + n.madd( + "Link", + names=df.index, + suffix="-fwd-dr-discharger", + bus0=df.index + "-fwd-dr", + bus1=df.index, + carrier=df.carrier, + p_nom_extendable=False, + p_nom=np.inf, + ) + + n.madd( + "Link", + names=df.index, + suffix="-bck-dr-charger", + bus0=df.index, + bus1=df.index + "-bck-dr", + carrier=df.carrier, + p_nom_extendable=False, + p_nom=np.inf, + ) + + n.madd( + "Link", + names=df.index, + suffix="-bck-dr-discharger", + bus0=df.index + "-bck-dr", + bus1=df.index, + carrier=df.carrier, + p_nom_extendable=False, + p_nom=np.inf, + ) + + # backward stores have positive marginal cost storage and postive e + # forward stores have negative marginal cost storage and negative e + + n.madd( + "Store", + names=df.index, + suffix="-bck-dr", + bus=df.index + "-bck-dr", + e_cyclic=True, + e_nom_extendable=False, + e_nom=np.inf, + e_min_pu=0, + e_max_pu=1, + carrier=df.carrier, + marginal_cost_storage=marginal_cost_storage, + ) + + n.madd( + "Store", + names=df.index, + suffix="-fwd-dr", + bus=df.index + "-fwd-dr", + e_cyclic=True, + e_nom_extendable=False, + e_nom=np.inf, + e_min_pu=-1, + e_max_pu=0, + carrier=df.carrier, + marginal_cost_storage=marginal_cost_storage * (-1), + ) + + if __name__ == "__main__": if "snakemake" not in globals(): from _helpers import mock_snakemake @@ -634,8 +770,8 @@ def apply_max_annual_growth_rate(n, max_growth): snakemake = mock_snakemake( "add_extra_components", interconnect="western", - simpl=12, - clusters=6, + simpl="70", + clusters="4m", ) configure_logging(snakemake) @@ -708,7 +844,11 @@ def apply_max_annual_growth_rate(n, max_growth): apply_max_annual_growth_rate(n, snakemake.config["costs"]["max_growth"]) add_nice_carrier_names(n, snakemake.config) add_co2_emissions(n, costs_dict[n.investment_periods[0]], n.carriers.index) - # n.generators.to_csv("generators_ec.csv") + + dr_config = snakemake.config["electricity"].get("demand_response", {}) + if dr_config: + add_demand_response(n, dr_config) + n.consistency_check() n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards))) n.export_to_netcdf(snakemake.output[0]) From 2484c002ef07cb19e5d168198f3adbe37dc19abc Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 4 Feb 2025 15:54:44 -0800 Subject: [PATCH 21/75] plotting runs --- workflow/scripts/add_extra_components.py | 12 +++++----- workflow/scripts/plot_network_maps.py | 24 +++++++++++-------- .../scripts/plot_validation_production.py | 15 ++++++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index 648d89e14..c3dbd23c5 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -693,7 +693,7 @@ def add_demand_response( suffix="-fwd-dr-charger", bus0=df.index, bus1=df.index + "-fwd-dr", - carrier=df.carrier, + carrier="demand_response", p_nom_extendable=False, p_nom=np.inf, ) @@ -704,7 +704,7 @@ def add_demand_response( suffix="-fwd-dr-discharger", bus0=df.index + "-fwd-dr", bus1=df.index, - carrier=df.carrier, + carrier="demand_response", p_nom_extendable=False, p_nom=np.inf, ) @@ -715,7 +715,7 @@ def add_demand_response( suffix="-bck-dr-charger", bus0=df.index, bus1=df.index + "-bck-dr", - carrier=df.carrier, + carrier="demand_response", p_nom_extendable=False, p_nom=np.inf, ) @@ -726,7 +726,7 @@ def add_demand_response( suffix="-bck-dr-discharger", bus0=df.index + "-bck-dr", bus1=df.index, - carrier=df.carrier, + carrier="demand_response", p_nom_extendable=False, p_nom=np.inf, ) @@ -744,7 +744,7 @@ def add_demand_response( e_nom=np.inf, e_min_pu=0, e_max_pu=1, - carrier=df.carrier, + carrier="demand_response", marginal_cost_storage=marginal_cost_storage, ) @@ -758,7 +758,7 @@ def add_demand_response( e_nom=np.inf, e_min_pu=-1, e_max_pu=0, - carrier=df.carrier, + carrier="demand_response", marginal_cost_storage=marginal_cost_storage * (-1), ) diff --git a/workflow/scripts/plot_network_maps.py b/workflow/scripts/plot_network_maps.py index e862bd150..e2d1a97eb 100644 --- a/workflow/scripts/plot_network_maps.py +++ b/workflow/scripts/plot_network_maps.py @@ -311,7 +311,7 @@ def plot_demand_map( bus_values = get_demand_base(n).mul(1e-3) line_values = n.lines.s_nom - link_values = n.links.p_nom.replace(to_replace={pd.NA: 0}) + link_values = n.links[n.links.carrier == "AC"].p_nom.replace(to_replace={pd.NA: 0}) # plot data title = create_title("Network Demand", **wildcards) @@ -399,7 +399,7 @@ def plot_base_capacity_map( bus_values = remove_sector_buses(bus_values).groupby(by=["bus", "carrier"]).sum() line_values = n.lines.s_nom - # link_values = n.links.p_nom.replace(0, None) + link_values = n.links[n.links.carrier == "AC"].p_nom.replace(to_replace={pd.NA: 0}) # plot data @@ -412,7 +412,7 @@ def plot_base_capacity_map( n=n, bus_values=bus_values, line_values=line_values, - link_values=n.links.p_nom.replace(to_replace={pd.NA: 0}), + link_values=link_values, regions=regions, line_scale=line_scale, bus_scale=bus_scale, @@ -441,6 +441,9 @@ def plot_opt_capacity_map( bus_values = bus_values[bus_values.index.get_level_values("carrier").isin(carriers)] bus_values = remove_sector_buses(bus_values).reset_index().groupby(by=["bus", "carrier"]).sum().squeeze() line_values = n.lines.s_nom_opt + link_values = n.links[n.links.carrier == "AC"].p_nom_opt.replace( + to_replace={pd.NA: 0}, + ) # plot data title = create_title("Optimal Network Capacities", **wildcards) @@ -452,7 +455,7 @@ def plot_opt_capacity_map( n=n, bus_values=bus_values, line_values=line_values, - link_values=n.links.p_nom_opt.replace(to_replace={pd.NA: 0}), + link_values=link_values, regions=regions, line_scale=line_scale, bus_scale=bus_scale, @@ -488,8 +491,8 @@ def plot_new_capacity_map( line_snom_opt = n.lines.s_nom_opt line_values = line_snom_opt - line_snom - link_pnom = n.links.p_nom - link_pnom_opt = n.links.p_nom_opt + link_pnom = n.links[n.links.carrier == "AC"].p_nom + link_pnom_opt = n.links[n.links.carrier == "AC"].p_nom_opt link_values = link_pnom_opt - link_pnom # plot data @@ -611,10 +614,11 @@ def plot_lmp_map(network: pypsa.Network, save: str, **wildcards): snakemake = mock_snakemake( "plot_network_maps", - interconnect="texas", - clusters=7, - ll="v1.00", - opts="REM-400SEG", + interconnect="western", + clusters="4m", + simpl="70", + ll="v1.0", + opts="1h-TCT", sector="E", ) configure_logging(snakemake) diff --git a/workflow/scripts/plot_validation_production.py b/workflow/scripts/plot_validation_production.py index 92488f9d1..f215e27e8 100644 --- a/workflow/scripts/plot_validation_production.py +++ b/workflow/scripts/plot_validation_production.py @@ -222,13 +222,17 @@ def create_optimized_by_carrier(n, order, region_buses=None): # Pos = exports from region 0 # Neg = imports to region 0 interface_lines_b0 = n.lines[(n.lines.bus0.isin(region_buses) & ~n.lines.bus1.isin(region_buses))] - interface_links_b0 = n.links[(n.links.bus0.isin(region_buses) & ~n.links.bus1.isin(region_buses))] + interface_links_b0 = n.links[ + (n.links.bus0.isin(region_buses) & ~n.links.bus1.isin(region_buses)) & n.links.carrier == "AC" + ] # bus1 branch flow (pos if branch is withdrawing from region 1) # Pos = imports to region 0 # Neg = exports from region 0 interface_lines_b1 = n.lines[(n.lines.bus1.isin(region_buses) & ~n.lines.bus0.isin(region_buses))] - interface_links_b1 = n.links[(n.links.bus1.isin(region_buses) & ~n.links.bus0.isin(region_buses))] + interface_links_b1 = n.links[ + (n.links.bus1.isin(region_buses) & ~n.links.bus0.isin(region_buses)) & n.links.carrier == "AC" + ] # imports positive, exports negative flows = n.lines_t.p1.loc[:, interface_lines_b0.index].sum(axis=1) @@ -436,12 +440,13 @@ def plot_load_shedding_map( interconnect = wildcards.get("interconnect", None) bus_scale = get_bus_scale(interconnect) if interconnect else 1 line_scale = get_line_scale(interconnect) if interconnect else 1 + link_values = n.links[n.links.carrier == "AC"].p_nom.replace(to_replace={pd.NA: 0}) fig, _ = plot_capacity_map( n=n, bus_values=bus_values, line_values=line_values, - link_values=n.links.p_nom.replace(to_replace={pd.NA: 0}), + link_values=link_values, regions=regions, line_scale=line_scale, bus_scale=bus_scale, @@ -470,12 +475,14 @@ def plot_line_loading_map( link_loading = n.links_t.p0.abs().mean() / n.links.p_nom / n.links.p_max_pu * 100 norm = plt.Normalize(vmin=0, vmax=100) + link_values = n.links[n.links.carrier == "AC"].p_nom.replace(to_replace={pd.NA: 0}) + n.carriers.loc["AC_exp", "color"] = "#dd2e23" fig, _ = plot_capacity_map( n=n, bus_values=gen / 5e3, line_values=line_values, - link_values=n.links.p_nom.replace(to_replace={pd.NA: 0}), + link_values=link_values, regions=regions, flow="mean", line_scale=line_scale, From df23d53c102591df23728fa0359b99518726faef Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 5 Feb 2025 20:39:02 -0800 Subject: [PATCH 22/75] carrier added --- workflow/scripts/add_extra_components.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index c3dbd23c5..ae6ce9203 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -633,6 +633,8 @@ def add_demand_response( ) -> None: """Add price based demand response to network.""" + n.add("Carrier", "demand_response", color="#dd2e23", nice_name="Demand Response") + shift = dr_config.get("shift", 0) if shift == 0: logger.info(f"DR not applied as allowable sift is {shift}") From fec2d3f5cc531df55705c9f441a0615119444c9a Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Thu, 6 Feb 2025 16:16:59 -0800 Subject: [PATCH 23/75] dr constraint templated --- workflow/scripts/solve_network.py | 103 +++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 44b44b958..667a453da 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -906,6 +906,87 @@ def add_battery_constraints(n): n.model.add_constraints(lhs == 0, name="Link-charger_ratio") +def add_demand_response_constraint(n, config): + """Add demand response capacity constraint.""" + + def add_capacity_constraint( + n: pypsa.Network, + shift: float, # per_unit + ): + """Add limit on deferable load. No need for snapshot weights.""" + + dr_links = n.links[n.links.carrier == "demand_response"].copy() + + if dr_links.empty: + logger.info("No demand response links identified.") + return + + deferrable_links = dr_links[dr_links.index.str.endswith("-discharger")] + deferrable_loads = n.loads[n.loads.bus.isin(deferrable_links.bus1)] + + lhs = n.model["Link-p"].loc[:, deferrable_links.index].groupby(deferrable_links.bus1).sum() + rhs = n.loads_t["p_set"][deferrable_loads.index].mul(shift).round(2) + rhs.columns.name = "bus1" + rhs = rhs.rename(columns={x: x.strip(" AC") for x in rhs}) + + # force rhs to be same order as lhs + # idk why but coordinates were not aligning and this gets around that + bus_order = [lhs.vars.bus1.data] + rhs = rhs[bus_order] + + n.model.add_constraints(lhs <= rhs, name="demand_response_capacity") + + def add_sector_capacity_constraint( + n: pypsa.Network, + shift: float, # per_unit + ): + """Add limit on deferable load. No need for snapshot weights.""" + + dr_links = n.links[n.links.carrier == "demand_response"].copy() + + if dr_links.empty: + logger.info("No demand response links identified.") + return + + deferrable_links = dr_links[dr_links.index.str.endswith("-discharger")] + deferrable_loads = n.loads[n.loads.bus.isin(deferrable_links.bus1)] + + lhs = n.model["Link-p"].loc[:, deferrable_links.index].groupby(deferrable_links.bus1).sum() + rhs = n.loads_t["p_set"][deferrable_loads.index].mul(shift).round(2) + rhs.columns.name = "bus1" + + # force rhs to be same order as lhs + # idk why but coordinates were not aligning and this gets around that + bus_order = [lhs.vars.bus1.data] + rhs = rhs[bus_order] + + n.model.add_constraints(lhs <= rhs, name="demand_response_capacity") + + dr_config = config["electricity"].get("demand_response", {}) + + shift = dr_config.get("shift", 0) + + if "sector" in config: + fn = add_sector_capacity_constraint + else: + fn = add_capacity_constraint + + if isinstance(shift, str): + if shift == "inf": + pass + else: + logger.error(f"Unknown arguement of {shift} for DR") + raise ValueError(shift) + elif isinstance(shift, int | float): + if shift < 0.001: + logger.info("Demand response not enabled") + else: + fn(n, shift) + else: + logger.error(f"Unknown arguement of {shift} for DR") + raise ValueError(shift) + + def add_sector_co2_constraints(n, config): """ Adds sector co2 constraints. @@ -1319,11 +1400,12 @@ def add_water_heater_constraints(n, config): n.model.add_constraints(lhs >= rhs, name=f"water_heater-{period}") -def add_demand_response_constraints(n, config): +def add_sector_demand_response_constraints(n, config): """ - Adds demand response equations + Add demand response equations for sector coupling. - Applies the p_nom_max here, as easier to iterate over different configs + These constraints are applied at the sector/carrier level. They are + fundamentally the same as the power sector constraints, tho. """ def add_capacity_constraint( @@ -1461,14 +1543,17 @@ def extra_functionality(n, snapshots): add_SAFE_constraints(n, config) if "SAFER" in opts and n.generators.p_nom_extendable.any(): add_SAFER_constraints(n, config) - if "TCT" in opts and n.generators.p_nom_extendable.any(): - add_technology_capacity_target_constraints(n, config) + # if "TCT" in opts and n.generators.p_nom_extendable.any(): + # add_technology_capacity_target_constraints(n, config) reserve = config["electricity"].get("operational_reserve", {}) if reserve.get("activate"): add_operational_reserve_margin(n, snapshots, config) interface_limits = config["lines"].get("interface_transmission_limits", {}) if interface_limits: add_interface_limits(n, snapshots, config) + dr_config = config["electricity"].get("demand_response", {}) + if dr_config: + add_demand_response_constraint(n, config) if "sector" in opts: add_cooling_heat_pump_constraints(n, config) if not config["sector"]["service_sector"].get("split_urban_rural", False): @@ -1480,7 +1565,7 @@ def extra_functionality(n, snapshots): water_config = config["sector"]["service_sector"].get("water_heating", {}) if not water_config.get("simple_storage", True): add_water_heater_constraints(n, config) - add_demand_response_constraints(n, config) + add_sector_demand_response_constraints(n, config) for o in opts: if "EQ" in o: @@ -1554,9 +1639,9 @@ def solve_network(n, config, solving, opts="", **kwargs): simpl="70", clusters="4m", ll="v1.0", - opts="3h-TCT", - sector="E-G", - planning_horizons="2019", + opts="1h-TCT", + sector="E", + planning_horizons="2030", ) configure_logging(snakemake) update_config_from_wildcards(snakemake.config, snakemake.wildcards) From 8be49095a0d8fde197c469dd98b817edfe71547c Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 7 Feb 2025 14:14:08 -0800 Subject: [PATCH 24/75] power sector runs --- workflow/scripts/solve_network.py | 43 +++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 667a453da..e79a3a257 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -906,7 +906,7 @@ def add_battery_constraints(n): n.model.add_constraints(lhs == 0, name="Link-charger_ratio") -def add_demand_response_constraint(n, config): +def add_demand_response_constraint(n, config, sector_study): """Add demand response capacity constraint.""" def add_capacity_constraint( @@ -931,10 +931,10 @@ def add_capacity_constraint( # force rhs to be same order as lhs # idk why but coordinates were not aligning and this gets around that - bus_order = [lhs.vars.bus1.data] - rhs = rhs[bus_order] + bus_order = lhs.vars.bus1.data + rhs = rhs[bus_order.tolist()] - n.model.add_constraints(lhs <= rhs, name="demand_response_capacity") + n.model.add_constraints(lhs <= rhs.T, name="demand_response_capacity") def add_sector_capacity_constraint( n: pypsa.Network, @@ -948,25 +948,29 @@ def add_sector_capacity_constraint( logger.info("No demand response links identified.") return - deferrable_links = dr_links[dr_links.index.str.endswith("-discharger")] - deferrable_loads = n.loads[n.loads.bus.isin(deferrable_links.bus1)] + inflow_links = dr_links[dr_links.index.str.endswith("-discharger")] + inflow = n.model["Link-p"].loc[:, inflow_links.index].groupby(inflow_links.bus1).sum() + inflow = inflow.rename({"bus1": "Bus"}) # align coordinate names - lhs = n.model["Link-p"].loc[:, deferrable_links.index].groupby(deferrable_links.bus1).sum() - rhs = n.loads_t["p_set"][deferrable_loads.index].mul(shift).round(2) - rhs.columns.name = "bus1" + outflow_links = n.links[n.links.bus0.isin(inflow_links.bus1) & ~n.links.carrier.str.endswith("-dr")] + outflow = n.model["Link-p"].loc[:, outflow_links.index].groupby(outflow_links.bus0).sum() + outflow = outflow.rename({"bus0": "Bus"}) # align coordinate names - # force rhs to be same order as lhs - # idk why but coordinates were not aligning and this gets around that - bus_order = [lhs.vars.bus1.data] - rhs = rhs[bus_order] + lhs = outflow.mul(shift) - inflow + rhs = 0 - n.model.add_constraints(lhs <= rhs, name="demand_response_capacity") + n.model.add_constraints( + lhs >= rhs, + name="demand_response_capacity", + ) dr_config = config["electricity"].get("demand_response", {}) shift = dr_config.get("shift", 0) - if "sector" in config: + # seperate, as the electrical constraint can directly apply to the load, + # while the sector constraint has to apply to the power flows out of the bus + if sector_study: fn = add_sector_capacity_constraint else: fn = add_capacity_constraint @@ -1402,7 +1406,7 @@ def add_water_heater_constraints(n, config): def add_sector_demand_response_constraints(n, config): """ - Add demand response equations for sector coupling. + Add demand response equations for sector coupling at a sector level. These constraints are applied at the sector/carrier level. They are fundamentally the same as the power sector constraints, tho. @@ -1543,8 +1547,8 @@ def extra_functionality(n, snapshots): add_SAFE_constraints(n, config) if "SAFER" in opts and n.generators.p_nom_extendable.any(): add_SAFER_constraints(n, config) - # if "TCT" in opts and n.generators.p_nom_extendable.any(): - # add_technology_capacity_target_constraints(n, config) + if "TCT" in opts and n.generators.p_nom_extendable.any(): + add_technology_capacity_target_constraints(n, config) reserve = config["electricity"].get("operational_reserve", {}) if reserve.get("activate"): add_operational_reserve_margin(n, snapshots, config) @@ -1553,7 +1557,8 @@ def extra_functionality(n, snapshots): add_interface_limits(n, snapshots, config) dr_config = config["electricity"].get("demand_response", {}) if dr_config: - add_demand_response_constraint(n, config) + sector = True if "sector" in opts else False + add_demand_response_constraint(n, config, sector) if "sector" in opts: add_cooling_heat_pump_constraints(n, config) if not config["sector"]["service_sector"].get("split_urban_rural", False): From 72eac9dd02a986a40009bc5bd2be320a515a56f2 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 7 Feb 2025 15:27:52 -0800 Subject: [PATCH 25/75] plotting updates --- workflow/scripts/plot_statistics_sector.py | 36 ++++++++++++++++------ workflow/scripts/summary_sector.py | 5 +++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/workflow/scripts/plot_statistics_sector.py b/workflow/scripts/plot_statistics_sector.py index ad830cb15..4923932bf 100644 --- a/workflow/scripts/plot_statistics_sector.py +++ b/workflow/scripts/plot_statistics_sector.py @@ -1067,13 +1067,19 @@ def plot_sector_dr_timeseries( **kwargs, ) -> tuple: + if sector == "pwr": + sec = "demand_response" # hack to use same function + else: + sec = sector + e = get_storage_level_timeseries_carrier( - n, - sector, - True, - state, - resample, - resample_fn, + n=n, + sector=sec, + remove_sns_weights=True, + state=state, + resample=resample, + resample_fn=resample_fn, + make_positive=True, ) e = e[[x for x in e if "-water-" not in x]] @@ -1097,7 +1103,7 @@ def plot_sector_dr_timeseries( df = df[df.index.get_level_values("timestep").month == month_i] if df.empty: - logger.warning(f"No Demand Response data to plot for {state}") + # logger.warning(f"No Demand Response data to plot for {state}") continue if nice_name: @@ -1354,6 +1360,17 @@ class PlottingData: "units": RoadTransportUnits, }, }, + { + "name": "production_demand_response", + "fn": plot_sector_dr_timeseries, + "nice_name": "Residential Demand Response", + "sector": "pwr", + "plot_by_month": False, + "fn_kwargs": { + # "resample": "D", + # "resample_fn": pd.Series.mean, + }, + }, { "name": "production_demand_response", "fn": plot_sector_dr_timeseries, @@ -1566,12 +1583,11 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: snakemake = mock_snakemake( "plot_sector_production", simpl="70", - opts="3h", + opts="3h-TCT", clusters="4m", ll="v1.0", - sector_opts="", sector="E-G", - planning_horizons="2018", + planning_horizons="2030", interconnect="western", ) rootpath = ".." diff --git a/workflow/scripts/summary_sector.py b/workflow/scripts/summary_sector.py index 9bc07c15b..0d26f6f9c 100644 --- a/workflow/scripts/summary_sector.py +++ b/workflow/scripts/summary_sector.py @@ -752,11 +752,16 @@ def get_storage_level_timeseries_carrier( state: Optional[str] = None, resample: Optional[str] = None, resample_fn: Optional[callable] = None, + make_positive: Optional[bool] = False, **kwargs, ) -> pd.DataFrame: df = get_storage_level_timeseries(n, sector, remove_sns_weights, state) df = df.rename(columns=n.stores.carrier) + + if make_positive: + df = df.abs() + df = df.T.groupby(level=0).sum().T if not (resample or resample_fn): From 130a467cb589ef6d3289d9c6e853833ff9ede336 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 7 Feb 2025 15:43:30 -0800 Subject: [PATCH 26/75] config updates --- workflow/repo_data/config/config.default.yaml | 4 ++++ workflow/repo_data/config/config.plotting.yaml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/workflow/repo_data/config/config.default.yaml b/workflow/repo_data/config/config.default.yaml index 54ae99b8f..c17f7b0b2 100644 --- a/workflow/repo_data/config/config.default.yaml +++ b/workflow/repo_data/config/config.default.yaml @@ -75,6 +75,10 @@ electricity: efs_speed: moderate # slow, moderate, rapid aeo: reference + demand_response: + shift: 0 + marginal_cost: 999999 + # docs : conventional: unit_commitment: false diff --git a/workflow/repo_data/config/config.plotting.yaml b/workflow/repo_data/config/config.plotting.yaml index d8c9aeff0..bad8e1bc3 100644 --- a/workflow/repo_data/config/config.plotting.yaml +++ b/workflow/repo_data/config/config.plotting.yaml @@ -85,7 +85,7 @@ plotting: "10hr_PHS_charger": "#058a79" "12hr_PHS_charger": "#047d6c" "hydrogen_ct": "#ea048a" - + "demand_response": "#8c03fc" # sector studies only @@ -318,6 +318,7 @@ plotting: ror: "Run of River" Load: "Load Shed" hydrogen_ct: "Hydrogen Combustion Turbine" + demand_response: "Demand Response" # sector studies only From 87b8561d9589df6572d48bdfea95b4dc94280069 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 7 Feb 2025 15:51:56 -0800 Subject: [PATCH 27/75] update dr docs --- docs/source/configtables/electricity.csv | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/source/configtables/electricity.csv b/docs/source/configtables/electricity.csv index 00974a915..a8f39340c 100644 --- a/docs/source/configtables/electricity.csv +++ b/docs/source/configtables/electricity.csv @@ -4,10 +4,10 @@ renewable_carriers,--,"Any subset of {solar, onwind, offwind-ac, offwind-dc, hyd retirement, --,One of ``economic`` or ``technical``,"Sets the retirement method for converntional generators. If ``technical`` all generators ``p_nom_min`` are set to ``p_nom`` to prevent selling off of the asset. Retirements are then tracked in post-proccessing. If ``economic`` existing plants have their ``p_nom_min`` set as ``0``, ``p_nom_max`` set to ``p_nom``, and capital costs set to fixed costs. Generators with ``p_nom`` are then added to handle capacity expansion.""" ,,, operational_reserve:,,,Settings for reserve requirements following `GenX `_ -#NAME?,bool,true or false,Whether to take operational reserve requirements into account during optimisation -#NAME?,--,float,share of total load -#NAME?,--,float,share of total renewable supply -#NAME?,MW,float,fixed reserve capacity +activate,bool,true or false,Whether to take operational reserve requirements into account during optimisation +epsilon_load,--,float,share of total load +epsilon_vres,--,float,share of total renewable supply +contingency,MW,float,fixed reserve capacity ,,, max_hours:,,, battery,h,float,Maximum state of charge capacity of the battery in terms of hours at full output capacity ``p_nom``. Cf. `PyPSA documentation `_. @@ -19,13 +19,15 @@ Store,--,Any subset of {``battery``},Adds extendable storage units (battery and/ Links,--,Any subset of {},Adds extendable linksat every connection where there are lines or HVDC links without capacity limits and with zero initial capacity. Hydrogen pipelines require hydrogen storage to be modelled as ``Store``. ,,, demand:,,, -#NAME?,--,"One of {``efs``, ``eia``}",Datasource for electrical load data. ``EFS`` pulls future state level electrical demand data. ``EIA`` pulls historical balancing level electrical demand dataa. -#NAME?,--,"One of {``efs``, ``aeo``}, or a float",(UNDER DEVELOPMENT) Used to scale the demand profile data. ``AEO`` will scale according to demand projections from the Annual Energy Outlook. ``EFS`` will scale according to growth rates from the Electrification Futures Study. Or can sclae according to a user defined value. -#NAME?,--,One of {``pop``},Method to dissagreagate load data. ``pop`` will dissagregate based on population from Breakthrough Energy. +profile,--,"One of {``efs``, ``eia``}",Datasource for electrical load data. ``EFS`` pulls future state level electrical demand data. ``EIA`` pulls historical balancing level electrical demand dataa. scenario:,,, -#NAME?,--,"One of {``reference``, ``medium``, ``high``}",(UNDER DEVELOPMENT) Extracts EFS data according to level of adoption -#NAME?,--,"One of {``slow``, ``moderate``, ``fast``}",(UNDER DEVELOPMENT) Extracts EFS data according to speed of electrification -#NAME?,--,One of the AEO scenarios `here `_,(UNDER DEVELOPMENT) Scales future demand according to the AEO scenario +-efs_case,--,"One of {``reference``, ``medium``, ``high``}",(UNDER DEVELOPMENT) Extracts EFS data according to level of adoption +-efs_speed,--,"One of {``slow``, ``moderate``, ``fast``}",(UNDER DEVELOPMENT) Extracts EFS data according to speed of electrification +-aeo,--,One of the AEO scenarios `here `_,(UNDER DEVELOPMENT) Scales future demand according to the AEO scenario +,,, +demand_response:,,,Settings to activate and configure demand response +-shift,per_unit,"float {0 <=, >= 1} or 'inf'",Allowable load to be shifted per snapshot. Set to 0 to turn off demand response. Set to 'inf' to not enforce capacity limits. +-marginal_cost,$/MWh,float {0 <=},Cost to store one unit of energy for one hour ,,, autarky,,, #NAME?,bool,``true`` or ``false``,Require each node to be autarkic by removing all lines and links. From 2b86e2f76dfedef5d36e9af96c755d340433234e Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sat, 8 Feb 2025 12:32:45 -0800 Subject: [PATCH 28/75] update TCT constraint name --- workflow/scripts/solve_network.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index b4f0b7e5b..53085f9df 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -193,7 +193,10 @@ def add_technology_capacity_target_constraints(n, config): """ Add Technology Capacity Target (TCT) constraint to the network. - Add minimum or maximum levels of generator nominal capacity per carrier for individual regions. Each constraint can be designated for a specified planning horizon in multi-period models. Opts and path for technology_capacity_targets.csv must be defined in config.yaml. Default file is available at config/policy_constraints/technology_capacity_targets.csv. + Add minimum or maximum levels of generator nominal capacity per carrier for individual regions. + Each constraint can be designated for a specified planning horizon in multi-period models. + Opts and path for technology_capacity_targets.csv must be defined in config.yaml. + Default file is available at config/policy_constraints/technology_capacity_targets.csv. Parameters ---------- @@ -304,7 +307,7 @@ def add_technology_capacity_target_constraints(n, config): n.model.add_constraints( lhs >= rhs, - name=f"{target.name}_{target.planning_horizon}_min", + name=f"GlobalConstraint-{target.name}_{target.planning_horizon}_min", ) logger.info( @@ -327,7 +330,7 @@ def add_technology_capacity_target_constraints(n, config): n.model.add_constraints( lhs <= rhs, - name=f"{target.name}_{target.planning_horizon}_max", + name=f"GlobalConstraint-{target.name}_{target.planning_horizon}_max", ) logger.info( From 62edb1072602a46e66e6f2dedac8321f509082fc Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sun, 9 Feb 2025 16:22:01 -0800 Subject: [PATCH 29/75] cluster config updates --- workflow/repo_data/config/config.cluster.yaml | 8 ++++---- workflow/run_slurm.sh | 2 +- workflow/snakemake_profiles/slurm/config.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/workflow/repo_data/config/config.cluster.yaml b/workflow/repo_data/config/config.cluster.yaml index 501e6daf6..dc111dc49 100644 --- a/workflow/repo_data/config/config.cluster.yaml +++ b/workflow/repo_data/config/config.cluster.yaml @@ -1,10 +1,10 @@ __default__: - account: iazevedo - partition: serc - email: ktehranchi@stanford.edu + account: + partition: + email: walltime: 00:30:00 # time limit for each job cpus_per_task: 1 # number of cores per job - chdir: $GROUP_HOME/kamran/pypsa-usa/workflow + chdir: output: logs/{rule}/log-%j.out error: logs/{rule}/errlog-%j.err diff --git a/workflow/run_slurm.sh b/workflow/run_slurm.sh index 386cfd502..7f6904a94 100644 --- a/workflow/run_slurm.sh +++ b/workflow/run_slurm.sh @@ -1,3 +1,3 @@ # SLURM specifications made in default.cluster.yaml & the individual rules # GRB_LICENSE_FILE=/share/software/user/restricted/gurobi/11.0.2/licenses/gurobi.lic⁠ -snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL --mail-user {cluster.email} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/base_paper/config.base.yaml +snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/config.default.yaml diff --git a/workflow/snakemake_profiles/slurm/config.yaml b/workflow/snakemake_profiles/slurm/config.yaml index d31465982..0a0a814b9 100644 --- a/workflow/snakemake_profiles/slurm/config.yaml +++ b/workflow/snakemake_profiles/slurm/config.yaml @@ -1,4 +1,4 @@ -cluster: sbatch --partition=serc --cpus-per-task={threads} --mem={resources.mem_mb} --job-name=smk-{rule}-{wildcards} --output=logs/{rule}/{rule}-{wildcards}-%j.out --error=logs/{rule}/{rule}-{wildcards}-.%j.err --mail-type ALL --mail-user ktehranchi@stanford.edu --account=iazevedo --ntasks=1 --nodes=1 --time={resources.walltime} +cluster: sbatch --partition= --cpus-per-task={threads} --mem={resources.mem_mb} --job-name=smk-{rule}-{wildcards} --output=logs/{rule}/{rule}-{wildcards}-%j.out --error=logs/{rule}/{rule}-{wildcards}-.%j.err --account= --ntasks=1 --nodes=1 --time={resources.walltime} default-resources: - mem_mb=5000 - walltime=00:30:00 From 930a0655140b3cf0f732b70cb1322da0ad9ee023 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sun, 9 Feb 2025 16:31:16 -0800 Subject: [PATCH 30/75] typo --- workflow/run_slurm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/run_slurm.sh b/workflow/run_slurm.sh index 7f6904a94..97598394e 100644 --- a/workflow/run_slurm.sh +++ b/workflow/run_slurm.sh @@ -1,3 +1,3 @@ # SLURM specifications made in default.cluster.yaml & the individual rules # GRB_LICENSE_FILE=/share/software/user/restricted/gurobi/11.0.2/licenses/gurobi.lic⁠ -snakemake --cluster "sbatch -A {cluster.account} --mail-type ALL -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/config.default.yaml +snakemake --cluster "sbatch -A {cluster.account} -p {cluster.partition} -t {cluster.walltime} -o {cluster.output} -e {cluster.error} -c {threads} --mem {resources.mem_mb}" --cluster-config config/config.cluster.yaml --jobs 10 --latency-wait 60 --configfile config/config.default.yaml From 12860abbf228e6f8d050d65a66aaf31543421549 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sun, 9 Feb 2025 18:56:28 -0800 Subject: [PATCH 31/75] update dependencies --- workflow/envs/environment.yaml | 6 +----- workflow/scripts/_helpers.py | 11 ----------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/workflow/envs/environment.yaml b/workflow/envs/environment.yaml index 799d35736..bac128657 100644 --- a/workflow/envs/environment.yaml +++ b/workflow/envs/environment.yaml @@ -52,17 +52,13 @@ dependencies: # TODO: check these dependencies - tqdm==4.66.1 -- pytz==2023.3.post1 - country_converter==1.0.0 -- tabula-py==2.7.0 -- fs==2.4.11 - pip: - - vresutils==0.3.1 - tsam>=1.1.0 - gurobipy==11.0.3 - highspy - duckdb==0.10.0 - flake8 - dill - - kaleido + - kaleido # for plotly export diff --git a/workflow/scripts/_helpers.py b/workflow/scripts/_helpers.py index 715952cc9..20e78291d 100644 --- a/workflow/scripts/_helpers.py +++ b/workflow/scripts/_helpers.py @@ -428,17 +428,6 @@ def make_accessable(*ios): return snakemake -def local_to_utc(group): - import pytz - from constants import STATE_2_TIMEZONE - - timezone_str = STATE_2_TIMEZONE[group.name] - timezone = pytz.timezone(timezone_str) - time_shift = -1 * group.iloc[0].tz_localize(timezone).utcoffset().total_seconds() / 3600 - utc = group + pd.Timedelta(hours=time_shift) - return utc - - def validate_checksum(file_path, zenodo_url=None, checksum=None): """ Validate file checksum against provided or Zenodo-retrieved checksum. From d0a626ad4cf941b9fd5ac8e689df2c360c3cf565 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 09:52:44 -0800 Subject: [PATCH 32/75] fix scenario comparison --- workflow/scripts/scenario_comparison.py | 186 +++++++++++++++++++++++- 1 file changed, 179 insertions(+), 7 deletions(-) diff --git a/workflow/scripts/scenario_comparison.py b/workflow/scripts/scenario_comparison.py index 8997a6967..57755328e 100644 --- a/workflow/scripts/scenario_comparison.py +++ b/workflow/scripts/scenario_comparison.py @@ -60,6 +60,7 @@ python script_name.py """ +import argparse from pathlib import Path import numpy as np @@ -119,6 +120,155 @@ def process_data(data, alias_dict=None, new_order=None): return stats +def prepare_combined_dataframe( + stats, + variable, + carriers, + include_link=False, + as_pct=False, + variable_units=None, +): + factor_units = {"GW": 1e3, "GWh": 1e3, "%": 1}.get(variable_units, 1e9) + + data = [] + for scenario, df in stats.items(): + df = df["statistics"].fillna(0) + + tech_filter = ["Generator", "StorageUnit", "Link"] + + df = df.loc[df.index.get_level_values(0).isin(tech_filter), variable] + df.index = df.index.get_level_values(1) + df = df.reindex(carriers.index).dropna() + + if as_pct: + df = ((df / df.sum()) * 100).round(2) + + for horizon in df.columns: + df_horizon = df[horizon] / factor_units + df_horizon = df_horizon.reset_index() + df_horizon["Scenario"] = scenario + df_horizon["horizon"] = horizon + data.append(df_horizon.rename(columns={horizon: "statistics"})) + + combined_df = pd.concat(data, ignore_index=True) + combined_df["scenario_name"] = combined_df["Scenario"].apply(lambda x: x.split("_")[0]) + combined_df["trans_expansion"] = combined_df["Scenario"].apply(lambda x: x.split("_")[1]) + combined_df.to_csv(figures_path / f"{variable}_comparison.csv") + return combined_df + + +def plot_scenario_comparison( + combined_df, + carriers, + variable, + variable_units, + title, + figures_path, + colors, + include_link=False, + reference_scenario=None, +): + planning_horizons = combined_df["horizon"].unique() + scenarios = combined_df["Scenario"].unique() + if not include_link: + combined_df = combined_df[~combined_df["nice_name"].isin(["Link", "Ac"])] + + fig, axes = plt.subplots( + nrows=len(planning_horizons), + ncols=1, + figsize=(8, 1.2 * len(planning_horizons) + 0.2 * len(scenarios)), + sharex=True, + ) + axes = np.atleast_1d(axes) # Ensure axes is iterable for single horizon + + for ax, horizon in zip(axes, planning_horizons): + horizon_df = combined_df[combined_df["horizon"] == horizon] + y_positions = np.arange(len(scenarios)) + + for j, scenario in enumerate(scenarios): + scenario_df = horizon_df[horizon_df["Scenario"] == scenario] + bottoms = np.zeros(len(y_positions)) + for tech in scenario_df["nice_name"].unique(): + values = scenario_df[scenario_df["nice_name"] == tech]["statistics"].values[0] + ax.barh( + y_positions[j], + values, + left=bottoms[j], + color=colors[tech], + label=tech if j == 0 else "", + ) + bottoms[j] += values + + ax.text(1.01, 0.5, horizon, transform=ax.transAxes, va="center", rotation="vertical") + ax.set_yticks(y_positions) + ax.set_yticklabels(scenarios) + ax.grid(True, axis="x", linestyle="--", alpha=0.5) + + plt.xlabel(f"{variable} [{variable_units}]") + plt.subplots_adjust(hspace=0.5) + + carriers_plotted = carriers.loc[carriers.index.intersection(combined_df["nice_name"].unique())] + legend_handles = [plt.Rectangle((0, 0), 1, 1, color=colors[tech]) for tech in carriers_plotted.index] + fig.legend( + handles=legend_handles, + labels=carriers_plotted.legend_name.tolist(), + loc="lower center", + bbox_to_anchor=(0.5, -0.4), + ncol=4, + title="Technologies", + ) + fig.suptitle(title, fontsize=12, fontweight="bold") + plt.tight_layout() + plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") + + if reference_scenario: + _plot_reference_comparison(combined_df, reference_scenario, carriers, colors, figures_path, variable, horizon) + + return + + +def _plot_reference_comparison(combined_df, reference_scenario, carriers, colors, figures_path, variable, horizon): + combined_df = combined_df.set_index("Scenario") + combined_df = combined_df.loc[combined_df.horizon == horizon] + ref = combined_df.loc[reference_scenario].set_index("nice_name") + combined_df = combined_df.reset_index().set_index("nice_name") + for carrier in combined_df.index.unique(): + combined_df.loc[carrier, "statistics"] = ( + (combined_df.loc[carrier, "statistics"] - ref.loc[carrier, "statistics"]) / ref.statistics.sum() * 100 + ) + combined_df = combined_df.reset_index().set_index("Scenario") + stacked_data = combined_df.reset_index().pivot(index="Scenario", columns="nice_name", values="statistics") + stacked_data.plot( + kind="bar", + stacked=True, + figsize=(10, 7), + color=[colors[tech] for tech in stacked_data.columns], + legend=False, + ) + plt.ylabel("∆ Capacity[%]") + plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + combined_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") + + # combined_df = combined_df.set_index(["Scenario", "horizon"]) + # ref = combined_df.loc[(reference_scenario, slice(None))] + # combined_df = combined_df.reset_index() + + # for horizon in ref.index.get_level_values(0).unique(): + # ref_stats = ref.loc[tech]["statistics"] + # combined_df["pct_diff"] = combined_df["statistics"] / ref_stats.sum() * 100 - 100 + + # pivoted_data = combined_df.pivot(index="Scenario", columns="nice_name", values="pct_diff") + # pivoted_data.plot( + # kind="bar", + # stacked=True, + # figsize=(10, 7), + # color=[colors[tech] for tech in pivoted_data.columns], + # legend=False, + # ) + # plt.ylabel("∆ Capacity [%]") + # plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + + # Plot comparison def scenario_comparison( stats, @@ -137,7 +287,7 @@ def scenario_comparison( fig, axes = plt.subplots( nrows=len(planning_horizons), ncols=1, - figsize=(8, 3 * len(planning_horizons)), + figsize=(8, 1.5 * len(planning_horizons) + 0.2 * len(stats)), sharex=True, ) if variable_units in ["GW", "GWh"]: @@ -154,7 +304,7 @@ def scenario_comparison( y_positions = np.arange(len(stats)) for j, (scenario, df) in enumerate(stats.items()): df = df["statistics"].fillna(0) - bottoms = np.zeros(len(df.columns)) + bottoms = np.zeros(len(y_positions)) if include_link: df = df.loc[df.index.get_level_values(0).isin(["Generator", "StorageUnit", "Link"]), variable] df = df.loc[~(df.index.get_level_values(1) == "Ac")] @@ -195,6 +345,10 @@ def scenario_comparison( plt.tight_layout() plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") + combined_df["scenario_name"] = combined_df["Scenario"].apply(lambda x: x.split("_")[0]) + combined_df["trans_expansion"] = combined_df["Scenario"].apply(lambda x: x.split("_")[1]) + combined_df.to_csv(figures_path / f"{variable}_comparison.csv") + if reference_scenario: combined_df = combined_df.set_index("Scenario") combined_df = combined_df.query("horizon == @horizon").drop(columns="horizon") # only plot last horizon @@ -202,7 +356,7 @@ def scenario_comparison( combined_df = combined_df.reset_index().set_index("nice_name") for scenario in combined_df.index.unique(): combined_df.loc[scenario, "statistics"] = ( - (combined_df.loc[scenario, "statistics"] - ref.loc[scenario, "statistics"]) / ref.sum().values * 100 + (combined_df.loc[scenario, "statistics"] - ref.loc[scenario, "statistics"]) / ref.statistics.sum() * 100 ) combined_df = combined_df.reset_index().set_index("Scenario") stacked_data = combined_df.reset_index().pivot(index="Scenario", columns="nice_name", values="statistics") @@ -216,6 +370,7 @@ def scenario_comparison( plt.ylabel("∆ Capacity[%]") plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") combined_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") + return combined_df def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path, reference_scenario=None): @@ -250,11 +405,19 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path pct_df.plot(kind="bar", y="statistics", title="Total System Costs", legend=False) plt.ylabel("∆ Annualized System Costs [%]") plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + pct_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") + combined_df.to_csv(figures_path / f"{variable}_comparison.csv") # Main execution if __name__ == "__main__": - yaml_path = Path.cwd().parent / "config/scenario_comparison.yaml" # Path to the YAML file + # Parse command line arguments + parser = argparse.ArgumentParser(description="Run scenario comparison script.") + parser.add_argument("yaml_name", type=str, help="Name of the YAML configuration file.") + args = parser.parse_args() + + yaml_name = args.yaml_name # Name of the YAML file from command line argument + yaml_path = Path.cwd() / yaml_name # Path to the YAML file # Load and process data config = load_yaml_config(yaml_path) @@ -266,7 +429,7 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path reference_scenario = config.get("reference_scenario", None) figures_path = ( - Path.cwd().parent / f"results/{config.get('output_folder_name', 'scenario_comparison')}" + Path.cwd() / f"results/{config.get('output_folder_name', 'scenario_comparison')}" ) # Directory to save the figures in the parent of cwd figures_path.mkdir(exist_ok=True) @@ -276,6 +439,7 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path n = pypsa.Network(config["network"]["path"]) # Example carrier setup carriers = get_carriers(n) + carriers.to_csv(figures_path / "carriers.csv") # Example variable and title variable = "Optimal Capacity" @@ -283,13 +447,21 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path title = "Capacity Comparison" # Generate plots - scenario_comparison( + combined_df = prepare_combined_dataframe( processed_data, variable, - variable_units, carriers, + as_pct=False, + variable_units=variable_units, + ) + plot_scenario_comparison( + combined_df, + carriers, + variable, + variable_units, title, figures_path, + colors=carriers["color"], reference_scenario=reference_scenario, ) From 7cad5db3890db7deca6b6eae36ec47be36bf1228 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 12:32:35 -0800 Subject: [PATCH 33/75] Update pre-commit config to use Ruff for formatting and linting --- .pre-commit-config.yaml | 35 ++++++----------------------------- pyproject.toml | 4 +++- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1035c701f..c9681ccc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,19 +17,13 @@ repos: - id: pretty-format-yaml args: [--autofix, --indent, "2", --preserve-quotes] -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - name: isort (python) - - repo: https://github.com/asottile/add-trailing-comma rev: v3.1.0 hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.19.0 + rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] @@ -45,31 +39,14 @@ repos: hooks: - id: snakefmt -# - repo: https://github.com/PyCQA/docformatter -# rev: v1.7.5 -# hooks: -# - id: docformatter -# args: ["--in-place", "--make-summary-multi-line", "--pre-summary-newline"] - - - repo: https://github.com/keewis/blackdoc rev: v0.3.9 hooks: - id: blackdoc -- repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black - language_version: python3.11 - args: ["--line-length", "120"] - - id: black-jupyter - language_version: python3.11 - args: ["--line-length", "120"] - - -- repo: https://github.com/pycqa/flake8 - rev: 7.1.1 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.6 hooks: - - id: flake8 - args: [--select=F841] # F841 is the error code for unused variables + - id: ruff + args: ["check"] + - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 08c1ea782..2fbb59d38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dev = [ myproj = "workflow/scripts" [tool.ruff] -line-length = 110 +line-length = 120 target-version = "py311" exclude = [ ".eggs", @@ -80,7 +80,9 @@ select = [ "UP", # pyupgrade "D", # pydocstyle "C90", # Complex code + "I", # isort (import sorting) ] + # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" extend-ignore = ['D105', 'D107', 'D205', 'D415'] From c50c977919af6c763a3743968069d369ca4f7672 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 16:11:23 -0800 Subject: [PATCH 34/75] update linting options --- pyproject.toml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2fbb59d38..69da3fad1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ exclude = [ select = [ "E", # pycodestyle "TD", # flake-8 todos - "PD", # pandas vet + #"PD", # pandas vet "RUF", # Ruff rules "N", # pep8 "F", # pyflakes @@ -82,10 +82,32 @@ select = [ "C90", # Complex code "I", # isort (import sorting) ] +mccabe.max-complexity = 10 # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-ignore = ['D105', 'D107', 'D205', 'D415'] +extend-ignore = [ + "D105", # Ignore: Missing docstring in magic methods (__init__, __str__, etc.). + "D107", # Ignore: Missing docstring in __init__ method. + "D205", # Ignore: 1st line of docstring should be separated from the summary with a blank line. + "D415", # Ignore: First line of a docstring should end with a period, question mark, or exclamation mark. + "D401", # Ignore: First line of a docstring should be in imperative mood (e.g., "Get" instead of "Gets"). + "D419", # Ignore: Empty docstrings should not be allowed. + "D103", # Ignore: Missing docstring in a public function. + "TD002", # Ignore: TODO comments should have an author name (e.g., "# TODO (John): Fix this"). + "TD003", # Ignore: TODO comments should have a specific issue/task reference. + "PD010", # Ignore: `df.isna().sum()` should be replaced with `df.isnull().sum()` (allows both styles). + "PD901", # Ignore: Avoid using `df` as a variable name for Pandas DataFrames (allows using `df`). + "C901", # Prevents Ruff from auto-fixing overly complex functions. + "N802", # Function naming lower case + "E501" +] + +unfixable = [ + + "E741", # Prevents Ruff from fixing ambiguous variable names (e.g., `l`, `O`, `I` which look like numbers). +] + pydocstyle.convention = "numpy" [tool.ruff.format] From 8161fa75f48b827bd476c46349183b2bc8867e17 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 16:23:12 -0800 Subject: [PATCH 35/75] Update pre-commit config to use Ruff for formatting and linting --- .pre-commit-config.yaml | 5 +++-- pyproject.toml | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c9681ccc6..93a12facc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,8 +45,9 @@ repos: - id: blackdoc - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.6 + rev: v0.1.4 # Check for the latest version hooks: - id: ruff - args: ["check"] + args: ["check", "--config=pyproject.toml"] - id: ruff-format + args: ["--config=pyproject.toml"] diff --git a/pyproject.toml b/pyproject.toml index 69da3fad1..4ebd047a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,5 +115,4 @@ quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" -docstring-code-format = true docstring-code-line-length = "dynamic" From fbce391e9c70b72ea4ecf7579454e902a8fea053 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 16:47:56 -0800 Subject: [PATCH 36/75] ruff re-formating --- .pre-commit-config.yaml | 2 +- docs/source/conf.py | 19 +- pyproject.toml | 13 +- .../docs/service/clustered-load.ipynb | 2 - .../docs/validation/load_validatiaon.ipynb | 1 - .../sector_loads/load_profiles.ipynb | 7 - workflow/rules/build_electricity.smk | 23 - workflow/scripts/_helpers.py | 48 +- workflow/scripts/add_demand.py | 14 +- workflow/scripts/add_electricity.py | 148 ++-- workflow/scripts/add_extra_components.py | 44 +- workflow/scripts/add_sectors.py | 34 +- .../scripts/additional_policy_constraints.py | 1 + workflow/scripts/build_base_network.py | 71 +- workflow/scripts/build_bus_regions.py | 11 +- .../build_clustered_population_layouts.py | 4 +- workflow/scripts/build_cop_profiles.py | 1 + workflow/scripts/build_cost_data.py | 36 +- workflow/scripts/build_cutout.py | 4 +- workflow/scripts/build_demand.py | 342 +++------ workflow/scripts/build_electricity_sector.py | 31 +- workflow/scripts/build_emission_tracking.py | 41 +- workflow/scripts/build_fuel_prices.py | 18 +- workflow/scripts/build_heat.py | 146 ++-- workflow/scripts/build_hydro_profile.py | 213 ------ workflow/scripts/build_natural_gas.py | 135 +--- workflow/scripts/build_population_layouts.py | 38 +- workflow/scripts/build_powerplants.py | 72 +- workflow/scripts/build_renewable_profiles.py | 167 +--- workflow/scripts/build_sector_costs.py | 79 +- workflow/scripts/build_shapes.py | 47 +- workflow/scripts/build_stock_data.py | 161 ++-- .../scripts/build_temperature_profiles.py | 8 +- workflow/scripts/build_transportation.py | 92 +-- workflow/scripts/cluster_network.py | 66 +- workflow/scripts/constants.py | 12 +- workflow/scripts/constants_sector.py | 7 +- workflow/scripts/eia.py | 170 ++--- workflow/scripts/eulp.py | 39 +- .../scripts/generate_stochastic_samples.py | 712 ------------------ workflow/scripts/log.py | 2 + workflow/scripts/plot_natural_gas.py | 79 +- workflow/scripts/plot_network_maps.py | 58 +- workflow/scripts/plot_sankey_carbon.py | 10 +- workflow/scripts/plot_sankey_energy.py | 57 +- workflow/scripts/plot_statistics.py | 144 ++-- workflow/scripts/plot_statistics_sector.py | 252 ++----- .../scripts/plot_validation_production.py | 130 +--- workflow/scripts/plot_validation_sector.py | 89 +-- workflow/scripts/prepare_network.py | 67 +- workflow/scripts/retrieve_caiso_data.py | 30 +- workflow/scripts/retrieve_databundles.py | 5 +- workflow/scripts/retrieve_egs.py | 7 +- workflow/scripts/retrieve_eulp.py | 34 +- .../scripts/retrieve_gridemissions_data.py | 12 +- workflow/scripts/retrieve_pudl.py | 14 +- workflow/scripts/scenario_comparison.py | 275 ++++--- workflow/scripts/simplify_network.py | 25 +- workflow/scripts/solve_network.py | 153 ++-- workflow/scripts/summary.py | 49 +- workflow/scripts/summary_natural_gas.py | 58 +- workflow/scripts/summary_sector.py | 166 ++-- 62 files changed, 1366 insertions(+), 3429 deletions(-) delete mode 100644 workflow/scripts/build_hydro_profile.py delete mode 100644 workflow/scripts/generate_stochastic_samples.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93a12facc..7c39110e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,6 +48,6 @@ repos: rev: v0.1.4 # Check for the latest version hooks: - id: ruff - args: ["check", "--config=pyproject.toml"] + args: ["--config=pyproject.toml", "--fix"] - id: ruff-format args: ["--config=pyproject.toml"] diff --git a/docs/source/conf.py b/docs/source/conf.py index a45e889ee..d1d348889 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,11 +1,14 @@ -# Configuration file for the Sphinx documentation builder. -# +"""Configuration file for the Sphinx documentation builder.""" + # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +import os +import sys + project = "pypsa-usa" copyright = "2024, Kamran Tehranchi, Trevor Barnes" author = "Kamran Tehranchi, Trevor Barnes" @@ -13,8 +16,6 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -import os -import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -22,8 +23,8 @@ sys.path.insert(0, os.path.abspath("../../workflow/scripts")) extensions = [ - #'sphinx.ext.autodoc', - #'sphinx.ext.autosummary', + # 'sphinx.ext.autodoc', + # 'sphinx.ext.autosummary', "myst_parser", # "sphinx.ext.autosectionlabel", "sphinx.ext.intersphinx", @@ -32,9 +33,9 @@ "sphinx.ext.napoleon", "sphinx.ext.graphviz", # "sphinxcontrib.bibtex", - #'sphinx.ext.pngmath', - #'sphinxcontrib.tikz', - #'rinoh.frontend.sphinx', + # 'sphinx.ext.pngmath', + # 'sphinxcontrib.tikz', + # 'rinoh.frontend.sphinx', "sphinx.ext.imgconverter", # for SVG conversion ] myst_enable_extensions = ["html_image", "colon_fence", "amsmath"] diff --git a/pyproject.toml b/pyproject.toml index 4ebd047a7..4917834fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,12 +61,7 @@ exclude = [ ".venv", ".vscode", "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "site-packages", - "venv", + "_build" ] [tool.ruff.lint] @@ -103,11 +98,6 @@ extend-ignore = [ "E501" ] -unfixable = [ - - "E741", # Prevents Ruff from fixing ambiguous variable names (e.g., `l`, `O`, `I` which look like numbers). -] - pydocstyle.convention = "numpy" [tool.ruff.format] @@ -115,4 +105,3 @@ quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" -docstring-code-line-length = "dynamic" diff --git a/workflow/notebooks/docs/service/clustered-load.ipynb b/workflow/notebooks/docs/service/clustered-load.ipynb index 96ea9ad7c..816b10040 100644 --- a/workflow/notebooks/docs/service/clustered-load.ipynb +++ b/workflow/notebooks/docs/service/clustered-load.ipynb @@ -145,14 +145,12 @@ " mapper = {\"ashp\": \"Air Source Heat Pump\", \"gshp\": \"Ground Source Heat Pump\"}\n", "\n", " for i, hp in enumerate([\"ashp\", \"gshp\"]):\n", - "\n", " df = cops[[x for x in cops if x.endswith(hp)]]\n", " avg = df.mean(axis=1)\n", "\n", " palette = sns.color_palette([\"lightgray\"], df.shape[1])\n", "\n", " try:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", diff --git a/workflow/notebooks/docs/validation/load_validatiaon.ipynb b/workflow/notebooks/docs/validation/load_validatiaon.ipynb index 3cd1d4c36..85d3a9857 100644 --- a/workflow/notebooks/docs/validation/load_validatiaon.ipynb +++ b/workflow/notebooks/docs/validation/load_validatiaon.ipynb @@ -288,7 +288,6 @@ "\n", "\n", "def plot_sector_state(efs, other, other_name, sector):\n", - "\n", " state_efs_data = efs.reset_index().drop(columns=\"snapshot\").groupby(\"State\").sum().rename(columns={\"value\": \"EFS\"})\n", " state_other_data = (\n", " other.reset_index().drop(columns=\"snapshot\").groupby(\"State\").sum().rename(columns={\"value\": other_name})\n", diff --git a/workflow/notebooks/sector_loads/load_profiles.ipynb b/workflow/notebooks/sector_loads/load_profiles.ipynb index cb6c909f2..2ac9bd620 100644 --- a/workflow/notebooks/sector_loads/load_profiles.ipynb +++ b/workflow/notebooks/sector_loads/load_profiles.ipynb @@ -79,7 +79,6 @@ "outputs": [], "source": [ "def get_profile(df: pd.DataFrame, bus: Optional[str] = None) -> pd.DataFrame:\n", - "\n", " if not bus:\n", " bus = df.columns[0]\n", "\n", @@ -142,7 +141,6 @@ " palette = sns.color_palette([\"lightgray\"], df.shape[1])\n", "\n", " if nrows > 1:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", @@ -161,7 +159,6 @@ " axs[row, col].set(yscale=\"log\")\n", "\n", " else:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", @@ -251,7 +248,6 @@ " palette = sns.color_palette([\"lightgray\"], df.shape[1])\n", "\n", " if nrows > 1:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", @@ -270,7 +266,6 @@ " axs[row, col].set(yscale=\"log\")\n", "\n", " else:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", @@ -359,7 +354,6 @@ " palette = sns.color_palette([\"lightgray\"], df.shape[1])\n", "\n", " if nrows > 1:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", @@ -378,7 +372,6 @@ " axs[row, col].set(yscale=\"log\")\n", "\n", " else:\n", - "\n", " sns.lineplot(\n", " df,\n", " color=\"lightgray\",\n", diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 2fa985da8..05b01a588 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -140,29 +140,6 @@ if config["enable"].get("build_cutout", False): "../scripts/build_cutout.py" -rule build_hydro_profile: - params: - hydro=config_provider("renewable", "hydro"), - snapshots=config_provider("snapshots"), - input: - reeds_shapes=RESOURCES + "{interconnect}/Geospatial/reeds_shapes.geojson", - cutout=lambda w: f"cutouts/" - + CDIR - + "{interconnect}_" - + config_provider("renewable", "hydro", "cutout")(w) - + ".nc", - output: - profile=RESOURCES + "{interconnect}/profile_hydro.nc", - log: - LOGS + "{interconnect}/build_hydro_profile.log", - resources: - mem_mb=5000, - conda: - "../envs/environment.yaml" - script: - "../scripts/build_hydro_profile.py" - - rule build_renewable_profiles: params: renewable=config["renewable"], diff --git a/workflow/scripts/_helpers.py b/workflow/scripts/_helpers.py index 20e78291d..61e71754b 100644 --- a/workflow/scripts/_helpers.py +++ b/workflow/scripts/_helpers.py @@ -35,8 +35,6 @@ def configure_logging(snakemake, skip_handlers=False): skip_handlers : True | False (default) Do (not) skip the default handlers created for redirecting output to STDERR and file. """ - import logging - kwargs = snakemake.config.get("logging", dict()).copy() kwargs.setdefault("level", "INFO") @@ -64,8 +62,6 @@ def configure_logging(snakemake, skip_handlers=False): def setup_custom_logger(name): - import logging - formatter = logging.Formatter( fmt="%(asctime)s - %(levelname)s - %(module)s - %(message)s", ) @@ -97,7 +93,9 @@ def load_network(import_name=None, custom_components=None): override_components: ShadowPrice: component: ["shadow_prices","Shadow price for a global constraint.",np.nan] - attributes: + + Attributes + ---------- name: ["string","n/a","n/a","Unique name","Input (required)"] value: ["float","n/a",0.,"shadow value","Output"] @@ -187,8 +185,8 @@ def load_network_for_plots(fn, tech_costs, config, combine_hydro_ps=True): # bus_carrier = n.storage_units.bus.map(n.buses.carrier) # n.storage_units.loc[bus_carrier == "heat","carrier"] = "water tanks" - Nyears = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 - costs = load_costs(tech_costs, config["costs"], config["electricity"], Nyears) + num_years = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 + costs = load_costs(tech_costs, config["costs"], config["electricity"], num_years) update_transmission_costs(n, costs) return n @@ -260,7 +258,6 @@ def aggregate_p_curtailed(n): def aggregate_costs(n, flatten=False, opts=None, existing_only=False): - components = dict( Link=("p_nom", "p0"), Generator=("p_nom", "p"), @@ -307,8 +304,8 @@ def progress_retrieve(url, file): pbar = ProgressBar(0, 100) - def dlProgress(count, blockSize, totalSize): - pbar.update(int(count * blockSize * 100 / totalSize)) + def dlProgress(count, block_size, total_size): + pbar.update(int(count * block_size * 100 / total_size)) urllib.request.urlretrieve(url, file, reporthook=dlProgress) @@ -331,10 +328,6 @@ def get_aggregation_strategies(aggregation_strategies): def export_network_for_gis_mapping(n, output_path): - import os - - import pandas as pd - # Creating GIS Table for Mapping Lines in QGIS lines_gis = n.lines.copy() lines_gis["latitude1"] = n.buses.loc[lines_gis.bus0].y.values @@ -363,7 +356,7 @@ def export_network_for_gis_mapping(n, output_path): def mock_snakemake(rulename, **wildcards): """ - This function is expected to be executed from the 'scripts'-directory of ' + Function is expected to be executed from the 'scripts'-directory of ' the snakemake project. It returns a snakemake.script.Snakemake object, based on the Snakefile. @@ -507,12 +500,13 @@ def set_scenario_config(snakemake): def update_config_with_sector_opts(config, sector_opts): + from packaging.version import parse from snakemake.utils import update_config for o in sector_opts.split("-"): if o.startswith("CF+"): - l = o.split("+")[1:] - update_config(config, parse(l)) + l_ = o.split("+")[1:] + update_config(config, parse(l_)) def get_opt(opts, expr, flags=None): @@ -531,9 +525,7 @@ def get_opt(opts, expr, flags=None): def find_opt(opts, expr): - """ - Return if available the float after the expression. - """ + """Return if available the float after the expression.""" for o in opts: if expr in o: m = re.findall(r"m?\d+(?:[\.p]\d+)?", o) @@ -545,9 +537,8 @@ def find_opt(opts, expr): def update_config_from_wildcards(config, w, inplace=True): - """ - Parses configuration settings from wildcards and updates the config. - """ + """Parses configuration settings from wildcards and updates the config.""" + from packaging.version import parse if not inplace: config = copy.deepcopy(config) @@ -733,17 +724,17 @@ def get_scenarios(run): def get_rdir(run): scenario_config = run.get("scenarios", {}) if run["name"] and scenario_config.get("enable"): - RDIR = "{run}/" + rdir = "{run}/" elif run["name"]: - RDIR = run["name"] + "/" + rdir = run["name"] + "/" else: - RDIR = "" + rdir = "" prefix = run.get("prefix", "") if prefix: - RDIR = f"{prefix}/{RDIR}" + rdir = f"{prefix}/{rdir}" - return RDIR + return rdir def get_run_path(fn, dir, rdir, shared_resources): @@ -825,7 +816,6 @@ def get_snapshots( Taken from PyPSA-Eur implementation """ - time = pd.date_range(freq=freq, **snapshots, **kwargs) if drop_leap_day and time.is_leap_year.any(): time = time[~((time.month == 2) & (time.day == 29))] diff --git a/workflow/scripts/add_demand.py b/workflow/scripts/add_demand.py index c83e1d376..b900e57ed 100644 --- a/workflow/scripts/add_demand.py +++ b/workflow/scripts/add_demand.py @@ -10,12 +10,7 @@ import pandas as pd import pypsa -from _helpers import ( - configure_logging, - get_multiindex_snapshots, - get_snapshots, - mock_snakemake, -) +from _helpers import configure_logging, get_multiindex_snapshots, mock_snakemake from constants_sector import ( AirTransport, BoatTransport, @@ -83,7 +78,6 @@ def attach_demand(n: pypsa.Network, df: pd.DataFrame, carrier: str, suffix: str) demand_files = [demand_files] if sectors == "E" or sectors == "": # electricity only - assert len(demand_files) == 1 suffix = "" @@ -91,17 +85,14 @@ def attach_demand(n: pypsa.Network, df: pd.DataFrame, carrier: str, suffix: str) df = pd.read_csv(demand_files[0], index_col=0) attach_demand(n, df, carrier, suffix) - logger.info(f"Electricity demand added to network") + logger.info("Electricity demand added to network") else: # sector files - for demand_file in demand_files: - parsed_name = Path(demand_file).name.split("_") parsed_name[-1] = parsed_name[-1].split(".pkl")[0] if len(parsed_name) == 2: - sector = parsed_name[0].upper() end_use = parsed_name[1].upper().replace("-", "_") @@ -113,7 +104,6 @@ def attach_demand(n: pypsa.Network, df: pd.DataFrame, carrier: str, suffix: str) log_statement = f"{sector} {end_use} demand added to network" elif len(parsed_name) == 3: - sector = parsed_name[0].upper() subsector = parsed_name[1].replace("-", "_") end_use = parsed_name[2].upper() # lpg | elec diff --git a/workflow/scripts/add_electricity.py b/workflow/scripts/add_electricity.py index 45a7cdfce..55ef70b98 100755 --- a/workflow/scripts/add_electricity.py +++ b/workflow/scripts/add_electricity.py @@ -1,40 +1,10 @@ # PyPSA USA Authors """ -**Description** +Adds existing conventional generators, renewable generators, and storage devices to the network. -This module integrates data produced by `build_renewable_profiles` and `build_cost_data`, `build_fuel_prices`, and `add_demand` to create a network model that includes generators and their associated costs. The module attaches generators and storage units to the network created by `add_demand`. Each generator is assigned regional capital costs, and regional and daily or monthly marginal costs. - -Extendable generators are assigned a maximum capacity based on land-use constraints defined in `build_renewable_profiles`. - -**Relevant Settings** - -.. code:: yaml - - snapshots: - start: - end: - inclusive: - - electricity: - -.. seealso:: - Documentation of the configuration file `config/config.yaml` at :ref:`costs_cf`, - :ref:`electricity_cf`, :ref:`renewable_cf`, :ref:`lines_cf` - -**Inputs** - -- ``resources/costs.csv``: The database of cost assumptions for all included technologies for specific years from various sources; e.g. discount rate, lifetime, investment (CAPEX), fixed operation and maintenance (FOM), variable operation and maintenance (VOM), fuel costs, efficiency, carbon-dioxide intensity. -- ``resources/regions_onshore.geojson``: confer :ref:`busregions` -- ``resources/profile_{}.nc``: all technologies in ``config["renewables"].keys()``, confer :ref:`renewableprofiles`. -- ``networks/elec_base_network.nc``: confer :ref:`base` -- ``resources/ng_fuel_prices.csv``: Natural gas fuel prices by state and BA. - -**Outputs** - -- ``networks/elec_base_network_l_pp.nc`` +This script will add all generator unit availabilities (capacity-factors) to the network, for all investment horizons. """ - import logging import os @@ -49,7 +19,6 @@ calculate_annuity, configure_logging, export_network_for_gis_mapping, - load_costs, update_p_nom_max, weighted_avg, ) @@ -86,7 +55,6 @@ def sanitize_carriers(n, config): -------- Raises a warning if any carrier's "tech_colors" are not defined in the config dictionary. """ - for c in n.iterate_components(): if "carrier" in c.df: add_missing_carriers(n, c.df.carrier) @@ -107,18 +75,14 @@ def sanitize_carriers(n, config): def add_missing_carriers(n, carriers): - """ - Function to add missing carriers to the network without raising errors. - """ + """Function to add missing carriers to the network without raising errors.""" missing_carriers = set(carriers) - set(n.carriers.index) if len(missing_carriers) > 0: n.madd("Carrier", missing_carriers) def clean_locational_multiplier(df: pd.DataFrame): - """ - Updates format of locational multiplier data. - """ + """Updates format of locational multiplier data.""" df = df.fillna(1) df = df[["State", "Location Variation"]] return df.groupby("State").mean() @@ -129,12 +93,8 @@ def update_capital_costs( carrier: str, costs: pd.DataFrame, multiplier: pd.DataFrame, - Nyears: float = 1.0, ): - """ - Applies regional multipliers to capital cost data. - """ - + """Applies regional multipliers to capital cost data.""" # map generators to states bus_state_mapper = n.buses.to_dict()["state"] gen = n.generators[n.generators.carrier == carrier].copy() @@ -184,7 +144,6 @@ def apply_dynamic_pricing( vom: float = 0 Additional flat $/MWh cost to add onto the fuel costs """ - assert geography in n.buses.columns gens = n.generators.copy() @@ -250,13 +209,13 @@ def update_transmission_costs(n, costs, length_factor=1.0): def load_powerplants( plants_fn, investment_periods: list[int], - interconnect: str = None, + interconnect: str | None = None, ) -> pd.DataFrame: plants = pd.read_csv( plants_fn, ) # Filter out non-conus plants and plants that are not built by first investment period. - plants.set_index("generator_name", inplace=True) + plants = plants.set_index("generator_name") plants = plants[plants.build_year <= investment_periods[0]] plants = plants[plants.nerc_region != "non-conus"] if (interconnect is not None) & (interconnect != "usa"): @@ -266,9 +225,7 @@ def load_powerplants( def match_nearest_bus(plants_subset, buses_subset): - """ - Assign the nearest bus to each plant in the given subsets. - """ + """Assign the nearest bus to each plant in the given subsets.""" if plants_subset.empty or buses_subset.empty: return plants_subset @@ -276,7 +233,10 @@ def match_nearest_bus(plants_subset, buses_subset): tree = BallTree(buses_subset[["x", "y"]].values, leaf_size=2) # Find nearest bus for each plant in the subset - distances, indices = tree.query(plants_subset[["longitude", "latitude"]].values, k=1) + distances, indices = tree.query( + plants_subset[["longitude", "latitude"]].values, + k=1, + ) # Map the nearest bus information back to the plants subset plants_subset["bus_assignment"] = buses_subset.reset_index().iloc[indices.flatten()]["Bus"].values @@ -345,7 +305,12 @@ def filter_plants_by_region( # Some plants like Diablo Canyon near oceans don't have region due to # imprecise ReEDS Shapes. We filter plants that have no reeds regions, # then search these points again. - plants_in_regions = gpd.sjoin(gdf_plants, reeds_shapes, how="inner", predicate="intersects") + plants_in_regions = gpd.sjoin( + gdf_plants, + reeds_shapes, + how="inner", + predicate="intersects", + ) plants_no_region = gdf_plants[~gdf_plants.index.isin(plants_in_regions.index)] if not plants_no_region.empty: plants_no_region = plants_no_region.to_crs(epsg=3857) @@ -359,7 +324,7 @@ def filter_plants_by_region( plants_nearshore = plants_nearshore.to_crs(epsg=4326) plants_filt = pd.concat([plants_filt, plants_nearshore]) - plants_filt.drop(columns=["geometry"], inplace=True) + plants_filt = plants_filt.drop(columns=["geometry"]) plants_filt = plants_filt[~plants_filt.index.duplicated()] plants_filt[plants_filt.index.str.contains("Diablo")] @@ -385,7 +350,9 @@ def attach_renewable_capacities_to_atlite( plants_filt["sub_assignment"] = plants_filt.bus_assignment.map(n.buses.sub_id) build_year_avg = plants_filt.groupby(["sub_assignment"])[plants_filt.columns].apply( - lambda x: pd.Series({field: weighted_avg(x, field, "p_nom") for field in ["build_year"]}), + lambda x: pd.Series( + {field: weighted_avg(x, field, "p_nom") for field in ["build_year"]}, + ), ) caps_per_bus = ( @@ -399,16 +366,18 @@ def attach_renewable_capacities_to_atlite( # missing_plants.to_csv(f"missing_{tech}_plants.csv",) logger.info( - f"There are {np.round(missing_capacity/1000,4)} GW of {tech} plants that are not in the network. See git issue #16.", + f"There are {np.round(missing_capacity / 1000, 4)} GW of {tech} plants that are not in the network. See git issue #16.", ) logger.info( - f"{np.round(caps_per_bus.sum()/1000,2)} GW of {tech} capacity added.", + f"{np.round(caps_per_bus.sum() / 1000, 2)} GW of {tech} capacity added.", ) mapped_values = generators_tech.sub_assignment.map(caps_per_bus).dropna() n.generators.loc[mapped_values.index, "p_nom"] = mapped_values n.generators.loc[mapped_values.index, "p_nom_min"] = mapped_values - mapped_values = generators_tech.sub_assignment.map(build_year_avg.build_year).dropna() + mapped_values = generators_tech.sub_assignment.map( + build_year_avg.build_year, + ).dropna() n.generators.loc[mapped_values.index, "build_year"] = mapped_values.astype(int) @@ -594,12 +563,14 @@ def attach_egs( discount_rate = 0.07 # load_costs(snakemake.input.tech_costs).loc["geothermal", "wacc_real"] drilling_cost = snakemake.config["renewable"]["EGS"]["drilling_cost"] - with xr.open_dataset( - getattr(input_profiles, "specs_egs"), - ) as ds_specs, xr.open_dataset( - getattr(input_profiles, "profile_egs"), - ) as ds_profile: - + with ( + xr.open_dataset( + getattr(input_profiles, "specs_egs"), + ) as ds_specs, + xr.open_dataset( + getattr(input_profiles, "profile_egs"), + ) as ds_profile, + ): bus2sub = ( pd.read_csv(input_profiles.bus2sub, dtype=str) .drop("interconnect", axis=1) @@ -616,13 +587,13 @@ def attach_egs( df_specs["bus_id"] = df_specs["bus_id"].astype(str) # bus_id must be in index for pypsa to read it - df_specs.set_index("bus_id", inplace=True) + df_specs = df_specs.set_index("bus_id") # columns must be renamed to refer to the right quantities for pypsa to read it correctly logger.info(f"Using {drilling_cost} EGS drilling costs.") df_specs = df_specs.rename( columns={ - "advanced_capex_usd_kw" if drilling_cost == "advanced" else "capex_usd_kw": "capital_cost", + ("advanced_capex_usd_kw" if drilling_cost == "advanced" else "capex_usd_kw"): "capital_cost", "avail_capacity_mw": "p_nom_max", "fixed_om": "fixed_om", }, @@ -688,16 +659,14 @@ def attach_battery_storage( costs: pd.DataFrame, plants: pd.DataFrame, ): - """ - Attaches Existing Battery Energy Storage Systems To the Network. - """ + """Attaches Existing Battery Energy Storage Systems To the Network.""" plants_filt = plants.query("carrier == 'battery' ") plants_filt.index = plants_filt.index.astype(str) + "_" + plants_filt.generator_id.astype(str) plants_filt.loc[:, "energy_storage_capacity_mwh"] = plants_filt.energy_storage_capacity_mwh.astype(float) plants_filt = plants_filt.dropna(subset=["energy_storage_capacity_mwh"]) logger.info( - f"Added Batteries as Storage Units to the network.\n{np.round(plants_filt.p_nom.sum()/1000,2)} GW Power Capacity \n{np.round(plants_filt.energy_storage_capacity_mwh.sum()/1000, 2)} GWh Energy Capacity", + f"Added Batteries as Storage Units to the network.\n{np.round(plants_filt.p_nom.sum() / 1000, 2)} GW Power Capacity \n{np.round(plants_filt.energy_storage_capacity_mwh.sum() / 1000, 2)} GWh Energy Capacity", ) plants_filt = plants_filt.dropna(subset=["energy_storage_capacity_mwh"]) @@ -751,7 +720,7 @@ def apply_seasonal_capacity_derates( conventional_carriers: list, sns: pd.DatetimeIndex, ): - "Applies conventional rerate factor p_max_pu based on the seasonal capacity derates defined in eia860" + """Applies conventional rerate factor p_max_pu based on the seasonal capacity derates defined in eia860.""" sns_dt = sns.get_level_values(1) summer_sns = sns_dt[sns_dt.month.isin([6, 7, 8])] winter_sns = sns_dt[~sns_dt.month.isin([6, 7, 8])] @@ -785,9 +754,7 @@ def apply_must_run_ratings( conventional_carriers: list, sns: pd.DatetimeIndex, ): - """ - Applies Minimum Loading Capacities only to WECC ADS designated Plants. - """ + """Applies Minimum Loading Capacities only to WECC ADS designated Plants.""" conv_plants = plants.query("carrier in @conventional_carriers").copy() conv_plants.index = "C" + conv_plants.index @@ -809,16 +776,14 @@ def apply_must_run_ratings( def clean_bus_data(n: pypsa.Network): - """ - Drops data from the network that are no longer needed in workflow. - """ + """Drops data from the network that are no longer needed in workflow.""" col_list = [ # "Pd", "load_dissag", "LAF", "LAF_state", ] - n.buses.drop(columns=[col for col in col_list if col in n.buses], inplace=True) + n.buses = n.buses.drop(columns=[col for col in col_list if col in n.buses]) def attach_breakthrough_renewable_plants( @@ -828,13 +793,12 @@ def attach_breakthrough_renewable_plants( extendable_carriers, costs, ): - add_missing_carriers(n, renewable_carriers) plants = pd.read_csv(fn_plants, dtype={"bus_id": str}, index_col=0).query( "bus_id in @n.buses.index", ) - plants.replace(["wind_offshore"], ["offwind"], inplace=True) + plants = plants.replace(["wind_offshore"], ["offwind"]) for tech in renewable_carriers: assert tech == "hydro" @@ -887,7 +851,6 @@ def apply_pudl_fuel_costs( plants, costs, ): - # Apply PuDL Fuel Costs for plants where listed pudl_fuel_costs = pd.read_csv(snakemake.input["pudl_fuel_costs"], index_col=0) @@ -922,7 +885,9 @@ def apply_pudl_fuel_costs( n.generators_t["marginal_cost"] = n.generators_t["marginal_cost"].join( pudl_fuel_costs, ) - logger.info(f"Applied PuDL fuel costs to {len(pudl_fuel_costs.columns)} generators.") + logger.info( + f"Applied PuDL fuel costs to {len(pudl_fuel_costs.columns)} generators.", + ) return n @@ -936,9 +901,8 @@ def main(snakemake): regions_offshore = gpd.read_file(snakemake.input.regions_offshore) reeds_shapes = gpd.read_file(snakemake.input.reeds_shapes) - Nyears = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 + num_years = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 - ### TODO COSTS TO REMOVE ### costs = pd.read_csv(snakemake.input.tech_costs) costs = costs.pivot(index="pypsa-name", columns="parameter", values="value") update_transmission_costs(n, costs, params.length_factor) @@ -990,7 +954,8 @@ def main(snakemake): ) if params.conventional.get("must_run", False): - # TODO (@ktehranchi): In the future the plants that are must-run should not be clustered and instead retire according to lifetime + # TODO (@ktehranchi): In the future the plants that are must-run should + # not be clustered and instead retire according to lifetime apply_must_run_ratings( n, plants, @@ -1040,12 +1005,12 @@ def main(snakemake): multiplier_file = snakemake.input[f"gen_cost_mult_{multiplier_data}"] df_multiplier = pd.read_csv(multiplier_file) df_multiplier = clean_locational_multiplier(df_multiplier) - update_capital_costs(n, carrier, costs, df_multiplier, Nyears) + update_capital_costs(n, carrier, costs, df_multiplier, num_years) if params.conventional["dynamic_fuel_price"].get("enable", False): logger.info("Applying dynamic fuel pricing to conventional generators") if params.conventional["dynamic_fuel_price"]["wholesale"]: - assert params.eia_api, f"Must provide EIA API key for dynamic fuel pricing" + assert params.eia_api, "Must provide EIA API key for dynamic fuel pricing" dynamic_fuel_prices = { "OCGT": { @@ -1070,7 +1035,10 @@ def main(snakemake): continue # if data should exist, try to read it in try: - df = pd.read_csv(snakemake.input[datafile], index_col="snapshot") + df = pd.read_csv( + snakemake.input[datafile], + index_col="snapshot", + ) if df.empty: logger.warning(f"No data provided for {datafile}") continue @@ -1087,7 +1055,9 @@ def main(snakemake): df=df, vom=vom, ) - logger.info(f"Applied dynamic price data for {carrier} from {datafile}") + logger.info( + f"Applied dynamic price data for {carrier} from {datafile}", + ) if params.conventional["dynamic_fuel_price"]["pudl"]: n = apply_pudl_fuel_costs(n, plants, costs) diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index 125c606ea..b0148b0aa 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -1,9 +1,6 @@ -""" -Adds extra extendable components to the clustered and simplified network. -""" +"""Adds extra extendable components to the clustered and simplified network.""" import logging -from typing import List import geopandas as gpd import numpy as np @@ -18,19 +15,18 @@ def add_co2_emissions(n, costs, carriers): - """ - Add CO2 emissions to the network's carriers attribute. - """ + """Add CO2 emissions to the network's carriers attribute.""" suptechs = n.carriers.loc[carriers].index.str.split("-").str[0] missing_carriers = set(suptechs) - set(costs.index) if missing_carriers: - logger.warning(f"CO2 emissions for carriers {missing_carriers} not defined in cost data.") + logger.warning( + f"CO2 emissions for carriers {missing_carriers} not defined in cost data.", + ) suptechs = suptechs.difference(missing_carriers) n.carriers.loc[suptechs, "co2_emissions"] = costs.co2_emissions[suptechs].values - n.carriers.fillna( + n.carriers = n.carriers.fillna( {"co2_emissions": 0}, - inplace=True, ) # TODO: FIX THIS ISSUE IN BUILD_COST_DATA- missing co2_emissions for some VRE carriers if any("CCS" in carrier for carrier in carriers): @@ -243,7 +239,7 @@ def attach_stores(n, costs, elec_opts, investment_year): def split_retirement_gens( n: pypsa.Network, costs: pd.DataFrame, - carriers: list[str] = None, + carriers: list[str] | None = None, economic: bool = True, ): """ @@ -281,7 +277,7 @@ def split_retirement_gens( n.generators["capital_cost"] = n.generators.apply( lambda row: ( row["capital_cost"] - if not row.name in (retirement_gens.index) + if row.name not in (retirement_gens.index) else costs.at[row["carrier"], "opex_fixed_per_kw"] * 1e3 ), axis=1, @@ -289,7 +285,7 @@ def split_retirement_gens( # Rename retiring generators to include "existing" suffix n.generators.index = n.generators.apply( - lambda row: (row.name if not row.name in (retirement_gens.index) else row.name + " existing"), + lambda row: (row.name if row.name not in (retirement_gens.index) else row.name + " existing"), axis=1, ) @@ -305,9 +301,10 @@ def split_retirement_gens( n.generators["p_nom_min"], ) - n.generators.loc[retirement_mask.values, "p_nom_extendable"] = ( - economic # if economic retirement is true enable extendable - ) + n.generators.loc[ + retirement_mask.values, + "p_nom_extendable", + ] = economic # if economic retirement is true enable extendable # Adding Expanding generators for the first investment period # There are generators that exist today and could expand @@ -437,7 +434,7 @@ def attach_multihorizon_egs( costs_dict: dict, Dict of costs for each investment period carriers: List[str] - List of carriers to add multiple investment options for + List of carriers to add multiple investment options for. """ if gens.empty or len(n.investment_periods) == 1: return @@ -682,18 +679,25 @@ def apply_max_annual_growth_rate(n, max_growth): ) ] - egs_gens = n.generators[n.generators["p_nom_extendable"] == True] + egs_gens = n.generators[n.generators["p_nom_extendable"] is True] egs_gens = egs_gens.loc[egs_gens["carrier"].str.contains("EGS")] new_carriers = list( set(elec_config["extendable_carriers"].get("Generator", [])) - set(n.generators.carrier.unique()) - | set(["nuclear"] if "nuclear" in elec_config["extendable_carriers"].get("Generator", []) else []), + | set( + ["nuclear"] if "nuclear" in elec_config["extendable_carriers"].get("Generator", []) else [], + ), ) for investment_year in n.investment_periods: costs = costs_dict[investment_year] attach_storageunits(n, costs, elec_config, investment_year) - attach_multihorizon_existing_generators(n, costs, multi_horizon_gens, investment_year) + attach_multihorizon_existing_generators( + n, + costs, + multi_horizon_gens, + investment_year, + ) attach_multihorizon_egs(n, costs, costs_dict, egs_gens, investment_year) attach_multihorizon_new_generators(n, costs, new_carriers, investment_year) # attach_stores(n, costs, elec_config, investment_year) diff --git a/workflow/scripts/add_sectors.py b/workflow/scripts/add_sectors.py index 19cf3dd43..5e2455c17 100644 --- a/workflow/scripts/add_sectors.py +++ b/workflow/scripts/add_sectors.py @@ -6,16 +6,12 @@ """ import logging +import sys import geopandas as gpd import numpy as np import pandas as pd import pypsa - -logger = logging.getLogger(__name__) -import sys -from typing import Optional - from _helpers import configure_logging, get_snapshots, load_costs from add_electricity import sanitize_carriers from build_electricity_sector import build_electricty @@ -32,25 +28,24 @@ get_transport_stock, ) from build_transportation import apply_exogenous_ev_policy, build_transportation -from constants import STATE_2_CODE, STATES_INTERCONNECT_MAPPER +from constants import CODE_2_STATE, STATE_2_CODE, STATES_INTERCONNECT_MAPPER from constants_sector import RoadTransport from shapely.geometry import Point -CODE_2_STATE = {v: k for k, v in STATE_2_CODE.items()} +logger = logging.getLogger(__name__) def assign_bus_2_state( n: pypsa.Network, shp: str, - states_2_include: list[str] = None, - state_2_state_name: dict[str, str] = None, + states_2_include: list[str] | None = None, + state_2_state_name: dict[str, str] | None = None, ) -> None: """ Adds a state column to the network buses dataframe. The shapefile must be the counties shapefile """ - buses = n.buses[["x", "y"]].copy() buses["geometry"] = buses.apply(lambda x: Point(x.x, x.y), axis=1) buses = gpd.GeoDataFrame(buses, crs="EPSG:4269") @@ -75,8 +70,8 @@ def add_sector_foundation( n: pypsa.Network, carrier: str, add_supply: bool = True, - costs: Optional[pd.DataFrame] = pd.DataFrame(), - center_points: Optional[pd.DataFrame] = pd.DataFrame(), + costs: pd.DataFrame | None = pd.DataFrame(), + center_points: pd.DataFrame | None = pd.DataFrame(), ) -> None: """ Adds carrier, state level bus and store for the energy carrier. @@ -85,7 +80,6 @@ def add_sector_foundation( only the bus is created and no energy supply will be added to the state level bus. """ - match carrier: case "gas": carrier_kwargs = {"color": "#d35050", "nice_name": "Natural Gas"} @@ -151,7 +145,6 @@ def add_sector_foundation( ) if add_supply: - n.madd( "Store", names=points.index, @@ -190,7 +183,6 @@ def convert_generators_2_links( bus0_suffix: str, suffix to attach link to """ - plants = n.generators[n.generators.carrier == carrier].copy() if plants.empty: @@ -257,7 +249,6 @@ def split_loads_by_carrier(n: pypsa.Network): Note: This will break the flow of energy in the model! You must add a new link between the new bus and old bus if you want to retain the flow """ - for bus in n.buses.index.unique(): df = n.loads[n.loads.bus == bus][["bus", "carrier"]] @@ -284,7 +275,6 @@ def get_pwr_co2_intensity(carrier: str, costs: pd.DataFrame) -> float: Spereate function, as there is some odd logic to account for different names in translation to a sector study. """ - # the ccs case are a hack solution match carrier: @@ -365,15 +355,15 @@ def get_pwr_co2_intensity(carrier: str, costs: pd.DataFrame) -> float: for carrier in ("OCGT", "CCGT", "CCGT-95CCS", "CCGT-97CCS"): co2_intensity = get_pwr_co2_intensity(carrier, costs) - convert_generators_2_links(n, carrier, f" gas", co2_intensity) + convert_generators_2_links(n, carrier, " gas", co2_intensity) for carrier in ("coal", "coal-95CCS", "coal-99CCS"): co2_intensity = get_pwr_co2_intensity(carrier, costs) - convert_generators_2_links(n, carrier, f" coal", co2_intensity) + convert_generators_2_links(n, carrier, " coal", co2_intensity) for carrier in ["oil"]: co2_intensity = get_pwr_co2_intensity(carrier, costs) - convert_generators_2_links(n, carrier, f" oil", co2_intensity) + convert_generators_2_links(n, carrier, " oil", co2_intensity) ng_options = snakemake.params.sector["natural_gas"] @@ -495,7 +485,6 @@ def get_pwr_co2_intensity(carrier: str, costs: pd.DataFrame) -> float: ) if snakemake.params.sector["service_sector"]["brownfield"]: - res_stock_dir = snakemake.input.residential_stock com_stock_dir = snakemake.input.commercial_stock @@ -504,7 +493,6 @@ def get_pwr_co2_intensity(carrier: str, costs: pd.DataFrame) -> float: else: fuels = ["heating", "cooling"] for fuel in fuels: - if fuel == "water_heating": simple_storage = snakemake.params.sector["service_sector"]["water_heating"].get("simple_storage", False) else: @@ -539,14 +527,12 @@ def get_pwr_co2_intensity(carrier: str, costs: pd.DataFrame) -> float: ) if snakemake.params.sector["industrial_sector"]["brownfield"]: - mecs_file = snakemake.input.industrial_stock ratios = get_industrial_stock(mecs_file) fuels = ["heat"] for fuel in fuels: - ratio = ratios.loc[fuel] add_industrial_brownfield( n=n, diff --git a/workflow/scripts/additional_policy_constraints.py b/workflow/scripts/additional_policy_constraints.py index e69de29bb..81da38799 100644 --- a/workflow/scripts/additional_policy_constraints.py +++ b/workflow/scripts/additional_policy_constraints.py @@ -0,0 +1 @@ +"""Users can add custom modifications the PyPSA Linopy model here.""" diff --git a/workflow/scripts/build_base_network.py b/workflow/scripts/build_base_network.py index 9cbf95318..c5a1e4274 100644 --- a/workflow/scripts/build_base_network.py +++ b/workflow/scripts/build_base_network.py @@ -1,23 +1,22 @@ +"""Builds base pypsa network with lines, buses, transformers.""" + # BY PyPSA-USA Authors import logging -from typing import Optional -import constants as const import geopandas as gpd import numpy as np import pandas as pd import pypsa from _helpers import configure_logging from build_shapes import load_na_shapes -from geopandas.tools import sjoin -from shapely.geometry import Point, Polygon +from shapely.geometry import Polygon from sklearn.neighbors import BallTree def haversine_np(lon1, lat1, lon2, lat2): """ Calculate the great circle distance between two points on the earth - (specified in decimal degrees) + (specified in decimal degrees). All args must be of equal length. source: https://stackoverflow.com/questions/29545704/fast-haversine-approximation-python-pandas @@ -71,7 +70,6 @@ def add_buses_from_file( def add_branches_from_file(n: pypsa.Network, fn_branches: str) -> pypsa.Network: - branches = pd.read_csv( fn_branches, dtype={"from_bus_id": str, "to_bus_id": str}, @@ -114,7 +112,6 @@ def assign_line_types(n: pypsa.Network): def add_dclines_from_file(n: pypsa.Network, fn_dclines: str) -> pypsa.Network: - dclines = pd.read_csv( fn_dclines, dtype={"from_bus_id": str, "to_bus_id": str}, @@ -137,17 +134,13 @@ def add_dclines_from_file(n: pypsa.Network, fn_dclines: str) -> pypsa.Network: def assign_sub_id(buses: pd.DataFrame, bus_locs: pd.DataFrame) -> pd.DataFrame: - """ - Adds sub id to dataframe as a new column. - """ + """Adds sub id to dataframe as a new column.""" buses["sub_id"] = bus_locs.sub_id return buses def assign_bus_location(buses: pd.DataFrame, buslocs: pd.DataFrame) -> gpd.GeoDataFrame: - """ - Attaches coordinates and sub ids to each bus. - """ + """Attaches coordinates and sub ids to each bus.""" gdf_bus = pd.merge( buses, buslocs[["lat", "lon"]], @@ -179,9 +172,7 @@ def map_bus_to_region( def assign_line_length(n: pypsa.Network): - """ - Assigns line length to each line in the network using Haversine distance. - """ + """Assigns line length to each line in the network using Haversine distance.""" bus_df = n.buses[["x", "y"]] bus0 = bus_df.loc[n.lines.bus0].values bus1 = bus_df.loc[n.lines.bus1].values @@ -240,7 +231,7 @@ def build_offshore_buses( offshore_shapes: gpd.GeoDataFrame, offshore_spacing: int, ) -> pd.DataFrame: - "Build dataframe of offshore buses by creating evenly spaced grid cells inside of the offshore shapes." + """Build dataframe of offshore buses by creating evenly spaced grid cells inside of the offshore shapes.""" offshore_buses = pd.DataFrame() offshore_shapes = offshore_shapes.to_crs("EPSG:5070") for shape in offshore_shapes.geometry: @@ -262,7 +253,7 @@ def build_offshore_buses( def add_offshore_buses(n: pypsa.Network, offshore_buses: pd.DataFrame) -> pypsa.Network: - "Add offshore buses to network" + """Add offshore buses to network.""" n.madd( "Bus", offshore_buses.index, @@ -291,7 +282,7 @@ def assign_texas_poi(n: pypsa.Network) -> pypsa.Network: def identify_osw_poi(n: pypsa.Network) -> pypsa.Network: - "Identify offshore wind points of interconnections in the base network." + """Identify offshore wind points of interconnections in the base network.""" offshore_lines = n.lines.loc[n.lines.bus0.isin(n.buses.loc[n.buses.substation_off].index)] poi_bus_ids = offshore_lines.bus1.unique() poi_sub_ids = n.buses.loc[poi_bus_ids, "sub_id"].unique() @@ -303,7 +294,7 @@ def identify_osw_poi(n: pypsa.Network) -> pypsa.Network: def match_missing_buses(buses_to_match_to, missing_buses): - "Match buses missing region assignment to their nearest bus" + """Match buses missing region assignment to their nearest bus.""" missing_buses = missing_buses.copy() missing_buses["bus_assignment"] = None @@ -322,12 +313,12 @@ def match_missing_buses(buses_to_match_to, missing_buses): k=1, # The number of nearest neighbors ) missing_buses["bus_assignment"] = buses_to_match_to.reset_index().iloc[missing_buses.id_nearest].Bus.values - missing_buses.drop(columns=["id_nearest"], inplace=True) + missing_buses = missing_buses.drop(columns=["id_nearest"]) return missing_buses def build_offshore_transmission_configuration(n: pypsa.Network) -> pypsa.Network: - "Builds offshore transmission configurations connecting offshore buses to the POIs onshore." + """Builds offshore transmission configurations connecting offshore buses to the POIs onshore.""" poi_buses = n.buses.loc[n.buses.poi_sub] # identify the buses at the POI highest_voltage_buses = poi_buses.loc[poi_buses.groupby("sub_id")["v_nom"].idxmax()] offshore_buses = match_missing_buses( @@ -372,7 +363,7 @@ def build_offshore_transmission_configuration(n: pypsa.Network) -> pypsa.Network ) # add offshore wind export cables - logger.info(f"Adding offshore wind export lines to the network.") + logger.info("Adding offshore wind export lines to the network.") n.madd( "Line", "OSW_export_" + osw_offsub_bus_ids, # name line after offshore substation @@ -493,9 +484,7 @@ def assign_missing_states_countries(n: pypsa.Network): def assign_reeds_memberships(n: pypsa.Network, fn_reeds_memberships: str): - """ - Assigns REeDS zone and balancing area memberships to buses. - """ + """Assigns REeDS zone and balancing area memberships to buses.""" reeds_memberships = pd.read_csv(fn_reeds_memberships, index_col=0) n.buses["nerc_reg"] = n.buses.reeds_zone.map(reeds_memberships.nercr) n.buses["trans_reg"] = n.buses.reeds_zone.map(reeds_memberships.transreg) @@ -504,12 +493,24 @@ def assign_reeds_memberships(n: pypsa.Network, fn_reeds_memberships: str): # Groupby county, and assign the most common reeds_zone, reeds_ba, reeds_state, nerc # This is a fix for the few counties that are split between unaligned county GIS and Reeds Zone Shapes. - n.buses["reeds_zone"] = n.buses.groupby("county")["reeds_zone"].transform(lambda x: x.mode()[0]) - n.buses["reeds_ba"] = n.buses.groupby("county")["reeds_ba"].transform(lambda x: x.mode()[0]) - n.buses["reeds_state"] = n.buses.groupby("county")["reeds_state"].transform(lambda x: x.mode()[0]) - n.buses["nerc_reg"] = n.buses.groupby("county")["nerc_reg"].transform(lambda x: x.mode()[0]) - n.buses["trans_reg"] = n.buses.groupby("county")["trans_reg"].transform(lambda x: x.mode()[0]) - n.buses["trans_grp"] = n.buses.groupby("county")["trans_grp"].transform(lambda x: x.mode()[0]) + n.buses["reeds_zone"] = n.buses.groupby("county")["reeds_zone"].transform( + lambda x: x.mode()[0], + ) + n.buses["reeds_ba"] = n.buses.groupby("county")["reeds_ba"].transform( + lambda x: x.mode()[0], + ) + n.buses["reeds_state"] = n.buses.groupby("county")["reeds_state"].transform( + lambda x: x.mode()[0], + ) + n.buses["nerc_reg"] = n.buses.groupby("county")["nerc_reg"].transform( + lambda x: x.mode()[0], + ) + n.buses["trans_reg"] = n.buses.groupby("county")["trans_reg"].transform( + lambda x: x.mode()[0], + ) + n.buses["trans_grp"] = n.buses.groupby("county")["trans_grp"].transform( + lambda x: x.mode()[0], + ) # # Assert that each county must have the same reeds_ba, reeds_zone, reeds_state, nerc_reg, and trans_reg # assert n.buses.groupby("county")["reeds_ba"].nunique().eq(1).all() @@ -604,9 +605,9 @@ def main(snakemake): # be calcualted here to capture splitting of states from the interconnect group_sums = gdf_bus.groupby("full_state")["Pd"].transform("sum") gdf_bus["LAF_state"] = gdf_bus["Pd"] / group_sums - gdf_bus.drop(columns=["full_state"], inplace=True) + gdf_bus = gdf_bus.drop(columns=["full_state"]) - # Removing few duplicated shapes where GIS shapes were overlapping. TODO Fix GIS shapes + # Removing few duplicated shapes where GIS shapes were overlapping. TODO: Fix GIS shapes gdf_bus = gdf_bus.reset_index().drop_duplicates(subset="bus_id", keep="first").set_index("bus_id") # add buses, transformers, lines and links @@ -661,7 +662,7 @@ def main(snakemake): ), "No buses remaining in network. Check your model_topology: inclusion:, you may be filtering the wrong zones for the selected interconnect" col_list = ["poi_bus", "poi_sub", "poi"] - n.buses.drop(columns=[col for col in col_list if col in n.buses], inplace=True) + n.buses = n.buses.drop(columns=[col for col in col_list if col in n.buses]) if ( len( diff --git a/workflow/scripts/build_bus_regions.py b/workflow/scripts/build_bus_regions.py index d01f23faf..d2479c805 100644 --- a/workflow/scripts/build_bus_regions.py +++ b/workflow/scripts/build_bus_regions.py @@ -1,6 +1,6 @@ # By PyPSA-USA Authors """ -**Description** +**Description**. Creates Voronoi shapes for each bus representing both onshore and offshore regions. @@ -23,7 +23,6 @@ - ``resources/regions_offshore.geojson`` """ - import logging import geopandas as gpd @@ -39,16 +38,17 @@ def voronoi_partition_pts(points, outline): """ Compute the polygons of a voronoi partition of `points` within the polygon `outline`. Taken from - https://github.com/FRESNA/vresutils/blob/master/vresutils/graph.py + https://github.com/FRESNA/vresutils/blob/master/vresutils/graph.py. + Attributes ---------- points : Nx2 - ndarray[dtype=float] outline : Polygon + Returns ------- polygons : N - ndarray[dtype=Polygon|MultiPolygon] """ - points = np.asarray(points) if len(points) == 1: @@ -195,7 +195,8 @@ def main(snakemake): "country": shape_name, }, ) - offshore_regions_c = offshore_regions_c.loc[offshore_regions_c.area > 1e-2] # remove extremely small regions + # remove extremely small regions + offshore_regions_c = offshore_regions_c.loc[offshore_regions_c.area > 1e-2] offshore_regions.append(offshore_regions_c) # Exporting if offshore_regions: diff --git a/workflow/scripts/build_clustered_population_layouts.py b/workflow/scripts/build_clustered_population_layouts.py index c9698b6de..91796f221 100644 --- a/workflow/scripts/build_clustered_population_layouts.py +++ b/workflow/scripts/build_clustered_population_layouts.py @@ -23,12 +23,12 @@ clustered_regions = gpd.read_file(snakemake.input.regions_onshore).set_index("name").buffer(0).squeeze() - I = cutout.indicatormatrix(clustered_regions) + indicator_matrix = cutout.indicatormatrix(clustered_regions) pop = {} for item in ["total", "urban", "rural"]: pop_layout = xr.open_dataarray(snakemake.input[f"pop_layout_{item}"]) - pop[item] = I.dot(pop_layout.stack(spatial=("y", "x"))) + pop[item] = indicator_matrix.dot(pop_layout.stack(spatial=("y", "x"))) pop = pd.DataFrame(pop, index=clustered_regions.index) diff --git a/workflow/scripts/build_cop_profiles.py b/workflow/scripts/build_cop_profiles.py index 0ea49b74e..e5353f58e 100644 --- a/workflow/scripts/build_cop_profiles.py +++ b/workflow/scripts/build_cop_profiles.py @@ -1,3 +1,4 @@ +# ruff: noqa: N816, N803 """ Build coefficient of performance (COP) time series for air- or ground-sourced heat pumps. diff --git a/workflow/scripts/build_cost_data.py b/workflow/scripts/build_cost_data.py index 8b2c7c1c0..63c5bccc6 100644 --- a/workflow/scripts/build_cost_data.py +++ b/workflow/scripts/build_cost_data.py @@ -1,9 +1,6 @@ -""" -Combines all time independent cost data sources into a standard format. -""" +"""Combines all time independent cost data sources into a standard format.""" import logging -from typing import Any, Optional import constants as const import duckdb @@ -38,7 +35,8 @@ LIFETIME_DATA = [ {"pypsa-name": "coal", "parameter": "lifetime", "value": 70}, {"pypsa-name": "oil", "parameter": "lifetime", "value": 55}, # using gas CT - {"pypsa-name": "geothermal", "parameter": "lifetime", "value": 70}, # Confirm with Jabs / NREL. 30 is way too small + # Confirm with Jabs / NREL. 30 is way too small + {"pypsa-name": "geothermal", "parameter": "lifetime", "value": 70}, {"pypsa-name": "waste", "parameter": "lifetime", "value": 55}, # using gas CT {"pypsa-name": "CCGT", "parameter": "lifetime", "value": 55}, {"pypsa-name": "OCGT", "parameter": "lifetime", "value": 55}, @@ -78,7 +76,7 @@ def create_duckdb_instance(pudl_fn: str): def load_pudl_atb_data(): - query = f""" + query = """ WITH finance_cte AS ( SELECT wacc_real, @@ -105,7 +103,7 @@ def load_pudl_atb_data(): def load_pudl_aeo_data(): - query = f""" + query = """ SELECT * FROM core_eiaaeo__yearly_projected_fuel_cost_in_electric_sector_by_type aeo WHERE aeo.report_year = 2023 @@ -134,11 +132,9 @@ def get_sector_costs( efs_icev_costs: str, eia_tech_costs, year: int, - additional_costs_csv: Optional[str] = None, + additional_costs_csv: str | None = None, ) -> pd.DataFrame: - """ - Gets end-use tech costs for sector coupling studies. - """ + """Gets end-use tech costs for sector coupling studies.""" def correct_units(df: pd.DataFrame) -> pd.DataFrame: # USD/gal -> USD/MWh (water storage) @@ -166,10 +162,7 @@ def get_investment_year_data(df: pd.DataFrame, year: int) -> pd.DataFrame: return df[df.year == year].drop(columns="year") def calculate_capex(df: pd.DataFrame, discount_rate: float) -> pd.DataFrame: - """ - Calcualtes capex based on annuity payments. - """ - + """Calcualtes capex based on annuity payments.""" capex = df.copy().set_index(["technology", "parameter"]) capex = capex.value.unstack().fillna(0) @@ -379,10 +372,9 @@ def calculate_capex(df: pd.DataFrame, discount_rate: float) -> pd.DataFrame: ], ignore_index=True, ) - pudl_atb.drop_duplicates( + pudl_atb = pudl_atb.drop_duplicates( subset=["pypsa-name", "parameter"], keep="last", - inplace=True, ) # Load AEO Fuel Cost Data @@ -400,7 +392,7 @@ def calculate_capex(df: pd.DataFrame, discount_rate: float) -> pd.DataFrame: var_name="parameter", value_name="value", ) - aeo.rename(columns={"fuel_type_eiaaeo": "pypsa-name"}, inplace=True) + aeo = aeo.rename(columns={"fuel_type_eiaaeo": "pypsa-name"}) addnl_fuels = pd.DataFrame( [ @@ -507,9 +499,7 @@ def calculate_capex(df: pd.DataFrame, discount_rate: float) -> pd.DataFrame: pivot_atb.loc[ pivot_atb["pypsa-name"].str.contains("offshore"), "capex_grid_connection_per_kw_km", - ] = ( - pivot_atb["capex_grid_connection_per_kw"] / 30 - ) + ] = pivot_atb["capex_grid_connection_per_kw"] / 30 pivot_atb["annualized_connection_capex_per_mw_km"] = ( calculate_annuity( @@ -532,7 +522,9 @@ def calculate_capex(df: pd.DataFrame, discount_rate: float) -> pd.DataFrame: pudl_atb["value"] = pudl_atb["value"].round(3) egs_costs = pd.read_csv(snakemake.input.egs_costs) - egs_costs = egs_costs.query("investment_horizon == @tech_year").drop(columns="investment_horizon") + egs_costs = egs_costs.query("investment_horizon == @tech_year").drop( + columns="investment_horizon", + ) pudl_atb = pd.concat([pudl_atb, egs_costs], ignore_index=True) pudl_atb.to_csv(snakemake.output.tech_costs, index=False) diff --git a/workflow/scripts/build_cutout.py b/workflow/scripts/build_cutout.py index f66581312..37e3a62f4 100644 --- a/workflow/scripts/build_cutout.py +++ b/workflow/scripts/build_cutout.py @@ -90,7 +90,7 @@ import atlite import geopandas as gpd import pandas as pd -from _helpers import configure_logging, get_snapshots +from _helpers import configure_logging from pandas import Timestamp logger = logging.getLogger(__name__) @@ -129,7 +129,7 @@ ) * 2 ) - interconnect_params["bounds"] = regions.total_bounds + [-d, -d, d, d] + interconnect_params["bounds"] = [*regions.total_bounds, -d, -d, d, d] elif {"x", "y"}.issubset(interconnect_params): interconnect_params["x"] = slice(*interconnect_params["x"]) interconnect_params["y"] = slice(*interconnect_params["y"]) diff --git a/workflow/scripts/build_demand.py b/workflow/scripts/build_demand.py index 859802e61..1c2cb65d8 100644 --- a/workflow/scripts/build_demand.py +++ b/workflow/scripts/build_demand.py @@ -1,6 +1,6 @@ -""" -Builds the demand data for the PyPSA network. -""" +# ruff: noqa: RUF012, D101, D102, F811 + +"""Builds the demand data for the PyPSA network.""" # snakemake is not liking this futures import. Removing type hints in context class # from __future__ import annotations @@ -11,7 +11,7 @@ import sys from abc import ABC, abstractmethod from pathlib import Path -from typing import Any, Optional +from typing import Any import constants as const import duckdb @@ -27,67 +27,49 @@ logger = logging.getLogger(__name__) STATE_2_CODE = const.STATE_2_CODE -CODE_2_STATE = {value: key for key, value in STATE_2_CODE.items()} +CODE_2_STATE = const.CODE_2_STATE STATE_TIMEZONE = const.STATE_2_TIMEZONE TBTU_2_MWH = const.TBTU_2_MWH class Context: - """ - The Context defines the interface of interest to clients. - """ + """The Context defines the interface of interest to clients.""" def __init__(self, read_strategy, write_strategy) -> None: - """ - (read_strategy: ReadStrategy, write_strategy: WriteStrategy) - """ + """(read_strategy: ReadStrategy, write_strategy: WriteStrategy).""" self._read_strategy = read_strategy self._write_strategy = write_strategy @property def read_strategy(self): # returns ReadStrategy: - """ - The Context maintains a reference to the Strategy objects. - """ + """The Context maintains a reference to the Strategy objects.""" return self._read_strategy @read_strategy.setter def strategy(self, strategy) -> None: # arg is ReadStrategy - """ - Usually, the Context allows replacing a Strategy object at runtime. - """ + """Usually, the Context allows replacing a Strategy object at runtime.""" self._read_strategy = strategy @property def write_strategy(self): # returns WriteStrategy: - """ - The Context maintains a reference to the Strategy objects. - """ + """The Context maintains a reference to the Strategy objects.""" return self._write_strategy @write_strategy.setter def strategy(self, strategy) -> None: # arg is WriteStrategy - """ - Usually, the Context allows replacing a Strategy object at runtime. - """ + """Usually, the Context allows replacing a Strategy object at runtime.""" self._write_strategy = strategy def _read(self) -> pd.DataFrame: - """ - Delegate reading to the strategy. - """ + """Delegate reading to the strategy.""" return self._read_strategy.read_demand() def _write(self, demand: pd.DataFrame, zone: str, **kwargs) -> pd.DataFrame: - """ - Delegate writing to the strategy. - """ + """Delegate writing to the strategy.""" return self._write_strategy.dissagregate_demand(demand, zone, **kwargs) def prepare_demand(self, **kwargs) -> pd.DataFrame: - """ - Read in and dissagregate demand. - """ + """Read in and dissagregate demand.""" demand = self._read() return self._write(demand, self._read_strategy.zone, **kwargs) @@ -97,10 +79,7 @@ def prepare_multiple_demands( fuels: str | list[str], **kwargs, ) -> dict[str, pd.DataFrame]: - """ - Returns demand by end-use energy carrier. - """ - + """Returns demand by end-use energy carrier.""" if isinstance(fuels, str): fuels = [fuels] @@ -132,7 +111,6 @@ def prepare_demand_by_subsector( > result["electricity"]["light-duty"] > result["lpg"]["heavy-duty"] """ - if isinstance(fuels, str): fuels = [fuels] @@ -167,7 +145,7 @@ class ReadStrategy(ABC): of some algorithm. """ - def __init__(self, filepath: Optional[str | list[str]] = None) -> None: + def __init__(self, filepath: str | list[str] | None = None) -> None: self.filepath = filepath @property @@ -176,16 +154,11 @@ def units(): @abstractmethod def _read_data(self, **kwargs) -> Any: - """ - Reads raw data into any arbitraty data structure. - """ + """Reads raw data into any arbitraty data structure.""" pass def read_demand(self) -> pd.DataFrame: - """ - Public interface to extract data. - """ - + """Public interface to extract data.""" data = self._read_data() df = self._format_data(data) self._check_index(df) @@ -217,9 +190,7 @@ def _format_data(self, data: Any) -> pd.DataFrame: pass def _check_index(self, df: pd.DataFrame) -> None: - """ - Enforces dimension labels. - """ + """Enforces dimension labels.""" assert all(x in ["snapshot", "sector", "subsector", "fuel"] for x in df.index.names) assert all( @@ -234,9 +205,7 @@ def _check_index(self, df: pd.DataFrame) -> None: @staticmethod def _format_snapshot_index(df: pd.DataFrame) -> pd.DataFrame: - """ - Makes index into datetime. - """ + """Makes index into datetime.""" if df.index.nlevels > 1: if "snapshot" not in df.index.names: logger.warning("Can not format snapshot index level") @@ -254,9 +223,7 @@ def _format_snapshot_index(df: pd.DataFrame) -> pd.DataFrame: class ReadEia(ReadStrategy): - """ - Reads data from GridEmissions. - """ + """Reads data from GridEmissions.""" def __init__(self, filepath: str | None = None) -> None: super().__init__(filepath) @@ -267,10 +234,7 @@ def zone(self): return self._zone def _read_data(self) -> pd.DataFrame: - """ - Reads raw data. - """ - + """Reads raw data.""" if not self.filepath: logger.error("Must provide filepath for EIA data") sys.exit() @@ -279,9 +243,7 @@ def _read_data(self) -> pd.DataFrame: return pd.read_csv(self.filepath, engine="pyarrow", index_col="timestamp") def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: - """ - Formats raw data. - """ + """Formats raw data.""" df = data.copy().fillna(0) df = self._correct_balancing_areas(df) df = self._format_snapshot_index(df) @@ -293,9 +255,7 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: @staticmethod def _correct_balancing_areas(df: pd.DataFrame) -> pd.DataFrame: - """ - Combine EIA Demand Data to Match GIS Shapes. - """ + """Combine EIA Demand Data to Match GIS Shapes.""" df["Arizona"] = df.pop("SRP") + df.pop("AZPS") + df.pop("TEPC") df["Carolina"] = df.pop("CPLE") + df.pop("CPLW") + df.pop("DUK") + df.pop("SC") + df.pop("SCEG") + df.pop("YAD") df["Florida"] = ( @@ -315,9 +275,7 @@ def _correct_balancing_areas(df: pd.DataFrame) -> pd.DataFrame: class ReadFERC714(ReadStrategy): - """ - Reads data from PuDLs FERC 714 based State historical Demand. - """ + """Reads data from PuDLs FERC 714 based State historical Demand.""" def __init__(self, filepath: str | None = None) -> None: super().__init__(filepath) @@ -328,10 +286,7 @@ def zone(self): return self._zone def _read_data(self) -> pd.DataFrame: - """ - Reads raw data. - """ - + """Reads raw data.""" if not self.filepath: logger.error("Must provide filepath for FERC714 data") sys.exit() @@ -340,9 +295,7 @@ def _read_data(self) -> pd.DataFrame: return pd.read_parquet(self.filepath[0], dtype_backend="pyarrow") def _read_census_data(self) -> pd.DataFrame: - """ - Reads in census data for population weighting. - """ + """Reads in census data for population weighting.""" duckdb.connect(database=":memory:", read_only=False) duckdb.query("INSTALL sqlite;") @@ -371,9 +324,7 @@ def _read_census_data(self) -> pd.DataFrame: return states def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: - """ - Formats raw data. - """ + """Formats raw data.""" states = self._read_census_data() df = data.copy() @@ -401,9 +352,7 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: class ReadEfs(ReadStrategy): - """ - Reads in electrifications future study demand. - """ + """Reads in electrifications future study demand.""" def __init__(self, filepath: str | None = None) -> None: super().__init__(filepath) @@ -414,7 +363,6 @@ def zone(self): return self._zone def _read_data(self) -> pd.DataFrame: - if not self.filepath: logger.error("Must provide filepath for EFS data") sys.exit() @@ -423,10 +371,7 @@ def _read_data(self) -> pd.DataFrame: return pd.read_csv(self.filepath, engine="pyarrow").round(3) def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: - """ - Formats raw data. - """ - + """Formats raw data.""" df = data.copy() df = self._build_snapshots(df) df = self._format_snapshot_index(df).reset_index() @@ -452,24 +397,17 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: return df def _build_snapshots(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Builds snapshots based on UTC time. - """ - + """Builds snapshots based on UTC time.""" df = self._apply_timezones(df) df = self._build_datetime(df) return df.set_index("time").sort_index() @staticmethod def _apply_timezones(df: pd.DataFrame) -> pd.DataFrame: - """ - Changes local time to relative time from UTC. - """ + """Changes local time to relative time from UTC.""" def apply_timezone_shift(timezone: str) -> int: - """ - All shifts realitive to UTC time. - """ + """All shifts realitive to UTC time.""" if timezone == "US/Pacific": return 8 elif timezone == "US/Mountain": @@ -496,9 +434,7 @@ def apply_timezone_shift(timezone: str) -> int: @staticmethod def _build_datetime(df: pd.DataFrame) -> pd.DataFrame: - """ - Builds snapshot from EFS data. - """ + """Builds snapshot from EFS data.""" # minus 1 cause indexing starts at 1 df["hoy"] = pd.to_timedelta(df.UtcHourID - 1, unit="h") # assign everything 2018 (non-leap year) then correct just the year @@ -521,8 +457,8 @@ def get_growth_rate(self): Yearly values are linearlly interpolated between EFS planning years - Returns: - + Returns + ------- | | State 1 | State 2 | ... | State n | |----- |---------|---------|-----|---------| | 2018 | ### | ### | | ### | @@ -534,7 +470,6 @@ def get_growth_rate(self): | 2049 | ### | ### | | ### | | 2050 | ### | ### | | ### | """ - # extract efs provided data efs_years = self._read_data()[["Year", "State", "LoadMW"]] efs_years = efs_years.groupby(["Year", "State"]).sum().reset_index() @@ -556,9 +491,7 @@ def get_growth_rate(self): class ReadEulp(ReadStrategy): - """ - Reads in End Use Load Profile data. - """ + """Reads in End Use Load Profile data.""" def __init__(self, filepath: str | list[str], stock: str) -> None: super().__init__(filepath) @@ -620,11 +553,9 @@ def _extract_state(filepath: str) -> str: @staticmethod def _apply_timeshift(data: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]: """Raw EULP given in EST. Shift data by in local state time.""" - data_shifted = {} for state, df in data.items(): - year = df.index[0].year timezone = STATE_TIMEZONE[state] @@ -722,9 +653,9 @@ class ReadCliu(ReadStrategy): def __init__( self, filepath: str | list[str], - epri_filepath: Optional[str] = None, - mecs_filepath: Optional[str] = None, - fips_filepath: Optional[str] = None, + epri_filepath: str | None = None, + mecs_filepath: str | None = None, + fips_filepath: str | None = None, ) -> None: super().__init__(filepath) self._epri_filepath = epri_filepath @@ -737,9 +668,7 @@ def zone(self): return self._zone def _read_data(self) -> Any: - """ - Unzipped 'County_industry_energy_use.gz' csv file. - """ + """Unzipped 'County_industry_energy_use.gz' csv file.""" df = pd.read_csv( self.filepath, dtype={ @@ -777,9 +706,7 @@ def _format_data(self, county_demand: Any, scale_mecs: bool = True) -> pd.DataFr return self._apply_profiles(annual_demand, profiles) def get_demand_profiles(self, add_lpg: bool = True): - """ - Public method to extract EPRI data. - """ + """Public method to extract EPRI data.""" if not self._epri_filepath: logger.warning("No EPRI profiles provided") return @@ -803,34 +730,25 @@ def _apply_profiles( """ def profile_2_xarray(df: pd.DataFrame) -> xr.Dataset: - """ - Converts EPRI profiles to dataset. - """ + """Converts EPRI profiles to dataset.""" assert all(x in ("snapshot", "state") for x in df.index.names) assert all(x in ("electricity", "cool", "heat", "lpg") for x in df.columns) return xr.Dataset.from_dataframe(df) def annual_demand_2_xarray(df: pd.DataFrame) -> xr.Dataset: - """ - Converts CLIU profiles to dataset. - """ + """Converts CLIU profiles to dataset.""" assert all(x in ("state", "sector", "subsector", "end_use", "county") for x in df.index.names) assert all(x in ("electricity", "cool", "heat", "lpg") for x in df.columns) return xr.Dataset.from_dataframe(df) def remove_counties(df: pd.DataFrame) -> pd.DataFrame: - """ - Removes counties to reduce data. - """ + """Removes counties to reduce data.""" indices = df.index.names df.index = df.index.droplevel("county") return df.reset_index().groupby([x for x in indices if x != "county"]).sum() def check_variables(ds: xr.Dataset) -> xr.Dataset: - """ - Confirms all data variables are present. - """ - + """Confirms all data variables are present.""" assert "electricity" in ds.data_vars for fuel in ("heat", "cool", "lpg"): @@ -840,7 +758,7 @@ def check_variables(ds: xr.Dataset) -> xr.Dataset: return ds - annual_demand = remove_counties(annual_demand) # todo: retain county data + annual_demand = remove_counties(annual_demand) # TODO: retain county data annual_demand = annual_demand_2_xarray(annual_demand) profiles = profile_2_xarray(profiles) @@ -850,7 +768,7 @@ def check_variables(ds: xr.Dataset) -> xr.Dataset: demand = profiles * annual_demand dims = [x for x in demand.sizes] - # todo: add county arregation + # TODO: add county arregation if "county" in dims: indices = dims.remove("state") columns = "county" @@ -895,9 +813,7 @@ def _group_by_naics( @staticmethod def _group_by_fuels(data: pd.DataFrame) -> pd.DataFrame: - """ - Groups in electricity, heat, cool, and lpg. - """ + """Groups in electricity, heat, cool, and lpg.""" df = data.copy() df["electricity"] = df["Net_electricity"] + df["Other"].div(3) df["heat"] = df["Coal"] + df["Coke_and_breeze"] + df["Natural_gas"] + df["Other"].div(3) @@ -1017,9 +933,7 @@ def _norm_epri_data(data: pd.DataFrame) -> pd.DataFrame: @staticmethod def _add_lpg_epri(df: pd.DataFrame) -> pd.DataFrame: - """ - Adds an LPG column of data as an average. - """ + """Adds an LPG column of data as an average.""" df["lpg"] = df.mean(axis=1) return df @@ -1030,7 +944,6 @@ def _read_mecs_data(self) -> pd.DataFrame: Adapted from: https://github.com/NREL/Industry-Energy-Tool/blob/master/data_foundation/data_comparison/compare_mecs.py """ - cols_renamed = { "Code(a)": "NAICS", "Electricity(b)": "Net_electricity", @@ -1062,14 +975,10 @@ def _apply_mecs( mecs: pd.DataFrame, fips: pd.DataFrame, ) -> pd.DataFrame: - """ - Scales CLIU data by MECS data. - """ + """Scales CLIU data by MECS data.""" def assign_mecs_regions(cliu: pd.DataFrame, fips: pd.DataFrame) -> pd.DataFrame: - """ - Adds a mecs column to the cliu df with associated fips region. - """ + """Adds a mecs column to the cliu df with associated fips region.""" df = cliu.copy() df["Region"] = df.index.get_level_values("state").map( fips.set_index("state")["mecs"].to_dict(), @@ -1092,9 +1001,7 @@ def get_cliu_totals(cliu: pd.DataFrame, fips: pd.DataFrame) -> pd.DataFrame: return df.reset_index(drop=True).groupby("Region").sum() def get_mecs_totals(mecs: pd.DataFrame) -> pd.DataFrame: - """ - Gets regional energy totals for the year per fuel from MECS. - """ + """Gets regional energy totals for the year per fuel from MECS.""" df = mecs.copy() df = df.reset_index().drop(columns=["NAICS"]) df["Region"] = df.Region.map(lambda x: x.split(" ")[0]) @@ -1102,9 +1009,7 @@ def get_mecs_totals(mecs: pd.DataFrame) -> pd.DataFrame: return df.groupby("Region").sum() def get_scaling_factors(df1: pd.DataFrame, df2: pd.DataFrame) -> pd.DataFrame: - """ - Gets factor to multiply df1 by to become df2. - """ + """Gets factor to multiply df1 by to become df2.""" assert all([x in df2.columns for x in df1.columns]) assert all([x in df2.index for x in df1.index.unique()]) return df2.div(df1) @@ -1126,10 +1031,7 @@ def scale_industrial_demand( return scale_industrial_demand(cliu, scaling_factors) def _read_fips_data(self) -> pd.DataFrame: - """ - Reads FIPS data. - """ - + """Reads FIPS data.""" return ( pd.read_csv(self._fips_filepath) .drop(columns=["FIPS_County", "FIPS State"]) @@ -1171,9 +1073,7 @@ class ReadTransportEfsAeo(ReadStrategy): efs_years = [2018, 2020, 2022, 2024, 2030, 2040, 2050] def __init__(self, filepath: str, api: str, efs_path: str) -> None: - """ - Filepath is state level breakdown of VMT by vehicle type. - """ + """Filepath is state level breakdown of VMT by vehicle type.""" super().__init__(filepath) self._zone = "state" # self.units = "VMT" @@ -1190,9 +1090,7 @@ def zone(self): @staticmethod def _assign_vehicle_type(vehicle: str) -> str: - """ - Coordinates vehicle names. - """ + """Coordinates vehicle names.""" match vehicle: case v if v.startswith(("light_duty", "light-duty vehicles", "Light-Duty")): return "light_duty" @@ -1225,7 +1123,6 @@ def _read_data(self) -> pd.DataFrame: | ... | ... | ... | | other | Texas | 1.5 | """ - df = pd.read_csv(self.filepath, index_col=0, header=0) # check as these are user defined @@ -1251,7 +1148,6 @@ def _read_efs_data(self): the year will equal **ONE THOUSAND** Its scaled from 1 to 1000 just to avoid numerical issues. """ - efs = ReadEfs(self.efs_path).read_demand() transport = efs[efs.index.get_level_values("sector") == "transport"].droplevel( ["sector", "fuel"], @@ -1293,10 +1189,7 @@ def _get_yearly_energy_efs(df: pd.DataFrame) -> pd.DataFrame: return totals.reindex_like(df).ffill() def _read_demand_aeo(self) -> pd.DataFrame: - """ - Gets yearly national level VMT data. - """ - + """Gets yearly national level VMT data.""" demand = [] for vehicle in ("light_duty", "med_duty", "heavy_duty", "bus"): demand.append(TransportationDemand(vehicle, 2050, self.api).get_data()) @@ -1323,11 +1216,10 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: """ Merges national VMT data (self.aeo_demand), proportions of VMT travelled per state (data), and electric charging profiles - (self.efs_profile) + (self.efs_profile). This is one ugly function. holy. """ - aeo = self.aeo_demand.drop(columns="units") aeo = xr.DataArray.from_series(aeo.squeeze()) @@ -1458,9 +1350,7 @@ def zone(self): @staticmethod def _assign_vehicle_type(vehicle: str) -> str: - """ - Coordinates vehicle names. - """ + """Coordinates vehicle names.""" match vehicle: case v if v.startswith(("Air", "air")): return "air" @@ -1489,7 +1379,6 @@ def _read_data(self) -> pd.DataFrame: | ... | ... | ... | | other | Texas | 1.5 | """ - df = pd.read_csv(self.filepath, index_col=0, header=0) # check as these are user defined @@ -1507,10 +1396,7 @@ def _read_data(self) -> pd.DataFrame: return df def _read_demand_aeo(self) -> pd.DataFrame: - """ - Gets yearly national level demand. - """ - + """Gets yearly national level demand.""" demand = [] demand.append(TransportationDemand(self.vehicle, 2050, self.api).get_data()) for year in self.years: @@ -1543,7 +1429,6 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: Merges national VMT data (self.aeo_demand) and proportions of demand travelled per state (data) to ceate uniform demand profiles. """ - demand_by_state = data.copy() # given as a percentage demand_national = self.aeo_demand.drop(columns="units") @@ -1553,7 +1438,7 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: df = pd.DataFrame( index=pd.date_range( f"{year}-01-01", - f"{year+1}-01-01", + f"{year + 1}-01-01", freq="h", inclusive="left", ), @@ -1583,9 +1468,7 @@ def _format_data(self, data: pd.DataFrame) -> pd.DataFrame: class WriteStrategy(ABC): - """ - Disaggregates demand based on a specified method. - """ + """Disaggregates demand based on a specified method.""" def __init__(self, n: pypsa.Network) -> None: self.n = n @@ -1593,7 +1476,7 @@ def __init__(self, n: pypsa.Network) -> None: @abstractmethod def _get_load_allocation_factor( self, - df: Optional[pd.Series] = None, + df: pd.Series | None = None, **kwargs, ) -> pd.Series: """ @@ -1642,7 +1525,6 @@ def dissagregate_demand( | ... | | | | ### | | 2019-12-31 23:00:00 | ### | ### | | ### | """ - # 'state' is states based on power regions # 'full_state' is actual geographic boundaries assert zone in ("ba", "state", "reeds") @@ -1667,9 +1549,7 @@ def dissagregate_demand( return self._disaggregate_demand_to_buses(demand, zone_data) def _get_load_dissagregation_zones(self, zone: str) -> pd.Series: - """ - Map each bus to the load dissagregation zone (states, ba, ...) - """ + """Map each bus to the load dissagregation zone (states, ba, ...).""" if zone == "ba": return self._get_balanceing_area_zones() elif zone == "state": @@ -1681,9 +1561,7 @@ def _get_load_dissagregation_zones(self, zone: str) -> pd.Series: @staticmethod def _check_datastructure(df: pd.DataFrame) -> None: - """ - Confirms formatting of input datastructure. - """ + """Confirms formatting of input datastructure.""" assert all(x in ["snapshot", "sector", "subsector", "fuel"] for x in df.index.names) assert not df.empty @@ -1692,9 +1570,7 @@ def _filter_on_snapshots( df: pd.DataFrame, sns: pd.DatetimeIndex, ) -> pd.DataFrame: - """ - Filters demand on network snapshots. - """ + """Filters demand on network snapshots.""" filtered = df[df.index.get_level_values("snapshot").isin(sns)].copy() filtered = filtered[~filtered.index.duplicated(keep="last")] # issue-272 return filtered @@ -1722,10 +1598,7 @@ def _filter_demand( fuels: str | list[str] | None = None, sns: pd.DatetimeIndex | None = None, ) -> pd.DataFrame: - """ - Filters on snapshots, sector, and fuel. - """ - + """Filters on snapshots, sector, and fuel.""" if isinstance(sns, pd.DatetimeIndex): filtered = self._filter_on_snapshots(df, sns) df = filtered.reset_index() @@ -1752,10 +1625,7 @@ def _disaggregate_demand_to_buses( demand: pd.DataFrame, laf: pd.DataFrame, ) -> pd.DataFrame: - """ - Zone power demand is disaggregated to buses proportional to laf. - """ - + """Zone power demand is disaggregated to buses proportional to laf.""" all_load = [] for load_zone in laf.zone.unique(): @@ -1802,17 +1672,13 @@ def _get_reeds_zones(self) -> pd.Series: return n.buses.reeds_zone def _make_empty_demand(self, columns: list[str]) -> pd.DataFrame: - """ - Make a demand dataframe with zeros. - """ + """Make a demand dataframe with zeros.""" n = self.n return pd.DataFrame(columns=columns, index=n.snapshots.get_level_values(1)).infer_objects().fillna(0) class WritePopulation(WriteStrategy): - """ - Based on Population Density from Breakthrough Energy. - """ + """Based on Population Density from Breakthrough Energy.""" def __init__(self, n: pypsa.Network) -> None: super().__init__(n) @@ -1823,9 +1689,7 @@ def _get_load_allocation_factor( zone: str, **kwargs, ) -> pd.Series: - """ - Pulls weighting from 'build_base_network'. - """ + """Pulls weighting from 'build_base_network'.""" logger.info("Setting load allocation factors based on BE population density") n = self.n if zone == "state": @@ -1894,9 +1758,7 @@ def _dissagregate_on_reeds(self) -> pd.Series: @staticmethod def _read_data(filepath: str) -> pd.DataFrame: - """ - Unzipped 'County_industry_energy_use.gz' csv file. - """ + """Unzipped 'County_industry_energy_use.gz' csv file.""" df = pd.read_csv( filepath, dtype={ @@ -1962,10 +1824,7 @@ def get_laf_per_bus( county: str | int, buses: list[str], ) -> pd.Series: - """ - Evenly distributes laf to buses within a county. - """ - + """Evenly distributes laf to buses within a county.""" county_laf = df.at[county, "laf"] num_buses = len(buses) bus_laf = county_laf / num_buses @@ -1994,9 +1853,9 @@ class DemandFormatter: def __init__( self, n: pypsa.Network, - scaling_method: Optional[str] = None, - filepath: Optional[str] = None, - api: Optional[str] = None, + scaling_method: str | None = None, + filepath: str | None = None, + api: str | None = None, ): self.n = n self.sns = n.snapshots @@ -2011,10 +1870,7 @@ def __init__( self.scaler = None def format_demand(self, df: pd.DataFrame, sector: str, **kwargs) -> pd.DataFrame: - """ - Public method to format demand ready to be ingested into the model. - """ - + """Public method to format demand ready to be ingested into the model.""" if self.need_scaling(df): assert isinstance(self.scaler, DemandScaler) @@ -2046,9 +1902,7 @@ def format_demand(self, df: pd.DataFrame, sector: str, **kwargs) -> pd.DataFrame return demand def need_scaling(self, df: pd.DataFrame) -> bool: - """ - Checks if any demand needs to be scaled. - """ + """Checks if any demand needs to be scaled.""" if len(df.index) != len(self.sns): return True return False @@ -2071,7 +1925,6 @@ def assign_scaler(self): # type DemandScaler class DemandScaler(ABC): - def __init__(self): self.projection = self.get_projections() @@ -2080,10 +1933,7 @@ def get_projections(self) -> pd.DataFrame: pass def get_growth(self, start_year: int, end_year: int, sector: str) -> float: - """ - Returns decimal change between two years. - """ - + """Returns decimal change between two years.""" min_year = self.projection.index.min() max_year = self.projection.index.max() @@ -2106,9 +1956,7 @@ def scale( end_year: int, sector: str, ) -> pd.DataFrame: - """ - Scales data. - """ + """Scales data.""" growth = self.get_growth(start_year, end_year, sector) new = df.mul(growth) return self.reindex(new, end_year) @@ -2136,14 +1984,12 @@ def reindex(df: pd.DataFrame, year: int) -> pd.DataFrame: | ... | ... | ... | | ... | | 2030-02-28 23:00:00 | ccc | fff | | iii | """ - new = df.copy() new.index = new.index.map(lambda x: x.replace(year=year)) return new class AeoElectricityScaler(DemandScaler): - def __init__(self, pudl: str, scenario: str = "reference"): self.pudl = pudl self.scenario = scenario @@ -2163,7 +2009,6 @@ def get_projections(self) -> pd.DataFrame: | 2049 | ### | ### | | 2050 | ### | ### | """ - con = sqlite3.connect(self.pudl) df = pd.read_sql_query( f""" @@ -2193,7 +2038,6 @@ def get_projections(self) -> pd.DataFrame: class AeoEnergyScaler(DemandScaler): - def __init__(self, api: str, scenario: str = "reference"): self.api = api self.scenario = scenario @@ -2201,9 +2045,7 @@ def __init__(self, api: str, scenario: str = "reference"): super().__init__() def get_sector_data(self, years: list[int], sector: str) -> pd.DataFrame: - """ - Function to piece togehter historical and projected values. - """ + """Function to piece togehter historical and projected values.""" start_year = min(years) end_year = max(years) @@ -2233,7 +2075,6 @@ def get_projections(self) -> pd.DataFrame: | 2049 | ### | ### | ### | ### | ### | | 2050 | ### | ### | ### | ### | ### | """ - years = range(2017, 2051) # sectors = ("residential", "commercial", "industry", "transport") @@ -2252,7 +2093,6 @@ def get_projections(self) -> pd.DataFrame: class AeoVmtScaler(DemandScaler): - def __init__(self, api: str, scenario: str = "reference"): self.api = api self.scenario = scenario @@ -2260,9 +2100,7 @@ def __init__(self, api: str, scenario: str = "reference"): super().__init__() def get_historical_value(self, year: int, sector: str) -> float: - """ - Returns single year value at a time. - """ + """Returns single year value at a time.""" return TransportationDemand(vehicle=sector, year=year, api=self.api).get_data(pivot=True).values[0][0] def get_future_values( @@ -2270,9 +2108,7 @@ def get_future_values( year: int, sector: str, ) -> pd.DataFrame: - """ - Returns all values from 2024 onwards. - """ + """Returns all values from 2024 onwards.""" return TransportationDemand( vehicle=sector, year=year, @@ -2294,7 +2130,6 @@ def get_projections(self) -> pd.DataFrame: | 2049 | ### | ### | ### | ### | ### | | 2050 | ### | ### | ### | ### | ### | """ - years = range(2017, 2051) vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") @@ -2324,7 +2159,6 @@ def get_projections(self) -> pd.DataFrame: class EfsElectricityScalar(DemandScaler): - def __init__(self, filepath: str): self.efs = filepath self.region = "united_states" @@ -2347,6 +2181,7 @@ def read(self) -> pd.DataFrame: ) def interpolate(self, df: pd.DataFrame) -> pd.DataFrame: + """Function interpolates between provided demand data years.""" efs_years = df.index new_years = range(min(efs_years), max(efs_years) + 1) df = df.reindex(new_years) @@ -2384,13 +2219,10 @@ def get_projections(self) -> pd.DataFrame: def get_demand_params( end_use: str, - demand_params: Optional[dict[str, str]] = None, + demand_params: dict[str, str] | None = None, **kwargs, ) -> tuple: - """ - Gets hard coded demand options. - """ - + """Gets hard coded demand options.""" match end_use: case "power": # electricity only study demand_profile = demand_params["profile"] diff --git a/workflow/scripts/build_electricity_sector.py b/workflow/scripts/build_electricity_sector.py index c07105a04..11115b8d3 100644 --- a/workflow/scripts/build_electricity_sector.py +++ b/workflow/scripts/build_electricity_sector.py @@ -1,7 +1,7 @@ -"""Functions for building electricity infrastructure in sector studies""" +"""Functions for building electricity infrastructure in sector studies.""" import logging -from typing import Any, Optional +from typing import Any import numpy as np import pandas as pd @@ -14,13 +14,11 @@ def build_electricty( n: pypsa.Network, sector: str, - pop_layout_path: Optional[pd.DataFrame] = None, - options: Optional[dict[str, Any]] = None, + pop_layout_path: pd.DataFrame | None = None, + options: dict[str, Any] | None = None, ) -> None: - """Adds electricity sector infrastructre data""" - + """Adds electricity sector infrastructre data.""" if sector in ("res", "com", "srv"): - split_urban_rural = options.get("split_urban_rural", False) if split_urban_rural: @@ -44,14 +42,17 @@ def build_electricty( add_electricity_dr(n, sector, dr_config) -def add_electricity_infrastructure(n: pypsa.Network, sector: str, suffix: Optional[str] = None): +def add_electricity_infrastructure( + n: pypsa.Network, + sector: str, + suffix: str | None = None, +): """ Adds links to connect electricity nodes. For example, will build the link between "p480 0" and "p480 0 ind- elec" """ - elec = SecCarriers.ELECTRICITY.value if suffix: @@ -86,10 +87,7 @@ def add_electricity_dr( sector: str, dr_config: dict[str, Any], ) -> None: - """ - Adds stores to the network to use for demand response. - """ - + """Adds stores to the network to use for demand response.""" by_carrier = dr_config.get("by_carrier", False) # check if dr is applied at a per-carrier level @@ -236,7 +234,6 @@ def _split_urban_rural_load( "p600 0 com-urban-elec" and "p600 0 com-rural-elec" at the same location as "p600 0"). """ - assert sector in ("com", "res") fuel = SecCarriers.ELECTRICITY.value @@ -244,7 +241,6 @@ def _split_urban_rural_load( load_names = n.loads[n.loads.carrier == f"{sector}-{fuel}"].index.to_list() for system in ("urban", "rural"): - # add buses to connect the new loads to new_buses = pd.DataFrame(index=load_names) new_buses.index = new_buses.index.map(n.loads.bus) @@ -299,10 +295,7 @@ def _format_total_load( n: pypsa.Network, sector: str, ) -> None: - """ - Formats load with 'total' prefix to match urban/rural split. - """ - + """Formats load with 'total' prefix to match urban/rural split.""" assert sector in ("com", "res", "srv") fuel = SecCarriers.ELECTRICITY.value diff --git a/workflow/scripts/build_emission_tracking.py b/workflow/scripts/build_emission_tracking.py index f0a7ef69c..d13ce1f79 100644 --- a/workflow/scripts/build_emission_tracking.py +++ b/workflow/scripts/build_emission_tracking.py @@ -1,10 +1,8 @@ -""" -Module for building state and sector level co2 tracking. -""" +"""Module for building state and sector level co2 tracking.""" import itertools import logging -from typing import Any, Optional +from typing import Any import numpy as np import pandas as pd @@ -15,12 +13,9 @@ def build_co2_tracking( n: pypsa.Network, - config: Optional[dict[str, Any]] = None, + config: dict[str, Any] | None = None, ) -> None: - """ - Main funtion to interface with. - """ - + """Main funtion to interface with.""" states = n.buses.STATE.unique() sectors = ["pwr", "trn", "res", "com", "ind"] @@ -39,14 +34,13 @@ def build_ch4_tracking( n: pypsa.Network, gwp: float, leakage_rate: float, - config: Optional[dict[str, Any]] = None, + config: dict[str, Any] | None = None, ) -> None: """ Builds CH4 tracking. Natural gas network must already be constructed """ - states = [x for x in n.buses.STATE.dropna().unique() if x != np.nan] if not config: @@ -74,10 +68,7 @@ def _add_co2_carrier(n, config: dict[Any]): def _build_co2_bus(n: pypsa.Network, states: list[str], sectors: list[str]): - """ - Builds state level co2 bus per sector. - """ - + """Builds state level co2 bus per sector.""" df = pd.DataFrame(itertools.product(states, sectors), columns=["state", "sector"]) df.index = df.state + " " + df.sector @@ -85,10 +76,7 @@ def _build_co2_bus(n: pypsa.Network, states: list[str], sectors: list[str]): def _build_co2_store(n: pypsa.Network, states: list[str], sectors: list[str]): - """ - Builds state level co2 stores per sector. - """ - + """Builds state level co2 stores per sector.""" df = pd.DataFrame(itertools.product(states, sectors), columns=["state", "sector"]) df.index = df.state + " " + df.sector @@ -124,10 +112,7 @@ def _add_ch4_carrier(n, config: dict[Any]): def _build_ch4_bus(n: pypsa.Network, states: list[str]): - """ - Builds state level co2 bus per sector. - """ - + """Builds state level co2 bus per sector.""" df = pd.DataFrame(states, columns=["state"]) df.index = df.state @@ -135,10 +120,7 @@ def _build_ch4_bus(n: pypsa.Network, states: list[str]): def _build_ch4_store(n: pypsa.Network, states: list[str]): - """ - Builds state level co2 stores per sector. - """ - + """Builds state level co2 stores per sector.""" df = pd.DataFrame(states, columns=["state"]) df.index = df.state @@ -161,10 +143,7 @@ def _build_ch4_store(n: pypsa.Network, states: list[str]): def _build_ch4_links(n, states: list[str], gwp: float, leakage_rate: float): - """ - Modifies existing gas production links. - """ - + """Modifies existing gas production links.""" # first extract out exising gas production links gas_production = [f"{x} gas production" for x in states] diff --git a/workflow/scripts/build_fuel_prices.py b/workflow/scripts/build_fuel_prices.py index fceb22c84..6f42bfbf0 100644 --- a/workflow/scripts/build_fuel_prices.py +++ b/workflow/scripts/build_fuel_prices.py @@ -1,6 +1,6 @@ # By PyPSA-USA Authors """ -**Description** +**Description**. Build_fuel_prices.py is a script that prepares data for dynamic fuel prices to be used in the `add_electricity` module. Data is input from `retrieve_caiso_data` and `retrieve_eia_data` to create a combined dataframe with all dynamic fuel prices available. The prices are modified to be on an hourly basis to match the network snapshots, and converted to $/MWh_thermal. The output is a CSV file containing the hourly fuel prices for each Balancing Authority and State. @@ -22,14 +22,10 @@ """ import logging -import sys from pathlib import Path -from typing import List import constants as const -import duckdb import eia -import numpy as np import pandas as pd from _helpers import configure_logging, get_snapshots, mock_snakemake from build_powerplants import ( @@ -47,10 +43,7 @@ def make_hourly(df: pd.DataFrame) -> pd.DataFrame: - """ - Makes the index hourly. - """ - + """Makes the index hourly.""" start = df.index.min() end = ( pd.to_datetime(start).to_period("Y").to_timestamp("Y").to_period("Y").to_timestamp("Y") @@ -117,7 +110,6 @@ def get_caiso_ng_power_prices( filepath: str, **kwargs, ) -> pd.DataFrame: - # pypsa-usa name: caiso name ba_mapper = { "CISO-PGAE": "CISO", @@ -150,9 +142,7 @@ def get_caiso_ng_power_prices( # Build PuDL EIA 923 Fuel Recipts based fuel costs ### def build_pudl_fuel_costs(snapshots: pd.DatetimeIndex, start_date: str, end_date: str): - """ - Build fuel costs based on PUDL EIA 923 Fuel Receipts data. - """ + """Build fuel costs based on PUDL EIA 923 Fuel Receipts data.""" _, fuel_cost_temporal = load_pudl_data(snakemake.input.pudl, start_date, end_date) fuel_cost_temporal["interconnect"] = fuel_cost_temporal["nerc_region"].map( const.NERC_REGION_MAPPER, @@ -190,8 +180,6 @@ def build_pudl_fuel_costs(snapshots: pd.DatetimeIndex, start_date: str, end_date if __name__ == "__main__": if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake("build_fuel_prices", interconnect="texas") configure_logging(snakemake) diff --git a/workflow/scripts/build_heat.py b/workflow/scripts/build_heat.py index b0f28f1b9..617c7ab01 100644 --- a/workflow/scripts/build_heat.py +++ b/workflow/scripts/build_heat.py @@ -1,16 +1,14 @@ -""" -Module for building heating and cooling infrastructure. -""" +"""Module for building heating and cooling infrastructure.""" import logging -from typing import Any, Optional +from typing import Any import numpy as np import pandas as pd import pypsa import xarray as xr from constants import NG_MWH_2_MMCF, STATE_2_CODE, COAL_dol_ton_2_MWHthermal -from constants_sector import SecCarriers, SecNames +from constants_sector import SecNames from eia import FuelCosts logger = logging.getLogger(__name__) @@ -25,15 +23,12 @@ def build_heat( pop_layout_path: str, cop_ashp_path: str, cop_gshp_path: str, - eia: Optional[str] = None, # for dynamic pricing - year: Optional[int] = None, # for dynamic pricing - options: Optional[dict[str, str | bool | int | float]] = None, + eia: str | None = None, # for dynamic pricing + year: int | None = None, # for dynamic pricing + options: dict[str, str | bool | int | float] | None = None, **kwargs, ) -> None: - """ - Main funtion to interface with. - """ - + """Main funtion to interface with.""" sns = n.snapshots pop_layout = pd.read_csv(pop_layout_path).set_index("name") @@ -54,7 +49,6 @@ def build_heat( dr_config = options.get("demand_response", {}) if sector in ("res", "com", "srv"): - split_urban_rural = options.get("split_urban_rural", False) technologies = options.get("technologies") water_heating_config = options.get("water_heating", {}) @@ -107,7 +101,6 @@ def build_heat( assert not n.links_t.p_set.isna().any().any() elif sector == SecNames.INDUSTRY.value: - if dynamic_costs: gas_costs = _get_dynamic_marginal_costs( n, @@ -138,11 +131,11 @@ def combined_heat(n: pypsa.Network, sector: str) -> bool: """ Searches loads for combined or split heat loads. - Returns: + Returns + ------- True - If only '-heat' is used in load indexing False - If '-water-heat' and '-space-heat' is used in load indexing """ - assert sector in ("res", "com") loads = n.loads.index.to_list() @@ -165,7 +158,6 @@ def reindex_cop(sns: pd.MultiIndex, da: xr.DataArray) -> pd.DataFrame: This will allign snapshots to match the planning horizon. This will also calcualte the mean COP for each period if tsa has occured """ - cop = da.to_pandas() investment_years = sns.get_level_values(0).unique() @@ -198,13 +190,10 @@ def _get_dynamic_marginal_costs( fuel: str, eia: str, year: int, - sector: Optional[str] = None, + sector: str | None = None, **kwargs, ) -> pd.DataFrame: - """ - Gets end-use fuel costs at a state level. - """ - + """Gets end-use fuel costs at a state level.""" sector_mapper = { "res": "residential", "com": "commercial", @@ -237,7 +226,12 @@ def _get_dynamic_marginal_costs( ).get_data( pivot=True, ) - proj = FuelCosts(fuel, year, eia, industry=sector_mapper[sector]).get_data( + proj = FuelCosts( + fuel, + year, + eia, + industry=sector_mapper[sector], + ).get_data( pivot=True, ) @@ -304,9 +298,7 @@ def get_link_marginal_costs( links: pd.DataFrame, dynamic_costs: pd.DataFrame, ) -> pd.DataFrame: - """ - Gets dynamic marginal costs dataframe to add to the system. - """ + """Gets dynamic marginal costs dataframe to add to the system.""" assert len(dynamic_costs) == len(n.snapshots.get_level_values(1)) if "USA" not in dynamic_costs.columns: @@ -338,12 +330,11 @@ def add_industrial_heat( n: pypsa.Network, sector: str, costs: pd.DataFrame, - marginal_gas: Optional[pd.DataFrame | float] = None, - marginal_coal: Optional[pd.DataFrame | float] = None, - dr_config: Optional[dict[str, Any]] = None, + marginal_gas: pd.DataFrame | float | None = None, + marginal_coal: pd.DataFrame | float | None = None, + dr_config: dict[str, Any] | None = None, **kwargs, ) -> None: - assert sector == SecNames.INDUSTRY.value add_industrial_gas_furnace(n, costs, marginal_gas) @@ -360,18 +351,15 @@ def add_service_heat( pop_layout: pd.DataFrame, costs: pd.DataFrame, split_urban_rural: bool, - technologies: Optional[dict[str, str | bool | float]] = None, - ashp_cop: Optional[pd.DataFrame] = None, - gshp_cop: Optional[pd.DataFrame] = None, - marginal_gas: Optional[pd.DataFrame | float] = None, - marginal_oil: Optional[pd.DataFrame | float] = None, - water_heating_config: Optional[dict[str, Any]] = None, - dr_config: Optional[dict[str, Any]] = None, + technologies: dict[str, str | bool | float] | None = None, + ashp_cop: pd.DataFrame | None = None, + gshp_cop: pd.DataFrame | None = None, + marginal_gas: pd.DataFrame | float | None = None, + marginal_oil: pd.DataFrame | float | None = None, + water_heating_config: dict[str, Any] | None = None, + dr_config: dict[str, Any] | None = None, ): - """ - Adds heating links for residential and commercial sectors. - """ - + """Adds heating links for residential and commercial sectors.""" assert sector in ("res", "com", "srv") if not technologies: @@ -409,9 +397,7 @@ def add_service_heat( # add heat pumps for heat_system in heat_systems: - if (heat_system in ["urban", "total"]) and include_hps: - heat_pump_type = "air" cop = ashp_cop @@ -427,7 +413,6 @@ def add_service_heat( ) if (heat_system in ["rural", "total"]) and include_hps: - heat_pump_type = "ground" cop = gshp_cop @@ -479,7 +464,6 @@ def add_service_heat( # check if water heat is needed if split_space_water: - simple_storage = water_heating_config.get("simple_storage", False) n_hours = water_heating_config.get("n_hours", None) @@ -529,12 +513,11 @@ def add_service_cooling( sector: str, pop_layout: pd.DataFrame, costs: pd.DataFrame, - split_urban_rural: Optional[bool] = True, - technologies: Optional[dict[str, bool]] = None, - dr_config: Optional[dict[str, Any]] = None, + split_urban_rural: bool | None = True, + technologies: dict[str, bool] | None = None, + dr_config: dict[str, Any] | None = None, **kwargs, ): - assert sector in ("res", "com", "srv") if not technologies: @@ -570,10 +553,7 @@ def add_air_cons( heat_system: str, costs: pd.DataFrame, ) -> None: - """ - Adds gas furnaces to the system. - """ - + """Adds gas furnaces to the system.""" assert heat_system in ("urban", "rural", "total") match sector: @@ -629,7 +609,6 @@ def add_service_heat_pumps_cooling( heat_system: str ("rural" or "urban") """ - assert sector in ("com", "res") assert heat_system in ("urban", "rural", "total") assert heat_carrier in ["cool"] @@ -646,7 +625,9 @@ def add_service_heat_pumps_cooling( cool_links = cool_links.rename(index=index_mapper) cool_links_cop = cool_links_cop.rename(columns=index_mapper) - cool_links["bus1"] = cool_links["bus0"].map(lambda x: x.split(f" {sector}")[0]) # node code + cool_links["bus1"] = cool_links["bus0"].map( + lambda x: x.split(f" {sector}")[0], + ) # node code cool_links["bus1"] = cool_links["bus1"] + f" {sector}-{heat_system}-{heat_carrier}" # carrier_name = f"{sector}-{heat_system}-{heat_carrier}" @@ -689,14 +670,12 @@ def _split_urban_rural_load( than pypsa-eur implementation, as we add all load before clustering; we are not adding load here, rather just splitting it up """ - assert sector in ("com", "res") assert fuel in ("heat", "cool", "space-heat", "water-heat") load_names = n.loads[n.loads.carrier == f"{sector}-{fuel}"].index.to_list() for system in ("urban", "rural"): - # add buses to connect the new loads to new_buses = pd.DataFrame(index=load_names) new_buses.index = new_buses.index.map(n.loads.bus) @@ -749,10 +728,7 @@ def _format_total_load( sector: str, fuel: str, ) -> None: - """ - Formats load with 'total' prefix to match urban/rural split. - """ - + """Formats load with 'total' prefix to match urban/rural split.""" assert sector in ("com", "res", "srv") assert fuel in ("heat", "cool", "space-heat", "water-heat") @@ -811,7 +787,7 @@ def add_service_furnace( heat_carrier: str, fuel: str, costs: pd.DataFrame, - marginal_cost: Optional[pd.DataFrame | float] = None, + marginal_cost: pd.DataFrame | float | None = None, ) -> None: """ Adds direct furnace heating to the system. @@ -881,7 +857,7 @@ def add_service_furnace( if isinstance(marginal_cost, pd.DataFrame): assert "state" in df.columns mc = get_link_marginal_costs(n, df, marginal_cost) - elif isinstance(marginal_cost, (int, float)): + elif isinstance(marginal_cost, int | float): mc = marginal_cost else: mc = 0 @@ -921,20 +897,16 @@ def add_heat_dr( n: pypsa.Network, sector: str, dr_config: dict[str, Any], - heat_system: Optional[str] = None, - heat_carrier: Optional[str] = None, - standing_loss: Optional[float] = None, + heat_system: str | None = None, + heat_carrier: str | None = None, + standing_loss: float | None = None, ) -> None: - """ - Adds end-use thermal demand response. - """ - + """Adds end-use thermal demand response.""" by_carrier = dr_config.get("by_carrier", False) # check if dr is applied at a per-carrier level if sector in ["res", "com"]: - assert heat_system in ("urban", "rural", "total") assert heat_carrier in ("heat", "space-heat", "cool") @@ -944,8 +916,7 @@ def add_heat_dr( dr_config = dr_config.get(heat_carrier, {}) elif sector == "ind": - - carrier_name = f"ind-heat" + carrier_name = "ind-heat" if by_carrier: dr_config = dr_config.get("heat", {}) @@ -1090,11 +1061,11 @@ def add_service_water_store( heat_system: str, fuel: str, costs: pd.DataFrame, - marginal_cost: Optional[pd.DataFrame | float] = None, - standing_loss: Optional[float] = None, - extendable: Optional[bool] = True, - simple_storage: Optional[bool] = True, - n_hours: Optional[int | float] = None, + marginal_cost: pd.DataFrame | float | None = None, + standing_loss: float | None = None, + extendable: bool | None = True, + simple_storage: bool | None = True, + n_hours: int | float | None = None, ) -> None: """ Adds end-use water heat storage system. @@ -1110,7 +1081,6 @@ def add_service_water_store( stores. If True, costs are applied to the discharging link based on 4hr storage capacity. """ - assert sector in ("res", "com") assert heat_system in ("urban", "rural", "total") @@ -1162,7 +1132,7 @@ def add_service_water_store( if isinstance(marginal_cost, pd.DataFrame): assert "state" in df.columns mc = get_link_marginal_costs(n, df, marginal_cost) - elif isinstance(marginal_cost, (int, float)): + elif isinstance(marginal_cost, int | float): mc = marginal_cost else: mc = 0 @@ -1258,7 +1228,7 @@ def add_service_heat_pumps( heat_carrier: str, hp_type: str, costs: pd.DataFrame, - cop: Optional[pd.DataFrame] = None, + cop: pd.DataFrame | None = None, ) -> None: """ Adds heat pumps to the system. @@ -1278,7 +1248,6 @@ def add_service_heat_pumps( cop: pd.DataFrame If not provided, uses eff in costs """ - hp_type = hp_type.capitalize() assert sector in ("com", "res") @@ -1339,9 +1308,8 @@ def add_service_heat_pumps( def add_industrial_gas_furnace( n: pypsa.Network, costs: pd.DataFrame, - marginal_cost: Optional[pd.DataFrame | float] = None, + marginal_cost: pd.DataFrame | float | None = None, ) -> None: - sector = SecNames.INDUSTRY.value capex = costs.at["direct firing gas", "capital_cost"].round(1) @@ -1369,7 +1337,7 @@ def add_industrial_gas_furnace( if isinstance(marginal_cost, pd.DataFrame): assert "state" in furnaces.columns mc = get_link_marginal_costs(n, furnaces, marginal_cost) - elif isinstance(marginal_cost, (int, float)): + elif isinstance(marginal_cost, int | float): mc = marginal_cost else: mc = 0 @@ -1377,7 +1345,7 @@ def add_industrial_gas_furnace( n.madd( "Link", furnaces.index, - suffix="-gas-furnace", #'ind' included in index already + suffix="-gas-furnace", # 'ind' included in index already bus0=furnaces.bus0, bus1=furnaces.bus1, bus2=furnaces.bus2, @@ -1394,9 +1362,8 @@ def add_industrial_gas_furnace( def add_industrial_coal_furnace( n: pypsa.Network, costs: pd.DataFrame, - marginal_cost: Optional[pd.DataFrame | float] = None, + marginal_cost: pd.DataFrame | float | None = None, ) -> None: - sector = SecNames.INDUSTRY.value # performance charasteristics taken from (Table 311.1a) @@ -1428,7 +1395,7 @@ def add_industrial_coal_furnace( if isinstance(marginal_cost, pd.DataFrame): assert "state" in furnace.columns mc = get_link_marginal_costs(n, furnace, marginal_cost) - elif isinstance(marginal_cost, (int, float)): + elif isinstance(marginal_cost, int | float): mc = marginal_cost else: mc = 0 @@ -1454,7 +1421,6 @@ def add_indusrial_heat_pump( n: pypsa.Network, costs: pd.DataFrame, ) -> None: - sector = SecNames.INDUSTRY.value capex = costs.at["industrial heat pump high temperature", "capital_cost"].round(1) diff --git a/workflow/scripts/build_hydro_profile.py b/workflow/scripts/build_hydro_profile.py deleted file mode 100644 index 3bf1f889a..000000000 --- a/workflow/scripts/build_hydro_profile.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python - -# SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors -# -# SPDX-License-Identifier: MIT -""" -Build hydroelectric inflow time-series for each country. - -Relevant Settings ------------------ - -.. code:: yaml - - countries: - - renewable: - hydro: - cutout: - clip_min_inflow: - -.. seealso:: - Documentation of the configuration file ``config/config.yaml`` at - :ref:`toplevel_cf`, :ref:`renewable_cf` - -Inputs ------- - -- ``data/bundle/eia_hydro_annual_generation.csv``: Hydroelectricity net generation per country and year (`EIA `_) - - .. image:: img/hydrogeneration.png - :scale: 33 % - -- ``resources/country_shapes.geojson``: confer :ref:`shapes` -- ``"cutouts/" + config["renewable"]['hydro']['cutout']``: confer :ref:`cutout` - -Outputs -------- - -- ``resources/profile_hydro.nc``: - - =================== ================ ========================================================= - Field Dimensions Description - =================== ================ ========================================================= - inflow countries, time Inflow to the state of charge (in MW), - e.g. due to river inflow in hydro reservoir. - =================== ================ ========================================================= - - .. image:: img/inflow-ts.png - :scale: 33 % - - .. image:: img/inflow-box.png - :scale: 33 % - -Description ------------ - -.. seealso:: - :mod:`build_renewable_profiles` -""" - -import logging - -import atlite -import country_converter as coco -import geopandas as gpd -import pandas as pd -from _helpers import configure_logging, set_scenario_config -from numpy.polynomial import Polynomial - -cc = coco.CountryConverter() - - -def get_eia_annual_hydro_generation(fn, countries, capacities=False): - # in billion kWh/a = TWh/a - df = pd.read_csv(fn, skiprows=2, index_col=1, na_values=[" ", "--"]).iloc[1:, 1:] - df.index = df.index.str.strip() - df.columns = df.columns.astype(int) - - former_countries = { - "Former Czechoslovakia": dict( - countries=["Czechia", "Slovakia"], - start=1980, - end=1992, - ), - "Former Serbia and Montenegro": dict( - countries=["Serbia", "Montenegro"], - start=1992, - end=2005, - ), - "Former Yugoslavia": dict( - countries=[ - "Slovenia", - "Croatia", - "Bosnia and Herzegovina", - "Serbia", - "Montenegro", - "North Macedonia", - ], - start=1980, - end=1991, - ), - } - - for k, v in former_countries.items(): - period = [i for i in range(v["start"], v["end"] + 1)] - ratio = df.loc[v["countries"]].T.dropna().sum() - ratio /= ratio.sum() - for country in v["countries"]: - df.loc[country, period] = df.loc[k, period] * ratio[country] - - baltic_states = ["Latvia", "Estonia", "Lithuania"] - df.loc[baltic_states] = df.loc[baltic_states].T.fillna(df.loc[baltic_states].mean(axis=1)).T - - df.loc["Germany"] = df.filter(like="Germany", axis=0).sum() - df.loc["Serbia"] += df.loc["Kosovo"].fillna(0.0) - df = df.loc[~df.index.str.contains("Former")] - df.drop(["Europe", "Germany, West", "Germany, East", "Kosovo"], inplace=True) - - df.index = cc.convert(df.index, to="iso2") - df.index.name = "countries" - - # convert to MW of MWh/a - factor = 1e3 if capacities else 1e6 - df = df.T[countries] * factor - - return df - - -def correct_eia_stats_by_capacity(eia_stats, fn, countries, baseyear=2019): - cap = get_eia_annual_hydro_generation(fn, countries, capacities=True) - ratio = cap / cap.loc[baseyear] - eia_stats_corrected = eia_stats / ratio - to_keep = ["AL", "AT", "CH", "DE", "GB", "NL", "RS", "RO", "SK"] - to_correct = eia_stats_corrected.columns.difference(to_keep) - eia_stats.loc[:, to_correct] = eia_stats_corrected.loc[:, to_correct] - - -def approximate_missing_eia_stats(eia_stats, runoff_fn, countries): - runoff = pd.read_csv(runoff_fn, index_col=0).T[countries] - runoff.index = runoff.index.astype(int) - - runoff_eia = runoff.loc[eia_stats.index] - - eia_stats_approximated = {} - - for c in countries: - X = runoff_eia[c] - Y = eia_stats[c] - - to_predict = runoff.index.difference(eia_stats.index) - X_pred = runoff.loc[to_predict, c] - - p = Polynomial.fit(X, Y, 1) - Y_pred = p(X_pred) - - eia_stats_approximated[c] = pd.Series(Y_pred, index=to_predict) - - eia_stats_approximated = pd.DataFrame(eia_stats_approximated) - return pd.concat([eia_stats, eia_stats_approximated]).sort_index() - - -logger = logging.getLogger(__name__) - -if __name__ == "__main__": - if "snakemake" not in globals(): - from _helpers import mock_snakemake - - snakemake = mock_snakemake("build_hydro_profile", interconnect="texas") - configure_logging(snakemake) - set_scenario_config(snakemake) - - params_hydro = snakemake.params.hydro - - time = pd.date_range(**snakemake.params.snapshots) - - cutout = atlite.Cutout(snakemake.input.cutout).sel(time=time) - - # countries = snakemake.params.countries - country_shapes = gpd.read_file(snakemake.input.reeds_shapes).set_index("name")["geometry"] - country_shapes.index.name = "countries" - - # fn = snakemake.input.eia_hydro_generation - # eia_stats = get_eia_annual_hydro_generation(fn, countries) - - # config_hydro = snakemake.config["renewable"]["hydro"] - - # if config_hydro.get("eia_correct_by_capacity"): - # fn = snakemake.input.eia_hydro_capacity - # correct_eia_stats_by_capacity(eia_stats, fn, countries) - - # if config_hydro.get("eia_approximate_missing"): - # fn = snakemake.input.era5_runoff - # eia_stats = approximate_missing_eia_stats(eia_stats, fn, countries) - - # contained_years = pd.date_range(freq="YE", **snakemake.params.snapshots).year - # norm_year = config_hydro.get("eia_norm_year") - # missing_years = contained_years.difference(eia_stats.index) - # if norm_year: - # eia_stats.loc[contained_years] = eia_stats.loc[norm_year] - # elif missing_years.any(): - # eia_stats.loc[missing_years] = eia_stats.median() - - inflow = cutout.runoff( - shapes=country_shapes, - smooth=True, - lower_threshold_quantile=True, - # normalize_using_yearly=eia_stats, - ) - - if "clip_min_inflow" in params_hydro: - inflow = inflow.where(inflow > params_hydro["clip_min_inflow"], 0) - - inflow.to_netcdf(snakemake.output.profile) diff --git a/workflow/scripts/build_natural_gas.py b/workflow/scripts/build_natural_gas.py index 73f632406..c7065b0f1 100644 --- a/workflow/scripts/build_natural_gas.py +++ b/workflow/scripts/build_natural_gas.py @@ -1,3 +1,5 @@ +# ruff: noqa: RUF012, D101, D102 + """ Module for adding the gas sector. @@ -14,21 +16,21 @@ """ import logging +from abc import ABC, abstractmethod +from math import pi +from typing import Any +import eia import geopandas as gpd +import numpy as np import pandas as pd import pypsa -from constants import NG_MWH_2_MMCF, STATE_2_CODE, STATES_INTERCONNECT_MAPPER +import yaml +from constants import CODE_2_STATE, NG_MWH_2_MMCF, STATE_2_CODE, STATES_INTERCONNECT_MAPPER from pypsa.components import Network logger = logging.getLogger(__name__) -from abc import ABC, abstractmethod -from math import pi -from typing import Any, Optional -import eia -import numpy as np -import yaml ### # Constants @@ -38,38 +40,28 @@ MWH_2_MMCF = NG_MWH_2_MMCF KJ_2_MWH = (1 / 1000) * (1 / 3600) -CODE_2_STATE = {v: k for k, v in STATE_2_CODE.items()} - ### # Geolocation of Assets class ### class StateGeometry: - """ - Holds state boundry data. - """ + """Holds state boundry data.""" def __init__(self, shapefile: str) -> None: - """ - Counties shapefile. - """ + """Counties shapefile.""" self._counties = gpd.read_file(shapefile) self._state_center_points = None self._states = None @property def counties(self) -> gpd.GeoDataFrame: - """ - Spatially resolved counties. - """ + """Spatially resolved counties.""" return self._counties @property def states(self) -> gpd.GeoDataFrame: - """ - Spatially resolved states. - """ + """Spatially resolved states.""" if self._states: return self._states else: @@ -78,9 +70,7 @@ def states(self) -> gpd.GeoDataFrame: @property def state_center_points(self) -> gpd.GeoDataFrame: - """ - Center points of Sates. - """ + """Center points of Sates.""" if self._state_center_points: return self._state_center_points else: @@ -90,9 +80,7 @@ def state_center_points(self) -> gpd.GeoDataFrame: return self._state_center_points def _get_state_boundaries(self) -> gpd.GeoDataFrame: - """ - Gets admin boundaries of state. - """ + """Gets admin boundaries of state.""" return ( self._counties.dissolve("STATE_NAME") .rename(columns={"STUSPS": "STATE"}) @@ -100,9 +88,7 @@ def _get_state_boundaries(self) -> gpd.GeoDataFrame: ) def _get_state_center_points(self) -> gpd.GeoDataFrame: - """ - Gets centerpoints of states using county shapefile. - """ + """Gets centerpoints of states using county shapefile.""" gdf = self._states.copy().rename(columns={"geometry": "shape"}) gdf["geometry"] = gdf["shape"].map(lambda x: x.centroid) gdf[["x", "y"]] = gdf["geometry"].apply( @@ -117,9 +103,7 @@ def _get_state_center_points(self) -> gpd.GeoDataFrame: class GasData(ABC): - """ - Main class to interface with data. - """ + """Main class to interface with data.""" state_2_interconnect = STATES_INTERCONNECT_MAPPER state_2_name = CODE_2_STATE @@ -158,12 +142,9 @@ def build_infrastructure(self, n: pypsa.Network) -> None: def filter_on_interconnect( self, df: pd.DataFrame, - additional_removals: list[str] = None, + additional_removals: list[str] | None = None, ) -> pd.DataFrame: - """ - Name of states must be in column called 'STATE'. - """ - + """Name of states must be in column called 'STATE'.""" states_2_remove = self.states_2_remove if additional_removals: states_2_remove += additional_removals @@ -247,7 +228,6 @@ def filter_on_sate( return df def build_infrastructure(self, n: Network) -> None: - df = self.filter_on_sate(n, self.data) states = df.set_index("STATE") @@ -268,9 +248,7 @@ def build_infrastructure(self, n: Network) -> None: class GasStorage(GasData): - """ - Creator for underground storage. - """ + """Creator for underground storage.""" def __init__(self, year: int, interconnect: str, api: str) -> None: self.api = api @@ -339,7 +317,6 @@ def filter_on_sate( return df def build_infrastructure(self, n: pypsa.Network, **kwargs): - df = self.filter_on_sate(n, self.data) df.index = df.STATE df["state_name"] = df.index.map(self.state_2_name) @@ -406,9 +383,7 @@ def build_infrastructure(self, n: pypsa.Network, **kwargs): class GasProcessing(GasData): - """ - Creator for processing capacity. - """ + """Creator for processing capacity.""" def __init__(self, year: int, interconnect: str, api: str) -> None: self.api = api @@ -455,7 +430,6 @@ def filter_on_sate( return df def build_infrastructure(self, n: pypsa.Network, **kwargs): - df = self.filter_on_sate(n, self.data) df = df.set_index("STATE") df["bus"] = df.index + " gas" @@ -528,13 +502,12 @@ def build_infrastructure(self, n: pypsa.Network, **kwargs): class _GasPipelineCapacity(GasData): - def __init__( self, year: int, interconnect: str, xlsx: str, - api: Optional[str] = None, + api: str | None = None, ) -> None: self.xlsx = xlsx self.api = api @@ -561,7 +534,6 @@ def filter_on_sate( df: pd.DataFrame, in_spatial_scope: bool, ) -> pd.DataFrame: - states_in_model = self.get_states_in_model(n) if ("STATE_TO" and "STATE_FROM") not in df.columns: @@ -633,7 +605,7 @@ def format_data(self, data: pd.DataFrame) -> pd.DataFrame: return self.extract_pipelines(df) def _get_capacity_based_on_trade_flows(self) -> pd.DataFrame: - """Check that trade flows do not exceed design capacity + """Check that trade flows do not exceed design capacity. See Issue #487 https://github.com/PyPSA/pypsa-usa/issues/487 @@ -662,7 +634,6 @@ def _merge_capacity_trade_data( capacity: pd.DataFrame, trade: pd.DataFrame, ) -> pd.DataFrame: - df = pd.concat([capacity, trade]) df = df.sort_values(by="CAPACITY_MW", ascending=False) df = df.drop_duplicates( @@ -693,10 +664,7 @@ def extract_pipelines(self, df: pd.DataFrame) -> pd.DataFrame: pass def assign_pipeline_interconnects(self, df: pd.DataFrame): - """ - Adds interconnect labels to the pipelines. - """ - + """Adds interconnect labels to the pipelines.""" df["STATE_TO"] = df.STATE_NAME_TO.map(self.name_2_state) df["STATE_FROM"] = df.STATE_NAME_FROM.map(self.name_2_state) @@ -709,21 +677,18 @@ def assign_pipeline_interconnects(self, df: pd.DataFrame): class InterconnectGasPipelineCapacity(_GasPipelineCapacity): - """ - Pipeline capacity within the interconnect. - """ + """Pipeline capacity within the interconnect.""" def __init__( self, year: int, interconnect: str, xlsx: str, - api: Optional[str] = None, + api: str | None = None, ) -> None: super().__init__(year, interconnect, xlsx, api) def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: - df = data.copy() # for some reason drop duplicates is not wokring here and I cant figure out why :( # df = df.drop_duplicates(subset=["STATE_TO", "STATE_FROM"], keep=False).copy() @@ -746,7 +711,6 @@ def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: return df.reset_index(drop=True) def build_infrastructure(self, n: pypsa.Network) -> None: - df = self.filter_on_sate(n, self.data, in_spatial_scope=True) if df.empty: @@ -775,9 +739,7 @@ def build_infrastructure(self, n: pypsa.Network) -> None: class TradeGasPipelineCapacity(_GasPipelineCapacity): - """ - Pipeline capcity connecting to the interconnect. - """ + """Pipeline capcity connecting to the interconnect.""" def __init__( self, @@ -791,7 +753,6 @@ def __init__( super().__init__(year, interconnect, xlsx, api) def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: - df = data.copy() if self.domestic: return self._get_domestic_pipeline_connections(df) @@ -799,10 +760,7 @@ def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: return self._get_international_pipeline_connections(df) def _get_domestic_pipeline_connections(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Gets all pipelines within the usa that connect to the interconnect. - """ - + """Gets all pipelines within the usa that connect to the interconnect.""" # get rid of international connections df = df[~((df.INTERCONNECT_TO.isin(["canada", "mexico"])) | (df.INTERCONNECT_FROM.isin(["canada", "mexico"])))] @@ -813,9 +771,7 @@ def _get_domestic_pipeline_connections(self, df: pd.DataFrame) -> pd.DataFrame: return df[df["INTERCONNECT_TO"].eq(self.interconnect) | df["INTERCONNECT_FROM"].eq(self.interconnect)] def _get_international_pipeline_connections(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Gets all international pipeline connections. - """ + """Gets all international pipeline connections.""" df = df[(df.INTERCONNECT_TO.isin(["canada", "mexico"])) | (df.INTERCONNECT_FROM.isin(["canada", "mexico"]))] if self.interconnect == "usa": return df @@ -833,7 +789,6 @@ def _get_international_costs( interpolation_method can be one of: - linear, zero """ - assert direction in ("imports", "exports") # fuel costs/profits at a national level @@ -846,9 +801,7 @@ def _get_international_costs( return costs.resample("1h").asfreq().interpolate(method=interpoloation_method) def _expand_costs(self, n: pypsa.Network, costs: pd.DataFrame) -> pd.DataFrame: - """ - Expands import/export costs over snapshots and investment periods. - """ + """Expands import/export costs over snapshots and investment periods.""" expanded_costs = [] for invesetment_period in n.investment_periods: # reindex to match any tsa @@ -937,10 +890,7 @@ def _get_marginal_costs( connections: pd.DataFrame, imports: bool, ) -> pd.DataFrame: - """ - Gets time varrying import/export costs. - """ - + """Gets time varrying import/export costs.""" df = connections.copy() states_in_model = self.get_states_in_model(n) @@ -964,7 +914,6 @@ def _assign_country(self, n: pypsa.Network, template: pd.DataFrame) -> pd.DataFr Country is always in model spatial scope. """ - df = template.copy() states_in_model = self.get_states_in_model(n) @@ -981,9 +930,7 @@ def _assign_link_buses( n: pypsa.Network, template: pd.DataFrame, ) -> pd.DataFrame: - """ - Assigns bus names for links. - """ + """Assigns bus names for links.""" def assign_bus0_name(row) -> str: if row["STATE_FROM"] in states_in_model: @@ -1013,7 +960,6 @@ def _assign_stores(self, template: pd.DataFrame) -> pd.DataFrame: imports) If bus0 is a state gas bus, energy will flow out of the model (ie. exports) """ - df = template.copy() df["store"] = df.bus0.map( @@ -1041,7 +987,6 @@ def build_infrastructure(self, n: pypsa.Network) -> None: - "WA BC gas trade" - "BC WA gas trade" """ - df = self.filter_on_sate(n, self.data, in_spatial_scope=False) df = self._add_zero_capacity_connections(df) @@ -1140,9 +1085,7 @@ def build_infrastructure(self, n: pypsa.Network) -> None: class PipelineLinepack(GasData): - """ - Creator for linepack infrastructure. - """ + """Creator for linepack infrastructure.""" def __init__( self, @@ -1157,7 +1100,7 @@ def __init__( super().__init__(year, interconnect) def read_data(self) -> gpd.GeoDataFrame: - """https://atlas.eia.gov/apps/3652f0f1860d45beb0fed27dc8a6fc8d/explore""" + """https://atlas.eia.gov/apps/3652f0f1860d45beb0fed27dc8a6fc8d/explore.""" return gpd.read_file(self.pipeline_geojson) def filter_on_sate( @@ -1165,7 +1108,6 @@ def filter_on_sate( n: pypsa.Network, df: pd.DataFrame, ) -> pd.DataFrame: - states_in_model = n.buses[ ~n.buses.carrier.isin( ["gas storage", "gas trade", "gas pipeline"], @@ -1230,7 +1172,6 @@ def format_data(self, data: gpd.GeoDataFrame) -> pd.DataFrame: return self.filter_on_interconnect(final) def build_infrastructure(self, n: pypsa.Network, **kwargs) -> None: - df = self.filter_on_sate(n, self.data) df = df.set_index("STATE") @@ -1266,10 +1207,7 @@ def build_infrastructure(self, n: pypsa.Network, **kwargs) -> None: def _remove_marginal_costs(n: pypsa.Network): - """ - Removes marginal costs of CCGT and OCGT plants. - """ - + """Removes marginal costs of CCGT and OCGT plants.""" links = n.links[n.links.carrier.str.contains("CCGT") | n.links.carrier.str.contains("OCGT")].index n.links.loc[links, "marginal_cost"] = 0 @@ -1288,10 +1226,9 @@ def build_natural_gas( county_path: str = "../data/counties/cb_2020_us_county_500k.shp", pipelines_path: str = "../data/natural_gas/EIA-StatetoStateCapacity_Feb2024.xlsx", pipeline_shape_path: str = "../data/natural_gas/pipelines.geojson", - options: Optional[dict[str, Any]] = None, + options: dict[str, Any] | None = None, **kwargs, ) -> None: - if not options: options = {} diff --git a/workflow/scripts/build_population_layouts.py b/workflow/scripts/build_population_layouts.py index 0963e4359..7a3f81c05 100644 --- a/workflow/scripts/build_population_layouts.py +++ b/workflow/scripts/build_population_layouts.py @@ -1,22 +1,18 @@ -""" -Builds mapping between cutout grid cells and population (total, urban, rural). -""" +"""Builds mapping between cutout grid cells and population (total, urban, rural).""" import logging - -logger = logging.getLogger(__name__) - from pathlib import Path import atlite import constants import geopandas as gpd import matplotlib.pyplot as plt -import numpy as np import pandas as pd import xarray as xr from _helpers import configure_logging, mock_snakemake +logger = logging.getLogger(__name__) + def load_urban_ratio(df: pd.DataFrame) -> pd.DataFrame: """ @@ -73,9 +69,9 @@ def load_population(df: pd.DataFrame) -> pd.DataFrame: def plot_county_data( gdf: gpd.GeoDataFrame, col: str, - title: str = None, - description: str = None, - save: str = None, + title: str | None = None, + description: str | None = None, + save: str | None = None, ): """ Plots heat map of geodataframe. @@ -83,7 +79,6 @@ def plot_county_data( Adapted from https://www.relataly.com/visualize-covid-19-data-on-a-geographic-heat-maps/291/ """ - # for legend range vmin = gdf[col].min() vmax = gdf[col].max() @@ -113,11 +108,8 @@ def plot_county_data( fig.savefig(save) -def plot_grid_data(da: xr.DataArray, title: str = None, save: str = None): - """ - Plots gridded population layout. - """ - +def plot_grid_data(da: xr.DataArray, title: str | None = None, save: str | None = None): + """Plots gridded population layout.""" fig, ax = plt.subplots(figsize=(5, 4)) da.plot(ax=ax, cbar_kwargs={"label": "population"}) ax.set_xlabel("longitude") @@ -132,8 +124,6 @@ def plot_grid_data(da: xr.DataArray, title: str = None, save: str = None): if __name__ == "__main__": if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake( "build_population_layouts", interconnect="western", @@ -177,12 +167,15 @@ def plot_grid_data(da: xr.DataArray, title: str = None, save: str = None): counties = counties.join(pop) # Indicator matrix counties -> grid cells - I = atlite.cutout.compute_indicatormatrix(counties.geometry, grid_cells) + indicator_matrix = atlite.cutout.compute_indicatormatrix( + counties.geometry, + grid_cells, + ) # population in each grid cell - cell_pop = pd.Series(I.dot(counties["population"])) - cell_rural_pop = pd.Series(I.dot(counties["rural_population"])) - cell_urban_pop = pd.Series(I.dot(counties["urban_population"])) + cell_pop = pd.Series(indicator_matrix.dot(counties["population"])) + cell_rural_pop = pd.Series(indicator_matrix.dot(counties["rural_population"])) + cell_urban_pop = pd.Series(indicator_matrix.dot(counties["urban_population"])) # save total, rural, urban population pops = { @@ -206,7 +199,6 @@ def plot_grid_data(da: xr.DataArray, title: str = None, save: str = None): # plot data if save_path: - plotting_data = counties.copy() plotting_data["density_person_per_km2"] = plotting_data.population / counties.ALAND * 1000000 columns = { diff --git a/workflow/scripts/build_powerplants.py b/workflow/scripts/build_powerplants.py index 21100567b..d3912f299 100644 --- a/workflow/scripts/build_powerplants.py +++ b/workflow/scripts/build_powerplants.py @@ -1,3 +1,5 @@ +"""Assimilates data on existing generator and storage resources from PUDL, CEMS, ADS, and other sources.""" + import logging import re @@ -118,9 +120,7 @@ def get_heat_rates(start_date, end_date): def set_non_conus(eia_data_operable): - """ - Set NERC region and balancing authority code for non-CONUS plants. - """ + """Set NERC region and balancing authority code for non-CONUS plants.""" eia_data_operable.loc[eia_data_operable.state.isin(["AK", "HI"]), "nerc_region"] = "non-conus" eia_data_operable.loc[ eia_data_operable.state.isin(["AK", "HI"]), @@ -211,7 +211,7 @@ def set_derates(plants): ], }, ) -eia_tech_map.set_index("Technology", inplace=True) +eia_tech_map = eia_tech_map.set_index("Technology") eia_fuel_map = pd.DataFrame( { "Energy Source 1": [ @@ -339,7 +339,7 @@ def set_derates(plants): ], }, ) -eia_fuel_map.set_index("Energy Source 1", inplace=True) +eia_fuel_map = eia_fuel_map.set_index("Energy Source 1") eia_primemover_map = pd.DataFrame( { "Prime Mover": [ @@ -394,7 +394,7 @@ def set_derates(plants): ], }, ) -eia_primemover_map.set_index("Prime Mover", inplace=True) +eia_primemover_map = eia_primemover_map.set_index("Prime Mover") def set_tech_fuels_primer_movers(eia_data_operable): @@ -433,12 +433,10 @@ def standardize_col_names(columns, prefix="", suffix=""): def merge_ads_data(eia_data_operable): - """ - Merges WECC ADS Data into the prepared EIA Data. - """ - ADS_PATH = snakemake.input.wecc_ads + """Merges WECC ADS Data into the prepared EIA Data.""" + path_ads = snakemake.input.wecc_ads ads_thermal = pd.read_csv( - ADS_PATH + "/Thermal_General_Info.csv", + path_ads + "/Thermal_General_Info.csv", skiprows=1, ) # encoding='unicode_escape') ads_thermal = ads_thermal[ @@ -460,7 +458,7 @@ def merge_ads_data(eia_data_operable): ads_thermal.columns = standardize_col_names(ads_thermal.columns) ads_ioc = pd.read_csv( - ADS_PATH + "/Thermal_IOCurve_Info.csv", + path_ads + "/Thermal_IOCurve_Info.csv", skiprows=1, ).rename(columns={"Generator Name": "GeneratorName"}) ads_ioc = ads_ioc[ @@ -478,7 +476,7 @@ def merge_ads_data(eia_data_operable): # loading ads to match ads_name with generator key in order to link with ads thermal file ads = pd.read_csv( - ADS_PATH + "/GeneratorList.csv", + path_ads + "/GeneratorList.csv", skiprows=2, encoding="unicode_escape", ) @@ -492,7 +490,7 @@ def merge_ads_data(eia_data_operable): ads["SubType"] = ads["SubType"].apply( lambda x: re.sub(r"[^a-zA-Z0-9]", "", x).lower(), ) - ads.rename( + ads = ads.rename( { "Name": "ads_name", "Long Name": "ads_long_name", @@ -502,9 +500,8 @@ def merge_ads_data(eia_data_operable): "Area Name": "balancing_area", }, axis=1, - inplace=True, ) - ads.rename(str.lower, axis="columns", inplace=True) + ads = ads.rename(str.lower, axis="columns") ads["long id"] = ads["long id"].astype(str) ads = ads.loc[ :, @@ -522,7 +519,8 @@ def merge_ads_data(eia_data_operable): ads_name_key_dict, ) - # Identify Generators not in ads generator list that are in the IOC curve. This could potentially be matched with manual work. + # Identify Generators not in ads generator list that are in the IOC curve. + # This could potentially be matched with manual work. ads_thermal_ioc[ads_thermal_ioc.generator_key.isna()] # Merge ads thermal_IOC data with ads generator data @@ -552,12 +550,12 @@ def merge_ads_data(eia_data_operable): eia_ads_mapper.columns, prefix="mapper_", ) - eia_ads_mapper.dropna(subset=["mapper_plant_id_eia"], inplace=True) + eia_ads_mapper = eia_ads_mapper.dropna(subset=["mapper_plant_id_eia"]) eia_ads_mapper.mapper_plant_id_eia = eia_ads_mapper.mapper_plant_id_eia.astype(int) eia_ads_mapper.mapper_ads_name = eia_ads_mapper.mapper_ads_name.astype(str) eia_ads_mapper.mapper_generatorkey = eia_ads_mapper.mapper_generatorkey.astype(int) - ads_complete.dropna(subset=["ads_generator_key"], inplace=True) + ads_complete = ads_complete.dropna(subset=["ads_generator_key"]) ads_complete.ads_generator_key = ads_complete.ads_generator_key.astype(int) eia_ads_mapper.mapper_generatorkey = eia_ads_mapper.mapper_generatorkey.astype(int) @@ -577,8 +575,8 @@ def merge_ads_data(eia_data_operable): right_on=["mapper_plant_id_eia", "mapper_generator_id_ads"], how="left", ) - eia_ads_merged.drop(columns=eia_ads_mapper.columns, inplace=True) - eia_ads_merged.drop( + eia_ads_merged = eia_ads_merged.drop(columns=eia_ads_mapper.columns) + eia_ads_merged = eia_ads_merged.drop( columns=[ "ads_generator_name_alt", "ads_generator_key", @@ -599,7 +597,6 @@ def merge_ads_data(eia_data_operable): "ads_commission_date", "ads_servicestatus", ], - inplace=True, ) eia_ads_merged = eia_ads_merged.drop_duplicates( subset=["plant_id_eia", "generator_id"], @@ -774,8 +771,8 @@ def set_parameters(plants: pd.DataFrame): set_derates(plants) - plants[f"heat_rate_source"] = plants[f"heat_rate_source"].fillna("NA") - plants[f"fuel_cost_source"] = plants[f"fuel_cost_source"].fillna("NA") + plants["heat_rate_source"] = plants["heat_rate_source"].fillna("NA") + plants["fuel_cost_source"] = plants["fuel_cost_source"].fillna("NA") # Check for missing heat rate data if plants["heat_rate"].isna().sum() > 0: @@ -792,29 +789,25 @@ def set_parameters(plants: pd.DataFrame): def filter_outliers_iqr_grouped(df, group_column, value_column): - """ - Filter outliers using IQR for each generator group. - """ + """Filter outliers using IQR for each generator group.""" def filter_outliers(group): - Q1 = group[value_column].quantile(0.25) - Q3 = group[value_column].quantile(0.75) - IQR = Q3 - Q1 - lower_bound = Q1 - 1.5 * IQR - upper_bound = Q3 + 1.5 * IQR + q1 = group[value_column].quantile(0.25) + q3 = group[value_column].quantile(0.75) + iqr = q3 - q1 + lower_bound = q1 - 1.5 * iqr + upper_bound = q3 + 1.5 * iqr return group[(group[value_column] >= lower_bound) & (group[value_column] <= upper_bound)] return df.groupby(group_column)[df.columns].apply(filter_outliers).reset_index(drop=True) def filter_outliers_zscore(temporal_data, target_field_name): - """ - Filter outliers using Z-score. - """ + """Filter outliers using Z-score.""" # Calculate mean and standard deviation for each generator stats = temporal_data.groupby(["generator_name"])[target_field_name].agg(["mean", "std"]).reset_index() stats["mean"] = stats["mean"].replace(np.inf, np.nan) - stats.dropna(inplace=True) + stats = stats.dropna() # Merge mean and std back to the original dataframe temporal_stats = temporal_data.merge( @@ -863,7 +856,7 @@ def merge_fc_hr_data( ) if target_field_name in plants.columns: - plants.drop(columns=[target_field_name], inplace=True) + plants = plants.drop(columns=[target_field_name]) temporal_average[f"{target_field_name}_source"] = "pudl_reciepts" @@ -896,7 +889,7 @@ def apply_cems_heat_rates(plants, crosswalk_fn, cems_fn): how="right", ) - plants.rename(columns={"Heat Input (mmBtu/MWh)": "heat_rate_"}, inplace=True) + plants = plants.rename(columns={"Heat Input (mmBtu/MWh)": "heat_rate_"}) plants.heat_rate_ = plants.heat_rate_.fillna( plants.unit_heat_rate_mmbtu_per_mwh, ) # First take CEMS, then use PUDL @@ -907,7 +900,7 @@ def apply_cems_heat_rates(plants, crosswalk_fn, cems_fn): ) plants.unit_heat_rate_mmbtu_per_mwh_source = plants.pop("hr_source_cems") - plants.drop( + plants = plants.drop( columns=[ "Facility ID", "Unit ID", @@ -916,7 +909,6 @@ def apply_cems_heat_rates(plants, crosswalk_fn, cems_fn): "EIA_PLANT_ID", "EIA_GENERATOR_ID", ], - inplace=True, ) return plants diff --git a/workflow/scripts/build_renewable_profiles.py b/workflow/scripts/build_renewable_profiles.py index ba2f9cc50..befe352cc 100644 --- a/workflow/scripts/build_renewable_profiles.py +++ b/workflow/scripts/build_renewable_profiles.py @@ -1,160 +1,9 @@ # BY PyPSA-USA Authors """ -Calculates for each network node the (i) installable capacity (based on land- -use), (ii) the available generation time series (based on weather data), and -(iii) the average distance from the node for onshore wind, AC-connected -offshore wind, DC-connected offshore wind and solar PV generators. In addition -for offshore wind it calculates the fraction of the grid connection which is -under water. - -.. note:: Hydroelectric profiles are built in script :mod:`build_hydro_profiles`. - -**Relevant settings** - -.. code:: yaml - - snapshots: - - atlite: - nprocesses: - - renewable: - {technology}: - cutout: - corine: - grid_codes: - distance: - natura: - min_depth: - max_depth: - max_shore_distance: - min_shore_distance: - capacity_per_sqkm: - correction_factor: - potential: - min_p_max_pu: - clip_p_max_pu: - resource: - -.. seealso:: - Documentation of the configuration file ``config/config.yaml`` at - :ref:`snapshots_cf`, :ref:`atlite_cf`, :ref:`renewable_cf` - -**Inputs** - -- ``data/bundle/corine/g250_clc06_V18_5.tif``: `CORINE Land Cover (CLC) `_ inventory on `44 classes `_ of land use (e.g. forests, arable land, industrial, urban areas). - - # .. image:: img/corine.png - # :scale: 33 % - -- ``data/bundle/GEBCO_2014_2D.nc``: A `bathymetric `_ data set with a global terrain model for ocean and land at 15 arc-second intervals by the `General Bathymetric Chart of the Oceans (GEBCO) `_. - - # .. image:: img/gebco_2019_grid_image.jpg - # :scale: 50 % - - **Source:** `GEBCO `_ - -- ``resources/natura.tiff``: confer :ref:`databundle` -- ``resources/offshore_shapes.geojson``: confer :ref:`shapes` -- ``resources/regions_onshore.geojson``: (if not offshore wind), confer :ref:`busregions` -- ``resources/regions_offshore.geojson``: (if offshore wind), :ref:`busregions` -- ``"cutouts/" + params["renewable"][{technology}]['cutout']``: :ref:`cutout` -- ``networks/base.nc``: :ref:`base` - -**Outputs** - -- `resources/profile_{technology}.nc` with the following structure - - =================== ========== ========================================================= - Field Dimensions Description - =================== ========== ========================================================= - profile bus, time the per unit hourly availability factors for each node - ------------------- ---------- --------------------------------------------------------- - weight bus sum of the layout weighting for each node - ------------------- ---------- --------------------------------------------------------- - p_nom_max bus maximal installable capacity at the node (in MW) - ------------------- ---------- --------------------------------------------------------- - potential y, x layout of generator units at cutout grid cells inside the - Voronoi cell (maximal installable capacity at each grid - cell multiplied by capacity factor) - ------------------- ---------- --------------------------------------------------------- - average_distance bus average distance of units in the Voronoi cell to the - grid node (in km) - ------------------- ---------- --------------------------------------------------------- - underwater_fraction bus fraction of the average connection distance which is - under water (only for offshore) - =================== ========== ========================================================= - - - **profile** - - # .. image:: img/profile_ts.png - # :scale: 33 % - # :align: center - - - **p_nom_max** - - # .. image:: img/p_nom_max_hist.png - # :scale: 33 % - # :align: center - - - **potential** - - # .. image:: img/potential_heatmap.png - # :scale: 33 % - # :align: center - - - **average_distance** - - # .. image:: img/distance_hist.png - # :scale: 33 % - # :align: center - - - **underwater_fraction** - - # .. image:: img/underwater_hist.png - # :scale: 33 % - # :align: center - -Description ------------ - -This script functions at two main spatial resolutions: the resolution of the -network nodes and their `Voronoi cells -`_, and the resolution of the -cutout grid cells for the weather data. Typically the weather data grid is -finer than the network nodes, so we have to work out the distribution of -generators across the grid cells within each Voronoi cell. This is done by -taking account of a combination of the available land at each grid cell and the -capacity factor there. - -First the script computes how much of the technology can be installed at each -cutout grid cell and each node using the `GLAES -`_ library. This uses the CORINE land use data, -Natura2000 nature reserves and GEBCO bathymetry data. - -To compute the layout of generators in each node's Voronoi cell, the -installable potential in each grid cell is multiplied with the capacity factor -at each grid cell. This is done since we assume more generators are installed -at cells with a higher capacity factor. - - -This layout is then used to compute the generation availability time series -from the weather data cutout from `atlite`. - -Two methods are available to compute the maximal installable potential for the -node (`p_nom_max`): `simple` and `conservative`: - -- `simple` adds up the installable potentials of the individual grid cells. - If the model comes close to this limit, then the time series may slightly - overestimate production since it is assumed the geographical distribution is - proportional to capacity factor. - -- `conservative` assertains the nodal limit by increasing capacities - proportional to the layout until the limit of an individual grid cell is - reached. +Calculates for each network substation the installable capacity (based on land- +use) and the available generation time series (based on weather data). """ - import functools import logging import time @@ -163,7 +12,6 @@ import geopandas as gpd import matplotlib.pyplot as plt import numpy as np -import pandas as pd import xarray as xr from _helpers import configure_logging, get_snapshots from dask.distributed import Client @@ -230,10 +78,9 @@ def plot_data(data): regions = gpd.read_file(snakemake.input.regions) - assert "x" in regions.columns and "y" in regions.columns, ( - f"List of regions in {snakemake.input.regions} is empty, please " - "disable the corresponding renewable technology" - ) + assert ( + "x" in regions.columns and "y" in regions.columns + ), f"List of regions in {snakemake.input.regions} is empty, please disable the corresponding renewable technology" # do not pull up, set_index does not work if geo dataframe is empty regions = regions.set_index("name").rename_axis("bus") buses = regions.index @@ -276,7 +123,7 @@ def plot_data(data): if params.get("boem_screen", 0): excluder.add_raster( - snakemake.input[f"boem_osw"], + snakemake.input["boem_osw"], invert=True, nodata=0, allow_no_overlap=True, @@ -363,7 +210,7 @@ def plot_data(data): p_nom_max = capacities / max_cap_factor else: raise AssertionError( - 'Config key `potential` should be one of "simple" ' f'(default) or "conservative", not "{p_nom_max_meth}"', + f'Config key `potential` should be one of "simple" (default) or "conservative", not "{p_nom_max_meth}"', ) logger.info("Calculate average distances.") diff --git a/workflow/scripts/build_sector_costs.py b/workflow/scripts/build_sector_costs.py index 4b1503c4e..8ec21edf6 100644 --- a/workflow/scripts/build_sector_costs.py +++ b/workflow/scripts/build_sector_costs.py @@ -1,3 +1,5 @@ +# ruff: noqa: RUF012, D101, D102 + """ Builds costs data from EFS studies. @@ -6,10 +8,8 @@ """ from abc import ABC, abstractmethod -from typing import Optional import pandas as pd -import xarray as xr from constants import TBTU_2_MWH, MMBTU_MWHthemal # Vehicle life assumptions for getting $/VMT capital cost @@ -76,9 +76,7 @@ def assign_vehicle_type(name: str) -> str: - """ - Must match class level VMT assumptions. - """ + """Must match class level VMT assumptions.""" if name.startswith("Light Duty Cars"): return "light_duty_cars" elif name.startswith("Light Duty Trucks"): @@ -139,9 +137,7 @@ def initialize(self, sector: str): # returns EfsSectorData raise NotImplementedError def get_data(self, sector: str) -> pd.DataFrame: - """ - Collects all data for the sector. - """ + """Collects all data for the sector.""" return pd.concat( [ self.get_capex(sector), @@ -165,9 +161,7 @@ def get_fixed_costs(self, sector: str) -> pd.DataFrame: class EfsSectorData(ABC): - """ - Class to collect EFS building and electric transportation data. - """ + """Class to collect EFS building and electric transportation data.""" columns = COLUMN_ORDER @@ -213,8 +207,8 @@ def _format_columns(df: pd.DataFrame) -> pd.DataFrame: def _format_data_structure( self, df: pd.DataFrame, - source: Optional[str] = "", - description: Optional[str] = "", + source: str | None = "", + description: str | None = "", ) -> pd.DataFrame: data = df.copy() data["technology"] = data.Subsector + " " + data.Technology @@ -223,9 +217,7 @@ def _format_data_structure( return self._format_columns(data)[self.columns] def expand_data(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Performs lienar interpolations to fill in values for all years. - """ + """Performs lienar interpolations to fill in values for all years.""" data = df.copy() years = range(data.year.min(), data.year.max() + 1) ds = df.set_index( @@ -242,9 +234,7 @@ def expand_data(self, df: pd.DataFrame) -> pd.DataFrame: class EfsBevTransportationData(EfsSectorData): - """ - Only contains BEV and PHBEV. - """ + """Only contains BEV and PHBEV.""" # Assumptions from https://www.nrel.gov/docs/fy18osti/70485.pdf wh_per_gallon = 33700 # footnote 24 @@ -283,9 +273,7 @@ def get_lifetime(self): return self.expand_data(df) def get_efficiency(self): - """ - Only pulls main efficiency. - """ + """Only pulls main efficiency.""" df = self.data.copy() df = df[ (df.Sector == "Transportation") & (df["EFS Case"] == self.efs_case) & (df.Metric == "Main Efficiency") @@ -311,9 +299,7 @@ def get_fixed_costs(self): return df def _correct_capex_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Coverts per unit into per VMT. - """ + """Coverts per unit into per VMT.""" corrected = df.copy() corrected["miles"] = corrected.technology.map(assign_vehicle_type) corrected["miles"] = corrected.miles.map(self.lifetime_miles) @@ -322,18 +308,14 @@ def _correct_capex_units(self, df: pd.DataFrame) -> pd.DataFrame: return corrected.drop(columns=["miles"]) def _correct_efficiency_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Coverts MPGe into per Miles/MWh. - """ + """Coverts MPGe into per Miles/MWh.""" corrected = df.copy() corrected["value"] = corrected.value.mul(1 / self.wh_per_gallon).mul(1e6) corrected["unit"] = "miles/MWh" return corrected def _calculate_fom(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Converts $/mile costs to %/year. - """ + """Converts $/mile costs to %/year.""" corrected = df.copy() corrected["value"] = corrected.technology.map(assign_vehicle_type) corrected["value"] = corrected.value.map(self.fixed_cost) @@ -464,9 +446,7 @@ def get_fixed_costs(self): return df[self.columns] def _correct_capex_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Coverts per unit into per VMT. - """ + """Coverts per unit into per VMT.""" corrected = df.copy() corrected["miles"] = corrected.technology.map(assign_vehicle_type) corrected["miles"] = corrected.miles.map(self.lifetime_miles) @@ -475,9 +455,7 @@ def _correct_capex_units(self, df: pd.DataFrame) -> pd.DataFrame: return corrected.drop(columns=["miles"]) def _correct_fom_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Converts $/mile costs to %/year. - """ + """Converts $/mile costs to %/year.""" corrected = df.copy() corrected["fixed"] = corrected.technology.map(assign_vehicle_type) corrected["fixed"] = corrected.fixed.map(self.fixed_cost) @@ -486,9 +464,7 @@ def _correct_fom_units(self, df: pd.DataFrame) -> pd.DataFrame: return corrected.drop(columns=["capex", "fixed"]) def _correct_efficiency_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Coverts MPGe into per Miles/MWh. - """ + """Coverts MPGe into per Miles/MWh.""" corrected = df.copy() corrected["value"] = corrected.value.mul(1 / self.wh_per_gallon).mul(1e6) corrected["unit"] = "miles/MWh" @@ -496,7 +472,6 @@ def _correct_efficiency_units(self, df: pd.DataFrame) -> pd.DataFrame: class EfsBuildingData(EfsSectorData): - mmbtu_2_mwh = MMBTU_MWHthemal # Assumptions from https://atb.nrel.gov/transportation/2022/definitions @@ -563,9 +538,7 @@ def get_fixed_costs(self): return self._correct_fom_units(df) def assign_tech_types(self, name: str) -> float: - """ - Must match class level VMT assumptions. - """ + """Must match class level VMT assumptions.""" if name.endswith("Air Source Heat Pump"): return "ashp" elif name.endswith("Heat Pump Water Heater"): @@ -575,18 +548,14 @@ def assign_tech_types(self, name: str) -> float: return 0 def _correct_capex_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Coverts $/kBtu to $/MWh. - """ + """Coverts $/kBtu to $/MWh.""" corrected = df.copy() corrected["value"] = corrected.value.mul(1000).mul(self.mmbtu_2_mwh) corrected["unit"] = "$/Mwh" return corrected def _correct_fom_units(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Converts $/kBtu costs to %/year. - """ + """Converts $/kBtu costs to %/year.""" corrected = df.copy() corrected["tech_type"] = corrected.technology.map(self.assign_tech_types) corrected["fom"] = corrected.tech_type.map(self.fixed_cost) # $ /kBTU @@ -654,7 +623,7 @@ def _correct_investment_units(df: pd.DataFrame) -> pd.DataFrame: df2.unit = df2.unit.str.replace("/kBtu/hr", "/MW") return df2 - def get_data(self, sector: Optional[str] = None) -> pd.DataFrame: + def get_data(self, sector: str | None = None) -> pd.DataFrame: sector = self._check_sector(sector) return pd.concat( [ @@ -665,7 +634,7 @@ def get_data(self, sector: Optional[str] = None) -> pd.DataFrame: ], ) - def get_capex(self, sector: Optional[str] = None): + def get_capex(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "investment") @@ -674,7 +643,7 @@ def get_capex(self, sector: Optional[str] = None): df = self.data[slicer] return self._correct_investment_units(df)[self.columns] - def get_lifetime(self, sector: Optional[str] = None): + def get_lifetime(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "lifetime") @@ -682,7 +651,7 @@ def get_lifetime(self, sector: Optional[str] = None): slicer = self.data.parameter == "lifetime" return self.data[slicer][self.columns] - def get_efficiency(self, sector: Optional[str] = None): + def get_efficiency(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "efficiency") @@ -690,7 +659,7 @@ def get_efficiency(self, sector: Optional[str] = None): slicer = self.data.parameter == "efficiency" return self.data[slicer][self.columns] - def get_fixed_costs(self, sector: Optional[str] = None): + def get_fixed_costs(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "FOM") diff --git a/workflow/scripts/build_shapes.py b/workflow/scripts/build_shapes.py index 03dccf589..d74702169 100644 --- a/workflow/scripts/build_shapes.py +++ b/workflow/scripts/build_shapes.py @@ -1,6 +1,6 @@ # BY PyPSA-USA Authors """ -**Description** +**Description**. The `build_shapes` rule builds the GIS shape files for the balancing authorities and offshore regions. The regions are only built for the {interconnect} wildcard. Because balancing authorities often overlap- we modify the GIS dataset developed by [Breakthrough Energy Sciences](https://breakthrough-energy.github.io/docs/). @@ -39,17 +39,13 @@ # :scale: 33 % """ - import logging -from typing import List -import cartopy.crs as ccrs import cartopy.io.shapereader as shpreader import geopandas as gpd -import matplotlib.pyplot as plt import pandas as pd from _helpers import configure_logging, mock_snakemake -from constants import * +from constants import GPS_CRS, MEASUREMENT_CRS from shapely.geometry import MultiPolygon @@ -61,11 +57,13 @@ def filter_small_polygons_gpd( Filters out polygons within each MultiPolygon in a GeoSeries that are smaller than a specified area. - Parameters: + Parameters + ---------- geo_series (gpd.GeoSeries): A GeoSeries containing MultiPolygon geometries. min_area (float): The minimum area threshold. - Returns: + Returns + ------- gpd.GeoSeries: A GeoSeries with MultiPolygons filtered based on the area criterion. """ # Explode the MultiPolygons into individual Polygons @@ -88,9 +86,7 @@ def load_na_shapes( state_shape: str = "admin_1_states_provinces_lakes", countries: list = ["US"], ) -> gpd.GeoDataFrame: - """ - Creates geodataframe of north america. - """ + """Creates geodataframe of north america.""" shpfilename = shpreader.natural_earth( resolution="10m", category="cultural", @@ -122,12 +118,9 @@ def filter_shapes( data: gpd.GeoDataFrame, zones: pd.DataFrame, interconnect: str = "western", - add_regions: list = None, + add_regions: list | None = None, ) -> gpd.GeoDataFrame: - """ - Filters breakthrough energy zone data by interconnect region. - """ - + """Filters breakthrough energy zone data by interconnect region.""" if interconnect not in ("western", "texas", "eastern", "usa"): logger.warning(f"Interconnector of {interconnect} is not valid") @@ -140,9 +133,7 @@ def filter_shapes( def load_ba_shape(ba_file: str) -> gpd.GeoDataFrame: - """ - Loads balancing authority into a geodataframe. - """ + """Loads balancing authority into a geodataframe.""" gdf = gpd.read_file(ba_file) gdf = gdf.rename(columns={"BA": "name"}) return gdf.to_crs(4326) @@ -165,9 +156,7 @@ def combine_offshore_shapes( interconnect: gpd.GeoDataFrame, buffer, ) -> gpd.GeoDataFrame: - """ - Conbines offshore shapes. - """ + """Conbines offshore shapes.""" if source == "ca_osw": offshore = _dissolve_boem(shape) elif source == "eez": @@ -179,11 +168,9 @@ def combine_offshore_shapes( def _dissolve_boem(shape: gpd.GeoDataFrame): - """ - Dissolves offshore shapes from boem. - """ + """Dissolves offshore shapes from boem.""" shape_split = shape.dissolve().explode(index_parts=False) - shape_split.rename(columns={"Lease_Name": "name"}, inplace=True) + shape_split = shape_split.rename(columns={"Lease_Name": "name"}) shape_split.name = ["Morro_Bay", "Humboldt"] return shape_split @@ -213,9 +200,7 @@ def trim_states_to_interconnect( gdf_nerc: gpd.GeoDataFrame, interconnect: str, ): - """ - Trims states to only include portions of states in NERC Interconnect. - """ + """Trims states to only include portions of states in NERC Interconnect.""" if interconnect == "western": gdf_nerc_f = gdf_nerc[gdf_nerc.OBJECTID.isin([3, 8, 9])] gdf_states = gpd.overlay( @@ -240,7 +225,7 @@ def trim_shape_to_interconnect( gdf: gpd.GeoDataFrame, interconnect_regions: gpd.GeoDataFrame, interconnect: str, - exclusion_dict: dict = None, + exclusion_dict: dict | None = None, ): """ Trim Shapes to only portions inside NERC/State Region to ensure renewables @@ -421,8 +406,6 @@ def main(snakemake): if __name__ == "__main__": logger = logging.getLogger(__name__) if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake("build_shapes", interconnect="eastern") configure_logging(snakemake) main(snakemake) diff --git a/workflow/scripts/build_stock_data.py b/workflow/scripts/build_stock_data.py index 42806c393..54c6bdb27 100644 --- a/workflow/scripts/build_stock_data.py +++ b/workflow/scripts/build_stock_data.py @@ -1,28 +1,24 @@ -""" -Builds End-Use initial stock data. -""" +# ruff: noqa: D100, D101, RUF012 +"""Builds End-Use initial stock data.""" # to supress warning in water heat xlsx # UserWarning: Print area cannot be set to Defined name: data!$A:$J -import warnings - -warnings.simplefilter("ignore") - import logging +import warnings from pathlib import Path -from typing import Optional import numpy as np import pandas as pd import pypsa -from build_heat import combined_heat -from constants import STATE_2_CODE, STATES_CENSUS_DIVISION_MAPPER, STATES_CENSUS_MAPPER +from constants import CODE_2_STATE, STATES_CENSUS_DIVISION_MAPPER, STATES_CENSUS_MAPPER from constants_sector import RoadTransport, SecCarriers, SecNames, Transport from eia import TransportationFuelUse +warnings.simplefilter("ignore") + + logger = logging.getLogger(__name__) -CODE_2_STATE = {v: k for k, v in STATE_2_CODE.items()} """ Hardcoded build years based on building year constructed starting from 2000 https://www.eia.gov/consumption/commercial/data/2018/bc/pdf/b6.pdf @@ -144,9 +140,7 @@ def _valid_name(s: str): ] def _read(self, stock: str) -> pd.DataFrame: - """ - Reads in the data. - """ + """Reads in the data.""" f = Path(self.dir, f"{self.file_mapper[stock]}.xlsx") df = ( @@ -168,9 +162,7 @@ def _read(self, stock: str) -> pd.DataFrame: return df def _get_data(self, stock: str, fillna: bool = False) -> pd.DataFrame: - """ - Formats data. - """ + """Formats data.""" self._valid_name(stock) df = self._read(stock) if fillna: @@ -179,23 +171,17 @@ def _get_data(self, stock: str, fillna: bool = False) -> pd.DataFrame: return df def get_percentage(self, stock: str) -> pd.DataFrame: - """ - Gets percentage of stock per state. - """ + """Gets percentage of stock per state.""" df = self._get_data(stock, fillna=True) return df[[x for x in df.columns if x.endswith("percent")]] def get_absolute(self, stock: str) -> pd.DataFrame: - """ - Gets raw stock values per state. - """ + """Gets raw stock values per state.""" df = self._get_data(stock, fillna=False) return df[[x for x in df.columns if x.endswith("stock")]] def _fill_missing(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Fills missing values with USA average. - """ + """Fills missing values with USA average.""" columns = df.columns for col in columns: df[col] = df[col].fillna(df.at["USA", col]) @@ -255,16 +241,12 @@ def _valid_name(s: str): assert s in ["aircon_fuel", "space_heat_fuel", "water_heat_fuel"] def _read(self, fuel: str) -> pd.DataFrame: - """ - Reads in the data. - """ - + """Reads in the data.""" skip_rows = self._get_skip_rows(fuel) dfs = [] for f_name in ("c7", "c8", "c9"): - f = Path(self.dir, f"{f_name}.xlsx") df = ( @@ -291,10 +273,7 @@ def _read(self, fuel: str) -> pd.DataFrame: @staticmethod def _get_skip_rows(fuel: str) -> list[int]: - """ - Gets rows to skip when reading in data files. - """ - + """Gets rows to skip when reading in data files.""" keep_rows = [3, 4] match fuel: @@ -317,9 +296,7 @@ def _get_data( fillna: bool = False, by_state: bool = True, ) -> pd.DataFrame: - """ - Formats data. - """ + """Formats data.""" self._valid_name(fuel) data = self._read(fuel) @@ -341,10 +318,7 @@ def _get_data( return df def _expand_by_state(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Maps census division to state. - """ - + """Maps census division to state.""" states = pd.DataFrame(index=df.index) for state, census_division in self.state_2_census_division.items(): try: @@ -354,9 +328,7 @@ def _expand_by_state(self, df: pd.DataFrame) -> pd.DataFrame: return states def _fill_missing(self, df: pd.DataFrame, fuel: str) -> pd.DataFrame: - """ - Fills missing values with USA average. - """ + """Fills missing values with USA average.""" match fuel: case "space_heat_fuel": fill_values = self.usa_avg_space_heating @@ -377,15 +349,11 @@ def _fill_missing(self, df: pd.DataFrame, fuel: str) -> pd.DataFrame: """ def get_percentage(self, fuel: str, by_state: bool = True) -> pd.DataFrame: - """ - Gets percentage of stock at a national level. - """ + """Gets percentage of stock at a national level.""" return self._get_data(fuel, as_percent=True, by_state=by_state, fillna=True).mul(100).round(2) def get_absolute(self, fuel: str, by_state: bool = True) -> pd.DataFrame: - """ - Gets raw stock values at national level. - """ + """Gets raw stock values at national level.""" return self._get_data(fuel, as_percent=False, by_state=by_state, fillna=False) @@ -397,7 +365,6 @@ def _already_retired(build_year: int, lifetime: int, year: int) -> bool: instead of '<' to follow pypsa convention. See folling link https://pypsa.readthedocs.io/en/latest/examples/multi-investment-optimisation.html#Multi-Investment-Optimization """ - if (build_year + lifetime) <= year: return True else: @@ -407,7 +374,7 @@ def _already_retired(build_year: int, lifetime: int, year: int) -> bool: def _get_marginal_cost( n: pypsa.Network, names: list[str], - fuel: Optional[str] = None, + fuel: str | None = None, ) -> float | pd.DataFrame: """ Gets marginal cost from the investable link. @@ -416,7 +383,6 @@ def _get_marginal_cost( Else, returns the static cost associated with the first name in the list """ - df = pd.DataFrame(index=n.links_t.marginal_cost.index) try: @@ -443,7 +409,6 @@ def get_residential_stock(root_dir: str, load: str) -> pd.DataFrame: Pass folder of data from the residential energy consumption survey """ - recs = Recs(root_dir) match load: @@ -468,10 +433,7 @@ def get_residential_stock(root_dir: str, load: str) -> pd.DataFrame: def get_commercial_stock(root_dir: Path | str, fuel: str) -> pd.DataFrame: - """ - Gets commercial fuel values as a percetange. - """ - + """Gets commercial fuel values as a percetange.""" cecs = Cecs(root_dir) match fuel: @@ -499,7 +461,6 @@ def get_transport_stock(api: str, year: int) -> pd.DataFrame: """ def _get_data(api: str, year: int) -> pd.DataFrame: - dfs = [] for vehicle in ("light_duty", "med_duty", "heavy_duty", "bus"): @@ -527,9 +488,7 @@ def _get_data(api: str, year: int) -> pd.DataFrame: return pd.concat(dfs, axis=1).fillna(0) def get_percentage(api: str, year: int) -> pd.DataFrame: - """ - Gets percentage of stock at a national level. - """ + """Gets percentage of stock at a national level.""" df = get_absolute(api, year) for col in df.columns: @@ -540,9 +499,7 @@ def get_percentage(api: str, year: int) -> pd.DataFrame: return df.mul(100).round(2) def get_absolute(api: str, year: int) -> pd.DataFrame: - """ - Gets raw stock values at national level. - """ + """Gets raw stock values at national level.""" return _get_data(api, year).round(2) df = get_percentage(api, year).T @@ -567,7 +524,6 @@ def get_absolute(api: str, year: int) -> pd.DataFrame: def get_industrial_stock(xlsx: str) -> pd.DataFrame: - def _get_census_to_state(data: dict[str, str]) -> dict[str, list[str]]: mapper = {} for state, census in data.items(): @@ -581,7 +537,6 @@ def _get_census_to_state(data: dict[str, str]) -> dict[str, list[str]]: return mapper def _get_data(xlsx: str) -> pd.DataFrame: - cols_renamed = { "Code(a)": "NAICS", "Electricity(a)": "electricity", @@ -601,7 +556,6 @@ def _get_data(xlsx: str) -> pd.DataFrame: ) def _format_raw_data(df: pd.DataFrame) -> pd.DataFrame: - slicer = [ "TOTAL FUEL CONSUMPTION", "Indirect Uses-Boiler Fuel", @@ -685,7 +639,7 @@ def _get_brownfield_template_df( n: pypsa.Network, fuel: str, sector: str, - subsector: Optional[str] = None, + subsector: str | None = None, ) -> None: """ Gets a dataframe in the following form. @@ -697,7 +651,6 @@ def _get_brownfield_template_df( | 2 | p610 0 com-urban-heat | p610 0 | com-urban-heat | TX | 1999.486 | | ... | ... | ... | ... | ... | ... | """ - assert fuel in [x.value for x in SecCarriers] if subsector: @@ -720,7 +673,7 @@ def _get_brownfield_template_df( def _get_endogenous_transport_brownfield_template_df( n: pypsa.Network, fuel: str, - veh_mode: Optional[str] = None, + veh_mode: str | None = None, ) -> pd.DataFrame: """ Gets a dataframe in the following form. @@ -732,7 +685,6 @@ def _get_endogenous_transport_brownfield_template_df( | 2 | p610 0 trn-veh-med | p610 0 | trn-veh-med | TX | 1999.486 | | ... | ... | ... | ... | ... | ... | """ - sector = SecNames.TRANSPORT.value subsector = Transport.ROAD.value if veh_mode: @@ -766,9 +718,7 @@ def add_road_transport_brownfield( costs: pd.DataFrame, exogenous_transport: bool, ) -> None: - """ - Adds existing stock to transportation sector. - """ + """Adds existing stock to transportation sector.""" def add_brownfield_ev( n: pypsa.Network, @@ -777,7 +727,6 @@ def add_brownfield_ev( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - match vehicle_mode: case RoadTransport.LIGHT.value: costs_name = "Light Duty Cars BEV 300" @@ -818,7 +767,6 @@ def add_brownfield_ev( start_year = start_year if start_year >= 2023 else 2023 for period in range(1, periods + 1): - build_year = start_year - period * step percent = step / lifetime # given as a ratio @@ -852,7 +800,6 @@ def add_brownfield_lpg( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - # existing stock efficiencies taken from 2016 EFS Technology data # This is consistent with where future efficiencies are taken from # https://data.nrel.gov/submissions/93 @@ -914,7 +861,6 @@ def add_brownfield_lpg( # start_year = start_year if start_year >= 2023 else 2023 for period in range(1, periods + 1): - build_year = start_year - period * step percent = step / lifetime # given as a ratio @@ -933,7 +879,7 @@ def add_brownfield_lpg( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = marginal_cost - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", @@ -959,7 +905,6 @@ def add_brownfield_lpg( lpg_fuel = SecCarriers.LPG.value if exogenous_transport: - veh_name = f"{veh_type}-{vehicle_mode}" # ev brownfield @@ -983,14 +928,21 @@ def add_brownfield_lpg( add_brownfield_lpg(n, df, vehicle_mode, ratios, costs) else: - # elec brownfield - df = _get_endogenous_transport_brownfield_template_df(n, fuel=elec_fuel, veh_mode=vehicle_mode) + df = _get_endogenous_transport_brownfield_template_df( + n, + fuel=elec_fuel, + veh_mode=vehicle_mode, + ) df["p_nom"] = df.p_max.mul(growth_multiplier) add_brownfield_ev(n, df, vehicle_mode, ratios, costs) # lpg brownfield - df = _get_endogenous_transport_brownfield_template_df(n, fuel=lpg_fuel, veh_mode=vehicle_mode) + df = _get_endogenous_transport_brownfield_template_df( + n, + fuel=lpg_fuel, + veh_mode=vehicle_mode, + ) df["p_nom"] = df.p_max.mul(growth_multiplier) add_brownfield_lpg(n, df, vehicle_mode, ratios, costs) @@ -1002,11 +954,9 @@ def add_service_brownfield( growth_multiplier: float, ratios: pd.DataFrame, costs: pd.DataFrame, - simple_storage: Optional[bool] = None, # for water heating only + simple_storage: bool | None = None, # for water heating only ) -> None: - """ - Adds existing stock to res/com sector. - """ + """Adds existing stock to res/com sector.""" def add_brownfield_gas_furnace( n: pypsa.Network, @@ -1015,7 +965,6 @@ def add_brownfield_gas_furnace( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - df = template.copy() # existing efficiency values taken from: @@ -1050,7 +999,6 @@ def add_brownfield_gas_furnace( # start_year if start_year >= 2023 else 2023 for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1066,7 +1014,7 @@ def add_brownfield_gas_furnace( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = 0 - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", @@ -1092,7 +1040,6 @@ def add_brownfield_oil_furnace( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - df = template.copy() # existing efficiency values taken from: @@ -1127,7 +1074,6 @@ def add_brownfield_oil_furnace( # start_year = start_year if start_year >= 2023 else 2023 for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1143,7 +1089,7 @@ def add_brownfield_oil_furnace( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = marginal_cost - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", @@ -1169,7 +1115,6 @@ def add_brownfield_elec_furnace( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - df = template.copy() # existing efficiency values taken from: @@ -1198,7 +1143,6 @@ def add_brownfield_elec_furnace( # start_year = start_year if start_year >= 2023 else 2023 for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1228,9 +1172,7 @@ def add_brownfield_heat_pump( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - """ - Need to pull in existing COP profiles. - """ + """Need to pull in existing COP profiles.""" pass def add_brownfield_aircon( @@ -1266,7 +1208,6 @@ def add_brownfield_aircon( # start_year = start_year if start_year >= 2023 else 2023 for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1297,7 +1238,6 @@ def add_brownfield_water_heater_simple_storage( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - # existing efficiency values taken from: # https://www.eia.gov/analysis/studies/buildings/equipcosts/pdf/full.pdf @@ -1346,7 +1286,6 @@ def add_brownfield_water_heater_simple_storage( start_year = n.investment_periods[0] for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1362,7 +1301,7 @@ def add_brownfield_water_heater_simple_storage( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = 0 - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", @@ -1388,7 +1327,6 @@ def add_brownfield_water_heater( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - # existing efficiency values taken from: # https://www.eia.gov/analysis/studies/buildings/equipcosts/pdf/full.pdf @@ -1436,7 +1374,6 @@ def add_brownfield_water_heater( start_year = n.investment_periods[0] for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1534,9 +1471,7 @@ def add_industrial_brownfield( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - """ - Adds existing stock to industrial sector. - """ + """Adds existing stock to industrial sector.""" def add_brownfield_gas_furnace( n: pypsa.Network, @@ -1544,7 +1479,6 @@ def add_brownfield_gas_furnace( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - sector = SecNames.INDUSTRY.value df = template.copy() @@ -1570,7 +1504,6 @@ def add_brownfield_gas_furnace( start_year = n.investment_periods[0] for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1586,7 +1519,7 @@ def add_brownfield_gas_furnace( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = marginal_cost - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", @@ -1619,7 +1552,6 @@ def add_brownfield_coal_furnace( ratios: pd.DataFrame, costs: pd.DataFrame, ) -> None: - sector = SecNames.INDUSTRY.value df = template.copy() @@ -1646,7 +1578,6 @@ def add_brownfield_coal_furnace( start_year = n.investment_periods[0] for build_year, percent in installed_capacity.items(): - if _already_retired(build_year, lifetime, start_year): continue @@ -1662,7 +1593,7 @@ def add_brownfield_coal_furnace( mc = mc.rename(columns={v: k for k, v in name_mapper.items()}) else: mc = marginal_cost - assert isinstance(mc, (float, int)) + assert isinstance(mc, float | int) n.madd( "Link", diff --git a/workflow/scripts/build_temperature_profiles.py b/workflow/scripts/build_temperature_profiles.py index 3f85927af..67f5e88a6 100644 --- a/workflow/scripts/build_temperature_profiles.py +++ b/workflow/scripts/build_temperature_profiles.py @@ -1,6 +1,4 @@ -""" -Build time series for air and soil temperatures per clustered model region. -""" +"""Build time series for air and soil temperatures per clustered model region.""" import atlite import geopandas as gpd @@ -32,12 +30,12 @@ clustered_regions = gpd.read_file(snakemake.input.regions_onshore).set_index("name").buffer(0).squeeze() - I = cutout.indicatormatrix(clustered_regions) + indicator_matrix = cutout.indicatormatrix(clustered_regions) pop_layout = xr.open_dataarray(snakemake.input.pop_layout) stacked_pop = pop_layout.stack(spatial=("y", "x")) - M = I.T.dot(np.diag(I.dot(stacked_pop))) + M = indicator_matrix.T.dot(np.diag(indicator_matrix.dot(stacked_pop))) nonzero_sum = M.sum(axis=0, keepdims=True) nonzero_sum[nonzero_sum == 0.0] = 1.0 diff --git a/workflow/scripts/build_transportation.py b/workflow/scripts/build_transportation.py index e590cfb21..741efffd7 100644 --- a/workflow/scripts/build_transportation.py +++ b/workflow/scripts/build_transportation.py @@ -1,17 +1,12 @@ -""" -Module for building transportation infrastructure. -""" +"""Module for building transportation infrastructure.""" import logging -from typing import Any, Optional +from typing import Any import numpy as np import pandas as pd import pypsa from build_heat import _get_dynamic_marginal_costs, get_link_marginal_costs - -logger = logging.getLogger(__name__) - from constants_sector import ( AirTransport, BoatTransport, @@ -20,6 +15,8 @@ Transport, ) +logger = logging.getLogger(__name__) + def build_transportation( n: pypsa.Network, @@ -29,15 +26,12 @@ def build_transportation( rail: bool = True, boat: bool = True, dynamic_pricing: bool = False, - eia: Optional[str] = None, # for dynamic pricing - year: Optional[int] = None, # for dynamic pricing - must_run_evs: Optional[bool] = None, # for endogenous EV investment - dr_config: Optional[dict[str, Any]] = None, + eia: str | None = None, # for dynamic pricing + year: int | None = None, # for dynamic pricing + must_run_evs: bool | None = None, # for endogenous EV investment + dr_config: dict[str, Any] | None = None, ) -> None: - """ - Main funtion to interface with. - """ - + """Main funtion to interface with.""" road_suffix = Transport.ROAD.value for fuel in ("elec", "lpg"): @@ -96,10 +90,7 @@ def add_ev_infrastructure( n: pypsa.Network, vehicle: str, ) -> None: - """ - Adds bus that all EVs attach to at a node level. - """ - + """Adds bus that all EVs attach to at a node level.""" nodes = n.buses[n.buses.carrier == "AC"] n.madd( @@ -131,12 +122,9 @@ def add_ev_infrastructure( def add_lpg_infrastructure( n: pypsa.Network, vehicle: str, - costs: Optional[pd.DataFrame] = None, + costs: pd.DataFrame | None = None, ) -> None: - """ - Adds lpg connections for vehicle type. - """ - + """Adds lpg connections for vehicle type.""" nodes = n.buses[n.buses.carrier == "AC"] n.madd( @@ -177,8 +165,7 @@ def add_lpg_infrastructure( def add_transport_dr(n: pypsa.Network, vehicle: str, dr_config: dict[str, Any]) -> None: - """Attachs DR infrastructure at load location""" - + """Attachs DR infrastructure at load location.""" shift = dr_config.get("shift", 0) marginal_cost_storage = dr_config.get("marginal_cost", 0) @@ -320,7 +307,6 @@ def add_elec_vehicle( - Light Duty Trucks PHEV 50 - Medium Duty Trucks BEV """ - match mode: case RoadTransport.LIGHT.value: costs_name = "Light Duty Cars BEV 300" @@ -372,7 +358,7 @@ def add_lpg_vehicle( vehicle: str, mode: str, costs: pd.DataFrame, - marginal_cost: Optional[pd.DataFrame | float] = None, + marginal_cost: pd.DataFrame | float | None = None, ) -> None: """ Adds electric vehicle to the network. @@ -384,7 +370,6 @@ def add_lpg_vehicle( - Heavy Duty Trucks ICEV - Buses ICEV """ - match mode: case RoadTransport.LIGHT.value: costs_name = "Light Duty Cars ICEV" @@ -421,7 +406,7 @@ def add_lpg_vehicle( if isinstance(marginal_cost, pd.DataFrame): assert "state" in vehicles.columns mc = get_link_marginal_costs(n, vehicles, marginal_cost) - elif isinstance(marginal_cost, (int, float)): + elif isinstance(marginal_cost, int | float): mc = marginal_cost elif isinstance(marginal_cost, None): mc = 0 @@ -455,7 +440,6 @@ def add_air( NOTE: COSTS ARE CURRENTLY HARD CODED IN FROM 2030. Look at travel indicators here: - https://www.eia.gov/outlooks/aeo/data/browser/ """ - # Assumptions from https://www.nrel.gov/docs/fy18osti/70485.pdf wh_per_gallon = 33700 # footnote 24 @@ -468,7 +452,7 @@ def add_air( loads = n.loads[(n.loads.carrier.str.contains("trn-")) & (n.loads.carrier.str.contains(f"{vehicle}-{mode}"))] vehicles = pd.DataFrame(index=loads.bus) - vehicles.index = vehicles.index.map(lambda x: x.split(f" trn-")[0]) + vehicles.index = vehicles.index.map(lambda x: x.split(" trn-")[0]) vehicles["bus0"] = vehicles.index + f" trn-lpg-{vehicle}" vehicles["bus1"] = vehicles.index + f" trn-lpg-{vehicle}-{mode}" vehicles["carrier"] = f"trn-lpg-{vehicle}-{mode}" @@ -499,7 +483,6 @@ def add_boat( NOTE: COSTS ARE CURRENTLY HARD CODED IN FROM 2030. Look at travel indicators here: - https://www.eia.gov/outlooks/aeo/data/browser/ """ - # efficiency = costs.at[costs_name, "efficiency"] / 1000 # base efficiency is 5 ton miles per thousand Btu # 1 kBTU / 0.000293 MWh @@ -510,7 +493,7 @@ def add_boat( loads = n.loads[(n.loads.carrier.str.contains("trn-")) & (n.loads.carrier.str.contains(f"{vehicle}-{mode}"))] vehicles = pd.DataFrame(index=loads.bus) - vehicles.index = vehicles.index.map(lambda x: x.split(f" trn-")[0]) + vehicles.index = vehicles.index.map(lambda x: x.split(" trn-")[0]) vehicles["bus0"] = vehicles.index + f" trn-lpg-{vehicle}" vehicles["bus1"] = vehicles.index + f" trn-lpg-{vehicle}-{mode}" vehicles["carrier"] = f"trn-lpg-{vehicle}-{mode}" @@ -541,7 +524,6 @@ def add_rail( NOTE: COSTS ARE CURRENTLY HARD CODED IN FROM 2030. Look at travel indicators here: - https://www.eia.gov/outlooks/aeo/data/browser/ """ - match mode: case RailTransport.SHIPPING.value: # efficiency = costs.at[costs_name, "efficiency"] / 1000 @@ -566,7 +548,7 @@ def add_rail( loads = n.loads[(n.loads.carrier.str.contains("trn-")) & (n.loads.carrier.str.contains(f"{vehicle}-{mode}"))] vehicles = pd.DataFrame(index=loads.bus) - vehicles.index = vehicles.index.map(lambda x: x.split(f" trn-")[0]) + vehicles.index = vehicles.index.map(lambda x: x.split(" trn-")[0]) vehicles["bus0"] = vehicles.index + f" trn-lpg-{vehicle}" vehicles["bus1"] = vehicles.index + f" trn-lpg-{vehicle}-{mode}" vehicles["carrier"] = f"trn-lpg-{vehicle}-{mode}" @@ -586,8 +568,7 @@ def add_rail( def _create_endogenous_buses(n: pypsa.Network) -> None: - """Creats new bus for grouped endogenous vehicle load""" - + """Creats new bus for grouped endogenous vehicle load.""" buses = n.buses[ n.buses.carrier.str.startswith("trn") & n.buses.carrier.str.contains("veh") @@ -619,12 +600,11 @@ def _create_endogenous_buses(n: pypsa.Network) -> None: def _create_endogenous_loads(n: pypsa.Network) -> None: - """Creates aggregated vehicle load + """Creates aggregated vehicle load. - Removes LPG load - Transfers EV load to central bus """ - loads = n.loads[n.loads.carrier.str.startswith("trn") & n.loads.carrier.str.contains("veh")] to_remove = [x for x in loads.index if "-lpg-" in x] to_shift = [x for x in loads.index if "-elec-" in x] @@ -645,28 +625,34 @@ def _create_endogenous_loads(n: pypsa.Network) -> None: # transfer elec load buses to general bus n.loads.loc[new_names, "bus"] = n.loads.loc[new_names, "bus"].map(new_name_mapper) - n.loads.loc[new_names, "carrier"] = n.loads.loc[new_names, "carrier"].map(lambda x: x.replace("trn-elec-", "trn-")) - n.loads.loc[new_names, "carrier"] = n.loads.loc[new_names, "carrier"].map(lambda x: x.replace("trn-lpg-", "trn-")) + n.loads.loc[new_names, "carrier"] = n.loads.loc[new_names, "carrier"].map( + lambda x: x.replace("trn-elec-", "trn-"), + ) + n.loads.loc[new_names, "carrier"] = n.loads.loc[new_names, "carrier"].map( + lambda x: x.replace("trn-lpg-", "trn-"), + ) def _create_endogenous_links(n: pypsa.Network) -> None: - """Creates links for LPG and EV to load bus + """Creates links for LPG and EV to load bus. Just involves transfering bus1 from exogenous load bus to endogenous load bus """ - slicer = ( n.links.carrier.str.startswith("trn") & n.links.carrier.str.contains("veh") & ~n.links.carrier.str.endswith("veh") ) - n.links.loc[slicer, "bus1"] = n.links.loc[slicer, "bus1"].map(lambda x: x.replace("trn-elec-", "trn-")) - n.links.loc[slicer, "bus1"] = n.links.loc[slicer, "bus1"].map(lambda x: x.replace("trn-lpg-", "trn-")) + n.links.loc[slicer, "bus1"] = n.links.loc[slicer, "bus1"].map( + lambda x: x.replace("trn-elec-", "trn-"), + ) + n.links.loc[slicer, "bus1"] = n.links.loc[slicer, "bus1"].map( + lambda x: x.replace("trn-lpg-", "trn-"), + ) def _remove_exogenous_buses(n: pypsa.Network) -> None: - """Removes buses that are used for exogenous vehicle loads""" - + """Removes buses that are used for exogenous vehicle loads.""" # this is super awkward filtering :( buses = n.buses[ (n.buses.index.str.contains("trn-elec-veh") | n.buses.index.str.contains("trn-lpg-veh")) @@ -676,7 +662,7 @@ def _remove_exogenous_buses(n: pypsa.Network) -> None: def _constrain_charing_rates(n: pypsa.Network, must_run_evs: bool) -> None: - """Applies limits to p_min_pu/p_max_pu on links + """Applies limits to p_min_pu/p_max_pu on links. must_run_evs: True @@ -685,7 +671,6 @@ def _constrain_charing_rates(n: pypsa.Network, must_run_evs: bool) -> None: p_max_pu of both EVs and LPGs are set to match the load profile. p_min_pu is left unchanged (ie. zero) """ - links = n.links[n.links.carrier.str.contains("-veh") & ~n.links.carrier.str.endswith("-veh")] evs = links[links.carrier.str.contains("-elec-")] lpgs = links[links.carrier.str.contains("-lpg-")] @@ -721,7 +706,10 @@ def _constrain_charing_rates(n: pypsa.Network, must_run_evs: bool) -> None: n.links_t["p_max_pu"] = pd.concat([n.links_t["p_max_pu"], p_max_pu], axis=1) -def apply_endogenous_road_investments(n: pypsa.Network, must_run_evs: bool = False) -> None: +def apply_endogenous_road_investments( + n: pypsa.Network, + must_run_evs: bool = False, +) -> None: """Merges EV and LPG load into a single load. This function will do the following: @@ -754,7 +742,6 @@ def apply_exogenous_ev_policy(n: pypsa.Network, policy: pd.DataFrame) -> None: - Figure 6.3 at https://www.nrel.gov/docs/fy18osti/71500.pdf - Sheet 6.3 at https://data.nrel.gov/submissions/90 """ - vehicle_mapper = { "light_duty": RoadTransport.LIGHT.value, "med_duty": RoadTransport.MEDIUM.value, @@ -770,7 +757,6 @@ def apply_exogenous_ev_policy(n: pypsa.Network, policy: pd.DataFrame) -> None: for period in n.investment_periods: ev_share = policy.at[period, vehicle] for fuel in ("elec", "lpg"): - # adjust load value load_names = [x for x in n.loads.index if x.endswith(f"trn-{fuel}-{abrev}-{vehicle_mapper[vehicle]}")] df = n.loads_t.p_set.loc[period,][load_names] diff --git a/workflow/scripts/cluster_network.py b/workflow/scripts/cluster_network.py index 7c15967c7..6fb0e1834 100644 --- a/workflow/scripts/cluster_network.py +++ b/workflow/scripts/cluster_network.py @@ -1,3 +1,5 @@ +"""Cluster_network aggregates the outputs of simplify_network, and transforms the network to a zonal power balance model if specified in the configuration.""" + import logging import warnings from functools import reduce @@ -16,7 +18,7 @@ update_p_nom_max, ) from add_electricity import update_transmission_costs -from constants import * +from constants import REEDS_NERC_INTERCONNECT_MAPPER from pypsa.clustering.spatial import ( busmap_by_greedy_modularity, busmap_by_hac, @@ -37,7 +39,6 @@ def normed(x): def weighting_for_region(n, x, weighting_strategy=None): """Calculate the weighting for internal nodes within a given region.""" - conv_carriers = {"nuclear", "OCGT", "CCGT", "PHS", "hydro", "coal", "biomass"} generators = n.generators generators["carrier_base"] = generators.carrier.str.split().str[0] @@ -55,14 +56,14 @@ def weighting_for_region(n, x, weighting_strategy=None): load = n.loads_t.p_set.mean().groupby(n.loads.bus).sum() b_i = x.index - g = normed(gen.reindex(b_i, fill_value=0)) - l = normed(load.reindex(b_i, fill_value=0)) - w = g + l + gen_weight = normed(gen.reindex(b_i, fill_value=0)) + load_weight = normed(load.reindex(b_i, fill_value=0)) + weighting = gen_weight + load_weight if weighting_strategy == "population": - w = normed(n.buses.loc[x.index].Pd) + weighting = normed(n.buses.loc[x.index].Pd) - return (w * (100.0 / w.max())).clip(lower=1.0).astype(int) + return (weighting * (100.0 / weighting.max())).clip(lower=1.0).astype(int) def get_feature_for_hac(n, buses_i=None, feature=None): @@ -105,10 +106,14 @@ def get_feature_for_hac(n, buses_i=None, feature=None): return feature_data -def distribute_clusters(n, n_clusters, focus_weights=None, solver_name="cbc", weighting_strategy=None): - """ - Determine the number of clusters per region. - """ +def distribute_clusters( + n, + n_clusters, + focus_weights=None, + solver_name="cbc", + weighting_strategy=None, +): + """Determine the number of clusters per region.""" if weighting_strategy == "population": bus_distribution_factor = n.buses.Pd else: @@ -203,12 +208,10 @@ def fix_country_assignment_for_hac(n): neighbor_bus = n.lines.query( "bus0 == @disconnected_bus or bus1 == @disconnected_bus", - ).iloc[ - 0 - ][["bus0", "bus1"]] - new_country = list( - set(n.buses.loc[neighbor_bus].country) - {country}, - )[0] + ).iloc[0][["bus0", "bus1"]] + new_country = next( + iter(set(n.buses.loc[neighbor_bus].country) - {country}), + ) logger.info( f"overwriting country `{country}` of bus `{disconnected_bus}` " @@ -224,8 +227,7 @@ def fix_country_assignment_for_hac(n): if (algorithm != "hac") and (feature is not None): logger.warning( - f"Keyword argument feature is only valid for algorithm `hac`. " - f"Given feature `{feature}` will be ignored.", + f"Keyword argument feature is only valid for algorithm `hac`. Given feature `{feature}` will be ignored.", ) n.determine_network_topology() @@ -243,7 +245,9 @@ def fix_country_assignment_for_hac(n): countries_remove = list(bus_set - nc_set) buses_remove = n.buses[n.buses.country.isin(countries_remove)] if not buses_remove.empty: - logger.warning(f"Reconciling TAMU and ReEDS Topologies. \n Removing buses: {buses_remove.index}") + logger.warning( + f"Reconciling TAMU and ReEDS Topologies. \n Removing buses: {buses_remove.index}", + ) for c in n.one_port_components: component = n.df(c) rm = component[component.bus.isin(buses_remove.index)] @@ -456,10 +460,12 @@ def convert_to_transport( if itl_agg_fn: # Aggregating the ITLs to lower resolution - topology_aggregation_key = list(topology_aggregation.keys())[0] + topology_aggregation_key = next(iter(topology_aggregation.keys())) itl_lower_res = pd.read_csv(itl_agg_fn) itl_lower_res.columns = itl_lower_res.columns.str.lower() - itl_lower_res = itl_lower_res.rename(columns={"transgrp": "r", "transgrpp": "rr"}) + itl_lower_res = itl_lower_res.rename( + columns={"transgrp": "r", "transgrpp": "rr"}, + ) itl_lower_res = itl_lower_res[ # Filter low-res ITLs to only include those that have an end in the network itl_lower_res.r.isin(buses["country"]) | itl_lower_res.rr.isin(buses["country"]) @@ -580,7 +586,10 @@ def plot_busmap(n, busmap, fn=None): else: n_clusters = int(snakemake.wildcards.clusters) - n.generators.loc[n.generators.carrier.isin(non_aggregated_carriers), "land_region"] = n.generators.loc[ + n.generators.loc[ + n.generators.carrier.isin(non_aggregated_carriers), + "land_region", + ] = n.generators.loc[ n.generators.carrier.isin(non_aggregated_carriers), "bus", ] @@ -647,10 +656,15 @@ def plot_busmap(n, busmap, fn=None): itl_fn = snakemake.input.itl_county itl_cost_fn = snakemake.input.itl_costs_county case _: - raise ValueError(f"Unknown aggregation zone {topological_boundaries}") + raise ValueError( + f"Unknown aggregation zone {topological_boundaries}", + ) if topology_aggregation: - assert isinstance(topology_aggregation, dict), "topology_aggregation must be a dictionary." + assert isinstance( + topology_aggregation, + dict, + ), "topology_aggregation must be a dictionary." assert len(topology_aggregation) == 1, "topology_aggregation must contain exactly one key." # Extract the single key and value @@ -677,7 +691,7 @@ def plot_busmap(n, busmap, fn=None): ), f"Number of clusters must be {len(nodes_req)} for current configuration." n.buses.interconnect = n.buses.nerc_reg.map(REEDS_NERC_INTERCONNECT_MAPPER) - n.lines.drop(columns=["interconnect"], inplace=True) + n.lines = n.lines.drop(columns=["interconnect"]) clustering = clustering_for_n_clusters( n, diff --git a/workflow/scripts/constants.py b/workflow/scripts/constants.py index a95479199..73068ef4e 100644 --- a/workflow/scripts/constants.py +++ b/workflow/scripts/constants.py @@ -1,11 +1,7 @@ -""" -Module for holding global constant values. -""" +"""Module for holding global constant values.""" from enum import Enum -import pandas as pd - ########################################### # Constants for GIS Coordinate Reference Systems ########################################### @@ -162,7 +158,7 @@ "Water": "Hydro", "Bio": "Biomass", "Wind": "Wind", - "WH": "Waste", ##TODO: #33 add waste into cost data + "WH": "Waste", # TODO: #33 add waste into cost data "Geo": "Geothermal", "Uranium": "Nuclear", "Petroleum Coke": "Oil", @@ -563,6 +559,8 @@ "Mexico": "MX", } +CODE_2_STATE = {value: key for key, value in STATE_2_CODE.items()} + REEDS_NERC_INTERCONNECT_MAPPER = { "WECC_CA": "western", "WECC_NWPP": "western", @@ -926,6 +924,8 @@ class Month(Enum): + """Enums for Month of Year.""" + JANUARY = 1 FEBRUARY = 2 MARCH = 3 diff --git a/workflow/scripts/constants_sector.py b/workflow/scripts/constants_sector.py index 85ffa60b0..f952fdb44 100644 --- a/workflow/scripts/constants_sector.py +++ b/workflow/scripts/constants_sector.py @@ -1,6 +1,5 @@ -""" -Constants specific for sector coupling. -""" +# ruff: noqa: D100, D101 +"""Constants specific for sector coupling.""" from enum import Enum @@ -103,7 +102,7 @@ class BoatTransportUnits(Enum): class TransportEfficiency(Enum): - """Approximate MWh/MWh efficiencies""" + """Approximate MWh/MWh efficiencies.""" LPG = 0.20 # LLNL estimates 21 combined ELEC = 0.75 diff --git a/workflow/scripts/eia.py b/workflow/scripts/eia.py index 758634899..f464aaa0e 100644 --- a/workflow/scripts/eia.py +++ b/workflow/scripts/eia.py @@ -1,3 +1,4 @@ +# ruff: noqa: RUF012, D101, D102, N818, D400 """ Extracts EIA data. @@ -9,7 +10,8 @@ - Storage(fuel, storage, year, api) - Emissions(sector, year, api, fuel) -Examples: +Examples +-------- >>> costs = FuelCosts("gas", "power", 2020, "xxxxxxxxxxxxxxxx") >>> costs.get_data() @@ -37,7 +39,6 @@ import logging import math from abc import ABC, abstractmethod -from typing import Optional import constants import numpy as np @@ -99,9 +100,7 @@ # exceptions class InputException(Exception): - """ - Class for exceptions. - """ + """Class for exceptions.""" def __init__(self, propery, valid_options, recived_option) -> None: self.message = f" {propery} must be in {valid_options}; recieved {recived_option}" @@ -112,15 +111,11 @@ def __str__(self): # creator class EiaData(ABC): - """ - Creator class to extract EIA data. - """ + """Creator class to extract EIA data.""" @abstractmethod def data_creator(self): # type DataExtractor - """ - Gets the data. - """ + """Gets the data.""" pass def get_data(self, pivot: bool = False) -> pd.DataFrame: @@ -142,15 +137,14 @@ def get_raw_data(self) -> pd.DataFrame: # concrete creator class FuelCosts(EiaData): - def __init__( self, fuel: str, year: int, api: str, - industry: Optional[str] = None, - grade: Optional[str] = None, - scenario: Optional[str] = None, + industry: str | None = None, + grade: str | None = None, + scenario: str | None = None, ) -> None: self.fuel = fuel self.year = year @@ -192,7 +186,6 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Trade(EiaData): - def __init__( self, fuel: str, @@ -225,7 +218,6 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Production(EiaData): - def __init__(self, fuel: str, production: str, year: int, api: str) -> None: self.fuel = fuel # (gas) self.production = production # (marketed|gross) @@ -258,7 +250,7 @@ def __init__( sector: str, year: int, api: str, - scenario: Optional[str] = None, + scenario: str | None = None, ) -> None: self.sector = sector # (residential, commercial, transport, industry) self.year = year @@ -296,7 +288,7 @@ def __init__( year: int, api: str, units: str = "travel", # travel | btu - scenario: Optional[str] = None, + scenario: str | None = None, ) -> None: self.vehicle = vehicle self.year = year @@ -365,7 +357,7 @@ def __init__( vehicle: str, year: int, api: str, - scenario: Optional[str] = None, + scenario: str | None = None, ) -> None: self.vehicle = vehicle self.year = year @@ -391,7 +383,6 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Storage(EiaData): - def __init__(self, fuel: str, storage: str, year: int, api: str) -> None: self.fuel = fuel self.storage = storage # (base|working|total|withdraw) @@ -411,8 +402,13 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Emissions(EiaData): - - def __init__(self, sector: str, year: int, api: str, fuel: str = None) -> None: + def __init__( + self, + sector: str, + year: int, + api: str, + fuel: str | None = None, + ) -> None: self.sector = sector # (power|residential|commercial|industry|transport|total) self.year = year # 1970 - 2021 self.api = api @@ -423,9 +419,7 @@ def data_creator(self): class Seds(EiaData): - """ - State Energy Demand System. - """ + """State Energy Demand System.""" def __init__(self, metric: str, sector: str, year: int, api: str) -> None: self.metric = metric # (consumption) @@ -445,7 +439,6 @@ def data_creator(self): class ElectricPowerData(EiaData): - def __init__(self, sector: str, year: int, api_key: str) -> None: self.sector = sector self.year = year @@ -457,11 +450,9 @@ def data_creator(self): # product class DataExtractor(ABC): - """ - Extracts and formats data. - """ + """Extracts and formats data.""" - def __init__(self, year: int, api_key: str = None): + def __init__(self, year: int, api_key: str | None = None): self.api_key = api_key # self.year = self._set_year(year) self.year = year @@ -480,9 +471,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: @abstractmethod def build_url(self) -> str: - """ - Builds API url. - """ + """Builds API url.""" pass def retrieve_data(self) -> pd.DataFrame: @@ -508,7 +497,6 @@ def _request_eia_data(url: str) -> dict[str, dict | str]: url in the form of "https://api.eia.gov/v2/" followed by api key and facets """ - # sometimes running into HTTPSConnectionPool error. adding in retries helped session = requests.Session() retries = Retry( @@ -527,9 +515,7 @@ def _request_eia_data(url: str) -> dict[str, dict | str]: @staticmethod def _format_period(dates: pd.Series) -> pd.Series: - """ - Parses dates into a standard monthly format. - """ + """Parses dates into a standard monthly format.""" try: # try to convert to YYYY-MM-DD format return pd.to_datetime(dates, format="%Y-%m-%d") except ValueError: @@ -540,9 +526,7 @@ def _format_period(dates: pd.Series) -> pd.Series: @staticmethod def _pivot_data(df: pd.DataFrame) -> pd.DataFrame: - """ - Pivots data on period and state. - """ + """Pivots data on period and state.""" df = df.reset_index() try: return df.pivot( @@ -570,7 +554,6 @@ def _assign_dtypes(df: pd.DataFrame) -> pd.DataFrame: # concrete product class GasCosts(DataExtractor): - industry_codes = { "power": "PEU", "residential": "PRS", @@ -592,14 +575,11 @@ def __init__(self, industry: str, year: int, api_key: str) -> None: def build_url(self) -> str: base_url = "natural-gas/pri/sum/data/" - facets = f"frequency=monthly&data[0]=value&facets[process][]={self.industry_codes[self.industry]}&start={self.year}-01&end={self.year+1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + facets = f"frequency=monthly&data[0]=value&facets[process][]={self.industry_codes[self.industry]}&start={self.year}-01&end={self.year + 1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - """ - Formats natural gas cost data. - """ - + """Formats natural gas cost data.""" # format dates df["period"] = self._format_period(df.period) df = df.set_index("period").copy() @@ -637,7 +617,6 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # concrete product class CoalCosts(DataExtractor): - industry_codes = { "power": "PEU", } @@ -654,11 +633,10 @@ def __init__(self, industry: str, year: int, api_key: str) -> None: def build_url(self) -> str: base_url = "coal/shipments/receipts/data/" - facets = f"frequency=quarterly&data[0]=price&facets[coalRankId][]=TOT&start={self.year}-Q1&end={self.year+1}-Q1&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + facets = f"frequency=quarterly&data[0]=price&facets[coalRankId][]=TOT&start={self.year}-Q1&end={self.year + 1}-Q1&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df = df[ (df.coalRankId.isin(["TOT"])) & ~(df.price == "w") @@ -726,7 +704,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class LpgCosts(DataExtractor): """ - This is motor gasoline! + The cost is motor gasoline! Not heating fuel! """ @@ -768,7 +746,7 @@ class LpgCosts(DataExtractor): def __init__(self, grade: str, year: int, api_key: str) -> None: self.grade = grade - if not grade in self.grade_codes: + if grade not in self.grade_codes: raise InputException( propery="Lpg Costs", valid_options=list(self.grade_codes), @@ -782,7 +760,6 @@ def build_url(self) -> str: return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - data = df[["period", "area-name", "series-description", "value", "units"]].copy() data["state"] = data["area-name"].map(self.padd_2_state) @@ -796,15 +773,13 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class HeatingFuelCosts(DataExtractor): - """ - Note, only returns data from October to March! - """ + """Note, only returns data from October to March!""" heating_fuel_codes = {"fuel_oil": "No 2 Fuel Oil", "propane": "Propane"} def __init__(self, heating_fuel: str, year: int, api_key: str) -> None: self.heating_fuel = heating_fuel - if not heating_fuel in self.heating_fuel_codes: + if heating_fuel not in self.heating_fuel_codes: raise InputException( propery="Heating Fuel Costs", valid_options=list(self.heating_fuel_codes), @@ -818,7 +793,6 @@ def build_url(self) -> str: return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - data = df[ ( df["product-name"].str.startswith( @@ -933,9 +907,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class ProjectedSectorEnergyDemand(DataExtractor): - """ - Extracts projected energy demand at a national level from AEO 2023. - """ + """Extracts projected energy demand at a national level from AEO 2023.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -981,9 +953,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class HistoricalTransportTravelDemand(DataExtractor): - """ - Gets Transport demand in units of travel. - """ + """Gets Transport demand in units of travel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -1049,9 +1019,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class ProjectedTransportTravelDemand(DataExtractor): - """ - Gets Transport demand in units of travel. - """ + """Gets Transport demand in units of travel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -1105,9 +1073,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class HistoricalTransportBtuDemand(DataExtractor): - """ - Gets Transport demand in units of btu. - """ + """Gets Transport demand in units of btu.""" # units will be different umong these! vehicle_codes = { @@ -1175,9 +1141,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class ProjectedTransportBtuDemand(DataExtractor): - """ - Gets Transport demand in units of quads. - """ + """Gets Transport demand in units of quads.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -1236,9 +1200,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class HistoricalProjectedTransportFuelUse(DataExtractor): - """ - Gets Transport Energy Use by fuel. - """ + """Gets Transport Energy Use by fuel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -1397,9 +1359,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class ProjectedGasCosts(DataExtractor): - """ - Extracts projected energy demand at a national level from AEO 2023. - """ + """Extracts projected energy demand at a national level from AEO 2023.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS @@ -1467,12 +1427,11 @@ def __init__(self, direction: str, year: int, api_key: str) -> None: super().__init__(year, api_key) def build_url(self) -> str: - base_url = f"natural-gas/move/ist/data/" - facets = f"frequency=annual&data[0]=value&facets[process][]={self.direction_codes[self.direction]}&start={self.year-1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + base_url = "natural-gas/move/ist/data/" + facets = f"frequency=annual&data[0]=value&facets[process][]={self.direction_codes[self.direction]}&start={self.year - 1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df["period"] = pd.to_datetime(df.period).map(lambda x: x.year) # extract only canada and mexico trade @@ -1534,12 +1493,11 @@ def __init__(self, direction: str, year: int, api_key: str) -> None: super().__init__(year, api_key) def build_url(self) -> str: - base_url = f"natural-gas/move/ist/data/" - facets = f"frequency=annual&data[0]=value&facets[process][]={self.direction_codes[self.direction]}&start={self.year-1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + base_url = "natural-gas/move/ist/data/" + facets = f"frequency=annual&data[0]=value&facets[process][]={self.direction_codes[self.direction]}&start={self.year - 1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df["period"] = pd.to_datetime(df.period).map(lambda x: x.year) # drop Federal Offshore--Gulf of Mexico Natural Gas Interstate Receipts @@ -1577,9 +1535,7 @@ def extract_state(description: str) -> str: class GasStorage(DataExtractor): - """ - Underground storage facilites for natural gas. - """ + """Underground storage facilites for natural gas.""" # https://www.eia.gov/naturalgas/storage/basics/ storage_codes = { @@ -1601,11 +1557,10 @@ def __init__(self, storage: str, year: int, api_key: str) -> None: def build_url(self) -> str: base_url = "natural-gas/stor/sum/data/" - facets = f"frequency=monthly&data[0]=value&facets[process][]={self.storage_codes[self.storage]}&start={self.year}-01&end={self.year+1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + facets = f"frequency=monthly&data[0]=value&facets[process][]={self.storage_codes[self.storage]}&start={self.year}-01&end={self.year + 1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df = df[~(df["area-name"] == "NA")].copy() df["period"] = self._format_period(df.period) df["state"] = df["series-description"].map(self.extract_state).map(self.map_state_names) @@ -1620,23 +1575,17 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: @staticmethod def extract_state(description: str) -> str: - """ - Extracts state from series descripion. - """ + """Extracts state from series descripion.""" return description.split(" Natural ")[0] @staticmethod def map_state_names(state: str) -> str: - """ - Maps state name to code. - """ + """Maps state name to code.""" return "U.S." if state == "U.S. Total" else STATE_CODES[state] class GasProduction(DataExtractor): - """ - Dry natural gas production. - """ + """Dry natural gas production.""" production_codes = { "market": "VGM", @@ -1655,11 +1604,10 @@ def __init__(self, production: str, year: int, api_key: str) -> None: def build_url(self) -> str: base_url = "natural-gas/prod/sum/data/" - facets = f"frequency=monthly&data[0]=value&facets[process][]={self.production_codes[self.production]}&start={self.year}-01&end={self.year+1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + facets = f"frequency=monthly&data[0]=value&facets[process][]={self.production_codes[self.production]}&start={self.year}-01&end={self.year + 1}-01&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df = df[~(df["area-name"] == "NA")].copy() df["period"] = self._format_period(df.period) df["state"] = df["series-description"].map(self.extract_state).map(self.map_state_names) @@ -1685,16 +1633,12 @@ def extract_state(description: str) -> str: @staticmethod def map_state_names(state: str) -> str: - """ - Maps state name to code. - """ + """Maps state name to code.""" return "U.S." if state == "U.S." else STATE_CODES[state] class StateEmissions(DataExtractor): - """ - State Level CO2 Emissions. - """ + """State Level CO2 Emissions.""" sector_codes = { "commercial": "CC", @@ -1738,7 +1682,6 @@ def build_url(self) -> str: return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df = df[~(df["state-name"] == "NA")].copy() df = df.rename( columns={ @@ -1762,9 +1705,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class SedsConsumption(DataExtractor): - """ - State Level End-Use Consumption. - """ + """State Level End-Use Consumption.""" sector_codes = { "commercial": "TNCCB", @@ -1793,7 +1734,6 @@ def build_url(self) -> str: return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: - df = df.rename( columns={ "unit": "units", @@ -1812,9 +1752,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: class ElectricPowerOperationalData(DataExtractor): - """ - Electric Power Operational Data. - """ + """Electric Power Operational Data.""" sector_codes = { "electric_utility": 1, @@ -1851,7 +1789,7 @@ def __init__(self, sector: str, year: int, api_key: str) -> None: def build_url(self) -> str: base_url = "electricity/electric-power-operational-data/data/" - facets = f"frequency=annual&data[0]=generation&facets[sectorid][]={self.sector_codes[self.sector]}&start={self.year-1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" + facets = f"frequency=annual&data[0]=generation&facets[sectorid][]={self.sector_codes[self.sector]}&start={self.year - 1}&end={self.year}&sort[0][column]=period&sort[0][direction]=desc&offset=0&length=5000" return f"{API_BASE}{base_url}?api_key={self.api_key}&{facets}" def format_data(self, df: pd.DataFrame) -> pd.DataFrame: diff --git a/workflow/scripts/eulp.py b/workflow/scripts/eulp.py index ec0dae6d2..16998796e 100644 --- a/workflow/scripts/eulp.py +++ b/workflow/scripts/eulp.py @@ -1,3 +1,5 @@ +# ruff: noqa: RUF012, D101, D102 + """ Holds data processing class for NREL End Use Load Profiles. @@ -6,9 +8,6 @@ See `retrieve_eulp` rule for the data extraction """ -import logging -from typing import Optional - import pandas as pd @@ -149,8 +148,8 @@ class Eulp: def __init__( self, - filepath: Optional[str] = None, - df: Optional[pd.DataFrame] = None, + filepath: str | None = None, + df: pd.DataFrame | None = None, ) -> None: if filepath: df = self._read_data(filepath) @@ -171,7 +170,7 @@ def __init__( ) else: raise TypeError( - f"missing 1 required positional argument: 'filepath' or 'df'", + "missing 1 required positional argument: 'filepath' or 'df'", ) def __add__(self, other): @@ -217,9 +216,7 @@ def _read_data(filepath: str) -> pd.DataFrame: @staticmethod def _resample_data(df: pd.DataFrame) -> pd.DataFrame: - """ - Locked to resampling at 1hr. - """ + """Locked to resampling at 1hr.""" if not isinstance(df.index, pd.DatetimeIndex): df.index = pd.to_datetime(df.index) df.index = df.index.map(lambda x: x.replace(year=2018)) @@ -228,7 +225,6 @@ def _resample_data(df: pd.DataFrame) -> pd.DataFrame: return resampled.sort_index() def _aggregate_data(self, df: pd.DataFrame) -> pd.DataFrame: - def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: sector_columns = [x for x in columns if x in df.columns.to_list()] return df[sector_columns].sum(axis=1) @@ -250,16 +246,15 @@ def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: def plot( self, - sectors: Optional[list[str] | str] = [ + sectors: list[str] | str | None = [ "electricity", "heating", "cooling", "space_heating", "water_heating", ], - resample: Optional[str] = None, + resample: str | None = None, ): - if isinstance(sectors, str): sectors = [sectors] @@ -275,9 +270,7 @@ def to_csv(self, path_or_buf: str, **kwargs): class EulpTotals: - """ - End use by fuel. - """ + """End use by fuel.""" _elec_group = ["out.electricity.total.energy_consumption.kwh"] @@ -289,8 +282,8 @@ class EulpTotals: def __init__( self, - filepath: Optional[str] = None, - df: Optional[pd.DataFrame] = None, + filepath: str | None = None, + df: pd.DataFrame | None = None, ) -> None: if filepath: df = self._read_data(filepath) @@ -301,7 +294,7 @@ def __init__( assert (self.data.columns == ["electricity", "gas", "oil", "propane"]).all() else: raise TypeError( - f"missing 1 required positional argument: 'filepath' or 'df'", + "missing 1 required positional argument: 'filepath' or 'df'", ) def __add__(self, other): @@ -343,9 +336,7 @@ def _read_data(filepath: str) -> pd.DataFrame: @staticmethod def _resample_data(df: pd.DataFrame) -> pd.DataFrame: - """ - Locked to resampling at 1hr. - """ + """Locked to resampling at 1hr.""" if not isinstance(df.index, pd.DatetimeIndex): df.index = pd.to_datetime(df.index) df.index = df.index.map(lambda x: x.replace(year=2018)) @@ -354,7 +345,6 @@ def _resample_data(df: pd.DataFrame) -> pd.DataFrame: return resampled.sort_index() def _aggregate_data(self, df: pd.DataFrame) -> pd.DataFrame: - def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: sector_columns = [x for x in columns if x in df.columns.to_list()] return df[sector_columns].sum(axis=1) @@ -375,9 +365,8 @@ def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: def plot( self, - sectors: Optional[list[str] | str] = ["electricity", "gas", "oil", "propane"], + sectors: list[str] | str | None = ["electricity", "gas", "oil", "propane"], ): - if isinstance(sectors, str): sectors = [sectors] diff --git a/workflow/scripts/generate_stochastic_samples.py b/workflow/scripts/generate_stochastic_samples.py deleted file mode 100644 index 742ea3b71..000000000 --- a/workflow/scripts/generate_stochastic_samples.py +++ /dev/null @@ -1,712 +0,0 @@ -# Written by Kamran Tehranchi (Stanford University) -""" -This scripts stochastic samples for power systems models based on the Mean-Reversion Stochastic Process Method presented here: XXX placeholder for paper link XXX - -""" -# %% Imports and functions - - -import glob -import os -import time -from pathlib import Path - -import numpy as np -import pandas as pd -import xarray as xr - - -def import_profiles_from_folder(PATH_FILES): - filelist = [] - for file in glob.glob(os.path.join(PATH_FILES, f"*.csv")): - filelist.append(os.path.join(file)) - load_base = pd.read_csv([s for s in filelist if "loads" in s][0], index_col=0) - solar_base = pd.read_csv([s for s in filelist if "solar" in s][0], index_col=0) - wind_base = pd.read_csv([s for s in filelist if "wind" in s][0], index_col=0) - return load_base, solar_base, wind_base - - -def import_profiles_from_network(PATH_NETWORK, num_clusters): - """ - Imports the profiles from a pypsa network. - - Args: - network_path (str): Path to the pypsa network. - num_clusters (int): Number of clusters to be used in the network. - - Returns: - pandas.DataFrame: Base load profile. - pandas.DataFrame: Base solar profile. - pandas.DataFrame: Base wind profile. - """ - import pypsa - - network = pypsa.Network(PATH_NETWORK) - # Export network to csv to be run in NodalStochastic Profile Generator - network.loads_t.p_set.to_csv( - os.path.join("resources/pypsa", f"loads_t_p_set_{num_clusters}.csv"), - ) - - # filter columns with solar in name - solar_df = network.generators_t.p_max_pu.loc[ - :, - network.generators_t.p_max_pu.columns.str.contains("solar"), - ] - solar_df.to_csv( - os.path.join("resources/pypsa", f"solar_profiles_{num_clusters}.csv"), - ) - - # filter columns with wind in name and export as csv - wind_df = network.generators_t.p_max_pu.loc[ - :, - network.generators_t.p_max_pu.columns.str.contains("wind"), - ] - wind_df.to_csv(os.path.join("resources/pypsa", f"wind_profiles_{num_clusters}.csv")) - - load_base, solar_base, wind_base = import_profiles_from_folder("resources/pypsa") - - return load_base, solar_base, wind_base - - -def resample_and_group(base_data, area_mapping): - """ - Resamples the input base data from hourly to daily, grouped by area id. - - Args: - base_data (pandas.DataFrame): Input data to be resampled. - area_mapping (dict): Dictionary mapping column names to area ids. - - Returns: - pandas.DataFrame: Resampled data, grouped by day of year and area id. - """ - base_area = base_data.groupby( - base_data.columns.map(area_mapping.area_id), - axis=1, - ).mean() - base_area["doy"] = timestamp_reference.day_of_year - base_area_daily = base_area.groupby("doy").mean() - base_area.drop(columns=["doy"], inplace=True) - return base_area_daily, base_area - - -def create_allocation(base, mapping): - """ - Creates an allocation of generation based on the mean of the base data. - - Args: - base (pandas.DataFrame): Input data to be resampled. - mapping (pandas.DataFrame): Dictionary mapping column names to area ids. - - Returns: - pandas.DataFrame: Hourly allocation of generation based on the mean of the base data. - """ - base_ = base.copy().reset_index(drop=True) - total_area_hourly = base_.groupby( - base_.columns.map(mapping.to_dict()["area_id"]), - axis=1, - ).sum() - base_.columns = base.columns.map(mapping.to_dict()["area_id"]) - base_[base_ < 0.0001] = 0 - allocation = base_.apply(lambda x: x / total_area_hourly.loc[:, x.name], axis=0) - allocation.fillna(0, inplace=True) - return allocation - - -def define_solar_hours(solar_profile, timestamps): - """ - Defines the first and last hour of the day for solar generation. - - Args: - solar_profile (pandas.DataFrame): Solar generation profile. - timestamps (pandas.DataFrame): Timestamps for the solar generation profile. - - Returns: - pandas.DataFrame: First and last hour of the day for solar generation. - """ - first_hour, last_hour, yesterday_last_hour = np.zeros((3, len(solar_profile))) - last_selected_index = 0 - timestamps = timestamps.copy(deep=True) - timestamps["solar_profile"] = pd.Series(solar_profile, name="solar_profile") - - for _, day_of_year_df in timestamps.groupby("day_of_year"): - first_hour_idx = day_of_year_df.solar_profile.idxmax() - last_hour_idx = day_of_year_df.solar_profile[::-1].idxmax() - start_hour = day_of_year_df.solar_profile.idxmin() - first_hour[first_hour_idx] = 1 - yesterday_last_hour[first_hour_idx] = 0 if start_hour < 23 else last_selected_index - last_hour[last_hour_idx] = 1 - last_selected_index = last_hour_idx - - return first_hour, last_hour, yesterday_last_hour - - -def assign_stochastic_mu(df_params, timestamp_reference, column_name="profile_type"): - """ - Assigns a stochastic mu to each hour of the year based on the season and - profile type. - - Args: - df_params (pandas.DataFrame): Dataframe containing the parameters for the stochastic mu. - timestamp_reference (pandas.DataFrame): Dataframe containing the timestamp reference. - column_name (str): Column name to be used for the merge. - - Returns: - pandas.DataFrame: Dataframe containing the stochastic mu for each hour of the year. - """ - df_mu_hourly = pd.merge( - left=timestamp_reference, - right=df_params[[column_name, "area_id", "season", "mu"]], - left_on="season_num", - right_on="season", - how="outer", - ) - order = df_mu_hourly["area_id"].unique() - df_mu_hourly_unstack = df_mu_hourly.set_index(["timestamp", column_name, "area_id"])["mu"].unstack().reset_index() - df_mu_hourly_unstack.columns = df_mu_hourly_unstack.columns.map(str) - df_mu_hourly_unstack = df_mu_hourly_unstack[np.append("timestamp", order)] - return df_mu_hourly_unstack - - -def sampling_warmup(df, thresholds, rng, min_steps=50): - """ - Performs a warmup of the sampling. - - Args: - df (pandas.DataFrame): Dataframe containing the parameters for the ratio sample. - thresholds (pandas.DataFrame): Dataframe containing the thresholds for the ratio sample. - rng (numpy.random.Generator): Random number generator. - min_steps (int): Minimum number of steps to be performed. - - Returns: - pandas.DataFrame: Dataframe containing the first sample of the ratio and epsilon. - """ - winter_params = df.query("season == 1") - ratio = winter_params.mu - for _ in range(min_steps): - eps = rng_eps(winter_params, rng) - ratio += winter_params.kai * (winter_params.mu - ratio) + eps - ratio, eps, na = resample_ratio( - ratio, - winter_params, - thresholds, - eps, - rng, - True, - ) - return ratio, eps - - -# idea: to better replicate inverter curtailment, i should retain out of threshold samples but maintain a seperate 'ratio' df that is the max(sample, 0) or min(sample, threshold))... this simplifies the need for re-sampling but retains proper distributional properties -def resample_ratio( - original_ratio, - params, - thresholds, - original_epsilon, - rng, - initialization, -): - resample_count = 0 - param_dict = params.sampling_id.to_dict() - while np.any( - np.logical_or(original_ratio > thresholds, original_ratio < 0), - ): # if any values are out of bounds - resampled_ratio = original_ratio.copy(deep=True) - mask_to_resample = np.logical_or( - original_ratio > thresholds, - original_ratio < 0, - ) # mask of values to resample - resampled_epsilon = rng_eps( - params[mask_to_resample.values], - rng, - ) # resample epsilon for values to resample - resampled_ratio[mask_to_resample] = ( - original_ratio[mask_to_resample] - original_epsilon[mask_to_resample] + resampled_epsilon.values - ) # resample ratio. subtract original epsilon and add new epsilon - resample_count += 1 - - if np.any( - np.logical_or(resampled_ratio > thresholds, resampled_ratio < 0), - ): # if any values are still out of bounds - - in_bounds_mask = np.logical_and( - mask_to_resample, - np.logical_and(resampled_ratio < thresholds, resampled_ratio > 0), - ) - - if np.any( - in_bounds_mask, - ): # if any values are in bounds (i.e. resampling was successful), then update original ratio and epsilon so that we reduce the number of resampled values in the next iteration - if not initialization: - resampled_epsilon.index = resampled_epsilon.index.map(param_dict) - original_ratio.loc[in_bounds_mask] = resampled_ratio[in_bounds_mask] - original_epsilon.loc[in_bounds_mask] = resampled_epsilon[in_bounds_mask].values - - else: # if no values are out of bounds, then set original ratio to resampled ratio, and we are done. - original_ratio = resampled_ratio.copy(deep=True) - original_epsilon[mask_to_resample] = resampled_epsilon.values - - if resample_count > 200: - print(resampled_ratio[in_bounds_mask]) - raise ValueError("Max number of iterations reached") - - return original_ratio, original_epsilon, resample_count - - -def rng_eps(params, rng): - return rng.standard_normal(len(params)) * params.sigma - - -################### Import Parameters ################### -area_mapping_solar = pd.read_csv("parameters/pypsa_area_mapping_solar.csv", index_col=0) -area_mapping_wind = pd.read_csv("parameters/pypsa_area_mapping_wind.csv", index_col=0) -load_parameters = pd.read_csv("parameters/pypsa_load_parameters.csv", index_col=0) -solar_parameters = pd.read_csv("parameters/pypsa_solar_parameters.csv", index_col=0) -wind_parameters = pd.read_csv("parameters/pypsa_wind_parameters.csv", index_col=0) - -################### Parameters ################### -num_samples = 10 -load_ratio_threshold = 5 -solar_ratio_threshold = 5 -wind_ratio_threshold = 100000 -year = 2016 -NETWORK_PATH = ( - "/Users/kamrantehranchi/Local_Documents/pypsa-breakthroughenergy-usa/workflow/notebooks/elec_s_96_offwind_2.nc" -) -num_clusters = 96 - -################### Sampling ################### -# %% -load_base, solar_base, wind_base = import_profiles_from_network( - NETWORK_PATH, - num_clusters, -) -num_zones = { - "load": load_base.shape[1], - "solar": len(area_mapping_solar.area_id.unique()), - "wind": len(area_mapping_wind.area_id.unique()), -} - -Path("sampled_data").mkdir(parents=True, exist_ok=True) -PATH_SAMPLE = Path("sampled_data") -num_hours = load_base.shape[0] - -solar_parameters = solar_parameters[solar_parameters.area_id.isin(area_mapping_solar.area_id.unique())] -wind_parameters = wind_parameters[wind_parameters.area_id.isin(area_mapping_wind.area_id.unique())] - -# Define Sampling IDs -load_parameters["sampling_id"] = ( - load_parameters.index + "_" + load_parameters.timescale + "_" + load_parameters.area_id.astype(str) -) -solar_parameters["sampling_id"] = ( - solar_parameters.index + "_" + solar_parameters.timescale + "_" + solar_parameters.area_id.astype(str) -) -wind_parameters["sampling_id"] = ( - wind_parameters.index + "_" + wind_parameters.timescale + "_" + wind_parameters.area_id.astype(str) -) -parameters_concat = pd.concat( - [load_parameters, solar_parameters, wind_parameters], -).reset_index() - -# Create a timeseries index dataframe to reference when sampling -timestamp_reference = pd.DataFrame( - {"timestamp": pd.period_range(year, periods=num_hours, freq="h")}, -) -timestamp_reference = timestamp_reference.assign( - month=timestamp_reference["timestamp"].dt.month, - day=timestamp_reference["timestamp"].dt.day, - hour=timestamp_reference["timestamp"].dt.hour, - day_of_year=timestamp_reference["timestamp"].dt.dayofyear, -) -timestamp_reference["season"] = timestamp_reference["month"].map( - { - 12: "winter", - 1: "winter", - 2: "winter", - 3: "spring", - 4: "spring", - 5: "spring", - 6: "summer", - 7: "summer", - 8: "summer", - 9: "fall", - 10: "fall", - 11: "fall", - }, -) -timestamp_reference["season_num"] = timestamp_reference["season"].map( - {"winter": 1, "spring": 2, "summer": 3, "fall": 4}, -) - -load_base.reset_index(inplace=True, drop=True) -solar_base.reset_index(inplace=True, drop=True) -wind_base.reset_index(inplace=True, drop=True) - -# resample load, solar, and wind to daily and area level values. -solar_base_area_daily, solar_base_area = resample_and_group( - solar_base, - area_mapping_solar, -) -wind_base_area_daily, wind_base_area = resample_and_group(wind_base, area_mapping_wind) - -# %% #Create Generation-Plant Allocations to Map from Area Stochastic Profiles to Plant level Profiles. -solar_allocation = create_allocation(solar_base, area_mapping_solar) -wind_allocation = create_allocation(wind_base, area_mapping_wind) - -# %% #Define Solar Hours -daytime_mask = solar_base_area >= 0.0001 -daytime_mask = daytime_mask.apply(lambda x: x.astype(int)) -df_daytime_solar_tracking = pd.DataFrame( - np.zeros_like(solar_base_area), - index=solar_base_area.index, - columns=solar_base_area.columns, -) -df_daytime_solar_tracking = df_daytime_solar_tracking.apply( - lambda x: define_solar_hours(daytime_mask[x.name], timestamp_reference)[2], -) - -# %%#Define Stochastic Mu Dataframes -cols_to_use = timestamp_reference.columns.difference(load_parameters.columns) -df_mu_load_hourly_unstack = assign_stochastic_mu( - load_parameters.reset_index(), - timestamp_reference[cols_to_use], -) -df_mu_solar_hourly_unstack = assign_stochastic_mu( - solar_parameters.query("timescale == 'hourly'").reset_index(), - timestamp_reference[cols_to_use], -) -df_mu_solar_daily_unstack = assign_stochastic_mu( - solar_parameters.query(" timescale == 'daily' ").reset_index(), - timestamp_reference, -) -df_mu_wind_hourly_unstack = assign_stochastic_mu( - wind_parameters.query(" timescale == 'daily' ").reset_index(), - timestamp_reference[cols_to_use], -) -df_mu_wind_daily_unstack = assign_stochastic_mu( - wind_parameters.query(" timescale == 'daily' ").reset_index(), - timestamp_reference[cols_to_use], -) - -# %% Create Output NP Arrays -load_zones_samples = np.zeros([num_hours, num_zones["load"], num_samples]) -solar_zones_samples = np.zeros([num_hours, num_zones["solar"], num_samples]) -wind_zones_samples = np.zeros([num_hours, num_zones["wind"], num_samples]) -solar_gen_samples = np.zeros([num_hours, solar_base.columns.shape[0], num_samples]) -wind_gen_samples = np.zeros([num_hours, wind_base.columns.shape[0], num_samples]) -ratio_samples_combined = np.zeros( - [num_hours, parameters_concat.sampling_id.unique().shape[0], num_samples], -) - -# %% Sampling Process -for sample_num in range(0, num_samples): - print(f"Sample {sample_num}") - rng = np.random.default_rng(seed=sample_num) - start_time = time.time() - resample_count = 0 - n_hours = timestamp_reference.shape[0] - sampling_profile_names = parameters_concat.sampling_id.unique() - solar_sampling_id_dict = dict( - parameters_concat[parameters_concat.sampling_id.str.contains("solar")] - .reset_index()[["sampling_id", "index"]] - .values, - ) - ratio_samples = pd.DataFrame( - 0, - index=np.arange(n_hours), - columns=sampling_profile_names, - ) - epsilon_samples = pd.DataFrame( - 0, - index=np.arange(n_hours), - columns=sampling_profile_names, - ) - - thresholds = np.concatenate( - [ - [load_ratio_threshold] * ratio_samples.columns.str.contains("load").sum(), - [solar_ratio_threshold] * ratio_samples.columns.str.contains("solar").sum(), - [wind_ratio_threshold] * ratio_samples.columns.str.contains("wind").sum(), - ], - ) - - ratio_samples.iloc[0, :], epsilon_samples.iloc[0, :] = sampling_warmup( - parameters_concat, - thresholds, - rng, - ) - - for i in range(1, n_hours): - - season_df = parameters_concat[parameters_concat.season == timestamp_reference.iloc[i].season_num] - p_eps = rng_eps(season_df, rng) - ratio_samples.iloc[i, :] = ( - ratio_samples.iloc[i - 1, :] - + season_df.kai.values * (season_df.mu.values - ratio_samples.iloc[i - 1, :]) - + p_eps.values - ) - epsilon_samples.iloc[i, :] = p_eps.values - - if i % 24 != 0: - ratio_samples.loc[i, ratio_samples.columns.str.contains("daily")] = ratio_samples.loc[ - i - 1, - ratio_samples.columns.str.contains("daily"), - ] - epsilon_samples.loc[i, epsilon_samples.columns.str.contains("daily")] = epsilon_samples.loc[ - i - 1, - epsilon_samples.columns.str.contains("daily"), - ] - - previous_sunset_idx = df_daytime_solar_tracking.iloc[i].astype(int) - if (previous_sunset_idx > 0).any(): - area_sunset_dict = dict( - zip( - [ - solar_sampling_id_dict.get(param_code) - for param_code in ratio_samples.columns - if "solar" in param_code - ], - previous_sunset_idx[previous_sunset_idx > 0].index, - ), - ) - previous_mu_vals = ratio_samples.loc[ - :, - ratio_samples.columns.str.contains("solar"), - ].iloc[i - 1] - # previous_mu_vals = ratio_samples.loc[:,ratio_samples.columns.str.contains('solar')].iloc[i - 1].rename(index=solar_sampling_id_dict).loc[area_sunset_dict.keys()] - solar_season = season_df[season_df.sampling_id.str.contains("solar")] - solar_mu_vals, solar_kai_vals = solar_season[["mu", "kai"]][ - solar_season.sampling_id.isin(previous_mu_vals.index) - ].values.T - eps_filtered = ( - p_eps[solar_season.index] - .rename(index=season_df.sampling_id.to_dict()) - .loc[previous_mu_vals.index] - .values - ) - ratio_samples.loc[i, previous_mu_vals.index] = ( - previous_mu_vals.values + solar_kai_vals * (solar_mu_vals - previous_mu_vals.values) + eps_filtered - ) - - if (ratio_samples.iloc[i] > thresholds).any() or (ratio_samples.iloc[i] < 0).any(): - r0, eps = ratio_samples.iloc[i], epsilon_samples.iloc[i, :] - ratios_new, eps_new, resample_count = resample_ratio( - r0, - season_df, - thresholds, - eps, - rng, - False, - ) - ratio_samples.iloc[i], epsilon_samples.iloc[i, :], resample_count = ( - ratios_new, - eps_new, - resample_count, - ) - print(f"Sampling time : {time.time() - start_time}") - - ratio_samples_combined[:, :, sample_num] = ratio_samples.values - - # Load Calculation - sampled_load_ratio = ratio_samples.filter(like="load") - sampled_load = load_base * sampled_load_ratio.values / df_mu_load_hourly_unstack.iloc[:, 1:].values - sampled_load["timestamp"] = timestamp_reference.timestamp - sampled_load.set_index("timestamp", inplace=True) - sampled_load.columns = load_base.columns - load_zones_samples[:, :, sample_num] = sampled_load.values - - # Solar Calculation - sampled_hourly_solar_ratio = ratio_samples.filter(regex="solar.*hourly") - sampled_daily_solar_ratio = ratio_samples.filter(regex="solar.*daily") - sampled_solar = ( - solar_base_area.values - * (0.333 * sampled_hourly_solar_ratio.values + 0.667 * sampled_daily_solar_ratio.values) - / (df_mu_solar_hourly_unstack.iloc[:, 1:] * 0.333 + df_mu_solar_daily_unstack.iloc[:, 1:] * 0.667) - ) - sampled_solar[sampled_solar > 1] = 1 # capacity factor cannot be greater than 1 - - sampled_solar_generatorlvl = np.ones_like(solar_base.values) * solar_allocation.values - sampled_solar_generatorlvl = pd.DataFrame( - sampled_solar_generatorlvl, - index=sampled_solar.index, - columns=solar_allocation.columns, - ) - sampled_solar_generatorlvl = sampled_solar_generatorlvl.apply( - lambda x: x * sampled_solar.loc[:, str(x.name)], - axis=0, - ) - - solar_zones_samples[:, :, sample_num] = sampled_solar.values - solar_gen_samples[:, :, sample_num] = sampled_solar_generatorlvl.values - - # Wind Calculation - sampled_hourly_wind_ratio = ratio_samples.filter(regex="wind.*hourly") - sampled_daily_wind_ratio = ratio_samples.filter(regex="wind.*daily") - sampled_wind = ( - wind_base_area.values - * (0.333 * sampled_hourly_wind_ratio.values + 0.667 * sampled_daily_wind_ratio.values) - / (df_mu_wind_hourly_unstack.iloc[:, 1:] * 0.333 + df_mu_wind_daily_unstack.iloc[:, 1:] * 0.667) - ) - sampled_wind[sampled_wind > 1] = 1 # capacity factor cannot be greater than 1 - - sampled_wind_generatorlvl = np.ones_like(wind_base.values) * wind_allocation.values - sampled_wind_generatorlvl = pd.DataFrame( - sampled_wind_generatorlvl, - index=sampled_wind.index, - columns=wind_allocation.columns, - ) - sampled_wind_generatorlvl = sampled_wind_generatorlvl.apply( - lambda x: x * sampled_wind.loc[:, str(x.name)], - axis=0, - ) - - wind_zones_samples[:, :, sample_num] = sampled_wind.values - wind_gen_samples[:, :, sample_num] = sampled_wind_generatorlvl.values - -# %% Combining into Xarray Dataset -# Match headers across datasets -solar_cols = area_mapping_solar.drop("area_id", axis=1) -wind_cols = area_mapping_wind.drop("area_id", axis=1) -# remove words solar/wind/offwind from values in index to match column headers from load data. Used to match buses with eachother -solar_cols.index = solar_cols.index.str.replace(" solar", "") -wind_cols.index = wind_cols.index.str.replace(" wind", "") -wind_cols.index = wind_cols.index.str.replace(" offwind", "") - -solar_cols["np_ind_solar"] = np.arange(0, len(solar_cols)) -wind_cols["np_ind_wind"] = np.arange(0, len(wind_cols)) -df_ind_match = pd.DataFrame(load_base.columns, columns=["load"]) -df_ind_match["np_ind_load"] = np.arange(0, len(load_base.columns)) -df_ind_match = df_ind_match.merge( - solar_cols, - left_on="load", - right_index=True, - how="outer", -).merge(wind_cols, left_on="load", right_index=True, how="outer") -df_ind_match.index = np.arange(0, len(df_ind_match)) -df_bus_coordinates = df_ind_match["load"] -df_ind_match = df_ind_match.drop(columns=["load"]) -df_ind_match_combinedarr = df_ind_match.where( - df_ind_match.isnull(), - np.column_stack([df_ind_match.index.values] * 3), -) - -# Create index references for each dataset -solar_inds_combined = df_ind_match_combinedarr.np_ind_solar.values[ - ~np.isnan(df_ind_match_combinedarr.np_ind_solar.values) -].astype(int) -wind_inds_combined = df_ind_match_combinedarr.np_ind_wind.values[ - ~np.isnan(df_ind_match_combinedarr.np_ind_wind.values) -].astype(int) -load_inds_combined = df_ind_match_combinedarr.np_ind_load.values[ - ~np.isnan(df_ind_match_combinedarr.np_ind_load.values) -].astype(int) - -wind_inds_orig = df_ind_match.np_ind_wind.dropna().astype(int).values -solar_inds_orig = df_ind_match.np_ind_solar.dropna().astype(int).values -load_inds_orig = df_ind_match.np_ind_load.dropna().astype(int).values - -# Combine load solar wind data into one array for each sample -combined_gen_samples = np.zeros( - [8784, 97, 3, num_samples], -) # Dimensions are (t, node, profile type, samples) -combined_gen_samples[:, load_inds_combined, 0, :] = load_zones_samples[ - :, - load_inds_orig, - :, -] -combined_gen_samples[:, solar_inds_combined, 1, :] = solar_gen_samples[ - :, - solar_inds_orig, - :, -] -combined_gen_samples[:, wind_inds_combined, 2, :] = wind_gen_samples[ - :, - wind_inds_orig, - :, -] - -# Create xarray dataset -da_bus_data = xr.DataArray( - combined_gen_samples, - coords={ - "timestamp": timestamp_reference.timestamp.dt.to_timestamp(), - "bus": df_bus_coordinates.values, - "profile_type": ["load", "solar", "wind"], - "sample_num": np.arange(0, num_samples), - }, - dims=["timestamp", "bus", "profile_type", "sample_num"], -) - -################Combining zonal data into one array for each profile type ############################# -# remove numeric from wind and solar columns -base_zones_solar = pd.DataFrame( - solar_cols.index.str.replace("[0-9]", "").drop_duplicates(), - columns=["solar"], -) -base_zones_wind = pd.DataFrame( - wind_cols.index.str.replace("[0-9]", "").drop_duplicates(), - columns=["wind"], -) - -base_zones_wind = base_zones_wind[~base_zones_wind.wind.str.contains("SDGE")].reset_index( - drop=True, -) # temporary fix -df_zone_ind_match = base_zones_solar.reset_index(names="solar_ind").merge( - base_zones_wind.reset_index(names="wind_ind"), - left_on="solar", - right_on="wind", - how="outer", -) -# combine non nan values into new column -zone_coords = df_zone_ind_match.solar.combine_first(df_zone_ind_match.wind) -solar_zone_inds_combined = df_zone_ind_match.solar_ind.values[~np.isnan(df_zone_ind_match.solar_ind.values)].astype(int) -wind_zone_inds_combined = df_zone_ind_match.wind_ind.values[~np.isnan(df_zone_ind_match.wind_ind.values)].astype(int) -wind_zone_inds_orig = df_zone_ind_match.wind_ind.dropna().astype(int).values -solar_zone_inds_orig = df_zone_ind_match.solar_ind.dropna().astype(int).values - -combined_zone_data = np.zeros( - [8784, 22, 2, num_samples], -) # Dimensions are (t, node, profile type, samples) -combined_zone_data[:, solar_zone_inds_combined, 0, :] = solar_zones_samples[ - :, - solar_zone_inds_orig, - :, -] -combined_zone_data[:, wind_zone_inds_combined, 1, :] = wind_zones_samples[ - :, - wind_zone_inds_orig, - :, -] - -da_zone_data = xr.DataArray( - combined_zone_data, - coords={ - "timestamp": timestamp_reference.timestamp.dt.to_timestamp(), - "zone": zone_coords.values, - "profile_type": ["solar", "wind"], - "sample_num": np.arange(0, num_samples), - }, - dims=["timestamp", "zone", "profile_type", "sample_num"], -) - -da_ratios = xr.DataArray( - ratio_samples_combined, - coords={ - "timestamp": timestamp_reference.timestamp.dt.to_timestamp(), - "ratio_sample": parameters_concat.sampling_id.unique(), - "sample_num": np.arange(0, num_samples), - }, - dims=["timestamp", "ratio_sample", "sample_num"], -) - -# # Saving files -da_zone_data.to_netcdf(os.path.join(os.getcwd(), PATH_SAMPLE, "sampled_zone_data.nc")) -da_bus_data.to_netcdf(os.path.join(os.getcwd(), PATH_SAMPLE, "sampled_bus_data.nc")) -da_ratios.to_netcdf(os.path.join(os.getcwd(), PATH_SAMPLE, "sampled_ratio_data.nc")) -np.save( - os.path.join(os.getcwd(), PATH_SAMPLE, "combined_gen_samples.npy"), - combined_gen_samples, -) -np.save( - os.path.join(os.getcwd(), PATH_SAMPLE, "combined_ratio_samples.npy"), - ratio_samples_combined, -) diff --git a/workflow/scripts/log.py b/workflow/scripts/log.py index f125b16e7..b1ba7a1df 100644 --- a/workflow/scripts/log.py +++ b/workflow/scripts/log.py @@ -1,3 +1,5 @@ +"""Set up for custom logging.""" + import logging diff --git a/workflow/scripts/plot_natural_gas.py b/workflow/scripts/plot_natural_gas.py index 438843a7d..17f91c02b 100644 --- a/workflow/scripts/plot_natural_gas.py +++ b/workflow/scripts/plot_natural_gas.py @@ -1,9 +1,10 @@ +"""Plotting for natural gas networks.""" + import logging -import sys -from dataclasses import dataclass, field +from dataclasses import dataclass from functools import partial from pathlib import Path -from typing import Any, Optional +from typing import Any import matplotlib.pyplot as plt import pandas as pd @@ -30,15 +31,17 @@ @dataclass class PlottingData: + """Class for ploting sector network data.""" + name: str getter: callable plotter: callable - nice_name: Optional[str] = None - unit: Optional[str] = None - converter: Optional[float] = 1.0 - resample: Optional[str] = None # "D", "W", "12h" for example - resample_func: Optional[callable] = None # pd.Series.sum for example - plot_by_month: Optional[bool] = False # not resampled + nice_name: str | None = None + unit: str | None = None + converter: float | None = 1.0 + resample: str | None = None # "D", "W", "12h" for example + resample_func: callable | None = None # pd.Series.sum for example + plot_by_month: bool | None = False # not resampled def _get_month_name(month: Month) -> str: @@ -46,9 +49,7 @@ def _get_month_name(month: Month) -> str: def _resample_data(df: pd.DataFrame, freq: str, agg_func: callable) -> pd.DataFrame: - """ - Helper for resampling data based on input function. - """ + """Helper for resampling data based on input function.""" if df.empty: return df else: @@ -60,9 +61,7 @@ def _group_data(df: pd.DataFrame) -> pd.DataFrame: def _sum_state_data(data: dict[str, pd.DataFrame]) -> pd.DataFrame: - """ - Sums state data together. - """ + """Sums state data together.""" if not data: return pd.DataFrame() @@ -73,10 +72,7 @@ def _sum_state_data(data: dict[str, pd.DataFrame]) -> pd.DataFrame: def _sum_state_trade_data( data: dict[dict[str, pd.DataFrame]], ) -> dict[str, pd.DataFrame]: - """ - Sums state data together. - """ - + """Sums state data together.""" import_data = {} export_data = {} @@ -108,10 +104,7 @@ def plot_gas( units: str, **kwargs, ) -> tuple[plt.Figure, plt.Axes]: - """ - General gas plotting function. - """ - + """General gas plotting function.""" df = data.copy() if df.empty: @@ -148,19 +141,20 @@ def plot_gas_trade( units: str, **kwargs, ) -> tuple[plt.Figure, plt.Axes]: - """ - General gas trade plotting function. - """ - + """General gas trade plotting function.""" # periods will be the same for imports or exports periods = data["imports"].index.get_level_values("period").unique() n_rows = len(periods) - fig, axs = plt.subplots(n_rows, 2, sharey=True, figsize=(FIG_WIDTH, FIG_HEIGHT * n_rows)) + fig, axs = plt.subplots( + n_rows, + 2, + sharey=True, + figsize=(FIG_WIDTH, FIG_HEIGHT * n_rows), + ) for i, period in enumerate(periods): - # plot imports imports = data["imports"].copy() @@ -285,7 +279,6 @@ def plot_gas_trade( ] if __name__ == "__main__": - if "snakemake" not in globals(): from _helpers import mock_snakemake @@ -312,7 +305,8 @@ def plot_gas_trade( plotting_metadata = [PlottingData(**x) for x in PLOTTING_META] # hack to only read in the network once, but get images to all states independently - # ie. "interconnect}/figures/s{{simpl}}_c{{clusters}}/l{{ll}}_{{opts}}_{{sector}}/system/natural_gas/%s.png" + # ie. + # "interconnect}/figures/s{{simpl}}_c{{clusters}}/l{{ll}}_{{opts}}_{{sector}}/system/natural_gas/%s.png" # {result_name: {state: save_path.png}} expected_figures = {} @@ -323,13 +317,12 @@ def plot_gas_trade( result = p.stem # ie. 'demand' state_paths = {} for state in states: - full_path = root_path + [state] + figure_name + full_path = [*root_path, state, *figure_name] full_path = Path("/".join(full_path)) state_paths[state] = full_path expected_figures[result] = state_paths for meta in plotting_metadata: - if meta.name not in expected_figures: logger.warning(f"Not expecting {meta.name} natural gas chart") continue @@ -337,7 +330,6 @@ def plot_gas_trade( data = meta.getter(n) for state in states: - if state == "system": if _is_trade_data(data): state_data = _sum_state_trade_data(data) @@ -364,11 +356,19 @@ def plot_gas_trade( if meta.resample: title = f"{state} {meta.nice_name} resampled to {meta.resample}" if isinstance(state_data, pd.DataFrame): - state_data_resampled = _resample_data(state_data, meta.resample, meta.resample_func) + state_data_resampled = _resample_data( + state_data, + meta.resample, + meta.resample_func, + ) elif isinstance(state_data, dict): state_data_resampled = {} for k, v in state_data.items(): - state_data_resampled[k] = _resample_data(v, meta.resample, meta.resample_func) + state_data_resampled[k] = _resample_data( + v, + meta.resample, + meta.resample_func, + ) else: title = f"{state} {meta.nice_name}" state_data_resampled = state_data @@ -390,7 +390,6 @@ def plot_gas_trade( months = {month.value: _get_month_name(month) for month in Month} for month_i, month_name in months.items(): - if isinstance(state_data, pd.DataFrame): if not state_data.empty: state_data_month = state_data[state_data.index.get_level_values("timestep").month == month_i] @@ -407,7 +406,11 @@ def plot_gas_trade( fig.tight_layout() # this is ugly, but just create subdir of name and index by month - save_path = Path(expected_figures[meta.name][state].parent, meta.name, f"{month_name}.png") + save_path = Path( + expected_figures[meta.name][state].parent, + meta.name, + f"{month_name}.png", + ) if not save_path.parent.exists(): save_path.parent.mkdir(parents=True, exist_ok=True) fig.savefig(str(save_path)) diff --git a/workflow/scripts/plot_network_maps.py b/workflow/scripts/plot_network_maps.py index e862bd150..bc6eaefbe 100644 --- a/workflow/scripts/plot_network_maps.py +++ b/workflow/scripts/plot_network_maps.py @@ -32,12 +32,10 @@ import pandas as pd import pypsa import seaborn as sns -from cartopy import crs as ccrs -from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches - -logger = logging.getLogger(__name__) from _helpers import configure_logging from add_electricity import sanitize_carriers +from cartopy import crs as ccrs +from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches from summary import ( get_capacity_base, get_capacity_brownfield, @@ -45,14 +43,14 @@ get_node_emissions_timeseries, ) +logger = logging.getLogger(__name__) + # Global Plotting Settings TITLE_SIZE = 16 def get_color_palette(n: pypsa.Network) -> pd.Series: - """ - Returns colors based on nice name. - """ + """Returns colors based on nice name.""" colors = (n.carriers.reset_index().set_index("nice_name")).color # additional = { @@ -89,9 +87,7 @@ def get_color_palette(n: pypsa.Network) -> pd.Series: def get_bus_scale(interconnect: str) -> float: - """ - Scales lines based on interconnect size. - """ + """Scales lines based on interconnect size.""" if interconnect != "usa": return 1e5 else: @@ -99,9 +95,7 @@ def get_bus_scale(interconnect: str) -> float: def get_line_scale(interconnect: str) -> float: - """ - Scales lines based on interconnect size. - """ + """Scales lines based on interconnect size.""" if interconnect != "usa": return 2e3 else: @@ -135,9 +129,7 @@ def create_title(title: str, **wildcards) -> str: def remove_sector_buses(df: pd.DataFrame) -> pd.DataFrame: - """ - Removes buses for sector coupling. - """ + """Removes buses for sector coupling.""" num_levels = df.index.nlevels if num_levels > 1: @@ -160,7 +152,6 @@ def plot_emissions_map( save: str, **wildcards, ) -> None: - # get data emissions = ( @@ -227,9 +218,7 @@ def plot_capacity_map( line_cmap="viridis", line_norm=None, ) -> tuple[plt.figure, plt.axes]: - """ - Generic network plotting function for capacity pie charts at each node. - """ + """Generic network plotting function for capacity pie charts at each node.""" fig, ax = plt.subplots( figsize=(10, 10), subplot_kw={"projection": ccrs.EqualEarth(n.buses.x.mean())}, @@ -304,9 +293,7 @@ def plot_demand_map( save: str, **wildcards, ) -> None: - """ - Plots map of network nodal demand. - """ + """Plots map of network nodal demand.""" # get data bus_values = get_demand_base(n).mul(1e-3) @@ -389,9 +376,7 @@ def plot_base_capacity_map( save: str, **wildcards, ) -> None: - """ - Plots map of base network capacities. - """ + """Plots map of base network capacities.""" # get data bus_values = get_capacity_base(n) @@ -429,9 +414,7 @@ def plot_opt_capacity_map( save: str, **wildcards, ) -> None: - """ - Plots map of optimal network capacities. - """ + """Plots map of optimal network capacities.""" # get data # capacity = n.statistics()[['Optimal Capacity']] # capacity = capacity[capacity.index.get_level_values(0).isin(['Generator', 'StorageUnit'])] @@ -469,14 +452,7 @@ def plot_new_capacity_map( save: str, **wildcards, ) -> None: - """ - Plots map of new capacity. - """ - # get data - # expanded_capacity = n.statistics.expanded_capacity() - # expanded_capacity = expanded_capacity[expanded_capacity.index.get_level_values(0).isin(['Generator', 'StorageUnit'])] - # expanded_capacity.index = expanded_capacity.index.droplevel(0) - + """Plots map of new capacity.""" bus_pnom = get_capacity_base(n) bus_pnom_opt = get_capacity_brownfield(n) @@ -518,9 +494,7 @@ def plot_renewable_potential( save: str, **wildcards, ) -> None: - """ - Plots wind and solar resource potential by node. - """ + """Plots wind and solar resource potential by node.""" # get data renew = n.generators[ (n.generators.p_nom_max != np.inf) @@ -534,10 +508,6 @@ def plot_renewable_potential( bus_values = renew.groupby(["bus", "carrier"]).p_nom_max.sum() - # bus_pnom_opt = get_capacity_brownfield(n) - # bus_pnom_opt = bus_pnom_opt.loc[bus_values.index] - # print((bus_values - bus_pnom_opt)/bus_values) - # do not show lines or links line_values = pd.Series(0, index=n.lines.s_nom.index) link_values = pd.Series(0, index=n.links.p_nom.index) diff --git a/workflow/scripts/plot_sankey_carbon.py b/workflow/scripts/plot_sankey_carbon.py index 611b6a1e6..b3ce38be1 100644 --- a/workflow/scripts/plot_sankey_carbon.py +++ b/workflow/scripts/plot_sankey_carbon.py @@ -6,7 +6,6 @@ """ from pathlib import Path -from typing import Optional import pandas as pd import plotly.graph_objects as go @@ -81,7 +80,6 @@ def get_pwr_flows(n: pypsa.Network, investment_period: int, state: str) -> pd.DataFrame: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -124,7 +122,6 @@ def get_sector_flows( investment_period: int, state: str, ) -> pd.DataFrame: - weights = n.snapshot_weightings.objective if state: @@ -162,7 +159,7 @@ def get_sector_flows( def get_sankey_dataframe( n: pypsa.Network, investment_period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: dfs = [ get_pwr_flows(n, investment_period, state), @@ -181,7 +178,6 @@ def format_sankey_data( name_mapper: dict[str, str], sankey_codes: dict[str, int], ) -> pd.DataFrame: - def map_sankey_name(name: str): try: return name_mapper[name] @@ -222,10 +218,7 @@ def assign_link_color(row: pd.Series) -> str: ### if __name__ == "__main__": - if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake( "plot_sankey_energy", simpl="70", @@ -261,7 +254,6 @@ def assign_link_color(row: pd.Series) -> str: # plot state level for state in states: - df = get_sankey_dataframe( n=n, investment_period=investment_period, diff --git a/workflow/scripts/plot_sankey_energy.py b/workflow/scripts/plot_sankey_energy.py index e95093ec3..6d5e5f492 100644 --- a/workflow/scripts/plot_sankey_energy.py +++ b/workflow/scripts/plot_sankey_energy.py @@ -6,10 +6,8 @@ """ from pathlib import Path -from typing import Optional import pandas as pd -import plotly import plotly.graph_objects as go import pypsa from _helpers import configure_logging, mock_snakemake @@ -102,9 +100,8 @@ def _get_consumption_generators( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - if state: generators = _get_gens_in_state(n, state) else: @@ -123,9 +120,8 @@ def _get_consumption_generators( def _get_rejected_generators( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - if state: generators = _get_gens_in_state(n, state) else: @@ -147,7 +143,7 @@ def _get_rejected_generators( def _get_consumption_links( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: if state: links = _get_links_in_state(n, state) @@ -164,9 +160,8 @@ def _get_consumption_links( def _get_rejected_links( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - if state: links = _get_links_in_state(n, state) else: @@ -191,7 +186,7 @@ def get_electricity_consumption( n: pypsa.Network, carriers: list[str], period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: df = pd.concat( [ @@ -210,7 +205,7 @@ def get_electricity_rejected( n: pypsa.Network, carriers: list[str], period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: df = pd.concat( [ @@ -235,9 +230,8 @@ def _get_sector_consumption( sector: str, fuel: str, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if fuel == "elec": fuel = "AC" @@ -262,9 +256,8 @@ def _get_service_supply( n: pypsa.Network, sector: str, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -292,9 +285,8 @@ def _get_service_rejected( n: pypsa.Network, sector: str, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -319,9 +311,8 @@ def _get_service_rejected( def _get_industry_supply( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -340,9 +331,8 @@ def _get_industry_supply( def _get_industry_rejected( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -367,9 +357,8 @@ def _get_industry_rejected( def _get_transport_supply( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -402,9 +391,8 @@ def _get_transport_supply( def _get_transport_rejected( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> float: - if state: links_in_state = _get_links_in_state(n, state) else: @@ -439,9 +427,8 @@ def _get_transport_rejected( def get_energy_flow_res( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - elec_consumption = _get_sector_consumption(n, "res", "elec", period, state) lpg_consumption = _get_sector_consumption(n, "res", "oil", period, state) gas_consumption = _get_sector_consumption(n, "res", "gas", period, state) @@ -466,9 +453,8 @@ def get_energy_flow_res( def get_energy_flow_com( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - elec_consumption = _get_sector_consumption(n, "com", "elec", period, state) lpg_consumption = _get_sector_consumption(n, "com", "oil", period, state) gas_consumption = _get_sector_consumption(n, "com", "gas", period, state) @@ -493,9 +479,8 @@ def get_energy_flow_com( def get_energy_flow_ind( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - elec_consumption = _get_sector_consumption(n, "ind", "elec", period, state) coal_consumption = _get_sector_consumption(n, "ind", "coal", period, state) gas_consumption = _get_sector_consumption(n, "ind", "gas", period, state) @@ -520,9 +505,8 @@ def get_energy_flow_ind( def get_energy_flow_trn( n: pypsa.Network, period: int, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - elec_consumption = _get_sector_consumption(n, "trn", "elec", period, state) oil_consumption = _get_sector_consumption(n, "trn", "oil", period, state) @@ -551,7 +535,7 @@ def get_sankey_dataframe( n: pypsa.Network, investment_period: int, pwr_carriers: list[str], - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: dfs = [ get_electricity_consumption(n, pwr_carriers, investment_period, state), @@ -571,7 +555,6 @@ def format_sankey_data( name_mapper: dict[str, str], sankey_codes: dict[str, int], ) -> pd.DataFrame: - def map_sankey_name(name: str): try: return name_mapper[name] @@ -612,10 +595,7 @@ def assign_link_color(row: pd.Series) -> str: ### if __name__ == "__main__": - if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake( "plot_sankey_energy", simpl="70", @@ -653,7 +633,6 @@ def assign_link_color(row: pd.Series) -> str: # plot state level for state in states: - df = get_sankey_dataframe( n=n, pwr_carriers=power_carriers, diff --git a/workflow/scripts/plot_statistics.py b/workflow/scripts/plot_statistics.py index f9763751c..4b98a2956 100644 --- a/workflow/scripts/plot_statistics.py +++ b/workflow/scripts/plot_statistics.py @@ -39,8 +39,6 @@ import pandas as pd import pypsa import seaborn as sns - -logger = logging.getLogger(__name__) from _helpers import configure_logging from add_electricity import sanitize_carriers from plot_network_maps import get_color_palette @@ -53,6 +51,8 @@ get_tech_emissions_timeseries, ) +logger = logging.getLogger(__name__) + # Global Plotting Settings TITLE_SIZE = 16 @@ -93,7 +93,7 @@ def stacked_bar_horizons( colors_ = carriers["color"] carriers_legend = carriers # to track which carriers have non-zero values # Create subplots - planning_horizons = stats[list(stats.keys())[0]].columns + planning_horizons = stats[next(iter(stats.keys()))].columns fig, axes = plt.subplots( nrows=len(planning_horizons), ncols=1, @@ -168,9 +168,7 @@ def plot_capacity_additions_bar( save: str, **wildcards, ) -> None: - """ - Plots base capacity vs optimal capacity as a bar chart. - """ + """Plots base capacity vs optimal capacity as a bar chart.""" existing_capacity = n.generators.groupby("carrier").p_nom.sum().round(0) existing_capacity = existing_capacity.to_frame(name="Existing Capacity") storage_units = n.storage_units.groupby("carrier").p_nom.sum().round(0) @@ -181,10 +179,10 @@ def plot_capacity_additions_bar( optimal_capacity = n.statistics.optimal_capacity() optimal_capacity = optimal_capacity[optimal_capacity.index.get_level_values(0).isin(["Generator", "StorageUnit"])] optimal_capacity.index = optimal_capacity.index.droplevel(0) - optimal_capacity.reset_index(inplace=True) - optimal_capacity.rename(columns={"index": "carrier"}, inplace=True) + optimal_capacity = optimal_capacity.reset_index() + optimal_capacity = optimal_capacity.rename(columns={"index": "carrier"}) - optimal_capacity.set_index("carrier", inplace=True) + optimal_capacity = optimal_capacity.set_index("carrier") optimal_capacity.insert(0, "Existing", existing_capacity["Existing Capacity"]) optimal_capacity = optimal_capacity.fillna(0) @@ -202,9 +200,7 @@ def plot_production_bar( save: str, **wildcards, ) -> None: - """ - Plot diaptch per carrier. - """ + """Plot diaptch per carrier.""" energy_mix = n.statistics.supply().round(0) energy_mix = energy_mix[ energy_mix.index.get_level_values("component").isin( @@ -227,9 +223,7 @@ def plot_global_constraint_shadow_prices( save: str, **wildcards, ) -> None: - """ - Plots shadow prices on global constraints. - """ + """Plots shadow prices on global constraints.""" shadow_prices = n.global_constraints.mu.round(3).reset_index() # plot data @@ -252,9 +246,7 @@ def plot_global_constraint_shadow_prices( def get_currently_installed_capacity(n: pypsa.Network) -> pd.DataFrame: - """ - Returns a DataFrame with the currently installed capacity for each carrier and nerc region. - """ + """Returns a DataFrame with the currently installed capacity for each carrier and nerc region.""" n.generators["nerc_reg"] = n.generators.bus.map(n.buses.nerc_reg) existing_capacity = n.generators.groupby(["nerc_reg", "carrier"]).p_nom.sum().round(0) existing_capacity = existing_capacity.to_frame(name="Existing") @@ -271,11 +263,11 @@ def get_currently_installed_capacity(n: pypsa.Network) -> pd.DataFrame: index=existing_capacity.index, ) existing_capacity = existing_capacity.drop(columns="index") - existing_capacity.set_index(["Region", "Carrier"], inplace=True) + existing_capacity = existing_capacity.set_index(["Region", "Carrier"]) nn_carriers = existing_capacity.index.get_level_values(1).map(n.carriers.nice_name) existing_capacity = existing_capacity.droplevel(1) - existing_capacity.set_index(nn_carriers, append=True, inplace=True) + existing_capacity = existing_capacity.set_index(nn_carriers, append=True) return existing_capacity @@ -283,11 +275,13 @@ def get_statistics(n, column_name): """ Prepare the statistics data for plotting by extracting and grouping by region and carrier. - Parameters: + Parameters + ---------- - n: pypsa.Network - column_name: str, the name of the column to extract from statistics (e.g., 'Optimal Capacity', 'Supply') - Returns: + Returns + ------- - pd.DataFrame: Prepared and grouped data """ groupers = n.statistics.groupers @@ -301,11 +295,11 @@ def get_statistics(n, column_name): su_reg = su.map(n.storage_units.bus.map(n.buses.nerc_reg)).to_series() nerc_reg = pd.concat([gens_reg, su_reg]) - df.set_index(nerc_reg, append=True, inplace=True) + df = df.set_index(nerc_reg, append=True) df = df.droplevel([0, 1, 2]) - df.reset_index(inplace=True) - df.rename(columns={"level_0": "carrier", "level_1": "region"}, inplace=True) - df.set_index(["region", "carrier"], inplace=True) + df = df.reset_index() + df = df.rename(columns={"level_0": "carrier", "level_1": "region"}) + df = df.set_index(["region", "carrier"]) df_selected = df[column_name] df_selected = df_selected.groupby(df_selected.index).sum() @@ -315,7 +309,7 @@ def get_statistics(n, column_name): index=df_selected.index, ) df_selected = df_selected.drop(columns="index") - df_selected.set_index(["Region", "Carrier"], inplace=True) + df_selected = df_selected.set_index(["Region", "Carrier"]) return df_selected @@ -324,7 +318,8 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): """ Plot the data in a bar chart with subplots by region and carrier. - Parameters: + Parameters + ---------- - data: pd.DataFrame, data to plot - n: pypsa.Network - save: str, file path to save the plot @@ -338,7 +333,7 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): data = data[["Existing"] + [col for col in data.columns if col != "Existing"]] retirements = data.diff(axis=1).clip(upper=0) retirements = retirements[(retirements < -0.001).any(axis=1)] - retirements.fillna(0, inplace=True) + retirements = retirements.fillna(0) data = pd.concat([data, retirements]) data = data / 1e3 # Convert to GW @@ -352,7 +347,13 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): rows = math.ceil(num_regions / columns) # Set up the figure and axes - fig, axes = plt.subplots(rows, columns, figsize=(columns * 2.5, rows * 5), sharex=True, sharey=True) + fig, axes = plt.subplots( + rows, + columns, + figsize=(columns * 2.5, rows * 5), + sharex=True, + sharey=True, + ) # Ensure axes is a flattened array for consistent indexing if num_regions == 1: @@ -395,18 +396,14 @@ def plot_bar(data, n, save, title, ylabel, is_capacity=False): def plot_regional_capacity_additions_bar(n, save): - """ - Plot capacity evolution by NERC region in a stacked bar plot. - """ + """Plot capacity evolution by NERC region in a stacked bar plot.""" data = get_statistics(n, "Optimal Capacity") data.to_csv(f"{Path(save).parent.parent}/statistics/bar_regional_capacity.csv") plot_bar(data, n, save, "", "Capacity (GW)", is_capacity=True) def plot_regional_production_bar(n, save): - """ - Plot production evolution by NERC region in a stacked bar plot. - """ + """Plot production evolution by NERC region in a stacked bar plot.""" data = get_statistics(n, "Supply") data.to_csv(f"{Path(save).parent.parent}/statistics/bar_regional_production.csv") plot_bar(data, n, save, "", "Production (GWh)") @@ -416,9 +413,7 @@ def plot_regional_emissions_bar( n: pypsa.Network, save: str, ) -> None: - """ - PLOT OF CO2 EMISSIONS BY NERC REGION AND INVESTMENT PERIOD. - """ + """PLOT OF CO2 EMISSIONS BY NERC REGION AND INVESTMENT PERIOD.""" regional_emisssions_ts = get_node_emissions_timeseries(n).T.groupby(n.buses.nerc_reg).sum().T / 1e6 regional_emissions = ( regional_emisssions_ts.groupby(regional_emisssions_ts.index.get_level_values(0)).sum().round(3).T @@ -431,7 +426,13 @@ def plot_regional_emissions_bar( rows = math.ceil(num_regions / columns) # Set up the figure and axes - fig, axes = plt.subplots(rows, columns, figsize=(columns * 2.5, rows * 5), sharex=True, sharey=True) + fig, axes = plt.subplots( + rows, + columns, + figsize=(columns * 2.5, rows * 5), + sharex=True, + sharey=True, + ) # Ensure axes is a flattened array for consistent indexing if num_regions == 1: @@ -470,9 +471,7 @@ def plot_emissions_bar( n: pypsa.Network, save: str, ) -> None: - """ - PLOT OF CO2 EMISSIONS BY INVESTMENT PERIOD. - """ + """PLOT OF CO2 EMISSIONS BY INVESTMENT PERIOD.""" emisssions_ts = get_node_emissions_timeseries(n).T.sum().T / 1e6 emissions = emisssions_ts.groupby(emisssions_ts.index.get_level_values(0)).sum().round(3).T @@ -536,7 +535,7 @@ def plot_production_area( num_periods = len(n.investment_periods) base_plot_size = 4 - for month in ["all"] + months.to_list(): + for month in ["all", *months.to_list()]: figsize = (14, (base_plot_size * num_periods)) fig, axs = plt.subplots(figsize=figsize, ncols=1, nrows=num_periods) if not isinstance(axs, np.ndarray): # only one horizon @@ -575,9 +574,7 @@ def plot_production_area( def plot_hourly_emissions(n: pypsa.Network, save: str, **wildcards) -> None: - """ - Plots snapshot emissions by technology. - """ + """Plots snapshot emissions by technology.""" # get data emissions = get_tech_emissions_timeseries(n).mul(1e-6) # T -> MT zeros = emissions.columns[(np.abs(emissions) < 1e-7).all()] @@ -605,9 +602,7 @@ def plot_hourly_emissions(n: pypsa.Network, save: str, **wildcards) -> None: def plot_accumulated_emissions_tech(n: pypsa.Network, save: str, **wildcards) -> None: - """ - Creates area plot of accumulated emissions by technology. - """ + """Creates area plot of accumulated emissions by technology.""" # get data emissions = get_tech_emissions_timeseries(n).cumsum().mul(1e-6) # T -> MT @@ -637,9 +632,7 @@ def plot_accumulated_emissions_tech(n: pypsa.Network, save: str, **wildcards) -> def plot_accumulated_emissions(n: pypsa.Network, save: str, **wildcards) -> None: - """ - Plots accumulated emissions. - """ + """Plots accumulated emissions.""" # get data emissions = get_tech_emissions_timeseries(n).mul(1e-6).sum(axis=1) # T -> MT @@ -667,22 +660,29 @@ def plot_accumulated_emissions(n: pypsa.Network, save: str, **wildcards) -> None def plot_capacity_factor_heatmap(n: pypsa.Network, save: str, **wildcards) -> None: - """ - HEATMAP OF RENEWABLE CAPACITY FACTORS BY CARRIER. - """ - df_long = n.generators_t.p.loc[n.investment_periods[0]].melt(var_name="bus", value_name="p", ignore_index=False) + """HEATMAP OF RENEWABLE CAPACITY FACTORS BY CARRIER.""" + df_long = n.generators_t.p.loc[n.investment_periods[0]].melt( + var_name="bus", + value_name="p", + ignore_index=False, + ) df_long["region"] = df_long["bus"].map(n.generators.bus.map(n.buses.country)) df_long["carrier"] = df_long["bus"].map(n.generators.carrier) df_long["hour"] = df_long.index.hour df_long["month"] = df_long.index.month - df_long.drop(columns="bus", inplace=True) + df_long = df_long.drop(columns="bus") df_long = df_long.drop(columns="region").groupby(["carrier", "month", "hour"]).mean().reset_index() # Get unique months for separate panels unique_months = df_long["month"].unique() # Prepare figure and axes - fig, axs = plt.subplots(len(unique_months), 1, figsize=(12, len(unique_months) * 4), sharex=True) + fig, axs = plt.subplots( + len(unique_months), + 1, + figsize=(12, len(unique_months) * 4), + sharex=True, + ) # Iterate over each month to create a panel for idx, month in enumerate(sorted(unique_months)): @@ -710,7 +710,6 @@ def plot_generator_data_panel( save: str, **wildcards, ): - df_capex_expand = n.generators.loc[ n.generators.p_nom_extendable & ~n.generators.index.str.contains("existing"), :, @@ -818,9 +817,7 @@ def plot_region_lmps( save: str, **wildcards, ) -> None: - """ - Plots a box plot of LMPs for each region. - """ + """Plots a box plot of LMPs for each region.""" df_lmp = n.buses_t.marginal_price df_long = pd.melt( df_lmp.reset_index(), @@ -830,7 +827,7 @@ def plot_region_lmps( ) df_long["season"] = df_long["timestep"].dt.quarter df_long["hour"] = df_long["timestep"].dt.hour - df_long.drop(columns="timestep", inplace=True) + df_long = df_long.drop(columns="timestep") df_long["region"] = df_long.bus.map(n.buses.country) plt.figure(figsize=(10, 10)) @@ -852,7 +849,7 @@ def plot_region_lmps( plt.close() -#### Fuel costs +# Fuel costs def plot_fuel_costs( @@ -860,7 +857,6 @@ def plot_fuel_costs( save: str, **wildcards, ) -> None: - fuel_costs = get_fuel_costs(n) fuels = set(fuel_costs.index.get_level_values("carrier")) @@ -880,9 +876,9 @@ def plot_fuel_costs( legend=True, palette=color_palette, ) - axs[0].set_title("Daily Average Fuel Costs [$/MWh]"), - axs[0].set_xlabel(""), - axs[0].set_ylabel("$/MWh"), + (axs[0].set_title("Daily Average Fuel Costs [$/MWh]"),) + (axs[0].set_xlabel(""),) + (axs[0].set_ylabel("$/MWh"),) # plot bus fuel prices for each fuel for i, fuel in enumerate(fuels): @@ -895,9 +891,9 @@ def plot_fuel_costs( dashes=False, ax=axs[i + 1], ) - axs[i + 1].set_title(f"Daily Average {nice_name} Fuel Costs per Bus [$/MWh]"), - axs[i + 1].set_xlabel(""), - axs[i + 1].set_ylabel("$/MWh"), + (axs[i + 1].set_title(f"Daily Average {nice_name} Fuel Costs per Bus [$/MWh]"),) + (axs[i + 1].set_xlabel(""),) + (axs[i + 1].set_ylabel("$/MWh"),) fig.savefig(save) plt.close() @@ -941,7 +937,9 @@ def plot_fuel_costs( # Export Statistics Tables groupers = n.statistics.groupers - n.statistics(groupby=groupers.get_name_bus_and_carrier).round(3).to_csv(snakemake.output.statistics_dissaggregated) + n.statistics(groupby=groupers.get_name_bus_and_carrier).round(3).to_csv( + snakemake.output.statistics_dissaggregated, + ) n.statistics().round(2).to_csv(snakemake.output.statistics_summary) n.generators.to_csv(snakemake.output.generators) n.storage_units.to_csv(snakemake.output.storage_units) diff --git a/workflow/scripts/plot_statistics_sector.py b/workflow/scripts/plot_statistics_sector.py index ad830cb15..486c53f0c 100644 --- a/workflow/scripts/plot_statistics_sector.py +++ b/workflow/scripts/plot_statistics_sector.py @@ -1,13 +1,13 @@ -""" -Plots Sector Coupling Statistics. -""" +# ruff: noqa: D101, D102 +"""Plots Sector Coupling Statistics.""" import logging +from collections.abc import Callable from dataclasses import dataclass from enum import Enum from math import ceil from pathlib import Path -from typing import Any, Callable, Optional +from typing import Any import matplotlib.pyplot as plt import pandas as pd @@ -15,7 +15,7 @@ import seaborn as sns from _helpers import configure_logging, mock_snakemake from add_electricity import sanitize_carriers -from constants import STATE_2_CODE, Month +from constants import Month from constants_sector import ( AirTransport, AirTransportUnits, @@ -36,11 +36,8 @@ get_end_use_load_timeseries_carrier, get_hp_cop, get_load_factor_timeseries, - get_load_per_sector_per_fuel, - get_sector_production_timeseries, get_sector_production_timeseries_by_carrier, get_storage_level_timeseries_carrier, - get_transport_consumption_by_mode, ) logger = logging.getLogger(__name__) @@ -76,10 +73,7 @@ def _get_month_name(month: Month) -> str: def is_urban_rural_split(n: pypsa.Network) -> bool: - """ - Checks for urban/rural split based on com/res load names. - """ - + """Checks for urban/rural split based on com/res load names.""" com_res_load = n.loads[(n.loads.index.str.contains("res-")) | (n.loads.index.str.contains("com-"))].index.to_list() rural_urban_loads = ["res-urban-", "res-rural-", "com-urban-", "com-rural-"] @@ -91,7 +85,6 @@ def is_urban_rural_split(n: pypsa.Network) -> bool: def get_plotting_colors(n: pypsa.Network, nice_name: bool) -> dict[str, str]: - if nice_name: return n.carriers.set_index("nice_name")["color"].to_dict() else: @@ -110,11 +103,8 @@ def get_sectors(n: pypsa.Network) -> list[str]: ### -def plot_hp_cop(n: pypsa.Network, state: Optional[str] = None, **kwargs) -> tuple: - """ - Plots gshp and ashp cops. - """ - +def plot_hp_cop(n: pypsa.Network, state: str | None = None, **kwargs) -> tuple: + """Plots gshp and ashp cops.""" investment_period = n.investment_periods[0] cops = get_hp_cop(n, state).loc[investment_period] @@ -127,7 +117,6 @@ def plot_hp_cop(n: pypsa.Network, state: Optional[str] = None, **kwargs) -> tupl ) for i, hp in enumerate(["ashp", "gshp"]): - df = cops[[x for x in cops if x.endswith(hp)]] avg = df.mean(axis=1) @@ -138,7 +127,6 @@ def plot_hp_cop(n: pypsa.Network, state: Optional[str] = None, **kwargs) -> tupl palette = sns.color_palette(["lightgray"], df.shape[1]) try: - sns.lineplot( df, color="lightgray", @@ -161,18 +149,15 @@ def plot_hp_cop(n: pypsa.Network, state: Optional[str] = None, **kwargs) -> tupl def plot_sector_production_timeseries( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, remove_sns_weights: bool = True, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, - month: Optional[int] = None, + resample: str | None = None, + resample_fn: callable | None = None, + month: int | None = None, **kwargs, ) -> tuple: - """ - Plots timeseries production as area chart. - """ - + """Plots timeseries production as area chart.""" y_label = kwargs.get("ylabel", "MWh") assert sector in ("res", "com", "ind", "pwr") @@ -199,7 +184,6 @@ def plot_sector_production_timeseries( ) for row, period in enumerate(investment_periods): - df = df_all.loc[period] if month: @@ -213,9 +197,7 @@ def plot_sector_production_timeseries( df = df.rename(columns=n.carriers.nice_name.to_dict()) try: - if nrows > 1: - df.plot(kind="area", ax=axs[row], color=colors) axs[row].set_xlabel("") axs[row].set_ylabel(y_label) @@ -223,7 +205,6 @@ def plot_sector_production_timeseries( axs[row].tick_params(axis="x", labelrotation=45) else: - df.plot(kind="area", ax=axs, color=colors) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -243,18 +224,15 @@ def plot_transportation_production_timeseries( vehicle: str, # veh, air, rail, ect.. . modes: Enum, # AirTransport, RoadTransport, ect.. units: Enum, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, remove_sns_weights: bool = True, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, - month: Optional[int] = None, + resample: str | None = None, + resample_fn: callable | None = None, + month: int | None = None, **kwargs, ) -> tuple: - """ - Plots timeseries production as area chart. - """ - + """Plots timeseries production as area chart.""" assert sector == "trn" def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: @@ -293,14 +271,12 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: return fig, axs for row, period in enumerate(investment_periods): - df_veh_period = df_veh.loc[period] if month: df_veh_period = df_veh_period[df_veh_period.index.get_level_values("timestep").month == month_i].copy() for i, unit in enumerate(diff_units): - all_modes = [x.name for x in modes] modes_per_unit = [modes[x].value for x in all_modes if units[x].value == unit] @@ -310,16 +286,13 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df = df.rename(columns=n.carriers.nice_name.to_dict()) try: - if nrows > 1: - df.plot(kind="area", ax=axs[row + i], color=colors) axs[row + i].set_xlabel("") axs[row + i].set_ylabel(f"{unit}") axs[row + i].tick_params(axis="x", labelrotation=45) else: - df.plot(kind="area", ax=axs, color=colors) axs.set_xlabel("") axs.set_ylabel(f"{unit}") @@ -336,14 +309,11 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: def plot_sector_production( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Plots model period production as bar chart. - """ - + """Plots model period production as bar chart.""" y_label = kwargs.get("ylabel", "MWh") assert sector in ("res", "com", "ind", "pwr", "trn") @@ -361,7 +331,6 @@ def plot_sector_production( df_all = get_sector_production_timeseries_by_carrier(n, sector=sector, state=state) for row, period in enumerate(investment_periods): - df = df_all.loc[period].sum(axis=0) if df.empty: @@ -372,9 +341,7 @@ def plot_sector_production( df.index = df.index.map(n.carriers.nice_name) try: - if nrows > 1: - df.plot.bar(ax=axs[row]) axs[row].set_xlabel("") axs[row].set_ylabel(y_label) @@ -382,7 +349,6 @@ def plot_sector_production( axs[row].tick_params(axis="x", labelrotation=45) else: - df.plot.bar(ax=axs) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -396,13 +362,10 @@ def plot_sector_production( def plot_sector_emissions( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, **kwargws, ) -> tuple: - """ - Plots model period emissions by sector. - """ - + """Plots model period emissions by sector.""" investment_period = n.investment_periods[0] sectors = ("res", "com", "ind", "trn", "pwr", "ch4") @@ -410,7 +373,6 @@ def plot_sector_emissions( data = [] for sector in sectors: - df = get_emission_timeseries_by_sector(n, sector, state=state) if df.empty: @@ -446,13 +408,10 @@ def plot_sector_emissions( def plot_state_emissions( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, **kwargws, ) -> tuple: - """ - Plots stacked bar plot of state level emissions. - """ - + """Plots stacked bar plot of state level emissions.""" investment_period = n.investment_periods[0] fig, axs = plt.subplots( @@ -478,14 +437,11 @@ def plot_state_emissions( def plot_capacity_by_carrier( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Bar plot of capacity by carrier. - """ - + """Bar plot of capacity by carrier.""" investment_periods = n.investment_periods nrows = len(investment_periods) @@ -500,7 +456,6 @@ def plot_capacity_by_carrier( df_all = df_all.reset_index()[["carrier", "p_nom_opt"]] for row, _ in enumerate(investment_periods): - df = df_all.copy() if df.empty: @@ -513,9 +468,7 @@ def plot_capacity_by_carrier( df = df.groupby("carrier").sum() try: - if nrows > 1: - df.plot(kind="bar", stacked=False, ax=axs[row]) axs[row].set_xlabel("") axs[row].set_ylabel("Capacity (MW)") @@ -523,7 +476,6 @@ def plot_capacity_by_carrier( axs[row].tick_params(axis="x", labelrotation=45) else: - df.plot(kind="bar", stacked=False, ax=axs) axs.set_xlabel("") axs.set_ylabel("Capacity (MW)") @@ -542,13 +494,11 @@ def plot_transportation_capacity_by_carrier( vehicle: str, # veh, air, rail, ect.. . modes: Enum, # AirTransport, RoadTransport, ect.. units: Enum, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Bar plot of capacity by carrier. - """ + """Bar plot of capacity by carrier.""" def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df["vehicle"] = df.index.get_level_values("carrier").map( @@ -577,7 +527,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: ) for row, _ in enumerate(investment_periods): - df = df_veh.copy() if df.empty: @@ -587,7 +536,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df["mode"] = df.carrier.map(lambda x: x.split("-")[-1]) for i, unit in enumerate(diff_units): - all_modes = [x.name for x in modes] modes_per_unit = [modes[x].value for x in all_modes if units[x].value == unit] @@ -599,9 +547,7 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df_mode = df_mode.groupby("carrier").sum() try: - if nrows > 1: - df_mode.plot(kind="bar", stacked=False, ax=axs[row + i]) axs[row + i].set_xlabel("") axs[row + i].set_ylabel(f"Capacity ({unit})") @@ -609,7 +555,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: axs[row + i].tick_params(axis="x", labelrotation=45) else: - df_mode.plot(kind="bar", stacked=False, ax=axs) axs.set_xlabel("") axs.set_ylabel(f"Capacity ({unit})") @@ -626,14 +571,11 @@ def plot_capacity_per_node( n: pypsa.Network, sharey: bool = True, percentage: bool = True, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Plots capacity percentage per node. - """ - + """Plots capacity percentage per node.""" sectors = get_sectors(n) nrows = len(sectors) @@ -651,7 +593,6 @@ def plot_capacity_per_node( colors = get_plotting_colors(n, nice_name) for i, sector in enumerate(sectors): - df = get_capacity_per_node(n, sector=sector, state=state) df = df.reset_index()[["node", "carrier", data_col]] @@ -661,16 +602,13 @@ def plot_capacity_per_node( df = df.pivot(columns="carrier", index="node", values=data_col) try: - if nrows > 1: - df.plot(kind="bar", stacked=True, ax=axs[i], color=colors) axs[i].set_xlabel("") axs[i].set_ylabel(y_label) axs[i].set_title(f"{sector} Capacity") else: - df.plot(kind="bar", stacked=True, ax=axs, color=colors) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -685,14 +623,11 @@ def plot_capacity_per_node( def plot_capacity_brownfield( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Plots old and new capacity at a state level by carrier. - """ - + """Plots old and new capacity at a state level by carrier.""" investment_periods = n.investment_periods nrows = len(investment_periods) @@ -706,7 +641,6 @@ def plot_capacity_brownfield( y_label = "Capacity (MW)" for row, _ in enumerate(investment_periods): - df = get_capacity_per_node(n, sector, state) if nice_name: @@ -717,9 +651,7 @@ def plot_capacity_brownfield( df = df.reset_index()[["carrier", "existing", "new"]].groupby("carrier").sum() try: - if nrows > 1: - df.plot(kind="bar", ax=axs[row]) axs[row].set_xlabel("") axs[row].set_ylabel(y_label) @@ -727,7 +659,6 @@ def plot_capacity_brownfield( axs.tick_params(axis="x", labelrotation=45) else: - df.plot(kind="bar", ax=axs) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -746,13 +677,11 @@ def plot_transportation_capacity_brownfield( vehicle: str, # veh, air, rail, ect.. . modes: Enum, # AirTransport, RoadTransport, ect.. units: Enum, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Plots old and new capacity at a state level by carrier. - """ + """Plots old and new capacity at a state level by carrier.""" def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df["vehicle"] = df.index.get_level_values("carrier").map( @@ -781,7 +710,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: ) for row, _ in enumerate(investment_periods): - df = df_veh.copy() if df.empty: @@ -791,7 +719,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df["mode"] = df.carrier.map(lambda x: x.split("-")[-1]) for i, unit in enumerate(diff_units): - all_modes = [x.name for x in modes] modes_per_unit = [modes[x].value for x in all_modes if units[x].value == unit] @@ -803,9 +730,7 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: df_mode = df_mode.groupby("carrier").sum() try: - if nrows > 1: - df_mode.plot(kind="bar", stacked=False, ax=axs[row + i]) axs[row + i].set_xlabel("") axs[row + i].set_ylabel(f"Capacity ({unit})") @@ -813,7 +738,6 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: axs[row + i].tick_params(axis="x", labelrotation=45) else: - df_mode.plot(kind="bar", stacked=False, ax=axs) axs.set_xlabel("") axs.set_ylabel(f"Capacity ({unit})") @@ -829,13 +753,10 @@ def _filter_vehicle_type(df: pd.DataFrame, vehicle: str) -> pd.DataFrame: def plot_sector_load_factor_timeseries( n: pypsa.Network, sharey: bool = True, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - """ - Plots timeseries of load factor resampled to days. - """ - + """Plots timeseries of load factor resampled to days.""" investment_period = n.investment_periods[0] sectors = ("res", "com", "ind") @@ -853,23 +774,19 @@ def plot_sector_load_factor_timeseries( col = 0 for i, sector in enumerate(sectors): - row = i // 2 col = i % 2 df = get_load_factor_timeseries(n, sector, state=state).loc[investment_period].resample("d").mean().dropna() try: - if nrows > 1: - df.plot(ax=axs[row, col]) axs[row, col].set_xlabel("") axs[row, col].set_ylabel("Load Factor (%)") axs[row, col].set_title(f"{sector}") else: - df.plot(ax=axs[i]) axs[i].set_xlabel("") axs[i].set_ylabel("Load Factor (%)") @@ -884,14 +801,11 @@ def plot_sector_load_factor_timeseries( def plot_sector_load_factor_boxplot( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - """ - Plots boxplot of load factors. - """ - + """Plots boxplot of load factors.""" assert sector in ("res", "com", "ind") investment_periods = n.investment_periods @@ -907,7 +821,6 @@ def plot_sector_load_factor_boxplot( df_all = get_load_factor_timeseries(n, sector, state=state) for row, period in enumerate(investment_periods): - df = df_all.loc[period] if nice_name: @@ -917,9 +830,7 @@ def plot_sector_load_factor_boxplot( ) try: - if nrows > 1: - sns.boxplot(df, ax=axs[row]) axs[row].set_xlabel("") axs[row].set_ylabel("Load Factor (%)") @@ -927,7 +838,6 @@ def plot_sector_load_factor_boxplot( axs[row].tick_params(axis="x", labelrotation=45) else: - sns.boxplot(df, ax=axs) axs.set_xlabel("") axs.set_ylabel("Load Factor (%)") @@ -944,10 +854,9 @@ def plot_sector_load_timeseries( n: pypsa.Network, sector: str, sharey: bool = False, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - investment_period = n.investment_periods[0] df = get_end_use_load_timeseries(n, sector, sns_weight=False, state=state).loc[investment_period].T @@ -967,19 +876,17 @@ def plot_sector_load_timeseries( ) for i, load in enumerate(loads): - - l = df[load] + load_ = df[load] # sns.lineplot(l, ax=axs[i], legend=False) - avg = l.mean(axis=1) + avg = load_.mean(axis=1) # palette = sns.color_palette(["lightgray"]) try: - sns.lineplot( - l, + load_, color="lightgray", legend=False, # palette=palette, @@ -1000,10 +907,9 @@ def plot_sector_load_timeseries( def plot_sector_load_bar( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - investment_period = n.investment_periods[0] sectors = ("res", "com", "ind") @@ -1022,7 +928,6 @@ def plot_sector_load_bar( title = state if state else "System" for i, sector in enumerate(sectors): - row = i // 2 col = i % 2 @@ -1033,20 +938,17 @@ def plot_sector_load_bar( continue try: - if nrows > 1: - df.T.plot.bar(ax=axs[row, col]) axs[row, col].set_xlabel("") - axs[row, col].set_ylabel(f"Load (MWh)") + axs[row, col].set_ylabel("Load (MWh)") axs[row, col].set_title(f"{title} {SECTOR_MAPPER[sector]}") axs[row, col].tick_params(axis="x", labelrotation=0) else: - df.T.plot.bar(ax=axs[i]) axs[i].set_xlabel("") - axs[i].set_ylabel(f"Load (MWh)") + axs[i].set_ylabel("Load (MWh)") axs[i].set_title(f"{title} {SECTOR_MAPPER[sector]}") axs[i].tick_params(axis="x", labelrotation=0) @@ -1059,14 +961,13 @@ def plot_sector_load_bar( def plot_sector_dr_timeseries( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, - month: Optional[int] = None, + state: str | None = None, + nice_name: bool | None = True, + resample: str | None = None, + resample_fn: callable | None = None, + month: int | None = None, **kwargs, ) -> tuple: - e = get_storage_level_timeseries_carrier( n, sector, @@ -1090,7 +991,6 @@ def plot_sector_dr_timeseries( ) for row, period in enumerate(investment_periods): - df = e.loc[period] if month: @@ -1104,9 +1004,7 @@ def plot_sector_dr_timeseries( df = df.rename(columns=n.carriers.nice_name.to_dict()) try: - if nrows > 1: - df.plot.line(ax=axs[row]) axs[row].set_xlabel("") axs[row].set_ylabel(y_label) @@ -1114,7 +1012,6 @@ def plot_sector_dr_timeseries( axs[row].tick_params(axis="x", labelrotation=45) else: - df.plot.line(ax=axs) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -1129,11 +1026,10 @@ def plot_sector_dr_timeseries( def plot_consumption( n: pypsa.Network, sector: str, - state: Optional[str] = None, - nice_name: Optional[bool] = True, + state: str | None = None, + nice_name: bool | None = True, **kwargs, ) -> tuple: - assert sector in ("res", "com", "ind", "trn") investment_periods = n.investment_periods @@ -1151,7 +1047,6 @@ def plot_consumption( df_all = get_end_use_consumption(n, sector, state) for row, period in enumerate(investment_periods): - df = df_all.loc[period] if nice_name: @@ -1160,16 +1055,13 @@ def plot_consumption( df = df.sum(axis=0).to_frame() try: - if nrows > 1: - df.plot(kind="bar", ax=axs[row], legend=False) axs[row].set_xlabel("") axs[row].set_ylabel(y_label) axs[row].tick_params(axis="x", labelrotation=45) else: - df.plot(kind="bar", ax=axs) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -1191,13 +1083,10 @@ def save_fig( n: pypsa.Network, save: str, title: str, - wildcards: dict[str, Any] = None, + wildcards: dict[str, Any] | None = None, **kwargs, ) -> None: - """ - Saves the result figure. - """ - + """Saves the result figure.""" fig, _ = fn(n, **kwargs) if not wildcards: @@ -1250,10 +1139,10 @@ def save_fig( class PlottingData: name: str # snakemake name fn: callable - sector: Optional[str] = None # None = 'system' - fn_kwargs: Optional[dict[str, Any]] = None - nice_name: Optional[str] = None - plot_by_month: Optional[bool] = False + sector: str | None = None # None = 'system' + fn_kwargs: dict[str, Any] | None = None + nice_name: str | None = None + plot_by_month: bool | None = False EMISSIONS_PLOTS = [ @@ -1561,8 +1450,6 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: if __name__ == "__main__": if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake( "plot_sector_production", simpl="70", @@ -1606,7 +1493,6 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: # plot at system level for plot_data in plotting_data: - fn = plot_data.fn title = plot_data.nice_name if plot_data.nice_name else plot_data.name @@ -1649,7 +1535,6 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: months = {month.value: _get_month_name(month) for month in Month} for month_i, month_name in months.items(): - if plot_data.sector: f_path = Path( results_dir, @@ -1678,12 +1563,10 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: # plot each state for plot_data in plotting_data: - fn = plot_data.fn title = plot_data.nice_name if plot_data.nice_name else plot_data.name for state in states: - if plot_data.fn_kwargs: fn_kwargs = plot_data.fn_kwargs else: @@ -1724,7 +1607,6 @@ def _initialize_metadata(data: dict[str, Any]) -> list[PlottingData]: months = {month.value: _get_month_name(month) for month in Month} for month_i, month_name in months.items(): - if plot_data.sector: f_path = Path( results_dir, diff --git a/workflow/scripts/plot_validation_production.py b/workflow/scripts/plot_validation_production.py index 92488f9d1..2ea97b595 100644 --- a/workflow/scripts/plot_validation_production.py +++ b/workflow/scripts/plot_validation_production.py @@ -1,3 +1,5 @@ +"""Plotting script for electricity sector validation studies.""" + import logging from collections import OrderedDict from pathlib import Path @@ -8,14 +10,12 @@ import pandas as pd import pypsa import seaborn as sns - -logger = logging.getLogger(__name__) from _helpers import configure_logging, get_snapshots from constants import ( + CODE_2_STATE, EIA_930_REGION_MAPPER, EIA_BA_2_REGION, EIA_FUEL_MAPPER_2, - STATE_2_CODE, ) from eia import Emissions from plot_network_maps import ( @@ -27,6 +27,8 @@ from plot_statistics import plot_fuel_costs, plot_generator_data_panel, plot_region_lmps from summary import get_node_emissions_timeseries +logger = logging.getLogger(__name__) + sns.set_theme("paper", style="whitegrid") DPI = 300 @@ -65,48 +67,6 @@ def get_regions(n): return regions -def get_state_generation_mix(n: pypsa.Network, var="p"): - storage_devices = n.storage_units.copy() - storage_devices["state"] = storage_devices.bus.map(n.buses.reeds_state) - storage_devices["state_carrier"] = storage_devices["state"] + "_" + storage_devices["carrier"] - # Group by state and carrier - storage = n.storage_units_t[var].clip(lower=0).copy() - storage = storage.T.groupby(storage_devices["state_carrier"]).sum().T - storage.index = storage.index.droplevel(1) - storage = storage.groupby("period").sum().T.mul(1e-3) # convert to GW - - gens = n.generators.copy() - gens["state"] = gens.bus.map(n.buses.reeds_state) - gens["state_carrier"] = gens["state"] + "_" + gens["carrier"] - # Group by state and carrier - generation = n.generators_t[var].copy() - generation = generation.T.groupby(gens["state_carrier"]).sum().T - generation.index = generation.index.droplevel(1) - generation = generation.groupby("period").sum().T - generation = generation / 1e3 # convert to GWh - - production = pd.concat([generation, storage], axis=0) - production = production.reset_index() - production.columns = ["state_carrier", "generation"] - production["state"] = production["state_carrier"].str.split("_").str[0] - production["carrier"] = production["state_carrier"].str.split("_").str[1:].str.join("_") - production_pivot = production.pivot( - index="state", - columns="carrier", - values="generation", - ) - if "load" in production_pivot.columns: - production_pivot.load = production_pivot.load.mul(1e-3) - return production_pivot - - -def get_state_loads(n: pypsa.Network): - loads = n.loads_t.p - n.loads["state"] = n.loads.bus.map(n.buses.reeds_state) - loads = loads.T.groupby(n.loads.state).sum().T - loads = loads / 1e3 # convert to GW - - def add_missing_carriers(df1, df2): # Create new columns for historic for missing carriers in optimized for carrier in df1.columns: @@ -126,9 +86,7 @@ def plot_timeseries_comparison( title="Electricity Production by Carrier", **wildcards, ): - """ - plots a stacked plot for seasonal production for snapshots: January 2 - December 30 (inclusive) - """ + """Plots a stacked plot for seasonal production for snapshots: January 2 - December 30 (inclusive).""" historic, optimized = add_missing_carriers(historic, optimized) kwargs = dict(color=colors, ylabel="Production [GW]", xlabel="", linewidth=0) @@ -174,10 +132,10 @@ def plot_timeseries_comparison( top=max(upper_lim, diff_lim_upper), ) - h, l = axes[0].get_legend_handles_labels() + h, label_ = axes[0].get_legend_handles_labels() fig.legend( h[::-1], - l[::-1], + label_[::-1], loc="lower right", bbox_to_anchor=(1, 0), ncol=1, @@ -210,9 +168,7 @@ def plot_bar_carrier_production( def create_optimized_by_carrier(n, order, region_buses=None): - """ - Create a DataFrame from the model output/optimized. - """ + """Create a DataFrame from the model output/optimized.""" if region_buses is not None: gen_p = n.generators_t["p"].loc[ :, @@ -327,9 +283,7 @@ def plot_regional_comparisons( order=None, **wildcards, ): - """ - Plot regional comparison of results. - """ + """Plot regional comparison of results.""" Path.mkdir( Path(snakemake.output[0]).parents[0] / "regional_timeseries", exist_ok=True, @@ -337,7 +291,6 @@ def plot_regional_comparisons( buses = n.buses.copy() if snakemake.config["model_topology"]["topological_boundaries"] == "reeds_zone": - regions = n.buses.reeds_ba.unique() regions = list(OrderedDict.fromkeys(regions)) buses["region"] = buses.reeds_ba @@ -418,13 +371,13 @@ def plot_load_shedding_map( regions: gpd.GeoDataFrame, **wildcards, ): - load_curtailment = n.generators_t.p.filter(regex="^(.*load).*$") load_curtailment_sum = load_curtailment.sum() / 1e3 # convert to MW - # split the generator name into a multi index where the first level is the bus and the second level is the carrier name + # split the generator name into a multi index where the first level is the + # bus and the second level is the carrier name multi_index = load_curtailment_sum.index.str.rsplit(" ", n=1, expand=True) - multi_index.rename({0: "bus", 1: "carrier"}, inplace=True) + multi_index = multi_index.rename({0: "bus", 1: "carrier"}) load_curtailment_sum.index = multi_index bus_values = load_curtailment_sum @@ -510,7 +463,12 @@ def plot_generator_cost_stack( # Get unique NERC regions unique_regions = df_sorted["nerc_reg"].unique() - fig, axs = plt.subplots(len(unique_regions), 1, figsize=(10, 5 * len(unique_regions)), constrained_layout=True) + fig, axs = plt.subplots( + len(unique_regions), + 1, + figsize=(10, 5 * len(unique_regions)), + constrained_layout=True, + ) colors = n.carriers.color.to_dict() @@ -565,9 +523,7 @@ def plot_state_emissions_historical_bar( eia_api: str, **wildcards, ) -> None: - """ - Compares regional annual emissions to the year. - """ + """Compares regional annual emissions to the year.""" year = snapshots[0].year sectors = wildcards["sector"].split("-") @@ -606,11 +562,10 @@ def plot_state_emissions_historical_bar( region_mapper = n.buses[["country", "reeds_state"]].drop_duplicates().set_index("country")["reeds_state"].to_dict() optimized["region"] = optimized.index.map(region_mapper) optimized = optimized.groupby("region").sum() - CODE_2_STATE = {v: k for k, v in STATE_2_CODE.items()} optimized.index = optimized.index.map(CODE_2_STATE) optimized.index.name = "state" - optimized.sort_index(inplace=True) + optimized = optimized.sort_index() historical = historical.loc[optimized.index] final = optimized.join(historical).reset_index() @@ -644,9 +599,7 @@ def plot_ba_emissions_historical_bar( eia_api: str, **wildcards, ) -> None: - """ - Compares regional annual emissions to the year. - """ + """Compares regional annual emissions to the year.""" # year = snapshots[0].year # sectors = wildcards["sector"].split("-") @@ -660,9 +613,7 @@ def plot_ba_emissions_historical_bar( None, region, emissions=True, - ).sum( - axis=0, - )["Net Generation"] + ).sum(axis=0)["Net Generation"] region_em = region_em.sum() / 1e9 historical.loc[region] = region_em historical.name = "Historical" @@ -676,9 +627,8 @@ def plot_ba_emissions_historical_bar( if snakemake.config["model_topology"]["topological_boundaries"] == "balancing_area": optimized.loc["CISO"] = optimized.loc[["CISO-PGAE", "CISO-SCE", "CISO-SDGE", "CISO-VEA"]].sum() - optimized.drop( + optimized = optimized.drop( index=["CISO-PGAE", "CISO-SCE", "CISO-SDGE", "CISO-VEA"], - inplace=True, ) elif snakemake.config["model_topology"]["topological_boundaries"] == "reeds_zone": region_mapper = n.buses[["country", "reeds_ba"]].drop_duplicates().set_index("country")["reeds_ba"].to_dict() @@ -687,7 +637,7 @@ def plot_ba_emissions_historical_bar( optimized = optimized.groupby("region").sum() optimized.index.name = "country" - optimized.sort_index(inplace=True) + optimized = optimized.sort_index() historical = historical.loc[optimized.index] final = optimized.join(historical).reset_index() @@ -734,13 +684,6 @@ def get_state_generation_mix(n: pypsa.Network, var="p"): return generation_pivot -def get_state_loads(n: pypsa.Network): - loads = n.loads_t.p - n.loads["state"] = n.loads.bus.map(n.buses.reeds_state) - loads = loads.T.groupby(n.loads.state).sum().T - loads = loads / 1e3 # convert to GW - - def plot_state_generation_mix( n: pypsa.Network, snapshots: pd.date_range, @@ -749,9 +692,7 @@ def plot_state_generation_mix( save_carrier: str, **wildcards, ): - """ - Creates a stacked bar chart for each state's generation mix. - """ + """Creates a stacked bar chart for each state's generation mix.""" year = snapshots[0].year optimized = get_state_generation_mix(n) @@ -761,13 +702,12 @@ def plot_state_generation_mix( historical_gen = historical_gen[historical_gen.YEAR == year] historical_gen = historical_gen[historical_gen["TYPE OF PRODUCER"] == "Total Electric Power Industry"] historical_gen = historical_gen[historical_gen["ENERGY SOURCE"] != "Total"] - historical_gen.drop(columns=["YEAR", "TYPE OF PRODUCER"], inplace=True) - historical_gen.rename( + historical_gen = historical_gen.drop(columns=["YEAR", "TYPE OF PRODUCER"]) + historical_gen = historical_gen.rename( columns={ "ENERGY SOURCE": "carrier", "GENERATION (Megawatthours)": "Historical", }, - inplace=True, ) historical_gen["carrier"] = historical_gen.carrier.map(EIA_FUEL_MAPPER_2) historical_gen.carrier = historical_gen.carrier.str.lower() @@ -824,9 +764,7 @@ def plot_state_generation_capacities( save: str, **wildcards, ): - """ - Creates a stacked bar chart for each state's generation mix. - """ + """Creates a stacked bar chart for each state's generation mix.""" n.generators["state"] = n.generators.bus.map(n.buses.reeds_state) n.generators["state_carrier"] = n.generators["state"] + "_" + n.generators["carrier"] @@ -842,7 +780,7 @@ def plot_state_generation_capacities( columns="carrier", values="capacity", ) - generation_pivot.drop(columns=["load"], inplace=True) + generation_pivot = generation_pivot.drop(columns=["load"]) # Create Stacked Bar Plot for each State's Generation Mix colors = n.carriers.color.to_dict() @@ -861,8 +799,8 @@ def plot_lmp_distribution_comparison( **wildcards, ): lmps = n.buses_t.marginal_price.copy() - ISOs = ["CISO", "MISO", "ERCO", "ISNE", "NYIS", "PJM", "SWPP"] - iso_buses = n.buses[n.buses.reeds_ba.isin(ISOs)] + iso_list = ["CISO", "MISO", "ERCO", "ISNE", "NYIS", "PJM", "SWPP"] + iso_buses = n.buses[n.buses.reeds_ba.isin(iso_list)] lmps_iso = lmps.loc[:, iso_buses.index] lmps_iso.index = lmps_iso.index.get_level_values(1) @@ -918,7 +856,7 @@ def main(snakemake): ge_all = ge_all.loc[ge_all.period.isin(snapshots), :] ge_all = ge_all.rename(columns=lambda x: x[2:] if x.startswith("E_") else x) - ge_all.set_index("period", inplace=True) + ge_all = ge_all.set_index("period") ge_all.columns = pd.MultiIndex.from_tuples( ge_all.columns.str.split("_", expand=True).tolist(), ) @@ -944,7 +882,7 @@ def main(snakemake): ge_co2.period = pd.to_datetime(ge_co2.period) ge_co2 = ge_co2.loc[ge_co2.period.isin(snapshots), :] ge_co2 = ge_co2.rename(columns=lambda x: x[4:] if x.startswith("CO2_") else x) - ge_co2.set_index("period", inplace=True) + ge_co2 = ge_co2.set_index("period") ge_co2.columns = pd.MultiIndex.from_tuples( ge_co2.columns.str.split("_", expand=True).tolist(), ) diff --git a/workflow/scripts/plot_validation_sector.py b/workflow/scripts/plot_validation_sector.py index 423899a98..928df6062 100644 --- a/workflow/scripts/plot_validation_sector.py +++ b/workflow/scripts/plot_validation_sector.py @@ -1,21 +1,18 @@ -""" -Plots sector validation plots. -""" +# ruff: noqa: D100, D101 +"""Plots sector validation plots.""" import logging +from collections.abc import Callable from dataclasses import dataclass -from enum import Enum -from math import ceil from pathlib import Path -from typing import Any, Callable, Optional +from typing import Any import matplotlib.pyplot as plt import pandas as pd import pypsa -import seaborn as sns from _helpers import configure_logging, mock_snakemake from add_electricity import sanitize_carriers -from constants import STATE_2_CODE, Month +from constants import STATE_2_CODE from plot_statistics import create_title from summary_natural_gas import get_historical_ng_prices, get_ng_price from summary_sector import ( @@ -42,7 +39,6 @@ "pwr": "power", "ind": "industrial", "trn": "transport", - "pwr": "power", "ch4": "methane", } @@ -54,22 +50,17 @@ def percent_difference(col_1: pd.Series, col_2: pd.Series) -> pd.Series: - """ - Calculates percent difference between two columns of numbers. - """ + """Calculates percent difference between two columns of numbers.""" return abs(col_1 - col_2).div((col_1 + col_2).div(2)).mul(100) def plot_sector_emissions_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - """ - Plots state by state sector emission comparison. - """ - + """Plots state by state sector emission comparison.""" investment_period = n.investment_periods[0] historical = get_historical_emissions( @@ -98,7 +89,7 @@ def plot_sector_emissions_validation( historical = historical.T for sector in modelled.columns: - if not sector in historical.columns: + if sector not in historical.columns: historical[sector] = 0 assert set(historical.columns) == set(modelled.columns) @@ -106,23 +97,20 @@ def plot_sector_emissions_validation( historical = historical.T if state: # plot at state level - historical = historical[state].to_frame("Actual") modelled = modelled[state].to_frame("Modelled") else: # plot at system level - historical = historical[modelled.columns].sum(axis=1).to_frame("Actual") modelled = modelled.sum(axis=1).to_frame("Modelled") df = historical.join(modelled) try: - df.plot.bar(ax=axs, stacked=False) axs.set_xlabel("") axs.set_ylabel("Emissions (MT)") - axs.set_title(f"Emissions by Sector") + axs.set_title("Emissions by Sector") axs.tick_params(axis="x", labelrotation=0) except TypeError: # no numeric data to plot @@ -134,13 +122,10 @@ def plot_sector_emissions_validation( def plot_state_emissions_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - """ - Plots total state emission comparison. - """ - + """Plots total state emission comparison.""" investment_period = n.investment_periods[0] historical = get_historical_emissions( @@ -192,10 +177,7 @@ def plot_system_emissions_validation_by_state( eia_api: str, **kwargs, ) -> tuple: - """ - Plots all states modelled and historcal. - """ - + """Plots all states modelled and historcal.""" investment_period = n.investment_periods[0] historical = get_historical_emissions( @@ -233,13 +215,10 @@ def plot_system_emissions_validation_by_state( def plot_sector_consumption_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - """ - Plots sector energy consumption comparison. - """ - + """Plots sector energy consumption comparison.""" investment_period = n.investment_periods[0] historical = get_historical_end_use_consumption( @@ -281,9 +260,7 @@ def plot_sector_consumption_validation( def _get_annual_generation(n: pypsa.Network, year: int, state) -> pd.DataFrame: - """ - Only for comparing agaist EIA data. - """ + """Only for comparing agaist EIA data.""" df = get_power_production_timeseries(n, False, state) df = df.T df.index = df.index.map(pd.concat([n.links.carrier, n.generators.carrier])) @@ -300,10 +277,9 @@ def _get_annual_generation(n: pypsa.Network, year: int, state) -> pd.DataFrame: def plot_power_generation_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - investment_period = n.investment_periods[0] modelled = _get_annual_generation(n, investment_period, state) @@ -340,10 +316,9 @@ def plot_power_generation_validation( def plot_ng_price_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - investment_period = n.investment_periods[0] modelled = get_ng_price(n) @@ -407,10 +382,9 @@ def plot_ng_price_validation( def plot_transportation_by_mode_validation( n: pypsa.Network, eia_api: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> tuple: - # to pull in from snakemake inputs transport_ratios = { "Alabama": 2.05, @@ -504,10 +478,9 @@ def plot_transportation_by_mode_validation( data = data.join(modelled.to_frame(name="modelled")).fillna(0) try: - data.plot.bar(ax=axs) axs.set_xlabel("") - axs.set_ylabel(f"Energy Consumption by Transport Mode (MWh)") + axs.set_ylabel("Energy Consumption by Transport Mode (MWh)") axs.tick_params(axis="x", labelrotation=45) except TypeError: # no numeric data to plot @@ -521,7 +494,6 @@ def plot_system_consumption_validation_by_state( eia_api: str, **kwargs, ) -> tuple: - states = [x for x in n.buses.STATE.unique() if x] # remove non-classified buses sectors = ("res", "com", "ind", "trn") @@ -537,7 +509,6 @@ def plot_system_consumption_validation_by_state( y_label = "Energy (MWh)" for i, sector in enumerate(sectors): - historical = get_historical_end_use_consumption( SECTOR_MAPPER[sector], 2020, @@ -556,16 +527,13 @@ def plot_system_consumption_validation_by_state( ) try: - if nrows > 1: - df.plot(kind="bar", ax=axs[i]) axs[i].set_xlabel("") axs[i].set_ylabel(y_label) axs[i].set_title(f"{sector} Production") else: - df.plot(kind="bar", ax=axs) axs.set_xlabel("") axs.set_ylabel(y_label) @@ -582,9 +550,9 @@ class PlottingData: name: str # snakemake name fn: callable system_only: bool - sector: Optional[str] = None # None = 'system' - fn_kwargs: Optional[dict[str, Any]] = None - nice_name: Optional[str] = None + sector: str | None = None # None = 'system' + fn_kwargs: dict[str, Any] | None = None + nice_name: str | None = None VALIDATION_PLOTS = [ @@ -648,13 +616,10 @@ def save_fig( n: pypsa.Network, save: str, title: str, - wildcards: dict[str, Any] = None, + wildcards: dict[str, Any] | None = None, **kwargs, ) -> None: - """ - Saves the result figure. - """ - + """Saves the result figure.""" fig, _ = fn(n, **kwargs) if not wildcards: @@ -673,8 +638,6 @@ def save_fig( if __name__ == "__main__": if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake( "plot_sector_validation", simpl="11", @@ -708,7 +671,6 @@ def save_fig( plotting_data = _initialize_metadata(VALIDATION_PLOTS) for plot_data in plotting_data: - fn = plot_data.fn title = plot_data.nice_name if plot_data.nice_name else plot_data.name @@ -749,7 +711,6 @@ def save_fig( continue for state in states: - if plot_data.fn_kwargs: fn_kwargs = plot_data.fn_kwargs else: diff --git a/workflow/scripts/prepare_network.py b/workflow/scripts/prepare_network.py index ee964dfc6..8f1161e7d 100644 --- a/workflow/scripts/prepare_network.py +++ b/workflow/scripts/prepare_network.py @@ -1,8 +1,3 @@ -# SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors -# -# SPDX-License-Identifier: MIT - -# coding: utf-8 """ Prepare PyPSA network for solving according to :ref:`opts` and :ref:`ll`, such as. @@ -14,46 +9,6 @@ - specifying an expansion limit on the **volume** of transmission expansion, and - reducing the **temporal** resolution by averaging over multiple hours or segmenting time series into chunks of varying lengths using ``tsam``. - -Relevant Settings ------------------ - -.. code:: yaml - - costs: - year: - version: - fill_values: - emission_prices: - marginal_cost: - capital_cost: - - electricity: - co2limit: - max_hours: - -.. seealso:: - Documentation of the configuration file ``config/config.yaml`` at - :ref:`costs_cf`, :ref:`electricity_cf` - -Inputs ------- - -- ``resources/costs.csv``: The database of cost assumptions for all included technologies for specific years from various sources; e.g. discount rate, lifetime, investment (CAPEX), fixed operation and maintenance (FOM), variable operation and maintenance (VOM), fuel costs, efficiency, carbon-dioxide intensity. -- ``networks/elec_s{simpl}_{clusters}.nc``: confer :ref:`cluster` - -Outputs -------- - -- ``networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc``: Complete PyPSA network that will be handed to the ``solve_network`` rule. - -Description ------------ - -.. tip:: - The rule :mod:`prepare_elec_networks` runs - for all ``scenario`` s in the configuration file - the rule :mod:`prepare_network`. """ import logging @@ -67,8 +22,6 @@ set_scenario_config, update_config_from_wildcards, ) -from add_electricity import update_transmission_costs -from pypsa.descriptors import expand_series idx = pd.IndexSlice @@ -98,17 +51,17 @@ def get_investment_weighting(time_weighting, r=0.01): ) -def add_co2limit(n, co2limit, Nyears=1.0): +def add_co2limit(n, co2limit, num_years=1.0): n.add( "GlobalConstraint", "CO2Limit", carrier_attribute="co2_emissions", sense="<=", - constant=co2limit * Nyears, + constant=co2limit * num_years, ) -def add_gaslimit(n, gaslimit, Nyears=1.0): +def add_gaslimit(n, gaslimit, num_years=1.0): sel = n.carriers.index.intersection(["OCGT", "CCGT", "CHP"]) n.carriers.loc[sel, "gas_usage"] = 1.0 @@ -117,7 +70,7 @@ def add_gaslimit(n, gaslimit, Nyears=1.0): "GasLimit", carrier_attribute="gas_usage", sense="<=", - constant=gaslimit * Nyears, + constant=gaslimit * num_years, ) @@ -233,9 +186,7 @@ def resample_multi_index(df, offset, func): def is_leap_year(year: int) -> bool: - """ - Check if a given year is a leap year. - """ + """Check if a given year is a leap year.""" if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0): return True else: @@ -247,7 +198,7 @@ def apply_time_segmentation(n, segments, solver_name="cbc"): import tsam.timeseriesaggregation as tsam except ImportError: raise ModuleNotFoundError( - "Optional dependency 'tsam' not found." "Install via 'pip install tsam'", + "Optional dependency 'tsam' not found.Install via 'pip install tsam'", ) # get all time-dependent data @@ -358,7 +309,7 @@ def set_line_nom_max( transport_model = is_transport_model(params.transmission_network) n = pypsa.Network(snakemake.input[0]) - Nyears = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 + num_years = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 costs = pd.read_csv(snakemake.input.tech_costs) costs = costs.pivot(index="pypsa-name", columns="parameter", values="value") # Set Investment Period Year Weightings @@ -389,10 +340,10 @@ def set_line_nom_max( n = apply_time_segmentation(n, segments, solver_name) if params.co2limit_enable: - add_co2limit(n, params.co2limit, Nyears) + add_co2limit(n, params.co2limit, num_years) if params.gaslimit_enable: - add_gaslimit(n, params.gaslimit, Nyears) + add_gaslimit(n, params.gaslimit, num_years) emission_prices = params.costs["emission_prices"] if emission_prices["enable"]: diff --git a/workflow/scripts/retrieve_caiso_data.py b/workflow/scripts/retrieve_caiso_data.py index 35de7d6c5..b89d6c9f7 100644 --- a/workflow/scripts/retrieve_caiso_data.py +++ b/workflow/scripts/retrieve_caiso_data.py @@ -31,7 +31,6 @@ import pandas as pd import requests -import seaborn as sns def download_oasis_report( @@ -53,7 +52,8 @@ def download_oasis_report( - node: Specific fuel region ID or 'ALL' for all regions. - resultformat: Format of the result ('6' for CSV, '5' for XML). - Returns: + Returns + ------- - None. Downloads the file to the current directory. """ base_url = "http://oasis.caiso.com/oasisapi/SingleZip" @@ -79,9 +79,7 @@ def download_oasis_report( def generate_monthly_intervals(year): - """ - Generate monthly start and end datetime strings for a given year. - """ + """Generate monthly start and end datetime strings for a given year.""" intervals = [] for month in range(1, 13): start_date = datetime(year, month, 1) @@ -125,9 +123,7 @@ def step_download_oasis_reports( def combine_reports(file_names, year): - """ - Combine all reports into a single DataFrame. - """ + """Combine all reports into a single DataFrame.""" all_data_frames = [] for file in file_names: file = file.replace(":", "_") @@ -135,7 +131,7 @@ def combine_reports(file_names, year): all_data_frames.append(df) combined_data = pd.concat(all_data_frames, ignore_index=True) - combined_data.sort_values(by="INTERVALSTARTTIME_GMT", inplace=True) + combined_data = combined_data.sort_values(by="INTERVALSTARTTIME_GMT") return combined_data @@ -147,7 +143,8 @@ def get_files_starting_with(folder_path, prefix): - folder_path: Path to the folder. - prefix: The string that the file names should start with. - Returns: + Returns + ------- - A list of file names that start with the specified prefix. """ file_names = [] @@ -158,9 +155,7 @@ def get_files_starting_with(folder_path, prefix): def merge_fuel_regions_data(combined_data, year): - """ - Merge the fuel regions with the combined data. - """ + """Merge the fuel regions with the combined data.""" df = pd.read_excel(snakemake.input.fuel_regions, sheet_name="GPI_Fuel_Region") df = df[["Fuel Region", "Balancing Authority"]] df["Fuel Region"] = df["Fuel Region"].str.strip(" ") @@ -172,9 +167,8 @@ def merge_fuel_regions_data(combined_data, year): right_on="Fuel Region", how="left", ) - combined_data_merged.drop( + combined_data_merged = combined_data_merged.drop( columns=["Fuel Region", "FUEL_REGION_ID_XML"], - inplace=True, ) return combined_data_merged @@ -199,7 +193,6 @@ def reduce_select_pricing_nodes(combined_data_merged): def main(snakemake): - fuel_year = snakemake.params.fuel_year file_names = step_download_oasis_reports( @@ -219,4 +212,9 @@ def main(snakemake): if __name__ == "__main__": + if "snakemake" not in globals(): + from _helpers import mock_snakemake + + snakemake = mock_snakemake("retrieve_caiso_data") + main(snakemake) diff --git a/workflow/scripts/retrieve_databundles.py b/workflow/scripts/retrieve_databundles.py index 8e3b3e6f8..95921ea3a 100644 --- a/workflow/scripts/retrieve_databundles.py +++ b/workflow/scripts/retrieve_databundles.py @@ -1,12 +1,11 @@ -import io +"""Script retrieves data from various zenodo repositories specified by the snakemake rule. Used by multiple snakemake rules.""" + import logging -import os import platform import subprocess import zipfile from pathlib import Path -import requests from _helpers import configure_logging, progress_retrieve logger = logging.getLogger(__name__) diff --git a/workflow/scripts/retrieve_egs.py b/workflow/scripts/retrieve_egs.py index b96fdf5cc..60259d243 100644 --- a/workflow/scripts/retrieve_egs.py +++ b/workflow/scripts/retrieve_egs.py @@ -1,12 +1,9 @@ -import io +"""Retreives data for Enhanced Geothermal supply curves and capacity factor profiles.""" + import logging -import os -import platform -import subprocess import zipfile from pathlib import Path -import requests from _helpers import configure_logging, progress_retrieve logger = logging.getLogger(__name__) diff --git a/workflow/scripts/retrieve_eulp.py b/workflow/scripts/retrieve_eulp.py index 4efc2c73d..f5249bfe4 100644 --- a/workflow/scripts/retrieve_eulp.py +++ b/workflow/scripts/retrieve_eulp.py @@ -1,7 +1,10 @@ +# ruff: noqa: RUF012 + """ Module to download end use load profiles (eulp) for comstock and restock data. -Notes: +Notes +----- - Downloaded at state level - Multisector 15-min load profiles for a year (ie. lots of data) - Locked to 2018 Amy Weather data @@ -10,22 +13,16 @@ import logging from pathlib import Path -from typing import List, Optional -import constants -import pandas as pd import requests from _helpers import configure_logging +from eulp import Eulp logger = logging.getLogger(__name__) -from eulp import Eulp - class OediDownload: - """ - Downlaods Oedi restock or comstock data at a state level. - """ + """Downlaods Oedi restock or comstock data at a state level.""" root = "https://oedi-data-lake.s3.amazonaws.com/nrel-pds-building-stock/end-use-load-profiles-for-us-building-stock/2024" @@ -59,11 +56,8 @@ def __init__(self, stock: str) -> None: self.stock = stock self.release = 2 if self.stock == "res" else 1 - def _get_html_folder(self, state: str, upgrade: Optional[int] = 0) -> str: - """ - Gets html of folder. - """ - + def _get_html_folder(self, state: str, upgrade: int | None = 0) -> str: + """Gets html of folder.""" if self.stock == "res": data_folder = f"resstock_amy2018_release_{self.release}" elif self.stock == "com": @@ -77,7 +71,6 @@ def _get_htmls( buildings: str | list[str], upgrade: int = 0, ) -> list[str]: - folder = self._get_html_folder(state) if isinstance(buildings, str): @@ -92,7 +85,6 @@ def _get_htmls( return htmls def _request_data(self, url: str, save: str) -> dict[str, dict | str]: - response = requests.get(url) if response.status_code == 200: logger.info(f"Writing {save}") @@ -112,13 +104,10 @@ def _create_save_dir(self, directory: str) -> None: def download_data( self, state: str, - buildings: Optional[str | list[str]] = None, - directory: Optional[str] = None, + buildings: str | list[str] | None = None, + directory: str | None = None, ) -> None: - """ - Public method to interface with. - """ - + """Public method to interface with.""" if not directory: directory = f"{state}" else: @@ -141,7 +130,6 @@ def download_data( if __name__ == "__main__": - if "snakemake" not in globals(): from _helpers import mock_snakemake diff --git a/workflow/scripts/retrieve_gridemissions_data.py b/workflow/scripts/retrieve_gridemissions_data.py index 6acf35dae..7e2bd2ffc 100644 --- a/workflow/scripts/retrieve_gridemissions_data.py +++ b/workflow/scripts/retrieve_gridemissions_data.py @@ -1,5 +1,5 @@ """ -**Description** +**Description**. Historical electrical generation, demand, interchange, and emissions data are retrieved from the `GridEmissions `_. Data is downloaded at hourly temporal resolution and at a spatial resolution of balancing authority region. @@ -10,13 +10,11 @@ """ import glob -import gzip import logging import os import re import tarfile import warnings -from io import BytesIO from pathlib import Path import pandas as pd @@ -59,20 +57,20 @@ def download_and_extract(url, extract_path): print(f"File extracted to {extract_path}") -def prepare_historical_data(PATH_DOWNLOAD: str, suffix: str = "elec") -> None: +def prepare_historical_data(path_download: str, suffix: str = "elec") -> None: """ Combines and filters Data Files from GridEmissions files. Returns single dataframe of all demand data. """ - file_paths = glob.glob(f"{PATH_DOWNLOAD}/processed/*_{suffix}.csv") + file_paths = glob.glob(f"{path_download}/processed/*_{suffix}.csv") dfs = [] for file_path in file_paths: df = pd.read_csv(file_path) dfs.append(df) df = pd.concat(dfs) df["period"] = pd.to_datetime(df["period"]) - df.sort_values(by="period", inplace=True) + df = df.sort_values(by="period") return df @@ -103,7 +101,7 @@ def filter_demand_data(df: pd.DataFrame) -> pd.DataFrame: grid_emissions_data_url = "https://gridemissions.s3.us-east-2.amazonaws.com/processed.tar.gz" - PATH_DOWNLOAD = Path(f"data/GridEmissions") + PATH_DOWNLOAD = Path("data/GridEmissions") PATH_DOWNLOAD.mkdir(parents=True, exist_ok=True) download_and_extract(grid_emissions_data_url, PATH_DOWNLOAD) df_elec = prepare_historical_data(PATH_DOWNLOAD, suffix="elec") diff --git a/workflow/scripts/retrieve_pudl.py b/workflow/scripts/retrieve_pudl.py index 9cfe88973..d1ba8d220 100644 --- a/workflow/scripts/retrieve_pudl.py +++ b/workflow/scripts/retrieve_pudl.py @@ -1,28 +1,22 @@ -""" -Retrieves PUDL data. -""" +"""Retrieves PUDL data.""" import logging import zipfile -import zlib from pathlib import Path -import requests from _helpers import mock_snakemake, progress_retrieve -from tqdm import tqdm logger = logging.getLogger(__name__) if __name__ == "__main__": if "snakemake" not in globals(): - from _helpers import mock_snakemake - snakemake = mock_snakemake("retrieve_pudl") rootpath = ".." else: rootpath = "." - # Recommended to use the stable version of PUDL documented here: https://catalystcoop-pudl.readthedocs.io/en/latest/data_access.html#stable-builds + # Recommended to use the stable version of PUDL documented here: + # https://catalystcoop-pudl.readthedocs.io/en/latest/data_access.html#stable-builds url_pudl = "https://zenodo.org/records/13346011/files/pudl.sqlite.zip?download=1" url_census = "https://zenodo.org/records/13346011/files/censusdp1tract.sqlite.zip?download=1" @@ -40,7 +34,7 @@ zip_ref.extractall(Path(save_pudl).parent) # Get PUDL FERC Form 714 Parquet - parquet = f"https://zenodo.org/records/11292273/files/out_ferc714__hourly_estimated_state_demand.parquet?download=1" + parquet = "https://zenodo.org/records/11292273/files/out_ferc714__hourly_estimated_state_demand.parquet?download=1" save_ferc = snakemake.output.pudl_ferc714 diff --git a/workflow/scripts/scenario_comparison.py b/workflow/scripts/scenario_comparison.py index 57755328e..008a4c766 100644 --- a/workflow/scripts/scenario_comparison.py +++ b/workflow/scripts/scenario_comparison.py @@ -1,64 +1,4 @@ -""" -This script facilitates comparison of scenarios run in PyPSA-USA. It loads data from multiple scenarios, processes it for analysis, and generates comparative plots for system metrics such as optimal capacities, supply, and costs. The script is designed for users working with PyPSA networks and scenario data stored in a specific YAML configuration format. - -Usage: ------- - -1. **Setup Configuration File**: - - Create a YAML configuration file containing details about the scenarios, aliases, order of scenarios, and network data path. An example structure for the configuration file: - ```yaml - scenarios: - - name: "Scenario 1" - path: "path/to/scenario1/" - - name: "Scenario 2" - path: "path/to/scenario2/" - alias_dict: - "Scenario 1": "S1" - "Scenario 2": "S2" - new_order: - - "S1" - - "S2" - reference_scenario: "S1" - output_folder_name: "folder_name" - network: - path: "path/to/network/file.nc" - ``` - -2. **Prepare Directory Structure**: - - Place the YAML configuration file in the parent directory of the current working directory under `config/scenario_comparison.yaml`. - - Ensure the scenario data contains statistics files (`statistics/statistics*.csv`). - -3. **Execution**: - - Run the script from its directory using a Python environment with the required libraries (`yaml`, `pandas`, `pypsa`, `matplotlib`, `numpy`, `seaborn`). - -4. **Outputs**: - - Plots will be saved in the `results/{output_folder_name}` directory in the parent of the current working directory. These include: - - Bar charts comparing system metrics like "Optimal Capacity" or "System Costs." - - Comparative percentage charts, if a reference scenario is specified. - -5. **Adjusting Variables**: - - Modify variables such as `variable`, `variable_units`, and `title` in the script to customize the metrics and plot titles. - -Functions: ----------- -- `get_carriers(n)`: Processes the carrier data from the PyPSA network for plotting. -- `load_yaml_config(yaml_path)`: Loads the YAML configuration file. -- `load_scenario_data(scenarios)`: Reads scenario CSV data from paths specified in the configuration. -- `process_data(data, alias_dict=None, new_order=None)`: Processes raw scenario data, applying aliases and ordering. -- `scenario_comparison(...)`: Generates horizontal bar charts comparing scenario data for a specific variable. -- `plot_cost_comparison(...)`: Plots a cost comparison across scenarios and optionally a percentage comparison relative to a reference scenario. - -Dependencies: -------------- -- Libraries: `yaml`, `pandas`, `pypsa`, `matplotlib`, `numpy`, `seaborn`. -- Ensure all dependencies are installed in your Python environment before running the script. - -Example: --------- -Run the script to generate comparison plots for energy system scenarios defined in the configuration file: -```bash -python script_name.py -""" +"""Script used to compare outputs from multiple snakemake scenarios.""" import argparse from pathlib import Path @@ -66,7 +6,6 @@ import numpy as np import pandas as pd import pypsa -import seaborn as sns import yaml from matplotlib import pyplot as plt @@ -77,8 +16,8 @@ def get_carriers(n): carriers.loc["DC", "legend_name"] = "Transmission" carriers.loc["DC", "color"] = "#cf1dab" carriers.loc["battery", "legend_name"] = "Existing BESS" - carriers.set_index("nice_name", inplace=True) - carriers.sort_values(by="co2_emissions", ascending=False, inplace=True) + carriers = carriers.set_index("nice_name") + carriers = carriers.sort_values(by="co2_emissions", ascending=False) return carriers @@ -151,8 +90,12 @@ def prepare_combined_dataframe( data.append(df_horizon.rename(columns={horizon: "statistics"})) combined_df = pd.concat(data, ignore_index=True) - combined_df["scenario_name"] = combined_df["Scenario"].apply(lambda x: x.split("_")[0]) - combined_df["trans_expansion"] = combined_df["Scenario"].apply(lambda x: x.split("_")[1]) + combined_df["scenario_name"] = combined_df["Scenario"].apply( + lambda x: x.split("_")[0], + ) + combined_df["trans_expansion"] = combined_df["Scenario"].apply( + lambda x: x.split("_")[1], + ) combined_df.to_csv(figures_path / f"{variable}_comparison.csv") return combined_df @@ -199,7 +142,14 @@ def plot_scenario_comparison( ) bottoms[j] += values - ax.text(1.01, 0.5, horizon, transform=ax.transAxes, va="center", rotation="vertical") + ax.text( + 1.01, + 0.5, + horizon, + transform=ax.transAxes, + va="center", + rotation="vertical", + ) ax.set_yticks(y_positions) ax.set_yticklabels(scenarios) ax.grid(True, axis="x", linestyle="--", alpha=0.5) @@ -219,15 +169,35 @@ def plot_scenario_comparison( ) fig.suptitle(title, fontsize=12, fontweight="bold") plt.tight_layout() - plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_comparison.png", + dpi=300, + bbox_inches="tight", + ) if reference_scenario: - _plot_reference_comparison(combined_df, reference_scenario, carriers, colors, figures_path, variable, horizon) + _plot_reference_comparison( + combined_df, + reference_scenario, + carriers, + colors, + figures_path, + variable, + horizon, + ) return -def _plot_reference_comparison(combined_df, reference_scenario, carriers, colors, figures_path, variable, horizon): +def _plot_reference_comparison( + combined_df, + reference_scenario, + carriers, + colors, + figures_path, + variable, + horizon, +): combined_df = combined_df.set_index("Scenario") combined_df = combined_df.loc[combined_df.horizon == horizon] ref = combined_df.loc[reference_scenario].set_index("nice_name") @@ -237,7 +207,11 @@ def _plot_reference_comparison(combined_df, reference_scenario, carriers, colors (combined_df.loc[carrier, "statistics"] - ref.loc[carrier, "statistics"]) / ref.statistics.sum() * 100 ) combined_df = combined_df.reset_index().set_index("Scenario") - stacked_data = combined_df.reset_index().pivot(index="Scenario", columns="nice_name", values="statistics") + stacked_data = combined_df.reset_index().pivot( + index="Scenario", + columns="nice_name", + values="statistics", + ) stacked_data.plot( kind="bar", stacked=True, @@ -246,7 +220,11 @@ def _plot_reference_comparison(combined_df, reference_scenario, carriers, colors legend=False, ) plt.ylabel("∆ Capacity[%]") - plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_pct_comparison.png", + dpi=300, + bbox_inches="tight", + ) combined_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") # combined_df = combined_df.set_index(["Scenario", "horizon"]) @@ -281,7 +259,10 @@ def scenario_comparison( as_pct=False, reference_scenario=None, ): - combined_df = pd.DataFrame(columns=["Scenario", "horizon", "nice_name", "statistics"], index=[]) + combined_df = pd.DataFrame( + columns=["Scenario", "horizon", "nice_name", "statistics"], + index=[], + ) colors = carriers["color"] planning_horizons = stats[next(iter(stats.keys()))]["statistics"][variable].columns fig, axes = plt.subplots( @@ -306,25 +287,48 @@ def scenario_comparison( df = df["statistics"].fillna(0) bottoms = np.zeros(len(y_positions)) if include_link: - df = df.loc[df.index.get_level_values(0).isin(["Generator", "StorageUnit", "Link"]), variable] + df = df.loc[ + df.index.get_level_values(0).isin( + ["Generator", "StorageUnit", "Link"], + ), + variable, + ] df = df.loc[~(df.index.get_level_values(1) == "Ac")] else: - df = df.loc[df.index.get_level_values(0).isin(["Generator", "StorageUnit"]), variable] + df = df.loc[ + df.index.get_level_values(0).isin(["Generator", "StorageUnit"]), + variable, + ] df.index = df.index.get_level_values(1) df = df.reindex(carriers.index).dropna() if as_pct: df = ((df / df.sum()) * 100).round(2) for i, tech in enumerate(df.index.unique()): values = df.loc[tech, horizon] / factor_units - ax.barh(y_positions[j], values, left=bottoms[j], color=colors[tech], label=tech if j == 0 else "") + ax.barh( + y_positions[j], + values, + left=bottoms[j], + color=colors[tech], + label=tech if j == 0 else "", + ) bottoms[j] += values df[["Scenario", "horizon"]] = scenario, horizon df = df.reset_index() - df.rename(columns={horizon: "statistics"}, inplace=True) - combined_df = pd.concat([combined_df, df[["Scenario", "nice_name", "statistics", "horizon"]]]) + df = df.rename(columns={horizon: "statistics"}) + combined_df = pd.concat( + [combined_df, df[["Scenario", "nice_name", "statistics", "horizon"]]], + ) - ax.text(1.01, 0.5, f"{horizon}", transform=ax.transAxes, va="center", rotation="vertical") + ax.text( + 1.01, + 0.5, + f"{horizon}", + transform=ax.transAxes, + va="center", + rotation="vertical", + ) ax.set_yticks(y_positions) ax.set_yticklabels(stats.keys()) ax.grid(True, axis="x", linestyle="--", alpha=0.5) @@ -343,15 +347,25 @@ def scenario_comparison( ) fig.suptitle(title, fontsize=12, fontweight="bold") plt.tight_layout() - plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_comparison.png", + dpi=300, + bbox_inches="tight", + ) - combined_df["scenario_name"] = combined_df["Scenario"].apply(lambda x: x.split("_")[0]) - combined_df["trans_expansion"] = combined_df["Scenario"].apply(lambda x: x.split("_")[1]) + combined_df["scenario_name"] = combined_df["Scenario"].apply( + lambda x: x.split("_")[0], + ) + combined_df["trans_expansion"] = combined_df["Scenario"].apply( + lambda x: x.split("_")[1], + ) combined_df.to_csv(figures_path / f"{variable}_comparison.csv") if reference_scenario: combined_df = combined_df.set_index("Scenario") - combined_df = combined_df.query("horizon == @horizon").drop(columns="horizon") # only plot last horizon + combined_df = combined_df.query("horizon == @horizon").drop( + columns="horizon", + ) # only plot last horizon ref = combined_df.loc[reference_scenario].set_index("nice_name") combined_df = combined_df.reset_index().set_index("nice_name") for scenario in combined_df.index.unique(): @@ -359,7 +373,11 @@ def scenario_comparison( (combined_df.loc[scenario, "statistics"] - ref.loc[scenario, "statistics"]) / ref.statistics.sum() * 100 ) combined_df = combined_df.reset_index().set_index("Scenario") - stacked_data = combined_df.reset_index().pivot(index="Scenario", columns="nice_name", values="statistics") + stacked_data = combined_df.reset_index().pivot( + index="Scenario", + columns="nice_name", + values="statistics", + ) stacked_data.plot( kind="bar", stacked=True, @@ -368,12 +386,24 @@ def scenario_comparison( legend=False, ) plt.ylabel("∆ Capacity[%]") - plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_pct_comparison.png", + dpi=300, + bbox_inches="tight", + ) combined_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") return combined_df -def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path, reference_scenario=None): +def plot_cost_comparison( + stats, + n, + variable, + variable_units, + title, + figures_path, + reference_scenario=None, +): combined_df = pd.DataFrame(columns=["Scenario", "statistics"], index=[]) for j, (scenario, stat) in enumerate(stats.items()): stat = stat["statistics"] @@ -394,17 +424,36 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path ], ) - combined_df.plot(kind="bar", x="Scenario", y="statistics", title="Total System Costs", legend=False) + combined_df.plot( + kind="bar", + x="Scenario", + y="statistics", + title="Total System Costs", + legend=False, + ) plt.ylabel("Annualized System Costs [B$]") - plt.savefig(figures_path / f"{variable}_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_comparison.png", + dpi=300, + bbox_inches="tight", + ) if reference_scenario: combined_df = combined_df.set_index("Scenario") ref = combined_df.loc[reference_scenario] pct_df = (combined_df - ref) / combined_df * 100 - pct_df.plot(kind="bar", y="statistics", title="Total System Costs", legend=False) + pct_df.plot( + kind="bar", + y="statistics", + title="Total System Costs", + legend=False, + ) plt.ylabel("∆ Annualized System Costs [%]") - plt.savefig(figures_path / f"{variable}_pct_comparison.png", dpi=300, bbox_inches="tight") + plt.savefig( + figures_path / f"{variable}_pct_comparison.png", + dpi=300, + bbox_inches="tight", + ) pct_df.to_csv(figures_path / f"{variable}_pct_comparison.csv") combined_df.to_csv(figures_path / f"{variable}_comparison.csv") @@ -413,7 +462,11 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path if __name__ == "__main__": # Parse command line arguments parser = argparse.ArgumentParser(description="Run scenario comparison script.") - parser.add_argument("yaml_name", type=str, help="Name of the YAML configuration file.") + parser.add_argument( + "yaml_name", + type=str, + help="Name of the YAML configuration file.", + ) args = parser.parse_args() yaml_name = args.yaml_name # Name of the YAML file from command line argument @@ -471,7 +524,15 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path title = "Supply Comparison" # Generate plots - scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=True) + scenario_comparison( + processed_data, + variable, + variable_units, + carriers, + title, + figures_path, + as_pct=True, + ) # Example variable and title variable = "Capital Expenditure" @@ -479,7 +540,15 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path title = "CAPEX Comparison" # Generate plots - scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=False) + scenario_comparison( + processed_data, + variable, + variable_units, + carriers, + title, + figures_path, + as_pct=False, + ) # Example variable and title variable = "Operational Expenditure" @@ -487,10 +556,26 @@ def plot_cost_comparison(stats, n, variable, variable_units, title, figures_path title = "OPEX Comparison" # Generate plots - scenario_comparison(processed_data, variable, variable_units, carriers, title, figures_path, as_pct=False) + scenario_comparison( + processed_data, + variable, + variable_units, + carriers, + title, + figures_path, + as_pct=False, + ) # Example variable and title variable = "System Costs" variable_units = "$B" title = "Scenario Comparison" - plot_cost_comparison(processed_data, n, variable, variable_units, title, figures_path, reference_scenario) + plot_cost_comparison( + processed_data, + n, + variable, + variable_units, + title, + figures_path, + reference_scenario, + ) diff --git a/workflow/scripts/simplify_network.py b/workflow/scripts/simplify_network.py index 8b6996215..aec8e6a6d 100644 --- a/workflow/scripts/simplify_network.py +++ b/workflow/scripts/simplify_network.py @@ -1,8 +1,5 @@ # BY PyPSA-USA Authors -""" -Aggregates network to substations and simplifies to a single voltage level. -""" - +"""Aggregates network to substations and simplifies to a single voltage level.""" import logging from functools import reduce @@ -30,7 +27,7 @@ def convert_to_per_unit(df): df["susceptance_pu"] = df["b"] / df["base_susceptance"] # Dropping intermediate columns (optional) - df.drop(["base_impedance", "base_susceptance"], axis=1, inplace=True) + df = df.drop(["base_impedance", "base_susceptance"], axis=1) return df @@ -39,7 +36,8 @@ def convert_to_voltage_level(n, new_voltage): """ Converts network.lines parameters to a given voltage. - Parameters: + Parameters + ---------- n (pypsa.Network): Network new_voltage (float): New voltage level """ @@ -55,10 +53,9 @@ def convert_to_voltage_level(n, new_voltage): df.v_nom = new_voltage # Dropping intermediate column - df.drop( + df = df.drop( ["new_base_impedance", "resistance_pu", "reactance_pu", "susceptance_pu"], axis=1, - inplace=True, ) # Update network lines @@ -101,7 +98,6 @@ def aggregate_to_substations( First step in clusterings, if use_ba_zones is True, then the network retains balancing Authority zones in clustering. """ - logger.info("Aggregating buses to substation level...") generator_strategies = aggregation_strategies.get("generators", dict()) @@ -173,9 +169,8 @@ def aggregate_to_substations( else: cols2drop = ["balancing_area", "substation_off", "sub_id", "state"] - network_s.buses.drop( + network_s.buses = network_s.buses.drop( columns=cols2drop, - inplace=True, ) return network_s, clustering.busmap @@ -221,9 +216,8 @@ def assign_line_lengths(n, line_length_factor): # n = pypsa.Network(snakemake.input.network) n = pickle.load(open(snakemake.input.network, "rb")) - n.generators.drop( + n.generators = n.generators.drop( columns=["ba_eia", "ba_ads"], - inplace=True, ) # temp added these columns and need to drop for workflow n = convert_to_voltage_level(n, 230) @@ -238,7 +232,8 @@ def assign_line_lengths(n, line_length_factor): busmaps = [trafo_map, busmap_to_sub.sub_id] busmaps = reduce(lambda x, y: x.map(y), busmaps[1:], busmaps[0]) - # TODO: WHEN WE REPLACE NETWORK WITH NEW NETWORK WE SHOULD CALACULATE LINE LENGTHS BASED ON THE actual GIS line files. + # TODO: WHEN WE REPLACE NETWORK WITH NEW NETWORK WE SHOULD CALACULATE LINE + # LENGTHS BASED ON THE actual GIS line files. n = assign_line_lengths(n, 1.25) n.links["underwater_fraction"] = 0 # TODO: CALULATE UNDERWATER FRACTIONS. @@ -251,7 +246,7 @@ def assign_line_lengths(n, line_length_factor): ) if topological_boundaries == "reeds_zone": - n.buses.drop(columns=["county"], inplace=True) + n.buses = n.buses.drop(columns=["county"]) if snakemake.wildcards.simpl: n.set_investment_periods(periods=snakemake.params.planning_horizons) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 53085f9df..d44afedd6 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -25,7 +25,7 @@ import logging import re -from typing import Any, Optional +from typing import Any import numpy as np import pandas as pd @@ -70,7 +70,8 @@ def filter_components( """ Filter components based on common criteria. - Parameters: + Parameters + ---------- - n: pypsa.Network The PyPSA network object. - component_type: str @@ -84,7 +85,8 @@ def filter_components( - extendable: bool, optional If specified, filters by extendable or non-extendable assets. - Returns: + Returns + ------- - pd.DataFrame Filtered assets. """ @@ -116,7 +118,9 @@ def add_land_use_constraints(n): constraints. """ model = n.model - generators = n.generators.query("p_nom_extendable & land_region != '' ").rename_axis(index="Generator-ext") + generators = n.generators.query( + "p_nom_extendable & land_region != '' ", + ).rename_axis(index="Generator-ext") if generators.empty: return @@ -133,7 +137,10 @@ def add_land_use_constraints(n): if not index.empty: logger.info("Adding land-use constraints") - model.add_constraints(lhs.sel(group=index) <= rhs.loc[index], name="land_use_constraint") + model.add_constraints( + lhs.sel(group=index) <= rhs.loc[index], + name="land_use_constraint", + ) def prepare_network( @@ -149,7 +156,7 @@ def prepare_network( n.generators_t.p_min_pu, n.storage_units_t.inflow, ): - df.where(df > solve_opts["clip_p_max_pu"], other=0.0, inplace=True) + df = df.where(df > solve_opts["clip_p_max_pu"], other=0.0) load_shedding = solve_opts.get("load_shedding") if load_shedding: @@ -302,7 +309,6 @@ def add_technology_capacity_target_constraints(n, config): target["min"] = float(target["min"]) if not np.isnan(target["min"]): - rhs = target["min"] - round(lhs_existing, 2) n.model.add_constraints( @@ -321,7 +327,6 @@ def add_technology_capacity_target_constraints(n, config): ) if not np.isnan(target["max"]): - assert ( target["max"] >= lhs_existing ), f"TCT constraint of {target['max']} MW for {target['carrier']} must be at least {lhs_existing}" @@ -365,25 +370,32 @@ def add_RPS_constraints(n, config): """ def process_reeds_data(filepath, carriers, value_col): - """ - Helper function to process RPS or CES REEDS data. - """ + """Helper function to process RPS or CES REEDS data.""" reeds = pd.read_csv(filepath) # Handle both wide and long formats if "rps_all" not in reeds.columns: - reeds = reeds.melt(id_vars="st", var_name="planning_horizon", value_name=value_col) + reeds = reeds.melt( + id_vars="st", + var_name="planning_horizon", + value_name=value_col, + ) # Standardize column names - reeds.rename(columns={"st": "region", "t": "planning_horizon", "rps_all": "pct"}, inplace=True) + reeds = reeds.rename( + columns={"st": "region", "t": "planning_horizon", "rps_all": "pct"}, + ) reeds["carrier"] = [", ".join(carriers)] * len(reeds) # Extract and create new rows for `rps_solar` and `rps_wind` additional_rows = [] - for carrier_col, carrier_name in [("rps_solar", "solar"), ("rps_wind", "onwind, offwind, offwind_floating")]: + for carrier_col, carrier_name in [ + ("rps_solar", "solar"), + ("rps_wind", "onwind, offwind, offwind_floating"), + ]: if carrier_col in reeds.columns: temp = reeds[["region", "planning_horizon", carrier_col]].copy() - temp.rename(columns={carrier_col: "pct"}, inplace=True) + temp = temp.rename(columns={carrier_col: "pct"}) temp["carrier"] = carrier_name additional_rows.append(temp) @@ -402,8 +414,17 @@ def process_reeds_data(filepath, carriers, value_col): portfolio_standards = pd.read_csv(config["electricity"]["portfolio_standards"]) # Define carriers for RPS and CES - rps_carriers = ["onwind", "offwind", "offwind_floating", "solar", "hydro", "geothermal", "biomass", "EGS"] - ces_carriers = rps_carriers + ["nuclear", "SMR"] + rps_carriers = [ + "onwind", + "offwind", + "offwind_floating", + "solar", + "hydro", + "geothermal", + "biomass", + "EGS", + ] + ces_carriers = [*rps_carriers, "nuclear", "SMR"] # Process RPS and CES REEDS data rps_reeds = process_reeds_data( @@ -421,7 +442,11 @@ def process_reeds_data(filepath, carriers, value_col): portfolio_standards = pd.concat([portfolio_standards, rps_reeds, ces_reeds]) portfolio_standards = portfolio_standards[ (portfolio_standards.pct > 0.0) - & (portfolio_standards.planning_horizon.isin(snakemake.params.planning_horizons)) + & ( + portfolio_standards.planning_horizon.isin( + snakemake.params.planning_horizons, + ) + ) & (portfolio_standards.region.isin(n.buses.reeds_state.unique())) ] @@ -464,7 +489,9 @@ def process_reeds_data(filepath, carriers, value_col): lhs >= rhs, name=f"GlobalConstraint-{constraint_row.name}_{constraint_row.planning_horizon}_rps_limit", ) - logger.info(f"Added RPS {constraint_row.name} for {constraint_row.planning_horizon}.") + logger.info( + f"Added RPS {constraint_row.name} for {constraint_row.planning_horizon}.", + ) def add_EQ_constraints(n, o, scaling=1e-1): @@ -607,7 +634,8 @@ def add_interface_limits(n, sns, config): if ( not (pd.concat([interface_links_b0, interface_links_b1]).empty) and ("RESOLVE" in interface.interface or transport_model) - # Apply link constraints if RESOLVE constraint or if zonal model. ITLs should usually only apply to AC lines if DC PF is used. + # Apply link constraints if RESOLVE constraint or if zonal model. ITLs + # should usually only apply to AC lines if DC PF is used. ): link_flows = n.model["Link-p"].loc[:, interface_links_b1.index].sum( dims="Link", @@ -627,10 +655,7 @@ def add_interface_limits(n, sns, config): def add_regional_co2limit(n, sns, config): - """ - Adding regional regional CO2 Limits Specified in the config.yaml. - """ - + """Adding regional regional CO2 Limits Specified in the config.yaml.""" regional_co2_lims = pd.read_csv( config["electricity"]["regional_Co2_limits"], index_col=[0], @@ -769,14 +794,13 @@ def add_SAFER_constraints(n, config): snakemake.input.safer_reeds, index_col=[0], ) - NERC_memberships = n.buses.groupby("nerc_reg")["reeds_zone"].apply(lambda x: ", ".join(x)).to_dict() - reeds_prm["region"] = reeds_prm.index.map(NERC_memberships) - reeds_prm.dropna(subset="region", inplace=True) - reeds_prm.drop( + nerc_memberships = n.buses.groupby("nerc_reg")["reeds_zone"].apply(lambda x: ", ".join(x)).to_dict() + reeds_prm["region"] = reeds_prm.index.map(nerc_memberships) + reeds_prm = reeds_prm.dropna(subset="region") + reeds_prm = reeds_prm.drop( columns=["none", "ramp2025_20by50", "ramp2025_25by50", "ramp2025_30by50"], - inplace=True, ) - reeds_prm.rename(columns={"static": "prm", "t": "planning_horizon"}, inplace=True) + reeds_prm = reeds_prm.rename(columns={"static": "prm", "t": "planning_horizon"}) regional_prm = pd.concat([regional_prm, reeds_prm]) regional_prm = regional_prm[regional_prm.planning_horizon.isin(snakemake.params.planning_horizons)] @@ -836,9 +860,9 @@ def add_operational_reserve_margin(n, sns, config): contingency: 400000 # MW """ reserve_config = config["electricity"]["operational_reserve"] - EPSILON_LOAD = reserve_config["epsilon_load"] - EPSILON_VRES = reserve_config["epsilon_vres"] - CONTINGENCY = reserve_config["contingency"] + eps_load = reserve_config["epsilon_load"] + eps_vres = reserve_config["epsilon_vres"] + contingency = reserve_config["contingency"] # Reserve Variables n.model.add_variables( @@ -856,7 +880,7 @@ def add_operational_reserve_margin(n, sns, config): if not ext_i.empty and not vres_i.empty: capacity_factor = n.generators_t.p_max_pu[vres_i.intersection(ext_i)] p_nom_vres = n.model["Generator-p_nom"].loc[vres_i.intersection(ext_i)].rename({"Generator-ext": "Generator"}) - lhs = summed_reserve + (p_nom_vres * (-EPSILON_VRES * capacity_factor)).sum( + lhs = summed_reserve + (p_nom_vres * (-eps_vres * capacity_factor)).sum( "Generator", ) else: # if no extendable VRES @@ -871,7 +895,7 @@ def add_operational_reserve_margin(n, sns, config): potential = (capacity_factor * renewable_capacity).sum(axis=1) # Right-hand-side - rhs = EPSILON_LOAD * demand + EPSILON_VRES * potential + CONTINGENCY + rhs = eps_load * demand + eps_vres * potential + contingency n.model.add_constraints(lhs >= rhs, name="reserve_margin") @@ -903,7 +927,7 @@ def add_operational_reserve_margin(n, sns, config): def add_battery_constraints(n): """ Add constraint ensuring that charger = discharger, i.e. - 1 * charger_size - efficiency * discharger_size = 0 + 1 * charger_size - efficiency * discharger_size = 0. """ if not n.links.p_nom_extendable.any(): return @@ -931,7 +955,6 @@ def add_sector_co2_constraints(n, config): """ def apply_total_state_limit(n, year, state, value): - sns = n.snapshots snapshot = sns[sns.get_level_values("period") == year][-1] @@ -947,11 +970,10 @@ def apply_total_state_limit(n, year, state, value): n.model.add_constraints(lhs <= rhs, name=f"co2_limit-{year}-{state}") logger.info( - f"Adding {state} co2 Limit in {year} of {rhs* 1e-6} MMT CO2", + f"Adding {state} co2 Limit in {year} of {rhs * 1e-6} MMT CO2", ) def apply_sector_state_limit(n, year, state, sector, value): - sns = n.snapshots snapshot = sns[sns.get_level_values("period") == year][-1] @@ -967,11 +989,10 @@ def apply_sector_state_limit(n, year, state, sector, value): n.model.add_constraints(lhs <= rhs, name=f"co2_limit-{year}-{state}-{sector}") logger.info( - f"Adding {state} co2 Limit for {sector} in {year} of {rhs* 1e-6} MMT CO2", + f"Adding {state} co2 Limit for {sector} in {year} of {rhs * 1e-6} MMT CO2", ) def apply_total_national_limit(n, year, value): - sns = n.snapshots snapshot = sns[sns.get_level_values("period") == year][-1] @@ -984,11 +1005,10 @@ def apply_total_national_limit(n, year, value): n.model.add_constraints(lhs <= rhs, name=f"co2_limit-{year}") logger.info( - f"Adding national co2 Limit in {year} of {rhs* 1e-6} MMT CO2", + f"Adding national co2 Limit in {year} of {rhs * 1e-6} MMT CO2", ) def apply_sector_national_limit(n, year, sector, value): - sns = n.snapshots snapshot = sns[sns.get_level_values("period") == year][-1] @@ -1003,7 +1023,7 @@ def apply_sector_national_limit(n, year, sector, value): n.model.add_constraints(lhs <= rhs, name=f"co2_limit-{year}-{sector}") logger.info( - f"Adding national co2 Limit for {sector} sector in {year} of {rhs* 1e-6} MMT CO2", + f"Adding national co2 Limit for {sector} sector in {year} of {rhs * 1e-6} MMT CO2", ) try: @@ -1021,12 +1041,10 @@ def apply_sector_national_limit(n, year, sector, value): sectors = df.sector.unique() for sector in sectors: - df_sector = df[df.sector == sector] states = df_sector.state.unique() for state in states: - df_state = df_sector[df_sector.state == state] years = [x for x in df_state.year.unique() if x in n.investment_periods] @@ -1037,7 +1055,6 @@ def apply_sector_national_limit(n, year, sector, value): continue for year in years: - df_limit = df_state[df_state.year == year].reset_index(drop=True) assert df_limit.shape[0] == 1 @@ -1045,14 +1062,12 @@ def apply_sector_national_limit(n, year, sector, value): value = df_limit.loc[0, "co2_limit_mmt"] * 1e6 if state.upper() == "USA": - if sector == "all": apply_total_national_limit(n, year, value) else: apply_sector_national_limit(n, year, sector, value) else: - if sector == "all": apply_total_state_limit(n, year, state, value) else: @@ -1077,7 +1092,6 @@ def add_cooling_heat_pump_constraints(n, config): """ def add_hp_capacity_constraint(n, hp_type): - assert hp_type in ("ashp", "gshp") heating_hps = n.links[n.links.index.str.endswith(hp_type)].index @@ -1093,7 +1107,6 @@ def add_hp_capacity_constraint(n, hp_type): n.model.add_constraints(lhs == rhs, name=f"Link-{hp_type}_cooling_capacity") def add_hp_generation_constraint(n, hp_type): - heating_hps = n.links[n.links.index.str.endswith(hp_type)].index if heating_hps.empty: return @@ -1136,7 +1149,6 @@ def add_gshp_capacity_constraint(n, config): - The constraint is: [ASHP - (urban / rural) * GSHP >= 0] - ie. for every unit of GSHP, we need to install 3 units of ASHP """ - pop = pd.read_csv(snakemake.input.pop_layout) pop["urban_rural_fraction"] = (pop.urban_fraction / pop.rural_fraction).round(2) fraction = pop.set_index("name")["urban_rural_fraction"].to_dict() @@ -1157,20 +1169,18 @@ def add_gshp_capacity_constraint(n, config): lhs = ashp_capacity - gshp_capacity.mul(gshp_multiplier.values) rhs = 0 - n.model.add_constraints(lhs >= rhs, name=f"Link-gshp_capacity_ratio") + n.model.add_constraints(lhs >= rhs, name="Link-gshp_capacity_ratio") def add_ng_import_export_limits(n, config): - def _format_link_name(s: str) -> str: states = s.split("-") return f"{states[0]} {states[1]} gas" def _format_data( prod: pd.DataFrame, - link_suffix: Optional[str] = None, + link_suffix: str | None = None, ) -> pd.DataFrame: - df = prod.copy() df["link"] = df.state.map(_format_link_name) if link_suffix: @@ -1182,10 +1192,7 @@ def _format_data( return df[["link", "value"]].rename(columns={"value": "rhs"}).set_index("link") def add_import_limits(n, data, constraint, multiplier=None): - """ - Sets gas import limit over each year. - """ - + """Sets gas import limit over each year.""" assert constraint in ("max", "min") if not multiplier: @@ -1216,10 +1223,7 @@ def add_import_limits(n, data, constraint, multiplier=None): ) def add_export_limits(n, data, constraint, multiplier=None): - """ - Sets maximum export limit over the year. - """ - + """Sets maximum export limit over the year.""" assert constraint in ("max", "min") if not multiplier: @@ -1308,17 +1312,13 @@ def add_export_limits(n, data, constraint, multiplier=None): def add_water_heater_constraints(n, config): - """ - Adds constraint so energy to meet water demand must flow through store. - """ - + """Adds constraint so energy to meet water demand must flow through store.""" links = n.links[(n.links.index.str.contains("-water-")) & (n.links.index.str.contains("-discharger"))] link_names = links.index store_names = [x.replace("-discharger", "") for x in links.index] for period in n.investment_periods: - # first snapshot does not respect constraint e_previous = n.model["Store-e"].loc[period, store_names] e_previous = e_previous.roll(timestep=1) @@ -1335,7 +1335,7 @@ def add_water_heater_constraints(n, config): def add_demand_response_constraints(n, config): """ - Adds demand response equations + Adds demand response equations. Applies the p_nom_max here, as easier to iterate over different configs """ @@ -1344,13 +1344,12 @@ def add_capacity_constraint( n: pypsa.Network, sector: str, shift: float, # as a percentage - carrier: Optional[str] = None, + carrier: str | None = None, ): - """Adds limit on deferable load + """Adds limit on deferable load. No need to multiply out snapshot weights here """ - dr_links = n.links[n.links.carrier.str.endswith("-dr") & n.links.carrier.str.startswith(f"{sector}-")].copy() constraint_name = f"demand_response_capacity-{sector}" @@ -1362,7 +1361,6 @@ def add_capacity_constraint( return if sector != "trn": - deferrable_links = dr_links[dr_links.index.str.endswith("-dr-discharger")] deferrable_loads = deferrable_links.bus1.unique().tolist() @@ -1381,7 +1379,6 @@ def add_capacity_constraint( # transport dr is at the aggregation bus # sum all outgoing capacity and apply the capacity limit to that else: - inflow_links = dr_links[dr_links.index.str.endswith("-dr-discharger")] inflow = n.model["Link-p"].loc[:, inflow_links.index].groupby(inflow_links.bus1).sum() inflow = inflow.rename({"bus1": "Bus"}) # align coordinate names @@ -1404,9 +1401,8 @@ def _apply_constraint( n: pypsa.Network, sector: str, cfg: dict[str, Any], - carrier: Optional[str] = None, + carrier: str | None = None, ): - shift = cfg.get("shift", 0) if isinstance(shift, str): @@ -1415,7 +1411,7 @@ def _apply_constraint( else: logger.info(f"Unknown arguement of {shift} for {sector} DR") raise ValueError(shift) - elif isinstance(shift, (int, float)): + elif isinstance(shift, int | float): if shift < 0.001: logger.info(f"Demand response not enabled for {sector}") else: @@ -1429,7 +1425,6 @@ def _apply_constraint( sectors = ["res", "com", "ind", "trn"] for sector in sectors: - if sector in ["res", "com"]: dr_config = config["sector"]["service_sector"].get("demand_response", {}) elif sector == "trn": diff --git a/workflow/scripts/summary.py b/workflow/scripts/summary.py index b9adfbf05..46d75d4ad 100644 --- a/workflow/scripts/summary.py +++ b/workflow/scripts/summary.py @@ -21,10 +21,7 @@ def get_primary_energy_use(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries primary energy use by bus and carrier. - """ - + """Gets timeseries primary energy use by bus and carrier.""" link_energy_use = ( StatisticsAccessor(n) .withdrawal( @@ -53,14 +50,13 @@ def get_primary_energy_use(n: pypsa.Network) -> pd.DataFrame: return ( pd.concat([gen_energy_use, link_energy_use]) # .reset_index() commenting this out seems to fix issue in multi-horizon indexing - .groupby(["bus", "carrier"]).sum() + .groupby(["bus", "carrier"]) + .sum() ) def get_energy_total(n: pypsa.Network): - """ - Gets energy production totals. - """ + """Gets energy production totals.""" def _get_energy_one_port(n: pypsa.Network, c: str) -> pd.DataFrame: return ( @@ -97,9 +93,7 @@ def _get_energy_multi_port(n: pypsa.Network, c: str) -> pd.DataFrame: def get_energy_timeseries(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries energy production. - """ + """Gets timeseries energy production.""" def _get_energy_one_port(n: pypsa.Network, c: str) -> pd.DataFrame: return ( @@ -151,9 +145,7 @@ def _get_energy_multi_port(n: pypsa.Network, c: str) -> pd.DataFrame: def get_demand_timeseries(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries energy demand. - """ + """Gets timeseries energy demand.""" return pd.DataFrame(n.loads_t.p.sum(1)).rename(columns={0: "Demand"}) @@ -185,9 +177,11 @@ def get_capacity_base(n: pypsa.Network) -> pd.DataFrame: if c.name in ("Generator", "StorageUnit"): totals.append((c.df.p_nom).groupby(by=[c.df.bus, c.df.carrier]).sum()) elif c.name == "Link": - totals.append( - (c.df.p_nom).groupby(by=[c.df.bus0, c.df.carrier]).sum().rename_axis(index={"bus0": "bus"}), - ), + ( + totals.append( + (c.df.p_nom).groupby(by=[c.df.bus0, c.df.carrier]).sum().rename_axis(index={"bus0": "bus"}), + ), + ) totals.append( (c.df.p_nom).groupby(by=[c.df.bus1, c.df.carrier]).sum().rename_axis(index={"bus1": "bus"}), ) @@ -229,8 +223,9 @@ def _economic_retirement(c: str) -> pd.DataFrame: totals = [] if retirement_method == "technical": - if c.name in ("Generator", "StorageUnit", "Link"): - totals.append(_technical_retirement(c)) + for c in n.iterate_components(n.one_port_components | n.branch_components): + if c.name in ("Generator", "StorageUnit", "Link"): + totals.append(_technical_retirement(c)) return pd.concat(totals) elif retirement_method == "economic": for c in n.iterate_components(n.one_port_components | n.branch_components): @@ -250,6 +245,7 @@ def _economic_retirement(c: str) -> pd.DataFrame: def get_capital_costs(n: pypsa.Network) -> pd.DataFrame: + """Returns statistics capex - installed_capex.""" return n.statistics.capex() - n.statistics.installed_capex() @@ -283,7 +279,6 @@ def get_fuel_costs(n: pypsa.Network) -> pd.DataFrame: Units are $/MWh """ - # approximates for 2030 fixed_voms = { "coal": 8.18, @@ -323,19 +318,14 @@ def get_fuel_costs(n: pypsa.Network) -> pd.DataFrame: def get_node_carrier_emissions_timeseries(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries emissions by bus and carrier. - """ + """Gets timeseries emissions by bus and carrier.""" energy = get_primary_energy_use(n) co2 = n.carriers[["nice_name", "co2_emissions"]].reset_index().set_index("nice_name")[["co2_emissions"]].squeeze() return energy.mul(co2, level="carrier", axis=0) def get_node_emissions_timeseries(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries emissions per node. - """ - + """Gets timeseries emissions per node.""" return ( get_node_carrier_emissions_timeseries(n) .droplevel("carrier") @@ -347,10 +337,7 @@ def get_node_emissions_timeseries(n: pypsa.Network) -> pd.DataFrame: def get_tech_emissions_timeseries(n: pypsa.Network) -> pd.DataFrame: - """ - Gets timeseries emissions per technology. - """ - + """Gets timeseries emissions per technology.""" return ( get_node_carrier_emissions_timeseries(n) .droplevel("bus") diff --git a/workflow/scripts/summary_natural_gas.py b/workflow/scripts/summary_natural_gas.py index 1394d5ccb..a001dd168 100644 --- a/workflow/scripts/summary_natural_gas.py +++ b/workflow/scripts/summary_natural_gas.py @@ -1,24 +1,18 @@ -""" -Module for summarizing natural gas results. -""" - -from typing import List +"""Module for summarizing natural gas results.""" import constants import pandas as pd import pypsa from eia import FuelCosts -CODE_2_STATE = {v: k for k, v in constants.STATE_2_CODE.items()} +CODE_2_STATE = constants.CODE_2_STATE MMBTU_2_MWH = constants.MMBTU_MWHthemal MWH_2_MMCF = constants.NG_MWH_2_MMCF STATE_2_CODE = constants.STATE_2_CODE def _rename_columns(n: pypsa.Network, df: pd.DataFrame) -> pd.DataFrame: - """ - Renames columns to carrier nicenames. - """ + """Renames columns to carrier nicenames.""" return df.rename(columns=n.carriers.nice_name) @@ -30,7 +24,6 @@ def get_gas_demand( This in input energy required (ie. not applying efficiency losses) """ - data = {} buses = n.buses[n.buses.index.str.endswith(" gas")].index @@ -53,17 +46,12 @@ def get_imports_exports( n: pypsa.Network, international: bool = True, ) -> dict[str, dict[str, pd.DataFrame]]: - """ - Gets gas flow into and out of the state. - """ - + """Gets gas flow into and out of the state.""" # catches any of the following codes at the start of the string regex = "(?:^|.{2})(?:MX|AB|BC|MB|NB|NL|NT|NS|NU|ON|PE|QC|SK|YT)" def get_import_export(df: pd.DataFrame, direction: str) -> pd.DataFrame: - """ - Input data must be stores dataframe. - """ + """Input data must be stores dataframe.""" if direction == "import": return df[(df.carrier == "gas trade") & (df.bus0.str.endswith(" gas trade"))] elif direction == "export": @@ -72,15 +60,11 @@ def get_import_export(df: pd.DataFrame, direction: str) -> pd.DataFrame: raise NotImplementedError def get_international(df: pd.DataFrame) -> pd.DataFrame: - """ - Input data must be stores dataframe. - """ + """Input data must be stores dataframe.""" return df[(df.bus0.str.contains(regex)) | (df.bus1.str.contains(regex))] def get_domestic(df: pd.DataFrame) -> pd.DataFrame: - """ - Input data must be stores dataframe. - """ + """Input data must be stores dataframe.""" return df[~((df.bus0.str.contains(regex)) | (df.bus1.str.contains(regex)))] df = n.links.copy() @@ -104,7 +88,6 @@ def get_domestic(df: pd.DataFrame) -> pd.DataFrame: data = {} for state in states: - data[state] = {} import_cols = [x for x in imports_t.columns if f"{state} " in x] @@ -117,9 +100,7 @@ def get_domestic(df: pd.DataFrame) -> pd.DataFrame: def get_gas_processing(n: pypsa.Network) -> dict[str, pd.DataFrame]: - """ - Gets timeseries gas processing. - """ + """Gets timeseries gas processing.""" processing = n.links[n.links.carrier == "gas production"] processing = n.links_t.p1[processing.index].mul(-1) @@ -133,9 +114,7 @@ def get_gas_processing(n: pypsa.Network) -> dict[str, pd.DataFrame]: def get_linepack(n: pypsa.Network) -> dict[str, pd.DataFrame]: - """ - Gets linepack data. - """ + """Gets linepack data.""" stores = n.stores[n.stores.carrier == "gas pipeline"] stores = n.stores_t.e[stores.index] @@ -149,9 +128,7 @@ def get_linepack(n: pypsa.Network) -> dict[str, pd.DataFrame]: def get_underground_storage(n: pypsa.Network) -> dict[str, pd.DataFrame]: - """ - Gets underground storage data. - """ + """Gets underground storage data.""" stores = n.stores[n.stores.carrier == "gas storage"] stores = n.stores_t.e[stores.index] @@ -165,10 +142,7 @@ def get_underground_storage(n: pypsa.Network) -> dict[str, pd.DataFrame]: def get_ng_price(n: pypsa.Network) -> dict[str, pd.DataFrame]: - """ - Gets state level natural gas price. - """ - + """Gets state level natural gas price.""" buses = n.buses[n.buses.carrier == "gas"] buses = n.buses_t.marginal_price[buses.index] @@ -182,11 +156,13 @@ def get_ng_price(n: pypsa.Network) -> dict[str, pd.DataFrame]: def get_historical_ng_prices(year: int, industry: str, api: str) -> pd.DataFrame: - """ - Gets hourly fuel price per state. - """ + """Gets hourly fuel price per state.""" df = FuelCosts("gas", year, api, industry).get_data(pivot=True) - idx = pd.date_range(start=df.index.min(), end=(df.index.max() + pd.offsets.MonthEnd(1)).replace(hour=23), freq="h") + idx = pd.date_range( + start=df.index.min(), + end=(df.index.max() + pd.offsets.MonthEnd(1)).replace(hour=23), + freq="h", + ) df = df.reindex(idx).ffill().bfill() # convert $/MCF to $/MWh df = df.mul(1000).div(MWH_2_MMCF).div(MMBTU_2_MWH).round(3) diff --git a/workflow/scripts/summary_sector.py b/workflow/scripts/summary_sector.py index 9bc07c15b..7ea222202 100644 --- a/workflow/scripts/summary_sector.py +++ b/workflow/scripts/summary_sector.py @@ -1,9 +1,6 @@ -""" -Calcualtes summary statistics for sector coupling studies. -""" +"""Calcualtes summary statistics for sector coupling studies.""" import logging -from typing import Optional import pandas as pd import pypsa @@ -30,7 +27,7 @@ # case _: # raise NotImplementedError -# todo - pull this from config +# TODO: Pull PWR_CARRIES from the configuration file PWR_CARRIERS = [ "nuclear", "oil", @@ -52,48 +49,36 @@ def _get_buses_in_state(n: pypsa.Network, state: str) -> list[str]: - """ - Returns buses in a specified state. - """ + """Returns buses in a specified state.""" return n.buses[n.buses.STATE == state].index.to_list() def _get_loads_in_state(n: pypsa.Network, state: str) -> list[str]: - """ - Returns buses in a specified state. - """ + """Returns buses in a specified state.""" buses = _get_buses_in_state(n, state) return n.loads[n.loads.bus.isin(buses)].index.to_list() def _get_links_in_state(n: pypsa.Network, state: str) -> list[str]: - """ - Returns links in a specified state. - """ + """Returns links in a specified state.""" buses = _get_buses_in_state(n, state) return n.links[n.links.bus0.isin(buses) | n.links.bus1.isin(buses)].index.to_list() def _get_gens_in_state(n: pypsa.Network, state: str) -> list[str]: - """ - Returns buses in a specified state. - """ + """Returns buses in a specified state.""" buses = _get_buses_in_state(n, state) return n.generators[n.generators.bus.isin(buses)].index.to_list() def _get_stores_in_state(n: pypsa.Network, state: str) -> list[str]: - """ - Returns buses in a specified state. - """ + """Returns buses in a specified state.""" buses = _get_buses_in_state(n, state) return n.stores[n.stores.bus.isin(buses)].index.to_list() def _filter_link_on_sector(n: pypsa.Network, sector: str) -> pd.DataFrame: - """ - Filters network links to exclude dummy links. - """ + """Filters network links to exclude dummy links.""" match sector: case "res" | "res-urban" | "res-rural" | "res-total" | "com" | "com-urban" | "com-rural" | "com-total": return n.links[ @@ -129,7 +114,7 @@ def _filter_gens_on_sector(n: pypsa.Network, sector: str) -> pd.DataFrame: def _resample_data(df: pd.DataFrame, freq: str, agg_fn: callable) -> pd.DataFrame: if not callable(agg_fn): - f"Must provide resampling function in the form of 'pd.Series.sum'" + "Must provide resampling function in the form of 'pd.Series.sum'" return df else: return df.groupby("period").resample(freq, level="timestep").apply(agg_fn) @@ -141,17 +126,13 @@ def _resample_data(df: pd.DataFrame, freq: str, agg_fn: callable) -> pd.DataFram def get_load_per_sector_per_fuel(n: pypsa.Network, sector: str, fuel: str, period: int): - """ - Time series load per bus per fuel per sector. - """ + """Time series load per bus per fuel per sector.""" loads = n.loads[(n.loads.carrier.str.startswith(sector)) & (n.loads.carrier.str.endswith(fuel))] return n.loads_t.p[loads.index].loc[period] -def get_hp_cop(n: pypsa.Network, state: Optional[str] = None) -> pd.DataFrame: - """ - Com and res hps have the same cop. - """ +def get_hp_cop(n: pypsa.Network, state: str | None = None) -> pd.DataFrame: + """Com and res hps have the same cop.""" cops = n.links_t.efficiency if state: @@ -168,10 +149,9 @@ def _get_opt_capacity_per_node( n: pypsa.Network, sector: str, include_elec: bool = False, - state: Optional[str] = None, + state: str | None = None, ) -> pd.Series: - - assert not sector in ["pwr"] + assert sector not in ["pwr"] df = _filter_link_on_sector(n, sector) @@ -200,10 +180,9 @@ def _get_opt_capacity_per_node( def _get_opt_pwr_capacity_per_node( n: pypsa.Network, group_existing: bool = True, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.Series: - links = _filter_link_on_sector(n, "pwr") gens = _filter_gens_on_sector(n, "pwr") @@ -229,10 +208,9 @@ def _get_total_capacity_per_node( n: pypsa.Network, sector: str, include_elec: bool = False, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - - assert not sector in ["pwr"] + assert sector not in ["pwr"] df = _filter_link_on_sector(n, sector) @@ -259,10 +237,9 @@ def _get_total_capacity_per_node( def _get_total_pwr_capacity_per_node( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - links = _filter_link_on_sector(n, "pwr") gens = _filter_gens_on_sector(n, "pwr") @@ -283,10 +260,9 @@ def _get_total_pwr_capacity_per_node( def _get_brownfield_pwr_capacity_per_node( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - links = _filter_link_on_sector(n, "pwr") gens = _filter_gens_on_sector(n, "pwr") @@ -315,11 +291,10 @@ def _get_brownfield_capacity_per_node( n: pypsa.Network, sector: str, include_elec: bool = False, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - - assert not sector in ["pwr"] + assert sector not in ["pwr"] df = _filter_link_on_sector(n, sector) @@ -354,10 +329,9 @@ def _get_brownfield_capacity_per_node( def get_capacity_per_node( n: pypsa.Network, sector: str, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - if sector == "pwr": total = _get_total_pwr_capacity_per_node( n, @@ -386,9 +360,9 @@ def get_sector_production_timeseries( n: pypsa.Network, sector: str, remove_sns_weights: bool = False, - state: Optional[str] = None, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, + state: str | None = None, + resample: str | None = None, + resample_fn: callable | None = None, ) -> pd.DataFrame: """ Gets timeseries production to meet sectoral demand. @@ -398,7 +372,6 @@ def get_sector_production_timeseries( Note: can not use statistics module as multi-output links for co2 tracking > n.statistics.supply("Link", nice_names=False, aggregate_time=False).T """ - links = _filter_link_on_sector(n, sector).index.to_list() if remove_sns_weights: @@ -419,9 +392,9 @@ def get_sector_production_timeseries( def get_power_production_timeseries( n: pypsa.Network, remove_sns_weights: bool = False, - state: Optional[str] = None, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, + state: str | None = None, + resample: str | None = None, + resample_fn: callable | None = None, ) -> pd.DataFrame: """ Gets power timeseries production to meet sectoral demand. @@ -431,7 +404,6 @@ def get_power_production_timeseries( Note: can not use statistics module as multi-output links for co2 tracking > n.statistics.supply("Link", nice_names=False, aggregate_time=False).T """ - links = _filter_link_on_sector(n, "pwr").index.to_list() gens = _filter_gens_on_sector(n, "pwr").index.to_list() @@ -460,14 +432,11 @@ def get_sector_production_timeseries_by_carrier( n: pypsa.Network, sector: str, remove_sns_weights: bool = False, - state: Optional[str] = None, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, + state: str | None = None, + resample: str | None = None, + resample_fn: callable | None = None, ) -> pd.DataFrame: - """ - Gets timeseries production by carrier. - """ - + """Gets timeseries production by carrier.""" if sector == "pwr": df = get_power_production_timeseries( n, @@ -495,11 +464,9 @@ def get_sector_production_timeseries_by_carrier( def get_sector_max_production_timeseries( n: pypsa.Network, sector: str, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - """ - Max production timeseries at a carrier level. - """ + """Max production timeseries at a carrier level.""" eff = n.get_switchable_as_dense("Link", "efficiency") if state: @@ -518,9 +485,8 @@ def get_load_factor_timeseries( n: pypsa.Network, sector: str, include_elec: bool = False, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - max_prod = get_sector_max_production_timeseries(n, sector, state=state) act_prod = get_sector_production_timeseries(n, sector, state=state) @@ -541,16 +507,14 @@ def get_load_factor_timeseries( def get_emission_timeseries_by_sector( n: pypsa.Network, - sector: Optional[str] = None, - state: Optional[str] = None, + sector: str | None = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - """ - Cummulative emissions by sector in MT. - """ + """Cummulative emissions by sector in MT.""" if sector: if sector == "ch4": - stores_in_sector = [x for x in n.stores.index if f"gas-ch4" in x] + stores_in_sector = [x for x in n.stores.index if "gas-ch4" in x] else: stores_in_sector = [x for x in n.stores.index if f"{sector}-co2" in x] else: @@ -569,10 +533,7 @@ def get_historical_emissions( year: int, api: str, ) -> pd.DataFrame: - """ - Emissions by state/sector in units of million metric tons. - """ - + """Emissions by state/sector in units of million metric tons.""" dfs = [] if isinstance(sectors, str): @@ -593,10 +554,7 @@ def get_historical_end_use_consumption( year: int, api: str, ) -> pd.DataFrame: - """ - End-Use consumption by state/sector in units of MWh. - """ - + """End-Use consumption by state/sector in units of MWh.""" dfs = [] if isinstance(sectors, str): @@ -616,7 +574,6 @@ def get_historical_end_use_consumption( def get_historical_power_production(year: int, api: str) -> pd.DataFrame: - fuel_mapper = { "BIO": "biomass", "COW": "coal", @@ -645,7 +602,7 @@ def get_historical_power_production(year: int, api: str) -> pd.DataFrame: def get_end_use_consumption( n: pypsa.Network, sector: str, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: """ Gets timeseries energy consumption in MWh. @@ -657,13 +614,13 @@ def get_end_use_consumption( def get_service_consumption( n: pypsa.Network, sector: str, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: assert sector in ("res", "com", "ind") loads = n.loads[n.loads.carrier.str.startswith(sector)] if state: - l = _get_loads_in_state(n, state) - loads = loads[loads.index.isin(l)] + load_ = _get_loads_in_state(n, state) + loads = loads[loads.index.isin(load_)] df = n.loads_t.p[loads.index].mul(n.snapshot_weightings["objective"], axis=0).T df.index = df.index.map(n.loads.carrier).map(lambda x: x.split("-")[1:]) df.index = df.index.map(lambda x: "-".join(x)) @@ -671,11 +628,9 @@ def get_service_consumption( def get_transport_consumption( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: - """ - Takes load from p0 link as loads are in kVMT or similar. - """ + """Takes load from p0 link as loads are in kVMT or similar.""" loads = n.links[ (n.links.carrier.str.startswith("trn-")) & ~(n.links.carrier.str.endswith("-veh")) @@ -684,8 +639,8 @@ def get_transport_consumption( & ~(n.links.carrier == ("trn-boat")) ] if state: - l = _get_links_in_state(n, state) - loads = loads[loads.index.isin(l)] + load_ = _get_links_in_state(n, state) + loads = loads[loads.index.isin(load_)] df = n.links_t.p0[loads.index].mul(n.snapshot_weightings["objective"], axis=0).T df.index = df.index.map(lambda x: x.split("trn-")[1]) return df.groupby(level=0).sum().T @@ -703,14 +658,13 @@ def get_end_use_load_timeseries( n: pypsa.Network, sector: str, sns_weight: bool = True, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: """ Gets timeseries load per node. - Residential, Commercial, Industrial are in untis of MWh """ - assert sector in ("res", "com", "ind") loads = n.loads[n.loads.carrier.str.startswith(sector)] @@ -729,10 +683,9 @@ def get_storage_level_timeseries( n: pypsa.Network, sector: str, remove_sns_weights: bool = True, - state: Optional[str] = None, + state: str | None = None, **kwargs, ) -> pd.DataFrame: - stores = n.stores[n.stores.carrier.str.startswith(sector)] if state: @@ -749,12 +702,11 @@ def get_storage_level_timeseries_carrier( n: pypsa.Network, sector: str, remove_sns_weights: bool = True, - state: Optional[str] = None, - resample: Optional[str] = None, - resample_fn: Optional[callable] = None, + state: str | None = None, + resample: str | None = None, + resample_fn: callable | None = None, **kwargs, ) -> pd.DataFrame: - df = get_storage_level_timeseries(n, sector, remove_sns_weights, state) df = df.rename(columns=n.stores.carrier) df = df.T.groupby(level=0).sum().T @@ -769,14 +721,13 @@ def get_end_use_load_timeseries_carrier( n: pypsa.Network, sector: str, sns_weight: bool = True, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: """ Gets timeseries load per node per carrier. - Residential, Commercial, Industrial are in untis of MWh """ - df = get_end_use_load_timeseries(n, sector, sns_weight).T if state: buses = _get_loads_in_state(n, state) @@ -789,7 +740,7 @@ def get_end_use_load_timeseries_carrier( def get_transport_consumption_by_mode( n: pypsa.Network, - state: Optional[str] = None, + state: str | None = None, ) -> pd.DataFrame: df = get_end_use_consumption(n, "trn", state) df = df.rename(columns={x: "-".join(x.split("-")[1:]) for x in df.columns}) @@ -798,10 +749,7 @@ def get_transport_consumption_by_mode( def get_historical_transport_consumption_by_mode(api: str) -> pd.DataFrame: - """ - Will return data in units of MWh. - """ - + """Will return data in units of MWh.""" vehicles = [ "light_duty", "med_duty", From 217a45ab2e004965f5fe45305a022789deaea907 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 16:54:01 -0800 Subject: [PATCH 37/75] new linting --- .pre-commit-config.yaml | 36 +++++++----------------------------- pyproject.toml | 34 +++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1035c701f..7c39110e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,19 +17,13 @@ repos: - id: pretty-format-yaml args: [--autofix, --indent, "2", --preserve-quotes] -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - name: isort (python) - - repo: https://github.com/asottile/add-trailing-comma rev: v3.1.0 hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.19.0 + rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] @@ -45,31 +39,15 @@ repos: hooks: - id: snakefmt -# - repo: https://github.com/PyCQA/docformatter -# rev: v1.7.5 -# hooks: -# - id: docformatter -# args: ["--in-place", "--make-summary-multi-line", "--pre-summary-newline"] - - - repo: https://github.com/keewis/blackdoc rev: v0.3.9 hooks: - id: blackdoc -- repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black - language_version: python3.11 - args: ["--line-length", "120"] - - id: black-jupyter - language_version: python3.11 - args: ["--line-length", "120"] - - -- repo: https://github.com/pycqa/flake8 - rev: 7.1.1 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.4 # Check for the latest version hooks: - - id: flake8 - args: [--select=F841] # F841 is the error code for unused variables + - id: ruff + args: ["--config=pyproject.toml", "--fix"] + - id: ruff-format + args: ["--config=pyproject.toml"] diff --git a/pyproject.toml b/pyproject.toml index 08c1ea782..4917834fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dev = [ myproj = "workflow/scripts" [tool.ruff] -line-length = 110 +line-length = 120 target-version = "py311" exclude = [ ".eggs", @@ -61,29 +61,43 @@ exclude = [ ".venv", ".vscode", "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "site-packages", - "venv", + "_build" ] [tool.ruff.lint] select = [ "E", # pycodestyle "TD", # flake-8 todos - "PD", # pandas vet + #"PD", # pandas vet "RUF", # Ruff rules "N", # pep8 "F", # pyflakes "UP", # pyupgrade "D", # pydocstyle "C90", # Complex code + "I", # isort (import sorting) ] +mccabe.max-complexity = 10 + # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-ignore = ['D105', 'D107', 'D205', 'D415'] +extend-ignore = [ + "D105", # Ignore: Missing docstring in magic methods (__init__, __str__, etc.). + "D107", # Ignore: Missing docstring in __init__ method. + "D205", # Ignore: 1st line of docstring should be separated from the summary with a blank line. + "D415", # Ignore: First line of a docstring should end with a period, question mark, or exclamation mark. + "D401", # Ignore: First line of a docstring should be in imperative mood (e.g., "Get" instead of "Gets"). + "D419", # Ignore: Empty docstrings should not be allowed. + "D103", # Ignore: Missing docstring in a public function. + "TD002", # Ignore: TODO comments should have an author name (e.g., "# TODO (John): Fix this"). + "TD003", # Ignore: TODO comments should have a specific issue/task reference. + "PD010", # Ignore: `df.isna().sum()` should be replaced with `df.isnull().sum()` (allows both styles). + "PD901", # Ignore: Avoid using `df` as a variable name for Pandas DataFrames (allows using `df`). + "C901", # Prevents Ruff from auto-fixing overly complex functions. + "N802", # Function naming lower case + "E501" +] + pydocstyle.convention = "numpy" [tool.ruff.format] @@ -91,5 +105,3 @@ quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" -docstring-code-format = true -docstring-code-line-length = "dynamic" From 39d52ee212dc2ec26b25671ad4ea9c8706cc727a Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 17:07:02 -0800 Subject: [PATCH 38/75] rm isort conf --- .isort.cfg | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .isort.cfg diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index b9fb3f3e8..000000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=black From 7f850c8f79547912fc5610ba26d899a57333fbee Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 17:22:13 -0800 Subject: [PATCH 39/75] rm old docs page --- docs/source/index.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/source/index.md b/docs/source/index.md index 91ca5e7f6..3ff279561 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -82,9 +82,3 @@ license changelog contributing ``` - -```{toctree} -:hidden: -rules-retrieving-data -config-co2-base -``` From 974546f55b860c48f23b726b6ae1992124dae830 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 17:36:29 -0800 Subject: [PATCH 40/75] reorganize repo_data/geospatial --- .../BA_shapes_new/Modified_BE_BA_Shapes.cpg | 0 .../BA_shapes_new/Modified_BE_BA_Shapes.dbf | Bin .../BA_shapes_new/Modified_BE_BA_Shapes.prj | 0 .../BA_shapes_new/Modified_BE_BA_Shapes.shp | Bin .../BA_shapes_new/Modified_BE_BA_Shapes.shx | Bin .../BA_shapes_new/remap_ba_area.cpg | 0 .../BA_shapes_new/remap_ba_area.dbf | Bin .../BA_shapes_new/remap_ba_area.prj | 0 .../BA_shapes_new/remap_ba_area.shp | Bin .../BA_shapes_new/remap_ba_area.shx | Bin .../BA_shapes_new/remap_ba_area.tif | Bin .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg | 0 .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf | Bin .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj | 0 .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd | 0 .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp | Bin .../BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx | Bin .../CEC_Solar_BaseScreen_epsg3310.tif | Bin .../CEC_GIS}/CEC_Wind_BaseScreen_epsg3310.tif | Bin .../NERC_Regions/NERC_Regions.xml | 0 .../NERC_Regions/NERC_Regions_Subregions.cpg | 0 .../NERC_Regions/NERC_Regions_Subregions.dbf | Bin .../NERC_Regions/NERC_Regions_Subregions.prj | 0 .../NERC_Regions/NERC_Regions_Subregions.shp | Bin .../NERC_Regions/NERC_Regions_Subregions.shx | Bin .../Reeds_Shapes/rb_and_ba_areas.cpg | 0 .../Reeds_Shapes/rb_and_ba_areas.dbf | Bin .../Reeds_Shapes/rb_and_ba_areas.prj | 0 .../Reeds_Shapes/rb_and_ba_areas.shp | Bin .../Reeds_Shapes/rb_and_ba_areas.shx | Bin .../boem_osw_planning_areas.tif | Bin workflow/repo_data/network_500.jpg | Bin 140387 -> 0 bytes .../plants/Offshore_Wind_CEC_PLEXOS_2030.csv | 8761 ------ workflow/repo_data/plants/plants_merged.csv | 25386 ---------------- .../western_land_availability_onwind.png | Bin 243969 -> 0 bytes .../western_land_availability_solar.png | Bin 201805 -> 0 bytes .../repo_data/western_osw_availability.png | Bin 37500 -> 0 bytes workflow/rules/build_electricity.smk | 15 +- workflow/scripts/build_shapes.py | 41 +- 39 files changed, 8 insertions(+), 34195 deletions(-) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/Modified_BE_BA_Shapes.cpg (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/Modified_BE_BA_Shapes.dbf (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/Modified_BE_BA_Shapes.prj (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/Modified_BE_BA_Shapes.shp (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/Modified_BE_BA_Shapes.shx (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.cpg (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.dbf (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.prj (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.shp (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.shx (100%) rename workflow/repo_data/{ => geospatial}/BA_shapes_new/remap_ba_area.tif (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp (100%) rename workflow/repo_data/{ => geospatial}/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx (100%) rename workflow/repo_data/{ => geospatial/CEC_GIS}/CEC_Solar_BaseScreen_epsg3310.tif (100%) rename workflow/repo_data/{ => geospatial/CEC_GIS}/CEC_Wind_BaseScreen_epsg3310.tif (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions.xml (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions_Subregions.cpg (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions_Subregions.dbf (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions_Subregions.prj (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions_Subregions.shp (100%) rename workflow/repo_data/{ => geospatial}/NERC_Regions/NERC_Regions_Subregions.shx (100%) rename workflow/repo_data/{ => geospatial}/Reeds_Shapes/rb_and_ba_areas.cpg (100%) rename workflow/repo_data/{ => geospatial}/Reeds_Shapes/rb_and_ba_areas.dbf (100%) rename workflow/repo_data/{ => geospatial}/Reeds_Shapes/rb_and_ba_areas.prj (100%) rename workflow/repo_data/{ => geospatial}/Reeds_Shapes/rb_and_ba_areas.shp (100%) rename workflow/repo_data/{ => geospatial}/Reeds_Shapes/rb_and_ba_areas.shx (100%) rename workflow/repo_data/{ => geospatial}/boem_osw_planning_areas.tif (100%) delete mode 100644 workflow/repo_data/network_500.jpg delete mode 100644 workflow/repo_data/plants/Offshore_Wind_CEC_PLEXOS_2030.csv delete mode 100644 workflow/repo_data/plants/plants_merged.csv delete mode 100644 workflow/repo_data/western_land_availability_onwind.png delete mode 100644 workflow/repo_data/western_land_availability_solar.png delete mode 100644 workflow/repo_data/western_osw_availability.png diff --git a/workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.cpg b/workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.cpg similarity index 100% rename from workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.cpg rename to workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.cpg diff --git a/workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.dbf b/workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.dbf similarity index 100% rename from workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.dbf rename to workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.dbf diff --git a/workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.prj b/workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.prj similarity index 100% rename from workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.prj rename to workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.prj diff --git a/workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shp b/workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.shp similarity index 100% rename from workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shp rename to workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.shp diff --git a/workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shx b/workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.shx similarity index 100% rename from workflow/repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shx rename to workflow/repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.shx diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.cpg b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.cpg similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.cpg rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.cpg diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.dbf b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.dbf similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.dbf rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.dbf diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.prj b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.prj similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.prj rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.prj diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.shp b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.shp similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.shp rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.shp diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.shx b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.shx similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.shx rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.shx diff --git a/workflow/repo_data/BA_shapes_new/remap_ba_area.tif b/workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.tif similarity index 100% rename from workflow/repo_data/BA_shapes_new/remap_ba_area.tif rename to workflow/repo_data/geospatial/BA_shapes_new/remap_ba_area.tif diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.cpg diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.dbf diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.prj diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.qmd diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp diff --git a/workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx b/workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx similarity index 100% rename from workflow/repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx rename to workflow/repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shx diff --git a/workflow/repo_data/CEC_Solar_BaseScreen_epsg3310.tif b/workflow/repo_data/geospatial/CEC_GIS/CEC_Solar_BaseScreen_epsg3310.tif similarity index 100% rename from workflow/repo_data/CEC_Solar_BaseScreen_epsg3310.tif rename to workflow/repo_data/geospatial/CEC_GIS/CEC_Solar_BaseScreen_epsg3310.tif diff --git a/workflow/repo_data/CEC_Wind_BaseScreen_epsg3310.tif b/workflow/repo_data/geospatial/CEC_GIS/CEC_Wind_BaseScreen_epsg3310.tif similarity index 100% rename from workflow/repo_data/CEC_Wind_BaseScreen_epsg3310.tif rename to workflow/repo_data/geospatial/CEC_GIS/CEC_Wind_BaseScreen_epsg3310.tif diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions.xml b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions.xml similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions.xml rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions.xml diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.cpg b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.cpg similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.cpg rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.cpg diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.dbf b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.dbf similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.dbf rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.dbf diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.prj b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.prj similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.prj rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.prj diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.shp b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shp similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.shp rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shp diff --git a/workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.shx b/workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shx similarity index 100% rename from workflow/repo_data/NERC_Regions/NERC_Regions_Subregions.shx rename to workflow/repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shx diff --git a/workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.cpg b/workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.cpg similarity index 100% rename from workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.cpg rename to workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.cpg diff --git a/workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.dbf b/workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.dbf similarity index 100% rename from workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.dbf rename to workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.dbf diff --git a/workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.prj b/workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.prj similarity index 100% rename from workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.prj rename to workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.prj diff --git a/workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.shp b/workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.shp similarity index 100% rename from workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.shp rename to workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.shp diff --git a/workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.shx b/workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.shx similarity index 100% rename from workflow/repo_data/Reeds_Shapes/rb_and_ba_areas.shx rename to workflow/repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.shx diff --git a/workflow/repo_data/boem_osw_planning_areas.tif b/workflow/repo_data/geospatial/boem_osw_planning_areas.tif similarity index 100% rename from workflow/repo_data/boem_osw_planning_areas.tif rename to workflow/repo_data/geospatial/boem_osw_planning_areas.tif diff --git a/workflow/repo_data/network_500.jpg b/workflow/repo_data/network_500.jpg deleted file mode 100644 index 6f84d11b270a74b3f7457446b029d23d18073c36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140387 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 T|9#i;lAAritP$ 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�qrLDb6iIM&H%%vHlh~`jQsUWS~UM#7#z4du{o{h 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 vha$mM^}w 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

Pb4C15bNTj=YtV6aI|EG~{0{ym<4 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

6Z52s_AJ^0|fVFOguEo-{IMCvVZWKrBz0|(I? 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

-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 O@RE~K1XoKK2K|4HzSk50 diff --git a/workflow/repo_data/western_osw_availability.png b/workflow/repo_data/western_osw_availability.png deleted file mode 100644 index e0f0ddfe62db6ee4fc2e5fdfe1b1e01d298ad600..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37500 zcmd43byQX1*FSg(1rNu>mo?nWAvkW>UwO1eWS6=}G1NSA;j-QC@A zf!{uS-}g7~%&eI;e-3N0TnoIMbDn2Ed+$%|^YNLy)a8p57f~qG2Zkt)h&5<~G)x4|pH2G2b+M z^~%fB%OEHuk2hTzN&A@JBA#N~=4dP=xx(H`X_i409AJWZ{YUqvy`?%Og(C z&n1SMHrCRu9=}nT7up^@mr9EDNHq7>O-E587utrP%XukuYFBYnrPnH-u2t@RevEyq z(t5YTQ(WbCLvs4&kyf9Mf@4SffU8Q9v*~PmKnoiI4iS8nTJ+C2q3GZ%v^X5~$@eGYFoa&K*t&8zY=PF@TJdv8$fJn+FZSnCf;R=iPuuH15;H1(#eMSR37@qg+4-=$ za$glwBumQ5CMPByRleC#E3wrN8a!FABm9@6OG`*d=;rQTx;N^oUiQiy{f0py(Wu^! z2>BH&E32EETghCatM%d9!bIH%^U(^v)zww_T@s5XdidE~n1b*Z` zc6|}sg8P;|Jvp{}{d#69Sfs-)4lWvme50*~^3DFxnRF)pz8x(1*xr6~4DEijQtWcL zkYWDs8(d3|yW`8t@lzh3hKmS@ zTCT$ia-aQ@sQHJpGwsKu}cenJ@r-wsjupT@p!``3jr}G6* z!|&-ov>6rDRDb&P)5OAp`LO5dF*<*K-mLCr(P&qdOwIPPndoZs_T`&cnmQ@3v^WiW%wC3Hnv%J`sM25-66Rn%tjOK*2&SDa< Tw|#(2TyNrbp*Nf{r!Dd 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

q z+?$3YIJyo3L0*UhS&w~AF<-A;-!oLGxDmp@F)Y9B<-Ckr$mNeJ)6j=oTrSvq_0*@U zSqGl5iT8Af&DVcXF}!f+z>@SDvvT;c_m<0I=-fPNX&kQqa6Q~@3<+@oD0A}LcB6Cx z(LRfHdOS~02j}d8q};X7qf77|lAH$nLl*RBT$@`mGqUK}*uqWV(ceud3}W&D8Y``- z0a$e2qJk^GfP#wblUty|9vaqPCdeNVRE&(w_@?-lCu}IbVr>IK3kF4pgQKRU(Y!g$ zLGo~j3CH~3{(fXe?gkJ{u}j;%L}g4~QIAZ&qUU{EXO4Dm30fjQ2%8c4AdWB?t9y*+ z_K1p3dk@piy8oF=f$0U86e8dqTuiERa&o%f0POtR_#62o4zU&qe}ZZ8nAw$R+lB+* zEIYkH;FR)r-lewxfp~}eYcWf0T-NkT=Ss zI;BoFaI_^iXPN9@^Dvo5_Vzj5Eigrq zw#2g96{SJ~h3>|HiyMCK^+tP8^6t>l;opfSaFr6@c~@DA3RUy>8@v3O`2QCB1wOc* zDX*g=g(~ZHNyh>EVkq9Chy`bZsFuuW$Zw>9xOu;?$s9U9Saz1E;ZDrXzCYLandnp; z{`u3HE#6mYFN81je9us}JgEEb_-S4CLMUbS_MBth8fzON6LpOIPj-nknu`Yn2U$&o zB8}TW9LruE(_Tq46-A@1ZiAmBhT~an&oR^A51=5JLO4vAd%@r#v9GMM3Bm|Ok<0c8yhfyhk2f9U&EgQaDvlgCFvMbp)idJP4r zv)8U2haL?{IgW6sRQ8H+zj*Lq6X{%tV9DEFs#wibv4qxd28DFJ_9@zwDu`&jWC~#9 z=wEPUe4Z7!@eV%2sfj`);b)_qX0%!Q#qua)ceMJb776O|XJ5{TOia!u2T4!%$-z^@ zJkz(Udco*}Wfe2311f|bS_$1N-YCvZa>YTy27w00?I(AiSLY|!CpsL**0zLUf-XejYUzYr%6N^mfgd<)`e^Vs1DWp_)ZN|XeR zTusX{GDs?n+a6f|$uSof$`RH%3n#SR#@de4S+r|wdM_H~zH$Q10Tg9}YqUx5mmYeS zx->uWwg?I+G--jfRJ*Kp>(_UBS$A1_)YWKLb#cQJhx=|D>)h$h%B)9=T5Fi_24@}2 zSGm}f5OpD}P0K{?!XqQN<{eZ-0_f$9aSf|8V>*Z)#PQ4b!>lP*8L>Cc{TLLHq^4II zNPt6JLZXT2{sJ^>B&D+|d(qj3c)o9yt~1&^U6AA^GbbA9W_B@m7M;8|mk3R%Zu`37!{o5!xYOP`16-gNHbfdsJDAIp4a=OT zN5yZoeLHep(xC{NNMhq8>0SpgZgFCj4ipOh8f~$U?touj|N8RNr%x-i*CA*mT-=nA z;o*m0OE~tWiYwX?*9%Gkj3)J14dT!ZS*#O^9`r6pUZ!_Ag)3}l z#2?;UgT4IKzD$r&NOzmuul|F7S0nZ0{25P1+-{b=BO&bsPr6;bg!6mQ&QBRzu}7t3S{&TD(t(Y|kEH!mC@=y^AXB_#zeI2royA zpYRqwJG6IUVezjCd`y%8fIw62=5}|>)~#>hv}c#SL+;7DS-QZJcj@xmQGiFvNA*2h}*RL4TWaLA*in zMy+3#(JMk0K1`_Knk&5noCwgE$ke)SRmyQJhV57k3QCCHQNH;LibchxRlMw-^z=7e zA9{PElv8w{P9`)V(#J@$#5CjdJVY2#sCV_RNJ%!HmGOsFy%|!mmNS7W{Eiv;F z^rFcYhN_aBN8Y+0wfgb$%q%NG+UH%paP6AY204y-L$X<)VQ(VgI2O^n1T_{S+m;R? zP*csD645Wpx0uCw0zSA zmxF{W1p>pmrRIv~2uxD+C#4QuiGMav$ZzCOZK4lH%3Pm#U6N49BuGJugZkpduQ$&U zTzsUgR<2S1-SJy{f_2?9GE$wQDqV)#4+UL=+7&&&?>o;JXi4ea6=n2X&k?x?E3Tia z-+S$OE7C|u`Yv3s+wbeGXdFSNzaO{mCP;F(1cMX{d}UZdHtrn)UWcUlh+!!;pwk%u zvK$8uSz!E_%gn9TfA8Qi!&S1Rk?ic`^dr~M^>@;B0jjhHZVl*6V>AckTjyTs42zkbraN>3Y>V-$!8FERdk>oOhmk2Ir zSCgK{dtheVz{yO{Zu-CFaqA9ls#0Oc1XlYR?&IdKx-(k%a69QrqE53qq_vjkH*2Tn zhHTKB+t-qMp_ki6_+fxVl3ktI1yT_3po3`+qa!1?{(R}SR3z0HiyEga%05ZHJn;1P zP}_4f1JvR}4--&}k+2l>&FlpJvXbT<2TvlgLb(7#Y_D_&ESj=O4XHeEMbp}}?WUr_)_STcMPG+=ZF zu#A%3M_p{bL_1yFgR2D+yA;!2YlLoU)v3&qj$=Iw$-N*A?5{I~P(g_xXFfn5_$&N` zVBFB`u63O(yKx!Jn;XDepufy0_*K*|Sha)tS^P zb{Y2_MZ(9Or+)K39Q@=B3@tgbE(6KoCzfVf8 zm}|ejoAvbG>#dSm@p|?!@uI|P*Z1SOo1Zr5Do0qnZ}d6tKx?CzIUl@eJ_-7gl| zkAI~b+e%JX7XN5|^XUdT-LQ$B)=yjlYv&yf;0!kwI;Y z>W$W{8HqjWD|3B<{!+C2^xEo)T^{#fGI66lS|>gHx22NULI!v-M=!TBMLB?aQN%c6Rr{hT4DoU%dSTpYqM8I z?)fU^{vgWb!)wEq`iW?;8$7ilMdUcW>gp*-GslK5z}=}-tMo1C>LIpM1_s>p8c;0W zN7MF+kB=l`%e6X#N>w83vm-3NYPTR+c9Ht=8W{~aImOrO{+a9#qoaBFf^Ui>Xf_lA zi-4@?Pm7FrpC){yW$3X@5%RQ^7!%^@v&~HjGge#I3ZV9&r&}1VcdSN8IB!Gw z|M$4&>Ghx17b_E)QG#1oTK;N2APA79rKITMAaCT2vX0TkR(gnXh#aHBA>$Y&wPr)U zgZZ!C;=Hyt=aM0#w$`PN^@E>R-V#5sVS0IMEE2ZxFYqVnSAquIcOF#;^ZvDeWFiS= zAKh){bb4COCb`b+N-c-kvRqb=&iU+Z`7lmS?{~*XenVubIV$rG@0KarGj_Z9!T56V zM2v_=l1|A@kt0jJbbCKBAD5K-K=$;&r!KNJ!J{tUSkQK*cw-b-D80nXZO2Q5W6viV zzYE#g+>Tj~)>JH3P*Ci>d8cq@pjS?W#%7_nsUvcg*DnBi&gSL_0$b8YRHxMTZQ8VH zqTt{BpOsc)L0#(of6q~S-K~S%Ad*(RjRY;N&VKuzBNC4P$n)_QH^>ss zibR^g05esZrW_Sv)NSNV!Or~P)k-gcHHEzO@v;G}sE+zJrC z$Uk~)*Iz}{r;?~RA=nEfEO)8lzb_882meSX|Nh5o>#Y$wxFs#OLS1e%Z({=D%gVxP z@yZz1SR_IERlA8|IKZY~uJPZ7sax*}+xGm)59j@$YzJm9=J@;GU!SS{%A)sHK-$ha zJJj_4Obsg`dE`W&|2y&RNntsJ><$0>aEON_DFfj9fQ z#}oekdTI1{y@vs9FGL(fe1GmGe}Q&V-s=El(S0;Td-nO%Qg~AG&7W$z>>|{>DPf`? zRd!)V|2wRKSvB1J{1Jkqne>c|Dz~p3C7G)yy*Elxf1pckRD4)yz#cwYyQmGwMMyZW z*GkY0=1q$XyfT8EqZieCy`!EyVreKg$Qq2iR@;nhWaG-re-GXhKQQ#?i5kXp5fo%#%&DvLZdWzO5?d z8r0M_Xz6z7rp==(sOc|v?|yTNIURI5F9kHj#_uuESMU44-5GLu$N%ChD{QIMz2<TGL85MQ^CkEETaoF?vAdwPep|ZwFd`V7NM{mn#((#`uVdt~O2>Pb3I+t{A0cEm3Qw zH9{YhVaSA9G0?NP%|pu}E*fk|5i+TZ+z&?@*E>89f~otQyTqWPkAUs8zj6S9G6)iJ zen$V%C9s5S9PG-_gfbr(9tdxG3yS8(evJXJh_u6bQ3Pcngn1Xx&fBszwz|o%jGl{2 zHSog|K8;){*fzRX9cA-{<0GW)gJFyWJGOPBA~x1=$p&Zi)YQT6KzCNaA_xWBFPf%9 zK{R{O)}8n#E9N?9Gx^RqffbYk`<{iZj6!2aP=qgF)yN%b0 zl!2+*@Mr*ⅈ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 N6;$LCPwC(LzW`Yuq-FpB diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 05b01a588..38ecabe60 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -9,10 +9,10 @@ rule build_shapes: offwind_params=config["renewable"]["offwind"], input: zone=DATA + "breakthrough_network/base_grid/zone.csv", - nerc_shapes="repo_data/NERC_Regions/NERC_Regions_Subregions.shp", - reeds_shapes="repo_data/Reeds_Shapes/rb_and_ba_areas.shp", - onshore_shapes="repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shp", - offshore_shapes_ca_osw="repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp", + nerc_shapes="repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shp", + reeds_shapes="repo_data/geospatial/Reeds_Shapes/rb_and_ba_areas.shp", + onshore_shapes="repo_data/geospatial/BA_shapes_new/Modified_BE_BA_Shapes.shp", + offshore_shapes_ca_osw="repo_data/geospatial/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp", offshore_shapes_eez=DATA + "eez/conus_eez.shp", county_shapes=DATA + "counties/cb_2020_us_county_500k.shp", output: @@ -161,9 +161,9 @@ rule build_renewable_profiles: ), country_shapes=RESOURCES + "{interconnect}/Geospatial/country_shapes.geojson", offshore_shapes=RESOURCES + "{interconnect}/Geospatial/offshore_shapes.geojson", - cec_onwind="repo_data/CEC_Wind_BaseScreen_epsg3310.tif", - cec_solar="repo_data/CEC_Solar_BaseScreen_epsg3310.tif", - boem_osw="repo_data/boem_osw_planning_areas.tif", + cec_onwind="repo_data/geospatial/CEC_GIS/CEC_Wind_BaseScreen_epsg3310.tif", + cec_solar="repo_data/geospatial/CEC_GIS/CEC_Solar_BaseScreen_epsg3310.tif", + boem_osw="repo_data/geospatial/boem_osw_planning_areas.tif", regions=lambda w: ( RESOURCES + "{interconnect}/Geospatial/regions_onshore.geojson" if w.technology in ("onwind", "solar") @@ -581,7 +581,6 @@ rule add_electricity: + "{interconnect}/Geospatial/regions_offshore.geojson", reeds_shapes=RESOURCES + "{interconnect}/Geospatial/reeds_shapes.geojson", powerplants=RESOURCES + "powerplants.csv", - plants_eia="repo_data/plants/plants_merged.csv", plants_breakthrough=DATA + "breakthrough_network/base_grid/plant.csv", hydro_breakthrough=DATA + "breakthrough_network/base_grid/hydro.csv", bus2sub=RESOURCES + "{interconnect}/bus2sub.csv", diff --git a/workflow/scripts/build_shapes.py b/workflow/scripts/build_shapes.py index d74702169..d32728377 100644 --- a/workflow/scripts/build_shapes.py +++ b/workflow/scripts/build_shapes.py @@ -1,43 +1,4 @@ -# BY PyPSA-USA Authors -""" -**Description**. - -The `build_shapes` rule builds the GIS shape files for the balancing authorities and offshore regions. The regions are only built for the {interconnect} wildcard. Because balancing authorities often overlap- we modify the GIS dataset developed by [Breakthrough Energy Sciences](https://breakthrough-energy.github.io/docs/). - -**Relevant Settings** - -.. code:: yaml - - interconnect: - -**Inputs** - -- ``breakthrough_network/base_grid/zone.csv``: confer :ref:`base` -- ``repo_data/BA_shapes_new/Modified_BE_BA_Shapes.shp``: confer :ref:`base` -- ``repo_data/BOEM_CA_OSW_GIS/CA_OSW_BOEM_CallAreas.shp``: confer :ref:`base` - -**Outputs** - -- ``resources/country_shapes.geojson``: - - # .. image:: ../img/regions_onshore.png - # :scale: 33 % - -- ``resources/onshore_shapes.geojson``: - - # .. image:: ../img/regions_offshore.png - # :scale: 33 % - -- ``resources/offshore_shapes.geojson``: - - # .. image:: ../img/regions_offshore.png - # :scale: 33 % - -- ``resources/state_boundaries.geojson``: - - # .. image:: ../img/regions_offshore.png - # :scale: 33 % -""" +"""The `build_shapes` rule builds the GIS shape files for the balancing authorities and offshore regions. The regions are only built for the {interconnect} wildcard.""" import logging From 3f478a1be269a557574acef7984372007b715732 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 17:40:30 -0800 Subject: [PATCH 41/75] clean repo_data --- .../{ => WECC_ADS_public}/eia_ads_generator_mapping_updated.csv | 0 workflow/rules/build_electricity.smk | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename workflow/repo_data/{ => WECC_ADS_public}/eia_ads_generator_mapping_updated.csv (100%) diff --git a/workflow/repo_data/eia_ads_generator_mapping_updated.csv b/workflow/repo_data/WECC_ADS_public/eia_ads_generator_mapping_updated.csv similarity index 100% rename from workflow/repo_data/eia_ads_generator_mapping_updated.csv rename to workflow/repo_data/WECC_ADS_public/eia_ads_generator_mapping_updated.csv diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 38ecabe60..348a4b031 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -773,7 +773,7 @@ rule build_powerplants: input: pudl=DATA + "pudl/pudl.sqlite", wecc_ads="repo_data/WECC_ADS_public", - eia_ads_generator_mapping="repo_data/eia_ads_generator_mapping_updated.csv", + eia_ads_generator_mapping="repo_data/WECC_ADS_public/eia_ads_generator_mapping_updated.csv", fuel_costs="repo_data/plants/fuelCost22.csv", cems="repo_data/plants/cems_heat_rates.xlsx", epa_crosswalk="repo_data/plants/epa_eia_crosswalk.csv", From e63ff207e6f1da3915a6d921435dd9f59046afdb Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:01:48 -0800 Subject: [PATCH 42/75] Add UV install support! #483 --- .isort.cfg | 2 - pyproject.toml | 37 + uv.lock | 3113 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3150 insertions(+), 2 deletions(-) delete mode 100644 .isort.cfg create mode 100644 uv.lock diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index b9fb3f3e8..000000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=black diff --git a/pyproject.toml b/pyproject.toml index 4917834fb..b88fd5f2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,43 @@ classifiers = [ ] license = {file = "LICENSE.md"} readme = "README.md" +dependencies = [ + "atlite==0.3.0", + "cartopy==0.23.0", + "descartes==1.1.0", + "dill>=0.3.9", + "duckdb==0.10.0", + "geopandas==1.0.1", + "geopy==2.4.0", + "graphviz>=0.20.3", + "gurobipy==11.0.3", + "highspy>=1.9.0", + "kaleido==0.4.0rc5", + "linopy==0.3.14", + "matplotlib==3.8.0", + "netcdf4==1.6.4", + "networkx==3.1", + "numpy==1.26.0", + "openpyxl==3.1.2", + "pandas==2.2.2", + "plotly==5.17.0", + "progressbar2==4.3.2", + "pulp==2.7.0", + "pyarrow==16.1.0", + "pycountry==22.3.5", + "pyomo==6.6.1", + "pypsa==0.30.2", + "pyyaml>=6.0.2", + "rasterio==1.3.8", + "scipy==1.11.3", + "seaborn==0.13.2", + "shapely==2.0.2", + "snakemake==7.32.4", + "tsam>=2.3.6", + "xarray==2024.9.0", + "xlrd==2.0.1", +] + [project.urls] Documentation = "https://pypsa-usa.readthedocs.io/en/latest/" diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..5a330fde0 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3113 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version < '3.12'", +] + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, +] + +[[package]] +name = "affine" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/98/d2f0bb06385069e799fc7d2870d9e078cfa0fa396dc8a2b81227d0da08b9/affine-2.4.0.tar.gz", hash = "sha256:a24d818d6a836c131976d22f8c27b8d3ca32d0af64c1d8d29deb7bafa4da1eea", size = 17132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/f7/85273299ab57117850cc0a936c64151171fac4da49bc6fba0dad984a7c5f/affine-2.4.0-py3-none-any.whl", hash = "sha256:8a3df80e2b2378aef598a83c1392efd47967afec4242021a0b06b4c7cbc61a92", size = 15662 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, +] + +[[package]] +name = "atlite" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bottleneck" }, + { name = "cdsapi" }, + { name = "dask" }, + { name = "geopandas" }, + { name = "netcdf4" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "progressbar2" }, + { name = "pyproj" }, + { name = "pyyaml" }, + { name = "rasterio" }, + { name = "requests" }, + { name = "scipy" }, + { name = "shapely" }, + { name = "toolz" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/b1/2e819f719452cc783e010e5c83741f30924af400ccc096f429807c214065/atlite-0.3.0.tar.gz", hash = "sha256:7d6cfdb0b4e92f590f306ac51c7680f0f662da576148dea1c2ae57dcce634085", size = 1875107 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/25/e87b3d1cfc50c78b694c4033acec9bbbada159cf3aa71d347cb0578c9881/atlite-0.3.0-py3-none-any.whl", hash = "sha256:7e04fa04b45dc815c61bb1992969456a79525171b395bbb52f2779a0e80bdc23", size = 125745 }, +] + +[[package]] +name = "attrs" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 }, +] + +[[package]] +name = "blosc2" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "msgpack" }, + { name = "ndindex" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "py-cpuinfo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/75/6b67b3c772ad014458e0caebc49c6210ef95076e3370d0809a3b49d3efa2/blosc2-3.0.0.tar.gz", hash = "sha256:d8c03a09ed11b644b48bf050bd108972ec56ac9cbc3f2aedca077255ed81ac69", size = 7331805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/8f/a58f57e0a6060d18c367d05655d7c19887feb657e65272e1feaf9adc04cd/blosc2-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:027158b6c6de85a5c7d2e60066819df58efb6a0d0108b613db2118453d0e3a8f", size = 3975036 }, + { url = "https://files.pythonhosted.org/packages/31/c7/692c821d3235d4a979aed13171f3d1d59ff83bd137e58e0c2d8e21d4f76c/blosc2-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3514427c04f8f3180b07807eab736d694db21dea518ffde6e36359657deb922", size = 3358207 }, + { url = "https://files.pythonhosted.org/packages/39/da/f8371965e5fb167e815d68a1ddb5543e6b094a2abc2f8c0330d6188244e8/blosc2-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3213763e8ed8f0dd81393e5ecf70fa6a498160aaec6611eb7d37d462b2814824", size = 4254723 }, + { url = "https://files.pythonhosted.org/packages/df/61/7c36c780f29f11e4e7627c5c8475788e8fc539b13d6352dab59e731771aa/blosc2-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d2a11c4241e4f718931d0ed7ce2519badd9a326c1cf631ac1355b6921d732f9", size = 4408605 }, + { url = "https://files.pythonhosted.org/packages/e2/f0/1dd1fa40acd8064d6895bd71052e295876e8bb16ad83fa0d48fc1f5bd6c6/blosc2-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a23b23dab715a3918564c70ffc2f75d0c103f80be41718959fa55bc805f66b6", size = 2176712 }, + { url = "https://files.pythonhosted.org/packages/0d/b8/e4fcf10610de0a69fca8a55a4b7a636386a30f376a48101b305645dab696/blosc2-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff945f653c111aa02008bc530b48565499a806899f3ff5ff6ea7f9e73e9a660b", size = 3986559 }, + { url = "https://files.pythonhosted.org/packages/87/fb/9ed609b59c3303f456db5fc5c0231cd441d7630f1777685fab8b1b56329d/blosc2-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a8ba5eb416d125f4dac9ae89fe1915b7ef0d84ee876e42815cb3093f998cb4e5", size = 3358355 }, + { url = "https://files.pythonhosted.org/packages/6e/d1/0710966dc56924cce66bdb44f9bfebca342699fa12780ca6eb9622fc25b6/blosc2-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecba37aa87b2a2974a8bc1316d7c85e1d42e2f4e0aef38267ae1e3d55d8cdf30", size = 4234403 }, + { url = "https://files.pythonhosted.org/packages/00/20/1aaa380bef966598ecdb0c0ef8daea8d3451acaaa0abd9c29b8eede67871/blosc2-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5567bc9d966f7215a81f4e2ba24628d5d9e66c87b79a3d011ffdd2ffe40b74ee", size = 4392236 }, + { url = "https://files.pythonhosted.org/packages/c8/09/a2bff6e1b87c0addc93e963533cb46834512f9be435a424cd569927e8f0c/blosc2-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a209aec1f365063f0a887a3fb6f37690da11d0f35411cc538bab25fd3d71498", size = 2171007 }, + { url = "https://files.pythonhosted.org/packages/89/eb/93b5b83220121bd177f8392d358ebc3a21586aa1e9916feac653d8ce6cb1/blosc2-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7e5220b83cf336391f883eba745206450efae766ed40c237b9ea41877306ae0", size = 3984594 }, + { url = "https://files.pythonhosted.org/packages/34/c8/5280f3f61a59ad4170a42ff6975bffd362d720530a9c0b52d0e849d68c33/blosc2-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8da04add23d4229bbfdd2a3be3baaa0c8e3035eec02ff484a9ca97c76aff2ed7", size = 3356546 }, + { url = "https://files.pythonhosted.org/packages/74/b9/a252f0723b52a220295cb7a90ba5dd60b8d85a69246d933bc35db0704f99/blosc2-3.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d949e86522a6001ed8e8bca0a29c8638c5b3a0900376a48a9536864cccb4b89", size = 4233755 }, + { url = "https://files.pythonhosted.org/packages/4b/ad/84329757bb623b2c427adb26018aa95b6465a85900af57c5df3666918870/blosc2-3.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e7cdad6c2361d2e2ed2de33f18ef073a3d4dc7a9beb4d1db172005e09b229e", size = 4391208 }, + { url = "https://files.pythonhosted.org/packages/79/86/e2d0aad6916f5d2160b8a6b513b3b92c2a15b669a243b6e10ba059473453/blosc2-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:37de3eab82df2738b2d81fa2fbfa46efdb108b7b54b37f1255f358f90ab7212d", size = 2171060 }, +] + +[[package]] +name = "bottleneck" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/61/9fb34409d58f04e1929da41666a055c36f9495903ff669b80c893bdee65f/bottleneck-1.4.2.tar.gz", hash = "sha256:fa8e8e1799dea5483ce6669462660f9d9a95649f6f98a80d315b84ec89f449f4", size = 103563 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b8/31a1cc8279bf11a60c04b844a42666927307a47bb48964cbd92ec9f40e3e/Bottleneck-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6902ebf3e85315b481bc084f10c5770f8240275ad1e039ac69c7c8d2013b040", size = 98565 }, + { url = "https://files.pythonhosted.org/packages/16/64/09d72babae7cc29341c52f2e9381066672743d4f797c86b1e735205d5fc8/Bottleneck-1.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2fd34b9b490204f95288f0dd35d37042486a95029617246c88c0f94a0ab49fe", size = 364986 }, + { url = "https://files.pythonhosted.org/packages/7e/d6/39e957e9df9ab16df9c531e8ddf71594877063d27aa036dd105b66d3b3b3/Bottleneck-1.4.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122845e3106c85465551d4a9a3777841347cfedfbebb3aa985cca110e07030b1", size = 360256 }, + { url = "https://files.pythonhosted.org/packages/ff/cb/d287febe0e6504194ba94cf4a6d80df66a0031ca33a32b30f00c030238cc/Bottleneck-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1f61658ebdf5a178298544336b65020730bf86cc092dab5f6579a99a86bd888b", size = 369507 }, + { url = "https://files.pythonhosted.org/packages/dc/1e/9310f058ddee71798a76ab15c5c1ad71f0a5c3c6348f7faab9b6da038484/Bottleneck-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c7d29c044a3511b36fd744503c3e697e279c273a8477a6d91a2831d04fd19e0", size = 360282 }, + { url = "https://files.pythonhosted.org/packages/96/cb/c1f2a37e86e9fa47845259f0a8f32d550f7f27b908432369de055be9f7c4/Bottleneck-1.4.2-cp311-cp311-win32.whl", hash = "sha256:c663cbba8f52011fd82ee08c6a85c93b34b19e0e7ebba322d2d67809f34e0597", size = 106936 }, + { url = "https://files.pythonhosted.org/packages/d3/eb/3fd23404bbc612cf9e4883c3c2b359bd14528e234d5c40bb29bcfd591ef8/Bottleneck-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:89651ef18c06616850203bf8875c958c5d316ea48d8ba60d9b450199d39ae391", size = 111617 }, + { url = "https://files.pythonhosted.org/packages/d2/26/6f5124e31a67f75e2a3b9239cc382145326e91fc45e7d7bc9ebffa05fdfa/Bottleneck-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a74ddd0417f42eeaba37375f0fc065b28451e0fba45cb2f99e88880b10b3fa43", size = 98681 }, + { url = "https://files.pythonhosted.org/packages/c4/93/e100b6eda77f2aecf5f16157b8c04dd3463913ba188b582650cd77ccf42b/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:070d22f2f62ab81297380a89492cca931e4d9443fa4b84c2baeb52db09c3b1b4", size = 365422 }, + { url = "https://files.pythonhosted.org/packages/82/2b/c6fea2bb048d04c13b8564052818a198d50ce58d5f439ec69c2b0c458703/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc4e7645bd425c05e05acd5541e9e09cb4179e71164e862f082561bf4509eac", size = 361844 }, + { url = "https://files.pythonhosted.org/packages/8f/4c/811475885bd60cf0cb28822568d0c0c3c7d7de4fbccd2ebb66863e7dc726/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:037315c56605128a39f77d19af6a6019dc8c21a63694a4bfef3c026ed963be2e", size = 370369 }, + { url = "https://files.pythonhosted.org/packages/fd/ee/0a8157e6bbd2168bf6171811534a5a73a35f54c453dd7d86a323773b5bd7/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99778329331d5fae8df19772a019e8b73ba4d9d1650f110cd995ab7657114db0", size = 361786 }, + { url = "https://files.pythonhosted.org/packages/fa/6b/e8fda0510b8fa0f3f9a3586efc941abe9d546198e95ae5690c3c83370b36/Bottleneck-1.4.2-cp312-cp312-win32.whl", hash = "sha256:7363b3c8ce6ca433779cd7e96bcb94c0e516dcacadff0011adcbf0b3ac86bc9d", size = 107149 }, + { url = "https://files.pythonhosted.org/packages/22/25/908b75a329a05b82d717661aa95a1968d9dae0e68c654d5e16bfe0d6fbb6/Bottleneck-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:48c6b9d9287c4102b803fcb01ae66ae7ef6b310b711b4b7b7e23bf952894dc05", size = 111766 }, + { url = "https://files.pythonhosted.org/packages/2e/65/148e146ca8c16af9881a0db1d8d1849d49a5186fc9f065c79a8d25d6fc0c/Bottleneck-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c1c885ad02a6a8fa1f7ee9099f29b9d4c03eb1da2c7ab25839482d5cce739021", size = 98701 }, + { url = "https://files.pythonhosted.org/packages/80/96/6540ac9a9943b0d6f0199eddbde55e878f970d2bdda31207dc3e7a195c2b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7a1b023de1de3d84b18826462718fba548fed41870df44354f9ab6a414ea82f", size = 365443 }, + { url = "https://files.pythonhosted.org/packages/d0/aa/ccae264aac3b2621fa8a98c7afe033f22a352467cbf85fa2799d176ec31b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9dbaf737b605b30c81611f2c1d197c2fd2e46c33f605876c1d332d3360c4fc", size = 361849 }, + { url = "https://files.pythonhosted.org/packages/f3/b3/5f96d7bb23a291b835bf0a34eec359c55613f6c4262ad1bb161d897499c0/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7ebbcbe5d4062e37507b9a81e2aacdb1fcccc6193f7feff124ef2b5a6a5eb740", size = 370654 }, + { url = "https://files.pythonhosted.org/packages/51/05/9d1ababa3fd34014b708351270307320c0bc595d2d66c2ba2b9b92f0d618/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:964f6ac4118ddab3bbbac79d4f726b093459be751baba73ee0aa364666e8068e", size = 362054 }, + { url = "https://files.pythonhosted.org/packages/92/e3/123488804830604432f84a2c43e611b8e1971e230b9466a7315850d22a58/Bottleneck-1.4.2-cp313-cp313-win32.whl", hash = "sha256:2db287f6ecdbb1c998085eca9b717fec2bfc48a4ab6ae070a9820ba8ab59c90b", size = 107160 }, + { url = "https://files.pythonhosted.org/packages/54/f0/e1640ccd8468c61693092f38f835ef35a68a1ea72c3388683148b3800aa6/Bottleneck-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:26b5f0531f7044befaad95c20365dd666372e66bdacbfaf009ff65d60285534d", size = 111774 }, +] + +[[package]] +name = "bump2version" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/2a/688aca6eeebfe8941235be53f4da780c6edee05dbbea5d7abaa3aab6fad2/bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6", size = 36236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/e3/fa60c47d7c344533142eb3af0b73234ef8ea3fb2da742ab976b947e717df/bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", size = 22030 }, +] + +[[package]] +name = "cartopy" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyproj" }, + { name = "pyshp" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/00/fed048eeb80129908f8bd5f5762e230864abc8a8fbbc493740bafcb696bd/Cartopy-0.23.0.tar.gz", hash = "sha256:231f37b35701f2ba31d94959cca75e6da04c2eea3a7f14ce1c75ee3b0eae7676", size = 10687976 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/79/7b8731fb3370482eee4e59e3070e7b0abd71aa8b7e87fce757ce19f37fc1/Cartopy-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86b07b6794b616674e4e485b8574e9197bca54a4467d28dd01ae0bf178f8dc2b", size = 10936638 }, + { url = "https://files.pythonhosted.org/packages/fb/96/02e0445ab41997f942e3d59c9fc3d08220e9b2651f85ac026a9dff7ce28f/Cartopy-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dece2aa8d5ff7bf989ded6b5f07c980fb5bb772952bc7cdeab469738abdecee", size = 10921586 }, + { url = "https://files.pythonhosted.org/packages/07/31/11428c479d9eddb5a1f1bef9ad51320db35c612f992163b72739581eef6e/Cartopy-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9dfd28352dc83d6b4e4cf85d84cb50fc4886d4c1510d61f4c7cf22477d1156f", size = 11662265 }, + { url = "https://files.pythonhosted.org/packages/e1/a2/a450fe2d20be42c6666a5146697a6ea1ab9caa1940cce892af02b492c3db/Cartopy-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2671b5354e43220f8e1074e7fe30a8b9f71cb38407c78e51db9c97772f0320b", size = 10904808 }, + { url = "https://files.pythonhosted.org/packages/72/82/6d44fc5a4e7ff859b4ca2dd4eaeae680c75e2e3c16ee6f0dde02fc0fad43/Cartopy-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:80b9fd666fd47f6370d29f7ad4e352828d54aaf688a03d0b83b51e141cfd77fa", size = 10931850 }, + { url = "https://files.pythonhosted.org/packages/b2/96/f2f271e971185e38e940498421a97cb1e5dac970919bd5d65c2c430fe1f5/Cartopy-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43e36b8b7e7e373a5698757458fd28fafbbbf5f3ebbe2d378f6a5ec3993d6dc0", size = 10921507 }, + { url = "https://files.pythonhosted.org/packages/82/7d/77f96f369f23182f42b7e124536d7022485c31e51204d0673fc0a43ea28d/Cartopy-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550173b91155d4d81cd14b4892cb6cabe3dd32bd34feacaa1ec78c0e56287832", size = 11660854 }, + { url = "https://files.pythonhosted.org/packages/a2/65/6dae4e0a1414251a7bc3c2d9f87853419f921bac991e35701b938b7713dc/Cartopy-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:55219ee0fb069cc3254426e87382cde03546e86c3f7c6759f076823b1e3a44d9", size = 10904186 }, +] + +[[package]] +name = "cdsapi" +version = "0.7.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "datapi" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/62/81b38105ef75486308179d510360c1aa251ce0b7d17fe88c0e1de05c6c94/cdsapi-0.7.5.tar.gz", hash = "sha256:55221c573b8cefe83cc0bfe01a3d31213c82bf9acce70455350dd24b8095c23a", size = 13188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/c4/49f01f1382d449581d5d3db0fa49ccc23a1b4f91d615108b631c7cff40cd/cdsapi-0.7.5-py2.py3-none-any.whl", hash = "sha256:8586b837aea89ceeae379b388fbb0ace0a19b94b221f731c65632417007f69fb", size = 12201 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "cftime" +version = "1.6.4.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/c8/1155d1d58003105307c7e5985f422ae5bcb2ca0cbc553cc828f3c5a934a7/cftime-1.6.4.post1.tar.gz", hash = "sha256:50ac76cc9f10ab7bd46e44a71c51a6927051b499b4407df4f29ab13d741b942f", size = 54631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/e6/6a7d2120fcffee208cf637d22b0d8f2701d91f69f68a96940056429950f3/cftime-1.6.4.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1bf7be0a0afc87628cb8c8483412aac6e48e83877004faa0936afb5bf8a877ba", size = 233445 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/fe0d14d52cffa72d3f1c281ff9f0f384968058d86ce24fdf9e736ce5b755/cftime-1.6.4.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0f64ca83acc4e3029f737bf3a32530ffa1fbf53124f5bee70b47548bc58671a7", size = 214458 }, + { url = "https://files.pythonhosted.org/packages/55/c6/72f8fb5ee057f33ab747ba361f1396d2839a4689669aabd6217bc38430f7/cftime-1.6.4.post1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ebdfd81726b0cfb8b524309224fa952898dfa177c13d5f6af5b18cefbf497d", size = 1379075 }, + { url = "https://files.pythonhosted.org/packages/77/81/6b30815698ede50f89013f25e46d66ed3a290b8a2d6b97f95bacbbe1eb5c/cftime-1.6.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ea0965a4c87739aebd84fe8eed966e5809d10065eeffd35c99c274b6f8da15", size = 1415218 }, + { url = "https://files.pythonhosted.org/packages/24/0d/73ab09a32da1478d3ef5f4ab6c59d42f2db2a2383b427c87e05ad81b71ad/cftime-1.6.4.post1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:800a18aea4e8cb2b206450397cb8a53b154798738af3cdd3c922ce1ca198b0e6", size = 1450704 }, + { url = "https://files.pythonhosted.org/packages/79/b1/6551603f8ea31de55913c84e4def3c36670563bdea6e195fcc4b6225ddf7/cftime-1.6.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:5dcfc872f455db1f12eabe3c3ba98e93757cd60ed3526a53246e966ccde46c8a", size = 190200 }, + { url = "https://files.pythonhosted.org/packages/50/81/0bb28d54088a61592f61a11e7fcabcea6d261c47af79e18d0f9cbcd940ae/cftime-1.6.4.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a590f73506f4704ba5e154ef55bfbaed5e1b4ac170f3caeb8c58e4f2c619ee4e", size = 226615 }, + { url = "https://files.pythonhosted.org/packages/f3/1e/38dbbf8a828dfb5e0e6e5c912818b77aacf2e7bcb97b262ac6126beeb29f/cftime-1.6.4.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:933cb10e1af4e362e77f513e3eb92b34a688729ddbf938bbdfa5ac20a7f44ba0", size = 209193 }, + { url = "https://files.pythonhosted.org/packages/9b/60/0db884c76311ecaaf31f628aa9358beae5fcb0fbbdc2eb0b790a93aa258f/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf17a1b36f62e9e73c4c9363dd811e1bbf1170f5ac26d343fb26012ccf482908", size = 1320215 }, + { url = "https://files.pythonhosted.org/packages/8d/7d/2d5fc7af06da4f3bdea59a204f741bf7a30bc5019355991b2f083e557e4e/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e18021f421aa26527bad8688c1acf0c85fa72730beb6efce969c316743294f2", size = 1367426 }, + { url = "https://files.pythonhosted.org/packages/5d/ab/e8b26d05323fc5629356c82a7f64026248f121ea1361b49df441bbc8f2d7/cftime-1.6.4.post1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5835b9d622f9304d1c23a35603a0f068739f428d902860f25e6e7e5a1b7cd8ea", size = 1385593 }, + { url = "https://files.pythonhosted.org/packages/af/7b/ca72a075a3f660315b031d62d39a3e9cfef71f7929da2621d5120077a75f/cftime-1.6.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:7f50bf0d1b664924aaee636eb2933746b942417d1f8b82ab6c1f6e8ba0da6885", size = 178918 }, + { url = "https://files.pythonhosted.org/packages/da/d8/81f086dbdc6f5a4e0bb068263471f1d12861b72562fe8c18df38268e4e29/cftime-1.6.4.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c89766ebf088c097832ea618c24ed5075331f0b7bf8e9c2d4144aefbf2f1850", size = 223418 }, + { url = "https://files.pythonhosted.org/packages/4a/cc/60a825d92a4023655e330470758280a31e7b82665ef77d0e2a0fe71ea958/cftime-1.6.4.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f27113f7ccd1ca32881fdcb9a4bec806a5f54ae621fc1c374f1171f3ed98ef2", size = 207395 }, + { url = "https://files.pythonhosted.org/packages/ca/90/f5b26949899decce262fc76a1e64915b92050473114e0160cd6f7297f854/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da367b23eea7cf4df071c88e014a1600d6c5bbf22e3393a4af409903fa397e28", size = 1318113 }, + { url = "https://files.pythonhosted.org/packages/c3/f8/6f13d37abb7ade46e65a08acc31af776a96dde0eb569e05d4c4b01422ba6/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c5c83cdf09d73aa94c7bc34925edd93c5f2c7dd28e074f568f7e376271a0", size = 1366034 }, + { url = "https://files.pythonhosted.org/packages/fa/08/335cb17f3b708f9a24f96ca4abb00889c7aa20b0ae273313e7c11faf1f97/cftime-1.6.4.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6b731c7133d17b479ca0c3c46a7a04f96197f0a4d753f4c2284c3ff0447279b4", size = 1390156 }, + { url = "https://files.pythonhosted.org/packages/f3/2d/980323fb5ec1ef369604b61ba259a41d0336cc1a85b639ed7bd210bd1290/cftime-1.6.4.post1-cp313-cp313-win_amd64.whl", hash = "sha256:d2a8c223faea7f1248ab469cc0d7795dd46f2a423789038f439fee7190bae259", size = 178496 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "choreographer" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "logistro" }, + { name = "simplejson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/5f/2b8e4d08f4459180101bcc2e9d14eaf3060e933d1fcbe05598a667752c2f/choreographer-1.0.3.tar.gz", hash = "sha256:70c949767d7627009c0df62ae69ae5975537a31fad9ce69f8f8c754ac9958349", size = 35994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/df/99086b752c33d9bf58b5016069d805175311bead59e7b47f46efed178b09/choreographer-1.0.3-py3-none-any.whl", hash = "sha256:ff6fbaf634f5dbd7ace2ea5374034cf84342494b16e5062ef1b211815fc27d67", size = 41716 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "click-plugins" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497 }, +] + +[[package]] +name = "cligj" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/837dbd5d8430fd0f01ed72c4cfb2f548180f4c68c635df84ce87956cff32/cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", size = 9803 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069 }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "configargparse" +version = "1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489 }, +] + +[[package]] +name = "connection-pool" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/df/c9b4e25dce00f6349fd28aadba7b6c3f7431cc8bd4308a158fbe57b6a22e/connection_pool-0.0.3.tar.gz", hash = "sha256:bf429e7aef65921c69b4ed48f3d48d3eac1383b05d2df91884705842d974d0dc", size = 3795 } + +[[package]] +name = "contourpy" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/bb/11250d2906ee2e8b466b5f93e6b19d525f3e0254ac8b445b56e618527718/contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", size = 269555 }, + { url = "https://files.pythonhosted.org/packages/67/71/1e6e95aee21a500415f5d2dbf037bf4567529b6a4e986594d7026ec5ae90/contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", size = 254549 }, + { url = "https://files.pythonhosted.org/packages/31/2c/b88986e8d79ac45efe9d8801ae341525f38e087449b6c2f2e6050468a42c/contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", size = 313000 }, + { url = "https://files.pythonhosted.org/packages/c4/18/65280989b151fcf33a8352f992eff71e61b968bef7432fbfde3a364f0730/contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", size = 352925 }, + { url = "https://files.pythonhosted.org/packages/f5/c7/5fd0146c93220dbfe1a2e0f98969293b86ca9bc041d6c90c0e065f4619ad/contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", size = 323693 }, + { url = "https://files.pythonhosted.org/packages/85/fc/7fa5d17daf77306840a4e84668a48ddff09e6bc09ba4e37e85ffc8e4faa3/contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", size = 326184 }, + { url = "https://files.pythonhosted.org/packages/ef/e7/104065c8270c7397c9571620d3ab880558957216f2b5ebb7e040f85eeb22/contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", size = 1268031 }, + { url = "https://files.pythonhosted.org/packages/e2/4a/c788d0bdbf32c8113c2354493ed291f924d4793c4a2e85b69e737a21a658/contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", size = 1325995 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/a2f351a90d955f8b0564caf1ebe4b1451a3f01f83e5e3a414055a5b8bccb/contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375", size = 174396 }, + { url = "https://files.pythonhosted.org/packages/a8/7e/cd93cab453720a5d6cb75588cc17dcdc08fc3484b9de98b885924ff61900/contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", size = 219787 }, + { url = "https://files.pythonhosted.org/packages/37/6b/175f60227d3e7f5f1549fcb374592be311293132207e451c3d7c654c25fb/contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", size = 271494 }, + { url = "https://files.pythonhosted.org/packages/6b/6a/7833cfae2c1e63d1d8875a50fd23371394f540ce809d7383550681a1fa64/contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", size = 255444 }, + { url = "https://files.pythonhosted.org/packages/7f/b3/7859efce66eaca5c14ba7619791b084ed02d868d76b928ff56890d2d059d/contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", size = 307628 }, + { url = "https://files.pythonhosted.org/packages/48/b2/011415f5e3f0a50b1e285a0bf78eb5d92a4df000553570f0851b6e309076/contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", size = 347271 }, + { url = "https://files.pythonhosted.org/packages/84/7d/ef19b1db0f45b151ac78c65127235239a8cf21a59d1ce8507ce03e89a30b/contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", size = 318906 }, + { url = "https://files.pythonhosted.org/packages/ba/99/6794142b90b853a9155316c8f470d2e4821fe6f086b03e372aca848227dd/contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", size = 323622 }, + { url = "https://files.pythonhosted.org/packages/3c/0f/37d2c84a900cd8eb54e105f4fa9aebd275e14e266736778bb5dccbf3bbbb/contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", size = 1266699 }, + { url = "https://files.pythonhosted.org/packages/3a/8a/deb5e11dc7d9cc8f0f9c8b29d4f062203f3af230ba83c30a6b161a6effc9/contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", size = 1326395 }, + { url = "https://files.pythonhosted.org/packages/1a/35/7e267ae7c13aaf12322ccc493531f1e7f2eb8fba2927b9d7a05ff615df7a/contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", size = 175354 }, + { url = "https://files.pythonhosted.org/packages/a1/35/c2de8823211d07e8a79ab018ef03960716c5dff6f4d5bff5af87fd682992/contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", size = 220971 }, + { url = "https://files.pythonhosted.org/packages/9a/e7/de62050dce687c5e96f946a93546910bc67e483fe05324439e329ff36105/contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", size = 271548 }, + { url = "https://files.pythonhosted.org/packages/78/4d/c2a09ae014ae984c6bdd29c11e74d3121b25eaa117eca0bb76340efd7e1c/contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", size = 255576 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/915380ee96a5638bda80cd061ccb8e666bfdccea38d5741cb69e6dbd61fc/contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", size = 306635 }, + { url = "https://files.pythonhosted.org/packages/29/5c/c83ce09375428298acd4e6582aeb68b1e0d1447f877fa993d9bf6cd3b0a0/contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", size = 345925 }, + { url = "https://files.pythonhosted.org/packages/29/63/5b52f4a15e80c66c8078a641a3bfacd6e07106835682454647aca1afc852/contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", size = 318000 }, + { url = "https://files.pythonhosted.org/packages/9a/e2/30ca086c692691129849198659bf0556d72a757fe2769eb9620a27169296/contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", size = 322689 }, + { url = "https://files.pythonhosted.org/packages/6b/77/f37812ef700f1f185d348394debf33f22d531e714cf6a35d13d68a7003c7/contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", size = 1268413 }, + { url = "https://files.pythonhosted.org/packages/3f/6d/ce84e79cdd128542ebeb268f84abb4b093af78e7f8ec504676673d2675bc/contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", size = 1326530 }, + { url = "https://files.pythonhosted.org/packages/72/22/8282f4eae20c73c89bee7a82a19c4e27af9b57bb602ecaa00713d5bdb54d/contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", size = 175315 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/28bca491f65312b438fbf076589dcde7f6f966b196d900777f5811b9c4e2/contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", size = 220987 }, + { url = "https://files.pythonhosted.org/packages/2f/24/a4b285d6adaaf9746e4700932f579f1a7b6f9681109f694cfa233ae75c4e/contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", size = 285001 }, + { url = "https://files.pythonhosted.org/packages/48/1d/fb49a401b5ca4f06ccf467cd6c4f1fd65767e63c21322b29b04ec40b40b9/contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", size = 268553 }, + { url = "https://files.pythonhosted.org/packages/79/1e/4aef9470d13fd029087388fae750dccb49a50c012a6c8d1d634295caa644/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", size = 310386 }, + { url = "https://files.pythonhosted.org/packages/b0/34/910dc706ed70153b60392b5305c708c9810d425bde12499c9184a1100888/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", size = 349806 }, + { url = "https://files.pythonhosted.org/packages/31/3c/faee6a40d66d7f2a87f7102236bf4780c57990dd7f98e5ff29881b1b1344/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", size = 321108 }, + { url = "https://files.pythonhosted.org/packages/17/69/390dc9b20dd4bb20585651d7316cc3054b7d4a7b4f8b710b2b698e08968d/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", size = 327291 }, + { url = "https://files.pythonhosted.org/packages/ef/74/7030b67c4e941fe1e5424a3d988080e83568030ce0355f7c9fc556455b01/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", size = 1263752 }, + { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, + { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "dask" +version = "2025.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/41/43eb54e0f6d1ba971d5adcad8f0862b327af6a2041aa134acbcec630ad43/dask-2025.1.0.tar.gz", hash = "sha256:bb807586ff20f0f59f3d36fe34eb4a95f75a1aae2a775b521de6dd53727d2063", size = 10758681 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a0/016d956a3fec193e3a5b466ca912944669c18dccc736b64a9e28ccdcc5f7/dask-2025.1.0-py3-none-any.whl", hash = "sha256:db86220c8d19bdf464cbe11a87a2c8f5d537acf586bb02eed6d61a302af5c2fd", size = 1371235 }, +] + +[[package]] +name = "datapi" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "multiurl" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/af/d753f6872281ef933e97bc3e46874918cac335318ab687af05674fbb94a1/datapi-0.2.0.tar.gz", hash = "sha256:9346d1ce8bbada920da2d4fe0c932d8e7b36c8dd8bc7851862e87712cfa32fda", size = 43816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/9d/2149dbe5baf64726242e25a906559b243a3e9ab38103996d2982430a881d/datapi-0.2.0-py3-none-any.whl", hash = "sha256:867c9ac1ae91965c4223236a2927a819bc0fc1e987490e10468db577936e1f7c", size = 26900 }, +] + +[[package]] +name = "datrie" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d", size = 63278 } + +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178 }, +] + +[[package]] +name = "descartes" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/6f/81735a30432b74f41db6754dd13869021ccfed3088d1cf7a6cfc0af9ac49/descartes-1.1.0.tar.gz", hash = "sha256:135a502146af5ed6ff359975e2ebc5fa4b71b5432c355c2cafdc6dea1337035b", size = 3525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/b6/1ed2eb03989ae574584664985367ba70cd9cf8b32ee8cad0e8aaeac819f3/descartes-1.1.0-py3-none-any.whl", hash = "sha256:4c62dc41109689d03e4b35de0a2bcbdeeb81047badc607c4415d5c753bd683af", size = 5804 }, +] + +[[package]] +name = "dill" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, +] + +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "dpath" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ce/e1fd64d36e4a5717bd5e6b2ad188f5eaa2e902fde871ea73a79875793fc9/dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e", size = 28266 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/d1/8952806fbf9583004ab479d8f58a9496c3d35f6b6009ddd458bdd9978eaf/dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576", size = 17618 }, +] + +[[package]] +name = "duckdb" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/6c/95c45374b7b26a31d94ae4509a69b0692e1ea2022637042c6e0a570e156e/duckdb-0.10.0.tar.gz", hash = "sha256:c02bcc128002aa79e3c9d89b9de25e062d1096a8793bc0d7932317b7977f6845", size = 11119589 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/83/e02505094c99a69674b2ad2f8beac4946978aff5641314447b2e8fbf170a/duckdb-0.10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a05af63747f1d7021995f0811c333dee7316cec3b06c0d3e4741b9bdb678dd21", size = 30311342 }, + { url = "https://files.pythonhosted.org/packages/e7/f5/b1456642d028eb50333ced31249099b095286358c6234ae5de0d6a8ea411/duckdb-0.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:072d6eba5d8a59e0069a8b5b4252fed8a21f9fe3f85a9129d186a39b3d0aea03", size = 16313992 }, + { url = "https://files.pythonhosted.org/packages/80/3c/fa6fb03c1c7aaa5d8fac237d3b38031831ca0b8b86dae03c21908031ff26/duckdb-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a77b85668f59b919042832e4659538337f1c7f197123076c5311f1c9cf077df7", size = 14040261 }, + { url = "https://files.pythonhosted.org/packages/e1/ca/8ed6fe44357a4a8f1c25bfa39140cc50dcf1894dcbe4d13aef3a1d75f317/duckdb-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a666f1d2da65d03199a977aec246920920a5ea1da76b70ae02bd4fb1ffc48c", size = 16592778 }, + { url = "https://files.pythonhosted.org/packages/99/61/1ef0d3d13908b5ae6637103e0dbeafc58028f662caa366ecd871154dbc9f/duckdb-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ec76a4262b783628d26612d184834852d9c92fb203e91af789100c17e3d7173", size = 17835800 }, + { url = "https://files.pythonhosted.org/packages/83/dc/f542b404de04ed84c7ef91df96ff5d2252430f1ea09b9fb5178a1a58f718/duckdb-0.10.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:009dd9d2cdbd3b061a9efbdfc79f2d1a8377bcf49f1e5f430138621f8c083a6c", size = 16399505 }, + { url = "https://files.pythonhosted.org/packages/50/47/a0ad83025049fcdb2af6e0c66b754c6cacce7f1aaf457b61ea89354730b1/duckdb-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:878f06766088090dad4a2e5ee0081555242b2e8dcb29415ecc97e388cf0cf8d8", size = 18385038 }, + { url = "https://files.pythonhosted.org/packages/dc/f9/79d37cbe067bf2fc9fbe4a09d0e345c264a9f221fb6b4bcfc2ef80bb6a72/duckdb-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:713ff0a1fb63a6d60f454acf67f31656549fb5d63f21ac68314e4f522daa1a89", size = 9569568 }, + { url = "https://files.pythonhosted.org/packages/da/62/3572db3f4f854c5f668dc477a989241ff72c8d7aadb605de798be702a8f6/duckdb-0.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9c0ee450dfedfb52dd4957244e31820feef17228da31af6d052979450a80fd19", size = 30331347 }, + { url = "https://files.pythonhosted.org/packages/68/c8/aa52abf16b182c4cc1f67ac33a68828a60dcf23ff7399792c59d9e580f43/duckdb-0.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ff79b2ea9994398b545c0d10601cd73565fbd09f8951b3d8003c7c5c0cebc7cb", size = 16332722 }, + { url = "https://files.pythonhosted.org/packages/ef/ca/ef3e7efafaf12f93c319c45f5e865f2e1a9819acc647da590baba8d3c136/duckdb-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6bdf1aa71b924ef651062e6b8ff9981ad85bec89598294af8a072062c5717340", size = 14048257 }, + { url = "https://files.pythonhosted.org/packages/1f/92/5ac1433dbf54a759bceeb2658fd65ef8239e0ad433b965fcb85b5b25bd6c/duckdb-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0265bbc8216be3ced7b377ba8847128a3fc0ef99798a3c4557c1b88e3a01c23", size = 16584351 }, + { url = "https://files.pythonhosted.org/packages/8d/28/8279f26af136c8046e813992c9b7643051082285d4e15866abbd9ccfa6c0/duckdb-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d418a315a07707a693bd985274c0f8c4dd77015d9ef5d8d3da4cc1942fd82e0", size = 17830443 }, + { url = "https://files.pythonhosted.org/packages/7e/c8/6c184642bc2e28f857ae1fc248c920b51525fda93308cc548b525c6a944b/duckdb-0.10.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2828475a292e68c71855190b818aded6bce7328f79e38c04a0c75f8f1c0ceef0", size = 16403736 }, + { url = "https://files.pythonhosted.org/packages/a9/e5/6c04d248d44206678e1c85b11f3041f828ef34d815fd85995603607006b2/duckdb-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c3aaeaae2eba97035c65f31ffdb18202c951337bf2b3d53d77ce1da8ae2ecf51", size = 18389565 }, + { url = "https://files.pythonhosted.org/packages/0c/a7/49f0a479b2372a0a42bdd971a6a474e9ba4c7b83279850b3564536e26d73/duckdb-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c51790aaaea97d8e4a58a114c371ed8d2c4e1ca7cbf29e3bdab6d8ccfc5afc1e", size = 9569987 }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, +] + +[[package]] +name = "filelock" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, +] + +[[package]] +name = "fonttools" +version = "4.56.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/8c/9ffa2a555af0e5e5d0e2ed7fdd8c9bef474ed676995bb4c57c9cd0014248/fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4", size = 3462892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/56/a2f3e777d48fcae7ecd29de4d96352d84e5ea9871e5f3fc88241521572cf/fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df", size = 2753325 }, + { url = "https://files.pythonhosted.org/packages/71/85/d483e9c4e5ed586b183bf037a353e8d766366b54fd15519b30e6178a6a6e/fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c", size = 2281554 }, + { url = "https://files.pythonhosted.org/packages/09/67/060473b832b2fade03c127019794df6dc02d9bc66fa4210b8e0d8a99d1e5/fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c", size = 4869260 }, + { url = "https://files.pythonhosted.org/packages/28/e9/47c02d5a7027e8ed841ab6a10ca00c93dadd5f16742f1af1fa3f9978adf4/fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049", size = 4898508 }, + { url = "https://files.pythonhosted.org/packages/bf/8a/221d456d1afb8ca043cfd078f59f187ee5d0a580f4b49351b9ce95121f57/fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62", size = 4877700 }, + { url = "https://files.pythonhosted.org/packages/a4/8c/e503863adf7a6aeff7b960e2f66fa44dd0c29a7a8b79765b2821950d7b05/fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0", size = 5045817 }, + { url = "https://files.pythonhosted.org/packages/2b/50/79ba3b7e42f4eaa70b82b9e79155f0f6797858dc8a97862428b6852c6aee/fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b", size = 2154426 }, + { url = "https://files.pythonhosted.org/packages/3b/90/4926e653041c4116ecd43e50e3c79f5daae6dcafc58ceb64bc4f71dd4924/fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05", size = 2200937 }, + { url = "https://files.pythonhosted.org/packages/39/32/71cfd6877999576a11824a7fe7bc0bb57c5c72b1f4536fa56a3e39552643/fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9", size = 2747757 }, + { url = "https://files.pythonhosted.org/packages/15/52/d9f716b072c5061a0b915dd4c387f74bef44c68c069e2195c753905bd9b7/fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f", size = 2279007 }, + { url = "https://files.pythonhosted.org/packages/d1/97/f1b3a8afa9a0d814a092a25cd42f59ccb98a0bb7a295e6e02fc9ba744214/fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2", size = 4783991 }, + { url = "https://files.pythonhosted.org/packages/95/70/2a781bedc1c45a0c61d29c56425609b22ed7f971da5d7e5df2679488741b/fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563", size = 4855109 }, + { url = "https://files.pythonhosted.org/packages/0c/02/a2597858e61a5e3fb6a14d5f6be9e6eb4eaf090da56ad70cedcbdd201685/fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a", size = 4762496 }, + { url = "https://files.pythonhosted.org/packages/f2/00/aaf00100d6078fdc73f7352b44589804af9dc12b182a2540b16002152ba4/fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28", size = 4990094 }, + { url = "https://files.pythonhosted.org/packages/bf/dc/3ff1db522460db60cf3adaf1b64e0c72b43406717d139786d3fa1eb20709/fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c", size = 2142888 }, + { url = "https://files.pythonhosted.org/packages/6f/e3/5a181a85777f7809076e51f7422e0dc77eb04676c40ec8bf6a49d390d1ff/fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba", size = 2189734 }, + { url = "https://files.pythonhosted.org/packages/a5/55/f06b48d48e0b4ec3a3489efafe9bd4d81b6e0802ac51026e3ee4634e89ba/fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692", size = 2735127 }, + { url = "https://files.pythonhosted.org/packages/59/db/d2c7c9b6dd5cbd46f183e650a47403ffb88fca17484eb7c4b1cd88f9e513/fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0", size = 2272519 }, + { url = "https://files.pythonhosted.org/packages/4d/a2/da62d779c34a0e0c06415f02eab7fa3466de5d46df459c0275a255cefc65/fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1", size = 4762423 }, + { url = "https://files.pythonhosted.org/packages/be/6a/fd4018e0448c8a5e12138906411282c5eab51a598493f080a9f0960e658f/fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea", size = 4834442 }, + { url = "https://files.pythonhosted.org/packages/6d/63/fa1dec8efb35bc11ef9c39b2d74754b45d48a3ccb2cf78c0109c0af639e8/fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3", size = 4742800 }, + { url = "https://files.pythonhosted.org/packages/dd/f4/963247ae8c73ccc4cf2929e7162f595c81dbe17997d1d0ea77da24a217c9/fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278", size = 4963746 }, + { url = "https://files.pythonhosted.org/packages/ea/e0/46f9600c39c644b54e4420f941f75fa200d9288c9ae171e5d80918b8cbb9/fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f", size = 2140927 }, + { url = "https://files.pythonhosted.org/packages/27/6d/3edda54f98a550a0473f032d8050315fbc8f1b76a0d9f3879b72ebb2cdd6/fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6", size = 2186709 }, + { url = "https://files.pythonhosted.org/packages/bf/ff/44934a031ce5a39125415eb405b9efb76fe7f9586b75291d66ae5cbfc4e6/fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14", size = 1089800 }, +] + +[[package]] +name = "fsspec" +version = "2025.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/79/68612ed99700e6413de42895aa725463e821a6b3be75c87fcce1b4af4c70/fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd", size = 292283 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/94/758680531a00d06e471ef649e4ec2ed6bf185356a7f9fbfbb7368a40bd49/fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b", size = 184484 }, +] + +[[package]] +name = "geographiclib" +version = "2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/cd/90271fd195d79a9c2af0ca21632b297a6cc3e852e0413a2e4519e67be213/geographiclib-2.0.tar.gz", hash = "sha256:f7f41c85dc3e1c2d3d935ec86660dc3b2c848c83e17f9a9e51ba9d5146a15859", size = 36720 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/5a/a26132406f1f40cf51ea349a5f11b0a46cec02a2031ff82e391c2537247a/geographiclib-2.0-py3-none-any.whl", hash = "sha256:6b7225248e45ff7edcee32becc4e0a1504c606ac5ee163a5656d482e0cd38734", size = 40324 }, +] + +[[package]] +name = "geopandas" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyogrio" }, + { name = "pyproj" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/08/2cf5d85356e45b10b8d066cf4c3ba1e9e3185423c48104eed87e8afd0455/geopandas-1.0.1.tar.gz", hash = "sha256:b8bf70a5534588205b7a56646e2082fb1de9a03599651b3d80c99ea4c2ca08ab", size = 317736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/64/7d344cfcef5efddf9cf32f59af7f855828e9d74b5f862eddf5bfd9f25323/geopandas-1.0.1-py3-none-any.whl", hash = "sha256:01e147d9420cc374d26f51fc23716ac307f32b49406e4bd8462c07e82ed1d3d6", size = 323587 }, +] + +[[package]] +name = "geopy" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "geographiclib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/e5/4e358038626f9ed671cf4d94fdb767c39644ccf062fe6a1bc089873a9dd4/geopy-2.4.0.tar.gz", hash = "sha256:a59392bf17adb486b25dbdd71fbed27733bdf24a2dac588047a619de56695e36", size = 117582 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/58/9289c6a03116025cdb61461d99b2493daa4967a80b13755463d71a0affeb/geopy-2.4.0-py3-none-any.whl", hash = "sha256:d2639a46d0ce4c091e9688b750ba94348a14b898a1e55c68f4b4a07e7d1afa20", size = 125446 }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "graphviz" +version = "0.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/83/5a40d19b8347f017e417710907f824915fba411a9befd092e52746b63e9f/graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d", size = 256455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/be/d59db2d1d52697c6adc9eacaf50e8965b6345cc143f671e1ed068818d5cf/graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5", size = 47126 }, +] + +[[package]] +name = "gurobipy" +version = "11.0.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/ba/dd7d8f299a3a87ae5bd5553b82199851e6e5e7b6fcf8528570b05d343610/gurobipy-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:138b4be8fb42d494b8484bd4cfa99ea1e511e8860cdb1171349f4fce06d9aa01", size = 10784430 }, + { url = "https://files.pythonhosted.org/packages/b9/68/7ccd72bf75db54c70ec563f5f700119ea290c9a6ccac9f3191295e4739f5/gurobipy-11.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84c58e164c90fc1de8bc0aac8ad25a82b4a9d3079b5064735822cecf552b1b26", size = 27422010 }, + { url = "https://files.pythonhosted.org/packages/af/53/8b5194b41760718a8cfb01010dcab5a8f464468fffacceb256df8aefa906/gurobipy-11.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d8eed5ca4831d1f10b08cd701f75483b27810734750d414a5b3cb6e4ba4c17f3", size = 13446177 }, + { url = "https://files.pythonhosted.org/packages/28/41/c61b4b096a093479eb45292ec7a5d127f088a92d4748faec76e4fcd2443e/gurobipy-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9e57508f7d7a8ef154767894c693273d94bed771fa300677ae74892fde7dfc57", size = 10324769 }, + { url = "https://files.pythonhosted.org/packages/19/71/d21d15318a6f400f331e940924910aa5140da55dd84813bd9f8bdecaf707/gurobipy-11.0.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:05700979469511aedfcb4d0136c161077d085393b6a449c98e9ff7818747279c", size = 10709232 }, + { url = "https://files.pythonhosted.org/packages/ec/74/d2523173f9eb8ec66a7d892a49ca6899d75acddac418f8c81cd50d4b420a/gurobipy-11.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b752a8a4d898a3cc59b0670aa449dee8e2159d4f420f30033baf96f14a36516d", size = 27267858 }, + { url = "https://files.pythonhosted.org/packages/73/5a/4c764ccd1e9bcef3d01729af0811915f3fb36e72b544e5caf477b1dc545f/gurobipy-11.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d08a69a9884b2c7ab843c9967b44770c17c73ac5726db7a8794ae85d7c1fc446", size = 13257275 }, + { url = "https://files.pythonhosted.org/packages/f2/54/b4eea516b5e318c3522456f8a26a2fc6e8c9c657e0ed06ce5ec4f7faca4d/gurobipy-11.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:74fa0d9bace48bc0f25426b5cd70addb02a13b5ea1645494cad073a32c9ec2c4", size = 10296203 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "highspy" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/77/3b103db661372703843167d323c548ab479f812d5c633549347f748d5aee/highspy-1.9.0.tar.gz", hash = "sha256:d9765753991c7a4d9bc8815a75fc4df6f5c58b50117e391e2c494cdf0eb1ed13", size = 1420463 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/a3/7a5832c08a567bb3e2b24307a87fc3279fbe3888532d1cc8766f49693d36/highspy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:244b72ff31bd0cfb736893aecf1b324f9155bab020414ad89710319fb3cda17a", size = 2151196 }, + { url = "https://files.pythonhosted.org/packages/aa/b5/17c654e5d9e0d69649af01f6cc5ba133a622b942c0327df3d056288db013/highspy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:830da7691d39b0ef3266bdcdc805f15a215f1b91fe01cb2bb39ca9fb25e2fcfc", size = 1841765 }, + { url = "https://files.pythonhosted.org/packages/eb/33/b878f6faa73888286efa72717a6d9ad49b2aab4f321a7081a455495564da/highspy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a15b420cfe45b53e0a7b8e2096bfb407ae763f338cd2a43ba6acd987266697", size = 2065797 }, + { url = "https://files.pythonhosted.org/packages/2f/d2/1aa39bb9d381ecb0122a90e6e48480358e284a2891ff51cdf90b4a885857/highspy-1.9.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86977e537c2152e998c7e5d62a0a333507a314dae374daebad1dc2cdc5616d5e", size = 2384941 }, + { url = "https://files.pythonhosted.org/packages/ee/a2/e825e8bed155f9df748ea9319a83c2c89ab860ddf89656986b3964236d34/highspy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:664048c40ad4ee6b8b7aa279a46803a448dff6079940b85cc5fbcc2d9fde1cce", size = 2248144 }, + { url = "https://files.pythonhosted.org/packages/5c/32/a4b7d1498defcb5651f651d7f234a744c9de4658b9ddaf0155e1e211272c/highspy-1.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2fbec2a5c7ff42ed1fb9f54c4c5141fcaa070ed5884b7f966fde45921fb16c3", size = 3046337 }, + { url = "https://files.pythonhosted.org/packages/0a/4e/4548176b287567917c7e60868eb819cd70e619663c749384feab468c3e62/highspy-1.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2b97e07c5921a14a40cca32fcc8477853cc9a6e909b2f0ef44fb94d2be6d4e8e", size = 3573045 }, + { url = "https://files.pythonhosted.org/packages/5c/3b/78c8eaa0f2a6a20ddb1f8c488ba6dd787baf31fd79902301b58f5f75f72c/highspy-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43d1612e9091a2fe07bad2e24dea12f0e67bb77014c59281407cd72fd704de5b", size = 3250008 }, + { url = "https://files.pythonhosted.org/packages/42/b2/c1e07d7c55e2bb6f7a5c2ee0d57e0fd6d848261a263e6e6cf419e38cfe40/highspy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:95662940d87c9f3894e8e78a405f8701af7326e28db38b524ea19119821c3b8c", size = 1563554 }, + { url = "https://files.pythonhosted.org/packages/35/e9/aa8fce2ac95124bc880761856207e7b02263730a4eb1a9cfb97116a26b44/highspy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:18827d2e7e2d1b013b2c5eac0923646942700ff5683a93c23c383370861d424d", size = 1871049 }, + { url = "https://files.pythonhosted.org/packages/ae/0d/088bd34992071335839b2f62bc1fe7831e3bc57a90406b48295aa843ab37/highspy-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9509c4cdb70171a21c98fadbcd85e4f53d54b3106258a009486816153eb29847", size = 2153200 }, + { url = "https://files.pythonhosted.org/packages/16/24/507a4b7a45f5554b8593ccceaf377270fe9a0c8ffd8c057cf26986ee3658/highspy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73e430ebe85f4b6fbbebd98ae53d47b12cebfd8cbee6fffd17760efc8780ce36", size = 1843422 }, + { url = "https://files.pythonhosted.org/packages/10/f5/aea6b779925bfd449a8857c3ec527333b4a5914b6c7ded445da8cad4fd70/highspy-1.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04ed0b41e9e2e1ad4c2ce7580562cd6b45cf872d63f3c5e21e36e3d399eb9b2b", size = 2057475 }, + { url = "https://files.pythonhosted.org/packages/f9/45/1acd79703d1bdb745b0e6f596a77b7db209291ddfdba4982f61f023e5a37/highspy-1.9.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37dc773702bcbf155e70b822b652fc9b96b2e190c70d1402de8711a177e75690", size = 2372525 }, + { url = "https://files.pythonhosted.org/packages/09/75/3cb42a6d561f288f178b38e0f822a4c4f79c7bc4cace11ce895121941b6c/highspy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d2748e1c08d6d293402e2fb4cb6d44bfd4114dce438ae536a8cf8ddabe0e90", size = 2234977 }, + { url = "https://files.pythonhosted.org/packages/44/bd/ec6861d22e5a6891b84c37e711d7801eca5b70234dbac58a67415c19ba8c/highspy-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e0809e15532688f0d16c4e27dcf4bb0a67b3d7a548373710176ff1c375b55ca5", size = 3045209 }, + { url = "https://files.pythonhosted.org/packages/c2/a4/bcb297d83a658dbf4faecb6a8bdf5999d03e88ac9950ba7f36e6acbb7ebe/highspy-1.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a3bcc624804c661edeed982dccc667abc7dabd3298633e6e3b2e1dab733953f", size = 3573139 }, + { url = "https://files.pythonhosted.org/packages/ef/c0/11c381826af55977fcefc467399e0fbe777b0ab0fab6dbb1458094acc1ba/highspy-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be067a7a775811ddd03929ce8ba71d3d8dbce079e74afda0eb8155ddf85c74f2", size = 3245431 }, + { url = "https://files.pythonhosted.org/packages/51/0a/90a8161e13281af61e12e38aeedf561a21ea921b2d96e7a2d5301f6c506e/highspy-1.9.0-cp312-cp312-win32.whl", hash = "sha256:840ad28b3a937c3129e6efe40d3ed86391a6005d5878f832eadde222cd4c1895", size = 1561810 }, + { url = "https://files.pythonhosted.org/packages/1d/c8/ec26b570a9f953bc7f5bdaaa471c1da334eb6d00d48ad75f5781dbab4c60/highspy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fcf93ef8e8440b8d900c21c33e5fc8e2041e7d54bb680bc617c5a8d71c084454", size = 1871889 }, + { url = "https://files.pythonhosted.org/packages/85/e4/9411cb5918f88894bf899f9737a67ac8e197d33c9b2119bbe4af60417cfe/highspy-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9eea8125342c2049e6e81c808e1995e78aa0ba09e505d7d0d78b5997f8c9240d", size = 2153271 }, + { url = "https://files.pythonhosted.org/packages/4d/a7/442d7720a9a01f189f36c626473c150a9fdd11cab9eb99c62cb178989ba0/highspy-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22580ac1fbce24bae9fbb906d1967d6c6798401a1f134dfc46a5f87c58791e47", size = 1843436 }, + { url = "https://files.pythonhosted.org/packages/c5/5c/6eb6ccfb16b591bbbdd22bc6d45c03e68140d5cf34cc2fcdda1ba784d489/highspy-1.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22c8217c05f3df3bf9326aba777b2808d4da5757f156e4a20058d360e6a002a6", size = 2057482 }, + { url = "https://files.pythonhosted.org/packages/b2/ed/7160f0900d5eba0c6e36398f6b8375142323ecabdfb2041e3901973d080c/highspy-1.9.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:adbf983e661b651d1c27c9a9e7d63e5c8a311986b05e3944777752060ea41b1f", size = 2372449 }, + { url = "https://files.pythonhosted.org/packages/d9/03/83ccebbbd2233e478684884c2f4ef9d680939c2336f3292d40c29d4437b1/highspy-1.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1804f7d5fbf404ebab22033728582b745a793b3c9015285eb2ab8bac97438e61", size = 2234790 }, + { url = "https://files.pythonhosted.org/packages/93/1c/6fd80e197332cac6b72bbb0e4e75b2630c3a8b62438ec11558d88223289d/highspy-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0cbf60020769bc0b1af5da8efa65f9f50397d9d441079133f76ad962524ed20f", size = 3045276 }, + { url = "https://files.pythonhosted.org/packages/91/ab/6bd4743749b7f569d67b8b3ac896c0cc87658bcfb55b816f27cce0d56161/highspy-1.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:45d47d7c5421db8075fbef930f3a3a7e4e86f14f21ed74b6e50facdb65c3ea87", size = 3573156 }, + { url = "https://files.pythonhosted.org/packages/a0/de/0de13d9b478c352ea051b10255effa8acea1615cd395a364c77dd9032030/highspy-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c0e8004bef64df1537286e5dac5fb2f3ddc152ef205d3e2e8786bcaced08e92", size = 3245380 }, + { url = "https://files.pythonhosted.org/packages/38/de/14d2f8e73fc79ab82a6f9fee81e90783d3aa15c3e21762d695090b2c98d8/highspy-1.9.0-cp313-cp313-win32.whl", hash = "sha256:a77eb1e06278ef23ed5778a3c4e9348c57d917d9197c7d2cf30acea151089b59", size = 1561805 }, + { url = "https://files.pythonhosted.org/packages/b0/e1/9cd0c9860bc562ca872a029441ac42e586c0e5ed15c0d4c470b17dd3f0b0/highspy-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:9151e47745a3605617b200f173af46bd1aee5ef06083a17e9781d806ee9ed1fd", size = 1871841 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, +] + +[[package]] +name = "identify" +version = "2.6.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d1/524aa3350f78bcd714d148ade6133d67d6b7de2cdbae7d99039c024c9a25/identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684", size = 99260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/00/1fd4a117c6c93f2dcc5b7edaeaf53ea45332ef966429be566ca16c2beb94/identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0", size = 99097 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "ipython" +version = "8.32.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/80/4d2a072e0db7d250f134bc11676517299264ebe16d62a8619d49a78ced73/ipython-8.32.0.tar.gz", hash = "sha256:be2c91895b0b9ea7ba49d33b23e2040c352b33eb6a519cca7ce6e0c743444251", size = 5507441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e1/f4474a7ecdb7745a820f6f6039dc43c66add40f1bcc66485607d93571af6/ipython-8.32.0-py3-none-any.whl", hash = "sha256:cae85b0c61eff1fc48b0a8002de5958b6528fa9c8defb1894da63f42613708aa", size = 825524 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, +] + +[[package]] +name = "kaleido" +version = "0.4.0rc5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout" }, + { name = "choreographer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/1f/53a30b6ca64ae822fb77dc0d157b8761e525015edc49b402e52e16f702ed/kaleido-0.4.0rc5.tar.gz", hash = "sha256:cab65055ca382a51f2cf1ae726d8373190f8bf97997337e316d35c23f6da27e2", size = 3312827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8e/93f2e978c85800c040406e42d0f469bc2f4d5a02609be12e8ad2ef78e183/kaleido-0.4.0rc5-py3-none-any.whl", hash = "sha256:6001a0c32db8b9b78502dcedc41dff2bdff7ea12f20a2c40fa3c6a3a2d9616f8", size = 3819614 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635 }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717 }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413 }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994 }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804 }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690 }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839 }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109 }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269 }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468 }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394 }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901 }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306 }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966 }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311 }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156 }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071 }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053 }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278 }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139 }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952 }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132 }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997 }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060 }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471 }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793 }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855 }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430 }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294 }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736 }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194 }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942 }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341 }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455 }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138 }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857 }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129 }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538 }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, +] + +[[package]] +name = "linopy" +version = "0.3.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bottleneck" }, + { name = "dask" }, + { name = "deprecation" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "polars" }, + { name = "scipy" }, + { name = "toolz" }, + { name = "tqdm" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/9d/ff0eccefa8b768dac94d156fb96d287c87e30e7d1a8507f07b108f4f02eb/linopy-0.3.14.tar.gz", hash = "sha256:4c4d50fec3bde13f756c6f1978562bdbc7e9a6cd98f133437e5b32825d29ab7f", size = 1185647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/ab/e8c566e002e15d2ad320c74117b85c9750e5c053d15ceda7fe8563a384c2/linopy-0.3.14-py3-none-any.whl", hash = "sha256:70af5cd5834e9c97c2498d06feea890769b44e661493621e5cf08399b524cf5b", size = 85459 }, +] + +[[package]] +name = "locket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, +] + +[[package]] +name = "logistro" +version = "1.0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/dc/e4c4b1fd2e6eca7f4e288434ae7f8057c0c06bad797366e0cc027fef23ef/logistro-1.0.11.tar.gz", hash = "sha256:9386b65cde9cc71d8c356142a219868612357b3c54ed4e067a78f420c4e51846", size = 7550 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/fc/bf7cdfc51b13d6f4e2ebdacdd18ddb2aa7a70c00f8b7cd81c0906e0e7490/logistro-1.0.11-py3-none-any.whl", hash = "sha256:5f68bf18f2cb842de207947e6ee53d4980fba602935455d8fa2e9156b2e13323", size = 7006 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e1/77016194621fb1356aafeb2186f07b5dede62ea2043bf03f82325c4fccc5/matplotlib-3.8.0.tar.gz", hash = "sha256:df8505e1c19d5c2c26aff3497a7cbd3ccfc2e97043d1e4db3e76afa399164b69", size = 35864435 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/ee/e87d7a79a32f5ac833655ef29ebd37c7b389e8957bc71aacb10a95f822b9/matplotlib-3.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5dc945a9cb2deb7d197ba23eb4c210e591d52d77bf0ba27c35fc82dec9fa78d4", size = 7590852 }, + { url = "https://files.pythonhosted.org/packages/af/f3/fb27b3b902fc759bbca3f9d0336c48069c3022e57552c4b0095d997c7ea8/matplotlib-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8b5a1bf27d078453aa7b5b27f52580e16360d02df6d3dc9504f3d2ce11f6309", size = 7484528 }, + { url = "https://files.pythonhosted.org/packages/39/fc/fca496a890274b6628e310816710718d8184ede99956160c05a017789acc/matplotlib-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f25ffb6ad972cdffa7df8e5be4b1e3cadd2f8d43fc72085feb1518006178394", size = 11385752 }, + { url = "https://files.pythonhosted.org/packages/65/5b/3b8fd7d66043f0638a35fa650570cbe69efd42fe169e5024f9307598b47e/matplotlib-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee482731c8c17d86d9ddb5194d38621f9b0f0d53c99006275a12523ab021732", size = 11615276 }, + { url = "https://files.pythonhosted.org/packages/af/02/320e32ec24b062c29b4b580db3257190c66d5a8aa4d604a9c0204061ead9/matplotlib-3.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36eafe2128772195b373e1242df28d1b7ec6c04c15b090b8d9e335d55a323900", size = 9545819 }, + { url = "https://files.pythonhosted.org/packages/40/d9/c1784db9db0d484c8e5deeafbaac0d6ed66e165c6eb4a74fb43a5fa947d9/matplotlib-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:061ee58facb3580cd2d046a6d227fb77e9295599c5ec6ad069f06b5821ad1cfc", size = 7644668 }, + { url = "https://files.pythonhosted.org/packages/2e/34/121c49cd4e3e3ae4ad58c13cb7b000f2140f7bc48c7d3d950a19be634990/matplotlib-3.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3cc3776836d0f4f22654a7f2d2ec2004618d5cf86b7185318381f73b80fd8a2d", size = 7588410 }, + { url = "https://files.pythonhosted.org/packages/dd/73/8d69b0337a77f73d316232ea67708b4bcfe5a9c54dbc2327b1d1b730a0b6/matplotlib-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c49a2bd6981264bddcb8c317b6bd25febcece9e2ebfcbc34e7f4c0c867c09dc", size = 7480932 }, + { url = "https://files.pythonhosted.org/packages/84/be/ea8f33a4b9644cb6c09928cbaeddb025165a4a5f366c46370ed7abb6e6f3/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ed11654fc83cd6cfdf6170b453e437674a050a452133a064d47f2f1371f8d3", size = 11378619 }, + { url = "https://files.pythonhosted.org/packages/77/cd/1464efc9fe354026b8d2fb4ebb2f1746559b0e38308104d3ee60a5a05c71/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae97fdd6996b3a25da8ee43e3fc734fff502f396801063c6b76c20b56683196", size = 11602343 }, + { url = "https://files.pythonhosted.org/packages/83/68/aef60cdfc9818862ff2c6ea98e0bec7e230b1fc947339971d5efe4c2d02f/matplotlib-3.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87df75f528020a6299f76a1d986c0ed4406e3b2bd44bc5e306e46bca7d45e53e", size = 9539802 }, + { url = "https://files.pythonhosted.org/packages/59/c7/f8da659997fe3210fdda689cf2d7720b3a079578fb8aecc3623c4e091a77/matplotlib-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:90d74a95fe055f73a6cd737beecc1b81c26f2893b7a3751d52b53ff06ca53f36", size = 7644322 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "msgpack" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, + { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, + { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, + { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 }, + { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 }, + { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 }, + { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 }, + { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, + { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, + { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, + { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, + { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, + { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, + { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, + { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, + { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, + { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, + { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, + { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, + { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, + { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, +] + +[[package]] +name = "multiurl" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/db/aad981174d3bdaecc1d7e1f2d176641f022300ddf3a17b13c2775d041b7a/multiurl-0.3.3.tar.gz", hash = "sha256:f4d0b69dcf4a0ed740daa313dbcd4d5665420d305c50ca879285e96dc828093f", size = 18382 } + +[[package]] +name = "mypy" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/aa/cc56fb53ebe14c64f1fe91d32d838d6f4db948b9494e200d2f61b820b85d/mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385", size = 10859630 }, + { url = "https://files.pythonhosted.org/packages/04/c8/b19a760fab491c22c51975cf74e3d253b8c8ce2be7afaa2490fbf95a8c59/mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca", size = 10037973 }, + { url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 }, + { url = "https://files.pythonhosted.org/packages/fc/a6/37f7544666b63a27e46c48f49caeee388bf3ce95f9c570eb5cfba5234405/mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4", size = 12897010 }, + { url = "https://files.pythonhosted.org/packages/84/8b/459a513badc4d34acb31c736a0101c22d2bd0697b969796ad93294165cfb/mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6", size = 9562873 }, + { url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 }, + { url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 }, + { url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 }, + { url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 }, + { url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 }, + { url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "ndindex" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/f9/f8d491c18f16ffcb1a8abf78345e54879fd1fe6b61dcc8a9b471285cd27e/ndindex-1.9.2.tar.gz", hash = "sha256:b8658a06e52d6c47445c2ec11d292e1d52c3af259214c8b52e3a1aab733daa72", size = 243001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/6d/f272dbbe0a23575e57773272f02a237b51f4e4f33317bf28b2b8be47ac3d/ndindex-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1fa2e521a872870d55fa6fa85399f16c1c20bbe4e3e315bbfc80e3ea92561334", size = 163453 }, + { url = "https://files.pythonhosted.org/packages/77/f6/8ff749841f6f8f4096184e8e158e48d572358d4ad0ba1055728e4f7e0f44/ndindex-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b0ef52d15fa8755d00a6868c799ff4227f1f453901a6f4de395586f9a435b7a", size = 161947 }, + { url = "https://files.pythonhosted.org/packages/51/2d/bfc284ecc9b24acb916f5d04a69357ae56e0b6073286eaa71cf54bf0b136/ndindex-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f647eda61cae68a260017118ad8daac4d580ad221ff922bbaa1526e30e350feb", size = 506119 }, + { url = "https://files.pythonhosted.org/packages/5d/6b/83e328c3dc41ff244d7e79cd24ac62fff96405a3ea948810b2fa883c14f5/ndindex-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:384520b4d9f52cb2fd1d324e6f278ec422b2cc2885e95a00587394bf6f56a798", size = 485492 }, + { url = "https://files.pythonhosted.org/packages/87/f4/8a3a3c0d541d96a6cd39026a1211647d0fcbf047f8bac4332e4b95f54e8b/ndindex-1.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e2dd75a6e25269b66607f1722acd72e8f086837b2c58275d31b3bdfdf8a095bf", size = 489557 }, + { url = "https://files.pythonhosted.org/packages/0f/5c/5e96422400fad72762e85e3cc3a4bd52b11476b990c4e7df25836e8e9c0c/ndindex-1.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8634be005b18034846163067bce78a0209fab65e4bc77e0bc333aa160ef12b7a", size = 506268 }, + { url = "https://files.pythonhosted.org/packages/f8/8b/5948067de44c5484aa8a4db640b8b5dc5cc9b394e9f547a23fd694edf399/ndindex-1.9.2-cp311-cp311-win32.whl", hash = "sha256:89172e90e56a409197cbbe12a49aa16c83879274ca4f61fd8a03b30c6c90e3ca", size = 151566 }, + { url = "https://files.pythonhosted.org/packages/b0/f6/b2fde7ec7880d51f7280bb5e974e400bb716e3054048c409ba35ba509823/ndindex-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:d23f07831d28bb3c04c234936b6038078cd7c0c4966d2e2e37738edad6435f9f", size = 159516 }, + { url = "https://files.pythonhosted.org/packages/a3/a5/c3775c1a7279517027b86dc0c1a6a74f9a1fc7e0c298c960ed170fcf585e/ndindex-1.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:466d2e30a3c2afac6dac64e5ada19db30d23164befa7f69d29f209fb512b3d2f", size = 164104 }, + { url = "https://files.pythonhosted.org/packages/de/81/edb7ba51dae8d5a2879d39eb56651eeea4927f8292fc6286fae8b1cda0f1/ndindex-1.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e87eefa75af0f974cf2c5af14a488ee97dfdc7fb6da67f19f9dc600da5ae041", size = 161991 }, + { url = "https://files.pythonhosted.org/packages/f2/9e/79342047dd441fdcf25c776370c2b09ef8fad30bf06d7920b09278d93260/ndindex-1.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d98a41ff276fc623f3e068d40381ee06289644b530c3535bc00a8cbc5526c6", size = 521201 }, + { url = "https://files.pythonhosted.org/packages/fc/bd/834e4bb7054accc8bbf63c73f7c9f0bcbdc326fec0f560f375dd6637c63a/ndindex-1.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d42c5cd86d923f1606c3a9defbaeb3ece8f7b444f95a46ec6f1fb511e971f7", size = 498251 }, + { url = "https://files.pythonhosted.org/packages/35/1b/fe4d51e07f18596abd53b3b63dd1d4a8617af3896193418a86b7a7a95fa7/ndindex-1.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:959f8babfc3055933079296a33449e02c24074027c39ce974cf73757c7d5ea21", size = 501804 }, + { url = "https://files.pythonhosted.org/packages/ae/e5/95d5dd5a628c41db959e07ddc7212ed45844865d10375efe4fc0aa5c905b/ndindex-1.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d68d8285f3ab8a78b0db990fb25eddc637df4b00467fbf36a4656c7ee46ddc5d", size = 518095 }, + { url = "https://files.pythonhosted.org/packages/bc/49/ca6155435bb408173c3d07f07aac6e6c4a30cefec31c4dd2af75c44774d7/ndindex-1.9.2-cp312-cp312-win32.whl", hash = "sha256:c87aa8218b6eaaa9eacb2f68f1ce71be0e368280ef926d0ed9ad71d2fbe24fe6", size = 151487 }, + { url = "https://files.pythonhosted.org/packages/01/e3/c87442ba34a76e3d778135e967b625e5bb2217773a8c0be751e1537231b7/ndindex-1.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:d15f3a8566910ec25898e3d77b3b7c258b37f84a235d49cb17dfddc9036127b4", size = 159655 }, + { url = "https://files.pythonhosted.org/packages/88/41/250efa5a033b66043d18eca87d044f733ca017bd12767ddf0b9468120bb1/ndindex-1.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a33452b4a7b8510f809f8b59dbac5b1d7b3d8a68c9e42dee2c28c5131bbbfbc6", size = 162133 }, + { url = "https://files.pythonhosted.org/packages/b0/df/87f329590e807460cbd4cea47aaaadea9a5cf5e70854712eb89489d03c89/ndindex-1.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19d70a304f942c0aee89418d9c487de9c2dcfcda9073976a590c7fed587bd674", size = 160045 }, + { url = "https://files.pythonhosted.org/packages/4e/e3/407f31902bfdd6d991e8ce62307877afc23e39b8a7c93fd17ab8316a5415/ndindex-1.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0bba0f387d1d8204378e82b886c19f46ae39347ee7113c9028317270c0060be", size = 505012 }, + { url = "https://files.pythonhosted.org/packages/91/dc/4e335d8631939f267be7b16308246671c02123e28f693f544076dda8615b/ndindex-1.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876dcd82d6d1cbc9c2e6819bc7a5ca3686a5430f0a07520b94f78ff78097d2d", size = 484122 }, + { url = "https://files.pythonhosted.org/packages/c0/ed/007faa12149a21893ec64f0dcab36c70a4cd43825dcd11bd7090ef8d1c29/ndindex-1.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3c564768ffa7228d1768eb6df1a77d03d39efc9c98746d8c1b511ffcc23d5f9f", size = 486035 }, + { url = "https://files.pythonhosted.org/packages/2c/81/67d8a37aca8997d8e93554f3c39bca200a16685e84e03e2cc84cf0c93276/ndindex-1.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ddbdfee4560c3f7823de88257680c8fd6d0220a7b23bfd27b0f3fc7afa27bee1", size = 500916 }, + { url = "https://files.pythonhosted.org/packages/6e/ff/277768997fa82a865ff564a6bd91bb42f574662945e58b26c0eb3d690aba/ndindex-1.9.2-cp313-cp313-win32.whl", hash = "sha256:6274886f1348128fc4e10aef925272f904ac467175af52338d56f1cb763caf1a", size = 150906 }, + { url = "https://files.pythonhosted.org/packages/3c/4d/e121d109bf6f71bcb00c6198549152fa16358a031796c6a7aa9662191529/ndindex-1.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:98be658c00ec0827e398b0fe5c13d207ff70b027187d728cb22155cce3bf6fbe", size = 158778 }, +] + +[[package]] +name = "netcdf4" +version = "1.6.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "cftime" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/39/f8c4b2f3a4d78164e9850bb0924e1fd490e0bf8a8366b9b42cd295d7bbee/netCDF4-1.6.4.tar.gz", hash = "sha256:66da6542cbc7a6045cd1d979397dfd5a3f6c880c76d52b8f98bb108c82ee8c6e", size = 778542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/f6/b343f027582aa4db92d35f4ec89fcaa18562d0d6bf63ee15bfd494b49ba2/netCDF4-1.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08a7efab50d5a4da6227244e544a74f676b732ccdff2b8a4678174c15a3f8dff", size = 6709409 }, + { url = "https://files.pythonhosted.org/packages/98/b3/42bcbabf812e5c9e4568917a3e08987adabd2b01b0806f7c9c415e9657f8/netCDF4-1.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b7ad3f66d50970fa1bd5509c52bb8a99740c8a79fafa83bfd0bc7348b6ab5a", size = 3215491 }, + { url = "https://files.pythonhosted.org/packages/45/0d/a9708db20dd4d8ba3019722455f658ed544ae9d1e4c4edf75faa3d3314db/netCDF4-1.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5cad50d4e6e5a3b35c76862c58393ddf93baa44de98b6e040ac21896145881", size = 5041737 }, + { url = "https://files.pythonhosted.org/packages/56/9d/f9e7608e4fa3cafeb215217733ef08d770bcc0b0f70b4f3ad574113df4cf/netCDF4-1.6.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c2395c53a37717b1047650e1c9de68a3d69542bb25df5c594e1e14e9480bb18", size = 5397581 }, + { url = "https://files.pythonhosted.org/packages/f0/ef/39f1e32c623abbcff8fef81e484abafc74779388144540b804d96f0657cc/netCDF4-1.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:c85f77599c61a88d512d6536e181ff1c01fd16f4740367e4f8e5cacb36500293", size = 6555644 }, +] + +[[package]] +name = "networkx" +version = "3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/a1/47b974da1a73f063c158a1f4cc33ed0abf7c04f98a19050e80c533c31f0c/networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61", size = 2021691 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/05/9d4f9b78ead6b2661d6e8ea772e111fc4a9fbd866ad0c81906c11206b55e/networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36", size = 2072251 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numexpr" +version = "2.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/67/c7415cf04ebe418193cfd6595ae03e3a64d76dac7b9c010098b39cc7992e/numexpr-2.10.2.tar.gz", hash = "sha256:b0aff6b48ebc99d2f54f27b5f73a58cb92fde650aeff1b397c71c8788b4fff1a", size = 106787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/b7/f25d6166f92ef23737c1c90416144492a664f0a56510d90f7c6577c2cd14/numexpr-2.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b360eb8d392483410fe6a3d5a7144afa298c9a0aa3e9fe193e89590b47dd477", size = 145055 }, + { url = "https://files.pythonhosted.org/packages/66/64/428361ea6415826332f38ef2dd5c3abf4e7e601f033bfc9be68b680cb765/numexpr-2.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9a42f5c24880350d88933c4efee91b857c378aaea7e8b86221fff569069841e", size = 134743 }, + { url = "https://files.pythonhosted.org/packages/3f/fb/639ec91d2ea7b4a5d66e26e8ef8e06b020c8e9b9ebaf3bab7b0a9bee472e/numexpr-2.10.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fcb11988b57cc25b028a36d285287d706d1f536ebf2662ea30bd990e0de8b9", size = 410397 }, + { url = "https://files.pythonhosted.org/packages/89/5a/0f5c5b8a3a6d34eeecb30d0e2f722d50b9b38c0e175937e7c6268ffab997/numexpr-2.10.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4213a92efa9770bc28e3792134e27c7e5c7e97068bdfb8ba395baebbd12f991b", size = 398902 }, + { url = "https://files.pythonhosted.org/packages/a2/d5/ec734e735eba5a753efed5be3707ee7447ebd371772f8081b65a4153fb97/numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e", size = 1380354 }, + { url = "https://files.pythonhosted.org/packages/30/51/406e572531d817480bd612ee08239a36ee82865fea02fce569f15631f4ee/numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5", size = 151938 }, + { url = "https://files.pythonhosted.org/packages/04/32/5882ed1dbd96234f327a73316a481add151ff827cfaf2ea24fb4d5ad04db/numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0", size = 144961 }, + { url = "https://files.pythonhosted.org/packages/2b/96/d5053dea06d8298ae8052b4b049cbf8ef74998e28d57166cc27b8ae909e2/numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be", size = 145029 }, + { url = "https://files.pythonhosted.org/packages/3e/3c/fcd5a812ed5dda757b2d9ef2764a3e1cca6f6d1f02dbf113dc23a2c7702a/numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42", size = 134851 }, + { url = "https://files.pythonhosted.org/packages/0a/52/0ed3b306d8c9944129bce97fec73a2caff13adbd7e1df148d546d7eb2d4d/numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470", size = 411837 }, + { url = "https://files.pythonhosted.org/packages/7d/9c/6b671dd3fb67d7e7da93cb76b7c5277743f310a216b7856bb18776bb3371/numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6", size = 400577 }, + { url = "https://files.pythonhosted.org/packages/ea/4d/a167d1a215fe10ce58c45109f2869fd13aa0eef66f7e8c69af68be45d436/numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db", size = 1381735 }, + { url = "https://files.pythonhosted.org/packages/c1/d4/17e4434f989e4917d31cbd88a043e1c9c16958149cf43fa622987111392b/numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e", size = 152102 }, + { url = "https://files.pythonhosted.org/packages/b8/25/9ae599994076ef2a42d35ff6b0430da002647f212567851336a6c7b132d6/numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead", size = 145061 }, + { url = "https://files.pythonhosted.org/packages/8c/cb/2ea1848c46e4d75073c038dd75628d1aa442975303264ed230bf90f74f44/numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d", size = 145035 }, + { url = "https://files.pythonhosted.org/packages/ec/cf/bb2bcd81d6f3243590e19ac3e7795a1a370f3ebcd8ecec1f46dcd5333f37/numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f", size = 134858 }, + { url = "https://files.pythonhosted.org/packages/48/9b/c9128ffb453205c2a4c84a3abed35447c7591c2c2812e77e34fd238cb2bb/numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e", size = 415517 }, + { url = "https://files.pythonhosted.org/packages/7e/b0/64c04c9f8b4a563218d00daa1ec4563364961b79025162c5276ab2c7c407/numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527", size = 403846 }, + { url = "https://files.pythonhosted.org/packages/80/35/60e9041fd709fe98dd3109d73a03cdffaeb6ee2089179155f5c3754e9934/numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19", size = 1381659 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/955bf5b5cf8f3de7b044a999e36327e14191fa073ed0e329456ed0f8161d/numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01", size = 152105 }, + { url = "https://files.pythonhosted.org/packages/be/7a/8ce360a1848bb5bcc30a414493371678f43790ece397f8652d5f65757e57/numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd", size = 145060 }, +] + +[[package]] +name = "numpy" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/b3/b13bce39ba82b7398c06d10446f5ffd5c07db39b09bd37370dc720c7951c/numpy-1.26.0.tar.gz", hash = "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf", size = 15633455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/2f/b42860931c1479714201495ffe47d74460a916ae426a21fc9b68c5e329aa/numpy-1.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", size = 20619338 }, + { url = "https://files.pythonhosted.org/packages/35/21/9e150d654da358beb29fe216f339dc17f2b2ac13fff2a89669401a910550/numpy-1.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", size = 13981953 }, + { url = "https://files.pythonhosted.org/packages/a9/84/baf694be765d68c73f0f8a9d52151c339aed5f2d64205824a6f29021170c/numpy-1.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", size = 14167328 }, + { url = "https://files.pythonhosted.org/packages/c4/36/161e2f8110f8c49e59f6107bd6da4257d30aff9f06373d0471811f73dcc5/numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", size = 18178118 }, + { url = "https://files.pythonhosted.org/packages/37/41/63975634a93da2a384d3c8084eba467242cab68daab0cd8f4fd470dcee26/numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", size = 18020808 }, + { url = "https://files.pythonhosted.org/packages/58/d2/cbc329aa908cb963bd849f14e24f59c002a488e9055fab2c68887a6b5f1c/numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", size = 20750149 }, + { url = "https://files.pythonhosted.org/packages/93/fd/3f826c6d15d3bdcf65b8031e4835c52b7d9c45add25efa2314b53850e1a2/numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", size = 15794407 }, + { url = "https://files.pythonhosted.org/packages/e9/83/f8a62f08d38d831a2980427ffc465a4207fe600124b00cfb0ef8265594a7/numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", size = 20325091 }, + { url = "https://files.pythonhosted.org/packages/7a/72/6d1cbdf0d770016bc9485f9ef02e73d5cb4cf3c726f8e120b860a403d307/numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", size = 13672867 }, + { url = "https://files.pythonhosted.org/packages/2f/70/c071b2347e339f572f5aa61f649b70167e5dd218e3da3dc600c9b08154b9/numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", size = 13872627 }, + { url = "https://files.pythonhosted.org/packages/e3/e2/4ecfbc4a2e3f9d227b008c92a5d1f0370190a639b24fec3b226841eaaf19/numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", size = 17883864 }, + { url = "https://files.pythonhosted.org/packages/45/08/025bb65dbe19749f1a67a80655670941982e5d0144a4e588ebbdbcfe7983/numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", size = 17721550 }, + { url = "https://files.pythonhosted.org/packages/98/66/f0a846751044d0b6db5156fb6304d0336861ed055c21053a0f447103939c/numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", size = 19951520 }, + { url = "https://files.pythonhosted.org/packages/98/d7/1cc7a11118408ad21a5379ff2a4e0b0e27504c68ef6e808ebaa90ee95902/numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", size = 15504471 }, +] + +[[package]] +name = "openpyxl" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/e8/af028681d493814ca9c2ff8106fc62a4a32e4e0ae14602c2a98fc7b741c8/openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184", size = 185977 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/94/a59521de836ef0da54aaf50da6c4da8fb4072fb3053fa71f052fd9399e7a/openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5", size = 249985 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, + { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, + { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, + { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, + { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, + { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, + { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, + { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, + { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, + { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, + { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, + { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, + { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "partd" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "locket" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 }, + { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 }, + { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 }, + { url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 }, + { url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 }, + { url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 }, + { url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 }, + { url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 }, + { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 }, + { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 }, + { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 }, + { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, + { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, + { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, + { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, + { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, + { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, + { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, + { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, + { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, + { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, +] + +[[package]] +name = "plac" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/79/1edb4c836c69306d0ecb0865f46d62ea7e28ef16b3f95bb394e4f2a46330/plac-1.4.3.tar.gz", hash = "sha256:d4cb3387b2113a28aebd509433d0264a4e5d9bb7c1a86db4fbd0a8f11af74eb3", size = 38984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/af/4c61d2ac0d589719f548f5a1ba919738e44bac7b0c723ce147de5556d233/plac-1.4.3-py2.py3-none-any.whl", hash = "sha256:8a84fde8f950c9de6588a2d53c9deeac3ba1ddb456d887a33228460cf6549750", size = 22458 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "plotly" +version = "5.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/bd/6516a9f3105cf15bf5892b2eec6eac4dd1645a4b1ed82ae86e0a044dab36/plotly-5.17.0.tar.gz", hash = "sha256:290d796bf7bab87aad184fe24b86096234c4c95dcca6ecbca02d02bdf17d3d97", size = 7824316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/79/c80174d711ee26ee5da55a9cc3e248f1ec7a0188b5e4d6bbbbcd09b974b0/plotly-5.17.0-py2.py3-none-any.whl", hash = "sha256:7c84cdf11da162423da957bb093287134f2d6f170eb9a74f1459f825892247c3", size = 15641686 }, +] + +[[package]] +name = "ply" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567 }, +] + +[[package]] +name = "polars" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/f8/148964866c43e6c9d2440da69149cedaca8ad799efbeba1f2bd58c48d95f/polars-1.22.0.tar.gz", hash = "sha256:8d94ae25085d92de10d93ab6a06c94f8c911bd5d9c1ff17cd1073a9dca766029", size = 4399700 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/59/ff6185f1cd3898a655fb69571986433adec80c5fe5dbd37edf1025dbbd17/polars-1.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6250f838b916fab23ccafe90928d7952afc328d316c956b42d152b20c86ffd9c", size = 32294750 }, + { url = "https://files.pythonhosted.org/packages/4e/89/ac9178aaf4bfce1087d311ddf540b259a517b584a62ba75dfd114a38e049/polars-1.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5ee3cf3783205709ce31f070f2b4ee4296fec08f2c744a9c37acc7d360121022", size = 29145077 }, + { url = "https://files.pythonhosted.org/packages/91/b6/d7967ca14b8bacf7d3db96b55213571c43443f8802229606cca60458780b/polars-1.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f25b4ef131da046d05b8235c5f29997630ee2125ebc0553b92258e88f7a8fa", size = 32885965 }, + { url = "https://files.pythonhosted.org/packages/a2/ae/c014bcb259757acd9081b4db76d19157c8978cb6eeb864f80ac0544e85cd/polars-1.22.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:729e6be8a884812a206518195a2fb407b61962323886095ede1a2a934cdb1410", size = 30165643 }, + { url = "https://files.pythonhosted.org/packages/82/ef/2a13676be0f0cfa23f1899855d8342b303cc46111126b770cdacc7fd804d/polars-1.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:78b8bcd1735e9376815d117aeae49391441b2199b5a70a300669d692b34ec713", size = 33179742 }, + { url = "https://files.pythonhosted.org/packages/79/ae/f46ff902e5ad0d3ce377902ae8ff65eaac23d9aba3c6bcb3b38ce22f5544/polars-1.22.0-cp39-abi3-win_arm64.whl", hash = "sha256:cde8f56c408151ab9790c43485b90f690d5c198ce26ab38a845045c73c999325", size = 29458831 }, +] + +[[package]] +name = "pre-commit" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 }, +] + +[[package]] +name = "progressbar2" +version = "4.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/5a/66e4838702ee7152e89a03498409e5b47536e637da2df725600201748ee6/progressbar2-4.3.2.tar.gz", hash = "sha256:c37e6e1b4e57ab43f95c3d0e8d90061bec140e4fed56b8343183db3aa1e19a52", size = 92504 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/66/86ac1b2ad859641bf5186fae72d90ca091a25aee3d6a1ba1772ea19895ba/progressbar2-4.3.2-py3-none-any.whl", hash = "sha256:036fa3bd35ae27c92e73fce4fb18aa4ba5090a1880d880cf954ecb75ccd6f3fb", size = 50571 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, +] + +[[package]] +name = "psutil" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 }, + { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 }, + { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 }, + { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 }, + { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 }, + { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pudb" +version = "2024.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jedi" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "urwid" }, + { name = "urwid-readline" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/27/fb087bdf2cd9c8e56d0347b863ce5995967c15a3e2a0b9245c7a8f6f1598/pudb-2024.1.3.tar.gz", hash = "sha256:264f239e0538e52e83d3d020143100b3171cae17227674bb1b9f8b075f34849c", size = 219443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/00/592eeb47eb30d9a4e34512eb1208537546c1073d43cabe7def26d89a7134/pudb-2024.1.3-py3-none-any.whl", hash = "sha256:b1c64447fcee5848d0376231672b97ca8580ca6d3297c82cba6b0ba5bd5ee640", size = 87963 }, +] + +[[package]] +name = "pulp" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/41/44d617a67407ea5db026500025b8aa7cad0b2b52621c04991b248c3b383d/PuLP-2.7.0.tar.gz", hash = "sha256:e73ee6b32d639c9b8cf4b4aded334ba158be5f8313544e056f796ace0a10ae63", size = 1400315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/60/b91acaa7995bfcd72f1739ea2b0f5cda707329e17f0b7f921fd8acc79889/PuLP-2.7.0-py3-none-any.whl", hash = "sha256:b6de42c929e80325bf44cc7a2997f02535440800c376b9eb8cb7b4670ed53769", size = 14251631 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335 }, +] + +[[package]] +name = "pyarrow" +version = "16.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/f2/67533f116deb6dae7a0ac04681695fe06135912253a115c5ecdc714a32d4/pyarrow-16.1.0.tar.gz", hash = "sha256:15fbb22ea96d11f0b5768504a3f961edab25eaf4197c341720c4a387f6c60315", size = 1080280 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/17/a12aaddb818b7b73d17f3304afc22bce32ccb26723b507cc9c267aa809f3/pyarrow-16.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d0ebea336b535b37eee9eee31761813086d33ed06de9ab6fc6aaa0bace7b250c", size = 28380406 }, + { url = "https://files.pythonhosted.org/packages/f3/94/4e2a579bbac1adb19e63b054b300f6f7fa04f32f212ce86c18727bdda698/pyarrow-16.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e73cfc4a99e796727919c5541c65bb88b973377501e39b9842ea71401ca6c1c", size = 26040531 }, + { url = "https://files.pythonhosted.org/packages/7e/34/d5b6eb5066553533dd6eb9782d50f353f8c6451ee2e49e0ea54d0e67bc34/pyarrow-16.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9251264247ecfe93e5f5a0cd43b8ae834f1e61d1abca22da55b20c788417f6", size = 38666685 }, + { url = "https://files.pythonhosted.org/packages/d2/34/4e3c04e7398764e56ef00f8f267f8ebf565808478f5fee850cef4be670c3/pyarrow-16.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf5aace92d520d3d2a20031d8b0ec27b4395cab9f74e07cc95edf42a5cc0147", size = 40949577 }, + { url = "https://files.pythonhosted.org/packages/47/62/b446ee0971b00e7437b9c54a8409ae20413235a64c0a301d7cf97070cffa/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e", size = 38077480 }, + { url = "https://files.pythonhosted.org/packages/fa/15/48a68b30542a0231a75c26d8661bc5c9bbc07b42c5b219e929adba814ba7/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b", size = 40821141 }, + { url = "https://files.pythonhosted.org/packages/49/4d/62a09116ec357ade462fac4086e0711457a87177bea25ae46b25897d6d7c/pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b", size = 25889334 }, + { url = "https://files.pythonhosted.org/packages/84/bd/d5903125e38c33b74f7b3d57ffffd4ef48145208cfd8742367f12effb59c/pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f", size = 28372822 }, + { url = "https://files.pythonhosted.org/packages/9b/73/560ef6bf05f16305502b8e368c771e8f82d774898b37a3fb231f89c13342/pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a", size = 26004052 }, + { url = "https://files.pythonhosted.org/packages/56/5e/3cd956aceb1c960e8ac6fdc6eea69d642aa2e6ee10e2f10ce7815dbf62a9/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c", size = 38660648 }, + { url = "https://files.pythonhosted.org/packages/08/4a/668e7fb6bc564e5361097f1f160b2891ca40bcacfe018638e2841073ec3d/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2", size = 40961053 }, + { url = "https://files.pythonhosted.org/packages/f7/8f/a51a290a855172514b8496c8a74f0e0b98e5e0582d44ae7547cf68dd033b/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628", size = 38060675 }, + { url = "https://files.pythonhosted.org/packages/25/7b/8da91f8de0b40b760dd748031973b6ac2aa3d4f85c67f45b7e58577ca22e/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7", size = 40826735 }, + { url = "https://files.pythonhosted.org/packages/fa/2b/a0053f1304586f2976cb2c37ddb0e52cf4114220e805ebba272a1e231ccc/pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444", size = 25838156 }, +] + +[[package]] +name = "pycountry" +version = "22.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/24/033604d30f6cf82d661c0f9dfc2c71d52cafc2de516616f80d3b0600cb7c/pycountry-22.3.5.tar.gz", hash = "sha256:b2163a246c585894d808f18783e19137cb70a0c18fb36748dc01fc6f109c1646", size = 10141551 } + +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "babel" }, + { name = "beautifulsoup4" }, + { name = "docutils" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pyogrio" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "numpy" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/8f/5a784595524a79c269f2b1c880f4fdb152867df700c97005dda51997da02/pyogrio-0.10.0.tar.gz", hash = "sha256:ec051cb568324de878828fae96379b71858933413e185148acb6c162851ab23c", size = 281950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/2c/c761e6adeb81bd4029a137b3240e7214a8c9aaf225883356196afd6ef9d8/pyogrio-0.10.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5b1a51431a27a1cb3e4e19558939c1423106e06e7b67d6285f4fba9c2d0a91b9", size = 15083526 }, + { url = "https://files.pythonhosted.org/packages/c3/e5/983aa9ddf2ff784e973d6b2ec3e874065d6655a5329ca26311b0f3b9f92f/pyogrio-0.10.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:216d69cd77b2b4a0c9d7d449bc239f8b77f3d73f4a05d9c738a0745b236902d8", size = 16457867 }, + { url = "https://files.pythonhosted.org/packages/fa/9a/7103eee7aa3b6ec88e072ef18a05c3aae1ed96fe00009a7a5ce139b50f30/pyogrio-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2f0b75f0077ce33256aec6278c2a9c3b79bf0637ddf4f93d3ab2609f0501d96", size = 23926332 }, + { url = "https://files.pythonhosted.org/packages/8b/b2/2ca124343aba24b9a5dcd7c1f43da81e652849cfaf3110d3f507a80af0a1/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0a47f702d29808c557d2ebea8542c23903f021eae44e16838adef2ab4281c71b", size = 23138693 }, + { url = "https://files.pythonhosted.org/packages/ae/15/501aa4823c142232169d54255ab343f28c4ea9e7fa489b8433dcc873a942/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:11e6c71d12da6b445e77d0fc0198db1bd35a77e03a0685e45338cbab9ce02add", size = 24062952 }, + { url = "https://files.pythonhosted.org/packages/94/8d/24f21e6a93ca418231aee3bddade7a0766c89c523832f29e08a8860f83e6/pyogrio-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0d74e91a9c0ff2f9abe01b556ff663977193b2d6922208406172d0fc833beff", size = 16172573 }, + { url = "https://files.pythonhosted.org/packages/b5/b5/3c5dfd0b50cbce6f3d4e42c0484647feb1809dbe20e225c4c6abd067e69f/pyogrio-0.10.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d6558b180e020f71ab7aa7f82d592ed3305c9f698d98f6d0a4637ec7a84c4ce", size = 15079211 }, + { url = "https://files.pythonhosted.org/packages/b8/9a/1ba9c707a094976f343bd0177741eaba0e842fa05ecd8ab97192db4f2ec1/pyogrio-0.10.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a99102037eead8ba491bc57825c1e395ee31c9956d7bff7b4a9e4fdbff3a13c2", size = 16442782 }, + { url = "https://files.pythonhosted.org/packages/5e/bb/b4250746c2c85fea5004cae93e9e25ad01516e9e94e04de780a2e78139da/pyogrio-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a4c373281d7cbf560c5b61f8f3c7442103ad7f1c7ac4ef3a84572ed7a5dd2f6", size = 23899832 }, + { url = "https://files.pythonhosted.org/packages/bd/4c/79e47e40a8e54e79a45133786a0a58209534f580591c933d40c5ed314fe7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:19f18411bdf836d24cdc08b9337eb3ec415e4ac4086ba64516b36b73a2e88622", size = 23081469 }, + { url = "https://files.pythonhosted.org/packages/47/78/2b62c8a340bcb0ea56b9ddf2ef5fd3d1f101dc0e98816b9e6da87c5ac3b7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1abbcdd9876f30bebf1df8a0273f6cdeb29d03259290008275c7fddebe139f20", size = 24024758 }, + { url = "https://files.pythonhosted.org/packages/43/97/34605480f06b0ad9611bf58a174eccc6f3673275f3d519cf763391892881/pyogrio-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a3e09839590d71ff832aa95c4f23fa00a2c63c3de82c1fbd4fb8d265792acfc", size = 16160294 }, + { url = "https://files.pythonhosted.org/packages/14/4a/4c8e4f5b9edbca46e0f8d6c1c0b56c0d4af0900c29f4bea22d37853c07f3/pyogrio-0.10.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c90478209537a31dcc65664a87a04c094bb0e08efe502908a6682b8cec0259bf", size = 15076879 }, + { url = "https://files.pythonhosted.org/packages/5f/be/7db0644eef9ef3382518399aaf3332827c43018112d2a74f78784fd496ec/pyogrio-0.10.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:fec45e1963b7058e5a1aa98598aed07c0858512c833d6aad2c672c3ec98bbf04", size = 16440405 }, + { url = "https://files.pythonhosted.org/packages/96/77/f199230ba86fe88b1f57e71428c169ed982de68a32d6082cd7c12d0f5d55/pyogrio-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28cb139f8a5d0365ede602230104b407ae52bb6b55173c8d5a35424d28c4a2c5", size = 23871511 }, + { url = "https://files.pythonhosted.org/packages/25/ac/ca483bec408b59c54f7129b0244cc9de21d8461aefe89ece7bd74ad33807/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:cea0187fcc2d574e52af8cfab041fa0a7ad71d5ef6b94b49a3f3d2a04534a27e", size = 23048830 }, + { url = "https://files.pythonhosted.org/packages/d7/3e/c35f2d8dad95b24e568c468f09ff60fb61945065465e0ec7868400596566/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7c02b207ea8cf09c501ea3e95d29152781a00d3c32267286bc36fa457c332205", size = 23996873 }, + { url = "https://files.pythonhosted.org/packages/27/5d/0deb16d228362a097ee3258d0a887c9c0add4b9678bb4847b08a241e124d/pyogrio-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:02e54bcfb305af75f829044b0045f74de31b77c2d6546f7aaf96822066147848", size = 16158260 }, +] + +[[package]] +name = "pyomo" +version = "6.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ply" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/70/ea738ea23c23399e5e04f184bf78fb181e91284369b4fb3ce74b107edcea/Pyomo-6.6.1.tar.gz", hash = "sha256:3fb0aba7b0f4120e6ce0f242502c0e61478d61e326bc90b7dc392bbefd114b34", size = 2501065 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/9c/d83ade42771c3dc80e69f9eaf68c95ce4de33c790d7e188ce71f2966e20c/Pyomo-6.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fa2442abdd4872320c0a8eca960136fdd9d4faa14353be3391eff11403547027", size = 6999786 }, + { url = "https://files.pythonhosted.org/packages/2e/a1/b2f713d1f1a784a56f7561a498ebd550c3890b38c59a9bbb7350f223ba21/Pyomo-6.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f795a4fb345ab5676acf57b39643a3605f1d88c6d10d6710514d959bd52bfb1", size = 12847137 }, + { url = "https://files.pythonhosted.org/packages/1c/38/3e085badd0b9c38de2d1fbb34ce0a680579aac1db839a7af9044bed59ca4/Pyomo-6.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e0b12f82f0c70ed246bb8b5eed8279cee07d7f2218bc8dd955562b9a82e595", size = 4345819 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, +] + +[[package]] +name = "pyproj" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/c2/0572c8e31aebf0270f15f3368adebd10fc473de9f09567a0743a3bc41c8d/pyproj-3.7.0.tar.gz", hash = "sha256:bf658f4aaf815d9d03c8121650b6f0b8067265c36e31bc6660b98ef144d81813", size = 225577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/8f/15ff6ab10a08050e94afcd544962a1a930d0bb7ca102ad39795a847eb340/pyproj-3.7.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e66d8d42dbdf232e121546c5a1dec097caf0454e4885c09a8e03cdcee0753c03", size = 6272213 }, + { url = "https://files.pythonhosted.org/packages/2d/4d/610fe2a17de71b4fe210af69ce25f2d65379ba0a48299129894d0d0988ee/pyproj-3.7.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7764b64a0aefe40134a2828b3a40be88f6c8b7832c45d8a9f2bd592ace4b2a3b", size = 4634548 }, + { url = "https://files.pythonhosted.org/packages/d6/27/0327d0b0fcdfc4cf72696a2effca2963e524dcd846a0274ba503f8bf2648/pyproj-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53c442c5081dc95346996f5c4323fde2caafc69c6e60b4707aa46e88244f1e04", size = 6333913 }, + { url = "https://files.pythonhosted.org/packages/3c/e5/2cb256148c730b9c3f74bfb3c03904f5070499c6dcaea153073a9642c6c6/pyproj-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc5b305d4d5d7697885681d9b660623e328227612823d5c660e0a9566cb48838", size = 9460363 }, + { url = "https://files.pythonhosted.org/packages/ba/a3/4aa1e8e78ad18aa170efd2c94c1931bf2a34c526683b874d06e40fa323f6/pyproj-3.7.0-cp311-cp311-win32.whl", hash = "sha256:de2b47d748dc41cccb6b3b713d4d7dc9aa1046a82141c8665026908726426abc", size = 5820551 }, + { url = "https://files.pythonhosted.org/packages/26/0c/b084e8839a117eaad8cb4fbaa81bbb24c6f183de0ee95c6c4e2770ab6f09/pyproj-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:38cba7c4c5679e40242dd959133e95b908d3b912dd66291094fd13510e8517ff", size = 6231788 }, + { url = "https://files.pythonhosted.org/packages/bd/19/be806b711e9ebfb80411c653054157db128fffdd7f8493e3064136c8d880/pyproj-3.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:8cbec92bdd6e9933ca08795c12717d1384e9b51cf4b1acf0d753db255a75c51e", size = 6261400 }, + { url = "https://files.pythonhosted.org/packages/99/3b/8497995e8cae0049d013679c6a7ac6c57b816d590c733a388748dafe5af5/pyproj-3.7.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8c4a8e4d3ba76c3adac3c087544cf92f7f9a19ea34946904a13fca48cc1c0106", size = 4637848 }, + { url = "https://files.pythonhosted.org/packages/ea/f7/2a5b46d6f8da913d58d44942ab06ca4803b5424b73259b15344cf90040f6/pyproj-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82624fb42aa31f6b1a860fbc0316babd07fd712642bc31022df4e9b4056bf463", size = 6324856 }, + { url = "https://files.pythonhosted.org/packages/36/83/c257771077bcf9da20d0e97abc834f9037c219986cc76d40183903a30464/pyproj-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1bbb3f89c68d4a6835c40b2da8b27680eec60e8cc7cdb08c09bcc725b2b62", size = 9525831 }, + { url = "https://files.pythonhosted.org/packages/d6/50/a635de79def69fe03cdef3a4bd3bec780c30987bce3a15dd7099afb2506f/pyproj-3.7.0-cp312-cp312-win32.whl", hash = "sha256:952515d5592167ad4436b355485f82acebed2a49b46722159e4584b75a763dd3", size = 5811864 }, + { url = "https://files.pythonhosted.org/packages/a1/8b/96bc8c8f3eca4eb7fa3758fde0b755d1df30a19f494376e3ee8de1ef4e79/pyproj-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0692f806224e8ed82fe4acfa57268ff444fdaf9f330689f24c0d96e59480cce1", size = 6224720 }, + { url = "https://files.pythonhosted.org/packages/bf/da/a17c452bea1ff4cd58d6dc573055b9c8fb6af114b7d2c694782aec770865/pyproj-3.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:94e8b903a9e83448fd2379c49dec3e8cd83c9ed36f54354e68b601cef56d5426", size = 6254898 }, + { url = "https://files.pythonhosted.org/packages/c2/31/ab07b389f2caa527c95ab2ea1940d28879bd2a19e67b2529cb3e94648d26/pyproj-3.7.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:64cb5c17d6f6305a8b978a40f95560c87c5b363fcac40632337955664437875a", size = 4628612 }, + { url = "https://files.pythonhosted.org/packages/1d/24/def3ded6529db3e3d8351ad73481730249ab57d8d876d502f86d7958ce06/pyproj-3.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c54e9bdda7ab9c4a5af50f9d6e6ee7704e05fafd504896b96ed1208c7aea098", size = 6315895 }, + { url = "https://files.pythonhosted.org/packages/dd/14/07314f78302105d199fb25e73376d723efe9c2ef3906463aae209913a6d3/pyproj-3.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24fa4e9e0abba875f9524808410cc520067eaf38fd5549ed0ef7c43ac39923c9", size = 9466144 }, + { url = "https://files.pythonhosted.org/packages/00/f2/2a116920db3496e3ff3c94d7d8d15da41374f35cfe1b9e79682eca500a61/pyproj-3.7.0-cp313-cp313-win32.whl", hash = "sha256:b9e8353fc3c79dc14d1f5ac758a1a6e4eee04102c3c0b138670f121f5ac52eb4", size = 5807180 }, + { url = "https://files.pythonhosted.org/packages/f8/33/3c8c6302717096b54aa14ccbb271045ba04629e21cbf348f2f2dc94f69b4/pyproj-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:10a8dc6ec61af97c89ff032647d743f8dc023645773da42ef43f7ae1125b3509", size = 6218036 }, +] + +[[package]] +name = "pypsa" +version = "0.30.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecation" }, + { name = "geopandas" }, + { name = "highspy" }, + { name = "linopy" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "tables" }, + { name = "validators" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/c4/2765234ce7aceca75d3118952161801044ce7996b88fdf720114e70687aa/pypsa-0.30.2.tar.gz", hash = "sha256:2ea2d4b7c8def60ae7ee2557bcd456a8412386a1b55f91ef82067d5d1e9793b0", size = 4974370 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/0f/f20ad09b34315634400da3c63b8e9d625edad6e2a7fa28351b0ac595756f/pypsa-0.30.2-py3-none-any.whl", hash = "sha256:1ebc22d08c1cd1e06c76654d9665245136c2afae1da892bfcc90eff70dff4e0d", size = 135575 }, +] + +[[package]] +name = "pypsa-usa" +source = { editable = "." } +dependencies = [ + { name = "atlite" }, + { name = "cartopy" }, + { name = "descartes" }, + { name = "dill" }, + { name = "duckdb" }, + { name = "geopandas" }, + { name = "geopy" }, + { name = "graphviz" }, + { name = "gurobipy" }, + { name = "highspy" }, + { name = "kaleido" }, + { name = "linopy" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "openpyxl" }, + { name = "pandas" }, + { name = "plotly" }, + { name = "progressbar2" }, + { name = "pulp" }, + { name = "pyarrow" }, + { name = "pycountry" }, + { name = "pyomo" }, + { name = "pypsa" }, + { name = "pyyaml" }, + { name = "rasterio" }, + { name = "scipy" }, + { name = "seaborn" }, + { name = "shapely" }, + { name = "snakemake" }, + { name = "tsam" }, + { name = "xarray" }, + { name = "xlrd" }, +] + +[package.optional-dependencies] +dev = [ + { name = "bump2version" }, + { name = "ipython" }, + { name = "mypy" }, + { name = "myst-parser" }, + { name = "pre-commit" }, + { name = "pudb" }, + { name = "ruff" }, + { name = "sphinx" }, + { name = "sphinx-autobuild" }, + { name = "sphinx-book-theme" }, + { name = "types-pyyaml" }, +] + +[package.metadata] +requires-dist = [ + { name = "atlite", specifier = "==0.3.0" }, + { name = "bump2version", marker = "extra == 'dev'" }, + { name = "cartopy", specifier = "==0.23.0" }, + { name = "descartes", specifier = "==1.1.0" }, + { name = "dill", specifier = ">=0.3.9" }, + { name = "duckdb", specifier = "==0.10.0" }, + { name = "geopandas", specifier = "==1.0.1" }, + { name = "geopy", specifier = "==2.4.0" }, + { name = "graphviz", specifier = ">=0.20.3" }, + { name = "gurobipy", specifier = "==11.0.3" }, + { name = "highspy", specifier = ">=1.9.0" }, + { name = "ipython", marker = "extra == 'dev'" }, + { name = "kaleido", specifier = "==0.4.0rc5" }, + { name = "linopy", specifier = "==0.3.14" }, + { name = "matplotlib", specifier = "==3.8.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.11.0" }, + { name = "myst-parser", marker = "extra == 'dev'" }, + { name = "netcdf4", specifier = "==1.6.4" }, + { name = "networkx", specifier = "==3.1" }, + { name = "numpy", specifier = "==1.26.0" }, + { name = "openpyxl", specifier = "==3.1.2" }, + { name = "pandas", specifier = "==2.2.2" }, + { name = "plotly", specifier = "==5.17.0" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "progressbar2", specifier = "==4.3.2" }, + { name = "pudb", marker = "extra == 'dev'" }, + { name = "pulp", specifier = "==2.7.0" }, + { name = "pyarrow", specifier = "==16.1.0" }, + { name = "pycountry", specifier = "==22.3.5" }, + { name = "pyomo", specifier = "==6.6.1" }, + { name = "pypsa", specifier = "==0.30.2" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "rasterio", specifier = "==1.3.8" }, + { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.5.2" }, + { name = "scipy", specifier = "==1.11.3" }, + { name = "seaborn", specifier = "==0.13.2" }, + { name = "shapely", specifier = "==2.0.2" }, + { name = "snakemake", specifier = "==7.32.4" }, + { name = "sphinx", marker = "extra == 'dev'" }, + { name = "sphinx-autobuild", marker = "extra == 'dev'" }, + { name = "sphinx-book-theme", marker = "extra == 'dev'" }, + { name = "tsam", specifier = ">=2.3.6" }, + { name = "types-pyyaml", marker = "extra == 'dev'" }, + { name = "xarray", specifier = "==2024.9.0" }, + { name = "xlrd", specifier = "==2.0.1" }, +] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, +] + +[[package]] +name = "pyshp" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9f/0dd21250c60375a532c35e89fad8d5e8a3f1a2e3f7c389ccc5a60b05263e/pyshp-2.3.1.tar.gz", hash = "sha256:4caec82fd8dd096feba8217858068bacb2a3b5950f43c048c6dc32a3489d5af1", size = 1731544 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/2f/68116db5b36b895c0450e3072b8cb6c2fac0359279b182ea97014d3c8ac0/pyshp-2.3.1-py2.py3-none-any.whl", hash = "sha256:67024c0ccdc352ba5db777c4e968483782dfa78f8e200672a90d2d30fd8b7b49", size = 46537 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-utils" +version = "3.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/4c/ef8b7b1046d65c1f18ca31e5235c7d6627ca2b3f389ab1d44a74d22f5cc9/python_utils-3.9.1.tar.gz", hash = "sha256:eb574b4292415eb230f094cbf50ab5ef36e3579b8f09e9f2ba74af70891449a0", size = 35403 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/69/31c82567719b34d8f6b41077732589104883771d182a9f4ff3e71430999a/python_utils-3.9.1-py2.py3-none-any.whl", hash = "sha256:0273d7363c7ad4b70999b2791d5ba6b55333d6f7a4e4c8b6b39fb82b5fab4613", size = 32078 }, +] + +[[package]] +name = "pytz" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, +] + +[[package]] +name = "pywin32" +version = "308" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 }, + { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 }, + { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 }, + { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, + { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, + { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, + { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, + { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, + { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "rasterio" +version = "1.3.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "affine" }, + { name = "attrs" }, + { name = "certifi" }, + { name = "click" }, + { name = "click-plugins" }, + { name = "cligj" }, + { name = "numpy" }, + { name = "setuptools" }, + { name = "snuggs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/2e/65affa3bd9c6c8f4a3b4f7cbf7947d0a3b3a65675af58162f80201c01510/rasterio-1.3.8.tar.gz", hash = "sha256:ffdd18e78efdf8ad5861065fd812a66dd34264293317ff6540a078ea891cdef8", size = 412381 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/2c/16b7c4feb71b8e56d79e831ae26001e3e699c9f3c279fb5cb4b17e7d8c64/rasterio-1.3.8-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d5ccc8e6d30534d510ce5099d4a35616611cadcae79aa1216150c2696e03ddde", size = 22810323 }, + { url = "https://files.pythonhosted.org/packages/9f/47/320376453d416461e5d16101cdebdaae019b59ce5a169ce855c6ea7fd560/rasterio-1.3.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3b8410be847e8fd96cbe744e28e484437b370830052b5dcc7b11efc8c73fffc", size = 19453629 }, + { url = "https://files.pythonhosted.org/packages/7c/5d/6fb859b5df963b3213da844fe6c24065fc9d20e3a47146dd0454efa7021e/rasterio-1.3.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8e1b456f58b9ae023026730320424091af504ef066418ddcd296b9014845ee", size = 21263873 }, + { url = "https://files.pythonhosted.org/packages/03/96/1a35b15183bdc9c6a19e08cd92b5ebb201a9c76ec24c3f17a714202c5d2b/rasterio-1.3.8-cp311-cp311-win_amd64.whl", hash = "sha256:0323332ed1bfad522e53a3da45e0d3453e603862c3d2c08d8a639a7be76853fb", size = 22914349 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "reretry" +version = "0.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/1d/25d562a62b7471616bccd7c15a7533062eb383927e68667bf331db990415/reretry-0.11.8.tar.gz", hash = "sha256:f2791fcebe512ea2f1d153a2874778523a8064860b591cd90afc21a8bed432e3", size = 4836 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/11/e295e07d4ae500144177f875a8de11daa4d86b8246ab41c76a98ce9280ca/reretry-0.11.8-py2.py3-none-any.whl", hash = "sha256:5ec1084cd9644271ee386d34cd5dd24bdb3e91d55961b076d1a31d585ad68a79", size = 5609 }, +] + +[[package]] +name = "rpds-py" +version = "0.22.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/ad/8d1ddf78f2805a71253fcd388017e7b4a0615c22c762b6d35301fef20106/rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", size = 359773 }, + { url = "https://files.pythonhosted.org/packages/c8/75/68c15732293a8485d79fe4ebe9045525502a067865fa4278f178851b2d87/rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", size = 349214 }, + { url = "https://files.pythonhosted.org/packages/3c/4c/7ce50f3070083c2e1b2bbd0fb7046f3da55f510d19e283222f8f33d7d5f4/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", size = 380477 }, + { url = "https://files.pythonhosted.org/packages/9a/e9/835196a69cb229d5c31c13b8ae603bd2da9a6695f35fe4270d398e1db44c/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", size = 386171 }, + { url = "https://files.pythonhosted.org/packages/f9/8e/33fc4eba6683db71e91e6d594a2cf3a8fbceb5316629f0477f7ece5e3f75/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", size = 422676 }, + { url = "https://files.pythonhosted.org/packages/37/47/2e82d58f8046a98bb9497a8319604c92b827b94d558df30877c4b3c6ccb3/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", size = 446152 }, + { url = "https://files.pythonhosted.org/packages/e1/78/79c128c3e71abbc8e9739ac27af11dc0f91840a86fce67ff83c65d1ba195/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", size = 381300 }, + { url = "https://files.pythonhosted.org/packages/c9/5b/2e193be0e8b228c1207f31fa3ea79de64dadb4f6a4833111af8145a6bc33/rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", size = 409636 }, + { url = "https://files.pythonhosted.org/packages/c2/3f/687c7100b762d62186a1c1100ffdf99825f6fa5ea94556844bbbd2d0f3a9/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", size = 556708 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/c00cbc4b857e8b3d5e7f7fc4c81e23afd8c138b930f4f3ccf9a41a23e9e4/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", size = 583554 }, + { url = "https://files.pythonhosted.org/packages/d0/08/696c9872cf56effdad9ed617ac072f6774a898d46b8b8964eab39ec562d2/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", size = 552105 }, + { url = "https://files.pythonhosted.org/packages/18/1f/4df560be1e994f5adf56cabd6c117e02de7c88ee238bb4ce03ed50da9d56/rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", size = 220199 }, + { url = "https://files.pythonhosted.org/packages/b8/1b/c29b570bc5db8237553002788dc734d6bd71443a2ceac2a58202ec06ef12/rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", size = 231775 }, + { url = "https://files.pythonhosted.org/packages/75/47/3383ee3bd787a2a5e65a9b9edc37ccf8505c0a00170e3a5e6ea5fbcd97f7/rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", size = 352334 }, + { url = "https://files.pythonhosted.org/packages/40/14/aa6400fa8158b90a5a250a77f2077c0d0cd8a76fce31d9f2b289f04c6dec/rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", size = 342111 }, + { url = "https://files.pythonhosted.org/packages/7d/06/395a13bfaa8a28b302fb433fb285a67ce0ea2004959a027aea8f9c52bad4/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", size = 384286 }, + { url = "https://files.pythonhosted.org/packages/43/52/d8eeaffab047e6b7b7ef7f00d5ead074a07973968ffa2d5820fa131d7852/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e", size = 391739 }, + { url = "https://files.pythonhosted.org/packages/83/31/52dc4bde85c60b63719610ed6f6d61877effdb5113a72007679b786377b8/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", size = 427306 }, + { url = "https://files.pythonhosted.org/packages/70/d5/1bab8e389c2261dba1764e9e793ed6830a63f830fdbec581a242c7c46bda/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", size = 442717 }, + { url = "https://files.pythonhosted.org/packages/82/a1/a45f3e30835b553379b3a56ea6c4eb622cf11e72008229af840e4596a8ea/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", size = 385721 }, + { url = "https://files.pythonhosted.org/packages/a6/27/780c942de3120bdd4d0e69583f9c96e179dfff082f6ecbb46b8d6488841f/rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", size = 415824 }, + { url = "https://files.pythonhosted.org/packages/94/0b/aa0542ca88ad20ea719b06520f925bae348ea5c1fdf201b7e7202d20871d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", size = 561227 }, + { url = "https://files.pythonhosted.org/packages/0d/92/3ed77d215f82c8f844d7f98929d56cc321bb0bcfaf8f166559b8ec56e5f1/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", size = 587424 }, + { url = "https://files.pythonhosted.org/packages/09/42/cacaeb047a22cab6241f107644f230e2935d4efecf6488859a7dd82fc47d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", size = 555953 }, + { url = "https://files.pythonhosted.org/packages/e6/52/c921dc6d5f5d45b212a456c1f5b17df1a471127e8037eb0972379e39dff4/rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", size = 221339 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/f82b5be1e8456600395366f86104d1bd8d0faed3802ad511ef6d60c30d98/rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", size = 235786 }, + { url = "https://files.pythonhosted.org/packages/d0/bf/36d5cc1f2c609ae6e8bf0fc35949355ca9d8790eceb66e6385680c951e60/rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", size = 351657 }, + { url = "https://files.pythonhosted.org/packages/24/2a/f1e0fa124e300c26ea9382e59b2d582cba71cedd340f32d1447f4f29fa4e/rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", size = 341829 }, + { url = "https://files.pythonhosted.org/packages/cf/c2/0da1231dd16953845bed60d1a586fcd6b15ceaeb965f4d35cdc71f70f606/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", size = 384220 }, + { url = "https://files.pythonhosted.org/packages/c7/73/a4407f4e3a00a9d4b68c532bf2d873d6b562854a8eaff8faa6133b3588ec/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", size = 391009 }, + { url = "https://files.pythonhosted.org/packages/a9/c3/04b7353477ab360fe2563f5f0b176d2105982f97cd9ae80a9c5a18f1ae0f/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", size = 426989 }, + { url = "https://files.pythonhosted.org/packages/8d/e6/e4b85b722bcf11398e17d59c0f6049d19cd606d35363221951e6d625fcb0/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", size = 441544 }, + { url = "https://files.pythonhosted.org/packages/27/fc/403e65e56f65fff25f2973216974976d3f0a5c3f30e53758589b6dc9b79b/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", size = 385179 }, + { url = "https://files.pythonhosted.org/packages/57/9b/2be9ff9700d664d51fd96b33d6595791c496d2778cb0b2a634f048437a55/rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", size = 415103 }, + { url = "https://files.pythonhosted.org/packages/bb/a5/03c2ad8ca10994fcf22dd2150dd1d653bc974fa82d9a590494c84c10c641/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", size = 560916 }, + { url = "https://files.pythonhosted.org/packages/ba/2e/be4fdfc8b5b576e588782b56978c5b702c5a2307024120d8aeec1ab818f0/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", size = 587062 }, + { url = "https://files.pythonhosted.org/packages/67/e0/2034c221937709bf9c542603d25ad43a68b4b0a9a0c0b06a742f2756eb66/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", size = 555734 }, + { url = "https://files.pythonhosted.org/packages/ea/ce/240bae07b5401a22482b58e18cfbabaa392409b2797da60223cca10d7367/rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", size = 220663 }, + { url = "https://files.pythonhosted.org/packages/cb/f0/d330d08f51126330467edae2fa4efa5cec8923c87551a79299380fdea30d/rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", size = 235503 }, + { url = "https://files.pythonhosted.org/packages/f7/c4/dbe1cc03df013bf2feb5ad00615038050e7859f381e96fb5b7b4572cd814/rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", size = 347698 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/684f66dd6b0f37499cad24cd1c0e523541fd768576fa5ce2d0a8799c3cba/rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", size = 337330 }, + { url = "https://files.pythonhosted.org/packages/82/eb/e022c08c2ce2e8f7683baa313476492c0e2c1ca97227fe8a75d9f0181e95/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", size = 380022 }, + { url = "https://files.pythonhosted.org/packages/e4/21/5a80e653e4c86aeb28eb4fea4add1f72e1787a3299687a9187105c3ee966/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", size = 390754 }, + { url = "https://files.pythonhosted.org/packages/37/a4/d320a04ae90f72d080b3d74597074e62be0a8ecad7d7321312dfe2dc5a6a/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", size = 423840 }, + { url = "https://files.pythonhosted.org/packages/87/70/674dc47d93db30a6624279284e5631be4c3a12a0340e8e4f349153546728/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", size = 438970 }, + { url = "https://files.pythonhosted.org/packages/3f/64/9500f4d66601d55cadd21e90784cfd5d5f4560e129d72e4339823129171c/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", size = 383146 }, + { url = "https://files.pythonhosted.org/packages/4d/45/630327addb1d17173adcf4af01336fd0ee030c04798027dfcb50106001e0/rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", size = 408294 }, + { url = "https://files.pythonhosted.org/packages/5f/ef/8efb3373cee54ea9d9980b772e5690a0c9e9214045a4e7fa35046e399fee/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", size = 556345 }, + { url = "https://files.pythonhosted.org/packages/54/01/151d3b9ef4925fc8f15bfb131086c12ec3c3d6dd4a4f7589c335bf8e85ba/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", size = 582292 }, + { url = "https://files.pythonhosted.org/packages/30/89/35fc7a6cdf3477d441c7aca5e9bbf5a14e0f25152aed7f63f4e0b141045d/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", size = 553855 }, + { url = "https://files.pythonhosted.org/packages/8f/e0/830c02b2457c4bd20a8c5bb394d31d81f57fbefce2dbdd2e31feff4f7003/rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", size = 219100 }, + { url = "https://files.pythonhosted.org/packages/f8/30/7ac943f69855c2db77407ae363484b915d861702dbba1aa82d68d57f42be/rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", size = 233794 }, +] + +[[package]] +name = "ruff" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/2b/69e5e412f9d390adbdbcbf4f64d6914fa61b44b08839a6584655014fc524/ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5", size = 2449817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/eb/06e06aaf96af30a68e83b357b037008c54a2ddcbad4f989535007c700394/ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a", size = 9570571 }, + { url = "https://files.pythonhosted.org/packages/a4/10/1be32aeaab8728f78f673e7a47dd813222364479b2d6573dbcf0085e83ea/ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be", size = 8685138 }, + { url = "https://files.pythonhosted.org/packages/3d/1d/c218ce83beb4394ba04d05e9aa2ae6ce9fba8405688fe878b0fdb40ce855/ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e", size = 8266785 }, + { url = "https://files.pythonhosted.org/packages/26/79/7f49509bd844476235b40425756def366b227a9714191c91f02fb2178635/ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8", size = 9983964 }, + { url = "https://files.pythonhosted.org/packages/bf/b1/939836b70bf9fcd5e5cd3ea67fdb8abb9eac7631351d32f26544034a35e4/ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea", size = 9359490 }, + { url = "https://files.pythonhosted.org/packages/32/7d/b3db19207de105daad0c8b704b2c6f2a011f9c07017bd58d8d6e7b8eba19/ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc", size = 10170833 }, + { url = "https://files.pythonhosted.org/packages/a2/45/eae9da55f3357a1ac04220230b8b07800bf516e6dd7e1ad20a2ff3b03b1b/ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692", size = 10896360 }, + { url = "https://files.pythonhosted.org/packages/99/67/4388b36d145675f4c51ebec561fcd4298a0e2550c81e629116f83ce45a39/ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf", size = 10477094 }, + { url = "https://files.pythonhosted.org/packages/e1/9c/f5e6ed1751dc187a4ecf19a4970dd30a521c0ee66b7941c16e292a4043fb/ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb", size = 11480896 }, + { url = "https://files.pythonhosted.org/packages/c8/3b/2b683be597bbd02046678fc3fc1c199c641512b20212073b58f173822bb3/ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e", size = 10179702 }, + { url = "https://files.pythonhosted.org/packages/f1/38/c2d94054dc4b3d1ea4c2ba3439b2a7095f08d1c8184bc41e6abe2a688be7/ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499", size = 9982855 }, + { url = "https://files.pythonhosted.org/packages/7d/e7/1433db2da505ffa8912dcf5b28a8743012ee780cbc20ad0bf114787385d9/ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e", size = 9433156 }, + { url = "https://files.pythonhosted.org/packages/e0/36/4fa43250e67741edeea3d366f59a1dc993d4d89ad493a36cbaa9889895f2/ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5", size = 9782971 }, + { url = "https://files.pythonhosted.org/packages/80/0e/8c276103d518e5cf9202f70630aaa494abf6fc71c04d87c08b6d3cd07a4b/ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e", size = 10247775 }, + { url = "https://files.pythonhosted.org/packages/cb/b9/673096d61276f39291b729dddde23c831a5833d98048349835782688a0ec/ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a", size = 7841772 }, + { url = "https://files.pythonhosted.org/packages/67/1c/4520c98bfc06b9c73cd1457686d4d3935d40046b1ddea08403e5a6deff51/ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3", size = 8699779 }, + { url = "https://files.pythonhosted.org/packages/38/23/b3763a237d2523d40a31fe2d1a301191fe392dd48d3014977d079cf8c0bd/ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4", size = 8091891 }, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620 }, + { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234 }, + { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069 }, + { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809 }, + { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516 }, + { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728 }, + { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700 }, + { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613 }, + { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, + { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, + { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, + { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, + { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, + { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, + { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, + { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, +] + +[[package]] +name = "scipy" +version = "1.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/7b/9f265b7f074195392e893a5cdc66116c2f7a31fd5f3d9cceff661ec6df82/scipy-1.11.3.tar.gz", hash = "sha256:bba4d955f54edd61899776bad459bf7326e14b9fa1c552181f0479cc60a568cd", size = 56335652 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/a6/b6d66d4f4045ba59200d25f254ccd63340162c903f95231e3ae6863fc4ae/scipy-1.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:925c6f09d0053b1c0f90b2d92d03b261e889b20d1c9b08a3a51f61afc5f58165", size = 37156418 }, + { url = "https://files.pythonhosted.org/packages/50/8b/2057417a07a6fee8ed8be40e37bac4a502cae4cf44468a02962bbe81b8af/scipy-1.11.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5664e364f90be8219283eeb844323ff8cd79d7acbd64e15eb9c46b9bc7f6a42a", size = 29674269 }, + { url = "https://files.pythonhosted.org/packages/fd/bd/2905516155dbca3f5ae793caa1954a77369dec42d49ac3a60ea749acd3db/scipy-1.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f325434b6424952fbb636506f0567898dca7b0f7654d48f1c382ea338ce9a3", size = 32881937 }, + { url = "https://files.pythonhosted.org/packages/ef/1b/7538792254aec6850657d5b940fd05fe60582af829ffe40d6c054f065f34/scipy-1.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f290cf561a4b4edfe8d1001ee4be6da60c1c4ea712985b58bf6bc62badee221", size = 36401766 }, + { url = "https://files.pythonhosted.org/packages/36/95/69e32b4691daa6a6fe625d2e3724a39dc9b910c7860e739b583798d3d127/scipy-1.11.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:91770cb3b1e81ae19463b3c235bf1e0e330767dca9eb4cd73ba3ded6c4151e4d", size = 36621150 }, + { url = "https://files.pythonhosted.org/packages/81/d7/d2537d51efb692d0c411e64267ba349e7668d40f5bc73cefe78ccd650dcd/scipy-1.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1f97cd89c0fe1a0685f8f89d85fa305deb3067d0668151571ba50913e445820", size = 44095029 }, + { url = "https://files.pythonhosted.org/packages/e5/ee/c5bc0d4b66a9c38165adf86e8b57be6f76868edf5ea23b3bbee3680e7edf/scipy-1.11.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dfcc1552add7cb7c13fb70efcb2389d0624d571aaf2c80b04117e2755a0c5d15", size = 37051885 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/7e2c614d4c892e7fc9f44f4bf16a4661c7f9112f856c3a14f444e43a6ad4/scipy-1.11.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0d3a136ae1ff0883fffbb1b05b0b2fea251cb1046a5077d0b435a1839b3e52b7", size = 29643878 }, + { url = "https://files.pythonhosted.org/packages/85/61/0b1298353028ae5080674a02a44ea2514decb7c87664007cd1e0c4822522/scipy-1.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bae66a2d7d5768eaa33008fa5a974389f167183c87bf39160d3fefe6664f8ddc", size = 32244516 }, + { url = "https://files.pythonhosted.org/packages/c8/ae/c1e5e9f7ace48e299d7eb05a4b7f1ca0c98961659945a0270a735e5a045a/scipy-1.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2f6dee6cbb0e263b8142ed587bc93e3ed5e777f1f75448d24fb923d9fd4dce6", size = 35743498 }, + { url = "https://files.pythonhosted.org/packages/dc/b2/c58eb0e021696c46719f90ab970d0c034445e36180445c431feed3290871/scipy-1.11.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:74e89dc5e00201e71dd94f5f382ab1c6a9f3ff806c7d24e4e90928bb1aafb280", size = 35936106 }, + { url = "https://files.pythonhosted.org/packages/f4/ce/be0b376ba6069f3f8ba240aa532a374733447453c93582d4c474effdde21/scipy-1.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:90271dbde4be191522b3903fc97334e3956d7cfb9cce3f0718d0ab4fd7d8bfd6", size = 43734377 }, +] + +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, +] + +[[package]] +name = "setuptools" +version = "75.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, +] + +[[package]] +name = "shapely" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/c0/ef2c5eff1e8381710e211a063d0aa3e7215cea9e6fd8c31e75bf5f93df85/shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7", size = 279727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/70/8ac9c70da0e9428b8827953c7345a2f9f0e62adeccd9ca5a69425d693b8c/shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f", size = 2498345 }, + { url = "https://files.pythonhosted.org/packages/81/50/c7768a0a71c012464927228b6b949e55ad8d7ed50325b56b7224ea7dc7a6/shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88", size = 1431079 }, + { url = "https://files.pythonhosted.org/packages/2c/b1/ca09649b4abe06366d41e90c3eee95a7741657404404a63bd0e8b53e32b8/shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f", size = 1271111 }, + { url = "https://files.pythonhosted.org/packages/77/e6/5043c8c8b7e21922559b4faa9011566b0df9315c3d51f15fa07816b4409d/shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89", size = 2444184 }, + { url = "https://files.pythonhosted.org/packages/8c/47/05c8bb8322861113e72b903aebaaa4678ae6e44c886c189ad8fe297f2008/shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2", size = 2527925 }, + { url = "https://files.pythonhosted.org/packages/48/2a/6e590f4f13b1bd8ee39626bab5c1643b8746a4e469c61c5e38571f6cccd9/shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd", size = 1290073 }, + { url = "https://files.pythonhosted.org/packages/9e/39/029c441d8af32ab423b229c4525ce5ce6707318155b59634811a4c56f5c4/shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e", size = 1437690 }, + { url = "https://files.pythonhosted.org/packages/a7/e4/d0fdebc973ccfefe9feb8bca0995e0071c37aabb40448d9f170f94058a0b/shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c", size = 2498555 }, + { url = "https://files.pythonhosted.org/packages/cd/77/d36919327b4c6f5a92909ea194a1a4138bf0515bf0d6a5ca29f53cd0d879/shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2", size = 1430925 }, + { url = "https://files.pythonhosted.org/packages/5c/d9/d557c09d15406aad8252caeae181c2db2c562cc1b2ca6e6482b43274b25f/shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8", size = 1271480 }, + { url = "https://files.pythonhosted.org/packages/bc/e0/237b127f163737b3b4aa275943f4e75e56f50f0aa1c05fe997ddac6bd256/shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b", size = 2441270 }, + { url = "https://files.pythonhosted.org/packages/29/b7/754f971ebbcb747ef38147949f90b30d2232ee5b9af9daacd558cc1705e2/shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4", size = 2526036 }, + { url = "https://files.pythonhosted.org/packages/eb/5e/7aca7c3181f0bedbdb0931c825171b499c8a91b5aff667077efe81e75b4a/shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611", size = 1291039 }, + { url = "https://files.pythonhosted.org/packages/c7/d7/1bf4f48d83af09ce1af06c21752670fa8cdcafa72dbc452b809b215cda2a/shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033", size = 1438340 }, +] + +[[package]] +name = "simplejson" +version = "3.19.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/29/085111f19717f865eceaf0d4397bf3e76b08d60428b076b64e2a1903706d/simplejson-3.19.3.tar.gz", hash = "sha256:8e086896c36210ab6050f2f9f095a5f1e03c83fa0e7f296d6cba425411364680", size = 85237 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/bb/9ee3959e6929d228cf669b3f13f0edd43c5261b6cd69598640748b19ca35/simplejson-3.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e88abff510dcff903a18d11c2a75f9964e768d99c8d147839913886144b2065e", size = 91930 }, + { url = "https://files.pythonhosted.org/packages/ac/ae/a06523928af3a6783e2638cd4f6035c3e32de1c1063d563d9060c8d2f1ad/simplejson-3.19.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934a50a614fb831614db5dbfba35127ee277624dda4d15895c957d2f5d48610c", size = 74787 }, + { url = "https://files.pythonhosted.org/packages/c3/58/fea732e48a7540035fe46d39e6fd77679f5810311d31da8661ce7a18210a/simplejson-3.19.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:212fce86a22188b0c7f53533b0f693ea9605c1a0f02c84c475a30616f55a744d", size = 74612 }, + { url = "https://files.pythonhosted.org/packages/ab/4d/15718f20cb0e3875b8af9597d6bb3bfbcf1383834b82b6385ee9ac0b72a9/simplejson-3.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d9e8f836688a8fabe6a6b41b334aa550a6823f7b4ac3d3712fc0ad8655be9a8", size = 143550 }, + { url = "https://files.pythonhosted.org/packages/93/44/815a4343774760f7a82459c8f6a4d8268b4b6d23f81e7b922a5e2ca79171/simplejson-3.19.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23228037dc5d41c36666384062904d74409a62f52283d9858fa12f4c22cffad1", size = 153284 }, + { url = "https://files.pythonhosted.org/packages/9d/52/d3202d9bba95444090d1c98e43da3c10907875babf63ed3c134d1b9437e3/simplejson-3.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0791f64fed7d4abad639491f8a6b1ba56d3c604eb94b50f8697359b92d983f36", size = 141518 }, + { url = "https://files.pythonhosted.org/packages/b7/d4/850948bcbcfe0b4a6c69dfde10e245d3a1ea45252f16a1e2308a3b06b1da/simplejson-3.19.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f614581b61a26fbbba232a1391f6cee82bc26f2abbb6a0b44a9bba25c56a1c", size = 144688 }, + { url = "https://files.pythonhosted.org/packages/58/d2/b8dcb0a07d9cd54c47f9fe8733dbb83891d1efe4fc786d9dfc8781cc04f9/simplejson-3.19.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1df0aaf1cb787fdf34484ed4a1f0c545efd8811f6028623290fef1a53694e597", size = 144534 }, + { url = "https://files.pythonhosted.org/packages/a9/95/1e92d99039041f596e0923ec4f9153244acaf3830944dc69a7c11b23ceaa/simplejson-3.19.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:951095be8d4451a7182403354c22ec2de3e513e0cc40408b689af08d02611588", size = 146565 }, + { url = "https://files.pythonhosted.org/packages/21/04/c96aeb3a74031255e4cbcc0ca1b6ebfb5549902f0a065f06d65ce8447c0c/simplejson-3.19.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a954b30810988feeabde843e3263bf187697e0eb5037396276db3612434049b", size = 155014 }, + { url = "https://files.pythonhosted.org/packages/b7/41/e28a28593afc4a75d8999d057bfb7c73a103e35f927e66f4bb92571787ae/simplejson-3.19.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c40df31a75de98db2cdfead6074d4449cd009e79f54c1ebe5e5f1f153c68ad20", size = 148092 }, + { url = "https://files.pythonhosted.org/packages/2b/82/1c81a3af06f937afb6d2e9d74a465c0e0ae6db444d1bf2a436ea26de1965/simplejson-3.19.3-cp311-cp311-win32.whl", hash = "sha256:7e2a098c21ad8924076a12b6c178965d88a0ad75d1de67e1afa0a66878f277a5", size = 73942 }, + { url = "https://files.pythonhosted.org/packages/65/be/d8ab9717f471be3c114f16abd8be21d9a6a0a09b9b49177d93d64d3717d9/simplejson-3.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:c9bedebdc5fdad48af8783022bae307746d54006b783007d1d3c38e10872a2c6", size = 75469 }, + { url = "https://files.pythonhosted.org/packages/20/15/513fea93fafbdd4993eacfcb762965b2ff3d29e618c029e2956174d68c4b/simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99", size = 92921 }, + { url = "https://files.pythonhosted.org/packages/a4/4f/998a907ae1a6c104dc0ee48aa248c2478490152808d34d8e07af57f396c3/simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333", size = 75311 }, + { url = "https://files.pythonhosted.org/packages/db/44/acd6122201e927451869d45952b9ab1d3025cdb5e61548d286d08fbccc08/simplejson-3.19.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42e5acf80d4d971238d4df97811286a044d720693092b20a56d5e56b7dcc5d09", size = 74964 }, + { url = "https://files.pythonhosted.org/packages/27/ca/d0a1e8f16e1bbdc0b8c6d88166f45f565ed7285f53928cfef3b6ce78f14d/simplejson-3.19.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b0efc7279d768db7c74d3d07f0b5c81280d16ae3fb14e9081dc903e8360771", size = 150106 }, + { url = "https://files.pythonhosted.org/packages/63/59/0554b78cf26c98e2b9cae3f44723bd72c2394e2afec1a14eedc6211f7187/simplejson-3.19.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0552eb06e7234da892e1d02365cd2b7b2b1f8233aa5aabdb2981587b7cc92ea0", size = 158347 }, + { url = "https://files.pythonhosted.org/packages/b2/fe/9f30890352e431e8508cc569912d3322147d3e7e4f321e48c0adfcb4c97d/simplejson-3.19.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf6a3b9a7d7191471b464fe38f684df10eb491ec9ea454003edb45a011ab187", size = 148456 }, + { url = "https://files.pythonhosted.org/packages/37/e3/663a09542ee021d4131162f7a164cb2e7f04ef48433a67591738afbf12ea/simplejson-3.19.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7017329ca8d4dca94ad5e59f496e5fc77630aecfc39df381ffc1d37fb6b25832", size = 152190 }, + { url = "https://files.pythonhosted.org/packages/31/20/4e0c4d35e10ff6465003bec304316d822a559a1c38c66ef6892ca199c207/simplejson-3.19.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67a20641afebf4cfbcff50061f07daad1eace6e7b31d7622b6fa2c40d43900ba", size = 149846 }, + { url = "https://files.pythonhosted.org/packages/08/7a/46e2e072cac3987cbb05946f25167f0ad2fe536748e7405953fd6661a486/simplejson-3.19.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd6a7dabcc4c32daf601bc45e01b79175dde4b52548becea4f9545b0a4428169", size = 151714 }, + { url = "https://files.pythonhosted.org/packages/7f/7d/dbeeac10eb61d5d8858d0bb51121a21050d281dc83af4c557f86da28746c/simplejson-3.19.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08f9b443a94e72dd02c87098c96886d35790e79e46b24e67accafbf13b73d43b", size = 158777 }, + { url = "https://files.pythonhosted.org/packages/fc/8f/a98bdbb799c6a4a884b5823db31785a96ba895b4b0f4d8ac345d6fe98bbf/simplejson-3.19.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa97278ae6614346b5ca41a45a911f37a3261b57dbe4a00602048652c862c28b", size = 154230 }, + { url = "https://files.pythonhosted.org/packages/b1/db/852eebceb85f969ae40e06babed1a93d3bacb536f187d7a80ff5823a5979/simplejson-3.19.3-cp312-cp312-win32.whl", hash = "sha256:ef28c3b328d29b5e2756903aed888960bc5df39b4c2eab157ae212f70ed5bf74", size = 74002 }, + { url = "https://files.pythonhosted.org/packages/fe/68/9f0e5df0651cb79ef83cba1378765a00ee8038e6201cc82b8e7178a7778e/simplejson-3.19.3-cp312-cp312-win_amd64.whl", hash = "sha256:1e662336db50ad665777e6548b5076329a94a0c3d4a0472971c588b3ef27de3a", size = 75596 }, + { url = "https://files.pythonhosted.org/packages/93/3a/5896821ed543899fcb9c4256c7e71bb110048047349a00f42bc8b8fb379f/simplejson-3.19.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0959e6cb62e3994b5a40e31047ff97ef5c4138875fae31659bead691bed55896", size = 92931 }, + { url = "https://files.pythonhosted.org/packages/39/15/5d33d269440912ee40d856db0c8be2b91aba7a219690ab01f86cb0edd590/simplejson-3.19.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a7bfad839c624e139a4863007233a3f194e7c51551081f9789cba52e4da5167", size = 75318 }, + { url = "https://files.pythonhosted.org/packages/2a/8d/2e7483a2bf7ec53acf7e012bafbda79d7b34f90471dda8e424544a59d484/simplejson-3.19.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afab2f7f2486a866ff04d6d905e9386ca6a231379181a3838abce1f32fbdcc37", size = 74971 }, + { url = "https://files.pythonhosted.org/packages/4d/9d/9bdf34437c8834a7cf7246f85e9d5122e30579f512c10a0c2560e994294f/simplejson-3.19.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00313681015ac498e1736b304446ee6d1c72c5b287cd196996dad84369998f7", size = 150112 }, + { url = "https://files.pythonhosted.org/packages/a7/e2/1f2ae2d89eaf85f6163c82150180aae5eaa18085cfaf892f8a57d4c51cbd/simplejson-3.19.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d936ae682d5b878af9d9eb4d8bb1fdd5e41275c8eb59ceddb0aeed857bb264a2", size = 158354 }, + { url = "https://files.pythonhosted.org/packages/60/83/26f610adf234c8492b3f30501e12f2271e67790f946c6898fe0c58aefe99/simplejson-3.19.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c6657485393f2e9b8177c77a7634f13ebe70d5e6de150aae1677d91516ce6b", size = 148455 }, + { url = "https://files.pythonhosted.org/packages/b5/4b/109af50006af77133653c55b5b91b4bd2d579ff8254ce11216c0b75f911b/simplejson-3.19.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a6a750d3c7461b1c47cfc6bba8d9e57a455e7c5f80057d2a82f738040dd1129", size = 152191 }, + { url = "https://files.pythonhosted.org/packages/75/dc/108872a8825cbd99ae6f4334e0490ff1580367baf12198bcaf988f6820ba/simplejson-3.19.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea7a4a998c87c5674a27089e022110a1a08a7753f21af3baf09efe9915c23c3c", size = 149954 }, + { url = "https://files.pythonhosted.org/packages/eb/be/deec1d947a5d0472276ab4a4d1a9378dc5ee27f3dc9e54d4f62ffbad7a08/simplejson-3.19.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6300680d83a399be2b8f3b0ef7ef90b35d2a29fe6e9c21438097e0938bbc1564", size = 151812 }, + { url = "https://files.pythonhosted.org/packages/e9/58/4ee130702d36b1551ef66e7587eefe56651f3669255bf748cd71691e2434/simplejson-3.19.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ab69f811a660c362651ae395eba8ce84f84c944cea0df5718ea0ba9d1e4e7252", size = 158880 }, + { url = "https://files.pythonhosted.org/packages/0f/e1/59cc6a371b60f89e3498d9f4c8109f6b7359094d453f5fe80b2677b777b0/simplejson-3.19.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:256e09d0f94d9c3d177d9e95fd27a68c875a4baa2046633df387b86b652f5747", size = 154344 }, + { url = "https://files.pythonhosted.org/packages/79/45/1b36044670016f5cb25ebd92497427d2d1711ecb454d00f71eb9a00b77cc/simplejson-3.19.3-cp313-cp313-win32.whl", hash = "sha256:2c78293470313aefa9cfc5e3f75ca0635721fb016fb1121c1c5b0cb8cc74712a", size = 74002 }, + { url = "https://files.pythonhosted.org/packages/e2/58/b06226e6b0612f2b1fa13d5273551da259f894566b1eef32249ddfdcce44/simplejson-3.19.3-cp313-cp313-win_amd64.whl", hash = "sha256:3bbcdc438dc1683b35f7a8dc100960c721f922f9ede8127f63bed7dfded4c64c", size = 75599 }, + { url = "https://files.pythonhosted.org/packages/0d/e7/f9fafbd4f39793a20cc52e77bbd766f7384312526d402c382928dc7667f6/simplejson-3.19.3-py3-none-any.whl", hash = "sha256:49cc4c7b940d43bd12bf87ec63f28cbc4964fc4e12c031cc8cd01650f43eb94e", size = 57004 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smart-open" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/30/1f41c3d3b8cec82024b4b277bfd4e5b18b765ae7279eb9871fa25c503778/smart_open-7.1.0.tar.gz", hash = "sha256:a4f09f84f0f6d3637c6543aca7b5487438877a21360e7368ccf1f704789752ba", size = 72044 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/18/9a8d9f01957aa1f8bbc5676d54c2e33102d247e146c1a3679d3bd5cc2e3a/smart_open-7.1.0-py3-none-any.whl", hash = "sha256:4b8489bb6058196258bafe901730c7db0dcf4f083f316e97269c66f45502055b", size = 61746 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "snakemake" +version = "7.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appdirs" }, + { name = "configargparse" }, + { name = "connection-pool" }, + { name = "datrie" }, + { name = "docutils" }, + { name = "gitpython" }, + { name = "humanfriendly" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pulp" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "reretry" }, + { name = "smart-open" }, + { name = "stopit" }, + { name = "tabulate" }, + { name = "throttler" }, + { name = "toposort" }, + { name = "wrapt" }, + { name = "yte" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/94/884160dab89886cef7802df0a8c8217bfb2d795427dee01ad0e0dc15964a/snakemake-7.32.4.tar.gz", hash = "sha256:fdc3f15dd7b06fabb7da30d460e0a3b1fba08e4ea91f9c32c47a83705cdc7b6e", size = 371171 } + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "snuggs" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/19/0d11ab370735dde61076a0e41644e5593821776e69e3b0344626cfa0e56a/snuggs-1.4.7.tar.gz", hash = "sha256:501cf113fe3892e14e2fee76da5cd0606b7e149c411c271898e6259ebde2617b", size = 8196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/0e/d27d6e806d6c0d1a2cfdc5d1f088e42339a0a54a09c3343f7f81ec8947ea/snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07", size = 5370 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, +] + +[[package]] +name = "sphinx-autobuild" +version = "2024.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "sphinx" }, + { name = "starlette" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 }, +] + +[[package]] +name = "sphinx-book-theme" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/69/61dfa3b3851610b5f45960737bd99f8c5b2d70ba73f9ac84a527e0c564ae/sphinx_book_theme-1.1.3.tar.gz", hash = "sha256:1f25483b1846cb3d353a6bc61b3b45b031f4acf845665d7da90e01ae0aef5b4d", size = 434230 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/80/90574e2e82c955b9c6f6b77f7badb2cf2ef4ef77599e4343cced2d098681/sphinx_book_theme-1.1.3-py3-none-any.whl", hash = "sha256:a554a9a7ac3881979a87a2b10f633aa2a5706e72218a10f71be38b3c9e831ae9", size = 430129 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "starlette" +version = "0.45.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, +] + +[[package]] +name = "stopit" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/58/e8bb0b0fb05baf07bbac1450c447d753da65f9701f551dca79823ce15d50/stopit-1.1.2.tar.gz", hash = "sha256:f7f39c583fd92027bd9d06127b259aee7a5b7945c1f1fa56263811e1e766996d", size = 18281 } + +[[package]] +name = "tables" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blosc2" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "py-cpuinfo" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/50/23ead25f60bb1babe7f2f061d8a2f8c2f6804c1a20b3058677beb9085b56/tables-3.10.2.tar.gz", hash = "sha256:2544812a7186fadba831d6dd34eb49ccd788d6a83f4e4c2b431b835b6796c910", size = 4779722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/f6/ef0c376c1fa01b916d5db0c2681be063f6289ee99faf7bb6610e0b55b773/tables-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63f8adec3c4421a011c5c6a245c0c1fccf16dba7aaa67d9915d2821cf365ed4a", size = 6767194 }, + { url = "https://files.pythonhosted.org/packages/d9/d0/accd41382fa9da45bf816c56f85bda64223a3b8d0006d3496b67e0781a6e/tables-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c120bff666d33d3bdfb9e33173a4869d5f34e6c87824f2c7ec6a72c8dfab82", size = 5482665 }, + { url = "https://files.pythonhosted.org/packages/59/2f/c95e94423c463177b8a7d55a1dbbd524840fe6a684844ff728f238e71f68/tables-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e71f63ac67c583ac42943c99c2d33bcc9e361e94d1ab1a763dc0698bdd9ff815", size = 7117696 }, + { url = "https://files.pythonhosted.org/packages/88/d5/71665919aa2a5a3d2a20eeef3c71dc7c2ebbd9f26d114a7808514aba24d6/tables-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154773f97763ccc91a29bcead6ab7b5ef164c2ed8c409cd79a2115aa9b4184c9", size = 7520921 }, + { url = "https://files.pythonhosted.org/packages/46/96/b5023c1f7b9d560cac3e2c0daceebaeb88dd24c70c75db2d291abfa563e5/tables-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:96b5e945d275415e79ddb0578657ecc6ac77030dcc0632ab2c39f89390bb239d", size = 6407137 }, + { url = "https://files.pythonhosted.org/packages/ab/c4/1efbcc699db863d88874f3d111e5bb6dd2e0fbaca38f91c992e696324730/tables-3.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6ba58205d1f6a4e0e2212bc221e76cf104f22190f90c3f1683f3c1ab138f28f", size = 6734990 }, + { url = "https://files.pythonhosted.org/packages/4a/db/4c7facfc805ab764f2ee256011d20f96791d2426afa3389ca7ff2a8a4ea8/tables-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdb5c040aa43e5e96259d6f6bb9df5b66fef2b071a6eb035c21bf6508e865d40", size = 5483377 }, + { url = "https://files.pythonhosted.org/packages/93/0a/53815b516a2465b329e5dc2079c99a8b6b1a23f6b9ce5da8a7ebc7892bf4/tables-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e694123fa886d9be57f55fc7e1dcacac49f0b4ed4a931c795bd8f82f7111b5a8", size = 7081356 }, + { url = "https://files.pythonhosted.org/packages/d3/e1/3f4adfc83eb7390abb964682a7d1df0dbe451dd2cee99750b1c7ca8e2c9d/tables-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c12d0d04de89297763923ebeaddfd7e0b51f29041895db284fd4913e7448b7", size = 7483570 }, + { url = "https://files.pythonhosted.org/packages/9a/d4/0b9ba57a5a8d2d05d1108055a8d70a4b066db4ebed61921de34043a31bdb/tables-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:a406d5dbbcb6604bd1ca129af337e0790d4e02d29d06159ddb9f74e38d756d32", size = 6388443 }, + { url = "https://files.pythonhosted.org/packages/ab/02/8c7aeaa6c8aac8e0298d40dc5fc55477fddc30cb31e4dc7e5e473be4b464/tables-3.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7b8bc07c715bad3d447ed8f834388ef2e10265e2c4af6b1297fc61adb645948f", size = 6725764 }, + { url = "https://files.pythonhosted.org/packages/91/f4/8683395d294b9e4576fd7d888aa6cf5583c013c2c0a2e47f862c2842407f/tables-3.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28677ed8e1a371471495599078f48da0850f82457d6c852ca77959c974371140", size = 5442663 }, + { url = "https://files.pythonhosted.org/packages/72/9b/ea43159eed8f81bfa1ead8fa8201a3c352e84c7220e046bb548736833951/tables-3.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaaea478dcf27dd54679ef2643c26d3b8b15676ad81e4d80a88fd1682d23deb1", size = 7078747 }, + { url = "https://files.pythonhosted.org/packages/04/95/b3e88edc674e35d9011b168df0d7a9b1c3ab98733fa26e740ac7964edc2f/tables-3.10.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e67a9f901842f9a4b1f3d2307f4bdd94047514fe0d0c558ed19c11f53c402a", size = 7479985 }, + { url = "https://files.pythonhosted.org/packages/63/ca/eaa029a43d269bdda6985931d6cfd479e876cd8cf7c887d818bef05ef03b/tables-3.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:5637fdcded5ba5426aa24e0e42d6f990926a4da7f193830df131dfcb7e842900", size = 6385562 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, +] + +[[package]] +name = "throttler" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/22/638451122136d5280bc477c8075ea448b9ebdfbd319f0f120edaecea2038/throttler-1.2.2.tar.gz", hash = "sha256:d54db406d98e1b54d18a9ba2b31ab9f093ac64a0a59d730c1cf7bb1cdfc94a58", size = 7970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/d4/36bf6010b184286000b2334622bfb3446a40c22c1d2a9776bff025cb0fe5/throttler-1.2.2-py3-none-any.whl", hash = "sha256:fc6ae612a2529e01110b32335af40375258b98e3b81232ec77cd07f51bf71392", size = 7609 }, +] + +[[package]] +name = "toolz" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383 }, +] + +[[package]] +name = "toposort" +version = "1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/19/8e955d90985ecbd3b9adb2a759753a6840da2dff3c569d412b2c9217678b/toposort-1.10.tar.gz", hash = "sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd", size = 11132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/17/57b444fd314d5e1593350b9a31d000e7411ba8e17ce12dc7ad54ca76b810/toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87", size = 8500 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "tsam" +version = "2.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "highspy" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyomo" }, + { name = "scikit-learn" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/25/2c7c1cc58c1c09ebd2b5b55878e5e1ae4dcf43fad03f6f926b70f3f9fd17/tsam-2.3.6.tar.gz", hash = "sha256:f2f75cbcd5d5d63d9d77ec5edb61e7efeb3170ebef4591272ef13a7bd84cc9d4", size = 223586 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/e9/d1ddff8471ba389caf862867f60718b2843010ec8ba8693c642569012b3d/tsam-2.3.6-py3-none-any.whl", hash = "sha256:2d4ff20e1fcc5e061fbbcf772b51dc3d44dd68198f5a40eca7e0422c70cffe95", size = 36617 }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20241230" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/f9/4d566925bcf9396136c0a2e5dc7e230ff08d86fa011a69888dd184469d80/types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c", size = 17078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/c1/48474fbead512b70ccdb4f81ba5eb4a58f69d100ba19f17c92c0c4f50ae6/types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6", size = 20029 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "urwid" +version = "2.6.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/21/ad23c9e961b2d36d57c63686a6f86768dd945d406323fb58c84f09478530/urwid-2.6.16.tar.gz", hash = "sha256:93ad239939e44c385e64aa00027878b9e5c486d59e855ec8ab5b1e1adcdb32a2", size = 848179 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/cb/271a4f5a1bf4208dbdc96d85b9eae744cf4e5e11ac73eda76dc98c8fd2d7/urwid-2.6.16-py3-none-any.whl", hash = "sha256:de14896c6df9eb759ed1fd93e0384a5279e51e0dde8f621e4083f7a8368c0797", size = 297196 }, +] + +[[package]] +name = "urwid-readline" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urwid" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/70/be318554495555eba7d8ff6e489f6f74ddb225b24086ba4af62a82e723fd/urwid_readline-0.15.1.tar.gz", hash = "sha256:9301444b86d58f7d26388506b704f142cefd193888488b4070d3a0fdfcfc0f84", size = 9007 } + +[[package]] +name = "uvicorn" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, +] + +[[package]] +name = "validators" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/07/91582d69320f6f6daaf2d8072608a4ad8884683d4840e7e4f3a9dbdcc639/validators-0.34.0.tar.gz", hash = "sha256:647fe407b45af9a74d245b943b18e6a816acf4926974278f6dd617778e1e781f", size = 70955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/78/36828a4d857b25896f9774c875714ba4e9b3bc8a92d2debe3f4df3a83d4f/validators-0.34.0-py3-none-any.whl", hash = "sha256:c804b476e3e6d3786fa07a30073a4ef694e617805eb1946ceee3fe5a9b8b1321", size = 43536 }, +] + +[[package]] +name = "virtualenv" +version = "20.29.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 }, +] + +[[package]] +name = "watchfiles" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/bb/8461adc4b1fed009546fb797fc0d5698dcfe5e289cb37e1b8f16a93cdc30/watchfiles-1.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19", size = 394869 }, + { url = "https://files.pythonhosted.org/packages/55/88/9ebf36b3547176d1709c320de78c1fa3263a46be31b5b1267571d9102686/watchfiles-1.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235", size = 384905 }, + { url = "https://files.pythonhosted.org/packages/03/8a/04335ce23ef78d8c69f0913e8b20cf7d9233e3986543aeef95ef2d6e43d2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202", size = 449944 }, + { url = "https://files.pythonhosted.org/packages/17/4e/c8d5dcd14fe637f4633616dabea8a4af0a10142dccf3b43e0f081ba81ab4/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6", size = 456020 }, + { url = "https://files.pythonhosted.org/packages/5e/74/3e91e09e1861dd7fbb1190ce7bd786700dc0fbc2ccd33bb9fff5de039229/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317", size = 482983 }, + { url = "https://files.pythonhosted.org/packages/a1/3d/e64de2d1ce4eb6a574fd78ce3a28c279da263be9ef3cfcab6f708df192f2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee", size = 520320 }, + { url = "https://files.pythonhosted.org/packages/2c/bd/52235f7063b57240c66a991696ed27e2a18bd6fcec8a1ea5a040b70d0611/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49", size = 500988 }, + { url = "https://files.pythonhosted.org/packages/3a/b0/ff04194141a5fe650c150400dd9e42667916bc0f52426e2e174d779b8a74/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c", size = 452573 }, + { url = "https://files.pythonhosted.org/packages/3d/9d/966164332c5a178444ae6d165082d4f351bd56afd9c3ec828eecbf190e6a/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1", size = 615114 }, + { url = "https://files.pythonhosted.org/packages/94/df/f569ae4c1877f96ad4086c153a8eee5a19a3b519487bf5c9454a3438c341/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226", size = 613076 }, + { url = "https://files.pythonhosted.org/packages/15/ae/8ce5f29e65d5fa5790e3c80c289819c55e12be2e1b9f5b6a0e55e169b97d/watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105", size = 271013 }, + { url = "https://files.pythonhosted.org/packages/a4/c6/79dc4a7c598a978e5fafa135090aaf7bbb03b8dec7bada437dfbe578e7ed/watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74", size = 284229 }, + { url = "https://files.pythonhosted.org/packages/37/3d/928633723211753f3500bfb138434f080363b87a1b08ca188b1ce54d1e05/watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3", size = 276824 }, + { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 }, + { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 }, + { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 }, + { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 }, + { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 }, + { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 }, + { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 }, + { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 }, + { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 }, + { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 }, + { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 }, + { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 }, + { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 }, + { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 }, + { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 }, + { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 }, + { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 }, + { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 }, + { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 }, + { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 }, + { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 }, + { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 }, + { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 }, + { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "websockets" +version = "14.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b6/504695fb9a33df0ca56d157f5985660b5fc5b4bf8c78f121578d2d653392/websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166", size = 163088 }, + { url = "https://files.pythonhosted.org/packages/81/26/ebfb8f6abe963c795122439c6433c4ae1e061aaedfc7eff32d09394afbae/websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f", size = 160745 }, + { url = "https://files.pythonhosted.org/packages/a1/c6/1435ad6f6dcbff80bb95e8986704c3174da8866ddb751184046f5c139ef6/websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910", size = 160995 }, + { url = "https://files.pythonhosted.org/packages/96/63/900c27cfe8be1a1f2433fc77cd46771cf26ba57e6bdc7cf9e63644a61863/websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c", size = 170543 }, + { url = "https://files.pythonhosted.org/packages/00/8b/bec2bdba92af0762d42d4410593c1d7d28e9bfd952c97a3729df603dc6ea/websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473", size = 169546 }, + { url = "https://files.pythonhosted.org/packages/6b/a9/37531cb5b994f12a57dec3da2200ef7aadffef82d888a4c29a0d781568e4/websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473", size = 169911 }, + { url = "https://files.pythonhosted.org/packages/60/d5/a6eadba2ed9f7e65d677fec539ab14a9b83de2b484ab5fe15d3d6d208c28/websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56", size = 170183 }, + { url = "https://files.pythonhosted.org/packages/76/57/a338ccb00d1df881c1d1ee1f2a20c9c1b5b29b51e9e0191ee515d254fea6/websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142", size = 169623 }, + { url = "https://files.pythonhosted.org/packages/64/22/e5f7c33db0cb2c1d03b79fd60d189a1da044e2661f5fd01d629451e1db89/websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d", size = 169583 }, + { url = "https://files.pythonhosted.org/packages/aa/2e/2b4662237060063a22e5fc40d46300a07142afe30302b634b4eebd717c07/websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a", size = 163969 }, + { url = "https://files.pythonhosted.org/packages/94/a5/0cda64e1851e73fc1ecdae6f42487babb06e55cb2f0dc8904b81d8ef6857/websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b", size = 164408 }, + { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 }, + { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 }, + { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 }, + { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 }, + { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 }, + { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 }, + { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 }, + { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 }, + { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 }, + { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 }, + { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 }, + { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 }, + { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 }, + { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 }, + { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 }, + { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 }, + { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 }, + { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 }, + { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 }, + { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 }, + { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 }, + { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 }, + { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "xarray" +version = "2024.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/d3/ae7a92c8448c40cd43f97fff93b1a57f87565b412fdc02eb14af5d4c3823/xarray-2024.9.0.tar.gz", hash = "sha256:e796a6b3eaec11da24f33e4bb14af41897011660a0516fa4037d3ae4bbd1d378", size = 3747432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/28/3a6365e45721c7c9078968ed94b4a60076bc31d73b8519021a69b4995b63/xarray-2024.9.0-py3-none-any.whl", hash = "sha256:4fd534abdf12d5fa75dd566c56483d5081f77864462cf3d6ad53e13f9db48222", size = 1191607 }, +] + +[[package]] +name = "xlrd" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/b3/19a2540d21dea5f908304375bd43f5ed7a4c28a370dc9122c565423e6b44/xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88", size = 100259 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/0c/c2a72d51fe56e08a08acc85d13013558a2d793028ae7385448a6ccdfae64/xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", size = 96531 }, +] + +[[package]] +name = "yte" +version = "1.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dpath" }, + { name = "plac" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/6173085d15e5cf55884b2706ab3f35d38a0680fc45c5b9e33b40c4a71bdb/yte-1.5.7.tar.gz", hash = "sha256:1e22a74e7c4d1aa70c54fe79d23938cb249d08c0804ad764ab97d5c587cbbad2", size = 6388 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/ba/f9d2417e72a8e50e2c749edf8b96fbe3c423be17b39cc2648154b3184fd3/yte-1.5.7-py3-none-any.whl", hash = "sha256:a66118fbe236bcf293fc920473ad19eb20f801172f0cd43764d3c283ceee6452", size = 7830 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] From f0270d754aec0385ed017b732524dd107c4bc9db Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:19:57 -0800 Subject: [PATCH 43/75] update uv env --- docs/source/about-install.md | 19 +++- docs/source/about-usage.md | 9 +- pyproject.toml | 3 + uv.lock | 97 ++++++++++++++++++- .../scripts/additional_policy_constraints.py | 1 - 5 files changed, 122 insertions(+), 7 deletions(-) delete mode 100644 workflow/scripts/additional_policy_constraints.py diff --git a/docs/source/about-install.md b/docs/source/about-install.md index fd3bc834e..d7fad8ae0 100644 --- a/docs/source/about-install.md +++ b/docs/source/about-install.md @@ -27,7 +27,21 @@ templates into the `workflow/config` folder. bash init_pypsa_usa.sh ``` -## Step 3: Create Mamba Environment +## Step 3: Set-up Environment (mamba or UV) + +### `uv` installation + +UV is a new python package managment tool from the creators of mamba. It replaces mamba, conda, and pip commands for one package and virtual environment managment tool. Instructions for installing [uv](https://docs.astral.sh/uv/getting-started/installation/). + +with UV installed, you can create and sync a new environment with: + +```console +uv venv +source .venv/bin/activate +uv pip sync pyproject.toml +``` + +### `mamba` Installation PyPSA-USA uses conda/mamba to manage project dependencies. You can download and install mamba following the [instructions](https://mamba.readthedocs.io/en/latest/mamba-installation.html). Follow links for mambaforge installation. There are two ways to install mamba, the first (recommended) method will start with a fresh install, meaning if you have previously installed conda environments, you will need to recreate these conda envs. If you already have conda installed and do not wish to install mamba, you can follow the same set of instructions replacing any `mamba` with `conda` @@ -40,6 +54,9 @@ mamba activate pypsa-usa You also have the option to use miniconda. Download [Miniconda](https://docs.conda.io/en/latest/miniconda.html) following their [instructions](https://docs.conda.io/en/latest/miniconda.html). + + + ```{seealso} If you are planning to develop PyPSA-USA, please see our [contribution guidelines](./contributing.md#code-contributions) for installing additional dependencies ``` diff --git a/docs/source/about-usage.md b/docs/source/about-usage.md index 74512aa06..782d3d74e 100644 --- a/docs/source/about-usage.md +++ b/docs/source/about-usage.md @@ -16,11 +16,16 @@ You can find more information on each configuration setting on the [configuratio To run the workflow, `cd` into the `workflow` directory and run the `snakemake` from your terminal with your selection of config file: +UV: ```console -snakemake -j1 --configfile config/config.default.yaml +uv run snakemake -j1 --configfile config/config.default.yaml --scheduler-ilp-solver GUROBI_CMD ``` -where 1 indicates the number of cores used. +mamba: +```console +mamba activate pypsa-usa +snakemake -j1 --configfile config/config.default.yaml +``` ## Running on HPC Cluster diff --git a/pyproject.toml b/pyproject.toml index b88fd5f2d..7ec78fdf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,11 @@ readme = "README.md" dependencies = [ "atlite==0.3.0", "cartopy==0.23.0", + "dask==2024.12.0", + "dask-expr==1.1.20", "descartes==1.1.0", "dill>=0.3.9", + "distributed==2024.12.0", "duckdb==0.10.0", "geopandas==1.0.1", "geopy==2.4.0", diff --git a/uv.lock b/uv.lock index 5a330fde0..7767ee4a5 100644 --- a/uv.lock +++ b/uv.lock @@ -486,7 +486,7 @@ wheels = [ [[package]] name = "dask" -version = "2025.1.0" +version = "2024.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -498,9 +498,23 @@ dependencies = [ { name = "pyyaml" }, { name = "toolz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/41/43eb54e0f6d1ba971d5adcad8f0862b327af6a2041aa134acbcec630ad43/dask-2025.1.0.tar.gz", hash = "sha256:bb807586ff20f0f59f3d36fe34eb4a95f75a1aae2a775b521de6dd53727d2063", size = 10758681 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/0a/1011ce75bc1e2da627d481f4bfcaf0eaf3367eb2e432ed908620e0a0fcfa/dask-2024.12.0.tar.gz", hash = "sha256:ffd02b06ac06b993df0b48e0ba4fe02abceb5c8b34b40bd91d63f33ec7a272a4", size = 10691490 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a0/016d956a3fec193e3a5b466ca912944669c18dccc736b64a9e28ccdcc5f7/dask-2025.1.0-py3-none-any.whl", hash = "sha256:db86220c8d19bdf464cbe11a87a2c8f5d537acf586bb02eed6d61a302af5c2fd", size = 1371235 }, + { url = "https://files.pythonhosted.org/packages/3a/46/9ea782eb1accabad0931d80818fcf8b286bdd1b457ae9c718f039430c489/dask-2024.12.0-py3-none-any.whl", hash = "sha256:e038e87b9f06e7927b81ecde6cf2b49aa699bb902fec11abba5697cb48baeb8d", size = 1268483 }, +] + +[[package]] +name = "dask-expr" +version = "1.1.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dask" }, + { name = "pandas" }, + { name = "pyarrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/ef/9b7b7ccd25cef726f0b38a4d440092596e5fa6f593b82c676278c925327b/dask_expr-1.1.20.tar.gz", hash = "sha256:c5be6243296c299e21e02aa93e863e28e6f21ab3c01b645cec3dd56e1b1eac9b", size = 224530 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/a4/c7c8e2a895eba9ce41bf811b2b6755c227a489b687d1a52521350ae37436/dask_expr-1.1.20-py3-none-any.whl", hash = "sha256:a10755f2bc7d7cfb060b4fc9c7e8b139a03d961c4420ebd50ea319ea906f8b80", size = 245309 }, ] [[package]] @@ -575,6 +589,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] +[[package]] +name = "distributed" +version = "2024.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "dask" }, + { name = "jinja2" }, + { name = "locket" }, + { name = "msgpack" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "sortedcontainers" }, + { name = "tblib" }, + { name = "toolz" }, + { name = "tornado" }, + { name = "urllib3" }, + { name = "zict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/6e/463536dba2893634537f62ef00abf9421bf55f867892fc7c47d865c27d85/distributed-2024.12.0.tar.gz", hash = "sha256:6a2c04e63d31973ee3c1f2160d66521ed8f08e637d6a25a450e7561582920f38", size = 1115740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/ef/f0a96745cabeda5735d456403a007c2f9b6ac64a0f4c5fa4faf9d41f70dc/distributed-2024.12.0-py3-none-any.whl", hash = "sha256:ed05aa13b6c62b69b33d1ba7d1ca95e78406c8f37163fafd07f7ca94ae036b66", size = 1022908 }, +] + [[package]] name = "docutils" version = "0.21.2" @@ -1953,8 +1993,11 @@ source = { editable = "." } dependencies = [ { name = "atlite" }, { name = "cartopy" }, + { name = "dask" }, + { name = "dask-expr" }, { name = "descartes" }, { name = "dill" }, + { name = "distributed" }, { name = "duckdb" }, { name = "geopandas" }, { name = "geopy" }, @@ -2007,8 +2050,11 @@ requires-dist = [ { name = "atlite", specifier = "==0.3.0" }, { name = "bump2version", marker = "extra == 'dev'" }, { name = "cartopy", specifier = "==0.23.0" }, + { name = "dask", specifier = "==2024.12.0" }, + { name = "dask-expr", specifier = "==1.1.20" }, { name = "descartes", specifier = "==1.1.0" }, { name = "dill", specifier = ">=0.3.9" }, + { name = "distributed", specifier = "==2024.12.0" }, { name = "duckdb", specifier = "==0.10.0" }, { name = "geopandas", specifier = "==1.0.1" }, { name = "geopy", specifier = "==2.4.0" }, @@ -2543,6 +2589,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/0e/d27d6e806d6c0d1a2cfdc5d1f088e42339a0a54a09c3343f7f81ec8947ea/snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07", size = 5370 }, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, +] + [[package]] name = "soupsieve" version = "2.6" @@ -2735,6 +2790,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] +[[package]] +name = "tblib" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/df/4f2cd7eaa6d41a7994d46527349569d46e34d9cdd07590b5c5b0dcf53de3/tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6", size = 30616 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/87/ce70db7cae60e67851eb94e1a2127d4abb573d3866d2efd302ceb0d4d2a5/tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", size = 12478 }, +] + [[package]] name = "tenacity" version = "9.0.0" @@ -2780,6 +2844,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f6/17/57b444fd314d5e1593350b9a31d000e7411ba8e17ce12dc7ad54ca76b810/toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87", size = 8500 }, ] +[[package]] +name = "tornado" +version = "6.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, + { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, + { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, + { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, + { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, + { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, + { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, + { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, + { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, + { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -3103,6 +3185,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/97/ba/f9d2417e72a8e50e2c749edf8b96fbe3c423be17b39cc2648154b3184fd3/yte-1.5.7-py3-none-any.whl", hash = "sha256:a66118fbe236bcf293fc920473ad19eb20f801172f0cd43764d3c283ceee6452", size = 7830 }, ] +[[package]] +name = "zict" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ac/3c494dd7ec5122cff8252c1a209b282c0867af029f805ae9befd73ae37eb/zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5", size = 33238 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/ab/11a76c1e2126084fde2639514f24e6111b789b0bfa4fc6264a8975c7e1f1/zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", size = 43332 }, +] + [[package]] name = "zipp" version = "3.21.0" diff --git a/workflow/scripts/additional_policy_constraints.py b/workflow/scripts/additional_policy_constraints.py deleted file mode 100644 index 81da38799..000000000 --- a/workflow/scripts/additional_policy_constraints.py +++ /dev/null @@ -1 +0,0 @@ -"""Users can add custom modifications the PyPSA Linopy model here.""" From 90b9edf2505a67253b7ea9c9b7e9279a8b4cefba Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:38:41 -0800 Subject: [PATCH 44/75] add uv pyproj --- pyproject.toml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4917834fb..7ec78fdf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,46 @@ classifiers = [ ] license = {file = "LICENSE.md"} readme = "README.md" +dependencies = [ + "atlite==0.3.0", + "cartopy==0.23.0", + "dask==2024.12.0", + "dask-expr==1.1.20", + "descartes==1.1.0", + "dill>=0.3.9", + "distributed==2024.12.0", + "duckdb==0.10.0", + "geopandas==1.0.1", + "geopy==2.4.0", + "graphviz>=0.20.3", + "gurobipy==11.0.3", + "highspy>=1.9.0", + "kaleido==0.4.0rc5", + "linopy==0.3.14", + "matplotlib==3.8.0", + "netcdf4==1.6.4", + "networkx==3.1", + "numpy==1.26.0", + "openpyxl==3.1.2", + "pandas==2.2.2", + "plotly==5.17.0", + "progressbar2==4.3.2", + "pulp==2.7.0", + "pyarrow==16.1.0", + "pycountry==22.3.5", + "pyomo==6.6.1", + "pypsa==0.30.2", + "pyyaml>=6.0.2", + "rasterio==1.3.8", + "scipy==1.11.3", + "seaborn==0.13.2", + "shapely==2.0.2", + "snakemake==7.32.4", + "tsam>=2.3.6", + "xarray==2024.9.0", + "xlrd==2.0.1", +] + [project.urls] Documentation = "https://pypsa-usa.readthedocs.io/en/latest/" From 57fcc9ed6b6cf41eb55bf6aee1986dc114bfd079 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:45:40 -0800 Subject: [PATCH 45/75] add lock file --- pyproject.toml | 1 + uv.lock | 3206 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3207 insertions(+) create mode 100644 uv.lock diff --git a/pyproject.toml b/pyproject.toml index 7ec78fdf8..03e6013d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ "openpyxl==3.1.2", "pandas==2.2.2", "plotly==5.17.0", + "pre-commit>=4.1.0", "progressbar2==4.3.2", "pulp==2.7.0", "pyarrow==16.1.0", diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..a8bcf72c6 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3206 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version < '3.12'", +] + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, +] + +[[package]] +name = "affine" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/98/d2f0bb06385069e799fc7d2870d9e078cfa0fa396dc8a2b81227d0da08b9/affine-2.4.0.tar.gz", hash = "sha256:a24d818d6a836c131976d22f8c27b8d3ca32d0af64c1d8d29deb7bafa4da1eea", size = 17132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/f7/85273299ab57117850cc0a936c64151171fac4da49bc6fba0dad984a7c5f/affine-2.4.0-py3-none-any.whl", hash = "sha256:8a3df80e2b2378aef598a83c1392efd47967afec4242021a0b06b4c7cbc61a92", size = 15662 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, +] + +[[package]] +name = "atlite" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bottleneck" }, + { name = "cdsapi" }, + { name = "dask" }, + { name = "geopandas" }, + { name = "netcdf4" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "progressbar2" }, + { name = "pyproj" }, + { name = "pyyaml" }, + { name = "rasterio" }, + { name = "requests" }, + { name = "scipy" }, + { name = "shapely" }, + { name = "toolz" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/b1/2e819f719452cc783e010e5c83741f30924af400ccc096f429807c214065/atlite-0.3.0.tar.gz", hash = "sha256:7d6cfdb0b4e92f590f306ac51c7680f0f662da576148dea1c2ae57dcce634085", size = 1875107 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/25/e87b3d1cfc50c78b694c4033acec9bbbada159cf3aa71d347cb0578c9881/atlite-0.3.0-py3-none-any.whl", hash = "sha256:7e04fa04b45dc815c61bb1992969456a79525171b395bbb52f2779a0e80bdc23", size = 125745 }, +] + +[[package]] +name = "attrs" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 }, +] + +[[package]] +name = "blosc2" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "msgpack" }, + { name = "ndindex" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "py-cpuinfo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/75/6b67b3c772ad014458e0caebc49c6210ef95076e3370d0809a3b49d3efa2/blosc2-3.0.0.tar.gz", hash = "sha256:d8c03a09ed11b644b48bf050bd108972ec56ac9cbc3f2aedca077255ed81ac69", size = 7331805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/8f/a58f57e0a6060d18c367d05655d7c19887feb657e65272e1feaf9adc04cd/blosc2-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:027158b6c6de85a5c7d2e60066819df58efb6a0d0108b613db2118453d0e3a8f", size = 3975036 }, + { url = "https://files.pythonhosted.org/packages/31/c7/692c821d3235d4a979aed13171f3d1d59ff83bd137e58e0c2d8e21d4f76c/blosc2-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3514427c04f8f3180b07807eab736d694db21dea518ffde6e36359657deb922", size = 3358207 }, + { url = "https://files.pythonhosted.org/packages/39/da/f8371965e5fb167e815d68a1ddb5543e6b094a2abc2f8c0330d6188244e8/blosc2-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3213763e8ed8f0dd81393e5ecf70fa6a498160aaec6611eb7d37d462b2814824", size = 4254723 }, + { url = "https://files.pythonhosted.org/packages/df/61/7c36c780f29f11e4e7627c5c8475788e8fc539b13d6352dab59e731771aa/blosc2-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d2a11c4241e4f718931d0ed7ce2519badd9a326c1cf631ac1355b6921d732f9", size = 4408605 }, + { url = "https://files.pythonhosted.org/packages/e2/f0/1dd1fa40acd8064d6895bd71052e295876e8bb16ad83fa0d48fc1f5bd6c6/blosc2-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a23b23dab715a3918564c70ffc2f75d0c103f80be41718959fa55bc805f66b6", size = 2176712 }, + { url = "https://files.pythonhosted.org/packages/0d/b8/e4fcf10610de0a69fca8a55a4b7a636386a30f376a48101b305645dab696/blosc2-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff945f653c111aa02008bc530b48565499a806899f3ff5ff6ea7f9e73e9a660b", size = 3986559 }, + { url = "https://files.pythonhosted.org/packages/87/fb/9ed609b59c3303f456db5fc5c0231cd441d7630f1777685fab8b1b56329d/blosc2-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a8ba5eb416d125f4dac9ae89fe1915b7ef0d84ee876e42815cb3093f998cb4e5", size = 3358355 }, + { url = "https://files.pythonhosted.org/packages/6e/d1/0710966dc56924cce66bdb44f9bfebca342699fa12780ca6eb9622fc25b6/blosc2-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecba37aa87b2a2974a8bc1316d7c85e1d42e2f4e0aef38267ae1e3d55d8cdf30", size = 4234403 }, + { url = "https://files.pythonhosted.org/packages/00/20/1aaa380bef966598ecdb0c0ef8daea8d3451acaaa0abd9c29b8eede67871/blosc2-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5567bc9d966f7215a81f4e2ba24628d5d9e66c87b79a3d011ffdd2ffe40b74ee", size = 4392236 }, + { url = "https://files.pythonhosted.org/packages/c8/09/a2bff6e1b87c0addc93e963533cb46834512f9be435a424cd569927e8f0c/blosc2-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a209aec1f365063f0a887a3fb6f37690da11d0f35411cc538bab25fd3d71498", size = 2171007 }, + { url = "https://files.pythonhosted.org/packages/89/eb/93b5b83220121bd177f8392d358ebc3a21586aa1e9916feac653d8ce6cb1/blosc2-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7e5220b83cf336391f883eba745206450efae766ed40c237b9ea41877306ae0", size = 3984594 }, + { url = "https://files.pythonhosted.org/packages/34/c8/5280f3f61a59ad4170a42ff6975bffd362d720530a9c0b52d0e849d68c33/blosc2-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8da04add23d4229bbfdd2a3be3baaa0c8e3035eec02ff484a9ca97c76aff2ed7", size = 3356546 }, + { url = "https://files.pythonhosted.org/packages/74/b9/a252f0723b52a220295cb7a90ba5dd60b8d85a69246d933bc35db0704f99/blosc2-3.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d949e86522a6001ed8e8bca0a29c8638c5b3a0900376a48a9536864cccb4b89", size = 4233755 }, + { url = "https://files.pythonhosted.org/packages/4b/ad/84329757bb623b2c427adb26018aa95b6465a85900af57c5df3666918870/blosc2-3.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e7cdad6c2361d2e2ed2de33f18ef073a3d4dc7a9beb4d1db172005e09b229e", size = 4391208 }, + { url = "https://files.pythonhosted.org/packages/79/86/e2d0aad6916f5d2160b8a6b513b3b92c2a15b669a243b6e10ba059473453/blosc2-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:37de3eab82df2738b2d81fa2fbfa46efdb108b7b54b37f1255f358f90ab7212d", size = 2171060 }, +] + +[[package]] +name = "bottleneck" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/61/9fb34409d58f04e1929da41666a055c36f9495903ff669b80c893bdee65f/bottleneck-1.4.2.tar.gz", hash = "sha256:fa8e8e1799dea5483ce6669462660f9d9a95649f6f98a80d315b84ec89f449f4", size = 103563 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b8/31a1cc8279bf11a60c04b844a42666927307a47bb48964cbd92ec9f40e3e/Bottleneck-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6902ebf3e85315b481bc084f10c5770f8240275ad1e039ac69c7c8d2013b040", size = 98565 }, + { url = "https://files.pythonhosted.org/packages/16/64/09d72babae7cc29341c52f2e9381066672743d4f797c86b1e735205d5fc8/Bottleneck-1.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2fd34b9b490204f95288f0dd35d37042486a95029617246c88c0f94a0ab49fe", size = 364986 }, + { url = "https://files.pythonhosted.org/packages/7e/d6/39e957e9df9ab16df9c531e8ddf71594877063d27aa036dd105b66d3b3b3/Bottleneck-1.4.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122845e3106c85465551d4a9a3777841347cfedfbebb3aa985cca110e07030b1", size = 360256 }, + { url = "https://files.pythonhosted.org/packages/ff/cb/d287febe0e6504194ba94cf4a6d80df66a0031ca33a32b30f00c030238cc/Bottleneck-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1f61658ebdf5a178298544336b65020730bf86cc092dab5f6579a99a86bd888b", size = 369507 }, + { url = "https://files.pythonhosted.org/packages/dc/1e/9310f058ddee71798a76ab15c5c1ad71f0a5c3c6348f7faab9b6da038484/Bottleneck-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c7d29c044a3511b36fd744503c3e697e279c273a8477a6d91a2831d04fd19e0", size = 360282 }, + { url = "https://files.pythonhosted.org/packages/96/cb/c1f2a37e86e9fa47845259f0a8f32d550f7f27b908432369de055be9f7c4/Bottleneck-1.4.2-cp311-cp311-win32.whl", hash = "sha256:c663cbba8f52011fd82ee08c6a85c93b34b19e0e7ebba322d2d67809f34e0597", size = 106936 }, + { url = "https://files.pythonhosted.org/packages/d3/eb/3fd23404bbc612cf9e4883c3c2b359bd14528e234d5c40bb29bcfd591ef8/Bottleneck-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:89651ef18c06616850203bf8875c958c5d316ea48d8ba60d9b450199d39ae391", size = 111617 }, + { url = "https://files.pythonhosted.org/packages/d2/26/6f5124e31a67f75e2a3b9239cc382145326e91fc45e7d7bc9ebffa05fdfa/Bottleneck-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a74ddd0417f42eeaba37375f0fc065b28451e0fba45cb2f99e88880b10b3fa43", size = 98681 }, + { url = "https://files.pythonhosted.org/packages/c4/93/e100b6eda77f2aecf5f16157b8c04dd3463913ba188b582650cd77ccf42b/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:070d22f2f62ab81297380a89492cca931e4d9443fa4b84c2baeb52db09c3b1b4", size = 365422 }, + { url = "https://files.pythonhosted.org/packages/82/2b/c6fea2bb048d04c13b8564052818a198d50ce58d5f439ec69c2b0c458703/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc4e7645bd425c05e05acd5541e9e09cb4179e71164e862f082561bf4509eac", size = 361844 }, + { url = "https://files.pythonhosted.org/packages/8f/4c/811475885bd60cf0cb28822568d0c0c3c7d7de4fbccd2ebb66863e7dc726/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:037315c56605128a39f77d19af6a6019dc8c21a63694a4bfef3c026ed963be2e", size = 370369 }, + { url = "https://files.pythonhosted.org/packages/fd/ee/0a8157e6bbd2168bf6171811534a5a73a35f54c453dd7d86a323773b5bd7/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99778329331d5fae8df19772a019e8b73ba4d9d1650f110cd995ab7657114db0", size = 361786 }, + { url = "https://files.pythonhosted.org/packages/fa/6b/e8fda0510b8fa0f3f9a3586efc941abe9d546198e95ae5690c3c83370b36/Bottleneck-1.4.2-cp312-cp312-win32.whl", hash = "sha256:7363b3c8ce6ca433779cd7e96bcb94c0e516dcacadff0011adcbf0b3ac86bc9d", size = 107149 }, + { url = "https://files.pythonhosted.org/packages/22/25/908b75a329a05b82d717661aa95a1968d9dae0e68c654d5e16bfe0d6fbb6/Bottleneck-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:48c6b9d9287c4102b803fcb01ae66ae7ef6b310b711b4b7b7e23bf952894dc05", size = 111766 }, + { url = "https://files.pythonhosted.org/packages/2e/65/148e146ca8c16af9881a0db1d8d1849d49a5186fc9f065c79a8d25d6fc0c/Bottleneck-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c1c885ad02a6a8fa1f7ee9099f29b9d4c03eb1da2c7ab25839482d5cce739021", size = 98701 }, + { url = "https://files.pythonhosted.org/packages/80/96/6540ac9a9943b0d6f0199eddbde55e878f970d2bdda31207dc3e7a195c2b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7a1b023de1de3d84b18826462718fba548fed41870df44354f9ab6a414ea82f", size = 365443 }, + { url = "https://files.pythonhosted.org/packages/d0/aa/ccae264aac3b2621fa8a98c7afe033f22a352467cbf85fa2799d176ec31b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9dbaf737b605b30c81611f2c1d197c2fd2e46c33f605876c1d332d3360c4fc", size = 361849 }, + { url = "https://files.pythonhosted.org/packages/f3/b3/5f96d7bb23a291b835bf0a34eec359c55613f6c4262ad1bb161d897499c0/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7ebbcbe5d4062e37507b9a81e2aacdb1fcccc6193f7feff124ef2b5a6a5eb740", size = 370654 }, + { url = "https://files.pythonhosted.org/packages/51/05/9d1ababa3fd34014b708351270307320c0bc595d2d66c2ba2b9b92f0d618/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:964f6ac4118ddab3bbbac79d4f726b093459be751baba73ee0aa364666e8068e", size = 362054 }, + { url = "https://files.pythonhosted.org/packages/92/e3/123488804830604432f84a2c43e611b8e1971e230b9466a7315850d22a58/Bottleneck-1.4.2-cp313-cp313-win32.whl", hash = "sha256:2db287f6ecdbb1c998085eca9b717fec2bfc48a4ab6ae070a9820ba8ab59c90b", size = 107160 }, + { url = "https://files.pythonhosted.org/packages/54/f0/e1640ccd8468c61693092f38f835ef35a68a1ea72c3388683148b3800aa6/Bottleneck-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:26b5f0531f7044befaad95c20365dd666372e66bdacbfaf009ff65d60285534d", size = 111774 }, +] + +[[package]] +name = "bump2version" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/2a/688aca6eeebfe8941235be53f4da780c6edee05dbbea5d7abaa3aab6fad2/bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6", size = 36236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/e3/fa60c47d7c344533142eb3af0b73234ef8ea3fb2da742ab976b947e717df/bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", size = 22030 }, +] + +[[package]] +name = "cartopy" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyproj" }, + { name = "pyshp" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/00/fed048eeb80129908f8bd5f5762e230864abc8a8fbbc493740bafcb696bd/Cartopy-0.23.0.tar.gz", hash = "sha256:231f37b35701f2ba31d94959cca75e6da04c2eea3a7f14ce1c75ee3b0eae7676", size = 10687976 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/79/7b8731fb3370482eee4e59e3070e7b0abd71aa8b7e87fce757ce19f37fc1/Cartopy-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86b07b6794b616674e4e485b8574e9197bca54a4467d28dd01ae0bf178f8dc2b", size = 10936638 }, + { url = "https://files.pythonhosted.org/packages/fb/96/02e0445ab41997f942e3d59c9fc3d08220e9b2651f85ac026a9dff7ce28f/Cartopy-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dece2aa8d5ff7bf989ded6b5f07c980fb5bb772952bc7cdeab469738abdecee", size = 10921586 }, + { url = "https://files.pythonhosted.org/packages/07/31/11428c479d9eddb5a1f1bef9ad51320db35c612f992163b72739581eef6e/Cartopy-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9dfd28352dc83d6b4e4cf85d84cb50fc4886d4c1510d61f4c7cf22477d1156f", size = 11662265 }, + { url = "https://files.pythonhosted.org/packages/e1/a2/a450fe2d20be42c6666a5146697a6ea1ab9caa1940cce892af02b492c3db/Cartopy-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2671b5354e43220f8e1074e7fe30a8b9f71cb38407c78e51db9c97772f0320b", size = 10904808 }, + { url = "https://files.pythonhosted.org/packages/72/82/6d44fc5a4e7ff859b4ca2dd4eaeae680c75e2e3c16ee6f0dde02fc0fad43/Cartopy-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:80b9fd666fd47f6370d29f7ad4e352828d54aaf688a03d0b83b51e141cfd77fa", size = 10931850 }, + { url = "https://files.pythonhosted.org/packages/b2/96/f2f271e971185e38e940498421a97cb1e5dac970919bd5d65c2c430fe1f5/Cartopy-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43e36b8b7e7e373a5698757458fd28fafbbbf5f3ebbe2d378f6a5ec3993d6dc0", size = 10921507 }, + { url = "https://files.pythonhosted.org/packages/82/7d/77f96f369f23182f42b7e124536d7022485c31e51204d0673fc0a43ea28d/Cartopy-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550173b91155d4d81cd14b4892cb6cabe3dd32bd34feacaa1ec78c0e56287832", size = 11660854 }, + { url = "https://files.pythonhosted.org/packages/a2/65/6dae4e0a1414251a7bc3c2d9f87853419f921bac991e35701b938b7713dc/Cartopy-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:55219ee0fb069cc3254426e87382cde03546e86c3f7c6759f076823b1e3a44d9", size = 10904186 }, +] + +[[package]] +name = "cdsapi" +version = "0.7.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "datapi" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/62/81b38105ef75486308179d510360c1aa251ce0b7d17fe88c0e1de05c6c94/cdsapi-0.7.5.tar.gz", hash = "sha256:55221c573b8cefe83cc0bfe01a3d31213c82bf9acce70455350dd24b8095c23a", size = 13188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/c4/49f01f1382d449581d5d3db0fa49ccc23a1b4f91d615108b631c7cff40cd/cdsapi-0.7.5-py2.py3-none-any.whl", hash = "sha256:8586b837aea89ceeae379b388fbb0ace0a19b94b221f731c65632417007f69fb", size = 12201 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "cftime" +version = "1.6.4.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/c8/1155d1d58003105307c7e5985f422ae5bcb2ca0cbc553cc828f3c5a934a7/cftime-1.6.4.post1.tar.gz", hash = "sha256:50ac76cc9f10ab7bd46e44a71c51a6927051b499b4407df4f29ab13d741b942f", size = 54631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/e6/6a7d2120fcffee208cf637d22b0d8f2701d91f69f68a96940056429950f3/cftime-1.6.4.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1bf7be0a0afc87628cb8c8483412aac6e48e83877004faa0936afb5bf8a877ba", size = 233445 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/fe0d14d52cffa72d3f1c281ff9f0f384968058d86ce24fdf9e736ce5b755/cftime-1.6.4.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0f64ca83acc4e3029f737bf3a32530ffa1fbf53124f5bee70b47548bc58671a7", size = 214458 }, + { url = "https://files.pythonhosted.org/packages/55/c6/72f8fb5ee057f33ab747ba361f1396d2839a4689669aabd6217bc38430f7/cftime-1.6.4.post1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ebdfd81726b0cfb8b524309224fa952898dfa177c13d5f6af5b18cefbf497d", size = 1379075 }, + { url = "https://files.pythonhosted.org/packages/77/81/6b30815698ede50f89013f25e46d66ed3a290b8a2d6b97f95bacbbe1eb5c/cftime-1.6.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ea0965a4c87739aebd84fe8eed966e5809d10065eeffd35c99c274b6f8da15", size = 1415218 }, + { url = "https://files.pythonhosted.org/packages/24/0d/73ab09a32da1478d3ef5f4ab6c59d42f2db2a2383b427c87e05ad81b71ad/cftime-1.6.4.post1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:800a18aea4e8cb2b206450397cb8a53b154798738af3cdd3c922ce1ca198b0e6", size = 1450704 }, + { url = "https://files.pythonhosted.org/packages/79/b1/6551603f8ea31de55913c84e4def3c36670563bdea6e195fcc4b6225ddf7/cftime-1.6.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:5dcfc872f455db1f12eabe3c3ba98e93757cd60ed3526a53246e966ccde46c8a", size = 190200 }, + { url = "https://files.pythonhosted.org/packages/50/81/0bb28d54088a61592f61a11e7fcabcea6d261c47af79e18d0f9cbcd940ae/cftime-1.6.4.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a590f73506f4704ba5e154ef55bfbaed5e1b4ac170f3caeb8c58e4f2c619ee4e", size = 226615 }, + { url = "https://files.pythonhosted.org/packages/f3/1e/38dbbf8a828dfb5e0e6e5c912818b77aacf2e7bcb97b262ac6126beeb29f/cftime-1.6.4.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:933cb10e1af4e362e77f513e3eb92b34a688729ddbf938bbdfa5ac20a7f44ba0", size = 209193 }, + { url = "https://files.pythonhosted.org/packages/9b/60/0db884c76311ecaaf31f628aa9358beae5fcb0fbbdc2eb0b790a93aa258f/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf17a1b36f62e9e73c4c9363dd811e1bbf1170f5ac26d343fb26012ccf482908", size = 1320215 }, + { url = "https://files.pythonhosted.org/packages/8d/7d/2d5fc7af06da4f3bdea59a204f741bf7a30bc5019355991b2f083e557e4e/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e18021f421aa26527bad8688c1acf0c85fa72730beb6efce969c316743294f2", size = 1367426 }, + { url = "https://files.pythonhosted.org/packages/5d/ab/e8b26d05323fc5629356c82a7f64026248f121ea1361b49df441bbc8f2d7/cftime-1.6.4.post1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5835b9d622f9304d1c23a35603a0f068739f428d902860f25e6e7e5a1b7cd8ea", size = 1385593 }, + { url = "https://files.pythonhosted.org/packages/af/7b/ca72a075a3f660315b031d62d39a3e9cfef71f7929da2621d5120077a75f/cftime-1.6.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:7f50bf0d1b664924aaee636eb2933746b942417d1f8b82ab6c1f6e8ba0da6885", size = 178918 }, + { url = "https://files.pythonhosted.org/packages/da/d8/81f086dbdc6f5a4e0bb068263471f1d12861b72562fe8c18df38268e4e29/cftime-1.6.4.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c89766ebf088c097832ea618c24ed5075331f0b7bf8e9c2d4144aefbf2f1850", size = 223418 }, + { url = "https://files.pythonhosted.org/packages/4a/cc/60a825d92a4023655e330470758280a31e7b82665ef77d0e2a0fe71ea958/cftime-1.6.4.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f27113f7ccd1ca32881fdcb9a4bec806a5f54ae621fc1c374f1171f3ed98ef2", size = 207395 }, + { url = "https://files.pythonhosted.org/packages/ca/90/f5b26949899decce262fc76a1e64915b92050473114e0160cd6f7297f854/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da367b23eea7cf4df071c88e014a1600d6c5bbf22e3393a4af409903fa397e28", size = 1318113 }, + { url = "https://files.pythonhosted.org/packages/c3/f8/6f13d37abb7ade46e65a08acc31af776a96dde0eb569e05d4c4b01422ba6/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c5c83cdf09d73aa94c7bc34925edd93c5f2c7dd28e074f568f7e376271a0", size = 1366034 }, + { url = "https://files.pythonhosted.org/packages/fa/08/335cb17f3b708f9a24f96ca4abb00889c7aa20b0ae273313e7c11faf1f97/cftime-1.6.4.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6b731c7133d17b479ca0c3c46a7a04f96197f0a4d753f4c2284c3ff0447279b4", size = 1390156 }, + { url = "https://files.pythonhosted.org/packages/f3/2d/980323fb5ec1ef369604b61ba259a41d0336cc1a85b639ed7bd210bd1290/cftime-1.6.4.post1-cp313-cp313-win_amd64.whl", hash = "sha256:d2a8c223faea7f1248ab469cc0d7795dd46f2a423789038f439fee7190bae259", size = 178496 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "choreographer" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "logistro" }, + { name = "simplejson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/5f/2b8e4d08f4459180101bcc2e9d14eaf3060e933d1fcbe05598a667752c2f/choreographer-1.0.3.tar.gz", hash = "sha256:70c949767d7627009c0df62ae69ae5975537a31fad9ce69f8f8c754ac9958349", size = 35994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/df/99086b752c33d9bf58b5016069d805175311bead59e7b47f46efed178b09/choreographer-1.0.3-py3-none-any.whl", hash = "sha256:ff6fbaf634f5dbd7ace2ea5374034cf84342494b16e5062ef1b211815fc27d67", size = 41716 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "click-plugins" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497 }, +] + +[[package]] +name = "cligj" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/837dbd5d8430fd0f01ed72c4cfb2f548180f4c68c635df84ce87956cff32/cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", size = 9803 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069 }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "configargparse" +version = "1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489 }, +] + +[[package]] +name = "connection-pool" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/df/c9b4e25dce00f6349fd28aadba7b6c3f7431cc8bd4308a158fbe57b6a22e/connection_pool-0.0.3.tar.gz", hash = "sha256:bf429e7aef65921c69b4ed48f3d48d3eac1383b05d2df91884705842d974d0dc", size = 3795 } + +[[package]] +name = "contourpy" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/bb/11250d2906ee2e8b466b5f93e6b19d525f3e0254ac8b445b56e618527718/contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", size = 269555 }, + { url = "https://files.pythonhosted.org/packages/67/71/1e6e95aee21a500415f5d2dbf037bf4567529b6a4e986594d7026ec5ae90/contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", size = 254549 }, + { url = "https://files.pythonhosted.org/packages/31/2c/b88986e8d79ac45efe9d8801ae341525f38e087449b6c2f2e6050468a42c/contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", size = 313000 }, + { url = "https://files.pythonhosted.org/packages/c4/18/65280989b151fcf33a8352f992eff71e61b968bef7432fbfde3a364f0730/contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", size = 352925 }, + { url = "https://files.pythonhosted.org/packages/f5/c7/5fd0146c93220dbfe1a2e0f98969293b86ca9bc041d6c90c0e065f4619ad/contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", size = 323693 }, + { url = "https://files.pythonhosted.org/packages/85/fc/7fa5d17daf77306840a4e84668a48ddff09e6bc09ba4e37e85ffc8e4faa3/contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", size = 326184 }, + { url = "https://files.pythonhosted.org/packages/ef/e7/104065c8270c7397c9571620d3ab880558957216f2b5ebb7e040f85eeb22/contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", size = 1268031 }, + { url = "https://files.pythonhosted.org/packages/e2/4a/c788d0bdbf32c8113c2354493ed291f924d4793c4a2e85b69e737a21a658/contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", size = 1325995 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/a2f351a90d955f8b0564caf1ebe4b1451a3f01f83e5e3a414055a5b8bccb/contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375", size = 174396 }, + { url = "https://files.pythonhosted.org/packages/a8/7e/cd93cab453720a5d6cb75588cc17dcdc08fc3484b9de98b885924ff61900/contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", size = 219787 }, + { url = "https://files.pythonhosted.org/packages/37/6b/175f60227d3e7f5f1549fcb374592be311293132207e451c3d7c654c25fb/contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", size = 271494 }, + { url = "https://files.pythonhosted.org/packages/6b/6a/7833cfae2c1e63d1d8875a50fd23371394f540ce809d7383550681a1fa64/contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", size = 255444 }, + { url = "https://files.pythonhosted.org/packages/7f/b3/7859efce66eaca5c14ba7619791b084ed02d868d76b928ff56890d2d059d/contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", size = 307628 }, + { url = "https://files.pythonhosted.org/packages/48/b2/011415f5e3f0a50b1e285a0bf78eb5d92a4df000553570f0851b6e309076/contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", size = 347271 }, + { url = "https://files.pythonhosted.org/packages/84/7d/ef19b1db0f45b151ac78c65127235239a8cf21a59d1ce8507ce03e89a30b/contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", size = 318906 }, + { url = "https://files.pythonhosted.org/packages/ba/99/6794142b90b853a9155316c8f470d2e4821fe6f086b03e372aca848227dd/contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", size = 323622 }, + { url = "https://files.pythonhosted.org/packages/3c/0f/37d2c84a900cd8eb54e105f4fa9aebd275e14e266736778bb5dccbf3bbbb/contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", size = 1266699 }, + { url = "https://files.pythonhosted.org/packages/3a/8a/deb5e11dc7d9cc8f0f9c8b29d4f062203f3af230ba83c30a6b161a6effc9/contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", size = 1326395 }, + { url = "https://files.pythonhosted.org/packages/1a/35/7e267ae7c13aaf12322ccc493531f1e7f2eb8fba2927b9d7a05ff615df7a/contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", size = 175354 }, + { url = "https://files.pythonhosted.org/packages/a1/35/c2de8823211d07e8a79ab018ef03960716c5dff6f4d5bff5af87fd682992/contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", size = 220971 }, + { url = "https://files.pythonhosted.org/packages/9a/e7/de62050dce687c5e96f946a93546910bc67e483fe05324439e329ff36105/contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", size = 271548 }, + { url = "https://files.pythonhosted.org/packages/78/4d/c2a09ae014ae984c6bdd29c11e74d3121b25eaa117eca0bb76340efd7e1c/contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", size = 255576 }, + { url = "https://files.pythonhosted.org/packages/ab/8a/915380ee96a5638bda80cd061ccb8e666bfdccea38d5741cb69e6dbd61fc/contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", size = 306635 }, + { url = "https://files.pythonhosted.org/packages/29/5c/c83ce09375428298acd4e6582aeb68b1e0d1447f877fa993d9bf6cd3b0a0/contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", size = 345925 }, + { url = "https://files.pythonhosted.org/packages/29/63/5b52f4a15e80c66c8078a641a3bfacd6e07106835682454647aca1afc852/contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", size = 318000 }, + { url = "https://files.pythonhosted.org/packages/9a/e2/30ca086c692691129849198659bf0556d72a757fe2769eb9620a27169296/contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", size = 322689 }, + { url = "https://files.pythonhosted.org/packages/6b/77/f37812ef700f1f185d348394debf33f22d531e714cf6a35d13d68a7003c7/contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", size = 1268413 }, + { url = "https://files.pythonhosted.org/packages/3f/6d/ce84e79cdd128542ebeb268f84abb4b093af78e7f8ec504676673d2675bc/contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", size = 1326530 }, + { url = "https://files.pythonhosted.org/packages/72/22/8282f4eae20c73c89bee7a82a19c4e27af9b57bb602ecaa00713d5bdb54d/contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", size = 175315 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/28bca491f65312b438fbf076589dcde7f6f966b196d900777f5811b9c4e2/contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", size = 220987 }, + { url = "https://files.pythonhosted.org/packages/2f/24/a4b285d6adaaf9746e4700932f579f1a7b6f9681109f694cfa233ae75c4e/contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", size = 285001 }, + { url = "https://files.pythonhosted.org/packages/48/1d/fb49a401b5ca4f06ccf467cd6c4f1fd65767e63c21322b29b04ec40b40b9/contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", size = 268553 }, + { url = "https://files.pythonhosted.org/packages/79/1e/4aef9470d13fd029087388fae750dccb49a50c012a6c8d1d634295caa644/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", size = 310386 }, + { url = "https://files.pythonhosted.org/packages/b0/34/910dc706ed70153b60392b5305c708c9810d425bde12499c9184a1100888/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", size = 349806 }, + { url = "https://files.pythonhosted.org/packages/31/3c/faee6a40d66d7f2a87f7102236bf4780c57990dd7f98e5ff29881b1b1344/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", size = 321108 }, + { url = "https://files.pythonhosted.org/packages/17/69/390dc9b20dd4bb20585651d7316cc3054b7d4a7b4f8b710b2b698e08968d/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", size = 327291 }, + { url = "https://files.pythonhosted.org/packages/ef/74/7030b67c4e941fe1e5424a3d988080e83568030ce0355f7c9fc556455b01/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", size = 1263752 }, + { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, + { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "dask" +version = "2024.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/0a/1011ce75bc1e2da627d481f4bfcaf0eaf3367eb2e432ed908620e0a0fcfa/dask-2024.12.0.tar.gz", hash = "sha256:ffd02b06ac06b993df0b48e0ba4fe02abceb5c8b34b40bd91d63f33ec7a272a4", size = 10691490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/46/9ea782eb1accabad0931d80818fcf8b286bdd1b457ae9c718f039430c489/dask-2024.12.0-py3-none-any.whl", hash = "sha256:e038e87b9f06e7927b81ecde6cf2b49aa699bb902fec11abba5697cb48baeb8d", size = 1268483 }, +] + +[[package]] +name = "dask-expr" +version = "1.1.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dask" }, + { name = "pandas" }, + { name = "pyarrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/ef/9b7b7ccd25cef726f0b38a4d440092596e5fa6f593b82c676278c925327b/dask_expr-1.1.20.tar.gz", hash = "sha256:c5be6243296c299e21e02aa93e863e28e6f21ab3c01b645cec3dd56e1b1eac9b", size = 224530 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/a4/c7c8e2a895eba9ce41bf811b2b6755c227a489b687d1a52521350ae37436/dask_expr-1.1.20-py3-none-any.whl", hash = "sha256:a10755f2bc7d7cfb060b4fc9c7e8b139a03d961c4420ebd50ea319ea906f8b80", size = 245309 }, +] + +[[package]] +name = "datapi" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "multiurl" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/af/d753f6872281ef933e97bc3e46874918cac335318ab687af05674fbb94a1/datapi-0.2.0.tar.gz", hash = "sha256:9346d1ce8bbada920da2d4fe0c932d8e7b36c8dd8bc7851862e87712cfa32fda", size = 43816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/9d/2149dbe5baf64726242e25a906559b243a3e9ab38103996d2982430a881d/datapi-0.2.0-py3-none-any.whl", hash = "sha256:867c9ac1ae91965c4223236a2927a819bc0fc1e987490e10468db577936e1f7c", size = 26900 }, +] + +[[package]] +name = "datrie" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d", size = 63278 } + +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178 }, +] + +[[package]] +name = "descartes" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/6f/81735a30432b74f41db6754dd13869021ccfed3088d1cf7a6cfc0af9ac49/descartes-1.1.0.tar.gz", hash = "sha256:135a502146af5ed6ff359975e2ebc5fa4b71b5432c355c2cafdc6dea1337035b", size = 3525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/b6/1ed2eb03989ae574584664985367ba70cd9cf8b32ee8cad0e8aaeac819f3/descartes-1.1.0-py3-none-any.whl", hash = "sha256:4c62dc41109689d03e4b35de0a2bcbdeeb81047badc607c4415d5c753bd683af", size = 5804 }, +] + +[[package]] +name = "dill" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, +] + +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, +] + +[[package]] +name = "distributed" +version = "2024.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "dask" }, + { name = "jinja2" }, + { name = "locket" }, + { name = "msgpack" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "sortedcontainers" }, + { name = "tblib" }, + { name = "toolz" }, + { name = "tornado" }, + { name = "urllib3" }, + { name = "zict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/6e/463536dba2893634537f62ef00abf9421bf55f867892fc7c47d865c27d85/distributed-2024.12.0.tar.gz", hash = "sha256:6a2c04e63d31973ee3c1f2160d66521ed8f08e637d6a25a450e7561582920f38", size = 1115740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/ef/f0a96745cabeda5735d456403a007c2f9b6ac64a0f4c5fa4faf9d41f70dc/distributed-2024.12.0-py3-none-any.whl", hash = "sha256:ed05aa13b6c62b69b33d1ba7d1ca95e78406c8f37163fafd07f7ca94ae036b66", size = 1022908 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "dpath" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ce/e1fd64d36e4a5717bd5e6b2ad188f5eaa2e902fde871ea73a79875793fc9/dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e", size = 28266 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/d1/8952806fbf9583004ab479d8f58a9496c3d35f6b6009ddd458bdd9978eaf/dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576", size = 17618 }, +] + +[[package]] +name = "duckdb" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/6c/95c45374b7b26a31d94ae4509a69b0692e1ea2022637042c6e0a570e156e/duckdb-0.10.0.tar.gz", hash = "sha256:c02bcc128002aa79e3c9d89b9de25e062d1096a8793bc0d7932317b7977f6845", size = 11119589 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/83/e02505094c99a69674b2ad2f8beac4946978aff5641314447b2e8fbf170a/duckdb-0.10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a05af63747f1d7021995f0811c333dee7316cec3b06c0d3e4741b9bdb678dd21", size = 30311342 }, + { url = "https://files.pythonhosted.org/packages/e7/f5/b1456642d028eb50333ced31249099b095286358c6234ae5de0d6a8ea411/duckdb-0.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:072d6eba5d8a59e0069a8b5b4252fed8a21f9fe3f85a9129d186a39b3d0aea03", size = 16313992 }, + { url = "https://files.pythonhosted.org/packages/80/3c/fa6fb03c1c7aaa5d8fac237d3b38031831ca0b8b86dae03c21908031ff26/duckdb-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a77b85668f59b919042832e4659538337f1c7f197123076c5311f1c9cf077df7", size = 14040261 }, + { url = "https://files.pythonhosted.org/packages/e1/ca/8ed6fe44357a4a8f1c25bfa39140cc50dcf1894dcbe4d13aef3a1d75f317/duckdb-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a666f1d2da65d03199a977aec246920920a5ea1da76b70ae02bd4fb1ffc48c", size = 16592778 }, + { url = "https://files.pythonhosted.org/packages/99/61/1ef0d3d13908b5ae6637103e0dbeafc58028f662caa366ecd871154dbc9f/duckdb-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ec76a4262b783628d26612d184834852d9c92fb203e91af789100c17e3d7173", size = 17835800 }, + { url = "https://files.pythonhosted.org/packages/83/dc/f542b404de04ed84c7ef91df96ff5d2252430f1ea09b9fb5178a1a58f718/duckdb-0.10.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:009dd9d2cdbd3b061a9efbdfc79f2d1a8377bcf49f1e5f430138621f8c083a6c", size = 16399505 }, + { url = "https://files.pythonhosted.org/packages/50/47/a0ad83025049fcdb2af6e0c66b754c6cacce7f1aaf457b61ea89354730b1/duckdb-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:878f06766088090dad4a2e5ee0081555242b2e8dcb29415ecc97e388cf0cf8d8", size = 18385038 }, + { url = "https://files.pythonhosted.org/packages/dc/f9/79d37cbe067bf2fc9fbe4a09d0e345c264a9f221fb6b4bcfc2ef80bb6a72/duckdb-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:713ff0a1fb63a6d60f454acf67f31656549fb5d63f21ac68314e4f522daa1a89", size = 9569568 }, + { url = "https://files.pythonhosted.org/packages/da/62/3572db3f4f854c5f668dc477a989241ff72c8d7aadb605de798be702a8f6/duckdb-0.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9c0ee450dfedfb52dd4957244e31820feef17228da31af6d052979450a80fd19", size = 30331347 }, + { url = "https://files.pythonhosted.org/packages/68/c8/aa52abf16b182c4cc1f67ac33a68828a60dcf23ff7399792c59d9e580f43/duckdb-0.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ff79b2ea9994398b545c0d10601cd73565fbd09f8951b3d8003c7c5c0cebc7cb", size = 16332722 }, + { url = "https://files.pythonhosted.org/packages/ef/ca/ef3e7efafaf12f93c319c45f5e865f2e1a9819acc647da590baba8d3c136/duckdb-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6bdf1aa71b924ef651062e6b8ff9981ad85bec89598294af8a072062c5717340", size = 14048257 }, + { url = "https://files.pythonhosted.org/packages/1f/92/5ac1433dbf54a759bceeb2658fd65ef8239e0ad433b965fcb85b5b25bd6c/duckdb-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0265bbc8216be3ced7b377ba8847128a3fc0ef99798a3c4557c1b88e3a01c23", size = 16584351 }, + { url = "https://files.pythonhosted.org/packages/8d/28/8279f26af136c8046e813992c9b7643051082285d4e15866abbd9ccfa6c0/duckdb-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d418a315a07707a693bd985274c0f8c4dd77015d9ef5d8d3da4cc1942fd82e0", size = 17830443 }, + { url = "https://files.pythonhosted.org/packages/7e/c8/6c184642bc2e28f857ae1fc248c920b51525fda93308cc548b525c6a944b/duckdb-0.10.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2828475a292e68c71855190b818aded6bce7328f79e38c04a0c75f8f1c0ceef0", size = 16403736 }, + { url = "https://files.pythonhosted.org/packages/a9/e5/6c04d248d44206678e1c85b11f3041f828ef34d815fd85995603607006b2/duckdb-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c3aaeaae2eba97035c65f31ffdb18202c951337bf2b3d53d77ce1da8ae2ecf51", size = 18389565 }, + { url = "https://files.pythonhosted.org/packages/0c/a7/49f0a479b2372a0a42bdd971a6a474e9ba4c7b83279850b3564536e26d73/duckdb-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c51790aaaea97d8e4a58a114c371ed8d2c4e1ca7cbf29e3bdab6d8ccfc5afc1e", size = 9569987 }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, +] + +[[package]] +name = "filelock" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, +] + +[[package]] +name = "fonttools" +version = "4.56.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/8c/9ffa2a555af0e5e5d0e2ed7fdd8c9bef474ed676995bb4c57c9cd0014248/fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4", size = 3462892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/56/a2f3e777d48fcae7ecd29de4d96352d84e5ea9871e5f3fc88241521572cf/fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df", size = 2753325 }, + { url = "https://files.pythonhosted.org/packages/71/85/d483e9c4e5ed586b183bf037a353e8d766366b54fd15519b30e6178a6a6e/fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c", size = 2281554 }, + { url = "https://files.pythonhosted.org/packages/09/67/060473b832b2fade03c127019794df6dc02d9bc66fa4210b8e0d8a99d1e5/fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c", size = 4869260 }, + { url = "https://files.pythonhosted.org/packages/28/e9/47c02d5a7027e8ed841ab6a10ca00c93dadd5f16742f1af1fa3f9978adf4/fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049", size = 4898508 }, + { url = "https://files.pythonhosted.org/packages/bf/8a/221d456d1afb8ca043cfd078f59f187ee5d0a580f4b49351b9ce95121f57/fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62", size = 4877700 }, + { url = "https://files.pythonhosted.org/packages/a4/8c/e503863adf7a6aeff7b960e2f66fa44dd0c29a7a8b79765b2821950d7b05/fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0", size = 5045817 }, + { url = "https://files.pythonhosted.org/packages/2b/50/79ba3b7e42f4eaa70b82b9e79155f0f6797858dc8a97862428b6852c6aee/fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b", size = 2154426 }, + { url = "https://files.pythonhosted.org/packages/3b/90/4926e653041c4116ecd43e50e3c79f5daae6dcafc58ceb64bc4f71dd4924/fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05", size = 2200937 }, + { url = "https://files.pythonhosted.org/packages/39/32/71cfd6877999576a11824a7fe7bc0bb57c5c72b1f4536fa56a3e39552643/fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9", size = 2747757 }, + { url = "https://files.pythonhosted.org/packages/15/52/d9f716b072c5061a0b915dd4c387f74bef44c68c069e2195c753905bd9b7/fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f", size = 2279007 }, + { url = "https://files.pythonhosted.org/packages/d1/97/f1b3a8afa9a0d814a092a25cd42f59ccb98a0bb7a295e6e02fc9ba744214/fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2", size = 4783991 }, + { url = "https://files.pythonhosted.org/packages/95/70/2a781bedc1c45a0c61d29c56425609b22ed7f971da5d7e5df2679488741b/fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563", size = 4855109 }, + { url = "https://files.pythonhosted.org/packages/0c/02/a2597858e61a5e3fb6a14d5f6be9e6eb4eaf090da56ad70cedcbdd201685/fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a", size = 4762496 }, + { url = "https://files.pythonhosted.org/packages/f2/00/aaf00100d6078fdc73f7352b44589804af9dc12b182a2540b16002152ba4/fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28", size = 4990094 }, + { url = "https://files.pythonhosted.org/packages/bf/dc/3ff1db522460db60cf3adaf1b64e0c72b43406717d139786d3fa1eb20709/fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c", size = 2142888 }, + { url = "https://files.pythonhosted.org/packages/6f/e3/5a181a85777f7809076e51f7422e0dc77eb04676c40ec8bf6a49d390d1ff/fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba", size = 2189734 }, + { url = "https://files.pythonhosted.org/packages/a5/55/f06b48d48e0b4ec3a3489efafe9bd4d81b6e0802ac51026e3ee4634e89ba/fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692", size = 2735127 }, + { url = "https://files.pythonhosted.org/packages/59/db/d2c7c9b6dd5cbd46f183e650a47403ffb88fca17484eb7c4b1cd88f9e513/fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0", size = 2272519 }, + { url = "https://files.pythonhosted.org/packages/4d/a2/da62d779c34a0e0c06415f02eab7fa3466de5d46df459c0275a255cefc65/fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1", size = 4762423 }, + { url = "https://files.pythonhosted.org/packages/be/6a/fd4018e0448c8a5e12138906411282c5eab51a598493f080a9f0960e658f/fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea", size = 4834442 }, + { url = "https://files.pythonhosted.org/packages/6d/63/fa1dec8efb35bc11ef9c39b2d74754b45d48a3ccb2cf78c0109c0af639e8/fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3", size = 4742800 }, + { url = "https://files.pythonhosted.org/packages/dd/f4/963247ae8c73ccc4cf2929e7162f595c81dbe17997d1d0ea77da24a217c9/fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278", size = 4963746 }, + { url = "https://files.pythonhosted.org/packages/ea/e0/46f9600c39c644b54e4420f941f75fa200d9288c9ae171e5d80918b8cbb9/fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f", size = 2140927 }, + { url = "https://files.pythonhosted.org/packages/27/6d/3edda54f98a550a0473f032d8050315fbc8f1b76a0d9f3879b72ebb2cdd6/fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6", size = 2186709 }, + { url = "https://files.pythonhosted.org/packages/bf/ff/44934a031ce5a39125415eb405b9efb76fe7f9586b75291d66ae5cbfc4e6/fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14", size = 1089800 }, +] + +[[package]] +name = "fsspec" +version = "2025.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/79/68612ed99700e6413de42895aa725463e821a6b3be75c87fcce1b4af4c70/fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd", size = 292283 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/94/758680531a00d06e471ef649e4ec2ed6bf185356a7f9fbfbb7368a40bd49/fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b", size = 184484 }, +] + +[[package]] +name = "geographiclib" +version = "2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/cd/90271fd195d79a9c2af0ca21632b297a6cc3e852e0413a2e4519e67be213/geographiclib-2.0.tar.gz", hash = "sha256:f7f41c85dc3e1c2d3d935ec86660dc3b2c848c83e17f9a9e51ba9d5146a15859", size = 36720 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/5a/a26132406f1f40cf51ea349a5f11b0a46cec02a2031ff82e391c2537247a/geographiclib-2.0-py3-none-any.whl", hash = "sha256:6b7225248e45ff7edcee32becc4e0a1504c606ac5ee163a5656d482e0cd38734", size = 40324 }, +] + +[[package]] +name = "geopandas" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyogrio" }, + { name = "pyproj" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/08/2cf5d85356e45b10b8d066cf4c3ba1e9e3185423c48104eed87e8afd0455/geopandas-1.0.1.tar.gz", hash = "sha256:b8bf70a5534588205b7a56646e2082fb1de9a03599651b3d80c99ea4c2ca08ab", size = 317736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/64/7d344cfcef5efddf9cf32f59af7f855828e9d74b5f862eddf5bfd9f25323/geopandas-1.0.1-py3-none-any.whl", hash = "sha256:01e147d9420cc374d26f51fc23716ac307f32b49406e4bd8462c07e82ed1d3d6", size = 323587 }, +] + +[[package]] +name = "geopy" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "geographiclib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/e5/4e358038626f9ed671cf4d94fdb767c39644ccf062fe6a1bc089873a9dd4/geopy-2.4.0.tar.gz", hash = "sha256:a59392bf17adb486b25dbdd71fbed27733bdf24a2dac588047a619de56695e36", size = 117582 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/58/9289c6a03116025cdb61461d99b2493daa4967a80b13755463d71a0affeb/geopy-2.4.0-py3-none-any.whl", hash = "sha256:d2639a46d0ce4c091e9688b750ba94348a14b898a1e55c68f4b4a07e7d1afa20", size = 125446 }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "graphviz" +version = "0.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/83/5a40d19b8347f017e417710907f824915fba411a9befd092e52746b63e9f/graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d", size = 256455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/be/d59db2d1d52697c6adc9eacaf50e8965b6345cc143f671e1ed068818d5cf/graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5", size = 47126 }, +] + +[[package]] +name = "gurobipy" +version = "11.0.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/ba/dd7d8f299a3a87ae5bd5553b82199851e6e5e7b6fcf8528570b05d343610/gurobipy-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:138b4be8fb42d494b8484bd4cfa99ea1e511e8860cdb1171349f4fce06d9aa01", size = 10784430 }, + { url = "https://files.pythonhosted.org/packages/b9/68/7ccd72bf75db54c70ec563f5f700119ea290c9a6ccac9f3191295e4739f5/gurobipy-11.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84c58e164c90fc1de8bc0aac8ad25a82b4a9d3079b5064735822cecf552b1b26", size = 27422010 }, + { url = "https://files.pythonhosted.org/packages/af/53/8b5194b41760718a8cfb01010dcab5a8f464468fffacceb256df8aefa906/gurobipy-11.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d8eed5ca4831d1f10b08cd701f75483b27810734750d414a5b3cb6e4ba4c17f3", size = 13446177 }, + { url = "https://files.pythonhosted.org/packages/28/41/c61b4b096a093479eb45292ec7a5d127f088a92d4748faec76e4fcd2443e/gurobipy-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9e57508f7d7a8ef154767894c693273d94bed771fa300677ae74892fde7dfc57", size = 10324769 }, + { url = "https://files.pythonhosted.org/packages/19/71/d21d15318a6f400f331e940924910aa5140da55dd84813bd9f8bdecaf707/gurobipy-11.0.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:05700979469511aedfcb4d0136c161077d085393b6a449c98e9ff7818747279c", size = 10709232 }, + { url = "https://files.pythonhosted.org/packages/ec/74/d2523173f9eb8ec66a7d892a49ca6899d75acddac418f8c81cd50d4b420a/gurobipy-11.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b752a8a4d898a3cc59b0670aa449dee8e2159d4f420f30033baf96f14a36516d", size = 27267858 }, + { url = "https://files.pythonhosted.org/packages/73/5a/4c764ccd1e9bcef3d01729af0811915f3fb36e72b544e5caf477b1dc545f/gurobipy-11.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d08a69a9884b2c7ab843c9967b44770c17c73ac5726db7a8794ae85d7c1fc446", size = 13257275 }, + { url = "https://files.pythonhosted.org/packages/f2/54/b4eea516b5e318c3522456f8a26a2fc6e8c9c657e0ed06ce5ec4f7faca4d/gurobipy-11.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:74fa0d9bace48bc0f25426b5cd70addb02a13b5ea1645494cad073a32c9ec2c4", size = 10296203 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "highspy" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/77/3b103db661372703843167d323c548ab479f812d5c633549347f748d5aee/highspy-1.9.0.tar.gz", hash = "sha256:d9765753991c7a4d9bc8815a75fc4df6f5c58b50117e391e2c494cdf0eb1ed13", size = 1420463 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/a3/7a5832c08a567bb3e2b24307a87fc3279fbe3888532d1cc8766f49693d36/highspy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:244b72ff31bd0cfb736893aecf1b324f9155bab020414ad89710319fb3cda17a", size = 2151196 }, + { url = "https://files.pythonhosted.org/packages/aa/b5/17c654e5d9e0d69649af01f6cc5ba133a622b942c0327df3d056288db013/highspy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:830da7691d39b0ef3266bdcdc805f15a215f1b91fe01cb2bb39ca9fb25e2fcfc", size = 1841765 }, + { url = "https://files.pythonhosted.org/packages/eb/33/b878f6faa73888286efa72717a6d9ad49b2aab4f321a7081a455495564da/highspy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a15b420cfe45b53e0a7b8e2096bfb407ae763f338cd2a43ba6acd987266697", size = 2065797 }, + { url = "https://files.pythonhosted.org/packages/2f/d2/1aa39bb9d381ecb0122a90e6e48480358e284a2891ff51cdf90b4a885857/highspy-1.9.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86977e537c2152e998c7e5d62a0a333507a314dae374daebad1dc2cdc5616d5e", size = 2384941 }, + { url = "https://files.pythonhosted.org/packages/ee/a2/e825e8bed155f9df748ea9319a83c2c89ab860ddf89656986b3964236d34/highspy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:664048c40ad4ee6b8b7aa279a46803a448dff6079940b85cc5fbcc2d9fde1cce", size = 2248144 }, + { url = "https://files.pythonhosted.org/packages/5c/32/a4b7d1498defcb5651f651d7f234a744c9de4658b9ddaf0155e1e211272c/highspy-1.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2fbec2a5c7ff42ed1fb9f54c4c5141fcaa070ed5884b7f966fde45921fb16c3", size = 3046337 }, + { url = "https://files.pythonhosted.org/packages/0a/4e/4548176b287567917c7e60868eb819cd70e619663c749384feab468c3e62/highspy-1.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2b97e07c5921a14a40cca32fcc8477853cc9a6e909b2f0ef44fb94d2be6d4e8e", size = 3573045 }, + { url = "https://files.pythonhosted.org/packages/5c/3b/78c8eaa0f2a6a20ddb1f8c488ba6dd787baf31fd79902301b58f5f75f72c/highspy-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43d1612e9091a2fe07bad2e24dea12f0e67bb77014c59281407cd72fd704de5b", size = 3250008 }, + { url = "https://files.pythonhosted.org/packages/42/b2/c1e07d7c55e2bb6f7a5c2ee0d57e0fd6d848261a263e6e6cf419e38cfe40/highspy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:95662940d87c9f3894e8e78a405f8701af7326e28db38b524ea19119821c3b8c", size = 1563554 }, + { url = "https://files.pythonhosted.org/packages/35/e9/aa8fce2ac95124bc880761856207e7b02263730a4eb1a9cfb97116a26b44/highspy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:18827d2e7e2d1b013b2c5eac0923646942700ff5683a93c23c383370861d424d", size = 1871049 }, + { url = "https://files.pythonhosted.org/packages/ae/0d/088bd34992071335839b2f62bc1fe7831e3bc57a90406b48295aa843ab37/highspy-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9509c4cdb70171a21c98fadbcd85e4f53d54b3106258a009486816153eb29847", size = 2153200 }, + { url = "https://files.pythonhosted.org/packages/16/24/507a4b7a45f5554b8593ccceaf377270fe9a0c8ffd8c057cf26986ee3658/highspy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73e430ebe85f4b6fbbebd98ae53d47b12cebfd8cbee6fffd17760efc8780ce36", size = 1843422 }, + { url = "https://files.pythonhosted.org/packages/10/f5/aea6b779925bfd449a8857c3ec527333b4a5914b6c7ded445da8cad4fd70/highspy-1.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04ed0b41e9e2e1ad4c2ce7580562cd6b45cf872d63f3c5e21e36e3d399eb9b2b", size = 2057475 }, + { url = "https://files.pythonhosted.org/packages/f9/45/1acd79703d1bdb745b0e6f596a77b7db209291ddfdba4982f61f023e5a37/highspy-1.9.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37dc773702bcbf155e70b822b652fc9b96b2e190c70d1402de8711a177e75690", size = 2372525 }, + { url = "https://files.pythonhosted.org/packages/09/75/3cb42a6d561f288f178b38e0f822a4c4f79c7bc4cace11ce895121941b6c/highspy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d2748e1c08d6d293402e2fb4cb6d44bfd4114dce438ae536a8cf8ddabe0e90", size = 2234977 }, + { url = "https://files.pythonhosted.org/packages/44/bd/ec6861d22e5a6891b84c37e711d7801eca5b70234dbac58a67415c19ba8c/highspy-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e0809e15532688f0d16c4e27dcf4bb0a67b3d7a548373710176ff1c375b55ca5", size = 3045209 }, + { url = "https://files.pythonhosted.org/packages/c2/a4/bcb297d83a658dbf4faecb6a8bdf5999d03e88ac9950ba7f36e6acbb7ebe/highspy-1.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a3bcc624804c661edeed982dccc667abc7dabd3298633e6e3b2e1dab733953f", size = 3573139 }, + { url = "https://files.pythonhosted.org/packages/ef/c0/11c381826af55977fcefc467399e0fbe777b0ab0fab6dbb1458094acc1ba/highspy-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be067a7a775811ddd03929ce8ba71d3d8dbce079e74afda0eb8155ddf85c74f2", size = 3245431 }, + { url = "https://files.pythonhosted.org/packages/51/0a/90a8161e13281af61e12e38aeedf561a21ea921b2d96e7a2d5301f6c506e/highspy-1.9.0-cp312-cp312-win32.whl", hash = "sha256:840ad28b3a937c3129e6efe40d3ed86391a6005d5878f832eadde222cd4c1895", size = 1561810 }, + { url = "https://files.pythonhosted.org/packages/1d/c8/ec26b570a9f953bc7f5bdaaa471c1da334eb6d00d48ad75f5781dbab4c60/highspy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fcf93ef8e8440b8d900c21c33e5fc8e2041e7d54bb680bc617c5a8d71c084454", size = 1871889 }, + { url = "https://files.pythonhosted.org/packages/85/e4/9411cb5918f88894bf899f9737a67ac8e197d33c9b2119bbe4af60417cfe/highspy-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9eea8125342c2049e6e81c808e1995e78aa0ba09e505d7d0d78b5997f8c9240d", size = 2153271 }, + { url = "https://files.pythonhosted.org/packages/4d/a7/442d7720a9a01f189f36c626473c150a9fdd11cab9eb99c62cb178989ba0/highspy-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22580ac1fbce24bae9fbb906d1967d6c6798401a1f134dfc46a5f87c58791e47", size = 1843436 }, + { url = "https://files.pythonhosted.org/packages/c5/5c/6eb6ccfb16b591bbbdd22bc6d45c03e68140d5cf34cc2fcdda1ba784d489/highspy-1.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22c8217c05f3df3bf9326aba777b2808d4da5757f156e4a20058d360e6a002a6", size = 2057482 }, + { url = "https://files.pythonhosted.org/packages/b2/ed/7160f0900d5eba0c6e36398f6b8375142323ecabdfb2041e3901973d080c/highspy-1.9.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:adbf983e661b651d1c27c9a9e7d63e5c8a311986b05e3944777752060ea41b1f", size = 2372449 }, + { url = "https://files.pythonhosted.org/packages/d9/03/83ccebbbd2233e478684884c2f4ef9d680939c2336f3292d40c29d4437b1/highspy-1.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1804f7d5fbf404ebab22033728582b745a793b3c9015285eb2ab8bac97438e61", size = 2234790 }, + { url = "https://files.pythonhosted.org/packages/93/1c/6fd80e197332cac6b72bbb0e4e75b2630c3a8b62438ec11558d88223289d/highspy-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0cbf60020769bc0b1af5da8efa65f9f50397d9d441079133f76ad962524ed20f", size = 3045276 }, + { url = "https://files.pythonhosted.org/packages/91/ab/6bd4743749b7f569d67b8b3ac896c0cc87658bcfb55b816f27cce0d56161/highspy-1.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:45d47d7c5421db8075fbef930f3a3a7e4e86f14f21ed74b6e50facdb65c3ea87", size = 3573156 }, + { url = "https://files.pythonhosted.org/packages/a0/de/0de13d9b478c352ea051b10255effa8acea1615cd395a364c77dd9032030/highspy-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c0e8004bef64df1537286e5dac5fb2f3ddc152ef205d3e2e8786bcaced08e92", size = 3245380 }, + { url = "https://files.pythonhosted.org/packages/38/de/14d2f8e73fc79ab82a6f9fee81e90783d3aa15c3e21762d695090b2c98d8/highspy-1.9.0-cp313-cp313-win32.whl", hash = "sha256:a77eb1e06278ef23ed5778a3c4e9348c57d917d9197c7d2cf30acea151089b59", size = 1561805 }, + { url = "https://files.pythonhosted.org/packages/b0/e1/9cd0c9860bc562ca872a029441ac42e586c0e5ed15c0d4c470b17dd3f0b0/highspy-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:9151e47745a3605617b200f173af46bd1aee5ef06083a17e9781d806ee9ed1fd", size = 1871841 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, +] + +[[package]] +name = "identify" +version = "2.6.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d1/524aa3350f78bcd714d148ade6133d67d6b7de2cdbae7d99039c024c9a25/identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684", size = 99260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/00/1fd4a117c6c93f2dcc5b7edaeaf53ea45332ef966429be566ca16c2beb94/identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0", size = 99097 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "ipython" +version = "8.32.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/80/4d2a072e0db7d250f134bc11676517299264ebe16d62a8619d49a78ced73/ipython-8.32.0.tar.gz", hash = "sha256:be2c91895b0b9ea7ba49d33b23e2040c352b33eb6a519cca7ce6e0c743444251", size = 5507441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e1/f4474a7ecdb7745a820f6f6039dc43c66add40f1bcc66485607d93571af6/ipython-8.32.0-py3-none-any.whl", hash = "sha256:cae85b0c61eff1fc48b0a8002de5958b6528fa9c8defb1894da63f42613708aa", size = 825524 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, +] + +[[package]] +name = "kaleido" +version = "0.4.0rc5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout" }, + { name = "choreographer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/1f/53a30b6ca64ae822fb77dc0d157b8761e525015edc49b402e52e16f702ed/kaleido-0.4.0rc5.tar.gz", hash = "sha256:cab65055ca382a51f2cf1ae726d8373190f8bf97997337e316d35c23f6da27e2", size = 3312827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8e/93f2e978c85800c040406e42d0f469bc2f4d5a02609be12e8ad2ef78e183/kaleido-0.4.0rc5-py3-none-any.whl", hash = "sha256:6001a0c32db8b9b78502dcedc41dff2bdff7ea12f20a2c40fa3c6a3a2d9616f8", size = 3819614 }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635 }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717 }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413 }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994 }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804 }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690 }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839 }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109 }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269 }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468 }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394 }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901 }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306 }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966 }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311 }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156 }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555 }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071 }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053 }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278 }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139 }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952 }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132 }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997 }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060 }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471 }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793 }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855 }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430 }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294 }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736 }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194 }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942 }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341 }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455 }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138 }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857 }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129 }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538 }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, +] + +[[package]] +name = "linopy" +version = "0.3.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bottleneck" }, + { name = "dask" }, + { name = "deprecation" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "polars" }, + { name = "scipy" }, + { name = "toolz" }, + { name = "tqdm" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/9d/ff0eccefa8b768dac94d156fb96d287c87e30e7d1a8507f07b108f4f02eb/linopy-0.3.14.tar.gz", hash = "sha256:4c4d50fec3bde13f756c6f1978562bdbc7e9a6cd98f133437e5b32825d29ab7f", size = 1185647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/ab/e8c566e002e15d2ad320c74117b85c9750e5c053d15ceda7fe8563a384c2/linopy-0.3.14-py3-none-any.whl", hash = "sha256:70af5cd5834e9c97c2498d06feea890769b44e661493621e5cf08399b524cf5b", size = 85459 }, +] + +[[package]] +name = "locket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, +] + +[[package]] +name = "logistro" +version = "1.0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/dc/e4c4b1fd2e6eca7f4e288434ae7f8057c0c06bad797366e0cc027fef23ef/logistro-1.0.11.tar.gz", hash = "sha256:9386b65cde9cc71d8c356142a219868612357b3c54ed4e067a78f420c4e51846", size = 7550 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/fc/bf7cdfc51b13d6f4e2ebdacdd18ddb2aa7a70c00f8b7cd81c0906e0e7490/logistro-1.0.11-py3-none-any.whl", hash = "sha256:5f68bf18f2cb842de207947e6ee53d4980fba602935455d8fa2e9156b2e13323", size = 7006 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e1/77016194621fb1356aafeb2186f07b5dede62ea2043bf03f82325c4fccc5/matplotlib-3.8.0.tar.gz", hash = "sha256:df8505e1c19d5c2c26aff3497a7cbd3ccfc2e97043d1e4db3e76afa399164b69", size = 35864435 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/ee/e87d7a79a32f5ac833655ef29ebd37c7b389e8957bc71aacb10a95f822b9/matplotlib-3.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5dc945a9cb2deb7d197ba23eb4c210e591d52d77bf0ba27c35fc82dec9fa78d4", size = 7590852 }, + { url = "https://files.pythonhosted.org/packages/af/f3/fb27b3b902fc759bbca3f9d0336c48069c3022e57552c4b0095d997c7ea8/matplotlib-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8b5a1bf27d078453aa7b5b27f52580e16360d02df6d3dc9504f3d2ce11f6309", size = 7484528 }, + { url = "https://files.pythonhosted.org/packages/39/fc/fca496a890274b6628e310816710718d8184ede99956160c05a017789acc/matplotlib-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f25ffb6ad972cdffa7df8e5be4b1e3cadd2f8d43fc72085feb1518006178394", size = 11385752 }, + { url = "https://files.pythonhosted.org/packages/65/5b/3b8fd7d66043f0638a35fa650570cbe69efd42fe169e5024f9307598b47e/matplotlib-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee482731c8c17d86d9ddb5194d38621f9b0f0d53c99006275a12523ab021732", size = 11615276 }, + { url = "https://files.pythonhosted.org/packages/af/02/320e32ec24b062c29b4b580db3257190c66d5a8aa4d604a9c0204061ead9/matplotlib-3.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36eafe2128772195b373e1242df28d1b7ec6c04c15b090b8d9e335d55a323900", size = 9545819 }, + { url = "https://files.pythonhosted.org/packages/40/d9/c1784db9db0d484c8e5deeafbaac0d6ed66e165c6eb4a74fb43a5fa947d9/matplotlib-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:061ee58facb3580cd2d046a6d227fb77e9295599c5ec6ad069f06b5821ad1cfc", size = 7644668 }, + { url = "https://files.pythonhosted.org/packages/2e/34/121c49cd4e3e3ae4ad58c13cb7b000f2140f7bc48c7d3d950a19be634990/matplotlib-3.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3cc3776836d0f4f22654a7f2d2ec2004618d5cf86b7185318381f73b80fd8a2d", size = 7588410 }, + { url = "https://files.pythonhosted.org/packages/dd/73/8d69b0337a77f73d316232ea67708b4bcfe5a9c54dbc2327b1d1b730a0b6/matplotlib-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c49a2bd6981264bddcb8c317b6bd25febcece9e2ebfcbc34e7f4c0c867c09dc", size = 7480932 }, + { url = "https://files.pythonhosted.org/packages/84/be/ea8f33a4b9644cb6c09928cbaeddb025165a4a5f366c46370ed7abb6e6f3/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ed11654fc83cd6cfdf6170b453e437674a050a452133a064d47f2f1371f8d3", size = 11378619 }, + { url = "https://files.pythonhosted.org/packages/77/cd/1464efc9fe354026b8d2fb4ebb2f1746559b0e38308104d3ee60a5a05c71/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae97fdd6996b3a25da8ee43e3fc734fff502f396801063c6b76c20b56683196", size = 11602343 }, + { url = "https://files.pythonhosted.org/packages/83/68/aef60cdfc9818862ff2c6ea98e0bec7e230b1fc947339971d5efe4c2d02f/matplotlib-3.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87df75f528020a6299f76a1d986c0ed4406e3b2bd44bc5e306e46bca7d45e53e", size = 9539802 }, + { url = "https://files.pythonhosted.org/packages/59/c7/f8da659997fe3210fdda689cf2d7720b3a079578fb8aecc3623c4e091a77/matplotlib-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:90d74a95fe055f73a6cd737beecc1b81c26f2893b7a3751d52b53ff06ca53f36", size = 7644322 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "msgpack" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, + { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, + { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, + { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 }, + { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 }, + { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 }, + { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 }, + { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, + { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, + { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, + { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, + { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, + { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, + { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, + { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, + { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, + { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, + { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, + { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, + { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, + { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, +] + +[[package]] +name = "multiurl" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/db/aad981174d3bdaecc1d7e1f2d176641f022300ddf3a17b13c2775d041b7a/multiurl-0.3.3.tar.gz", hash = "sha256:f4d0b69dcf4a0ed740daa313dbcd4d5665420d305c50ca879285e96dc828093f", size = 18382 } + +[[package]] +name = "mypy" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/aa/cc56fb53ebe14c64f1fe91d32d838d6f4db948b9494e200d2f61b820b85d/mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385", size = 10859630 }, + { url = "https://files.pythonhosted.org/packages/04/c8/b19a760fab491c22c51975cf74e3d253b8c8ce2be7afaa2490fbf95a8c59/mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca", size = 10037973 }, + { url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 }, + { url = "https://files.pythonhosted.org/packages/fc/a6/37f7544666b63a27e46c48f49caeee388bf3ce95f9c570eb5cfba5234405/mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4", size = 12897010 }, + { url = "https://files.pythonhosted.org/packages/84/8b/459a513badc4d34acb31c736a0101c22d2bd0697b969796ad93294165cfb/mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6", size = 9562873 }, + { url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 }, + { url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 }, + { url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 }, + { url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 }, + { url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 }, + { url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "ndindex" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/f9/f8d491c18f16ffcb1a8abf78345e54879fd1fe6b61dcc8a9b471285cd27e/ndindex-1.9.2.tar.gz", hash = "sha256:b8658a06e52d6c47445c2ec11d292e1d52c3af259214c8b52e3a1aab733daa72", size = 243001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/6d/f272dbbe0a23575e57773272f02a237b51f4e4f33317bf28b2b8be47ac3d/ndindex-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1fa2e521a872870d55fa6fa85399f16c1c20bbe4e3e315bbfc80e3ea92561334", size = 163453 }, + { url = "https://files.pythonhosted.org/packages/77/f6/8ff749841f6f8f4096184e8e158e48d572358d4ad0ba1055728e4f7e0f44/ndindex-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b0ef52d15fa8755d00a6868c799ff4227f1f453901a6f4de395586f9a435b7a", size = 161947 }, + { url = "https://files.pythonhosted.org/packages/51/2d/bfc284ecc9b24acb916f5d04a69357ae56e0b6073286eaa71cf54bf0b136/ndindex-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f647eda61cae68a260017118ad8daac4d580ad221ff922bbaa1526e30e350feb", size = 506119 }, + { url = "https://files.pythonhosted.org/packages/5d/6b/83e328c3dc41ff244d7e79cd24ac62fff96405a3ea948810b2fa883c14f5/ndindex-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:384520b4d9f52cb2fd1d324e6f278ec422b2cc2885e95a00587394bf6f56a798", size = 485492 }, + { url = "https://files.pythonhosted.org/packages/87/f4/8a3a3c0d541d96a6cd39026a1211647d0fcbf047f8bac4332e4b95f54e8b/ndindex-1.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e2dd75a6e25269b66607f1722acd72e8f086837b2c58275d31b3bdfdf8a095bf", size = 489557 }, + { url = "https://files.pythonhosted.org/packages/0f/5c/5e96422400fad72762e85e3cc3a4bd52b11476b990c4e7df25836e8e9c0c/ndindex-1.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8634be005b18034846163067bce78a0209fab65e4bc77e0bc333aa160ef12b7a", size = 506268 }, + { url = "https://files.pythonhosted.org/packages/f8/8b/5948067de44c5484aa8a4db640b8b5dc5cc9b394e9f547a23fd694edf399/ndindex-1.9.2-cp311-cp311-win32.whl", hash = "sha256:89172e90e56a409197cbbe12a49aa16c83879274ca4f61fd8a03b30c6c90e3ca", size = 151566 }, + { url = "https://files.pythonhosted.org/packages/b0/f6/b2fde7ec7880d51f7280bb5e974e400bb716e3054048c409ba35ba509823/ndindex-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:d23f07831d28bb3c04c234936b6038078cd7c0c4966d2e2e37738edad6435f9f", size = 159516 }, + { url = "https://files.pythonhosted.org/packages/a3/a5/c3775c1a7279517027b86dc0c1a6a74f9a1fc7e0c298c960ed170fcf585e/ndindex-1.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:466d2e30a3c2afac6dac64e5ada19db30d23164befa7f69d29f209fb512b3d2f", size = 164104 }, + { url = "https://files.pythonhosted.org/packages/de/81/edb7ba51dae8d5a2879d39eb56651eeea4927f8292fc6286fae8b1cda0f1/ndindex-1.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e87eefa75af0f974cf2c5af14a488ee97dfdc7fb6da67f19f9dc600da5ae041", size = 161991 }, + { url = "https://files.pythonhosted.org/packages/f2/9e/79342047dd441fdcf25c776370c2b09ef8fad30bf06d7920b09278d93260/ndindex-1.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d98a41ff276fc623f3e068d40381ee06289644b530c3535bc00a8cbc5526c6", size = 521201 }, + { url = "https://files.pythonhosted.org/packages/fc/bd/834e4bb7054accc8bbf63c73f7c9f0bcbdc326fec0f560f375dd6637c63a/ndindex-1.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d42c5cd86d923f1606c3a9defbaeb3ece8f7b444f95a46ec6f1fb511e971f7", size = 498251 }, + { url = "https://files.pythonhosted.org/packages/35/1b/fe4d51e07f18596abd53b3b63dd1d4a8617af3896193418a86b7a7a95fa7/ndindex-1.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:959f8babfc3055933079296a33449e02c24074027c39ce974cf73757c7d5ea21", size = 501804 }, + { url = "https://files.pythonhosted.org/packages/ae/e5/95d5dd5a628c41db959e07ddc7212ed45844865d10375efe4fc0aa5c905b/ndindex-1.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d68d8285f3ab8a78b0db990fb25eddc637df4b00467fbf36a4656c7ee46ddc5d", size = 518095 }, + { url = "https://files.pythonhosted.org/packages/bc/49/ca6155435bb408173c3d07f07aac6e6c4a30cefec31c4dd2af75c44774d7/ndindex-1.9.2-cp312-cp312-win32.whl", hash = "sha256:c87aa8218b6eaaa9eacb2f68f1ce71be0e368280ef926d0ed9ad71d2fbe24fe6", size = 151487 }, + { url = "https://files.pythonhosted.org/packages/01/e3/c87442ba34a76e3d778135e967b625e5bb2217773a8c0be751e1537231b7/ndindex-1.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:d15f3a8566910ec25898e3d77b3b7c258b37f84a235d49cb17dfddc9036127b4", size = 159655 }, + { url = "https://files.pythonhosted.org/packages/88/41/250efa5a033b66043d18eca87d044f733ca017bd12767ddf0b9468120bb1/ndindex-1.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a33452b4a7b8510f809f8b59dbac5b1d7b3d8a68c9e42dee2c28c5131bbbfbc6", size = 162133 }, + { url = "https://files.pythonhosted.org/packages/b0/df/87f329590e807460cbd4cea47aaaadea9a5cf5e70854712eb89489d03c89/ndindex-1.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19d70a304f942c0aee89418d9c487de9c2dcfcda9073976a590c7fed587bd674", size = 160045 }, + { url = "https://files.pythonhosted.org/packages/4e/e3/407f31902bfdd6d991e8ce62307877afc23e39b8a7c93fd17ab8316a5415/ndindex-1.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0bba0f387d1d8204378e82b886c19f46ae39347ee7113c9028317270c0060be", size = 505012 }, + { url = "https://files.pythonhosted.org/packages/91/dc/4e335d8631939f267be7b16308246671c02123e28f693f544076dda8615b/ndindex-1.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876dcd82d6d1cbc9c2e6819bc7a5ca3686a5430f0a07520b94f78ff78097d2d", size = 484122 }, + { url = "https://files.pythonhosted.org/packages/c0/ed/007faa12149a21893ec64f0dcab36c70a4cd43825dcd11bd7090ef8d1c29/ndindex-1.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3c564768ffa7228d1768eb6df1a77d03d39efc9c98746d8c1b511ffcc23d5f9f", size = 486035 }, + { url = "https://files.pythonhosted.org/packages/2c/81/67d8a37aca8997d8e93554f3c39bca200a16685e84e03e2cc84cf0c93276/ndindex-1.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ddbdfee4560c3f7823de88257680c8fd6d0220a7b23bfd27b0f3fc7afa27bee1", size = 500916 }, + { url = "https://files.pythonhosted.org/packages/6e/ff/277768997fa82a865ff564a6bd91bb42f574662945e58b26c0eb3d690aba/ndindex-1.9.2-cp313-cp313-win32.whl", hash = "sha256:6274886f1348128fc4e10aef925272f904ac467175af52338d56f1cb763caf1a", size = 150906 }, + { url = "https://files.pythonhosted.org/packages/3c/4d/e121d109bf6f71bcb00c6198549152fa16358a031796c6a7aa9662191529/ndindex-1.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:98be658c00ec0827e398b0fe5c13d207ff70b027187d728cb22155cce3bf6fbe", size = 158778 }, +] + +[[package]] +name = "netcdf4" +version = "1.6.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "cftime" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/39/f8c4b2f3a4d78164e9850bb0924e1fd490e0bf8a8366b9b42cd295d7bbee/netCDF4-1.6.4.tar.gz", hash = "sha256:66da6542cbc7a6045cd1d979397dfd5a3f6c880c76d52b8f98bb108c82ee8c6e", size = 778542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/f6/b343f027582aa4db92d35f4ec89fcaa18562d0d6bf63ee15bfd494b49ba2/netCDF4-1.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08a7efab50d5a4da6227244e544a74f676b732ccdff2b8a4678174c15a3f8dff", size = 6709409 }, + { url = "https://files.pythonhosted.org/packages/98/b3/42bcbabf812e5c9e4568917a3e08987adabd2b01b0806f7c9c415e9657f8/netCDF4-1.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b7ad3f66d50970fa1bd5509c52bb8a99740c8a79fafa83bfd0bc7348b6ab5a", size = 3215491 }, + { url = "https://files.pythonhosted.org/packages/45/0d/a9708db20dd4d8ba3019722455f658ed544ae9d1e4c4edf75faa3d3314db/netCDF4-1.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5cad50d4e6e5a3b35c76862c58393ddf93baa44de98b6e040ac21896145881", size = 5041737 }, + { url = "https://files.pythonhosted.org/packages/56/9d/f9e7608e4fa3cafeb215217733ef08d770bcc0b0f70b4f3ad574113df4cf/netCDF4-1.6.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c2395c53a37717b1047650e1c9de68a3d69542bb25df5c594e1e14e9480bb18", size = 5397581 }, + { url = "https://files.pythonhosted.org/packages/f0/ef/39f1e32c623abbcff8fef81e484abafc74779388144540b804d96f0657cc/netCDF4-1.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:c85f77599c61a88d512d6536e181ff1c01fd16f4740367e4f8e5cacb36500293", size = 6555644 }, +] + +[[package]] +name = "networkx" +version = "3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/a1/47b974da1a73f063c158a1f4cc33ed0abf7c04f98a19050e80c533c31f0c/networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61", size = 2021691 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/05/9d4f9b78ead6b2661d6e8ea772e111fc4a9fbd866ad0c81906c11206b55e/networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36", size = 2072251 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numexpr" +version = "2.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/67/c7415cf04ebe418193cfd6595ae03e3a64d76dac7b9c010098b39cc7992e/numexpr-2.10.2.tar.gz", hash = "sha256:b0aff6b48ebc99d2f54f27b5f73a58cb92fde650aeff1b397c71c8788b4fff1a", size = 106787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/b7/f25d6166f92ef23737c1c90416144492a664f0a56510d90f7c6577c2cd14/numexpr-2.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b360eb8d392483410fe6a3d5a7144afa298c9a0aa3e9fe193e89590b47dd477", size = 145055 }, + { url = "https://files.pythonhosted.org/packages/66/64/428361ea6415826332f38ef2dd5c3abf4e7e601f033bfc9be68b680cb765/numexpr-2.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9a42f5c24880350d88933c4efee91b857c378aaea7e8b86221fff569069841e", size = 134743 }, + { url = "https://files.pythonhosted.org/packages/3f/fb/639ec91d2ea7b4a5d66e26e8ef8e06b020c8e9b9ebaf3bab7b0a9bee472e/numexpr-2.10.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fcb11988b57cc25b028a36d285287d706d1f536ebf2662ea30bd990e0de8b9", size = 410397 }, + { url = "https://files.pythonhosted.org/packages/89/5a/0f5c5b8a3a6d34eeecb30d0e2f722d50b9b38c0e175937e7c6268ffab997/numexpr-2.10.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4213a92efa9770bc28e3792134e27c7e5c7e97068bdfb8ba395baebbd12f991b", size = 398902 }, + { url = "https://files.pythonhosted.org/packages/a2/d5/ec734e735eba5a753efed5be3707ee7447ebd371772f8081b65a4153fb97/numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e", size = 1380354 }, + { url = "https://files.pythonhosted.org/packages/30/51/406e572531d817480bd612ee08239a36ee82865fea02fce569f15631f4ee/numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5", size = 151938 }, + { url = "https://files.pythonhosted.org/packages/04/32/5882ed1dbd96234f327a73316a481add151ff827cfaf2ea24fb4d5ad04db/numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0", size = 144961 }, + { url = "https://files.pythonhosted.org/packages/2b/96/d5053dea06d8298ae8052b4b049cbf8ef74998e28d57166cc27b8ae909e2/numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be", size = 145029 }, + { url = "https://files.pythonhosted.org/packages/3e/3c/fcd5a812ed5dda757b2d9ef2764a3e1cca6f6d1f02dbf113dc23a2c7702a/numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42", size = 134851 }, + { url = "https://files.pythonhosted.org/packages/0a/52/0ed3b306d8c9944129bce97fec73a2caff13adbd7e1df148d546d7eb2d4d/numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470", size = 411837 }, + { url = "https://files.pythonhosted.org/packages/7d/9c/6b671dd3fb67d7e7da93cb76b7c5277743f310a216b7856bb18776bb3371/numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6", size = 400577 }, + { url = "https://files.pythonhosted.org/packages/ea/4d/a167d1a215fe10ce58c45109f2869fd13aa0eef66f7e8c69af68be45d436/numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db", size = 1381735 }, + { url = "https://files.pythonhosted.org/packages/c1/d4/17e4434f989e4917d31cbd88a043e1c9c16958149cf43fa622987111392b/numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e", size = 152102 }, + { url = "https://files.pythonhosted.org/packages/b8/25/9ae599994076ef2a42d35ff6b0430da002647f212567851336a6c7b132d6/numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead", size = 145061 }, + { url = "https://files.pythonhosted.org/packages/8c/cb/2ea1848c46e4d75073c038dd75628d1aa442975303264ed230bf90f74f44/numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d", size = 145035 }, + { url = "https://files.pythonhosted.org/packages/ec/cf/bb2bcd81d6f3243590e19ac3e7795a1a370f3ebcd8ecec1f46dcd5333f37/numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f", size = 134858 }, + { url = "https://files.pythonhosted.org/packages/48/9b/c9128ffb453205c2a4c84a3abed35447c7591c2c2812e77e34fd238cb2bb/numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e", size = 415517 }, + { url = "https://files.pythonhosted.org/packages/7e/b0/64c04c9f8b4a563218d00daa1ec4563364961b79025162c5276ab2c7c407/numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527", size = 403846 }, + { url = "https://files.pythonhosted.org/packages/80/35/60e9041fd709fe98dd3109d73a03cdffaeb6ee2089179155f5c3754e9934/numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19", size = 1381659 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/955bf5b5cf8f3de7b044a999e36327e14191fa073ed0e329456ed0f8161d/numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01", size = 152105 }, + { url = "https://files.pythonhosted.org/packages/be/7a/8ce360a1848bb5bcc30a414493371678f43790ece397f8652d5f65757e57/numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd", size = 145060 }, +] + +[[package]] +name = "numpy" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/b3/b13bce39ba82b7398c06d10446f5ffd5c07db39b09bd37370dc720c7951c/numpy-1.26.0.tar.gz", hash = "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf", size = 15633455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/2f/b42860931c1479714201495ffe47d74460a916ae426a21fc9b68c5e329aa/numpy-1.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", size = 20619338 }, + { url = "https://files.pythonhosted.org/packages/35/21/9e150d654da358beb29fe216f339dc17f2b2ac13fff2a89669401a910550/numpy-1.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", size = 13981953 }, + { url = "https://files.pythonhosted.org/packages/a9/84/baf694be765d68c73f0f8a9d52151c339aed5f2d64205824a6f29021170c/numpy-1.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", size = 14167328 }, + { url = "https://files.pythonhosted.org/packages/c4/36/161e2f8110f8c49e59f6107bd6da4257d30aff9f06373d0471811f73dcc5/numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", size = 18178118 }, + { url = "https://files.pythonhosted.org/packages/37/41/63975634a93da2a384d3c8084eba467242cab68daab0cd8f4fd470dcee26/numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", size = 18020808 }, + { url = "https://files.pythonhosted.org/packages/58/d2/cbc329aa908cb963bd849f14e24f59c002a488e9055fab2c68887a6b5f1c/numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", size = 20750149 }, + { url = "https://files.pythonhosted.org/packages/93/fd/3f826c6d15d3bdcf65b8031e4835c52b7d9c45add25efa2314b53850e1a2/numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", size = 15794407 }, + { url = "https://files.pythonhosted.org/packages/e9/83/f8a62f08d38d831a2980427ffc465a4207fe600124b00cfb0ef8265594a7/numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", size = 20325091 }, + { url = "https://files.pythonhosted.org/packages/7a/72/6d1cbdf0d770016bc9485f9ef02e73d5cb4cf3c726f8e120b860a403d307/numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", size = 13672867 }, + { url = "https://files.pythonhosted.org/packages/2f/70/c071b2347e339f572f5aa61f649b70167e5dd218e3da3dc600c9b08154b9/numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", size = 13872627 }, + { url = "https://files.pythonhosted.org/packages/e3/e2/4ecfbc4a2e3f9d227b008c92a5d1f0370190a639b24fec3b226841eaaf19/numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", size = 17883864 }, + { url = "https://files.pythonhosted.org/packages/45/08/025bb65dbe19749f1a67a80655670941982e5d0144a4e588ebbdbcfe7983/numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", size = 17721550 }, + { url = "https://files.pythonhosted.org/packages/98/66/f0a846751044d0b6db5156fb6304d0336861ed055c21053a0f447103939c/numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", size = 19951520 }, + { url = "https://files.pythonhosted.org/packages/98/d7/1cc7a11118408ad21a5379ff2a4e0b0e27504c68ef6e808ebaa90ee95902/numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", size = 15504471 }, +] + +[[package]] +name = "openpyxl" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/e8/af028681d493814ca9c2ff8106fc62a4a32e4e0ae14602c2a98fc7b741c8/openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184", size = 185977 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/94/a59521de836ef0da54aaf50da6c4da8fb4072fb3053fa71f052fd9399e7a/openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5", size = 249985 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, + { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, + { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, + { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, + { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, + { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, + { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, + { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, + { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, + { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, + { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, + { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, + { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "partd" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "locket" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 }, + { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 }, + { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 }, + { url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 }, + { url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 }, + { url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 }, + { url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 }, + { url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 }, + { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 }, + { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 }, + { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 }, + { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, + { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, + { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, + { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, + { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, + { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, + { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, + { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, + { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, + { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, +] + +[[package]] +name = "plac" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/79/1edb4c836c69306d0ecb0865f46d62ea7e28ef16b3f95bb394e4f2a46330/plac-1.4.3.tar.gz", hash = "sha256:d4cb3387b2113a28aebd509433d0264a4e5d9bb7c1a86db4fbd0a8f11af74eb3", size = 38984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/af/4c61d2ac0d589719f548f5a1ba919738e44bac7b0c723ce147de5556d233/plac-1.4.3-py2.py3-none-any.whl", hash = "sha256:8a84fde8f950c9de6588a2d53c9deeac3ba1ddb456d887a33228460cf6549750", size = 22458 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "plotly" +version = "5.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/bd/6516a9f3105cf15bf5892b2eec6eac4dd1645a4b1ed82ae86e0a044dab36/plotly-5.17.0.tar.gz", hash = "sha256:290d796bf7bab87aad184fe24b86096234c4c95dcca6ecbca02d02bdf17d3d97", size = 7824316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/79/c80174d711ee26ee5da55a9cc3e248f1ec7a0188b5e4d6bbbbcd09b974b0/plotly-5.17.0-py2.py3-none-any.whl", hash = "sha256:7c84cdf11da162423da957bb093287134f2d6f170eb9a74f1459f825892247c3", size = 15641686 }, +] + +[[package]] +name = "ply" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", size = 159130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567 }, +] + +[[package]] +name = "polars" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/f8/148964866c43e6c9d2440da69149cedaca8ad799efbeba1f2bd58c48d95f/polars-1.22.0.tar.gz", hash = "sha256:8d94ae25085d92de10d93ab6a06c94f8c911bd5d9c1ff17cd1073a9dca766029", size = 4399700 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/59/ff6185f1cd3898a655fb69571986433adec80c5fe5dbd37edf1025dbbd17/polars-1.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6250f838b916fab23ccafe90928d7952afc328d316c956b42d152b20c86ffd9c", size = 32294750 }, + { url = "https://files.pythonhosted.org/packages/4e/89/ac9178aaf4bfce1087d311ddf540b259a517b584a62ba75dfd114a38e049/polars-1.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5ee3cf3783205709ce31f070f2b4ee4296fec08f2c744a9c37acc7d360121022", size = 29145077 }, + { url = "https://files.pythonhosted.org/packages/91/b6/d7967ca14b8bacf7d3db96b55213571c43443f8802229606cca60458780b/polars-1.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f25b4ef131da046d05b8235c5f29997630ee2125ebc0553b92258e88f7a8fa", size = 32885965 }, + { url = "https://files.pythonhosted.org/packages/a2/ae/c014bcb259757acd9081b4db76d19157c8978cb6eeb864f80ac0544e85cd/polars-1.22.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:729e6be8a884812a206518195a2fb407b61962323886095ede1a2a934cdb1410", size = 30165643 }, + { url = "https://files.pythonhosted.org/packages/82/ef/2a13676be0f0cfa23f1899855d8342b303cc46111126b770cdacc7fd804d/polars-1.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:78b8bcd1735e9376815d117aeae49391441b2199b5a70a300669d692b34ec713", size = 33179742 }, + { url = "https://files.pythonhosted.org/packages/79/ae/f46ff902e5ad0d3ce377902ae8ff65eaac23d9aba3c6bcb3b38ce22f5544/polars-1.22.0-cp39-abi3-win_arm64.whl", hash = "sha256:cde8f56c408151ab9790c43485b90f690d5c198ce26ab38a845045c73c999325", size = 29458831 }, +] + +[[package]] +name = "pre-commit" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 }, +] + +[[package]] +name = "progressbar2" +version = "4.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/5a/66e4838702ee7152e89a03498409e5b47536e637da2df725600201748ee6/progressbar2-4.3.2.tar.gz", hash = "sha256:c37e6e1b4e57ab43f95c3d0e8d90061bec140e4fed56b8343183db3aa1e19a52", size = 92504 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/66/86ac1b2ad859641bf5186fae72d90ca091a25aee3d6a1ba1772ea19895ba/progressbar2-4.3.2-py3-none-any.whl", hash = "sha256:036fa3bd35ae27c92e73fce4fb18aa4ba5090a1880d880cf954ecb75ccd6f3fb", size = 50571 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, +] + +[[package]] +name = "psutil" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 }, + { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 }, + { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 }, + { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 }, + { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 }, + { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pudb" +version = "2024.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jedi" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "urwid" }, + { name = "urwid-readline" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/27/fb087bdf2cd9c8e56d0347b863ce5995967c15a3e2a0b9245c7a8f6f1598/pudb-2024.1.3.tar.gz", hash = "sha256:264f239e0538e52e83d3d020143100b3171cae17227674bb1b9f8b075f34849c", size = 219443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/00/592eeb47eb30d9a4e34512eb1208537546c1073d43cabe7def26d89a7134/pudb-2024.1.3-py3-none-any.whl", hash = "sha256:b1c64447fcee5848d0376231672b97ca8580ca6d3297c82cba6b0ba5bd5ee640", size = 87963 }, +] + +[[package]] +name = "pulp" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/41/44d617a67407ea5db026500025b8aa7cad0b2b52621c04991b248c3b383d/PuLP-2.7.0.tar.gz", hash = "sha256:e73ee6b32d639c9b8cf4b4aded334ba158be5f8313544e056f796ace0a10ae63", size = 1400315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/60/b91acaa7995bfcd72f1739ea2b0f5cda707329e17f0b7f921fd8acc79889/PuLP-2.7.0-py3-none-any.whl", hash = "sha256:b6de42c929e80325bf44cc7a2997f02535440800c376b9eb8cb7b4670ed53769", size = 14251631 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335 }, +] + +[[package]] +name = "pyarrow" +version = "16.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/f2/67533f116deb6dae7a0ac04681695fe06135912253a115c5ecdc714a32d4/pyarrow-16.1.0.tar.gz", hash = "sha256:15fbb22ea96d11f0b5768504a3f961edab25eaf4197c341720c4a387f6c60315", size = 1080280 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/17/a12aaddb818b7b73d17f3304afc22bce32ccb26723b507cc9c267aa809f3/pyarrow-16.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d0ebea336b535b37eee9eee31761813086d33ed06de9ab6fc6aaa0bace7b250c", size = 28380406 }, + { url = "https://files.pythonhosted.org/packages/f3/94/4e2a579bbac1adb19e63b054b300f6f7fa04f32f212ce86c18727bdda698/pyarrow-16.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e73cfc4a99e796727919c5541c65bb88b973377501e39b9842ea71401ca6c1c", size = 26040531 }, + { url = "https://files.pythonhosted.org/packages/7e/34/d5b6eb5066553533dd6eb9782d50f353f8c6451ee2e49e0ea54d0e67bc34/pyarrow-16.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9251264247ecfe93e5f5a0cd43b8ae834f1e61d1abca22da55b20c788417f6", size = 38666685 }, + { url = "https://files.pythonhosted.org/packages/d2/34/4e3c04e7398764e56ef00f8f267f8ebf565808478f5fee850cef4be670c3/pyarrow-16.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf5aace92d520d3d2a20031d8b0ec27b4395cab9f74e07cc95edf42a5cc0147", size = 40949577 }, + { url = "https://files.pythonhosted.org/packages/47/62/b446ee0971b00e7437b9c54a8409ae20413235a64c0a301d7cf97070cffa/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e", size = 38077480 }, + { url = "https://files.pythonhosted.org/packages/fa/15/48a68b30542a0231a75c26d8661bc5c9bbc07b42c5b219e929adba814ba7/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b", size = 40821141 }, + { url = "https://files.pythonhosted.org/packages/49/4d/62a09116ec357ade462fac4086e0711457a87177bea25ae46b25897d6d7c/pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b", size = 25889334 }, + { url = "https://files.pythonhosted.org/packages/84/bd/d5903125e38c33b74f7b3d57ffffd4ef48145208cfd8742367f12effb59c/pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f", size = 28372822 }, + { url = "https://files.pythonhosted.org/packages/9b/73/560ef6bf05f16305502b8e368c771e8f82d774898b37a3fb231f89c13342/pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a", size = 26004052 }, + { url = "https://files.pythonhosted.org/packages/56/5e/3cd956aceb1c960e8ac6fdc6eea69d642aa2e6ee10e2f10ce7815dbf62a9/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c", size = 38660648 }, + { url = "https://files.pythonhosted.org/packages/08/4a/668e7fb6bc564e5361097f1f160b2891ca40bcacfe018638e2841073ec3d/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2", size = 40961053 }, + { url = "https://files.pythonhosted.org/packages/f7/8f/a51a290a855172514b8496c8a74f0e0b98e5e0582d44ae7547cf68dd033b/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628", size = 38060675 }, + { url = "https://files.pythonhosted.org/packages/25/7b/8da91f8de0b40b760dd748031973b6ac2aa3d4f85c67f45b7e58577ca22e/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7", size = 40826735 }, + { url = "https://files.pythonhosted.org/packages/fa/2b/a0053f1304586f2976cb2c37ddb0e52cf4114220e805ebba272a1e231ccc/pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444", size = 25838156 }, +] + +[[package]] +name = "pycountry" +version = "22.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/24/033604d30f6cf82d661c0f9dfc2c71d52cafc2de516616f80d3b0600cb7c/pycountry-22.3.5.tar.gz", hash = "sha256:b2163a246c585894d808f18783e19137cb70a0c18fb36748dc01fc6f109c1646", size = 10141551 } + +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "babel" }, + { name = "beautifulsoup4" }, + { name = "docutils" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pyogrio" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "numpy" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/8f/5a784595524a79c269f2b1c880f4fdb152867df700c97005dda51997da02/pyogrio-0.10.0.tar.gz", hash = "sha256:ec051cb568324de878828fae96379b71858933413e185148acb6c162851ab23c", size = 281950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/2c/c761e6adeb81bd4029a137b3240e7214a8c9aaf225883356196afd6ef9d8/pyogrio-0.10.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5b1a51431a27a1cb3e4e19558939c1423106e06e7b67d6285f4fba9c2d0a91b9", size = 15083526 }, + { url = "https://files.pythonhosted.org/packages/c3/e5/983aa9ddf2ff784e973d6b2ec3e874065d6655a5329ca26311b0f3b9f92f/pyogrio-0.10.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:216d69cd77b2b4a0c9d7d449bc239f8b77f3d73f4a05d9c738a0745b236902d8", size = 16457867 }, + { url = "https://files.pythonhosted.org/packages/fa/9a/7103eee7aa3b6ec88e072ef18a05c3aae1ed96fe00009a7a5ce139b50f30/pyogrio-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2f0b75f0077ce33256aec6278c2a9c3b79bf0637ddf4f93d3ab2609f0501d96", size = 23926332 }, + { url = "https://files.pythonhosted.org/packages/8b/b2/2ca124343aba24b9a5dcd7c1f43da81e652849cfaf3110d3f507a80af0a1/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0a47f702d29808c557d2ebea8542c23903f021eae44e16838adef2ab4281c71b", size = 23138693 }, + { url = "https://files.pythonhosted.org/packages/ae/15/501aa4823c142232169d54255ab343f28c4ea9e7fa489b8433dcc873a942/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:11e6c71d12da6b445e77d0fc0198db1bd35a77e03a0685e45338cbab9ce02add", size = 24062952 }, + { url = "https://files.pythonhosted.org/packages/94/8d/24f21e6a93ca418231aee3bddade7a0766c89c523832f29e08a8860f83e6/pyogrio-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0d74e91a9c0ff2f9abe01b556ff663977193b2d6922208406172d0fc833beff", size = 16172573 }, + { url = "https://files.pythonhosted.org/packages/b5/b5/3c5dfd0b50cbce6f3d4e42c0484647feb1809dbe20e225c4c6abd067e69f/pyogrio-0.10.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d6558b180e020f71ab7aa7f82d592ed3305c9f698d98f6d0a4637ec7a84c4ce", size = 15079211 }, + { url = "https://files.pythonhosted.org/packages/b8/9a/1ba9c707a094976f343bd0177741eaba0e842fa05ecd8ab97192db4f2ec1/pyogrio-0.10.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a99102037eead8ba491bc57825c1e395ee31c9956d7bff7b4a9e4fdbff3a13c2", size = 16442782 }, + { url = "https://files.pythonhosted.org/packages/5e/bb/b4250746c2c85fea5004cae93e9e25ad01516e9e94e04de780a2e78139da/pyogrio-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a4c373281d7cbf560c5b61f8f3c7442103ad7f1c7ac4ef3a84572ed7a5dd2f6", size = 23899832 }, + { url = "https://files.pythonhosted.org/packages/bd/4c/79e47e40a8e54e79a45133786a0a58209534f580591c933d40c5ed314fe7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:19f18411bdf836d24cdc08b9337eb3ec415e4ac4086ba64516b36b73a2e88622", size = 23081469 }, + { url = "https://files.pythonhosted.org/packages/47/78/2b62c8a340bcb0ea56b9ddf2ef5fd3d1f101dc0e98816b9e6da87c5ac3b7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1abbcdd9876f30bebf1df8a0273f6cdeb29d03259290008275c7fddebe139f20", size = 24024758 }, + { url = "https://files.pythonhosted.org/packages/43/97/34605480f06b0ad9611bf58a174eccc6f3673275f3d519cf763391892881/pyogrio-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a3e09839590d71ff832aa95c4f23fa00a2c63c3de82c1fbd4fb8d265792acfc", size = 16160294 }, + { url = "https://files.pythonhosted.org/packages/14/4a/4c8e4f5b9edbca46e0f8d6c1c0b56c0d4af0900c29f4bea22d37853c07f3/pyogrio-0.10.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c90478209537a31dcc65664a87a04c094bb0e08efe502908a6682b8cec0259bf", size = 15076879 }, + { url = "https://files.pythonhosted.org/packages/5f/be/7db0644eef9ef3382518399aaf3332827c43018112d2a74f78784fd496ec/pyogrio-0.10.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:fec45e1963b7058e5a1aa98598aed07c0858512c833d6aad2c672c3ec98bbf04", size = 16440405 }, + { url = "https://files.pythonhosted.org/packages/96/77/f199230ba86fe88b1f57e71428c169ed982de68a32d6082cd7c12d0f5d55/pyogrio-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28cb139f8a5d0365ede602230104b407ae52bb6b55173c8d5a35424d28c4a2c5", size = 23871511 }, + { url = "https://files.pythonhosted.org/packages/25/ac/ca483bec408b59c54f7129b0244cc9de21d8461aefe89ece7bd74ad33807/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:cea0187fcc2d574e52af8cfab041fa0a7ad71d5ef6b94b49a3f3d2a04534a27e", size = 23048830 }, + { url = "https://files.pythonhosted.org/packages/d7/3e/c35f2d8dad95b24e568c468f09ff60fb61945065465e0ec7868400596566/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7c02b207ea8cf09c501ea3e95d29152781a00d3c32267286bc36fa457c332205", size = 23996873 }, + { url = "https://files.pythonhosted.org/packages/27/5d/0deb16d228362a097ee3258d0a887c9c0add4b9678bb4847b08a241e124d/pyogrio-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:02e54bcfb305af75f829044b0045f74de31b77c2d6546f7aaf96822066147848", size = 16158260 }, +] + +[[package]] +name = "pyomo" +version = "6.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ply" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/70/ea738ea23c23399e5e04f184bf78fb181e91284369b4fb3ce74b107edcea/Pyomo-6.6.1.tar.gz", hash = "sha256:3fb0aba7b0f4120e6ce0f242502c0e61478d61e326bc90b7dc392bbefd114b34", size = 2501065 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/9c/d83ade42771c3dc80e69f9eaf68c95ce4de33c790d7e188ce71f2966e20c/Pyomo-6.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fa2442abdd4872320c0a8eca960136fdd9d4faa14353be3391eff11403547027", size = 6999786 }, + { url = "https://files.pythonhosted.org/packages/2e/a1/b2f713d1f1a784a56f7561a498ebd550c3890b38c59a9bbb7350f223ba21/Pyomo-6.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f795a4fb345ab5676acf57b39643a3605f1d88c6d10d6710514d959bd52bfb1", size = 12847137 }, + { url = "https://files.pythonhosted.org/packages/1c/38/3e085badd0b9c38de2d1fbb34ce0a680579aac1db839a7af9044bed59ca4/Pyomo-6.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e0b12f82f0c70ed246bb8b5eed8279cee07d7f2218bc8dd955562b9a82e595", size = 4345819 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, +] + +[[package]] +name = "pyproj" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/c2/0572c8e31aebf0270f15f3368adebd10fc473de9f09567a0743a3bc41c8d/pyproj-3.7.0.tar.gz", hash = "sha256:bf658f4aaf815d9d03c8121650b6f0b8067265c36e31bc6660b98ef144d81813", size = 225577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/8f/15ff6ab10a08050e94afcd544962a1a930d0bb7ca102ad39795a847eb340/pyproj-3.7.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e66d8d42dbdf232e121546c5a1dec097caf0454e4885c09a8e03cdcee0753c03", size = 6272213 }, + { url = "https://files.pythonhosted.org/packages/2d/4d/610fe2a17de71b4fe210af69ce25f2d65379ba0a48299129894d0d0988ee/pyproj-3.7.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7764b64a0aefe40134a2828b3a40be88f6c8b7832c45d8a9f2bd592ace4b2a3b", size = 4634548 }, + { url = "https://files.pythonhosted.org/packages/d6/27/0327d0b0fcdfc4cf72696a2effca2963e524dcd846a0274ba503f8bf2648/pyproj-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53c442c5081dc95346996f5c4323fde2caafc69c6e60b4707aa46e88244f1e04", size = 6333913 }, + { url = "https://files.pythonhosted.org/packages/3c/e5/2cb256148c730b9c3f74bfb3c03904f5070499c6dcaea153073a9642c6c6/pyproj-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc5b305d4d5d7697885681d9b660623e328227612823d5c660e0a9566cb48838", size = 9460363 }, + { url = "https://files.pythonhosted.org/packages/ba/a3/4aa1e8e78ad18aa170efd2c94c1931bf2a34c526683b874d06e40fa323f6/pyproj-3.7.0-cp311-cp311-win32.whl", hash = "sha256:de2b47d748dc41cccb6b3b713d4d7dc9aa1046a82141c8665026908726426abc", size = 5820551 }, + { url = "https://files.pythonhosted.org/packages/26/0c/b084e8839a117eaad8cb4fbaa81bbb24c6f183de0ee95c6c4e2770ab6f09/pyproj-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:38cba7c4c5679e40242dd959133e95b908d3b912dd66291094fd13510e8517ff", size = 6231788 }, + { url = "https://files.pythonhosted.org/packages/bd/19/be806b711e9ebfb80411c653054157db128fffdd7f8493e3064136c8d880/pyproj-3.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:8cbec92bdd6e9933ca08795c12717d1384e9b51cf4b1acf0d753db255a75c51e", size = 6261400 }, + { url = "https://files.pythonhosted.org/packages/99/3b/8497995e8cae0049d013679c6a7ac6c57b816d590c733a388748dafe5af5/pyproj-3.7.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8c4a8e4d3ba76c3adac3c087544cf92f7f9a19ea34946904a13fca48cc1c0106", size = 4637848 }, + { url = "https://files.pythonhosted.org/packages/ea/f7/2a5b46d6f8da913d58d44942ab06ca4803b5424b73259b15344cf90040f6/pyproj-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82624fb42aa31f6b1a860fbc0316babd07fd712642bc31022df4e9b4056bf463", size = 6324856 }, + { url = "https://files.pythonhosted.org/packages/36/83/c257771077bcf9da20d0e97abc834f9037c219986cc76d40183903a30464/pyproj-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1bbb3f89c68d4a6835c40b2da8b27680eec60e8cc7cdb08c09bcc725b2b62", size = 9525831 }, + { url = "https://files.pythonhosted.org/packages/d6/50/a635de79def69fe03cdef3a4bd3bec780c30987bce3a15dd7099afb2506f/pyproj-3.7.0-cp312-cp312-win32.whl", hash = "sha256:952515d5592167ad4436b355485f82acebed2a49b46722159e4584b75a763dd3", size = 5811864 }, + { url = "https://files.pythonhosted.org/packages/a1/8b/96bc8c8f3eca4eb7fa3758fde0b755d1df30a19f494376e3ee8de1ef4e79/pyproj-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0692f806224e8ed82fe4acfa57268ff444fdaf9f330689f24c0d96e59480cce1", size = 6224720 }, + { url = "https://files.pythonhosted.org/packages/bf/da/a17c452bea1ff4cd58d6dc573055b9c8fb6af114b7d2c694782aec770865/pyproj-3.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:94e8b903a9e83448fd2379c49dec3e8cd83c9ed36f54354e68b601cef56d5426", size = 6254898 }, + { url = "https://files.pythonhosted.org/packages/c2/31/ab07b389f2caa527c95ab2ea1940d28879bd2a19e67b2529cb3e94648d26/pyproj-3.7.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:64cb5c17d6f6305a8b978a40f95560c87c5b363fcac40632337955664437875a", size = 4628612 }, + { url = "https://files.pythonhosted.org/packages/1d/24/def3ded6529db3e3d8351ad73481730249ab57d8d876d502f86d7958ce06/pyproj-3.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c54e9bdda7ab9c4a5af50f9d6e6ee7704e05fafd504896b96ed1208c7aea098", size = 6315895 }, + { url = "https://files.pythonhosted.org/packages/dd/14/07314f78302105d199fb25e73376d723efe9c2ef3906463aae209913a6d3/pyproj-3.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24fa4e9e0abba875f9524808410cc520067eaf38fd5549ed0ef7c43ac39923c9", size = 9466144 }, + { url = "https://files.pythonhosted.org/packages/00/f2/2a116920db3496e3ff3c94d7d8d15da41374f35cfe1b9e79682eca500a61/pyproj-3.7.0-cp313-cp313-win32.whl", hash = "sha256:b9e8353fc3c79dc14d1f5ac758a1a6e4eee04102c3c0b138670f121f5ac52eb4", size = 5807180 }, + { url = "https://files.pythonhosted.org/packages/f8/33/3c8c6302717096b54aa14ccbb271045ba04629e21cbf348f2f2dc94f69b4/pyproj-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:10a8dc6ec61af97c89ff032647d743f8dc023645773da42ef43f7ae1125b3509", size = 6218036 }, +] + +[[package]] +name = "pypsa" +version = "0.30.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecation" }, + { name = "geopandas" }, + { name = "highspy" }, + { name = "linopy" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "tables" }, + { name = "validators" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/c4/2765234ce7aceca75d3118952161801044ce7996b88fdf720114e70687aa/pypsa-0.30.2.tar.gz", hash = "sha256:2ea2d4b7c8def60ae7ee2557bcd456a8412386a1b55f91ef82067d5d1e9793b0", size = 4974370 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/0f/f20ad09b34315634400da3c63b8e9d625edad6e2a7fa28351b0ac595756f/pypsa-0.30.2-py3-none-any.whl", hash = "sha256:1ebc22d08c1cd1e06c76654d9665245136c2afae1da892bfcc90eff70dff4e0d", size = 135575 }, +] + +[[package]] +name = "pypsa-usa" +source = { editable = "." } +dependencies = [ + { name = "atlite" }, + { name = "cartopy" }, + { name = "dask" }, + { name = "dask-expr" }, + { name = "descartes" }, + { name = "dill" }, + { name = "distributed" }, + { name = "duckdb" }, + { name = "geopandas" }, + { name = "geopy" }, + { name = "graphviz" }, + { name = "gurobipy" }, + { name = "highspy" }, + { name = "kaleido" }, + { name = "linopy" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "openpyxl" }, + { name = "pandas" }, + { name = "plotly" }, + { name = "pre-commit" }, + { name = "progressbar2" }, + { name = "pulp" }, + { name = "pyarrow" }, + { name = "pycountry" }, + { name = "pyomo" }, + { name = "pypsa" }, + { name = "pyyaml" }, + { name = "rasterio" }, + { name = "scipy" }, + { name = "seaborn" }, + { name = "shapely" }, + { name = "snakemake" }, + { name = "tsam" }, + { name = "xarray" }, + { name = "xlrd" }, +] + +[package.optional-dependencies] +dev = [ + { name = "bump2version" }, + { name = "ipython" }, + { name = "mypy" }, + { name = "myst-parser" }, + { name = "pre-commit" }, + { name = "pudb" }, + { name = "ruff" }, + { name = "sphinx" }, + { name = "sphinx-autobuild" }, + { name = "sphinx-book-theme" }, + { name = "types-pyyaml" }, +] + +[package.metadata] +requires-dist = [ + { name = "atlite", specifier = "==0.3.0" }, + { name = "bump2version", marker = "extra == 'dev'" }, + { name = "cartopy", specifier = "==0.23.0" }, + { name = "dask", specifier = "==2024.12.0" }, + { name = "dask-expr", specifier = "==1.1.20" }, + { name = "descartes", specifier = "==1.1.0" }, + { name = "dill", specifier = ">=0.3.9" }, + { name = "distributed", specifier = "==2024.12.0" }, + { name = "duckdb", specifier = "==0.10.0" }, + { name = "geopandas", specifier = "==1.0.1" }, + { name = "geopy", specifier = "==2.4.0" }, + { name = "graphviz", specifier = ">=0.20.3" }, + { name = "gurobipy", specifier = "==11.0.3" }, + { name = "highspy", specifier = ">=1.9.0" }, + { name = "ipython", marker = "extra == 'dev'" }, + { name = "kaleido", specifier = "==0.4.0rc5" }, + { name = "linopy", specifier = "==0.3.14" }, + { name = "matplotlib", specifier = "==3.8.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.11.0" }, + { name = "myst-parser", marker = "extra == 'dev'" }, + { name = "netcdf4", specifier = "==1.6.4" }, + { name = "networkx", specifier = "==3.1" }, + { name = "numpy", specifier = "==1.26.0" }, + { name = "openpyxl", specifier = "==3.1.2" }, + { name = "pandas", specifier = "==2.2.2" }, + { name = "plotly", specifier = "==5.17.0" }, + { name = "pre-commit", specifier = ">=4.1.0" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "progressbar2", specifier = "==4.3.2" }, + { name = "pudb", marker = "extra == 'dev'" }, + { name = "pulp", specifier = "==2.7.0" }, + { name = "pyarrow", specifier = "==16.1.0" }, + { name = "pycountry", specifier = "==22.3.5" }, + { name = "pyomo", specifier = "==6.6.1" }, + { name = "pypsa", specifier = "==0.30.2" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "rasterio", specifier = "==1.3.8" }, + { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.5.2" }, + { name = "scipy", specifier = "==1.11.3" }, + { name = "seaborn", specifier = "==0.13.2" }, + { name = "shapely", specifier = "==2.0.2" }, + { name = "snakemake", specifier = "==7.32.4" }, + { name = "sphinx", marker = "extra == 'dev'" }, + { name = "sphinx-autobuild", marker = "extra == 'dev'" }, + { name = "sphinx-book-theme", marker = "extra == 'dev'" }, + { name = "tsam", specifier = ">=2.3.6" }, + { name = "types-pyyaml", marker = "extra == 'dev'" }, + { name = "xarray", specifier = "==2024.9.0" }, + { name = "xlrd", specifier = "==2.0.1" }, +] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, +] + +[[package]] +name = "pyshp" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9f/0dd21250c60375a532c35e89fad8d5e8a3f1a2e3f7c389ccc5a60b05263e/pyshp-2.3.1.tar.gz", hash = "sha256:4caec82fd8dd096feba8217858068bacb2a3b5950f43c048c6dc32a3489d5af1", size = 1731544 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/2f/68116db5b36b895c0450e3072b8cb6c2fac0359279b182ea97014d3c8ac0/pyshp-2.3.1-py2.py3-none-any.whl", hash = "sha256:67024c0ccdc352ba5db777c4e968483782dfa78f8e200672a90d2d30fd8b7b49", size = 46537 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-utils" +version = "3.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/4c/ef8b7b1046d65c1f18ca31e5235c7d6627ca2b3f389ab1d44a74d22f5cc9/python_utils-3.9.1.tar.gz", hash = "sha256:eb574b4292415eb230f094cbf50ab5ef36e3579b8f09e9f2ba74af70891449a0", size = 35403 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/69/31c82567719b34d8f6b41077732589104883771d182a9f4ff3e71430999a/python_utils-3.9.1-py2.py3-none-any.whl", hash = "sha256:0273d7363c7ad4b70999b2791d5ba6b55333d6f7a4e4c8b6b39fb82b5fab4613", size = 32078 }, +] + +[[package]] +name = "pytz" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, +] + +[[package]] +name = "pywin32" +version = "308" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 }, + { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 }, + { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 }, + { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, + { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, + { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, + { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, + { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, + { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "rasterio" +version = "1.3.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "affine" }, + { name = "attrs" }, + { name = "certifi" }, + { name = "click" }, + { name = "click-plugins" }, + { name = "cligj" }, + { name = "numpy" }, + { name = "setuptools" }, + { name = "snuggs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/2e/65affa3bd9c6c8f4a3b4f7cbf7947d0a3b3a65675af58162f80201c01510/rasterio-1.3.8.tar.gz", hash = "sha256:ffdd18e78efdf8ad5861065fd812a66dd34264293317ff6540a078ea891cdef8", size = 412381 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/2c/16b7c4feb71b8e56d79e831ae26001e3e699c9f3c279fb5cb4b17e7d8c64/rasterio-1.3.8-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d5ccc8e6d30534d510ce5099d4a35616611cadcae79aa1216150c2696e03ddde", size = 22810323 }, + { url = "https://files.pythonhosted.org/packages/9f/47/320376453d416461e5d16101cdebdaae019b59ce5a169ce855c6ea7fd560/rasterio-1.3.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3b8410be847e8fd96cbe744e28e484437b370830052b5dcc7b11efc8c73fffc", size = 19453629 }, + { url = "https://files.pythonhosted.org/packages/7c/5d/6fb859b5df963b3213da844fe6c24065fc9d20e3a47146dd0454efa7021e/rasterio-1.3.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8e1b456f58b9ae023026730320424091af504ef066418ddcd296b9014845ee", size = 21263873 }, + { url = "https://files.pythonhosted.org/packages/03/96/1a35b15183bdc9c6a19e08cd92b5ebb201a9c76ec24c3f17a714202c5d2b/rasterio-1.3.8-cp311-cp311-win_amd64.whl", hash = "sha256:0323332ed1bfad522e53a3da45e0d3453e603862c3d2c08d8a639a7be76853fb", size = 22914349 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "reretry" +version = "0.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/1d/25d562a62b7471616bccd7c15a7533062eb383927e68667bf331db990415/reretry-0.11.8.tar.gz", hash = "sha256:f2791fcebe512ea2f1d153a2874778523a8064860b591cd90afc21a8bed432e3", size = 4836 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/11/e295e07d4ae500144177f875a8de11daa4d86b8246ab41c76a98ce9280ca/reretry-0.11.8-py2.py3-none-any.whl", hash = "sha256:5ec1084cd9644271ee386d34cd5dd24bdb3e91d55961b076d1a31d585ad68a79", size = 5609 }, +] + +[[package]] +name = "rpds-py" +version = "0.22.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/ad/8d1ddf78f2805a71253fcd388017e7b4a0615c22c762b6d35301fef20106/rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", size = 359773 }, + { url = "https://files.pythonhosted.org/packages/c8/75/68c15732293a8485d79fe4ebe9045525502a067865fa4278f178851b2d87/rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", size = 349214 }, + { url = "https://files.pythonhosted.org/packages/3c/4c/7ce50f3070083c2e1b2bbd0fb7046f3da55f510d19e283222f8f33d7d5f4/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", size = 380477 }, + { url = "https://files.pythonhosted.org/packages/9a/e9/835196a69cb229d5c31c13b8ae603bd2da9a6695f35fe4270d398e1db44c/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", size = 386171 }, + { url = "https://files.pythonhosted.org/packages/f9/8e/33fc4eba6683db71e91e6d594a2cf3a8fbceb5316629f0477f7ece5e3f75/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", size = 422676 }, + { url = "https://files.pythonhosted.org/packages/37/47/2e82d58f8046a98bb9497a8319604c92b827b94d558df30877c4b3c6ccb3/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", size = 446152 }, + { url = "https://files.pythonhosted.org/packages/e1/78/79c128c3e71abbc8e9739ac27af11dc0f91840a86fce67ff83c65d1ba195/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", size = 381300 }, + { url = "https://files.pythonhosted.org/packages/c9/5b/2e193be0e8b228c1207f31fa3ea79de64dadb4f6a4833111af8145a6bc33/rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", size = 409636 }, + { url = "https://files.pythonhosted.org/packages/c2/3f/687c7100b762d62186a1c1100ffdf99825f6fa5ea94556844bbbd2d0f3a9/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", size = 556708 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/c00cbc4b857e8b3d5e7f7fc4c81e23afd8c138b930f4f3ccf9a41a23e9e4/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", size = 583554 }, + { url = "https://files.pythonhosted.org/packages/d0/08/696c9872cf56effdad9ed617ac072f6774a898d46b8b8964eab39ec562d2/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", size = 552105 }, + { url = "https://files.pythonhosted.org/packages/18/1f/4df560be1e994f5adf56cabd6c117e02de7c88ee238bb4ce03ed50da9d56/rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", size = 220199 }, + { url = "https://files.pythonhosted.org/packages/b8/1b/c29b570bc5db8237553002788dc734d6bd71443a2ceac2a58202ec06ef12/rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", size = 231775 }, + { url = "https://files.pythonhosted.org/packages/75/47/3383ee3bd787a2a5e65a9b9edc37ccf8505c0a00170e3a5e6ea5fbcd97f7/rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", size = 352334 }, + { url = "https://files.pythonhosted.org/packages/40/14/aa6400fa8158b90a5a250a77f2077c0d0cd8a76fce31d9f2b289f04c6dec/rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", size = 342111 }, + { url = "https://files.pythonhosted.org/packages/7d/06/395a13bfaa8a28b302fb433fb285a67ce0ea2004959a027aea8f9c52bad4/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", size = 384286 }, + { url = "https://files.pythonhosted.org/packages/43/52/d8eeaffab047e6b7b7ef7f00d5ead074a07973968ffa2d5820fa131d7852/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e", size = 391739 }, + { url = "https://files.pythonhosted.org/packages/83/31/52dc4bde85c60b63719610ed6f6d61877effdb5113a72007679b786377b8/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", size = 427306 }, + { url = "https://files.pythonhosted.org/packages/70/d5/1bab8e389c2261dba1764e9e793ed6830a63f830fdbec581a242c7c46bda/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", size = 442717 }, + { url = "https://files.pythonhosted.org/packages/82/a1/a45f3e30835b553379b3a56ea6c4eb622cf11e72008229af840e4596a8ea/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", size = 385721 }, + { url = "https://files.pythonhosted.org/packages/a6/27/780c942de3120bdd4d0e69583f9c96e179dfff082f6ecbb46b8d6488841f/rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", size = 415824 }, + { url = "https://files.pythonhosted.org/packages/94/0b/aa0542ca88ad20ea719b06520f925bae348ea5c1fdf201b7e7202d20871d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", size = 561227 }, + { url = "https://files.pythonhosted.org/packages/0d/92/3ed77d215f82c8f844d7f98929d56cc321bb0bcfaf8f166559b8ec56e5f1/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", size = 587424 }, + { url = "https://files.pythonhosted.org/packages/09/42/cacaeb047a22cab6241f107644f230e2935d4efecf6488859a7dd82fc47d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", size = 555953 }, + { url = "https://files.pythonhosted.org/packages/e6/52/c921dc6d5f5d45b212a456c1f5b17df1a471127e8037eb0972379e39dff4/rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", size = 221339 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/f82b5be1e8456600395366f86104d1bd8d0faed3802ad511ef6d60c30d98/rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", size = 235786 }, + { url = "https://files.pythonhosted.org/packages/d0/bf/36d5cc1f2c609ae6e8bf0fc35949355ca9d8790eceb66e6385680c951e60/rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", size = 351657 }, + { url = "https://files.pythonhosted.org/packages/24/2a/f1e0fa124e300c26ea9382e59b2d582cba71cedd340f32d1447f4f29fa4e/rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", size = 341829 }, + { url = "https://files.pythonhosted.org/packages/cf/c2/0da1231dd16953845bed60d1a586fcd6b15ceaeb965f4d35cdc71f70f606/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", size = 384220 }, + { url = "https://files.pythonhosted.org/packages/c7/73/a4407f4e3a00a9d4b68c532bf2d873d6b562854a8eaff8faa6133b3588ec/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", size = 391009 }, + { url = "https://files.pythonhosted.org/packages/a9/c3/04b7353477ab360fe2563f5f0b176d2105982f97cd9ae80a9c5a18f1ae0f/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", size = 426989 }, + { url = "https://files.pythonhosted.org/packages/8d/e6/e4b85b722bcf11398e17d59c0f6049d19cd606d35363221951e6d625fcb0/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", size = 441544 }, + { url = "https://files.pythonhosted.org/packages/27/fc/403e65e56f65fff25f2973216974976d3f0a5c3f30e53758589b6dc9b79b/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", size = 385179 }, + { url = "https://files.pythonhosted.org/packages/57/9b/2be9ff9700d664d51fd96b33d6595791c496d2778cb0b2a634f048437a55/rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", size = 415103 }, + { url = "https://files.pythonhosted.org/packages/bb/a5/03c2ad8ca10994fcf22dd2150dd1d653bc974fa82d9a590494c84c10c641/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", size = 560916 }, + { url = "https://files.pythonhosted.org/packages/ba/2e/be4fdfc8b5b576e588782b56978c5b702c5a2307024120d8aeec1ab818f0/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", size = 587062 }, + { url = "https://files.pythonhosted.org/packages/67/e0/2034c221937709bf9c542603d25ad43a68b4b0a9a0c0b06a742f2756eb66/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", size = 555734 }, + { url = "https://files.pythonhosted.org/packages/ea/ce/240bae07b5401a22482b58e18cfbabaa392409b2797da60223cca10d7367/rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", size = 220663 }, + { url = "https://files.pythonhosted.org/packages/cb/f0/d330d08f51126330467edae2fa4efa5cec8923c87551a79299380fdea30d/rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", size = 235503 }, + { url = "https://files.pythonhosted.org/packages/f7/c4/dbe1cc03df013bf2feb5ad00615038050e7859f381e96fb5b7b4572cd814/rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", size = 347698 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/684f66dd6b0f37499cad24cd1c0e523541fd768576fa5ce2d0a8799c3cba/rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", size = 337330 }, + { url = "https://files.pythonhosted.org/packages/82/eb/e022c08c2ce2e8f7683baa313476492c0e2c1ca97227fe8a75d9f0181e95/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", size = 380022 }, + { url = "https://files.pythonhosted.org/packages/e4/21/5a80e653e4c86aeb28eb4fea4add1f72e1787a3299687a9187105c3ee966/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", size = 390754 }, + { url = "https://files.pythonhosted.org/packages/37/a4/d320a04ae90f72d080b3d74597074e62be0a8ecad7d7321312dfe2dc5a6a/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", size = 423840 }, + { url = "https://files.pythonhosted.org/packages/87/70/674dc47d93db30a6624279284e5631be4c3a12a0340e8e4f349153546728/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", size = 438970 }, + { url = "https://files.pythonhosted.org/packages/3f/64/9500f4d66601d55cadd21e90784cfd5d5f4560e129d72e4339823129171c/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", size = 383146 }, + { url = "https://files.pythonhosted.org/packages/4d/45/630327addb1d17173adcf4af01336fd0ee030c04798027dfcb50106001e0/rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", size = 408294 }, + { url = "https://files.pythonhosted.org/packages/5f/ef/8efb3373cee54ea9d9980b772e5690a0c9e9214045a4e7fa35046e399fee/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", size = 556345 }, + { url = "https://files.pythonhosted.org/packages/54/01/151d3b9ef4925fc8f15bfb131086c12ec3c3d6dd4a4f7589c335bf8e85ba/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", size = 582292 }, + { url = "https://files.pythonhosted.org/packages/30/89/35fc7a6cdf3477d441c7aca5e9bbf5a14e0f25152aed7f63f4e0b141045d/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", size = 553855 }, + { url = "https://files.pythonhosted.org/packages/8f/e0/830c02b2457c4bd20a8c5bb394d31d81f57fbefce2dbdd2e31feff4f7003/rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", size = 219100 }, + { url = "https://files.pythonhosted.org/packages/f8/30/7ac943f69855c2db77407ae363484b915d861702dbba1aa82d68d57f42be/rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", size = 233794 }, +] + +[[package]] +name = "ruff" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/2b/69e5e412f9d390adbdbcbf4f64d6914fa61b44b08839a6584655014fc524/ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5", size = 2449817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/eb/06e06aaf96af30a68e83b357b037008c54a2ddcbad4f989535007c700394/ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a", size = 9570571 }, + { url = "https://files.pythonhosted.org/packages/a4/10/1be32aeaab8728f78f673e7a47dd813222364479b2d6573dbcf0085e83ea/ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be", size = 8685138 }, + { url = "https://files.pythonhosted.org/packages/3d/1d/c218ce83beb4394ba04d05e9aa2ae6ce9fba8405688fe878b0fdb40ce855/ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e", size = 8266785 }, + { url = "https://files.pythonhosted.org/packages/26/79/7f49509bd844476235b40425756def366b227a9714191c91f02fb2178635/ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8", size = 9983964 }, + { url = "https://files.pythonhosted.org/packages/bf/b1/939836b70bf9fcd5e5cd3ea67fdb8abb9eac7631351d32f26544034a35e4/ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea", size = 9359490 }, + { url = "https://files.pythonhosted.org/packages/32/7d/b3db19207de105daad0c8b704b2c6f2a011f9c07017bd58d8d6e7b8eba19/ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc", size = 10170833 }, + { url = "https://files.pythonhosted.org/packages/a2/45/eae9da55f3357a1ac04220230b8b07800bf516e6dd7e1ad20a2ff3b03b1b/ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692", size = 10896360 }, + { url = "https://files.pythonhosted.org/packages/99/67/4388b36d145675f4c51ebec561fcd4298a0e2550c81e629116f83ce45a39/ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf", size = 10477094 }, + { url = "https://files.pythonhosted.org/packages/e1/9c/f5e6ed1751dc187a4ecf19a4970dd30a521c0ee66b7941c16e292a4043fb/ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb", size = 11480896 }, + { url = "https://files.pythonhosted.org/packages/c8/3b/2b683be597bbd02046678fc3fc1c199c641512b20212073b58f173822bb3/ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e", size = 10179702 }, + { url = "https://files.pythonhosted.org/packages/f1/38/c2d94054dc4b3d1ea4c2ba3439b2a7095f08d1c8184bc41e6abe2a688be7/ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499", size = 9982855 }, + { url = "https://files.pythonhosted.org/packages/7d/e7/1433db2da505ffa8912dcf5b28a8743012ee780cbc20ad0bf114787385d9/ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e", size = 9433156 }, + { url = "https://files.pythonhosted.org/packages/e0/36/4fa43250e67741edeea3d366f59a1dc993d4d89ad493a36cbaa9889895f2/ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5", size = 9782971 }, + { url = "https://files.pythonhosted.org/packages/80/0e/8c276103d518e5cf9202f70630aaa494abf6fc71c04d87c08b6d3cd07a4b/ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e", size = 10247775 }, + { url = "https://files.pythonhosted.org/packages/cb/b9/673096d61276f39291b729dddde23c831a5833d98048349835782688a0ec/ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a", size = 7841772 }, + { url = "https://files.pythonhosted.org/packages/67/1c/4520c98bfc06b9c73cd1457686d4d3935d40046b1ddea08403e5a6deff51/ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3", size = 8699779 }, + { url = "https://files.pythonhosted.org/packages/38/23/b3763a237d2523d40a31fe2d1a301191fe392dd48d3014977d079cf8c0bd/ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4", size = 8091891 }, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620 }, + { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234 }, + { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069 }, + { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809 }, + { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516 }, + { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728 }, + { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700 }, + { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613 }, + { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, + { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, + { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, + { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, + { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, + { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, + { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, + { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, +] + +[[package]] +name = "scipy" +version = "1.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/7b/9f265b7f074195392e893a5cdc66116c2f7a31fd5f3d9cceff661ec6df82/scipy-1.11.3.tar.gz", hash = "sha256:bba4d955f54edd61899776bad459bf7326e14b9fa1c552181f0479cc60a568cd", size = 56335652 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/a6/b6d66d4f4045ba59200d25f254ccd63340162c903f95231e3ae6863fc4ae/scipy-1.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:925c6f09d0053b1c0f90b2d92d03b261e889b20d1c9b08a3a51f61afc5f58165", size = 37156418 }, + { url = "https://files.pythonhosted.org/packages/50/8b/2057417a07a6fee8ed8be40e37bac4a502cae4cf44468a02962bbe81b8af/scipy-1.11.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5664e364f90be8219283eeb844323ff8cd79d7acbd64e15eb9c46b9bc7f6a42a", size = 29674269 }, + { url = "https://files.pythonhosted.org/packages/fd/bd/2905516155dbca3f5ae793caa1954a77369dec42d49ac3a60ea749acd3db/scipy-1.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f325434b6424952fbb636506f0567898dca7b0f7654d48f1c382ea338ce9a3", size = 32881937 }, + { url = "https://files.pythonhosted.org/packages/ef/1b/7538792254aec6850657d5b940fd05fe60582af829ffe40d6c054f065f34/scipy-1.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f290cf561a4b4edfe8d1001ee4be6da60c1c4ea712985b58bf6bc62badee221", size = 36401766 }, + { url = "https://files.pythonhosted.org/packages/36/95/69e32b4691daa6a6fe625d2e3724a39dc9b910c7860e739b583798d3d127/scipy-1.11.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:91770cb3b1e81ae19463b3c235bf1e0e330767dca9eb4cd73ba3ded6c4151e4d", size = 36621150 }, + { url = "https://files.pythonhosted.org/packages/81/d7/d2537d51efb692d0c411e64267ba349e7668d40f5bc73cefe78ccd650dcd/scipy-1.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1f97cd89c0fe1a0685f8f89d85fa305deb3067d0668151571ba50913e445820", size = 44095029 }, + { url = "https://files.pythonhosted.org/packages/e5/ee/c5bc0d4b66a9c38165adf86e8b57be6f76868edf5ea23b3bbee3680e7edf/scipy-1.11.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dfcc1552add7cb7c13fb70efcb2389d0624d571aaf2c80b04117e2755a0c5d15", size = 37051885 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/7e2c614d4c892e7fc9f44f4bf16a4661c7f9112f856c3a14f444e43a6ad4/scipy-1.11.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0d3a136ae1ff0883fffbb1b05b0b2fea251cb1046a5077d0b435a1839b3e52b7", size = 29643878 }, + { url = "https://files.pythonhosted.org/packages/85/61/0b1298353028ae5080674a02a44ea2514decb7c87664007cd1e0c4822522/scipy-1.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bae66a2d7d5768eaa33008fa5a974389f167183c87bf39160d3fefe6664f8ddc", size = 32244516 }, + { url = "https://files.pythonhosted.org/packages/c8/ae/c1e5e9f7ace48e299d7eb05a4b7f1ca0c98961659945a0270a735e5a045a/scipy-1.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2f6dee6cbb0e263b8142ed587bc93e3ed5e777f1f75448d24fb923d9fd4dce6", size = 35743498 }, + { url = "https://files.pythonhosted.org/packages/dc/b2/c58eb0e021696c46719f90ab970d0c034445e36180445c431feed3290871/scipy-1.11.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:74e89dc5e00201e71dd94f5f382ab1c6a9f3ff806c7d24e4e90928bb1aafb280", size = 35936106 }, + { url = "https://files.pythonhosted.org/packages/f4/ce/be0b376ba6069f3f8ba240aa532a374733447453c93582d4c474effdde21/scipy-1.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:90271dbde4be191522b3903fc97334e3956d7cfb9cce3f0718d0ab4fd7d8bfd6", size = 43734377 }, +] + +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, +] + +[[package]] +name = "setuptools" +version = "75.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, +] + +[[package]] +name = "shapely" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/c0/ef2c5eff1e8381710e211a063d0aa3e7215cea9e6fd8c31e75bf5f93df85/shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7", size = 279727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/70/8ac9c70da0e9428b8827953c7345a2f9f0e62adeccd9ca5a69425d693b8c/shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f", size = 2498345 }, + { url = "https://files.pythonhosted.org/packages/81/50/c7768a0a71c012464927228b6b949e55ad8d7ed50325b56b7224ea7dc7a6/shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88", size = 1431079 }, + { url = "https://files.pythonhosted.org/packages/2c/b1/ca09649b4abe06366d41e90c3eee95a7741657404404a63bd0e8b53e32b8/shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f", size = 1271111 }, + { url = "https://files.pythonhosted.org/packages/77/e6/5043c8c8b7e21922559b4faa9011566b0df9315c3d51f15fa07816b4409d/shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89", size = 2444184 }, + { url = "https://files.pythonhosted.org/packages/8c/47/05c8bb8322861113e72b903aebaaa4678ae6e44c886c189ad8fe297f2008/shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2", size = 2527925 }, + { url = "https://files.pythonhosted.org/packages/48/2a/6e590f4f13b1bd8ee39626bab5c1643b8746a4e469c61c5e38571f6cccd9/shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd", size = 1290073 }, + { url = "https://files.pythonhosted.org/packages/9e/39/029c441d8af32ab423b229c4525ce5ce6707318155b59634811a4c56f5c4/shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e", size = 1437690 }, + { url = "https://files.pythonhosted.org/packages/a7/e4/d0fdebc973ccfefe9feb8bca0995e0071c37aabb40448d9f170f94058a0b/shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c", size = 2498555 }, + { url = "https://files.pythonhosted.org/packages/cd/77/d36919327b4c6f5a92909ea194a1a4138bf0515bf0d6a5ca29f53cd0d879/shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2", size = 1430925 }, + { url = "https://files.pythonhosted.org/packages/5c/d9/d557c09d15406aad8252caeae181c2db2c562cc1b2ca6e6482b43274b25f/shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8", size = 1271480 }, + { url = "https://files.pythonhosted.org/packages/bc/e0/237b127f163737b3b4aa275943f4e75e56f50f0aa1c05fe997ddac6bd256/shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b", size = 2441270 }, + { url = "https://files.pythonhosted.org/packages/29/b7/754f971ebbcb747ef38147949f90b30d2232ee5b9af9daacd558cc1705e2/shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4", size = 2526036 }, + { url = "https://files.pythonhosted.org/packages/eb/5e/7aca7c3181f0bedbdb0931c825171b499c8a91b5aff667077efe81e75b4a/shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611", size = 1291039 }, + { url = "https://files.pythonhosted.org/packages/c7/d7/1bf4f48d83af09ce1af06c21752670fa8cdcafa72dbc452b809b215cda2a/shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033", size = 1438340 }, +] + +[[package]] +name = "simplejson" +version = "3.19.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/29/085111f19717f865eceaf0d4397bf3e76b08d60428b076b64e2a1903706d/simplejson-3.19.3.tar.gz", hash = "sha256:8e086896c36210ab6050f2f9f095a5f1e03c83fa0e7f296d6cba425411364680", size = 85237 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/bb/9ee3959e6929d228cf669b3f13f0edd43c5261b6cd69598640748b19ca35/simplejson-3.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e88abff510dcff903a18d11c2a75f9964e768d99c8d147839913886144b2065e", size = 91930 }, + { url = "https://files.pythonhosted.org/packages/ac/ae/a06523928af3a6783e2638cd4f6035c3e32de1c1063d563d9060c8d2f1ad/simplejson-3.19.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934a50a614fb831614db5dbfba35127ee277624dda4d15895c957d2f5d48610c", size = 74787 }, + { url = "https://files.pythonhosted.org/packages/c3/58/fea732e48a7540035fe46d39e6fd77679f5810311d31da8661ce7a18210a/simplejson-3.19.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:212fce86a22188b0c7f53533b0f693ea9605c1a0f02c84c475a30616f55a744d", size = 74612 }, + { url = "https://files.pythonhosted.org/packages/ab/4d/15718f20cb0e3875b8af9597d6bb3bfbcf1383834b82b6385ee9ac0b72a9/simplejson-3.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d9e8f836688a8fabe6a6b41b334aa550a6823f7b4ac3d3712fc0ad8655be9a8", size = 143550 }, + { url = "https://files.pythonhosted.org/packages/93/44/815a4343774760f7a82459c8f6a4d8268b4b6d23f81e7b922a5e2ca79171/simplejson-3.19.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23228037dc5d41c36666384062904d74409a62f52283d9858fa12f4c22cffad1", size = 153284 }, + { url = "https://files.pythonhosted.org/packages/9d/52/d3202d9bba95444090d1c98e43da3c10907875babf63ed3c134d1b9437e3/simplejson-3.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0791f64fed7d4abad639491f8a6b1ba56d3c604eb94b50f8697359b92d983f36", size = 141518 }, + { url = "https://files.pythonhosted.org/packages/b7/d4/850948bcbcfe0b4a6c69dfde10e245d3a1ea45252f16a1e2308a3b06b1da/simplejson-3.19.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f614581b61a26fbbba232a1391f6cee82bc26f2abbb6a0b44a9bba25c56a1c", size = 144688 }, + { url = "https://files.pythonhosted.org/packages/58/d2/b8dcb0a07d9cd54c47f9fe8733dbb83891d1efe4fc786d9dfc8781cc04f9/simplejson-3.19.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1df0aaf1cb787fdf34484ed4a1f0c545efd8811f6028623290fef1a53694e597", size = 144534 }, + { url = "https://files.pythonhosted.org/packages/a9/95/1e92d99039041f596e0923ec4f9153244acaf3830944dc69a7c11b23ceaa/simplejson-3.19.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:951095be8d4451a7182403354c22ec2de3e513e0cc40408b689af08d02611588", size = 146565 }, + { url = "https://files.pythonhosted.org/packages/21/04/c96aeb3a74031255e4cbcc0ca1b6ebfb5549902f0a065f06d65ce8447c0c/simplejson-3.19.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a954b30810988feeabde843e3263bf187697e0eb5037396276db3612434049b", size = 155014 }, + { url = "https://files.pythonhosted.org/packages/b7/41/e28a28593afc4a75d8999d057bfb7c73a103e35f927e66f4bb92571787ae/simplejson-3.19.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c40df31a75de98db2cdfead6074d4449cd009e79f54c1ebe5e5f1f153c68ad20", size = 148092 }, + { url = "https://files.pythonhosted.org/packages/2b/82/1c81a3af06f937afb6d2e9d74a465c0e0ae6db444d1bf2a436ea26de1965/simplejson-3.19.3-cp311-cp311-win32.whl", hash = "sha256:7e2a098c21ad8924076a12b6c178965d88a0ad75d1de67e1afa0a66878f277a5", size = 73942 }, + { url = "https://files.pythonhosted.org/packages/65/be/d8ab9717f471be3c114f16abd8be21d9a6a0a09b9b49177d93d64d3717d9/simplejson-3.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:c9bedebdc5fdad48af8783022bae307746d54006b783007d1d3c38e10872a2c6", size = 75469 }, + { url = "https://files.pythonhosted.org/packages/20/15/513fea93fafbdd4993eacfcb762965b2ff3d29e618c029e2956174d68c4b/simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99", size = 92921 }, + { url = "https://files.pythonhosted.org/packages/a4/4f/998a907ae1a6c104dc0ee48aa248c2478490152808d34d8e07af57f396c3/simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333", size = 75311 }, + { url = "https://files.pythonhosted.org/packages/db/44/acd6122201e927451869d45952b9ab1d3025cdb5e61548d286d08fbccc08/simplejson-3.19.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42e5acf80d4d971238d4df97811286a044d720693092b20a56d5e56b7dcc5d09", size = 74964 }, + { url = "https://files.pythonhosted.org/packages/27/ca/d0a1e8f16e1bbdc0b8c6d88166f45f565ed7285f53928cfef3b6ce78f14d/simplejson-3.19.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b0efc7279d768db7c74d3d07f0b5c81280d16ae3fb14e9081dc903e8360771", size = 150106 }, + { url = "https://files.pythonhosted.org/packages/63/59/0554b78cf26c98e2b9cae3f44723bd72c2394e2afec1a14eedc6211f7187/simplejson-3.19.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0552eb06e7234da892e1d02365cd2b7b2b1f8233aa5aabdb2981587b7cc92ea0", size = 158347 }, + { url = "https://files.pythonhosted.org/packages/b2/fe/9f30890352e431e8508cc569912d3322147d3e7e4f321e48c0adfcb4c97d/simplejson-3.19.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf6a3b9a7d7191471b464fe38f684df10eb491ec9ea454003edb45a011ab187", size = 148456 }, + { url = "https://files.pythonhosted.org/packages/37/e3/663a09542ee021d4131162f7a164cb2e7f04ef48433a67591738afbf12ea/simplejson-3.19.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7017329ca8d4dca94ad5e59f496e5fc77630aecfc39df381ffc1d37fb6b25832", size = 152190 }, + { url = "https://files.pythonhosted.org/packages/31/20/4e0c4d35e10ff6465003bec304316d822a559a1c38c66ef6892ca199c207/simplejson-3.19.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67a20641afebf4cfbcff50061f07daad1eace6e7b31d7622b6fa2c40d43900ba", size = 149846 }, + { url = "https://files.pythonhosted.org/packages/08/7a/46e2e072cac3987cbb05946f25167f0ad2fe536748e7405953fd6661a486/simplejson-3.19.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd6a7dabcc4c32daf601bc45e01b79175dde4b52548becea4f9545b0a4428169", size = 151714 }, + { url = "https://files.pythonhosted.org/packages/7f/7d/dbeeac10eb61d5d8858d0bb51121a21050d281dc83af4c557f86da28746c/simplejson-3.19.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08f9b443a94e72dd02c87098c96886d35790e79e46b24e67accafbf13b73d43b", size = 158777 }, + { url = "https://files.pythonhosted.org/packages/fc/8f/a98bdbb799c6a4a884b5823db31785a96ba895b4b0f4d8ac345d6fe98bbf/simplejson-3.19.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa97278ae6614346b5ca41a45a911f37a3261b57dbe4a00602048652c862c28b", size = 154230 }, + { url = "https://files.pythonhosted.org/packages/b1/db/852eebceb85f969ae40e06babed1a93d3bacb536f187d7a80ff5823a5979/simplejson-3.19.3-cp312-cp312-win32.whl", hash = "sha256:ef28c3b328d29b5e2756903aed888960bc5df39b4c2eab157ae212f70ed5bf74", size = 74002 }, + { url = "https://files.pythonhosted.org/packages/fe/68/9f0e5df0651cb79ef83cba1378765a00ee8038e6201cc82b8e7178a7778e/simplejson-3.19.3-cp312-cp312-win_amd64.whl", hash = "sha256:1e662336db50ad665777e6548b5076329a94a0c3d4a0472971c588b3ef27de3a", size = 75596 }, + { url = "https://files.pythonhosted.org/packages/93/3a/5896821ed543899fcb9c4256c7e71bb110048047349a00f42bc8b8fb379f/simplejson-3.19.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0959e6cb62e3994b5a40e31047ff97ef5c4138875fae31659bead691bed55896", size = 92931 }, + { url = "https://files.pythonhosted.org/packages/39/15/5d33d269440912ee40d856db0c8be2b91aba7a219690ab01f86cb0edd590/simplejson-3.19.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a7bfad839c624e139a4863007233a3f194e7c51551081f9789cba52e4da5167", size = 75318 }, + { url = "https://files.pythonhosted.org/packages/2a/8d/2e7483a2bf7ec53acf7e012bafbda79d7b34f90471dda8e424544a59d484/simplejson-3.19.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afab2f7f2486a866ff04d6d905e9386ca6a231379181a3838abce1f32fbdcc37", size = 74971 }, + { url = "https://files.pythonhosted.org/packages/4d/9d/9bdf34437c8834a7cf7246f85e9d5122e30579f512c10a0c2560e994294f/simplejson-3.19.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00313681015ac498e1736b304446ee6d1c72c5b287cd196996dad84369998f7", size = 150112 }, + { url = "https://files.pythonhosted.org/packages/a7/e2/1f2ae2d89eaf85f6163c82150180aae5eaa18085cfaf892f8a57d4c51cbd/simplejson-3.19.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d936ae682d5b878af9d9eb4d8bb1fdd5e41275c8eb59ceddb0aeed857bb264a2", size = 158354 }, + { url = "https://files.pythonhosted.org/packages/60/83/26f610adf234c8492b3f30501e12f2271e67790f946c6898fe0c58aefe99/simplejson-3.19.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c6657485393f2e9b8177c77a7634f13ebe70d5e6de150aae1677d91516ce6b", size = 148455 }, + { url = "https://files.pythonhosted.org/packages/b5/4b/109af50006af77133653c55b5b91b4bd2d579ff8254ce11216c0b75f911b/simplejson-3.19.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a6a750d3c7461b1c47cfc6bba8d9e57a455e7c5f80057d2a82f738040dd1129", size = 152191 }, + { url = "https://files.pythonhosted.org/packages/75/dc/108872a8825cbd99ae6f4334e0490ff1580367baf12198bcaf988f6820ba/simplejson-3.19.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea7a4a998c87c5674a27089e022110a1a08a7753f21af3baf09efe9915c23c3c", size = 149954 }, + { url = "https://files.pythonhosted.org/packages/eb/be/deec1d947a5d0472276ab4a4d1a9378dc5ee27f3dc9e54d4f62ffbad7a08/simplejson-3.19.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6300680d83a399be2b8f3b0ef7ef90b35d2a29fe6e9c21438097e0938bbc1564", size = 151812 }, + { url = "https://files.pythonhosted.org/packages/e9/58/4ee130702d36b1551ef66e7587eefe56651f3669255bf748cd71691e2434/simplejson-3.19.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ab69f811a660c362651ae395eba8ce84f84c944cea0df5718ea0ba9d1e4e7252", size = 158880 }, + { url = "https://files.pythonhosted.org/packages/0f/e1/59cc6a371b60f89e3498d9f4c8109f6b7359094d453f5fe80b2677b777b0/simplejson-3.19.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:256e09d0f94d9c3d177d9e95fd27a68c875a4baa2046633df387b86b652f5747", size = 154344 }, + { url = "https://files.pythonhosted.org/packages/79/45/1b36044670016f5cb25ebd92497427d2d1711ecb454d00f71eb9a00b77cc/simplejson-3.19.3-cp313-cp313-win32.whl", hash = "sha256:2c78293470313aefa9cfc5e3f75ca0635721fb016fb1121c1c5b0cb8cc74712a", size = 74002 }, + { url = "https://files.pythonhosted.org/packages/e2/58/b06226e6b0612f2b1fa13d5273551da259f894566b1eef32249ddfdcce44/simplejson-3.19.3-cp313-cp313-win_amd64.whl", hash = "sha256:3bbcdc438dc1683b35f7a8dc100960c721f922f9ede8127f63bed7dfded4c64c", size = 75599 }, + { url = "https://files.pythonhosted.org/packages/0d/e7/f9fafbd4f39793a20cc52e77bbd766f7384312526d402c382928dc7667f6/simplejson-3.19.3-py3-none-any.whl", hash = "sha256:49cc4c7b940d43bd12bf87ec63f28cbc4964fc4e12c031cc8cd01650f43eb94e", size = 57004 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smart-open" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/30/1f41c3d3b8cec82024b4b277bfd4e5b18b765ae7279eb9871fa25c503778/smart_open-7.1.0.tar.gz", hash = "sha256:a4f09f84f0f6d3637c6543aca7b5487438877a21360e7368ccf1f704789752ba", size = 72044 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/18/9a8d9f01957aa1f8bbc5676d54c2e33102d247e146c1a3679d3bd5cc2e3a/smart_open-7.1.0-py3-none-any.whl", hash = "sha256:4b8489bb6058196258bafe901730c7db0dcf4f083f316e97269c66f45502055b", size = 61746 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "snakemake" +version = "7.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appdirs" }, + { name = "configargparse" }, + { name = "connection-pool" }, + { name = "datrie" }, + { name = "docutils" }, + { name = "gitpython" }, + { name = "humanfriendly" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pulp" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "reretry" }, + { name = "smart-open" }, + { name = "stopit" }, + { name = "tabulate" }, + { name = "throttler" }, + { name = "toposort" }, + { name = "wrapt" }, + { name = "yte" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/94/884160dab89886cef7802df0a8c8217bfb2d795427dee01ad0e0dc15964a/snakemake-7.32.4.tar.gz", hash = "sha256:fdc3f15dd7b06fabb7da30d460e0a3b1fba08e4ea91f9c32c47a83705cdc7b6e", size = 371171 } + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "snuggs" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/19/0d11ab370735dde61076a0e41644e5593821776e69e3b0344626cfa0e56a/snuggs-1.4.7.tar.gz", hash = "sha256:501cf113fe3892e14e2fee76da5cd0606b7e149c411c271898e6259ebde2617b", size = 8196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/0e/d27d6e806d6c0d1a2cfdc5d1f088e42339a0a54a09c3343f7f81ec8947ea/snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07", size = 5370 }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, +] + +[[package]] +name = "sphinx-autobuild" +version = "2024.10.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "sphinx" }, + { name = "starlette" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 }, +] + +[[package]] +name = "sphinx-book-theme" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/69/61dfa3b3851610b5f45960737bd99f8c5b2d70ba73f9ac84a527e0c564ae/sphinx_book_theme-1.1.3.tar.gz", hash = "sha256:1f25483b1846cb3d353a6bc61b3b45b031f4acf845665d7da90e01ae0aef5b4d", size = 434230 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/80/90574e2e82c955b9c6f6b77f7badb2cf2ef4ef77599e4343cced2d098681/sphinx_book_theme-1.1.3-py3-none-any.whl", hash = "sha256:a554a9a7ac3881979a87a2b10f633aa2a5706e72218a10f71be38b3c9e831ae9", size = 430129 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "starlette" +version = "0.45.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, +] + +[[package]] +name = "stopit" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/58/e8bb0b0fb05baf07bbac1450c447d753da65f9701f551dca79823ce15d50/stopit-1.1.2.tar.gz", hash = "sha256:f7f39c583fd92027bd9d06127b259aee7a5b7945c1f1fa56263811e1e766996d", size = 18281 } + +[[package]] +name = "tables" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blosc2" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "py-cpuinfo" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/50/23ead25f60bb1babe7f2f061d8a2f8c2f6804c1a20b3058677beb9085b56/tables-3.10.2.tar.gz", hash = "sha256:2544812a7186fadba831d6dd34eb49ccd788d6a83f4e4c2b431b835b6796c910", size = 4779722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/f6/ef0c376c1fa01b916d5db0c2681be063f6289ee99faf7bb6610e0b55b773/tables-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63f8adec3c4421a011c5c6a245c0c1fccf16dba7aaa67d9915d2821cf365ed4a", size = 6767194 }, + { url = "https://files.pythonhosted.org/packages/d9/d0/accd41382fa9da45bf816c56f85bda64223a3b8d0006d3496b67e0781a6e/tables-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c120bff666d33d3bdfb9e33173a4869d5f34e6c87824f2c7ec6a72c8dfab82", size = 5482665 }, + { url = "https://files.pythonhosted.org/packages/59/2f/c95e94423c463177b8a7d55a1dbbd524840fe6a684844ff728f238e71f68/tables-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e71f63ac67c583ac42943c99c2d33bcc9e361e94d1ab1a763dc0698bdd9ff815", size = 7117696 }, + { url = "https://files.pythonhosted.org/packages/88/d5/71665919aa2a5a3d2a20eeef3c71dc7c2ebbd9f26d114a7808514aba24d6/tables-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154773f97763ccc91a29bcead6ab7b5ef164c2ed8c409cd79a2115aa9b4184c9", size = 7520921 }, + { url = "https://files.pythonhosted.org/packages/46/96/b5023c1f7b9d560cac3e2c0daceebaeb88dd24c70c75db2d291abfa563e5/tables-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:96b5e945d275415e79ddb0578657ecc6ac77030dcc0632ab2c39f89390bb239d", size = 6407137 }, + { url = "https://files.pythonhosted.org/packages/ab/c4/1efbcc699db863d88874f3d111e5bb6dd2e0fbaca38f91c992e696324730/tables-3.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6ba58205d1f6a4e0e2212bc221e76cf104f22190f90c3f1683f3c1ab138f28f", size = 6734990 }, + { url = "https://files.pythonhosted.org/packages/4a/db/4c7facfc805ab764f2ee256011d20f96791d2426afa3389ca7ff2a8a4ea8/tables-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdb5c040aa43e5e96259d6f6bb9df5b66fef2b071a6eb035c21bf6508e865d40", size = 5483377 }, + { url = "https://files.pythonhosted.org/packages/93/0a/53815b516a2465b329e5dc2079c99a8b6b1a23f6b9ce5da8a7ebc7892bf4/tables-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e694123fa886d9be57f55fc7e1dcacac49f0b4ed4a931c795bd8f82f7111b5a8", size = 7081356 }, + { url = "https://files.pythonhosted.org/packages/d3/e1/3f4adfc83eb7390abb964682a7d1df0dbe451dd2cee99750b1c7ca8e2c9d/tables-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c12d0d04de89297763923ebeaddfd7e0b51f29041895db284fd4913e7448b7", size = 7483570 }, + { url = "https://files.pythonhosted.org/packages/9a/d4/0b9ba57a5a8d2d05d1108055a8d70a4b066db4ebed61921de34043a31bdb/tables-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:a406d5dbbcb6604bd1ca129af337e0790d4e02d29d06159ddb9f74e38d756d32", size = 6388443 }, + { url = "https://files.pythonhosted.org/packages/ab/02/8c7aeaa6c8aac8e0298d40dc5fc55477fddc30cb31e4dc7e5e473be4b464/tables-3.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7b8bc07c715bad3d447ed8f834388ef2e10265e2c4af6b1297fc61adb645948f", size = 6725764 }, + { url = "https://files.pythonhosted.org/packages/91/f4/8683395d294b9e4576fd7d888aa6cf5583c013c2c0a2e47f862c2842407f/tables-3.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28677ed8e1a371471495599078f48da0850f82457d6c852ca77959c974371140", size = 5442663 }, + { url = "https://files.pythonhosted.org/packages/72/9b/ea43159eed8f81bfa1ead8fa8201a3c352e84c7220e046bb548736833951/tables-3.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaaea478dcf27dd54679ef2643c26d3b8b15676ad81e4d80a88fd1682d23deb1", size = 7078747 }, + { url = "https://files.pythonhosted.org/packages/04/95/b3e88edc674e35d9011b168df0d7a9b1c3ab98733fa26e740ac7964edc2f/tables-3.10.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e67a9f901842f9a4b1f3d2307f4bdd94047514fe0d0c558ed19c11f53c402a", size = 7479985 }, + { url = "https://files.pythonhosted.org/packages/63/ca/eaa029a43d269bdda6985931d6cfd479e876cd8cf7c887d818bef05ef03b/tables-3.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:5637fdcded5ba5426aa24e0e42d6f990926a4da7f193830df131dfcb7e842900", size = 6385562 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "tblib" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/df/4f2cd7eaa6d41a7994d46527349569d46e34d9cdd07590b5c5b0dcf53de3/tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6", size = 30616 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/87/ce70db7cae60e67851eb94e1a2127d4abb573d3866d2efd302ceb0d4d2a5/tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", size = 12478 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, +] + +[[package]] +name = "throttler" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/22/638451122136d5280bc477c8075ea448b9ebdfbd319f0f120edaecea2038/throttler-1.2.2.tar.gz", hash = "sha256:d54db406d98e1b54d18a9ba2b31ab9f093ac64a0a59d730c1cf7bb1cdfc94a58", size = 7970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/d4/36bf6010b184286000b2334622bfb3446a40c22c1d2a9776bff025cb0fe5/throttler-1.2.2-py3-none-any.whl", hash = "sha256:fc6ae612a2529e01110b32335af40375258b98e3b81232ec77cd07f51bf71392", size = 7609 }, +] + +[[package]] +name = "toolz" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383 }, +] + +[[package]] +name = "toposort" +version = "1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/19/8e955d90985ecbd3b9adb2a759753a6840da2dff3c569d412b2c9217678b/toposort-1.10.tar.gz", hash = "sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd", size = 11132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/17/57b444fd314d5e1593350b9a31d000e7411ba8e17ce12dc7ad54ca76b810/toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87", size = 8500 }, +] + +[[package]] +name = "tornado" +version = "6.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, + { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, + { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, + { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, + { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, + { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, + { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, + { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, + { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, + { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "tsam" +version = "2.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "highspy" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyomo" }, + { name = "scikit-learn" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/25/2c7c1cc58c1c09ebd2b5b55878e5e1ae4dcf43fad03f6f926b70f3f9fd17/tsam-2.3.6.tar.gz", hash = "sha256:f2f75cbcd5d5d63d9d77ec5edb61e7efeb3170ebef4591272ef13a7bd84cc9d4", size = 223586 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/e9/d1ddff8471ba389caf862867f60718b2843010ec8ba8693c642569012b3d/tsam-2.3.6-py3-none-any.whl", hash = "sha256:2d4ff20e1fcc5e061fbbcf772b51dc3d44dd68198f5a40eca7e0422c70cffe95", size = 36617 }, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20241230" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/f9/4d566925bcf9396136c0a2e5dc7e230ff08d86fa011a69888dd184469d80/types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c", size = 17078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/c1/48474fbead512b70ccdb4f81ba5eb4a58f69d100ba19f17c92c0c4f50ae6/types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6", size = 20029 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "urwid" +version = "2.6.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/21/ad23c9e961b2d36d57c63686a6f86768dd945d406323fb58c84f09478530/urwid-2.6.16.tar.gz", hash = "sha256:93ad239939e44c385e64aa00027878b9e5c486d59e855ec8ab5b1e1adcdb32a2", size = 848179 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/cb/271a4f5a1bf4208dbdc96d85b9eae744cf4e5e11ac73eda76dc98c8fd2d7/urwid-2.6.16-py3-none-any.whl", hash = "sha256:de14896c6df9eb759ed1fd93e0384a5279e51e0dde8f621e4083f7a8368c0797", size = 297196 }, +] + +[[package]] +name = "urwid-readline" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urwid" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/70/be318554495555eba7d8ff6e489f6f74ddb225b24086ba4af62a82e723fd/urwid_readline-0.15.1.tar.gz", hash = "sha256:9301444b86d58f7d26388506b704f142cefd193888488b4070d3a0fdfcfc0f84", size = 9007 } + +[[package]] +name = "uvicorn" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, +] + +[[package]] +name = "validators" +version = "0.34.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/07/91582d69320f6f6daaf2d8072608a4ad8884683d4840e7e4f3a9dbdcc639/validators-0.34.0.tar.gz", hash = "sha256:647fe407b45af9a74d245b943b18e6a816acf4926974278f6dd617778e1e781f", size = 70955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/78/36828a4d857b25896f9774c875714ba4e9b3bc8a92d2debe3f4df3a83d4f/validators-0.34.0-py3-none-any.whl", hash = "sha256:c804b476e3e6d3786fa07a30073a4ef694e617805eb1946ceee3fe5a9b8b1321", size = 43536 }, +] + +[[package]] +name = "virtualenv" +version = "20.29.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 }, +] + +[[package]] +name = "watchfiles" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/bb/8461adc4b1fed009546fb797fc0d5698dcfe5e289cb37e1b8f16a93cdc30/watchfiles-1.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2a9f93f8439639dc244c4d2902abe35b0279102bca7bbcf119af964f51d53c19", size = 394869 }, + { url = "https://files.pythonhosted.org/packages/55/88/9ebf36b3547176d1709c320de78c1fa3263a46be31b5b1267571d9102686/watchfiles-1.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eea33ad8c418847dd296e61eb683cae1c63329b6d854aefcd412e12d94ee235", size = 384905 }, + { url = "https://files.pythonhosted.org/packages/03/8a/04335ce23ef78d8c69f0913e8b20cf7d9233e3986543aeef95ef2d6e43d2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31f1a379c9dcbb3f09cf6be1b7e83b67c0e9faabed0471556d9438a4a4e14202", size = 449944 }, + { url = "https://files.pythonhosted.org/packages/17/4e/c8d5dcd14fe637f4633616dabea8a4af0a10142dccf3b43e0f081ba81ab4/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab594e75644421ae0a2484554832ca5895f8cab5ab62de30a1a57db460ce06c6", size = 456020 }, + { url = "https://files.pythonhosted.org/packages/5e/74/3e91e09e1861dd7fbb1190ce7bd786700dc0fbc2ccd33bb9fff5de039229/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc2eb5d14a8e0d5df7b36288979176fbb39672d45184fc4b1c004d7c3ce29317", size = 482983 }, + { url = "https://files.pythonhosted.org/packages/a1/3d/e64de2d1ce4eb6a574fd78ce3a28c279da263be9ef3cfcab6f708df192f2/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f68d8e9d5a321163ddacebe97091000955a1b74cd43724e346056030b0bacee", size = 520320 }, + { url = "https://files.pythonhosted.org/packages/2c/bd/52235f7063b57240c66a991696ed27e2a18bd6fcec8a1ea5a040b70d0611/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9ce064e81fe79faa925ff03b9f4c1a98b0bbb4a1b8c1b015afa93030cb21a49", size = 500988 }, + { url = "https://files.pythonhosted.org/packages/3a/b0/ff04194141a5fe650c150400dd9e42667916bc0f52426e2e174d779b8a74/watchfiles-1.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b77d5622ac5cc91d21ae9c2b284b5d5c51085a0bdb7b518dba263d0af006132c", size = 452573 }, + { url = "https://files.pythonhosted.org/packages/3d/9d/966164332c5a178444ae6d165082d4f351bd56afd9c3ec828eecbf190e6a/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1941b4e39de9b38b868a69b911df5e89dc43767feeda667b40ae032522b9b5f1", size = 615114 }, + { url = "https://files.pythonhosted.org/packages/94/df/f569ae4c1877f96ad4086c153a8eee5a19a3b519487bf5c9454a3438c341/watchfiles-1.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f8c4998506241dedf59613082d1c18b836e26ef2a4caecad0ec41e2a15e4226", size = 613076 }, + { url = "https://files.pythonhosted.org/packages/15/ae/8ce5f29e65d5fa5790e3c80c289819c55e12be2e1b9f5b6a0e55e169b97d/watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105", size = 271013 }, + { url = "https://files.pythonhosted.org/packages/a4/c6/79dc4a7c598a978e5fafa135090aaf7bbb03b8dec7bada437dfbe578e7ed/watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74", size = 284229 }, + { url = "https://files.pythonhosted.org/packages/37/3d/928633723211753f3500bfb138434f080363b87a1b08ca188b1ce54d1e05/watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3", size = 276824 }, + { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 }, + { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 }, + { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 }, + { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 }, + { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 }, + { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 }, + { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 }, + { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 }, + { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 }, + { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 }, + { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 }, + { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 }, + { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 }, + { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 }, + { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 }, + { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 }, + { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 }, + { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 }, + { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 }, + { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 }, + { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 }, + { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 }, + { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 }, + { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "websockets" +version = "14.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b6/504695fb9a33df0ca56d157f5985660b5fc5b4bf8c78f121578d2d653392/websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166", size = 163088 }, + { url = "https://files.pythonhosted.org/packages/81/26/ebfb8f6abe963c795122439c6433c4ae1e061aaedfc7eff32d09394afbae/websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f", size = 160745 }, + { url = "https://files.pythonhosted.org/packages/a1/c6/1435ad6f6dcbff80bb95e8986704c3174da8866ddb751184046f5c139ef6/websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910", size = 160995 }, + { url = "https://files.pythonhosted.org/packages/96/63/900c27cfe8be1a1f2433fc77cd46771cf26ba57e6bdc7cf9e63644a61863/websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c", size = 170543 }, + { url = "https://files.pythonhosted.org/packages/00/8b/bec2bdba92af0762d42d4410593c1d7d28e9bfd952c97a3729df603dc6ea/websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473", size = 169546 }, + { url = "https://files.pythonhosted.org/packages/6b/a9/37531cb5b994f12a57dec3da2200ef7aadffef82d888a4c29a0d781568e4/websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473", size = 169911 }, + { url = "https://files.pythonhosted.org/packages/60/d5/a6eadba2ed9f7e65d677fec539ab14a9b83de2b484ab5fe15d3d6d208c28/websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56", size = 170183 }, + { url = "https://files.pythonhosted.org/packages/76/57/a338ccb00d1df881c1d1ee1f2a20c9c1b5b29b51e9e0191ee515d254fea6/websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142", size = 169623 }, + { url = "https://files.pythonhosted.org/packages/64/22/e5f7c33db0cb2c1d03b79fd60d189a1da044e2661f5fd01d629451e1db89/websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d", size = 169583 }, + { url = "https://files.pythonhosted.org/packages/aa/2e/2b4662237060063a22e5fc40d46300a07142afe30302b634b4eebd717c07/websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a", size = 163969 }, + { url = "https://files.pythonhosted.org/packages/94/a5/0cda64e1851e73fc1ecdae6f42487babb06e55cb2f0dc8904b81d8ef6857/websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b", size = 164408 }, + { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 }, + { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 }, + { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 }, + { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 }, + { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 }, + { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 }, + { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 }, + { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 }, + { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 }, + { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 }, + { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 }, + { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 }, + { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 }, + { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 }, + { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 }, + { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 }, + { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 }, + { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 }, + { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 }, + { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 }, + { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 }, + { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 }, + { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "xarray" +version = "2024.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/d3/ae7a92c8448c40cd43f97fff93b1a57f87565b412fdc02eb14af5d4c3823/xarray-2024.9.0.tar.gz", hash = "sha256:e796a6b3eaec11da24f33e4bb14af41897011660a0516fa4037d3ae4bbd1d378", size = 3747432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/28/3a6365e45721c7c9078968ed94b4a60076bc31d73b8519021a69b4995b63/xarray-2024.9.0-py3-none-any.whl", hash = "sha256:4fd534abdf12d5fa75dd566c56483d5081f77864462cf3d6ad53e13f9db48222", size = 1191607 }, +] + +[[package]] +name = "xlrd" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/b3/19a2540d21dea5f908304375bd43f5ed7a4c28a370dc9122c565423e6b44/xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88", size = 100259 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/0c/c2a72d51fe56e08a08acc85d13013558a2d793028ae7385448a6ccdfae64/xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", size = 96531 }, +] + +[[package]] +name = "yte" +version = "1.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dpath" }, + { name = "plac" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/6173085d15e5cf55884b2706ab3f35d38a0680fc45c5b9e33b40c4a71bdb/yte-1.5.7.tar.gz", hash = "sha256:1e22a74e7c4d1aa70c54fe79d23938cb249d08c0804ad764ab97d5c587cbbad2", size = 6388 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/ba/f9d2417e72a8e50e2c749edf8b96fbe3c423be17b39cc2648154b3184fd3/yte-1.5.7-py3-none-any.whl", hash = "sha256:a66118fbe236bcf293fc920473ad19eb20f801172f0cd43764d3c283ceee6452", size = 7830 }, +] + +[[package]] +name = "zict" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ac/3c494dd7ec5122cff8252c1a209b282c0867af029f805ae9befd73ae37eb/zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5", size = 33238 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/ab/11a76c1e2126084fde2639514f24e6111b789b0bfa4fc6264a8975c7e1f1/zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", size = 43332 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] From a1948c1ec0cd279505a4f1ae418a3c03f2556e9a Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:50:17 -0800 Subject: [PATCH 46/75] update docs for UV --- docs/source/about-install.md | 19 ++++++++++++++++++- docs/source/about-usage.md | 9 +++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/source/about-install.md b/docs/source/about-install.md index fd3bc834e..d7fad8ae0 100644 --- a/docs/source/about-install.md +++ b/docs/source/about-install.md @@ -27,7 +27,21 @@ templates into the `workflow/config` folder. bash init_pypsa_usa.sh ``` -## Step 3: Create Mamba Environment +## Step 3: Set-up Environment (mamba or UV) + +### `uv` installation + +UV is a new python package managment tool from the creators of mamba. It replaces mamba, conda, and pip commands for one package and virtual environment managment tool. Instructions for installing [uv](https://docs.astral.sh/uv/getting-started/installation/). + +with UV installed, you can create and sync a new environment with: + +```console +uv venv +source .venv/bin/activate +uv pip sync pyproject.toml +``` + +### `mamba` Installation PyPSA-USA uses conda/mamba to manage project dependencies. You can download and install mamba following the [instructions](https://mamba.readthedocs.io/en/latest/mamba-installation.html). Follow links for mambaforge installation. There are two ways to install mamba, the first (recommended) method will start with a fresh install, meaning if you have previously installed conda environments, you will need to recreate these conda envs. If you already have conda installed and do not wish to install mamba, you can follow the same set of instructions replacing any `mamba` with `conda` @@ -40,6 +54,9 @@ mamba activate pypsa-usa You also have the option to use miniconda. Download [Miniconda](https://docs.conda.io/en/latest/miniconda.html) following their [instructions](https://docs.conda.io/en/latest/miniconda.html). + + + ```{seealso} If you are planning to develop PyPSA-USA, please see our [contribution guidelines](./contributing.md#code-contributions) for installing additional dependencies ``` diff --git a/docs/source/about-usage.md b/docs/source/about-usage.md index 74512aa06..782d3d74e 100644 --- a/docs/source/about-usage.md +++ b/docs/source/about-usage.md @@ -16,11 +16,16 @@ You can find more information on each configuration setting on the [configuratio To run the workflow, `cd` into the `workflow` directory and run the `snakemake` from your terminal with your selection of config file: +UV: ```console -snakemake -j1 --configfile config/config.default.yaml +uv run snakemake -j1 --configfile config/config.default.yaml --scheduler-ilp-solver GUROBI_CMD ``` -where 1 indicates the number of cores used. +mamba: +```console +mamba activate pypsa-usa +snakemake -j1 --configfile config/config.default.yaml +``` ## Running on HPC Cluster From a95c11998e08bc67f4b64214da8683fcf1ad1144 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 19:55:10 -0800 Subject: [PATCH 47/75] fix bug from refactor --- workflow/scripts/add_electricity.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/workflow/scripts/add_electricity.py b/workflow/scripts/add_electricity.py index 55ef70b98..f4c840504 100755 --- a/workflow/scripts/add_electricity.py +++ b/workflow/scripts/add_electricity.py @@ -901,8 +901,6 @@ def main(snakemake): regions_offshore = gpd.read_file(snakemake.input.regions_offshore) reeds_shapes = gpd.read_file(snakemake.input.reeds_shapes) - num_years = n.snapshot_weightings.loc[n.investment_periods[0]].objective.sum() / 8760.0 - costs = pd.read_csv(snakemake.input.tech_costs) costs = costs.pivot(index="pypsa-name", columns="parameter", values="value") update_transmission_costs(n, costs, params.length_factor) @@ -1005,7 +1003,7 @@ def main(snakemake): multiplier_file = snakemake.input[f"gen_cost_mult_{multiplier_data}"] df_multiplier = pd.read_csv(multiplier_file) df_multiplier = clean_locational_multiplier(df_multiplier) - update_capital_costs(n, carrier, costs, df_multiplier, num_years) + update_capital_costs(n, carrier, costs, df_multiplier) if params.conventional["dynamic_fuel_price"].get("enable", False): logger.info("Applying dynamic fuel pricing to conventional generators") From 614c7a5e81d0993887d9156c4dc1224c0c5c3eed Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Mon, 10 Feb 2025 20:23:24 -0800 Subject: [PATCH 48/75] bug fix from refactor --- workflow/scripts/add_extra_components.py | 3 +-- workflow/scripts/solve_network.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index b0148b0aa..54b55f8fb 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -678,8 +678,7 @@ def apply_max_annual_growth_rate(n, max_growth): [car for car in elec_config["extendable_carriers"]["Generator"] if "EGS" not in car], ) ] - - egs_gens = n.generators[n.generators["p_nom_extendable"] is True] + egs_gens = n.generators[n.generators["p_nom_extendable"]] egs_gens = egs_gens.loc[egs_gens["carrier"].str.contains("EGS")] new_carriers = list( diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index d44afedd6..2141be753 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -722,7 +722,6 @@ def add_regional_co2limit(n, sns, config): # lhs -= (p_store_discharge * EF_imports).sum() # # Internal Demand - # breakpoint() # region_loads = n.loads[n.loads.bus.isin(region_buses.index)] # region_demand = ( # n.loads_t.p_set.loc[planning_horizon,region_loads.index].sum().sum() From 484cbde1f3d9324371287ff10cdee993e5dcccb2 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 10:05:01 -0800 Subject: [PATCH 49/75] minor corrections --- workflow/rules/build_electricity.smk | 1 + workflow/scripts/add_extra_components.py | 2 +- workflow/scripts/solve_network.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 05b01a588..43251fcf8 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -714,6 +714,7 @@ rule add_extra_components: + "{interconnect}/Geospatial/regions_onshore_s{simpl}_{clusters}.geojson", params: retirement=config["electricity"].get("retirement", "technical"), + demand_response=config["electricity"].get("demand_response", {}), output: RESOURCES + "{interconnect}/elec_s{simpl}_c{clusters}_ec.nc", log: diff --git a/workflow/scripts/add_extra_components.py b/workflow/scripts/add_extra_components.py index 2d9e0ec85..96fd56654 100644 --- a/workflow/scripts/add_extra_components.py +++ b/workflow/scripts/add_extra_components.py @@ -849,7 +849,7 @@ def add_demand_response( add_nice_carrier_names(n, snakemake.config) add_co2_emissions(n, costs_dict[n.investment_periods[0]], n.carriers.index) - dr_config = snakemake.config["electricity"].get("demand_response", {}) + dr_config = snakemake.params.demand_response if dr_config: add_demand_response(n, dr_config) diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 1e32fbef3..f975145de 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -322,7 +322,7 @@ def add_technology_capacity_target_constraints(n, config): f"Planning Horizon: {target.planning_horizon}\n" f"Region: {target.region}\n" f"Carrier: {target.carrier}\n" - f"Min Value: {target['min']}\n", + f"Min Value: {target['min']}\n" f"Min Value Adj: {rhs}", ) @@ -344,7 +344,7 @@ def add_technology_capacity_target_constraints(n, config): f"Planning Horizon: {target.planning_horizon}\n" f"Region: {target.region}\n" f"Carrier: {target.carrier}\n" - f"Max Value: {target['max']}\n", + f"Max Value: {target['max']}\n" f"Max Value Adj: {rhs}", ) From 79a025569b4379ea0f8abc5bca6b78983ba556a4 Mon Sep 17 00:00:00 2001 From: ktehranchi Date: Tue, 11 Feb 2025 10:54:05 -0800 Subject: [PATCH 50/75] remove unused config, and set dynamic memory resource request by input file size --- docs/source/configtables/electricity.csv | 4 - workflow/repo_data/config/config.default.yaml | 4 - .../repo_data/config/config.tutorial.yaml | 4 - workflow/rules/build_electricity.smk | 85 +++++++++---------- workflow/rules/build_sector.smk | 1 - workflow/scripts/solve_network.py | 27 ------ 6 files changed, 41 insertions(+), 84 deletions(-) diff --git a/docs/source/configtables/electricity.csv b/docs/source/configtables/electricity.csv index 00974a915..94bbadf96 100644 --- a/docs/source/configtables/electricity.csv +++ b/docs/source/configtables/electricity.csv @@ -8,10 +8,6 @@ operational_reserve:,,,Settings for reserve requirements following `GenX `_. -,,, extendable_carriers:,,, Generator,--,Any extendable carrier,"Defines existing or non-existing conventional and renewable power plants to be extendable during the optimization. Conventional generators can only be built/expanded where already existent today. If a listed conventional carrier is not included in the ``conventional_carriers`` list, the lower limit of the capacity expansion is set to 0." Storage Unit,--,Any subset of {``battery``},Adds extendable storage units (battery and/or hydrogen) at every node/bus after clustering without capacity limits and with zero initial capacity. diff --git a/workflow/repo_data/config/config.default.yaml b/workflow/repo_data/config/config.default.yaml index 54ae99b8f..a2bdf0ffd 100644 --- a/workflow/repo_data/config/config.default.yaml +++ b/workflow/repo_data/config/config.default.yaml @@ -58,10 +58,6 @@ electricity: epsilon_vres: 0.02 contingency: 4000 - max_hours: - battery: 6 - H2: 168 - extendable_carriers: Generator: [solar, onwind, offwind_floating, OCGT, CCGT, CCGT-95CCS, coal, nuclear, hydrogen_ct] #include CCGT-CCS StorageUnit: [4hr_battery_storage, 8hr_battery_storage] # [Xhr-battery-storage (2-10 hours)] diff --git a/workflow/repo_data/config/config.tutorial.yaml b/workflow/repo_data/config/config.tutorial.yaml index b0ed68e79..5f3b77277 100644 --- a/workflow/repo_data/config/config.tutorial.yaml +++ b/workflow/repo_data/config/config.tutorial.yaml @@ -59,10 +59,6 @@ electricity: epsilon_vres: 0.02 contingency: 4000 - max_hours: - battery: 6 - H2: 168 - extendable_carriers: Generator: [solar, onwind, offwind_floating, OCGT, CCGT, CCGT-95CCS, coal, nuclear] #include CCGT-CCS StorageUnit: [4hr_battery_storage, 8hr_battery_storage] # [Xhr-battery-storage (2-10 hours)] diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 348a4b031..7026c2fbe 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -5,8 +5,8 @@ from itertools import chain rule build_shapes: params: - source_offshore_shapes=config["offshore_shape"], - offwind_params=config["renewable"]["offwind"], + source_offshore_shapes=config_provider("offshore_shape"), + offwind_params=config_provider("renewable", "offwind"), input: zone=DATA + "breakthrough_network/base_grid/zone.csv", nerc_shapes="repo_data/geospatial/NERC_Regions/NERC_Regions_Subregions.shp", @@ -33,8 +33,8 @@ rule build_shapes: rule build_base_network: params: - build_offshore_network=config["offshore_network"], - model_topology=config["model_topology"]["include"], + build_offshore_network=config_provider("offshore_network"), + model_topology=config_provider("model_topology", "include"), input: buses=DATA + "breakthrough_network/base_grid/bus.csv", lines=DATA + "breakthrough_network/base_grid/branch.csv", @@ -67,7 +67,7 @@ rule build_bus_regions: topological_boundaries=config_provider( "model_topology", "topological_boundaries" ), - focus_weights=config["focus_weights"], + focus_weights=config_provider("focus_weights"), input: country_shapes=RESOURCES + "{interconnect}/Geospatial/country_shapes.geojson", county_shapes=RESOURCES + "{interconnect}/Geospatial/county_shapes.geojson", @@ -142,8 +142,8 @@ if config["enable"].get("build_cutout", False): rule build_renewable_profiles: params: - renewable=config["renewable"], - snapshots=config["snapshots"], + renewable=config_provider("renewable"), + snapshots=config_provider("snapshots"), input: corine=ancient( DATA @@ -336,10 +336,10 @@ rule build_sector_demand: wildcard_constraints: end_use="residential|commercial|industry", params: - planning_horizons=config["scenario"]["planning_horizons"], + planning_horizons=config_provider("scenario", "planning_horizons"), profile_year=pd.to_datetime(config["snapshots"]["start"]).year, - eia_api=config["api"]["eia"], - snapshots=config["snapshots"], + eia_api=config_provider("api", "eia"), + snapshots=config_provider("snapshots"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, @@ -368,10 +368,10 @@ rule build_transport_road_demand: wildcard_constraints: end_use="transport", params: - planning_horizons=config["scenario"]["planning_horizons"], + planning_horizons=config_provider("scenario", "planning_horizons"), profile_year=pd.to_datetime(config["snapshots"]["start"]).year, - eia_api=config["api"]["eia"], - snapshots=config["snapshots"], + eia_api=config_provider("api", "eia"), + snapshots=config_provider("snapshots"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, @@ -405,9 +405,9 @@ rule build_transport_other_demand: end_use="transport", vehicle="boat-shipping|air|rail-shipping|rail-passenger", params: - planning_horizons=config["scenario"]["planning_horizons"], - eia_api=config["api"]["eia"], - snapshots=config["snapshots"], + planning_horizons=config_provider("scenario", "planning_horizons"), + eia_api=config_provider("api", "eia"), + snapshots=config_provider("snapshots"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, @@ -481,8 +481,8 @@ def demand_to_add(wildcards): rule add_demand: params: sectors=config["scenario"]["sector"], - planning_horizons=config["scenario"]["planning_horizons"], - snapshots=config["snapshots"], + planning_horizons=config_provider("scenario", "planning_horizons"), + snapshots=config_provider("snapshots"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand=demand_to_add, @@ -542,11 +542,28 @@ def dynamic_fuel_price_files(wildcards): return {} +rule build_powerplants: + input: + pudl=DATA + "pudl/pudl.sqlite", + wecc_ads="repo_data/WECC_ADS_public", + eia_ads_generator_mapping="repo_data/WECC_ADS_public/eia_ads_generator_mapping_updated.csv", + fuel_costs="repo_data/plants/fuelCost22.csv", + cems="repo_data/plants/cems_heat_rates.xlsx", + epa_crosswalk="repo_data/plants/epa_eia_crosswalk.csv", + output: + powerplants=RESOURCES + "powerplants.csv", + log: + "logs/build_powerplants.log", + resources: + mem_mb=30000, + script: + "../scripts/build_powerplants.py" + + rule add_electricity: params: length_factor=config["lines"]["length_factor"], renewable=config["renewable"], - max_hours=config["electricity"]["max_hours"], renewable_carriers=config["electricity"]["renewable_carriers"], extendable_carriers=config["electricity"]["extendable_carriers"], conventional_carriers=config["electricity"]["conventional_carriers"], @@ -603,7 +620,7 @@ rule add_electricity: BENCHMARKS + "{interconnect}/add_electricity" threads: 1 resources: - mem_mb=interconnect_mem_a, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 2, script: "../scripts/add_electricity.py" @@ -635,7 +652,7 @@ rule simplify_network: "logs/simplify_network/{interconnect}/elec_s{simpl}.log", threads: 1 resources: - mem_mb=interconnect_mem_s, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 10, script: "../scripts/simplify_network.py" @@ -648,7 +665,6 @@ rule cluster_network: aggregation_strategies=config_provider("clustering", "aggregation_strategies"), custom_busmap=config_provider("enable", "custom_busmap", default=False), focus_weights=config_provider("focus_weights", default=False), - max_hours=config_provider("electricity", "max_hours"), length_factor=config_provider("lines", "length_factor"), costs=config_provider("costs"), planning_horizons=config_provider("scenario", "planning_horizons"), @@ -689,7 +705,7 @@ rule cluster_network: "benchmarks/cluster_network/{interconnect}/elec_s{simpl}_c{clusters}" threads: 1 resources: - mem_mb=interconnect_mem_c, + mem_mb=lambda wildcards, input, attempt: (input.size // 200000) * attempt * 2, script: "../scripts/cluster_network.py" @@ -719,7 +735,7 @@ rule add_extra_components: "logs/add_extra_components/{interconnect}/elec_s{simpl}_c{clusters}_ec.log", threads: 1 resources: - mem_mb=interconnect_mem_prepare, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 2, group: "prepare" script: @@ -738,7 +754,6 @@ rule prepare_network: gaslimit=config_provider("electricity", "gaslimit"), gaslimit_enable=config_provider("electricity", "gaslimit_enable", default=False), transmission_network=config_provider("model_topology", "transmission_network"), - max_hours=config_provider("electricity", "max_hours"), costs=config_provider("costs"), autarky=config_provider("electricity", "autarky"), input: @@ -760,28 +775,10 @@ rule prepare_network: solver="logs/prepare_network/{interconnect}/elec_s{simpl}_c{clusters}_ec_l{ll}_{opts}.log", threads: 1 resources: - mem_mb=interconnect_mem_prepare, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 2, group: "prepare" log: "logs/prepare_network", script: "../scripts/prepare_network.py" - - -rule build_powerplants: - input: - pudl=DATA + "pudl/pudl.sqlite", - wecc_ads="repo_data/WECC_ADS_public", - eia_ads_generator_mapping="repo_data/WECC_ADS_public/eia_ads_generator_mapping_updated.csv", - fuel_costs="repo_data/plants/fuelCost22.csv", - cems="repo_data/plants/cems_heat_rates.xlsx", - epa_crosswalk="repo_data/plants/epa_eia_crosswalk.csv", - output: - powerplants=RESOURCES + "powerplants.csv", - log: - "logs/build_powerplants.log", - resources: - mem_mb=30000, - script: - "../scripts/build_powerplants.py" diff --git a/workflow/rules/build_sector.smk b/workflow/rules/build_sector.smk index 6c1a87fe7..62a2231ac 100644 --- a/workflow/rules/build_sector.smk +++ b/workflow/rules/build_sector.smk @@ -46,7 +46,6 @@ rule add_sectors: params: electricity=config["electricity"], costs=config["costs"], - max_hours=config["electricity"]["max_hours"], plotting=config["plotting"], snapshots=config["snapshots"], api=config["api"], diff --git a/workflow/scripts/solve_network.py b/workflow/scripts/solve_network.py index 2141be753..08347ab59 100644 --- a/workflow/scripts/solve_network.py +++ b/workflow/scripts/solve_network.py @@ -146,9 +146,6 @@ def add_land_use_constraints(n): def prepare_network( n, solve_opts=None, - config=None, - foresight=None, - planning_horizons=None, ): if "clip_p_max_pu" in solve_opts: for df in ( @@ -923,26 +920,6 @@ def add_operational_reserve_margin(n, sns, config): n.model.add_constraints(lhs <= rhs, name="Generator-p-reserve-upper") -def add_battery_constraints(n): - """ - Add constraint ensuring that charger = discharger, i.e. - 1 * charger_size - efficiency * discharger_size = 0. - """ - if not n.links.p_nom_extendable.any(): - return - - discharger_bool = n.links.index.str.contains("battery discharger") - charger_bool = n.links.index.str.contains("battery charger") - - dischargers_ext = n.links[discharger_bool].query("p_nom_extendable").index - chargers_ext = n.links[charger_bool].query("p_nom_extendable").index - - eff = n.links.efficiency[dischargers_ext].values - lhs = n.model["Link-p_nom"].loc[chargers_ext] - n.model["Link-p_nom"].loc[dischargers_ext] * eff - - n.model.add_constraints(lhs == 0, name="Link-charger_ratio") - - def add_sector_co2_constraints(n, config): """ Adds sector co2 constraints. @@ -1493,7 +1470,6 @@ def extra_functionality(n, snapshots): for o in opts: if "EQ" in o: add_EQ_constraints(n, o) - add_battery_constraints(n) add_land_use_constraints(n) @@ -1591,9 +1567,6 @@ def solve_network(n, config, solving, opts="", **kwargs): n = prepare_network( n, solve_opts, - config=snakemake.config, - foresight=snakemake.params.foresight, - planning_horizons=snakemake.params.planning_horizons, ) n = solve_network( From 17a4c371796ba011f6999e458777eddd989d6d61 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 14:51:48 -0800 Subject: [PATCH 51/75] cop fixed --- workflow/scripts/build_cop_profiles.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/workflow/scripts/build_cop_profiles.py b/workflow/scripts/build_cop_profiles.py index e5353f58e..5ccc978ab 100644 --- a/workflow/scripts/build_cop_profiles.py +++ b/workflow/scripts/build_cop_profiles.py @@ -1,4 +1,3 @@ -# ruff: noqa: N816, N803 """ Build coefficient of performance (COP) time series for air- or ground-sourced heat pumps. @@ -19,13 +18,13 @@ def coefficient_of_performance( - delta_T: xr.DataArray, + delta_t: xr.DataArray, source: str = "air", ) -> xr.DataArray: if source == "air": - return 6.81 - 0.121 * delta_T + 0.000630 * delta_T**2 + return 6.81 - 0.121 * delta_t + 0.000630 * delta_t**2 elif source == "soil": - return 8.77 - 0.150 * delta_T + 0.000734 * delta_T**2 + return 8.77 - 0.150 * delta_t + 0.000734 * delta_t**2 else: raise NotImplementedError("'source' must be one of ['air', 'soil']") @@ -42,10 +41,10 @@ def coefficient_of_performance( for area in ["total", "urban", "rural"]: for source in ["air", "soil"]: - source_T = xr.open_dataarray(snakemake.input[f"temp_{source}_{area}"]) + source_t = xr.open_dataarray(snakemake.input[f"temp_{source}_{area}"]) - delta_T = snakemake.params.heat_pump_sink_T - source_T + delta_t = snakemake.params.heat_pump_sink_T - source_t - cop = coefficient_of_performance(delta_T, source) + cop = coefficient_of_performance(delta_t, source) cop.to_netcdf(snakemake.output[f"cop_{source}_{area}"]) From 3549a66347a1021b205dfece05f9dde1f8522e02 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:15:14 -0800 Subject: [PATCH 52/75] natural gas done --- workflow/scripts/build_natural_gas.py | 40 +++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/workflow/scripts/build_natural_gas.py b/workflow/scripts/build_natural_gas.py index c7065b0f1..4966e40aa 100644 --- a/workflow/scripts/build_natural_gas.py +++ b/workflow/scripts/build_natural_gas.py @@ -1,7 +1,4 @@ -# ruff: noqa: RUF012, D101, D102 - -""" -Module for adding the gas sector. +"""Module for adding the gas sector. This module will add a state level copperplate natural gas network to the model. Specifically, it will do the following @@ -26,7 +23,7 @@ import pandas as pd import pypsa import yaml -from constants import CODE_2_STATE, NG_MWH_2_MMCF, STATE_2_CODE, STATES_INTERCONNECT_MAPPER +from constants import CODE_2_STATE, EMPTY_STATES, NG_MWH_2_MMCF, STATE_2_CODE, STATES_INTERCONNECT_MAPPER from pypsa.components import Network logger = logging.getLogger(__name__) @@ -103,12 +100,12 @@ def _get_state_center_points(self) -> gpd.GeoDataFrame: class GasData(ABC): - """Main class to interface with data.""" + """Interface with any gas data.""" state_2_interconnect = STATES_INTERCONNECT_MAPPER state_2_name = CODE_2_STATE name_2_state = STATE_2_CODE - states_2_remove = [x for x, y in STATES_INTERCONNECT_MAPPER.items() if not y] + states_2_remove = EMPTY_STATES def __init__(self, year: int, interconnect: str) -> None: self.year = year @@ -121,14 +118,17 @@ def __init__(self, year: int, interconnect: str) -> None: @property def data(self) -> pd.DataFrame: + """Get formatted data.""" return self._data @abstractmethod def read_data(self) -> pd.DataFrame | gpd.GeoDataFrame: + """Read in data.""" pass @abstractmethod def format_data(self, data: pd.DataFrame | gpd.GeoDataFrame) -> pd.DataFrame: + """Format dataset.""" pass def _get_data(self) -> pd.DataFrame: @@ -137,6 +137,7 @@ def _get_data(self) -> pd.DataFrame: @abstractmethod def build_infrastructure(self, n: pypsa.Network) -> None: + """Add pypsa components to network.""" pass def filter_on_interconnect( @@ -199,9 +200,11 @@ def __init__(self, interconnect: str, counties: str) -> None: ) # year locked for location mapping def read_data(self) -> gpd.GeoDataFrame: + """Read in state centerpoints.""" return pd.DataFrame(self.states.state_center_points) def format_data(self, data: gpd.GeoDataFrame) -> pd.DataFrame: + """Format bus data.""" data = pd.DataFrame(data) data["name"] = data.STATE.map(self.state_2_name) return self.filter_on_interconnect(data) @@ -211,6 +214,7 @@ def filter_on_sate( n: pypsa.Network, df: pd.DataFrame, ) -> pd.DataFrame: + """Filter formatted data to only include states in geographic scope.""" states_in_model = n.buses[ ~n.buses.carrier.isin( ["gas storage", "gas trade", "gas pipeline"], @@ -228,6 +232,7 @@ def filter_on_sate( return df def build_infrastructure(self, n: Network) -> None: + """Add pypsa components to network.""" df = self.filter_on_sate(n, self.data) states = df.set_index("STATE") @@ -255,6 +260,7 @@ def __init__(self, year: int, interconnect: str, api: str) -> None: super().__init__(year, interconnect) def read_data(self): + """Read in data from EIA API.""" base = eia.Storage("gas", "base", self.year, self.api).get_data() base["storage_type"] = "base_capacity" total = eia.Storage("gas", "total", self.year, self.api).get_data() @@ -267,6 +273,7 @@ def read_data(self): return final def format_data(self, data: pd.DataFrame): + """Format storage data.""" df = data.copy() df["value"] = df.value * MWH_2_MMCF df = ( @@ -300,6 +307,7 @@ def filter_on_sate( n: pypsa.Network, df: pd.DataFrame, ) -> pd.DataFrame: + """Filter formatted data to only include states in geographic scope.""" states_in_model = n.buses[ ~n.buses.carrier.isin( ["gas storage", "gas trade", "gas pipeline"], @@ -317,6 +325,7 @@ def filter_on_sate( return df def build_infrastructure(self, n: pypsa.Network, **kwargs): + """Add pypsa components to network.""" df = self.filter_on_sate(n, self.data) df.index = df.STATE df["state_name"] = df.index.map(self.state_2_name) @@ -390,9 +399,11 @@ def __init__(self, year: int, interconnect: str, api: str) -> None: super().__init__(year=year, interconnect=interconnect) def read_data(self) -> pd.DataFrame: + """Read in data from EIA API.""" return eia.Production("gas", "market", self.year, self.api).get_data() def format_data(self, data: pd.DataFrame): + """Format processing capacity data.""" df = data.copy() df["value"] = ( @@ -413,6 +424,7 @@ def filter_on_sate( n: pypsa.Network, df: pd.DataFrame, ) -> pd.DataFrame: + """Filter formatted data to only include states in geographic scope.""" states_in_model = n.buses[ ~n.buses.carrier.isin( ["gas storage", "gas trade", "gas pipeline"], @@ -430,6 +442,7 @@ def filter_on_sate( return df def build_infrastructure(self, n: pypsa.Network, **kwargs): + """Add pypsa components to network.""" df = self.filter_on_sate(n, self.data) df = df.set_index("STATE") df["bus"] = df.index + " gas" @@ -514,6 +527,7 @@ def __init__( super().__init__(year, interconnect) def read_data(self) -> pd.DataFrame: + """Read in excel dataset.""" return pd.read_excel( self.xlsx, sheet_name="Pipeline State2State Capacity", @@ -550,6 +564,7 @@ def filter_on_sate( return df[~(df.STATE_TO == df.STATE_FROM)].copy() def format_data(self, data: pd.DataFrame) -> pd.DataFrame: + """Format pipeline data.""" df = data.copy() df.columns = df.columns.str.strip() df = df[df.index == int(self.year)] @@ -689,6 +704,7 @@ def __init__( super().__init__(year, interconnect, xlsx, api) def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: + """Get piplines within the geographic scope.""" df = data.copy() # for some reason drop duplicates is not wokring here and I cant figure out why :( # df = df.drop_duplicates(subset=["STATE_TO", "STATE_FROM"], keep=False).copy() @@ -711,6 +727,7 @@ def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: return df.reset_index(drop=True) def build_infrastructure(self, n: pypsa.Network) -> None: + """Add pypsa components to network.""" df = self.filter_on_sate(n, self.data, in_spatial_scope=True) if df.empty: @@ -753,6 +770,7 @@ def __init__( super().__init__(year, interconnect, xlsx, api) def extract_pipelines(self, data: pd.DataFrame) -> pd.DataFrame: + """Get pipelines within geographic scope.""" df = data.copy() if self.domestic: return self._get_domestic_pipeline_connections(df) @@ -1100,7 +1118,10 @@ def __init__( super().__init__(year, interconnect) def read_data(self) -> gpd.GeoDataFrame: - """https://atlas.eia.gov/apps/3652f0f1860d45beb0fed27dc8a6fc8d/explore.""" + """Read in geojson pipe locations. + + https://atlas.eia.gov/apps/3652f0f1860d45beb0fed27dc8a6fc8d/explore. + """ return gpd.read_file(self.pipeline_geojson) def filter_on_sate( @@ -1108,6 +1129,7 @@ def filter_on_sate( n: pypsa.Network, df: pd.DataFrame, ) -> pd.DataFrame: + """Filter formatted data to only include states in geographic scope.""" states_in_model = n.buses[ ~n.buses.carrier.isin( ["gas storage", "gas trade", "gas pipeline"], @@ -1125,6 +1147,7 @@ def filter_on_sate( return df def format_data(self, data: gpd.GeoDataFrame) -> pd.DataFrame: + """Format linepack data.""" gdf = data.copy() states = self.states.copy() @@ -1172,6 +1195,7 @@ def format_data(self, data: gpd.GeoDataFrame) -> pd.DataFrame: return self.filter_on_interconnect(final) def build_infrastructure(self, n: pypsa.Network, **kwargs) -> None: + """Add pypsa components to network.""" df = self.filter_on_sate(n, self.data) df = df.set_index("STATE") From 9f3b2839ea08d9d4a2925f0502b7142850aab2de Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:26:56 -0800 Subject: [PATCH 53/75] sector costs --- workflow/scripts/build_sector_costs.py | 118 +++++++++++++------------ 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/workflow/scripts/build_sector_costs.py b/workflow/scripts/build_sector_costs.py index 8ec21edf6..05927d44c 100644 --- a/workflow/scripts/build_sector_costs.py +++ b/workflow/scripts/build_sector_costs.py @@ -1,5 +1,3 @@ -# ruff: noqa: RUF012, D101, D102 - """ Builds costs data from EFS studies. @@ -114,6 +112,7 @@ def __init__(self, file_path: str, efs_case: str = "Moderate Advancement") -> No @property def efs_case(self): + """EFS case name.""" return self._efs_case @efs_case.setter @@ -129,6 +128,7 @@ def _read_efs(self) -> pd.DataFrame: return pd.read_excel(self.file_path, sheet_name="EFS Data") def initialize(self, sector: str): # returns EfsSectorData + """Initialize type of EFS data.""" if sector == "Transportation": return EfsBevTransportationData(self.data, self.efs_case) elif sector == "Buildings": @@ -147,16 +147,16 @@ def get_data(self, sector: str) -> pd.DataFrame: ], )[self.columns] - def get_capex(self, sector: str) -> pd.DataFrame: + def get_capex(self, sector: str) -> pd.DataFrame: # noqa: D102 return self.initialize(sector).get_capex() - def get_lifetime(self, sector: str) -> pd.DataFrame: + def get_lifetime(self, sector: str) -> pd.DataFrame: # noqa: D102 return self.initialize(sector).get_lifetime() - def get_efficiency(self, sector: str) -> pd.DataFrame: + def get_efficiency(self, sector: str) -> pd.DataFrame: # noqa: D102 return self.initialize(sector).get_efficiency() - def get_fixed_costs(self, sector: str) -> pd.DataFrame: + def get_fixed_costs(self, sector: str) -> pd.DataFrame: # noqa: D102 return self.initialize(sector).get_fixed_costs() @@ -170,19 +170,19 @@ def __init__(self, data: pd.DataFrame, efs_case: str) -> None: self.efs_case = efs_case @abstractmethod - def get_capex(self): + def get_capex(self): # noqa: D102 pass @abstractmethod - def get_efficiency(self): + def get_efficiency(self): # noqa: D102 pass @abstractmethod - def get_fixed_costs(self): + def get_fixed_costs(self): # noqa: D102 pass @abstractmethod - def get_lifetime(self): + def get_lifetime(self): # noqa: D102 pass @staticmethod @@ -249,7 +249,7 @@ class EfsBevTransportationData(EfsSectorData): def __init__(self, data: pd.DataFrame, efs_case: str) -> None: super().__init__(data, efs_case) - def get_capex(self): + def get_capex(self): # noqa: D102 df = self.data.copy() df = df[ (df.Sector == "Transportation") & (df["EFS Case"] == self.efs_case) & (df.Metric == "Capital Cost") @@ -261,7 +261,7 @@ def get_capex(self): df = self._correct_capex_units(df) return self.expand_data(df) - def get_lifetime(self): + def get_lifetime(self): # noqa: D102 df = self.data.copy() df = df[ (df.Sector == "Transportation") & (df["EFS Case"] == self.efs_case) & (df.Metric == "Capital Cost") @@ -290,7 +290,7 @@ def get_efficiency(self): # df = df.rename(columns={"value": "capex"}) # return self._calculate_fom(df) - def get_fixed_costs(self): + def get_fixed_costs(self): # noqa: D102 df = self.get_capex() df["parameter"] = "FOM" df["unit"] = "%/year" @@ -403,29 +403,30 @@ def _expand_data(df: pd.DataFrame) -> pd.DataFrame: return pd.concat(dfs).reset_index() def get_data(self): + """Get capex, lifetime, efficiency, and fixed cost data.""" return pd.concat( [ - self.get_capex(), - self.get_lifetime(), - self.get_efficiency(), - self.get_fixed_costs(), + self._get_capex(), + self._get_lifetime(), + self._get_efficiency(), + self._get_fixed_costs(), ], ) - def get_capex(self): + def _get_capex(self): df = self.data.copy() df = df[df.parameter == "investment"] df = self._correct_capex_units(df) return self._expand_data(df)[self.columns] - def get_lifetime(self): + def _get_lifetime(self): df = self.get_capex() df["parameter"] = "lifetime" df["value"] = self.lifetime df["unit"] = "years" return df[self.columns] - def get_efficiency(self): + def _get_efficiency(self): df = self.data.copy() df = df[df.parameter == "efficiency"] df = self._correct_efficiency_units(df) @@ -437,7 +438,7 @@ def get_efficiency(self): # df["capex"] = df.value # return self._correct_fom_units(df) - def get_fixed_costs(self): + def _get_fixed_costs(self): df = self.get_capex() df["parameter"] = "FOM" df["unit"] = "%/year" @@ -472,35 +473,37 @@ def _correct_efficiency_units(self, df: pd.DataFrame) -> pd.DataFrame: class EfsBuildingData(EfsSectorData): - mmbtu_2_mwh = MMBTU_MWHthemal - - # Assumptions from https://atb.nrel.gov/transportation/2022/definitions + """Processes EIA building technology data.""" - # table a3 - lifetimes = { # units in years - "ashp": 15, - "furnace": 15, - "elec_resistance": 20, - "hpwh": 13, # heat pump water heater - "ngwh": 13, # natural gas water heater - "ewh": 13, # electrical water heater - } - - # table a3 - # using commercal since it gives at a per-unit level - fixed_cost = { # units in $/kBTU/yr - "ashp": 1.47, - "furnace": 1.03, - "elec_resistance": 0.01, - "hpwh": 2.29, - "ngwh": 0.55, - "ewh": 0.88, - } + mmbtu_2_mwh = MMBTU_MWHthemal def __init__(self, data: pd.DataFrame, efs_case: str) -> None: super().__init__(data, efs_case) - def get_capex(self): + # # table a3 + # # units in years + # lifetimes: dict[str, int | float] = { + # "ashp": 15, + # "furnace": 15, + # "elec_resistance": 20, + # "hpwh": 13, # heat pump water heater + # "ngwh": 13, # natural gas water heater + # "ewh": 13, # electrical water heater + # } + + # # table a3 + # # using commercal since it gives at a per-unit level + # # units in $/kBTU/yr + # fixed_cost: dict[str, int | float] = { + # "ashp": 1.47, + # "furnace": 1.03, + # "elec_resistance": 0.01, + # "hpwh": 2.29, + # "ngwh": 0.55, + # "ewh": 0.88, + # } + + def get_capex(self): # noqa: D102 df = self.data.copy() df = df[ (df.Sector == "Buildings") @@ -515,7 +518,7 @@ def get_capex(self): df = self._correct_capex_units(df) return self.expand_data(df) - def get_lifetime(self): + def get_lifetime(self): # noqa: D102 df = self.get_capex() df["tech_type"] = df.technology.map(self.assign_tech_types) df["value"] = df.tech_type.map(self.lifetimes) @@ -523,7 +526,7 @@ def get_lifetime(self): df["parameter"] = "lifetime" return df.drop(columns=["tech_type"]) - def get_efficiency(self): + def get_efficiency(self): # noqa: D102 df = self.data.copy() df = df[(df.Sector == "Buildings") & (df["EFS Case"] == self.efs_case) & (df.Metric == "Efficiency")].copy() source = "NREL EFS at https://data.nrel.gov/submissions/78" @@ -532,7 +535,7 @@ def get_efficiency(self): df = self._format_data_structure(df, source=source, description="") return self.expand_data(df) - def get_fixed_costs(self): + def get_fixed_costs(self): # noqa: D102 df = self.get_capex() df["parameter"] = "FOM" return self._correct_fom_units(df) @@ -567,7 +570,7 @@ def _correct_fom_units(self, df: pd.DataFrame) -> pd.DataFrame: class EiaBuildingData: """ - Class for processing EIA residential and commercial data. + Processes EIA residential and commercial data. All data originates from this document: https://www.eia.gov/analysis/studies/buildings/equipcosts/pdf/full.pdf @@ -624,17 +627,18 @@ def _correct_investment_units(df: pd.DataFrame) -> pd.DataFrame: return df2 def get_data(self, sector: str | None = None) -> pd.DataFrame: + """Get capex, lifetime, efficiency, and fixed cost data.""" sector = self._check_sector(sector) return pd.concat( [ - self.get_capex(sector), - self.get_lifetime(sector), - self.get_efficiency(sector), - self.get_fixed_costs(sector), + self._get_capex(sector), + self._get_lifetime(sector), + self._get_efficiency(sector), + self._get_fixed_costs(sector), ], ) - def get_capex(self, sector: str | None = None): + def _get_capex(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "investment") @@ -643,7 +647,7 @@ def get_capex(self, sector: str | None = None): df = self.data[slicer] return self._correct_investment_units(df)[self.columns] - def get_lifetime(self, sector: str | None = None): + def _get_lifetime(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "lifetime") @@ -651,7 +655,7 @@ def get_lifetime(self, sector: str | None = None): slicer = self.data.parameter == "lifetime" return self.data[slicer][self.columns] - def get_efficiency(self, sector: str | None = None): + def _get_efficiency(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "efficiency") @@ -659,7 +663,7 @@ def get_efficiency(self, sector: str | None = None): slicer = self.data.parameter == "efficiency" return self.data[slicer][self.columns] - def get_fixed_costs(self, sector: str | None = None): + def _get_fixed_costs(self, sector: str | None = None): sector = self._check_sector(sector) if sector: slicer = (self.data.technology.str.startswith(sector)) & (self.data.parameter == "FOM") From 946b8995293022fdb079d120bc8104d1d13e015e Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:39:06 -0800 Subject: [PATCH 54/75] stock data --- workflow/scripts/build_stock_data.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/workflow/scripts/build_stock_data.py b/workflow/scripts/build_stock_data.py index 54c6bdb27..e26fb1e0f 100644 --- a/workflow/scripts/build_stock_data.py +++ b/workflow/scripts/build_stock_data.py @@ -1,4 +1,3 @@ -# ruff: noqa: D100, D101, RUF012 """Builds End-Use initial stock data.""" # to supress warning in water heat xlsx @@ -6,6 +5,7 @@ import logging import warnings from pathlib import Path +from typing import ClassVar import numpy as np import pandas as pd @@ -68,7 +68,7 @@ class Recs: - Highlights for water heating in U.S. homes by state, 2020 """ - file_mapper = { + FILE_MAPPER: ClassVar[dict[str, str]] = { "aircon_stock": "State Air Conditioning", "space_heat_stock": "State Space Heating", "water_heat": "State Water Heating", @@ -76,7 +76,7 @@ class Recs: "water_heat_fuel": "State Water Heating Fuels", } - column_mapper = { + COLUMN_MAPPER: ClassVar[dict[str, str]] = { "aircon_stock": { "Unnamed: 1": "total_stock", "Uses air-conditioning equipment": "ac_stock", @@ -141,7 +141,7 @@ def _valid_name(s: str): def _read(self, stock: str) -> pd.DataFrame: """Reads in the data.""" - f = Path(self.dir, f"{self.file_mapper[stock]}.xlsx") + f = Path(self.dir, f"{self.FILE_MAPPER[stock]}.xlsx") df = ( pd.read_excel( @@ -151,7 +151,7 @@ def _read(self, stock: str) -> pd.DataFrame: index_col=0, skipfooter=2, ) - .rename(columns=self.column_mapper[stock], index={"All homes": "USA"}) + .rename(columns=self.COLUMN_MAPPER[stock], index={"All homes": "USA"}) .dropna() .astype(str) # avoids downcasting object dtype error .replace("Q", np.nan) # > 50% RSE or n < 10 @@ -198,27 +198,27 @@ class Cecs: # Percentages from Page 7 from Table C7 at # https://www.eia.gov/consumption/commercial/data/2018/index.php?view=consumption#major - usa_avg_space_heating = { + USA_AVG_SPACE_HEATING: ClassVar[dict:float] = { "Electricity": 0.313, # 1856 / 5918 "Natural gas": 0.396, # 2344 / 5918 "Fuel oil": 0.038, # 222 / 5918 "District heat": 0.013, # 78 / 5918 "Propane": 0.057, # 338 / 5918 } - usa_avg_water_heating = { + USA_AVG_WATER_HEATING: ClassVar[dict:float] = { "Electricity": 0.471, # 2785 / 5918 "Natural gas": 0.319, # 1885 / 5918 "Fuel oil": 0.012, # 72 / 5918 "District heat": 0.006, # 38 / 5918 "Propane": 0.025, # 145 / 5918 } - usa_avg_cooling = { + USA_AVG_COOLING: ClassVar[dict:float] = { "Electricity": 0.775, # 4584 / 5918 "Natural gas": 0.0, # 4 / 5918 "District chilled": 0.01, # 55 / 5918 } - census_name_map = { + CENSUS_NAME_MAP: ClassVar[dict:float] = { "NewEngland": "new_england", "Middle Atlantic": "mid_atlantic", "EastNorthCentral": "east_north_central", @@ -265,7 +265,7 @@ def _read(self, fuel: str) -> pd.DataFrame: .astype(float) ) df = df.rename(columns={x: x.replace("\n", "") for x in df.columns}).rename( - columns=self.census_name_map, + columns=self.CENSUS_NAME_MAP, ) dfs.append(df) @@ -331,11 +331,11 @@ def _fill_missing(self, df: pd.DataFrame, fuel: str) -> pd.DataFrame: """Fills missing values with USA average.""" match fuel: case "space_heat_fuel": - fill_values = self.usa_avg_space_heating + fill_values = self.USA_AVG_SPACE_HEATING case "water_heat_fuel": - fill_values = self.usa_avg_water_heating + fill_values = self.USA_AVG_WATER_HEATING case "aircon_fuel": - fill_values = self.usa_avg_cooling + fill_values = self.USA_AVG_COOLING case _: raise NotImplementedError From 79a6da080092f5d7510f51d191436bd785d7a761 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:42:32 -0800 Subject: [PATCH 55/75] constants updated --- workflow/scripts/constants.py | 1 + workflow/scripts/constants_sector.py | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/workflow/scripts/constants.py b/workflow/scripts/constants.py index 73068ef4e..e041b1a33 100644 --- a/workflow/scripts/constants.py +++ b/workflow/scripts/constants.py @@ -361,6 +361,7 @@ "YT": "canada", "MX": "mexico", } +EMPTY_STATES = [x for x, y in STATES_INTERCONNECT_MAPPER.items() if not y] STATES_CENSUS_MAPPER = { "AL": "south", diff --git a/workflow/scripts/constants_sector.py b/workflow/scripts/constants_sector.py index f952fdb44..27c5ec392 100644 --- a/workflow/scripts/constants_sector.py +++ b/workflow/scripts/constants_sector.py @@ -1,4 +1,3 @@ -# ruff: noqa: D100, D101 """Constants specific for sector coupling.""" from enum import Enum @@ -9,6 +8,8 @@ class SecNames(Enum): + """Three letter sector names.""" + RESIDENTIAL = "res" COMMERCIAL = "com" INDUSTRY = "ind" @@ -17,6 +18,8 @@ class SecNames(Enum): class SecCarriers(Enum): + """Sector carrier names.""" + ELECTRICITY = "elec" HEATING = "heat" COOLING = "cool" @@ -31,6 +34,8 @@ class SecCarriers(Enum): class Transport(Enum): + """Transport mode names.""" + ROAD = "veh" AIR = "air" BOAT = "boat" @@ -38,6 +43,8 @@ class Transport(Enum): class RoadTransport(Enum): + """Road transport mode names.""" + LIGHT = "lgt" MEDIUM = "med" HEAVY = "hvy" @@ -45,6 +52,8 @@ class RoadTransport(Enum): class RoadTransportUnits(Enum): + """Road transport mode units.""" + LIGHT = "kVMT" MEDIUM = "kVMT" HEAVY = "kVMT" @@ -52,28 +61,40 @@ class RoadTransportUnits(Enum): class AirTransport(Enum): + """Air transport mode names.""" + PASSENGER = "psg" class AirTransportUnits(Enum): + """Air transport mode units.""" + PASSENGER = "kSeatMiles" class RailTransport(Enum): + """Rail transport mode names.""" + PASSENGER = "psg" SHIPPING = "ship" class RailTransportUnits(Enum): + """Rail transport mode units.""" + PASSENGER = "kPassengerMiles" SHIPPING = "kTonMiles" class BoatTransport(Enum): + """Boat transport mode names.""" + SHIPPING = "ship" class BoatTransportUnits(Enum): + """Boat transport mode units.""" + SHIPPING = "kTonMiles" From ca6f7f8fd18b0a697d2cf911495ffe7f89bc19c5 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:51:36 -0800 Subject: [PATCH 56/75] corrects eulp --- workflow/scripts/eulp.py | 49 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/workflow/scripts/eulp.py b/workflow/scripts/eulp.py index 16998796e..f4efaa725 100644 --- a/workflow/scripts/eulp.py +++ b/workflow/scripts/eulp.py @@ -1,5 +1,3 @@ -# ruff: noqa: RUF012, D101, D102 - """ Holds data processing class for NREL End Use Load Profiles. @@ -8,6 +6,9 @@ See `retrieve_eulp` rule for the data extraction """ +from typing import ClassVar + +import matplotlib as mpl import pandas as pd @@ -23,7 +24,7 @@ class Eulp: - water_heat -> end use water heating """ - _elec_group = [ + _elec_group: ClassVar[list[str]] = [ # residential "out.electricity.ceiling_fan.energy_consumption.kwh", "out.electricity.clothes_dryer.energy_consumption.kwh", @@ -55,7 +56,7 @@ class Eulp: "out.electricity.water_systems.energy_consumption.kwh", ] - _heat_group = [ + _heat_group: ClassVar[list[str]] = [ # residential "out.electricity.heating.energy_consumption.kwh", "out.electricity.heating_fans_pumps.energy_consumption.kwh", @@ -91,7 +92,7 @@ class Eulp: "out.electricity.heating.energy_consumption.kwh", ] - _space_heat_group = [ + _space_heat_group: ClassVar[list[str]] = [ # residential "out.electricity.heating.energy_consumption.kwh", "out.electricity.heating_fans_pumps.energy_consumption.kwh", @@ -120,7 +121,7 @@ class Eulp: "out.electricity.heating.energy_consumption.kwh", ] - _water_heat_group = [ + _water_heat_group: ClassVar[list[str]] = [ # residential "out.electricity.hot_water.energy_consumption.kwh", "out.electricity.pool_heater.energy_consumption.kwh", @@ -135,7 +136,7 @@ class Eulp: "out.other_fuel.water_systems.energy_consumption.kwh", ] - _cool_group = [ + _cool_group: ClassVar[list[str]] = [ # residential "out.electricity.cooling.energy_consumption.kwh", "out.electricity.cooling_fans_pumps.energy_consumption.kwh", @@ -189,23 +190,23 @@ def __repr__(self): return f"\n{self.data.head(3)}\n\n from {self.data.index[0]} to {self.data.index[-1]}" @property - def electric(self): + def electric(self): # noqa: D102 return self.data["electricity"] @property - def heating(self): + def heating(self): # noqa: D102 return self.data["heating"] @property - def space_heating(self): + def space_heating(self): # noqa: D102 return self.data["space_heating"] @property - def water_heating(self): + def water_heating(self): # noqa: D102 return self.data["water_heating"] @property - def cooling(self): + def cooling(self): # noqa: D102 return self.data["cooling"] @staticmethod @@ -254,7 +255,8 @@ def plot( "water_heating", ], resample: str | None = None, - ): + ) -> mpl.axes.Axes: + """Plot load profiles.""" if isinstance(sectors, str): sectors = [sectors] @@ -266,19 +268,20 @@ def plot( return df.plot(xlabel="", ylabel="MW") def to_csv(self, path_or_buf: str, **kwargs): + """Save load data as a csv.""" self.data.to_csv(path_or_buf=path_or_buf, **kwargs) class EulpTotals: """End use by fuel.""" - _elec_group = ["out.electricity.total.energy_consumption.kwh"] + _elec_group: ClassVar[list[str]] = ["out.electricity.total.energy_consumption.kwh"] - _ng_group = ["out.natural_gas.total.energy_consumption.kwh"] + _ng_group: ClassVar[list[str]] = ["out.natural_gas.total.energy_consumption.kwh"] - _oil_group = ["out.fuel_oil.total.energy_consumption.kwh"] + _oil_group: ClassVar[list[str]] = ["out.fuel_oil.total.energy_consumption.kwh"] - _propane_group = ["out.propane.total.energy_consumption.kwh"] + _propane_group: ClassVar[list[str]] = ["out.propane.total.energy_consumption.kwh"] def __init__( self, @@ -313,19 +316,19 @@ def __repr__(self): return f"\n{self.data.head(3)}\n\n from {self.data.index[0]} to {self.data.index[-1]}" @property - def electric(self): + def electric(self): # noqa: D102 return self.data["electricity"] @property - def gas(self): + def gas(self): # noqa: D102 return self.data["gas"] @property - def oil(self): + def oil(self): # noqa: D102 return self.data["oil"] @property - def propane(self): + def propane(self): # noqa: D102 return self.data["propane"] @staticmethod @@ -366,7 +369,8 @@ def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: def plot( self, sectors: list[str] | str | None = ["electricity", "gas", "oil", "propane"], - ): + ) -> mpl.axes.Axes: + """Plot load profiles.""" if isinstance(sectors, str): sectors = [sectors] @@ -375,6 +379,7 @@ def plot( return df.plot(xlabel="", ylabel="MWh") def to_csv(self, path_or_buf: str, **kwargs): + """Save load data as a csv.""" self.data.to_csv(path_or_buf=path_or_buf, **kwargs) From 54d52c936bd6405ec15aa2fa77a52948dad5ff6d Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Tue, 11 Feb 2025 15:54:56 -0800 Subject: [PATCH 57/75] more fixes --- workflow/scripts/plot_statistics_sector.py | 3 ++- workflow/scripts/retrieve_eulp.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/workflow/scripts/plot_statistics_sector.py b/workflow/scripts/plot_statistics_sector.py index 0e65ab784..4d96a4e36 100644 --- a/workflow/scripts/plot_statistics_sector.py +++ b/workflow/scripts/plot_statistics_sector.py @@ -1,4 +1,3 @@ -# ruff: noqa: D101, D102 """Plots Sector Coupling Statistics.""" import logging @@ -1143,6 +1142,8 @@ def save_fig( @dataclass class PlottingData: + """Describe data to plot.""" + name: str # snakemake name fn: callable sector: str | None = None # None = 'system' diff --git a/workflow/scripts/retrieve_eulp.py b/workflow/scripts/retrieve_eulp.py index f5249bfe4..0d1906760 100644 --- a/workflow/scripts/retrieve_eulp.py +++ b/workflow/scripts/retrieve_eulp.py @@ -1,5 +1,3 @@ -# ruff: noqa: RUF012 - """ Module to download end use load profiles (eulp) for comstock and restock data. @@ -13,6 +11,7 @@ import logging from pathlib import Path +from typing import ClassVar import requests from _helpers import configure_logging @@ -26,7 +25,7 @@ class OediDownload: root = "https://oedi-data-lake.s3.amazonaws.com/nrel-pds-building-stock/end-use-load-profiles-for-us-building-stock/2024" - res_files = [ + res_files: ClassVar[list[str]] = [ "mobile_home", "multi-family_with_2_-_4_units", "multi-family_with_5plus_units", @@ -34,7 +33,7 @@ class OediDownload: "single-family_detached", ] - com_files = [ + com_files: ClassVar[list[str]] = [ "fullservicerestaurant", "hospital", "largehotel", From 4f47ffab927c16c339da2729f7f2b198317e933d Mon Sep 17 00:00:00 2001 From: Kamran Date: Tue, 11 Feb 2025 19:36:06 -0800 Subject: [PATCH 58/75] dynamically set memory allocation by file size --- workflow/rules/build_electricity.smk | 18 ++++---- workflow/rules/common.smk | 61 ---------------------------- workflow/rules/solve_electricity.smk | 2 +- 3 files changed, 10 insertions(+), 71 deletions(-) diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 7026c2fbe..3344340ed 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -188,7 +188,7 @@ rule build_renewable_profiles: threads: ATLITE_NPROCESSES retries: 3 resources: - mem_mb=ATLITE_NPROCESSES * 5000, + mem_mb=lambda wildcards, input, attempt: (ATLITE_NPROCESSES * input.size // 3500000) * attempt * 1.5, wildcard_constraints: technology="(?!hydro|EGS).*", # Any technology other than hydro script: @@ -327,7 +327,7 @@ rule build_electrical_demand: BENCHMARKS + "{interconnect}/{end_use}_build_demand" threads: 2 resources: - mem_mb=interconnect_mem, + mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/build_demand.py" @@ -359,7 +359,7 @@ rule build_sector_demand: BENCHMARKS + "{interconnect}/demand/{end_use}_build_demand" threads: 2 resources: - mem_mb=interconnect_mem, + mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/build_demand.py" @@ -395,7 +395,7 @@ rule build_transport_road_demand: BENCHMARKS + "{interconnect}/demand/{end_use}_build_demand" threads: 2 resources: - mem_mb=interconnect_mem, + mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/build_demand.py" @@ -420,7 +420,7 @@ rule build_transport_other_demand: BENCHMARKS + "{interconnect}/demand/{end_use}_{vehicle}_build_demand" threads: 2 resources: - mem_mb=interconnect_mem, + mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/build_demand.py" @@ -493,7 +493,7 @@ rule add_demand: benchmark: BENCHMARKS + "{interconnect}/add_demand" resources: - mem_mb=interconnect_mem, + mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/add_demand.py" @@ -620,7 +620,7 @@ rule add_electricity: BENCHMARKS + "{interconnect}/add_electricity" threads: 1 resources: - mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 2, + mem_mb=lambda wildcards, input, attempt: (input.size // 400000) * attempt * 2, script: "../scripts/add_electricity.py" @@ -652,7 +652,7 @@ rule simplify_network: "logs/simplify_network/{interconnect}/elec_s{simpl}.log", threads: 1 resources: - mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 10, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 1.5, script: "../scripts/simplify_network.py" @@ -705,7 +705,7 @@ rule cluster_network: "benchmarks/cluster_network/{interconnect}/elec_s{simpl}_c{clusters}" threads: 1 resources: - mem_mb=lambda wildcards, input, attempt: (input.size // 200000) * attempt * 2, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 2, script: "../scripts/cluster_network.py" diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index e87888106..be1ec97d7 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -105,67 +105,6 @@ def memory(w): val = int(factor * (15000 + 195 * int(w.clusters))) return int(val * len(config_provider("scenario", "planning_horizons")(w))) - -def interconnect_mem(w): - mem = 50000 - if w.interconnect == "usa": - return int(mem * 1.5) - elif w.interconnect == "eastern": - return int(mem * 1.5) - elif w.interconnect == "western": - return int(mem) - elif w.interconnect == "texas": - return int(mem * 1) - - -def interconnect_mem_a(w): - mem = 30000 * len(config_provider("scenario", "planning_horizons")(w)) - if w.interconnect == "usa": - return int(mem * 4) - elif w.interconnect == "eastern": - return int(mem * 3) - elif w.interconnect == "western": - return int(mem * 2) - elif w.interconnect == "texas": - return int(mem * 0.5) - - -def interconnect_mem_s(w): - mem = 30000 * len(config_provider("scenario", "planning_horizons")(w)) - if w.interconnect == "usa": - return int(mem * 4) - elif w.interconnect == "eastern": - return int(mem * 3) - elif w.interconnect == "western": - return int(mem * 2) - elif w.interconnect == "texas": - return int(mem * 0.5) - - -def interconnect_mem_c(w): - mem = 1000 * len(config_provider("scenario", "planning_horizons")(w)) - if w.interconnect == "usa": - return int(mem * 4) - elif w.interconnect == "eastern": - return int(mem * 3) - elif w.interconnect == "western": - return int(mem * 2) - elif w.interconnect == "texas": - return int(mem * 0.75) - - -def interconnect_mem_prepare(w): - mem = 5000 * len(config_provider("scenario", "planning_horizons")(w)) - if w.interconnect == "usa": - return int(mem * 4) - elif w.interconnect == "eastern": - return int(mem * 3) - elif w.interconnect == "western": - return int(mem * 2) - elif w.interconnect == "texas": - return int(mem * 0.75) - - def input_custom_extra_functionality(w): path = config_provider( "solving", "options", "custom_extra_functionality", default=False diff --git a/workflow/rules/solve_electricity.smk b/workflow/rules/solve_electricity.smk index d9d9bd27a..54750283c 100644 --- a/workflow/rules/solve_electricity.smk +++ b/workflow/rules/solve_electricity.smk @@ -44,7 +44,7 @@ rule solve_network: ) threads: solver_threads resources: - mem_mb=memory, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 30, walltime=config["solving"].get("walltime", "12:00:00"), conda: "../envs/environment.yaml" From a43ef50a9bf240e1a903cc555942c91e7606ef34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 03:44:16 +0000 Subject: [PATCH 59/75] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- workflow/rules/build_electricity.smk | 6 +++++- workflow/rules/common.smk | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 6aede5873..f052ea7f2 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -188,7 +188,11 @@ rule build_renewable_profiles: threads: ATLITE_NPROCESSES retries: 3 resources: - mem_mb=lambda wildcards, input, attempt: (ATLITE_NPROCESSES * input.size // 3500000) * attempt * 1.5, + mem_mb=lambda wildcards, input, attempt: ( + ATLITE_NPROCESSES * input.size // 3500000 + ) + * attempt + * 1.5, wildcard_constraints: technology="(?!hydro|EGS).*", # Any technology other than hydro script: diff --git a/workflow/rules/common.smk b/workflow/rules/common.smk index be1ec97d7..fba894a80 100644 --- a/workflow/rules/common.smk +++ b/workflow/rules/common.smk @@ -105,6 +105,7 @@ def memory(w): val = int(factor * (15000 + 195 * int(w.clusters))) return int(val * len(config_provider("scenario", "planning_horizons")(w))) + def input_custom_extra_functionality(w): path = config_provider( "solving", "options", "custom_extra_functionality", default=False From 8b6e0557c061d55228d25040525d35fc6fc5bb44 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 10:38:27 -0800 Subject: [PATCH 60/75] eia fix --- workflow/scripts/eia.py | 236 ++++++++++++++++++++++------------------ 1 file changed, 129 insertions(+), 107 deletions(-) diff --git a/workflow/scripts/eia.py b/workflow/scripts/eia.py index f464aaa0e..9d70e3818 100644 --- a/workflow/scripts/eia.py +++ b/workflow/scripts/eia.py @@ -1,4 +1,3 @@ -# ruff: noqa: RUF012, D101, D102, N818, D400 """ Extracts EIA data. @@ -39,6 +38,7 @@ import logging import math from abc import ABC, abstractmethod +from typing import ClassVar import constants import numpy as np @@ -99,7 +99,7 @@ # exceptions -class InputException(Exception): +class InputPropertyError(Exception): """Class for exceptions.""" def __init__(self, propery, valid_options, recived_option) -> None: @@ -119,6 +119,7 @@ def data_creator(self): # type DataExtractor pass def get_data(self, pivot: bool = False) -> pd.DataFrame: + """Get formated data.""" product = self.data_creator() df = product.retrieve_data() df = product.format_data(df) @@ -127,16 +128,20 @@ def get_data(self, pivot: bool = False) -> pd.DataFrame: return df def get_api_call(self) -> pd.DataFrame: + """Get API URL.""" product = self.data_creator() return product.build_url() def get_raw_data(self) -> pd.DataFrame: + """Get unformatted data from API.""" product = self.data_creator() return product.retrieve_data() # concrete creator class FuelCosts(EiaData): + """Primary fuel cost data.""" + def __init__( self, fuel: str, @@ -154,30 +159,31 @@ def __init__( self.scenario = scenario def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.fuel == "gas": assert self.industry if self.year < 2024: - return GasCosts(self.industry, self.year, self.api) + return _GasCosts(self.industry, self.year, self.api) else: if self.industry in ("imports", "exports"): logger.warning( f"Projected {self.industry} prices not availble. Returning 2023 data.", ) - return GasCosts(self.industry, 2023, self.api) + return _GasCosts(self.industry, 2023, self.api) aeo = "reference" if not self.scenario else self.scenario - return ProjectedGasCosts(self.industry, self.year, aeo, self.api) + return _ProjectedGasCosts(self.industry, self.year, aeo, self.api) elif self.fuel == "coal": assert self.industry - return CoalCosts(self.industry, self.year, self.api) + return _CoalCosts(self.industry, self.year, self.api) elif self.fuel == "lpg": assert self.grade - return LpgCosts(self.grade, self.year, self.api) + return _LpgCosts(self.grade, self.year, self.api) elif self.fuel == "heating_oil": - return HeatingFuelCosts("fuel_oil", self.year, self.api) + return _HeatingFuelCosts("fuel_oil", self.year, self.api) elif self.fuel == "propane": - return HeatingFuelCosts("propane", self.year, self.api) + return _HeatingFuelCosts("propane", self.year, self.api) else: - raise InputException( + raise InputPropertyError( propery="Fuel Costs", valid_options=["gas", "coal", "lpg", "heating_oil", "propane"], recived_option=self.fuel, @@ -186,6 +192,8 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Trade(EiaData): + """Natural gas trade data.""" + def __init__( self, fuel: str, @@ -201,15 +209,16 @@ def __init__( self.api = api def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.fuel == "gas": if self.international: # gives monthly values - return InternationalGasTrade(self.direction, self.year, self.api) + return _InternationalGasTrade(self.direction, self.year, self.api) else: # gives annual values - return DomesticGasTrade(self.direction, self.year, self.api) + return _DomesticGasTrade(self.direction, self.year, self.api) else: - raise InputException( + raise InputPropertyError( propery="Energy Trade", valid_options=["gas"], recived_option=self.fuel, @@ -218,6 +227,8 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Production(EiaData): + """Primary energy production data.""" + def __init__(self, fuel: str, production: str, year: int, api: str) -> None: self.fuel = fuel # (gas) self.production = production # (marketed|gross) @@ -225,10 +236,11 @@ def __init__(self, fuel: str, production: str, year: int, api: str) -> None: self.api = api def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.fuel == "gas": - return GasProduction(self.production, self.year, self.api) + return _GasProduction(self.production, self.year, self.api) else: - raise InputException( + raise InputPropertyError( property="Production", valid_options=["gas"], recieved_option=self.fuel, @@ -258,15 +270,16 @@ def __init__( self.scenario = scenario # only for AEO scenario def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.year < 2024: if self.scenario: logger.warning("Can not apply AEO scenario to historical demand") - return HistoricalSectorEnergyDemand(self.sector, self.year, self.api) + return _HistoricalSectorEnergyDemand(self.sector, self.year, self.api) elif self.year >= 2024: aeo = "reference" if not self.scenario else self.scenario - return ProjectedSectorEnergyDemand(self.sector, self.year, aeo, self.api) + return _ProjectedSectorEnergyDemand(self.sector, self.year, aeo, self.api) else: - raise InputException( + raise InputPropertyError( propery="EnergyDemand", valid_options="year", recived_option=self.year, @@ -297,46 +310,47 @@ def __init__( self.scenario = scenario def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.units == "travel": if self.year < 2024: - return HistoricalTransportTravelDemand( + return _HistoricalTransportTravelDemand( self.vehicle, self.year, self.api, ) elif self.year >= 2024: aeo = "reference" if not self.scenario else self.scenario - return ProjectedTransportTravelDemand( + return _ProjectedTransportTravelDemand( self.vehicle, self.year, aeo, self.api, ) else: - raise InputException( + raise InputPropertyError( propery="TransportationTravelDemand", valid_options=range(2017, 2051), recived_option=self.year, ) elif self.units == "btu": if self.year < 2024: - return HistoricalTransportBtuDemand(self.vehicle, self.year, self.api) + return _HistoricalTransportBtuDemand(self.vehicle, self.year, self.api) elif self.year >= 2024: aeo = "reference" if not self.scenario else self.scenario - return ProjectedTransportBtuDemand( + return _ProjectedTransportBtuDemand( self.vehicle, self.year, aeo, self.api, ) else: - raise InputException( + raise InputPropertyError( propery="TransportationBtuDemand", valid_options=range(2017, 2051), recived_option=self.year, ) else: - raise InputException( + raise InputPropertyError( propery="TransportationDemand", valid_options=("travel", "btu"), recived_option=self.units, @@ -366,15 +380,16 @@ def __init__( self.aeo = "reference" if not self.scenario else self.scenario def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.year > 2016: - return HistoricalProjectedTransportFuelUse( + return _HistoricalProjectedTransportFuelUse( self.vehicle, self.year, self.aeo, self.api, ) else: - raise InputException( + raise InputPropertyError( propery="TransportationFuelUse", valid_options=range(2017, 2051), recived_option=self.year, @@ -383,6 +398,8 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Storage(EiaData): + """Primary energy storage data.""" + def __init__(self, fuel: str, storage: str, year: int, api: str) -> None: self.fuel = fuel self.storage = storage # (base|working|total|withdraw) @@ -390,10 +407,11 @@ def __init__(self, fuel: str, storage: str, year: int, api: str) -> None: self.api = api def data_creator(self) -> pd.DataFrame: + """Initializes data extractor.""" if self.fuel == "gas": - return GasStorage(self.storage, self.year, self.api) + return _GasStorage(self.storage, self.year, self.api) else: - raise InputException( + raise InputPropertyError( propery="Storage", valid_options=["gas"], recived_option=self.fuel, @@ -402,6 +420,8 @@ def data_creator(self) -> pd.DataFrame: # concrete creator class Emissions(EiaData): + """State level emissions data.""" + def __init__( self, sector: str, @@ -415,11 +435,12 @@ def __init__( self.fuel = "all" if not fuel else fuel # (coal|oil|gas|all) def data_creator(self): - return StateEmissions(self.sector, self.fuel, self.year, self.api) + """Initializes data extractor.""" + return _StateEmissions(self.sector, self.fuel, self.year, self.api) class Seds(EiaData): - """State Energy Demand System.""" + """State level energy demand.""" def __init__(self, metric: str, sector: str, year: int, api: str) -> None: self.metric = metric # (consumption) @@ -428,10 +449,11 @@ def __init__(self, metric: str, sector: str, year: int, api: str) -> None: self.api = api def data_creator(self): + """Initializes data extractor.""" if self.metric == "consumption": - return SedsConsumption(self.sector, self.year, self.api) + return _SedsConsumption(self.sector, self.year, self.api) else: - raise InputException( + raise InputPropertyError( propery="SEDS", valid_options=["consumption"], recived_option=self.metric, @@ -439,13 +461,16 @@ def data_creator(self): class ElectricPowerData(EiaData): + """Power system operational data.""" + def __init__(self, sector: str, year: int, api_key: str) -> None: self.sector = sector self.year = year self.api_key = api_key def data_creator(self): - return ElectricPowerOperationalData(self.sector, self.year, self.api_key) + """Initializes data extractor.""" + return _ElectricPowerOperationalData(self.sector, self.year, self.api_key) # product @@ -475,6 +500,7 @@ def build_url(self) -> str: pass def retrieve_data(self) -> pd.DataFrame: + """Retrieves and converts API data into dataframe.""" url = self.build_url() data = self._request_eia_data(url) return pd.DataFrame.from_dict(data["response"]["data"]) @@ -553,8 +579,8 @@ def _assign_dtypes(df: pd.DataFrame) -> pd.DataFrame: # concrete product -class GasCosts(DataExtractor): - industry_codes = { +class _GasCosts(DataExtractor): + industry_codes: ClassVar[dict[str, str]] = { "power": "PEU", "residential": "PRS", "commercial": "PCS", @@ -566,7 +592,7 @@ class GasCosts(DataExtractor): def __init__(self, industry: str, year: int, api_key: str) -> None: self.industry = industry if industry not in self.industry_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Gas Costs", valid_options=list(self.industry_codes), recived_option=industry, @@ -616,15 +642,15 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # concrete product -class CoalCosts(DataExtractor): - industry_codes = { +class _CoalCosts(DataExtractor): + industry_codes: ClassVar[dict[str, str]] = { "power": "PEU", } def __init__(self, industry: str, year: int, api_key: str) -> None: self.industry = industry if industry != "power": - raise InputException( + raise InputPropertyError( propery="Coal Costs", valid_options=list(self.industry_codes), recived_option=industry, @@ -702,14 +728,10 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(final) -class LpgCosts(DataExtractor): - """ - The cost is motor gasoline! - - Not heating fuel! - """ +class _LpgCosts(DataExtractor): + """The cost is motor gasoline. Not heating fuel.""" - grade_codes = { + grade_codes: ClassVar[dict[str, str]] = { "total": "EPM0", "regular": "EPMR", "premium": "EPMP", @@ -718,7 +740,7 @@ class LpgCosts(DataExtractor): } # https://en.wikipedia.org/wiki/Petroleum_Administration_for_Defense_Districts - padd_2_state = { + padd_2_state: ClassVar[dict[str, str]] = { "PADD 1A": ["CT", "ME", "MA", "NH", "RI", "VT"], "PADD 1B": ["DE", "DC", "MD", "NJ", "NY", "PA"], "PADD 1C": ["FL", "GA", "NC", "SC", "VA", "WV"], @@ -747,7 +769,7 @@ class LpgCosts(DataExtractor): def __init__(self, grade: str, year: int, api_key: str) -> None: self.grade = grade if grade not in self.grade_codes: - raise InputException( + raise InputPropertyError( propery="Lpg Costs", valid_options=list(self.grade_codes), recived_option=grade, @@ -772,15 +794,15 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(final) -class HeatingFuelCosts(DataExtractor): - """Note, only returns data from October to March!""" +class _HeatingFuelCosts(DataExtractor): + """Note, only returns data from October to March.""" - heating_fuel_codes = {"fuel_oil": "No 2 Fuel Oil", "propane": "Propane"} + heating_fuel_codes: ClassVar[dict[str, str]] = {"fuel_oil": "No 2 Fuel Oil", "propane": "Propane"} def __init__(self, heating_fuel: str, year: int, api_key: str) -> None: self.heating_fuel = heating_fuel if heating_fuel not in self.heating_fuel_codes: - raise InputException( + raise InputPropertyError( propery="Heating Fuel Costs", valid_options=list(self.heating_fuel_codes), recived_option=heating_fuel, @@ -836,7 +858,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # def __init__(self, sector: str, year: int, api: str) -> None: # self.sector = sector # if sector not in self.sector_codes.keys(): -# raise InputException( +# raise InputPropertyError( # propery="Historical Energy Demand", # valid_options=list(self.sector_codes), # recived_option=sector, @@ -858,7 +880,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # return self._assign_dtypes(df) -class HistoricalSectorEnergyDemand(DataExtractor): +class _HistoricalSectorEnergyDemand(DataExtractor): """ Extracts historical energy demand at a yearly national level. @@ -867,7 +889,7 @@ class HistoricalSectorEnergyDemand(DataExtractor): - https://www.eia.gov/outlooks/aeo/pdf/AEO2023_Release_Presentation.pdf (pg 17) """ - sector_codes = { + sector_codes: ClassVar[dict[str, str]] = { "residential": "TNR", "commercial": "TNC", "industry": "TNI", @@ -878,7 +900,7 @@ class HistoricalSectorEnergyDemand(DataExtractor): def __init__(self, sector: str, year: int, api: str) -> None: self.sector = sector if sector not in self.sector_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Historical Energy Demand", valid_options=list(self.sector_codes), recived_option=sector, @@ -906,7 +928,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class ProjectedSectorEnergyDemand(DataExtractor): +class _ProjectedSectorEnergyDemand(DataExtractor): """Extracts projected energy demand at a national level from AEO 2023.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php @@ -914,7 +936,7 @@ class ProjectedSectorEnergyDemand(DataExtractor): # note, these are all "total energy use by end use - total gross end use consumption" # https://www.eia.gov/totalenergy/data/flow-graphs/electricity.php - sector_codes = { + sector_codes: ClassVar[dict[str, str]] = { "residential": "cnsm_enu_resd_NA_dele_NA_NA_qbtu", "commercial": "cnsm_enu_comm_NA_dele_NA_NA_qbtu", "industry": "cnsm_enu_idal_NA_dele_NA_NA_qbtu", @@ -926,13 +948,13 @@ def __init__(self, sector: str, year: int, scenario: str, api: str): self.scenario = scenario self.sector = sector if scenario not in self.scenario_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Energy Demand Scenario", valid_options=list(self.scenario_codes), recived_option=scenario, ) if sector not in self.sector_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Energy Demand Sector", valid_options=list(self.sector_codes), recived_option=sector, @@ -952,14 +974,14 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class HistoricalTransportTravelDemand(DataExtractor): +class _HistoricalTransportTravelDemand(DataExtractor): """Gets Transport demand in units of travel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS # units will be different umong these! - vehicle_codes = { + vehicle_codes: ClassVar[dict[str, str]] = { "light_duty": "kei_trv_trn_NA_ldv_NA_NA_blnvehmls", "med_duty": "kei_trv_trn_NA_cml_NA_NA_blnvehmls", "heavy_duty": "kei_trv_trn_NA_fght_NA_NA_blnvehmls", @@ -973,7 +995,7 @@ class HistoricalTransportTravelDemand(DataExtractor): def __init__(self, vehicle: str, year: int, api: str) -> None: self.vehicle = vehicle if vehicle not in self.vehicle_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Historical Transport Travel Demand", valid_options=list(self.vehicle_codes), recived_option=vehicle, @@ -1018,14 +1040,14 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class ProjectedTransportTravelDemand(DataExtractor): +class _ProjectedTransportTravelDemand(DataExtractor): """Gets Transport demand in units of travel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS # units will be different umong these! - vehicle_codes = { + vehicle_codes: ClassVar[dict[str, str]] = { "light_duty": "kei_trv_trn_NA_ldv_NA_NA_blnvehmls", "med_duty": "kei_trv_trn_NA_cml_NA_NA_blnvehmls", "heavy_duty": "kei_trv_trn_NA_fght_NA_NA_blnvehmls", @@ -1040,13 +1062,13 @@ def __init__(self, vehicle: str, year: int, scenario: str, api: str) -> None: self.vehicle = vehicle self.scenario = scenario if scenario not in self.scenario_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Transport Travel Demand Scenario", valid_options=list(self.scenario_codes), recived_option=scenario, ) if vehicle not in self.vehicle_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Transport Travel Demand", valid_options=list(self.vehicle_codes), recived_option=vehicle, @@ -1072,11 +1094,11 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class HistoricalTransportBtuDemand(DataExtractor): +class _HistoricalTransportBtuDemand(DataExtractor): """Gets Transport demand in units of btu.""" # units will be different umong these! - vehicle_codes = { + vehicle_codes: ClassVar[dict[str, str]] = { "light_duty": "cnsm_NA_trn_ldv_use_NA_NA_qbtu", "med_duty": "cnsm_NA_trn_cml_use_NA_NA_qbtu", "heavy_duty": "cnsm_NA_trn_fght_use_NA_NA_qbtu", @@ -1095,7 +1117,7 @@ class HistoricalTransportBtuDemand(DataExtractor): def __init__(self, vehicle: str, year: int, api: str) -> None: self.vehicle = vehicle if vehicle not in self.vehicle_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Historical BTU Transport Demand", valid_options=list(self.vehicle_codes), recived_option=vehicle, @@ -1140,14 +1162,14 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class ProjectedTransportBtuDemand(DataExtractor): +class _ProjectedTransportBtuDemand(DataExtractor): """Gets Transport demand in units of quads.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS # units will be different umong these! - vehicle_codes = { + vehicle_codes: ClassVar[dict[str, str]] = { "light_duty": "cnsm_NA_trn_ldv_use_NA_NA_qbtu", "med_duty": "cnsm_NA_trn_cml_use_NA_NA_qbtu", "heavy_duty": "cnsm_NA_trn_fght_use_NA_NA_qbtu", @@ -1167,13 +1189,13 @@ def __init__(self, vehicle: str, year: int, scenario: str, api: str) -> None: self.vehicle = vehicle self.scenario = scenario if scenario not in self.scenario_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Transport BTU Demand Scenario", valid_options=list(self.scenario_codes), recived_option=scenario, ) if vehicle not in self.vehicle_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Transport BTU Demand", valid_options=list(self.vehicle_codes), recived_option=vehicle, @@ -1199,14 +1221,14 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class HistoricalProjectedTransportFuelUse(DataExtractor): +class _HistoricalProjectedTransportFuelUse(DataExtractor): """Gets Transport Energy Use by fuel.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS # units will be different umong these! - vehicle_codes = { + vehicle_codes: ClassVar[dict[str, list[str]]] = { "light_duty": [ f"&facets[seriesId][]=cnsm_NA_trn_ldty_{x}_NA_NA_trlbtu" for x in ("NA", "dfo", "elc", "eth", "hdg", "mgs", "ng", "prop") @@ -1242,13 +1264,13 @@ def __init__(self, vehicle: str, year: int, scenario: str, api: str) -> None: self.vehicle = vehicle self.scenario = scenario if vehicle not in self.vehicle_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Transport Energy Use by Fuel", valid_options=list(self.vehicle_codes), recived_option=vehicle, ) if scenario not in self.scenario_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Transport Energy Use by Fuel", valid_options=list(self.scenario_codes), recived_option=scenario, @@ -1308,7 +1330,7 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # def __init__(self, direction: str, year: int, api_key: str) -> None: # self.direction = direction # if self.direction not in list(self.direction_codes): -# raise InputException( +# raise InputPropertyError( # propery="Natural Gas International Imports and Exports", # valid_options=list(self.direction_codes), # recived_option=direction, @@ -1358,13 +1380,13 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: # return "-".join(connections) -class ProjectedGasCosts(DataExtractor): +class _ProjectedGasCosts(DataExtractor): """Extracts projected energy demand at a national level from AEO 2023.""" # https://www.eia.gov/outlooks/aeo/assumptions/case_descriptions.php scenario_codes = AEO_SCENARIOS - industry_codes = { + industry_codes: ClassVar[dict[str, str]] = { "residential": "prce_NA_resd_NA_ng_NA_usa_y13dlrpmcf", "commercial": "prce_NA_comm_NA_ng_NA_usa_y13dlrpmcf", "industry": "prce_NA_idal_NA_ng_NA_usa_y13dlrpmcf", @@ -1376,13 +1398,13 @@ def __init__(self, industry: str, year: int, scenario: str, api: str): self.scenario = scenario self.industry = industry if scenario not in self.scenario_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Natural Gas Costs Scenario", valid_options=list(self.scenario_codes), recived_option=scenario, ) if industry not in self.industry_codes.keys(): - raise InputException( + raise InputPropertyError( propery="Projected Natural Gas Costs Industry", valid_options=list(self.industry_codes), recived_option=industry, @@ -1402,14 +1424,14 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class InternationalGasTrade(DataExtractor): +class _InternationalGasTrade(DataExtractor): """ Gets imports/exports by point of entry. This filters for ONLY canada and mexico imports/exports """ - direction_codes = { + direction_codes: ClassVar[dict[str, str]] = { "imports": "IMI", "exports": "EEI", } @@ -1419,7 +1441,7 @@ class InternationalGasTrade(DataExtractor): def __init__(self, direction: str, year: int, api_key: str) -> None: self.direction = direction if self.direction not in list(self.direction_codes): - raise InputException( + raise InputPropertyError( propery="Natural Gas International Imports and Exports", valid_options=list(self.direction_codes), recived_option=direction, @@ -1469,7 +1491,7 @@ def extract_state(description: str) -> str: return description.split(" Natural Gas Pipeline")[0] -class DomesticGasTrade(DataExtractor): +class _DomesticGasTrade(DataExtractor): """ Gets imports/exports by state. @@ -1477,7 +1499,7 @@ class DomesticGasTrade(DataExtractor): example, "CA-OR" will represent from California to Oregon """ - direction_codes = { + direction_codes: ClassVar[dict[str, str]] = { "imports": "MIR", "exports": "MID", } @@ -1485,7 +1507,7 @@ class DomesticGasTrade(DataExtractor): def __init__(self, direction: str, year: int, api_key: str) -> None: self.direction = direction if self.direction not in list(self.direction_codes): - raise InputException( + raise InputPropertyError( propery="Natural Gas Domestic Imports and Exports", valid_options=list(self.direction_codes), recived_option=direction, @@ -1534,11 +1556,11 @@ def extract_state(description: str) -> str: return description.split(" Natural Gas Pipeline")[0] -class GasStorage(DataExtractor): +class _GasStorage(DataExtractor): """Underground storage facilites for natural gas.""" # https://www.eia.gov/naturalgas/storage/basics/ - storage_codes = { + storage_codes: ClassVar[dict[str, str]] = { "base": "SAB", "working": "SAO", "total": "SAT", @@ -1548,7 +1570,7 @@ class GasStorage(DataExtractor): def __init__(self, storage: str, year: int, api_key: str) -> None: self.storage = storage if self.storage not in list(self.storage_codes): - raise InputException( + raise InputPropertyError( propery="Natural Gas Underground Storage", valid_options=list(self.storage_codes), recived_option=storage, @@ -1584,10 +1606,10 @@ def map_state_names(state: str) -> str: return "U.S." if state == "U.S. Total" else STATE_CODES[state] -class GasProduction(DataExtractor): +class _GasProduction(DataExtractor): """Dry natural gas production.""" - production_codes = { + production_codes: ClassVar[dict[str, str]] = { "market": "VGM", "gross": "FGW", # gross withdrawls } @@ -1595,7 +1617,7 @@ class GasProduction(DataExtractor): def __init__(self, production: str, year: int, api_key: str) -> None: self.production = production if self.production not in list(self.production_codes): - raise InputException( + raise InputPropertyError( propery="Natural Gas Production", valid_options=list(self.production_codes), recived_option=production, @@ -1637,10 +1659,10 @@ def map_state_names(state: str) -> str: return "U.S." if state == "U.S." else STATE_CODES[state] -class StateEmissions(DataExtractor): +class _StateEmissions(DataExtractor): """State Level CO2 Emissions.""" - sector_codes = { + sector_codes: ClassVar[dict[str, str]] = { "commercial": "CC", "power": "EC", "industrial": "IC", @@ -1649,7 +1671,7 @@ class StateEmissions(DataExtractor): "total": "TT", } - fuel_codes = { + fuel_codes: ClassVar[dict[str, str]] = { "coal": "CO", "gas": "NG", "oil": "PE", @@ -1660,13 +1682,13 @@ def __init__(self, sector: str, fuel: str, year: int, api_key: str) -> None: self.sector = sector self.fuel = fuel if self.sector not in list(self.sector_codes): - raise InputException( + raise InputPropertyError( propery="State Level Emissions", valid_options=list(self.sector_codes), recived_option=sector, ) if self.fuel not in list(self.fuel_codes): - raise InputException( + raise InputPropertyError( propery="State Level Emissions", valid_options=list(self.fuel_codes), recived_option=fuel, @@ -1704,10 +1726,10 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class SedsConsumption(DataExtractor): +class _SedsConsumption(DataExtractor): """State Level End-Use Consumption.""" - sector_codes = { + sector_codes: ClassVar[dict[str, str]] = { "commercial": "TNCCB", "industrial": "TNICB", "residential": "TNRCB", @@ -1718,7 +1740,7 @@ class SedsConsumption(DataExtractor): def __init__(self, sector: str, year: int, api_key: str) -> None: self.sector = sector if self.sector not in list(self.sector_codes): - raise InputException( + raise InputPropertyError( propery="State Level Consumption", valid_options=list(self.sector_codes), recived_option=sector, @@ -1751,10 +1773,10 @@ def format_data(self, df: pd.DataFrame) -> pd.DataFrame: return self._assign_dtypes(df) -class ElectricPowerOperationalData(DataExtractor): +class _ElectricPowerOperationalData(DataExtractor): """Electric Power Operational Data.""" - sector_codes = { + sector_codes: ClassVar[dict[str, int]] = { "electric_utility": 1, "ipp_non_chp": 2, "ipp_chp": 3, @@ -1781,7 +1803,7 @@ def __init__(self, sector: str, year: int, api_key: str) -> None: ) self.year = 2023 if self.sector not in list(self.sector_codes): - raise InputException( + raise InputPropertyError( propery="Electric Power Operational Data", valid_options=list(self.sector_codes), recived_option=sector, From 8dfd9c2c90c0b8ac19a700fa50a6e2ad9e862360 Mon Sep 17 00:00:00 2001 From: Kamran Date: Wed, 12 Feb 2025 10:48:43 -0800 Subject: [PATCH 61/75] update solve elec rule mem --- workflow/rules/solve_electricity.smk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/rules/solve_electricity.smk b/workflow/rules/solve_electricity.smk index 54750283c..000043280 100644 --- a/workflow/rules/solve_electricity.smk +++ b/workflow/rules/solve_electricity.smk @@ -44,7 +44,7 @@ rule solve_network: ) threads: solver_threads resources: - mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 30, + mem_mb=lambda wildcards, input, attempt: (input.size // 100000) * attempt * 80, walltime=config["solving"].get("walltime", "12:00:00"), conda: "../envs/environment.yaml" From a1d51f3d58b1036fdb900f4ada22bf4d71b773b5 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 11:26:24 -0800 Subject: [PATCH 62/75] demand updated --- workflow/scripts/build_demand.py | 48 ++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/workflow/scripts/build_demand.py b/workflow/scripts/build_demand.py index 1c2cb65d8..215debebb 100644 --- a/workflow/scripts/build_demand.py +++ b/workflow/scripts/build_demand.py @@ -1,5 +1,3 @@ -# ruff: noqa: RUF012, D101, D102, F811 - """Builds the demand data for the PyPSA network.""" # snakemake is not liking this futures import. Removing type hints in context class @@ -11,7 +9,7 @@ import sys from abc import ABC, abstractmethod from pathlib import Path -from typing import Any +from typing import Any, ClassVar import constants as const import duckdb @@ -56,7 +54,7 @@ def write_strategy(self): # returns WriteStrategy: return self._write_strategy @write_strategy.setter - def strategy(self, strategy) -> None: # arg is WriteStrategy + def strategy(self, strategy) -> None: # arg is WriteStrategy # noqa: F811 """Usually, the Context allows replacing a Strategy object at runtime.""" self._write_strategy = strategy @@ -149,7 +147,7 @@ def __init__(self, filepath: str | list[str] | None = None) -> None: self.filepath = filepath @property - def units(): + def units(): # noqa: D102 return "MW" @abstractmethod @@ -230,7 +228,7 @@ def __init__(self, filepath: str | None = None) -> None: self._zone = "ba" @property - def zone(self): + def zone(self): # noqa: D102 return self._zone def _read_data(self) -> pd.DataFrame: @@ -282,7 +280,7 @@ def __init__(self, filepath: str | None = None) -> None: self._zone = "state" @property - def zone(self): + def zone(self): # noqa: D102 return self._zone def _read_data(self) -> pd.DataFrame: @@ -359,7 +357,7 @@ def __init__(self, filepath: str | None = None) -> None: self._zone = "state" @property - def zone(self): + def zone(self): # noqa: D102 return self._zone def _read_data(self) -> pd.DataFrame: @@ -500,11 +498,12 @@ def __init__(self, filepath: str | list[str], stock: str) -> None: self._zone = "state" @property - def zone(self): + def zone(self): # noqa: D102 return self._zone @property def stock(self): + """Data is from ResStock or ComStock.""" if self._stock == "res": return "residential" elif self._stock == "com": @@ -619,7 +618,7 @@ class ReadCliu(ReadStrategy): # MAIN -> MRO # MAPP -> SPP and some MRO # WSCC -> WECC (but left seperate here) - EPRI_NERC_2_STATE = { + EPRI_NERC_2_STATE: ClassVar[dict[str, list[str]]] = { "ECAR": ["IL", "IN", "KY", "MI", "OH", "WI", "WV"], "ERCOT": ["TX"], "MAAC": ["MD"], @@ -636,12 +635,12 @@ class ReadCliu(ReadStrategy): } # https://www.epri.com/research/products/000000003002018167 - EPRI_SEASON_2_MONTH = { + EPRI_SEASON_2_MONTH: ClassVar[dict[str, list[int]]] = { "Peak": [5, 6, 7, 8, 9], # may -> sept "OffPeak": [1, 2, 3, 4, 10, 11, 12], # oct -> april } - EPRI_ENDUSE = { + EPRI_ENDUSE: ClassVar[dict[str, str]] = { "HVAC": "electricity", "Lighting": "electricity", "MachineDrives": "electricity", @@ -664,7 +663,7 @@ def __init__( self._zone = "state" @property - def zone(self): + def zone(self): # noqa: D102 return self._zone def _read_data(self) -> Any: @@ -1070,7 +1069,7 @@ class ReadTransportEfsAeo(ReadStrategy): """ # TODO: extract this out directly from EFS - efs_years = [2018, 2020, 2022, 2024, 2030, 2040, 2050] + efs_years: ClassVar[list[int]] = [2018, 2020, 2022, 2024, 2030, 2040, 2050] def __init__(self, filepath: str, api: str, efs_path: str) -> None: """Filepath is state level breakdown of VMT by vehicle type.""" @@ -1085,7 +1084,7 @@ def __init__(self, filepath: str, api: str, efs_path: str) -> None: self.efs_profile = self._read_efs_data() @property - def zone(self): + def zone(self): # noqa: D102 return self._zone @staticmethod @@ -1313,13 +1312,13 @@ class ReadTransportAeo(ReadStrategy): """ # scales units so demand magnitudes are consistent - unit_scaler = { + unit_scaler: ClassVar[dict[str, int | float]] = { "air": 1000000, "boat_shipping": 1000000, "rail_shipping": 1000000, "rail_passenger": 1000000, } - unit_scaler_name = { + unit_scaler_name: ClassVar[dict[str, list[str]]] = { "air": ["billion", "thousand"], "boat_shipping": ["billion", "thousand"], "rail_shipping": ["billion", "thousand"], @@ -1345,7 +1344,7 @@ def __init__( self.aeo_demand = self._read_demand_aeo() @property - def zone(self): + def zone(self): # noqa: D102 return self._zone @staticmethod @@ -1908,6 +1907,7 @@ def need_scaling(self, df: pd.DataFrame) -> bool: return False def assign_scaler(self): # type DemandScaler + """Assign logic to scale demand with.""" if self.scaling_method == "aeo_energy": assert self.api, "Must provide eia api key" return AeoEnergyScaler(self.api) @@ -1925,11 +1925,14 @@ def assign_scaler(self): # type DemandScaler class DemandScaler(ABC): + """Allow the scaling of input data bases on different energy projections.""" + def __init__(self): self.projection = self.get_projections() @abstractmethod def get_projections(self) -> pd.DataFrame: + """Get implementation specific energy projections.""" pass def get_growth(self, start_year: int, end_year: int, sector: str) -> float: @@ -1990,6 +1993,8 @@ def reindex(df: pd.DataFrame, year: int) -> pd.DataFrame: class AeoElectricityScaler(DemandScaler): + """Scales against EIA Annual Energy Outlook electricity projections.""" + def __init__(self, pudl: str, scenario: str = "reference"): self.pudl = pudl self.scenario = scenario @@ -2038,6 +2043,8 @@ def get_projections(self) -> pd.DataFrame: class AeoEnergyScaler(DemandScaler): + """Scales against EIA Annual Energy Outlook energy projections.""" + def __init__(self, api: str, scenario: str = "reference"): self.api = api self.scenario = scenario @@ -2093,6 +2100,8 @@ def get_projections(self) -> pd.DataFrame: class AeoVmtScaler(DemandScaler): + """Scales against EIA Annual Energy Outlook vehicle mile traveled projections.""" + def __init__(self, api: str, scenario: str = "reference"): self.api = api self.scenario = scenario @@ -2159,12 +2168,15 @@ def get_projections(self) -> pd.DataFrame: class EfsElectricityScalar(DemandScaler): + """Scales against NREL Electrification Futures Study electricity projections.""" + def __init__(self, filepath: str): self.efs = filepath self.region = "united_states" super().__init__() def read(self) -> pd.DataFrame: + """Read in raw EFS data.""" df = pd.read_csv(self.efs, engine="pyarrow") return ( df.drop( From d00515cacbd94c138dd56b78ca8506f95bfadb32 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 11:26:49 -0800 Subject: [PATCH 63/75] valiadation plots linted --- workflow/scripts/plot_validation_sector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workflow/scripts/plot_validation_sector.py b/workflow/scripts/plot_validation_sector.py index 928df6062..6fda69705 100644 --- a/workflow/scripts/plot_validation_sector.py +++ b/workflow/scripts/plot_validation_sector.py @@ -1,4 +1,3 @@ -# ruff: noqa: D100, D101 """Plots sector validation plots.""" import logging @@ -547,6 +546,8 @@ def plot_system_consumption_validation_by_state( @dataclass class PlottingData: + """Describe data to plot.""" + name: str # snakemake name fn: callable system_only: bool From ec89daf65e397c14c060d8dc426b17ffb610bd7b Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 13:10:24 -0800 Subject: [PATCH 64/75] fix type hints --- workflow/scripts/eulp.py | 6 +++--- workflow/scripts/plot_natural_gas.py | 6 ++++-- workflow/scripts/plot_statistics_sector.py | 10 ++++++---- workflow/scripts/summary_sector.py | 11 +++++++---- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/workflow/scripts/eulp.py b/workflow/scripts/eulp.py index f4efaa725..0d5072931 100644 --- a/workflow/scripts/eulp.py +++ b/workflow/scripts/eulp.py @@ -8,8 +8,8 @@ from typing import ClassVar -import matplotlib as mpl import pandas as pd +from matplotlib.axes import Axes class Eulp: @@ -255,7 +255,7 @@ def plot( "water_heating", ], resample: str | None = None, - ) -> mpl.axes.Axes: + ) -> Axes: """Plot load profiles.""" if isinstance(sectors, str): sectors = [sectors] @@ -369,7 +369,7 @@ def aggregate_sector(df: pd.DataFrame, columns: list[str]) -> pd.Series: def plot( self, sectors: list[str] | str | None = ["electricity", "gas", "oil", "propane"], - ) -> mpl.axes.Axes: + ) -> Axes: """Plot load profiles.""" if isinstance(sectors, str): sectors = [sectors] diff --git a/workflow/scripts/plot_natural_gas.py b/workflow/scripts/plot_natural_gas.py index 17f91c02b..ffeed7cd2 100644 --- a/workflow/scripts/plot_natural_gas.py +++ b/workflow/scripts/plot_natural_gas.py @@ -4,7 +4,9 @@ from dataclasses import dataclass from functools import partial from pathlib import Path -from typing import Any + +# Optional used as 'arg: callable | None = None' gives TypeError with py3.11 +from typing import Any, Optional import matplotlib.pyplot as plt import pandas as pd @@ -40,7 +42,7 @@ class PlottingData: unit: str | None = None converter: float | None = 1.0 resample: str | None = None # "D", "W", "12h" for example - resample_func: callable | None = None # pd.Series.sum for example + resample_func: Optional[callable] = None # pd.Series.sum for example # noqa: UP007 plot_by_month: bool | None = False # not resampled diff --git a/workflow/scripts/plot_statistics_sector.py b/workflow/scripts/plot_statistics_sector.py index 4d96a4e36..da6cdcd8d 100644 --- a/workflow/scripts/plot_statistics_sector.py +++ b/workflow/scripts/plot_statistics_sector.py @@ -6,7 +6,9 @@ from enum import Enum from math import ceil from pathlib import Path -from typing import Any + +# Optional used as 'arg: callable | None = None' gives TypeError with py3.11 +from typing import Any, Optional import matplotlib.pyplot as plt import pandas as pd @@ -152,7 +154,7 @@ def plot_sector_production_timeseries( nice_name: bool | None = True, remove_sns_weights: bool = True, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 month: int | None = None, **kwargs, ) -> tuple: @@ -227,7 +229,7 @@ def plot_transportation_production_timeseries( nice_name: bool | None = True, remove_sns_weights: bool = True, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 month: int | None = None, **kwargs, ) -> tuple: @@ -963,7 +965,7 @@ def plot_sector_dr_timeseries( state: str | None = None, nice_name: bool | None = True, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 month: int | None = None, **kwargs, ) -> tuple: diff --git a/workflow/scripts/summary_sector.py b/workflow/scripts/summary_sector.py index c6b4b2263..00cb775e6 100644 --- a/workflow/scripts/summary_sector.py +++ b/workflow/scripts/summary_sector.py @@ -2,6 +2,9 @@ import logging +# Optional used as 'arg: callable | None = None' gives TypeError with py3.11 +from typing import Optional + import pandas as pd import pypsa from constants_sector import Transport @@ -362,7 +365,7 @@ def get_sector_production_timeseries( remove_sns_weights: bool = False, state: str | None = None, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 ) -> pd.DataFrame: """ Gets timeseries production to meet sectoral demand. @@ -394,7 +397,7 @@ def get_power_production_timeseries( remove_sns_weights: bool = False, state: str | None = None, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 ) -> pd.DataFrame: """ Gets power timeseries production to meet sectoral demand. @@ -434,7 +437,7 @@ def get_sector_production_timeseries_by_carrier( remove_sns_weights: bool = False, state: str | None = None, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 ) -> pd.DataFrame: """Gets timeseries production by carrier.""" if sector == "pwr": @@ -704,7 +707,7 @@ def get_storage_level_timeseries_carrier( remove_sns_weights: bool = True, state: str | None = None, resample: str | None = None, - resample_fn: callable | None = None, + resample_fn: Optional[callable] = None, # noqa: UP007 make_positive: bool | None = False, **kwargs, ) -> pd.DataFrame: From 45e7b4078c575d80cf0e4db7c671e80edae9d08e Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 13:11:04 -0800 Subject: [PATCH 65/75] add uv and ruff badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 090856b5e..56bd55f39 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![DOI](https://zenodo.org/badge/500892486.svg)](https://zenodo.org/doi/10.5281/zenodo.10815964) [![Documentation Status](https://readthedocs.org/projects/pypsa-usa/badge/?version=latest)](https://pypsa-usa.readthedocs.io/en/latest/?badge=latest) +[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) # PyPSA-USA: An Open-Source Energy System Optimization Model for the United States From 5273c5825eadb82deb5c56ba3e51277afb2428b1 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 17:11:03 -0800 Subject: [PATCH 66/75] sector plots update --- workflow/scripts/plot_sankey_carbon.py | 4 ++-- workflow/scripts/plot_sankey_energy.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/workflow/scripts/plot_sankey_carbon.py b/workflow/scripts/plot_sankey_carbon.py index b3ce38be1..d744e2c45 100644 --- a/workflow/scripts/plot_sankey_carbon.py +++ b/workflow/scripts/plot_sankey_carbon.py @@ -300,7 +300,7 @@ def assign_link_color(row: pd.Series) -> str: fig_name_html.parent.mkdir(parents=True) fig.write_html(str(fig_name_html)) - fig.write_image(str(fig_name_png)) + # fig.write_image(str(fig_name_png)) # plot system level @@ -349,4 +349,4 @@ def assign_link_color(row: pd.Series) -> str: fig_name_html.parent.mkdir(parents=True) fig.write_html(str(fig_name_html)) - fig.write_image(str(fig_name_png)) + # fig.write_image(str(fig_name_png)) diff --git a/workflow/scripts/plot_sankey_energy.py b/workflow/scripts/plot_sankey_energy.py index 6d5e5f492..5ff33136d 100644 --- a/workflow/scripts/plot_sankey_energy.py +++ b/workflow/scripts/plot_sankey_energy.py @@ -682,7 +682,7 @@ def assign_link_color(row: pd.Series) -> str: fig_name_html.parent.mkdir(parents=True) fig.write_html(str(fig_name_html)) - fig.write_image(str(fig_name_png)) + # fig.write_image(str(fig_name_png)) # plot system level @@ -734,4 +734,4 @@ def assign_link_color(row: pd.Series) -> str: fig_name_html.parent.mkdir(parents=True) fig.write_html(str(fig_name_html)) - fig.write_image(str(fig_name_png)) + # fig.write_image(str(fig_name_png)) From ba73e12f32b3092d33e0966963259a516a938648 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 17:14:01 -0800 Subject: [PATCH 67/75] env updates --- docs/source/about-install.md | 34 +- docs/source/contributing.md | 16 +- pyproject.toml | 2 +- uv.lock | 645 +------------------------- workflow/envs/dev.yaml | 19 + workflow/rules/postprocess_sector.smk | 4 +- 6 files changed, 60 insertions(+), 660 deletions(-) create mode 100644 workflow/envs/dev.yaml diff --git a/docs/source/about-install.md b/docs/source/about-install.md index d7fad8ae0..3cb309ecd 100644 --- a/docs/source/about-install.md +++ b/docs/source/about-install.md @@ -29,37 +29,39 @@ bash init_pypsa_usa.sh ## Step 3: Set-up Environment (mamba or UV) -### `uv` installation +PyPSA-USA can be managed though either [`UV`](https://github.com/astral-sh/uv) or [`mamba`](https://github.com/mamba-org/mamba). Users only need to install one, not both! -UV is a new python package managment tool from the creators of mamba. It replaces mamba, conda, and pip commands for one package and virtual environment managment tool. Instructions for installing [uv](https://docs.astral.sh/uv/getting-started/installation/). +```{seealso} +If you are planning to develop `PyPSA-USA`, please see our [contribution guidelines](./contributing.md#code-contributions) for installing additional dependencies. +``` + +### Step 3a: `uv` installation -with UV installed, you can create and sync a new environment with: +[`UV`](https://docs.astral.sh/uv/) is a new python package managment tool from [`Astral`](https://astral.sh/), the creators of [`ruff`](https://github.com/astral-sh/ruff). It replaces `mamba`, `conda`, and `pip` commands for one package and virtual environment managment tool. Instructions for installing `UV` can be found [here](https://docs.astral.sh/uv/getting-started/installation/). + +Once `UV` is installed, you can activate the environemnt with: ```console uv venv source .venv/bin/activate -uv pip sync pyproject.toml ``` -### `mamba` Installation +```{warning} +If you are migrating from `mamba`/`conda`, you may need to install system level dependencies that conda has previously handeled. These include, `HDF5` and `GDAL>=3.1` libraries, and if running sector studies, . +``` + +### Step 3b: `mamba` Installation -PyPSA-USA uses conda/mamba to manage project dependencies. You can download and install mamba following the [instructions](https://mamba.readthedocs.io/en/latest/mamba-installation.html). Follow links for mambaforge installation. There are two ways to install mamba, the first (recommended) method will start with a fresh install, meaning if you have previously installed conda environments, you will need to recreate these conda envs. If you already have conda installed and do not wish to install mamba, you can follow the same set of instructions replacing any `mamba` with `conda` +Alternatively, PyPSA-USA can manage project dependencies through `conda`/`mamba`. You can download and install `mamba` following the [instructions](https://mamba.readthedocs.io/en/latest/mamba-installation.html). Follow links for mambaforge installation. There are two ways to install `mamba`, the first (recommended) method will start with a fresh install, meaning if you have previously installed `conda` environments, you will need to recreate these `conda` envs. If you already have `conda` installed and do not wish to install `mamba`, you can follow the same set of instructions replacing any `mamba` with `conda` -Once mamba is installed, use the environment file within your git repository to activate the `pypsa-usa` conda environment. This step can take ~10-20 minutes. After creating the mamba environment, you will only need to activate it before running the snakemake workflow. +Once `mamba` is installed, use the environment file within the git repository to create the PyPSA-USA conda environment. This step can take ~10-20 minutes. After creating the mamba environment, you will need to activate it before running the snakemake workflow. ```console mamba env create -f workflow/envs/environment.yaml mamba activate pypsa-usa ``` -You also have the option to use miniconda. Download [Miniconda](https://docs.conda.io/en/latest/miniconda.html) following their [instructions](https://docs.conda.io/en/latest/miniconda.html). - - - - -```{seealso} -If you are planning to develop PyPSA-USA, please see our [contribution guidelines](./contributing.md#code-contributions) for installing additional dependencies -``` +You also have the option to use `miniconda`. Download [`miniconda`](https://docs.conda.io/en/latest/miniconda.html) following their [instructions](https://docs.conda.io/en/latest/miniconda.html). ## Step 4: Install a Solver @@ -78,6 +80,6 @@ and the non-free, commercial software (for some of which free academic licenses ## Step 5: Get an EIA API Key -The pypsa-usa workflow leverages the EIA API in several steps. The default configuration activates dynamic fuel-cost prices, which requires EIA API key. You can quickly get your key by completing this [form](https://www.eia.gov/opendata/register.php). +The PyPSA-USA workflow leverages the EIA API in several steps. The default configuration activates dynamic fuel-cost prices, which requires EIA API key. You can quickly get your key by completing this [form](https://www.eia.gov/opendata/register.php). The API key will be emailed to you, and you can copy the key into the `config.api.yaml` file. diff --git a/docs/source/contributing.md b/docs/source/contributing.md index 6fd9b67b2..a9a60ddac 100644 --- a/docs/source/contributing.md +++ b/docs/source/contributing.md @@ -72,10 +72,22 @@ copy of the code under your account on the repository service. ### 3. Install developer dependencies -If you plan on contributing to the respository, please install these packages into your activated mamba environment +If you plan on contributing to the respository, please install developer dependencies. + +#### 3a. If using UV + +Run the following command from the activated `pypsa-usa` conda environment. + +```console +uv sync +``` + +#### 3b. If using conda/mamba + +Run the following command from the activated `pypsa-usa` conda environment. ```console -python -m pip install -e ".[dev]" +conda env update --name my_env --file environment.yaml ``` ### 4. Install pre-commit hooks: diff --git a/pyproject.toml b/pyproject.toml index 03e6013d9..94eb340de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ ] license = {file = "LICENSE.md"} readme = "README.md" +requires-python = ">=3.11, <3.12.0" dependencies = [ "atlite==0.3.0", "cartopy==0.23.0", @@ -35,7 +36,6 @@ dependencies = [ "graphviz>=0.20.3", "gurobipy==11.0.3", "highspy>=1.9.0", - "kaleido==0.4.0rc5", "linopy==0.3.14", "matplotlib==3.8.0", "netcdf4==1.6.4", diff --git a/uv.lock b/uv.lock index a8bcf72c6..e1f8d7d35 100644 --- a/uv.lock +++ b/uv.lock @@ -1,9 +1,5 @@ version = 1 -requires-python = ">=3.11" -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version < '3.12'", -] +requires-python = ">=3.11, <3.12.0" [[package]] name = "accessible-pygments" @@ -42,7 +38,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } wheels = [ @@ -67,15 +63,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, ] -[[package]] -name = "async-timeout" -version = "5.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, -] - [[package]] name = "atlite" version = "0.3.0" @@ -156,16 +143,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/da/f8371965e5fb167e815d68a1ddb5543e6b094a2abc2f8c0330d6188244e8/blosc2-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3213763e8ed8f0dd81393e5ecf70fa6a498160aaec6611eb7d37d462b2814824", size = 4254723 }, { url = "https://files.pythonhosted.org/packages/df/61/7c36c780f29f11e4e7627c5c8475788e8fc539b13d6352dab59e731771aa/blosc2-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d2a11c4241e4f718931d0ed7ce2519badd9a326c1cf631ac1355b6921d732f9", size = 4408605 }, { url = "https://files.pythonhosted.org/packages/e2/f0/1dd1fa40acd8064d6895bd71052e295876e8bb16ad83fa0d48fc1f5bd6c6/blosc2-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a23b23dab715a3918564c70ffc2f75d0c103f80be41718959fa55bc805f66b6", size = 2176712 }, - { url = "https://files.pythonhosted.org/packages/0d/b8/e4fcf10610de0a69fca8a55a4b7a636386a30f376a48101b305645dab696/blosc2-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff945f653c111aa02008bc530b48565499a806899f3ff5ff6ea7f9e73e9a660b", size = 3986559 }, - { url = "https://files.pythonhosted.org/packages/87/fb/9ed609b59c3303f456db5fc5c0231cd441d7630f1777685fab8b1b56329d/blosc2-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a8ba5eb416d125f4dac9ae89fe1915b7ef0d84ee876e42815cb3093f998cb4e5", size = 3358355 }, - { url = "https://files.pythonhosted.org/packages/6e/d1/0710966dc56924cce66bdb44f9bfebca342699fa12780ca6eb9622fc25b6/blosc2-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecba37aa87b2a2974a8bc1316d7c85e1d42e2f4e0aef38267ae1e3d55d8cdf30", size = 4234403 }, - { url = "https://files.pythonhosted.org/packages/00/20/1aaa380bef966598ecdb0c0ef8daea8d3451acaaa0abd9c29b8eede67871/blosc2-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5567bc9d966f7215a81f4e2ba24628d5d9e66c87b79a3d011ffdd2ffe40b74ee", size = 4392236 }, - { url = "https://files.pythonhosted.org/packages/c8/09/a2bff6e1b87c0addc93e963533cb46834512f9be435a424cd569927e8f0c/blosc2-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a209aec1f365063f0a887a3fb6f37690da11d0f35411cc538bab25fd3d71498", size = 2171007 }, - { url = "https://files.pythonhosted.org/packages/89/eb/93b5b83220121bd177f8392d358ebc3a21586aa1e9916feac653d8ce6cb1/blosc2-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7e5220b83cf336391f883eba745206450efae766ed40c237b9ea41877306ae0", size = 3984594 }, - { url = "https://files.pythonhosted.org/packages/34/c8/5280f3f61a59ad4170a42ff6975bffd362d720530a9c0b52d0e849d68c33/blosc2-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8da04add23d4229bbfdd2a3be3baaa0c8e3035eec02ff484a9ca97c76aff2ed7", size = 3356546 }, - { url = "https://files.pythonhosted.org/packages/74/b9/a252f0723b52a220295cb7a90ba5dd60b8d85a69246d933bc35db0704f99/blosc2-3.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d949e86522a6001ed8e8bca0a29c8638c5b3a0900376a48a9536864cccb4b89", size = 4233755 }, - { url = "https://files.pythonhosted.org/packages/4b/ad/84329757bb623b2c427adb26018aa95b6465a85900af57c5df3666918870/blosc2-3.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e7cdad6c2361d2e2ed2de33f18ef073a3d4dc7a9beb4d1db172005e09b229e", size = 4391208 }, - { url = "https://files.pythonhosted.org/packages/79/86/e2d0aad6916f5d2160b8a6b513b3b92c2a15b669a243b6e10ba059473453/blosc2-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:37de3eab82df2738b2d81fa2fbfa46efdb108b7b54b37f1255f358f90ab7212d", size = 2171060 }, ] [[package]] @@ -184,20 +161,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/1e/9310f058ddee71798a76ab15c5c1ad71f0a5c3c6348f7faab9b6da038484/Bottleneck-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c7d29c044a3511b36fd744503c3e697e279c273a8477a6d91a2831d04fd19e0", size = 360282 }, { url = "https://files.pythonhosted.org/packages/96/cb/c1f2a37e86e9fa47845259f0a8f32d550f7f27b908432369de055be9f7c4/Bottleneck-1.4.2-cp311-cp311-win32.whl", hash = "sha256:c663cbba8f52011fd82ee08c6a85c93b34b19e0e7ebba322d2d67809f34e0597", size = 106936 }, { url = "https://files.pythonhosted.org/packages/d3/eb/3fd23404bbc612cf9e4883c3c2b359bd14528e234d5c40bb29bcfd591ef8/Bottleneck-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:89651ef18c06616850203bf8875c958c5d316ea48d8ba60d9b450199d39ae391", size = 111617 }, - { url = "https://files.pythonhosted.org/packages/d2/26/6f5124e31a67f75e2a3b9239cc382145326e91fc45e7d7bc9ebffa05fdfa/Bottleneck-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a74ddd0417f42eeaba37375f0fc065b28451e0fba45cb2f99e88880b10b3fa43", size = 98681 }, - { url = "https://files.pythonhosted.org/packages/c4/93/e100b6eda77f2aecf5f16157b8c04dd3463913ba188b582650cd77ccf42b/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:070d22f2f62ab81297380a89492cca931e4d9443fa4b84c2baeb52db09c3b1b4", size = 365422 }, - { url = "https://files.pythonhosted.org/packages/82/2b/c6fea2bb048d04c13b8564052818a198d50ce58d5f439ec69c2b0c458703/Bottleneck-1.4.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc4e7645bd425c05e05acd5541e9e09cb4179e71164e862f082561bf4509eac", size = 361844 }, - { url = "https://files.pythonhosted.org/packages/8f/4c/811475885bd60cf0cb28822568d0c0c3c7d7de4fbccd2ebb66863e7dc726/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:037315c56605128a39f77d19af6a6019dc8c21a63694a4bfef3c026ed963be2e", size = 370369 }, - { url = "https://files.pythonhosted.org/packages/fd/ee/0a8157e6bbd2168bf6171811534a5a73a35f54c453dd7d86a323773b5bd7/Bottleneck-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99778329331d5fae8df19772a019e8b73ba4d9d1650f110cd995ab7657114db0", size = 361786 }, - { url = "https://files.pythonhosted.org/packages/fa/6b/e8fda0510b8fa0f3f9a3586efc941abe9d546198e95ae5690c3c83370b36/Bottleneck-1.4.2-cp312-cp312-win32.whl", hash = "sha256:7363b3c8ce6ca433779cd7e96bcb94c0e516dcacadff0011adcbf0b3ac86bc9d", size = 107149 }, - { url = "https://files.pythonhosted.org/packages/22/25/908b75a329a05b82d717661aa95a1968d9dae0e68c654d5e16bfe0d6fbb6/Bottleneck-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:48c6b9d9287c4102b803fcb01ae66ae7ef6b310b711b4b7b7e23bf952894dc05", size = 111766 }, - { url = "https://files.pythonhosted.org/packages/2e/65/148e146ca8c16af9881a0db1d8d1849d49a5186fc9f065c79a8d25d6fc0c/Bottleneck-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c1c885ad02a6a8fa1f7ee9099f29b9d4c03eb1da2c7ab25839482d5cce739021", size = 98701 }, - { url = "https://files.pythonhosted.org/packages/80/96/6540ac9a9943b0d6f0199eddbde55e878f970d2bdda31207dc3e7a195c2b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7a1b023de1de3d84b18826462718fba548fed41870df44354f9ab6a414ea82f", size = 365443 }, - { url = "https://files.pythonhosted.org/packages/d0/aa/ccae264aac3b2621fa8a98c7afe033f22a352467cbf85fa2799d176ec31b/Bottleneck-1.4.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9dbaf737b605b30c81611f2c1d197c2fd2e46c33f605876c1d332d3360c4fc", size = 361849 }, - { url = "https://files.pythonhosted.org/packages/f3/b3/5f96d7bb23a291b835bf0a34eec359c55613f6c4262ad1bb161d897499c0/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7ebbcbe5d4062e37507b9a81e2aacdb1fcccc6193f7feff124ef2b5a6a5eb740", size = 370654 }, - { url = "https://files.pythonhosted.org/packages/51/05/9d1ababa3fd34014b708351270307320c0bc595d2d66c2ba2b9b92f0d618/Bottleneck-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:964f6ac4118ddab3bbbac79d4f726b093459be751baba73ee0aa364666e8068e", size = 362054 }, - { url = "https://files.pythonhosted.org/packages/92/e3/123488804830604432f84a2c43e611b8e1971e230b9466a7315850d22a58/Bottleneck-1.4.2-cp313-cp313-win32.whl", hash = "sha256:2db287f6ecdbb1c998085eca9b717fec2bfc48a4ab6ae070a9820ba8ab59c90b", size = 107160 }, - { url = "https://files.pythonhosted.org/packages/54/f0/e1640ccd8468c61693092f38f835ef35a68a1ea72c3388683148b3800aa6/Bottleneck-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:26b5f0531f7044befaad95c20365dd666372e66bdacbfaf009ff65d60285534d", size = 111774 }, ] [[package]] @@ -227,10 +190,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/96/02e0445ab41997f942e3d59c9fc3d08220e9b2651f85ac026a9dff7ce28f/Cartopy-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dece2aa8d5ff7bf989ded6b5f07c980fb5bb772952bc7cdeab469738abdecee", size = 10921586 }, { url = "https://files.pythonhosted.org/packages/07/31/11428c479d9eddb5a1f1bef9ad51320db35c612f992163b72739581eef6e/Cartopy-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9dfd28352dc83d6b4e4cf85d84cb50fc4886d4c1510d61f4c7cf22477d1156f", size = 11662265 }, { url = "https://files.pythonhosted.org/packages/e1/a2/a450fe2d20be42c6666a5146697a6ea1ab9caa1940cce892af02b492c3db/Cartopy-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2671b5354e43220f8e1074e7fe30a8b9f71cb38407c78e51db9c97772f0320b", size = 10904808 }, - { url = "https://files.pythonhosted.org/packages/72/82/6d44fc5a4e7ff859b4ca2dd4eaeae680c75e2e3c16ee6f0dde02fc0fad43/Cartopy-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:80b9fd666fd47f6370d29f7ad4e352828d54aaf688a03d0b83b51e141cfd77fa", size = 10931850 }, - { url = "https://files.pythonhosted.org/packages/b2/96/f2f271e971185e38e940498421a97cb1e5dac970919bd5d65c2c430fe1f5/Cartopy-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43e36b8b7e7e373a5698757458fd28fafbbbf5f3ebbe2d378f6a5ec3993d6dc0", size = 10921507 }, - { url = "https://files.pythonhosted.org/packages/82/7d/77f96f369f23182f42b7e124536d7022485c31e51204d0673fc0a43ea28d/Cartopy-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550173b91155d4d81cd14b4892cb6cabe3dd32bd34feacaa1ec78c0e56287832", size = 11660854 }, - { url = "https://files.pythonhosted.org/packages/a2/65/6dae4e0a1414251a7bc3c2d9f87853419f921bac991e35701b938b7713dc/Cartopy-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:55219ee0fb069cc3254426e87382cde03546e86c3f7c6759f076823b1e3a44d9", size = 10904186 }, ] [[package]] @@ -280,18 +239,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/81/6b30815698ede50f89013f25e46d66ed3a290b8a2d6b97f95bacbbe1eb5c/cftime-1.6.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ea0965a4c87739aebd84fe8eed966e5809d10065eeffd35c99c274b6f8da15", size = 1415218 }, { url = "https://files.pythonhosted.org/packages/24/0d/73ab09a32da1478d3ef5f4ab6c59d42f2db2a2383b427c87e05ad81b71ad/cftime-1.6.4.post1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:800a18aea4e8cb2b206450397cb8a53b154798738af3cdd3c922ce1ca198b0e6", size = 1450704 }, { url = "https://files.pythonhosted.org/packages/79/b1/6551603f8ea31de55913c84e4def3c36670563bdea6e195fcc4b6225ddf7/cftime-1.6.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:5dcfc872f455db1f12eabe3c3ba98e93757cd60ed3526a53246e966ccde46c8a", size = 190200 }, - { url = "https://files.pythonhosted.org/packages/50/81/0bb28d54088a61592f61a11e7fcabcea6d261c47af79e18d0f9cbcd940ae/cftime-1.6.4.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a590f73506f4704ba5e154ef55bfbaed5e1b4ac170f3caeb8c58e4f2c619ee4e", size = 226615 }, - { url = "https://files.pythonhosted.org/packages/f3/1e/38dbbf8a828dfb5e0e6e5c912818b77aacf2e7bcb97b262ac6126beeb29f/cftime-1.6.4.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:933cb10e1af4e362e77f513e3eb92b34a688729ddbf938bbdfa5ac20a7f44ba0", size = 209193 }, - { url = "https://files.pythonhosted.org/packages/9b/60/0db884c76311ecaaf31f628aa9358beae5fcb0fbbdc2eb0b790a93aa258f/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf17a1b36f62e9e73c4c9363dd811e1bbf1170f5ac26d343fb26012ccf482908", size = 1320215 }, - { url = "https://files.pythonhosted.org/packages/8d/7d/2d5fc7af06da4f3bdea59a204f741bf7a30bc5019355991b2f083e557e4e/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e18021f421aa26527bad8688c1acf0c85fa72730beb6efce969c316743294f2", size = 1367426 }, - { url = "https://files.pythonhosted.org/packages/5d/ab/e8b26d05323fc5629356c82a7f64026248f121ea1361b49df441bbc8f2d7/cftime-1.6.4.post1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5835b9d622f9304d1c23a35603a0f068739f428d902860f25e6e7e5a1b7cd8ea", size = 1385593 }, - { url = "https://files.pythonhosted.org/packages/af/7b/ca72a075a3f660315b031d62d39a3e9cfef71f7929da2621d5120077a75f/cftime-1.6.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:7f50bf0d1b664924aaee636eb2933746b942417d1f8b82ab6c1f6e8ba0da6885", size = 178918 }, - { url = "https://files.pythonhosted.org/packages/da/d8/81f086dbdc6f5a4e0bb068263471f1d12861b72562fe8c18df38268e4e29/cftime-1.6.4.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c89766ebf088c097832ea618c24ed5075331f0b7bf8e9c2d4144aefbf2f1850", size = 223418 }, - { url = "https://files.pythonhosted.org/packages/4a/cc/60a825d92a4023655e330470758280a31e7b82665ef77d0e2a0fe71ea958/cftime-1.6.4.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f27113f7ccd1ca32881fdcb9a4bec806a5f54ae621fc1c374f1171f3ed98ef2", size = 207395 }, - { url = "https://files.pythonhosted.org/packages/ca/90/f5b26949899decce262fc76a1e64915b92050473114e0160cd6f7297f854/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da367b23eea7cf4df071c88e014a1600d6c5bbf22e3393a4af409903fa397e28", size = 1318113 }, - { url = "https://files.pythonhosted.org/packages/c3/f8/6f13d37abb7ade46e65a08acc31af776a96dde0eb569e05d4c4b01422ba6/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c5c83cdf09d73aa94c7bc34925edd93c5f2c7dd28e074f568f7e376271a0", size = 1366034 }, - { url = "https://files.pythonhosted.org/packages/fa/08/335cb17f3b708f9a24f96ca4abb00889c7aa20b0ae273313e7c11faf1f97/cftime-1.6.4.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6b731c7133d17b479ca0c3c46a7a04f96197f0a4d753f4c2284c3ff0447279b4", size = 1390156 }, - { url = "https://files.pythonhosted.org/packages/f3/2d/980323fb5ec1ef369604b61ba259a41d0336cc1a85b639ed7bd210bd1290/cftime-1.6.4.post1-cp313-cp313-win_amd64.whl", hash = "sha256:d2a8c223faea7f1248ab469cc0d7795dd46f2a423789038f439fee7190bae259", size = 178496 }, ] [[package]] @@ -313,48 +260,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, - { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, - { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, - { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, - { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, - { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, - { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, - { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, - { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, - { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, - { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, - { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, - { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, - { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, - { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, - { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, - { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, - { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, - { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, - { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, - { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, - { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, - { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, - { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, - { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, - { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, - { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] -[[package]] -name = "choreographer" -version = "1.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "logistro" }, - { name = "simplejson" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fb/5f/2b8e4d08f4459180101bcc2e9d14eaf3060e933d1fcbe05598a667752c2f/choreographer-1.0.3.tar.gz", hash = "sha256:70c949767d7627009c0df62ae69ae5975537a31fad9ce69f8f8c754ac9958349", size = 35994 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/df/99086b752c33d9bf58b5016069d805175311bead59e7b47f46efed178b09/choreographer-1.0.3-py3-none-any.whl", hash = "sha256:ff6fbaf634f5dbd7ace2ea5374034cf84342494b16e5062ef1b211815fc27d67", size = 41716 }, -] - [[package]] name = "click" version = "8.1.8" @@ -443,36 +351,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/4a/c788d0bdbf32c8113c2354493ed291f924d4793c4a2e85b69e737a21a658/contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", size = 1325995 }, { url = "https://files.pythonhosted.org/packages/a6/e6/a2f351a90d955f8b0564caf1ebe4b1451a3f01f83e5e3a414055a5b8bccb/contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375", size = 174396 }, { url = "https://files.pythonhosted.org/packages/a8/7e/cd93cab453720a5d6cb75588cc17dcdc08fc3484b9de98b885924ff61900/contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", size = 219787 }, - { url = "https://files.pythonhosted.org/packages/37/6b/175f60227d3e7f5f1549fcb374592be311293132207e451c3d7c654c25fb/contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", size = 271494 }, - { url = "https://files.pythonhosted.org/packages/6b/6a/7833cfae2c1e63d1d8875a50fd23371394f540ce809d7383550681a1fa64/contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", size = 255444 }, - { url = "https://files.pythonhosted.org/packages/7f/b3/7859efce66eaca5c14ba7619791b084ed02d868d76b928ff56890d2d059d/contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", size = 307628 }, - { url = "https://files.pythonhosted.org/packages/48/b2/011415f5e3f0a50b1e285a0bf78eb5d92a4df000553570f0851b6e309076/contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", size = 347271 }, - { url = "https://files.pythonhosted.org/packages/84/7d/ef19b1db0f45b151ac78c65127235239a8cf21a59d1ce8507ce03e89a30b/contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", size = 318906 }, - { url = "https://files.pythonhosted.org/packages/ba/99/6794142b90b853a9155316c8f470d2e4821fe6f086b03e372aca848227dd/contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", size = 323622 }, - { url = "https://files.pythonhosted.org/packages/3c/0f/37d2c84a900cd8eb54e105f4fa9aebd275e14e266736778bb5dccbf3bbbb/contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", size = 1266699 }, - { url = "https://files.pythonhosted.org/packages/3a/8a/deb5e11dc7d9cc8f0f9c8b29d4f062203f3af230ba83c30a6b161a6effc9/contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", size = 1326395 }, - { url = "https://files.pythonhosted.org/packages/1a/35/7e267ae7c13aaf12322ccc493531f1e7f2eb8fba2927b9d7a05ff615df7a/contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", size = 175354 }, - { url = "https://files.pythonhosted.org/packages/a1/35/c2de8823211d07e8a79ab018ef03960716c5dff6f4d5bff5af87fd682992/contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", size = 220971 }, - { url = "https://files.pythonhosted.org/packages/9a/e7/de62050dce687c5e96f946a93546910bc67e483fe05324439e329ff36105/contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", size = 271548 }, - { url = "https://files.pythonhosted.org/packages/78/4d/c2a09ae014ae984c6bdd29c11e74d3121b25eaa117eca0bb76340efd7e1c/contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", size = 255576 }, - { url = "https://files.pythonhosted.org/packages/ab/8a/915380ee96a5638bda80cd061ccb8e666bfdccea38d5741cb69e6dbd61fc/contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", size = 306635 }, - { url = "https://files.pythonhosted.org/packages/29/5c/c83ce09375428298acd4e6582aeb68b1e0d1447f877fa993d9bf6cd3b0a0/contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", size = 345925 }, - { url = "https://files.pythonhosted.org/packages/29/63/5b52f4a15e80c66c8078a641a3bfacd6e07106835682454647aca1afc852/contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", size = 318000 }, - { url = "https://files.pythonhosted.org/packages/9a/e2/30ca086c692691129849198659bf0556d72a757fe2769eb9620a27169296/contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", size = 322689 }, - { url = "https://files.pythonhosted.org/packages/6b/77/f37812ef700f1f185d348394debf33f22d531e714cf6a35d13d68a7003c7/contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", size = 1268413 }, - { url = "https://files.pythonhosted.org/packages/3f/6d/ce84e79cdd128542ebeb268f84abb4b093af78e7f8ec504676673d2675bc/contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", size = 1326530 }, - { url = "https://files.pythonhosted.org/packages/72/22/8282f4eae20c73c89bee7a82a19c4e27af9b57bb602ecaa00713d5bdb54d/contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", size = 175315 }, - { url = "https://files.pythonhosted.org/packages/e3/d5/28bca491f65312b438fbf076589dcde7f6f966b196d900777f5811b9c4e2/contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", size = 220987 }, - { url = "https://files.pythonhosted.org/packages/2f/24/a4b285d6adaaf9746e4700932f579f1a7b6f9681109f694cfa233ae75c4e/contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", size = 285001 }, - { url = "https://files.pythonhosted.org/packages/48/1d/fb49a401b5ca4f06ccf467cd6c4f1fd65767e63c21322b29b04ec40b40b9/contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", size = 268553 }, - { url = "https://files.pythonhosted.org/packages/79/1e/4aef9470d13fd029087388fae750dccb49a50c012a6c8d1d634295caa644/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", size = 310386 }, - { url = "https://files.pythonhosted.org/packages/b0/34/910dc706ed70153b60392b5305c708c9810d425bde12499c9184a1100888/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", size = 349806 }, - { url = "https://files.pythonhosted.org/packages/31/3c/faee6a40d66d7f2a87f7102236bf4780c57990dd7f98e5ff29881b1b1344/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", size = 321108 }, - { url = "https://files.pythonhosted.org/packages/17/69/390dc9b20dd4bb20585651d7316cc3054b7d4a7b4f8b710b2b698e08968d/contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", size = 327291 }, - { url = "https://files.pythonhosted.org/packages/ef/74/7030b67c4e941fe1e5424a3d988080e83568030ce0355f7c9fc556455b01/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", size = 1263752 }, - { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, - { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, - { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, ] [[package]] @@ -492,7 +370,7 @@ dependencies = [ { name = "click" }, { name = "cloudpickle" }, { name = "fsspec" }, - { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "importlib-metadata" }, { name = "packaging" }, { name = "partd" }, { name = "pyyaml" }, @@ -647,14 +525,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/dc/f542b404de04ed84c7ef91df96ff5d2252430f1ea09b9fb5178a1a58f718/duckdb-0.10.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:009dd9d2cdbd3b061a9efbdfc79f2d1a8377bcf49f1e5f430138621f8c083a6c", size = 16399505 }, { url = "https://files.pythonhosted.org/packages/50/47/a0ad83025049fcdb2af6e0c66b754c6cacce7f1aaf457b61ea89354730b1/duckdb-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:878f06766088090dad4a2e5ee0081555242b2e8dcb29415ecc97e388cf0cf8d8", size = 18385038 }, { url = "https://files.pythonhosted.org/packages/dc/f9/79d37cbe067bf2fc9fbe4a09d0e345c264a9f221fb6b4bcfc2ef80bb6a72/duckdb-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:713ff0a1fb63a6d60f454acf67f31656549fb5d63f21ac68314e4f522daa1a89", size = 9569568 }, - { url = "https://files.pythonhosted.org/packages/da/62/3572db3f4f854c5f668dc477a989241ff72c8d7aadb605de798be702a8f6/duckdb-0.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9c0ee450dfedfb52dd4957244e31820feef17228da31af6d052979450a80fd19", size = 30331347 }, - { url = "https://files.pythonhosted.org/packages/68/c8/aa52abf16b182c4cc1f67ac33a68828a60dcf23ff7399792c59d9e580f43/duckdb-0.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ff79b2ea9994398b545c0d10601cd73565fbd09f8951b3d8003c7c5c0cebc7cb", size = 16332722 }, - { url = "https://files.pythonhosted.org/packages/ef/ca/ef3e7efafaf12f93c319c45f5e865f2e1a9819acc647da590baba8d3c136/duckdb-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6bdf1aa71b924ef651062e6b8ff9981ad85bec89598294af8a072062c5717340", size = 14048257 }, - { url = "https://files.pythonhosted.org/packages/1f/92/5ac1433dbf54a759bceeb2658fd65ef8239e0ad433b965fcb85b5b25bd6c/duckdb-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0265bbc8216be3ced7b377ba8847128a3fc0ef99798a3c4557c1b88e3a01c23", size = 16584351 }, - { url = "https://files.pythonhosted.org/packages/8d/28/8279f26af136c8046e813992c9b7643051082285d4e15866abbd9ccfa6c0/duckdb-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d418a315a07707a693bd985274c0f8c4dd77015d9ef5d8d3da4cc1942fd82e0", size = 17830443 }, - { url = "https://files.pythonhosted.org/packages/7e/c8/6c184642bc2e28f857ae1fc248c920b51525fda93308cc548b525c6a944b/duckdb-0.10.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2828475a292e68c71855190b818aded6bce7328f79e38c04a0c75f8f1c0ceef0", size = 16403736 }, - { url = "https://files.pythonhosted.org/packages/a9/e5/6c04d248d44206678e1c85b11f3041f828ef34d815fd85995603607006b2/duckdb-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c3aaeaae2eba97035c65f31ffdb18202c951337bf2b3d53d77ce1da8ae2ecf51", size = 18389565 }, - { url = "https://files.pythonhosted.org/packages/0c/a7/49f0a479b2372a0a42bdd971a6a474e9ba4c7b83279850b3564536e26d73/duckdb-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c51790aaaea97d8e4a58a114c371ed8d2c4e1ca7cbf29e3bdab6d8ccfc5afc1e", size = 9569987 }, ] [[package]] @@ -707,22 +577,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/8c/e503863adf7a6aeff7b960e2f66fa44dd0c29a7a8b79765b2821950d7b05/fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0", size = 5045817 }, { url = "https://files.pythonhosted.org/packages/2b/50/79ba3b7e42f4eaa70b82b9e79155f0f6797858dc8a97862428b6852c6aee/fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b", size = 2154426 }, { url = "https://files.pythonhosted.org/packages/3b/90/4926e653041c4116ecd43e50e3c79f5daae6dcafc58ceb64bc4f71dd4924/fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05", size = 2200937 }, - { url = "https://files.pythonhosted.org/packages/39/32/71cfd6877999576a11824a7fe7bc0bb57c5c72b1f4536fa56a3e39552643/fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9", size = 2747757 }, - { url = "https://files.pythonhosted.org/packages/15/52/d9f716b072c5061a0b915dd4c387f74bef44c68c069e2195c753905bd9b7/fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f", size = 2279007 }, - { url = "https://files.pythonhosted.org/packages/d1/97/f1b3a8afa9a0d814a092a25cd42f59ccb98a0bb7a295e6e02fc9ba744214/fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2", size = 4783991 }, - { url = "https://files.pythonhosted.org/packages/95/70/2a781bedc1c45a0c61d29c56425609b22ed7f971da5d7e5df2679488741b/fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563", size = 4855109 }, - { url = "https://files.pythonhosted.org/packages/0c/02/a2597858e61a5e3fb6a14d5f6be9e6eb4eaf090da56ad70cedcbdd201685/fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a", size = 4762496 }, - { url = "https://files.pythonhosted.org/packages/f2/00/aaf00100d6078fdc73f7352b44589804af9dc12b182a2540b16002152ba4/fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28", size = 4990094 }, - { url = "https://files.pythonhosted.org/packages/bf/dc/3ff1db522460db60cf3adaf1b64e0c72b43406717d139786d3fa1eb20709/fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c", size = 2142888 }, - { url = "https://files.pythonhosted.org/packages/6f/e3/5a181a85777f7809076e51f7422e0dc77eb04676c40ec8bf6a49d390d1ff/fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba", size = 2189734 }, - { url = "https://files.pythonhosted.org/packages/a5/55/f06b48d48e0b4ec3a3489efafe9bd4d81b6e0802ac51026e3ee4634e89ba/fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692", size = 2735127 }, - { url = "https://files.pythonhosted.org/packages/59/db/d2c7c9b6dd5cbd46f183e650a47403ffb88fca17484eb7c4b1cd88f9e513/fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0", size = 2272519 }, - { url = "https://files.pythonhosted.org/packages/4d/a2/da62d779c34a0e0c06415f02eab7fa3466de5d46df459c0275a255cefc65/fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1", size = 4762423 }, - { url = "https://files.pythonhosted.org/packages/be/6a/fd4018e0448c8a5e12138906411282c5eab51a598493f080a9f0960e658f/fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea", size = 4834442 }, - { url = "https://files.pythonhosted.org/packages/6d/63/fa1dec8efb35bc11ef9c39b2d74754b45d48a3ccb2cf78c0109c0af639e8/fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3", size = 4742800 }, - { url = "https://files.pythonhosted.org/packages/dd/f4/963247ae8c73ccc4cf2929e7162f595c81dbe17997d1d0ea77da24a217c9/fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278", size = 4963746 }, - { url = "https://files.pythonhosted.org/packages/ea/e0/46f9600c39c644b54e4420f941f75fa200d9288c9ae171e5d80918b8cbb9/fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f", size = 2140927 }, - { url = "https://files.pythonhosted.org/packages/27/6d/3edda54f98a550a0473f032d8050315fbc8f1b76a0d9f3879b72ebb2cdd6/fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6", size = 2186709 }, { url = "https://files.pythonhosted.org/packages/bf/ff/44934a031ce5a39125415eb405b9efb76fe7f9586b75291d66ae5cbfc4e6/fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14", size = 1089800 }, ] @@ -815,10 +669,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/68/7ccd72bf75db54c70ec563f5f700119ea290c9a6ccac9f3191295e4739f5/gurobipy-11.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84c58e164c90fc1de8bc0aac8ad25a82b4a9d3079b5064735822cecf552b1b26", size = 27422010 }, { url = "https://files.pythonhosted.org/packages/af/53/8b5194b41760718a8cfb01010dcab5a8f464468fffacceb256df8aefa906/gurobipy-11.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d8eed5ca4831d1f10b08cd701f75483b27810734750d414a5b3cb6e4ba4c17f3", size = 13446177 }, { url = "https://files.pythonhosted.org/packages/28/41/c61b4b096a093479eb45292ec7a5d127f088a92d4748faec76e4fcd2443e/gurobipy-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9e57508f7d7a8ef154767894c693273d94bed771fa300677ae74892fde7dfc57", size = 10324769 }, - { url = "https://files.pythonhosted.org/packages/19/71/d21d15318a6f400f331e940924910aa5140da55dd84813bd9f8bdecaf707/gurobipy-11.0.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:05700979469511aedfcb4d0136c161077d085393b6a449c98e9ff7818747279c", size = 10709232 }, - { url = "https://files.pythonhosted.org/packages/ec/74/d2523173f9eb8ec66a7d892a49ca6899d75acddac418f8c81cd50d4b420a/gurobipy-11.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b752a8a4d898a3cc59b0670aa449dee8e2159d4f420f30033baf96f14a36516d", size = 27267858 }, - { url = "https://files.pythonhosted.org/packages/73/5a/4c764ccd1e9bcef3d01729af0811915f3fb36e72b544e5caf477b1dc545f/gurobipy-11.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d08a69a9884b2c7ab843c9967b44770c17c73ac5726db7a8794ae85d7c1fc446", size = 13257275 }, - { url = "https://files.pythonhosted.org/packages/f2/54/b4eea516b5e318c3522456f8a26a2fc6e8c9c657e0ed06ce5ec4f7faca4d/gurobipy-11.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:74fa0d9bace48bc0f25426b5cd70addb02a13b5ea1645494cad073a32c9ec2c4", size = 10296203 }, ] [[package]] @@ -849,26 +699,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/3b/78c8eaa0f2a6a20ddb1f8c488ba6dd787baf31fd79902301b58f5f75f72c/highspy-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43d1612e9091a2fe07bad2e24dea12f0e67bb77014c59281407cd72fd704de5b", size = 3250008 }, { url = "https://files.pythonhosted.org/packages/42/b2/c1e07d7c55e2bb6f7a5c2ee0d57e0fd6d848261a263e6e6cf419e38cfe40/highspy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:95662940d87c9f3894e8e78a405f8701af7326e28db38b524ea19119821c3b8c", size = 1563554 }, { url = "https://files.pythonhosted.org/packages/35/e9/aa8fce2ac95124bc880761856207e7b02263730a4eb1a9cfb97116a26b44/highspy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:18827d2e7e2d1b013b2c5eac0923646942700ff5683a93c23c383370861d424d", size = 1871049 }, - { url = "https://files.pythonhosted.org/packages/ae/0d/088bd34992071335839b2f62bc1fe7831e3bc57a90406b48295aa843ab37/highspy-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9509c4cdb70171a21c98fadbcd85e4f53d54b3106258a009486816153eb29847", size = 2153200 }, - { url = "https://files.pythonhosted.org/packages/16/24/507a4b7a45f5554b8593ccceaf377270fe9a0c8ffd8c057cf26986ee3658/highspy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73e430ebe85f4b6fbbebd98ae53d47b12cebfd8cbee6fffd17760efc8780ce36", size = 1843422 }, - { url = "https://files.pythonhosted.org/packages/10/f5/aea6b779925bfd449a8857c3ec527333b4a5914b6c7ded445da8cad4fd70/highspy-1.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04ed0b41e9e2e1ad4c2ce7580562cd6b45cf872d63f3c5e21e36e3d399eb9b2b", size = 2057475 }, - { url = "https://files.pythonhosted.org/packages/f9/45/1acd79703d1bdb745b0e6f596a77b7db209291ddfdba4982f61f023e5a37/highspy-1.9.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37dc773702bcbf155e70b822b652fc9b96b2e190c70d1402de8711a177e75690", size = 2372525 }, - { url = "https://files.pythonhosted.org/packages/09/75/3cb42a6d561f288f178b38e0f822a4c4f79c7bc4cace11ce895121941b6c/highspy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d2748e1c08d6d293402e2fb4cb6d44bfd4114dce438ae536a8cf8ddabe0e90", size = 2234977 }, - { url = "https://files.pythonhosted.org/packages/44/bd/ec6861d22e5a6891b84c37e711d7801eca5b70234dbac58a67415c19ba8c/highspy-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e0809e15532688f0d16c4e27dcf4bb0a67b3d7a548373710176ff1c375b55ca5", size = 3045209 }, - { url = "https://files.pythonhosted.org/packages/c2/a4/bcb297d83a658dbf4faecb6a8bdf5999d03e88ac9950ba7f36e6acbb7ebe/highspy-1.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2a3bcc624804c661edeed982dccc667abc7dabd3298633e6e3b2e1dab733953f", size = 3573139 }, - { url = "https://files.pythonhosted.org/packages/ef/c0/11c381826af55977fcefc467399e0fbe777b0ab0fab6dbb1458094acc1ba/highspy-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be067a7a775811ddd03929ce8ba71d3d8dbce079e74afda0eb8155ddf85c74f2", size = 3245431 }, - { url = "https://files.pythonhosted.org/packages/51/0a/90a8161e13281af61e12e38aeedf561a21ea921b2d96e7a2d5301f6c506e/highspy-1.9.0-cp312-cp312-win32.whl", hash = "sha256:840ad28b3a937c3129e6efe40d3ed86391a6005d5878f832eadde222cd4c1895", size = 1561810 }, - { url = "https://files.pythonhosted.org/packages/1d/c8/ec26b570a9f953bc7f5bdaaa471c1da334eb6d00d48ad75f5781dbab4c60/highspy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fcf93ef8e8440b8d900c21c33e5fc8e2041e7d54bb680bc617c5a8d71c084454", size = 1871889 }, - { url = "https://files.pythonhosted.org/packages/85/e4/9411cb5918f88894bf899f9737a67ac8e197d33c9b2119bbe4af60417cfe/highspy-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9eea8125342c2049e6e81c808e1995e78aa0ba09e505d7d0d78b5997f8c9240d", size = 2153271 }, - { url = "https://files.pythonhosted.org/packages/4d/a7/442d7720a9a01f189f36c626473c150a9fdd11cab9eb99c62cb178989ba0/highspy-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22580ac1fbce24bae9fbb906d1967d6c6798401a1f134dfc46a5f87c58791e47", size = 1843436 }, - { url = "https://files.pythonhosted.org/packages/c5/5c/6eb6ccfb16b591bbbdd22bc6d45c03e68140d5cf34cc2fcdda1ba784d489/highspy-1.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22c8217c05f3df3bf9326aba777b2808d4da5757f156e4a20058d360e6a002a6", size = 2057482 }, - { url = "https://files.pythonhosted.org/packages/b2/ed/7160f0900d5eba0c6e36398f6b8375142323ecabdfb2041e3901973d080c/highspy-1.9.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:adbf983e661b651d1c27c9a9e7d63e5c8a311986b05e3944777752060ea41b1f", size = 2372449 }, - { url = "https://files.pythonhosted.org/packages/d9/03/83ccebbbd2233e478684884c2f4ef9d680939c2336f3292d40c29d4437b1/highspy-1.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1804f7d5fbf404ebab22033728582b745a793b3c9015285eb2ab8bac97438e61", size = 2234790 }, - { url = "https://files.pythonhosted.org/packages/93/1c/6fd80e197332cac6b72bbb0e4e75b2630c3a8b62438ec11558d88223289d/highspy-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0cbf60020769bc0b1af5da8efa65f9f50397d9d441079133f76ad962524ed20f", size = 3045276 }, - { url = "https://files.pythonhosted.org/packages/91/ab/6bd4743749b7f569d67b8b3ac896c0cc87658bcfb55b816f27cce0d56161/highspy-1.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:45d47d7c5421db8075fbef930f3a3a7e4e86f14f21ed74b6e50facdb65c3ea87", size = 3573156 }, - { url = "https://files.pythonhosted.org/packages/a0/de/0de13d9b478c352ea051b10255effa8acea1615cd395a364c77dd9032030/highspy-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c0e8004bef64df1537286e5dac5fb2f3ddc152ef205d3e2e8786bcaced08e92", size = 3245380 }, - { url = "https://files.pythonhosted.org/packages/38/de/14d2f8e73fc79ab82a6f9fee81e90783d3aa15c3e21762d695090b2c98d8/highspy-1.9.0-cp313-cp313-win32.whl", hash = "sha256:a77eb1e06278ef23ed5778a3c4e9348c57d917d9197c7d2cf30acea151089b59", size = 1561805 }, - { url = "https://files.pythonhosted.org/packages/b0/e1/9cd0c9860bc562ca872a029441ac42e586c0e5ed15c0d4c470b17dd3f0b0/highspy-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:9151e47745a3605617b200f173af46bd1aee5ef06083a17e9781d806ee9ed1fd", size = 1871841 }, ] [[package]] @@ -943,7 +773,7 @@ name = "importlib-metadata" version = "8.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.12'" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } wheels = [ @@ -964,7 +794,7 @@ dependencies = [ { name = "pygments" }, { name = "stack-data" }, { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/80/4d2a072e0db7d250f134bc11676517299264ebe16d62a8619d49a78ced73/ipython-8.32.0.tar.gz", hash = "sha256:be2c91895b0b9ea7ba49d33b23e2040c352b33eb6a519cca7ce6e0c743444251", size = 5507441 } wheels = [ @@ -1045,19 +875,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, ] -[[package]] -name = "kaleido" -version = "0.4.0rc5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-timeout" }, - { name = "choreographer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/1f/53a30b6ca64ae822fb77dc0d157b8761e525015edc49b402e52e16f702ed/kaleido-0.4.0rc5.tar.gz", hash = "sha256:cab65055ca382a51f2cf1ae726d8373190f8bf97997337e316d35c23f6da27e2", size = 3312827 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/8e/93f2e978c85800c040406e42d0f469bc2f4d5a02609be12e8ad2ef78e183/kaleido-0.4.0rc5-py3-none-any.whl", hash = "sha256:6001a0c32db8b9b78502dcedc41dff2bdff7ea12f20a2c40fa3c6a3a2d9616f8", size = 3819614 }, -] - [[package]] name = "kiwisolver" version = "1.4.8" @@ -1079,49 +896,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306 }, { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966 }, { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311 }, - { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, - { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, - { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, - { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, - { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, - { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, - { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, - { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, - { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, - { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, - { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, - { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, - { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, - { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, - { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, - { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156 }, - { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555 }, - { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071 }, - { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053 }, - { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278 }, - { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139 }, - { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517 }, - { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952 }, - { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132 }, - { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997 }, - { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060 }, - { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471 }, - { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793 }, - { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855 }, - { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430 }, - { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294 }, - { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736 }, - { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194 }, - { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942 }, - { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341 }, - { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455 }, - { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138 }, - { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857 }, - { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129 }, - { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538 }, - { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, - { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, - { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, ] [[package]] @@ -1154,15 +928,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, ] -[[package]] -name = "logistro" -version = "1.0.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/dc/e4c4b1fd2e6eca7f4e288434ae7f8057c0c06bad797366e0cc027fef23ef/logistro-1.0.11.tar.gz", hash = "sha256:9386b65cde9cc71d8c356142a219868612357b3c54ed4e067a78f420c4e51846", size = 7550 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/fc/bf7cdfc51b13d6f4e2ebdacdd18ddb2aa7a70c00f8b7cd81c0906e0e7490/logistro-1.0.11-py3-none-any.whl", hash = "sha256:5f68bf18f2cb842de207947e6ee53d4980fba602935455d8fa2e9156b2e13323", size = 7006 }, -] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -1191,36 +956,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, ] [[package]] @@ -1246,12 +981,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/65/5b/3b8fd7d66043f0638a35fa650570cbe69efd42fe169e5024f9307598b47e/matplotlib-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee482731c8c17d86d9ddb5194d38621f9b0f0d53c99006275a12523ab021732", size = 11615276 }, { url = "https://files.pythonhosted.org/packages/af/02/320e32ec24b062c29b4b580db3257190c66d5a8aa4d604a9c0204061ead9/matplotlib-3.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36eafe2128772195b373e1242df28d1b7ec6c04c15b090b8d9e335d55a323900", size = 9545819 }, { url = "https://files.pythonhosted.org/packages/40/d9/c1784db9db0d484c8e5deeafbaac0d6ed66e165c6eb4a74fb43a5fa947d9/matplotlib-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:061ee58facb3580cd2d046a6d227fb77e9295599c5ec6ad069f06b5821ad1cfc", size = 7644668 }, - { url = "https://files.pythonhosted.org/packages/2e/34/121c49cd4e3e3ae4ad58c13cb7b000f2140f7bc48c7d3d950a19be634990/matplotlib-3.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3cc3776836d0f4f22654a7f2d2ec2004618d5cf86b7185318381f73b80fd8a2d", size = 7588410 }, - { url = "https://files.pythonhosted.org/packages/dd/73/8d69b0337a77f73d316232ea67708b4bcfe5a9c54dbc2327b1d1b730a0b6/matplotlib-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c49a2bd6981264bddcb8c317b6bd25febcece9e2ebfcbc34e7f4c0c867c09dc", size = 7480932 }, - { url = "https://files.pythonhosted.org/packages/84/be/ea8f33a4b9644cb6c09928cbaeddb025165a4a5f366c46370ed7abb6e6f3/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ed11654fc83cd6cfdf6170b453e437674a050a452133a064d47f2f1371f8d3", size = 11378619 }, - { url = "https://files.pythonhosted.org/packages/77/cd/1464efc9fe354026b8d2fb4ebb2f1746559b0e38308104d3ee60a5a05c71/matplotlib-3.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae97fdd6996b3a25da8ee43e3fc734fff502f396801063c6b76c20b56683196", size = 11602343 }, - { url = "https://files.pythonhosted.org/packages/83/68/aef60cdfc9818862ff2c6ea98e0bec7e230b1fc947339971d5efe4c2d02f/matplotlib-3.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87df75f528020a6299f76a1d986c0ed4406e3b2bd44bc5e306e46bca7d45e53e", size = 9539802 }, - { url = "https://files.pythonhosted.org/packages/59/c7/f8da659997fe3210fdda689cf2d7720b3a079578fb8aecc3623c4e091a77/matplotlib-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:90d74a95fe055f73a6cd737beecc1b81c26f2893b7a3751d52b53ff06ca53f36", size = 7644322 }, ] [[package]] @@ -1304,28 +1033,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, - { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, - { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, - { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, - { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, - { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, - { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, - { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, - { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, - { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, - { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, - { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, - { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, - { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, - { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, - { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, - { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, - { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, - { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, - { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, - { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, - { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, - { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, ] [[package]] @@ -1355,11 +1062,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 }, { url = "https://files.pythonhosted.org/packages/fc/a6/37f7544666b63a27e46c48f49caeee388bf3ce95f9c570eb5cfba5234405/mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4", size = 12897010 }, { url = "https://files.pythonhosted.org/packages/84/8b/459a513badc4d34acb31c736a0101c22d2bd0697b969796ad93294165cfb/mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6", size = 9562873 }, - { url = "https://files.pythonhosted.org/packages/35/3a/ed7b12ecc3f6db2f664ccf85cb2e004d3e90bec928e9d7be6aa2f16b7cdf/mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318", size = 10990335 }, - { url = "https://files.pythonhosted.org/packages/04/e4/1a9051e2ef10296d206519f1df13d2cc896aea39e8683302f89bf5792a59/mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36", size = 10007119 }, - { url = "https://files.pythonhosted.org/packages/f3/3c/350a9da895f8a7e87ade0028b962be0252d152e0c2fbaafa6f0658b4d0d4/mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987", size = 12506856 }, - { url = "https://files.pythonhosted.org/packages/b6/49/ee5adf6a49ff13f4202d949544d3d08abb0ea1f3e7f2a6d5b4c10ba0360a/mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca", size = 12952066 }, - { url = "https://files.pythonhosted.org/packages/27/c0/b19d709a42b24004d720db37446a42abadf844d5c46a2c442e2a074d70d9/mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70", size = 9664000 }, { url = "https://files.pythonhosted.org/packages/42/3a/bdf730640ac523229dd6578e8a581795720a9321399de494374afc437ec5/mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12", size = 2619625 }, ] @@ -1418,22 +1120,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/5c/5e96422400fad72762e85e3cc3a4bd52b11476b990c4e7df25836e8e9c0c/ndindex-1.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8634be005b18034846163067bce78a0209fab65e4bc77e0bc333aa160ef12b7a", size = 506268 }, { url = "https://files.pythonhosted.org/packages/f8/8b/5948067de44c5484aa8a4db640b8b5dc5cc9b394e9f547a23fd694edf399/ndindex-1.9.2-cp311-cp311-win32.whl", hash = "sha256:89172e90e56a409197cbbe12a49aa16c83879274ca4f61fd8a03b30c6c90e3ca", size = 151566 }, { url = "https://files.pythonhosted.org/packages/b0/f6/b2fde7ec7880d51f7280bb5e974e400bb716e3054048c409ba35ba509823/ndindex-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:d23f07831d28bb3c04c234936b6038078cd7c0c4966d2e2e37738edad6435f9f", size = 159516 }, - { url = "https://files.pythonhosted.org/packages/a3/a5/c3775c1a7279517027b86dc0c1a6a74f9a1fc7e0c298c960ed170fcf585e/ndindex-1.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:466d2e30a3c2afac6dac64e5ada19db30d23164befa7f69d29f209fb512b3d2f", size = 164104 }, - { url = "https://files.pythonhosted.org/packages/de/81/edb7ba51dae8d5a2879d39eb56651eeea4927f8292fc6286fae8b1cda0f1/ndindex-1.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e87eefa75af0f974cf2c5af14a488ee97dfdc7fb6da67f19f9dc600da5ae041", size = 161991 }, - { url = "https://files.pythonhosted.org/packages/f2/9e/79342047dd441fdcf25c776370c2b09ef8fad30bf06d7920b09278d93260/ndindex-1.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d98a41ff276fc623f3e068d40381ee06289644b530c3535bc00a8cbc5526c6", size = 521201 }, - { url = "https://files.pythonhosted.org/packages/fc/bd/834e4bb7054accc8bbf63c73f7c9f0bcbdc326fec0f560f375dd6637c63a/ndindex-1.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d42c5cd86d923f1606c3a9defbaeb3ece8f7b444f95a46ec6f1fb511e971f7", size = 498251 }, - { url = "https://files.pythonhosted.org/packages/35/1b/fe4d51e07f18596abd53b3b63dd1d4a8617af3896193418a86b7a7a95fa7/ndindex-1.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:959f8babfc3055933079296a33449e02c24074027c39ce974cf73757c7d5ea21", size = 501804 }, - { url = "https://files.pythonhosted.org/packages/ae/e5/95d5dd5a628c41db959e07ddc7212ed45844865d10375efe4fc0aa5c905b/ndindex-1.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d68d8285f3ab8a78b0db990fb25eddc637df4b00467fbf36a4656c7ee46ddc5d", size = 518095 }, - { url = "https://files.pythonhosted.org/packages/bc/49/ca6155435bb408173c3d07f07aac6e6c4a30cefec31c4dd2af75c44774d7/ndindex-1.9.2-cp312-cp312-win32.whl", hash = "sha256:c87aa8218b6eaaa9eacb2f68f1ce71be0e368280ef926d0ed9ad71d2fbe24fe6", size = 151487 }, - { url = "https://files.pythonhosted.org/packages/01/e3/c87442ba34a76e3d778135e967b625e5bb2217773a8c0be751e1537231b7/ndindex-1.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:d15f3a8566910ec25898e3d77b3b7c258b37f84a235d49cb17dfddc9036127b4", size = 159655 }, - { url = "https://files.pythonhosted.org/packages/88/41/250efa5a033b66043d18eca87d044f733ca017bd12767ddf0b9468120bb1/ndindex-1.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a33452b4a7b8510f809f8b59dbac5b1d7b3d8a68c9e42dee2c28c5131bbbfbc6", size = 162133 }, - { url = "https://files.pythonhosted.org/packages/b0/df/87f329590e807460cbd4cea47aaaadea9a5cf5e70854712eb89489d03c89/ndindex-1.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19d70a304f942c0aee89418d9c487de9c2dcfcda9073976a590c7fed587bd674", size = 160045 }, - { url = "https://files.pythonhosted.org/packages/4e/e3/407f31902bfdd6d991e8ce62307877afc23e39b8a7c93fd17ab8316a5415/ndindex-1.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0bba0f387d1d8204378e82b886c19f46ae39347ee7113c9028317270c0060be", size = 505012 }, - { url = "https://files.pythonhosted.org/packages/91/dc/4e335d8631939f267be7b16308246671c02123e28f693f544076dda8615b/ndindex-1.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876dcd82d6d1cbc9c2e6819bc7a5ca3686a5430f0a07520b94f78ff78097d2d", size = 484122 }, - { url = "https://files.pythonhosted.org/packages/c0/ed/007faa12149a21893ec64f0dcab36c70a4cd43825dcd11bd7090ef8d1c29/ndindex-1.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3c564768ffa7228d1768eb6df1a77d03d39efc9c98746d8c1b511ffcc23d5f9f", size = 486035 }, - { url = "https://files.pythonhosted.org/packages/2c/81/67d8a37aca8997d8e93554f3c39bca200a16685e84e03e2cc84cf0c93276/ndindex-1.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ddbdfee4560c3f7823de88257680c8fd6d0220a7b23bfd27b0f3fc7afa27bee1", size = 500916 }, - { url = "https://files.pythonhosted.org/packages/6e/ff/277768997fa82a865ff564a6bd91bb42f574662945e58b26c0eb3d690aba/ndindex-1.9.2-cp313-cp313-win32.whl", hash = "sha256:6274886f1348128fc4e10aef925272f904ac467175af52338d56f1cb763caf1a", size = 150906 }, - { url = "https://files.pythonhosted.org/packages/3c/4d/e121d109bf6f71bcb00c6198549152fa16358a031796c6a7aa9662191529/ndindex-1.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:98be658c00ec0827e398b0fe5c13d207ff70b027187d728cb22155cce3bf6fbe", size = 158778 }, ] [[package]] @@ -1488,20 +1174,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/d5/ec734e735eba5a753efed5be3707ee7447ebd371772f8081b65a4153fb97/numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e", size = 1380354 }, { url = "https://files.pythonhosted.org/packages/30/51/406e572531d817480bd612ee08239a36ee82865fea02fce569f15631f4ee/numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5", size = 151938 }, { url = "https://files.pythonhosted.org/packages/04/32/5882ed1dbd96234f327a73316a481add151ff827cfaf2ea24fb4d5ad04db/numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0", size = 144961 }, - { url = "https://files.pythonhosted.org/packages/2b/96/d5053dea06d8298ae8052b4b049cbf8ef74998e28d57166cc27b8ae909e2/numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be", size = 145029 }, - { url = "https://files.pythonhosted.org/packages/3e/3c/fcd5a812ed5dda757b2d9ef2764a3e1cca6f6d1f02dbf113dc23a2c7702a/numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42", size = 134851 }, - { url = "https://files.pythonhosted.org/packages/0a/52/0ed3b306d8c9944129bce97fec73a2caff13adbd7e1df148d546d7eb2d4d/numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470", size = 411837 }, - { url = "https://files.pythonhosted.org/packages/7d/9c/6b671dd3fb67d7e7da93cb76b7c5277743f310a216b7856bb18776bb3371/numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6", size = 400577 }, - { url = "https://files.pythonhosted.org/packages/ea/4d/a167d1a215fe10ce58c45109f2869fd13aa0eef66f7e8c69af68be45d436/numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db", size = 1381735 }, - { url = "https://files.pythonhosted.org/packages/c1/d4/17e4434f989e4917d31cbd88a043e1c9c16958149cf43fa622987111392b/numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e", size = 152102 }, - { url = "https://files.pythonhosted.org/packages/b8/25/9ae599994076ef2a42d35ff6b0430da002647f212567851336a6c7b132d6/numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead", size = 145061 }, - { url = "https://files.pythonhosted.org/packages/8c/cb/2ea1848c46e4d75073c038dd75628d1aa442975303264ed230bf90f74f44/numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d", size = 145035 }, - { url = "https://files.pythonhosted.org/packages/ec/cf/bb2bcd81d6f3243590e19ac3e7795a1a370f3ebcd8ecec1f46dcd5333f37/numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f", size = 134858 }, - { url = "https://files.pythonhosted.org/packages/48/9b/c9128ffb453205c2a4c84a3abed35447c7591c2c2812e77e34fd238cb2bb/numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e", size = 415517 }, - { url = "https://files.pythonhosted.org/packages/7e/b0/64c04c9f8b4a563218d00daa1ec4563364961b79025162c5276ab2c7c407/numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527", size = 403846 }, - { url = "https://files.pythonhosted.org/packages/80/35/60e9041fd709fe98dd3109d73a03cdffaeb6ee2089179155f5c3754e9934/numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19", size = 1381659 }, - { url = "https://files.pythonhosted.org/packages/bd/5a/955bf5b5cf8f3de7b044a999e36327e14191fa073ed0e329456ed0f8161d/numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01", size = 152105 }, - { url = "https://files.pythonhosted.org/packages/be/7a/8ce360a1848bb5bcc30a414493371678f43790ece397f8652d5f65757e57/numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd", size = 145060 }, ] [[package]] @@ -1517,13 +1189,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/37/41/63975634a93da2a384d3c8084eba467242cab68daab0cd8f4fd470dcee26/numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", size = 18020808 }, { url = "https://files.pythonhosted.org/packages/58/d2/cbc329aa908cb963bd849f14e24f59c002a488e9055fab2c68887a6b5f1c/numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", size = 20750149 }, { url = "https://files.pythonhosted.org/packages/93/fd/3f826c6d15d3bdcf65b8031e4835c52b7d9c45add25efa2314b53850e1a2/numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", size = 15794407 }, - { url = "https://files.pythonhosted.org/packages/e9/83/f8a62f08d38d831a2980427ffc465a4207fe600124b00cfb0ef8265594a7/numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", size = 20325091 }, - { url = "https://files.pythonhosted.org/packages/7a/72/6d1cbdf0d770016bc9485f9ef02e73d5cb4cf3c726f8e120b860a403d307/numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", size = 13672867 }, - { url = "https://files.pythonhosted.org/packages/2f/70/c071b2347e339f572f5aa61f649b70167e5dd218e3da3dc600c9b08154b9/numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", size = 13872627 }, - { url = "https://files.pythonhosted.org/packages/e3/e2/4ecfbc4a2e3f9d227b008c92a5d1f0370190a639b24fec3b226841eaaf19/numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", size = 17883864 }, - { url = "https://files.pythonhosted.org/packages/45/08/025bb65dbe19749f1a67a80655670941982e5d0144a4e588ebbdbcfe7983/numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", size = 17721550 }, - { url = "https://files.pythonhosted.org/packages/98/66/f0a846751044d0b6db5156fb6304d0336861ed055c21053a0f447103939c/numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", size = 19951520 }, - { url = "https://files.pythonhosted.org/packages/98/d7/1cc7a11118408ad21a5379ff2a4e0b0e27504c68ef6e808ebaa90ee95902/numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", size = 15504471 }, ] [[package]] @@ -1566,13 +1231,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, - { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, - { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, - { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, - { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, - { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, - { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, - { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, ] [[package]] @@ -1626,36 +1284,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 }, { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 }, { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 }, - { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, - { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, - { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, - { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, - { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, - { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, - { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, - { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, - { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, - { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, - { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, - { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, - { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, - { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, - { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, - { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, - { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, - { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, - { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, - { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, - { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, - { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, - { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, - { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, - { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, - { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, - { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, - { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, - { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, ] [[package]] @@ -1835,13 +1463,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/47/62/b446ee0971b00e7437b9c54a8409ae20413235a64c0a301d7cf97070cffa/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e", size = 38077480 }, { url = "https://files.pythonhosted.org/packages/fa/15/48a68b30542a0231a75c26d8661bc5c9bbc07b42c5b219e929adba814ba7/pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b", size = 40821141 }, { url = "https://files.pythonhosted.org/packages/49/4d/62a09116ec357ade462fac4086e0711457a87177bea25ae46b25897d6d7c/pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b", size = 25889334 }, - { url = "https://files.pythonhosted.org/packages/84/bd/d5903125e38c33b74f7b3d57ffffd4ef48145208cfd8742367f12effb59c/pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f", size = 28372822 }, - { url = "https://files.pythonhosted.org/packages/9b/73/560ef6bf05f16305502b8e368c771e8f82d774898b37a3fb231f89c13342/pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a", size = 26004052 }, - { url = "https://files.pythonhosted.org/packages/56/5e/3cd956aceb1c960e8ac6fdc6eea69d642aa2e6ee10e2f10ce7815dbf62a9/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c", size = 38660648 }, - { url = "https://files.pythonhosted.org/packages/08/4a/668e7fb6bc564e5361097f1f160b2891ca40bcacfe018638e2841073ec3d/pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2", size = 40961053 }, - { url = "https://files.pythonhosted.org/packages/f7/8f/a51a290a855172514b8496c8a74f0e0b98e5e0582d44ae7547cf68dd033b/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628", size = 38060675 }, - { url = "https://files.pythonhosted.org/packages/25/7b/8da91f8de0b40b760dd748031973b6ac2aa3d4f85c67f45b7e58577ca22e/pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7", size = 40826735 }, - { url = "https://files.pythonhosted.org/packages/fa/2b/a0053f1304586f2976cb2c37ddb0e52cf4114220e805ebba272a1e231ccc/pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444", size = 25838156 }, ] [[package]] @@ -1897,18 +1518,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/b2/2ca124343aba24b9a5dcd7c1f43da81e652849cfaf3110d3f507a80af0a1/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0a47f702d29808c557d2ebea8542c23903f021eae44e16838adef2ab4281c71b", size = 23138693 }, { url = "https://files.pythonhosted.org/packages/ae/15/501aa4823c142232169d54255ab343f28c4ea9e7fa489b8433dcc873a942/pyogrio-0.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:11e6c71d12da6b445e77d0fc0198db1bd35a77e03a0685e45338cbab9ce02add", size = 24062952 }, { url = "https://files.pythonhosted.org/packages/94/8d/24f21e6a93ca418231aee3bddade7a0766c89c523832f29e08a8860f83e6/pyogrio-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0d74e91a9c0ff2f9abe01b556ff663977193b2d6922208406172d0fc833beff", size = 16172573 }, - { url = "https://files.pythonhosted.org/packages/b5/b5/3c5dfd0b50cbce6f3d4e42c0484647feb1809dbe20e225c4c6abd067e69f/pyogrio-0.10.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d6558b180e020f71ab7aa7f82d592ed3305c9f698d98f6d0a4637ec7a84c4ce", size = 15079211 }, - { url = "https://files.pythonhosted.org/packages/b8/9a/1ba9c707a094976f343bd0177741eaba0e842fa05ecd8ab97192db4f2ec1/pyogrio-0.10.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a99102037eead8ba491bc57825c1e395ee31c9956d7bff7b4a9e4fdbff3a13c2", size = 16442782 }, - { url = "https://files.pythonhosted.org/packages/5e/bb/b4250746c2c85fea5004cae93e9e25ad01516e9e94e04de780a2e78139da/pyogrio-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a4c373281d7cbf560c5b61f8f3c7442103ad7f1c7ac4ef3a84572ed7a5dd2f6", size = 23899832 }, - { url = "https://files.pythonhosted.org/packages/bd/4c/79e47e40a8e54e79a45133786a0a58209534f580591c933d40c5ed314fe7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:19f18411bdf836d24cdc08b9337eb3ec415e4ac4086ba64516b36b73a2e88622", size = 23081469 }, - { url = "https://files.pythonhosted.org/packages/47/78/2b62c8a340bcb0ea56b9ddf2ef5fd3d1f101dc0e98816b9e6da87c5ac3b7/pyogrio-0.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1abbcdd9876f30bebf1df8a0273f6cdeb29d03259290008275c7fddebe139f20", size = 24024758 }, - { url = "https://files.pythonhosted.org/packages/43/97/34605480f06b0ad9611bf58a174eccc6f3673275f3d519cf763391892881/pyogrio-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a3e09839590d71ff832aa95c4f23fa00a2c63c3de82c1fbd4fb8d265792acfc", size = 16160294 }, - { url = "https://files.pythonhosted.org/packages/14/4a/4c8e4f5b9edbca46e0f8d6c1c0b56c0d4af0900c29f4bea22d37853c07f3/pyogrio-0.10.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c90478209537a31dcc65664a87a04c094bb0e08efe502908a6682b8cec0259bf", size = 15076879 }, - { url = "https://files.pythonhosted.org/packages/5f/be/7db0644eef9ef3382518399aaf3332827c43018112d2a74f78784fd496ec/pyogrio-0.10.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:fec45e1963b7058e5a1aa98598aed07c0858512c833d6aad2c672c3ec98bbf04", size = 16440405 }, - { url = "https://files.pythonhosted.org/packages/96/77/f199230ba86fe88b1f57e71428c169ed982de68a32d6082cd7c12d0f5d55/pyogrio-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28cb139f8a5d0365ede602230104b407ae52bb6b55173c8d5a35424d28c4a2c5", size = 23871511 }, - { url = "https://files.pythonhosted.org/packages/25/ac/ca483bec408b59c54f7129b0244cc9de21d8461aefe89ece7bd74ad33807/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:cea0187fcc2d574e52af8cfab041fa0a7ad71d5ef6b94b49a3f3d2a04534a27e", size = 23048830 }, - { url = "https://files.pythonhosted.org/packages/d7/3e/c35f2d8dad95b24e568c468f09ff60fb61945065465e0ec7868400596566/pyogrio-0.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7c02b207ea8cf09c501ea3e95d29152781a00d3c32267286bc36fa457c332205", size = 23996873 }, - { url = "https://files.pythonhosted.org/packages/27/5d/0deb16d228362a097ee3258d0a887c9c0add4b9678bb4847b08a241e124d/pyogrio-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:02e54bcfb305af75f829044b0045f74de31b77c2d6546f7aaf96822066147848", size = 16158260 }, ] [[package]] @@ -1949,18 +1558,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/e5/2cb256148c730b9c3f74bfb3c03904f5070499c6dcaea153073a9642c6c6/pyproj-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc5b305d4d5d7697885681d9b660623e328227612823d5c660e0a9566cb48838", size = 9460363 }, { url = "https://files.pythonhosted.org/packages/ba/a3/4aa1e8e78ad18aa170efd2c94c1931bf2a34c526683b874d06e40fa323f6/pyproj-3.7.0-cp311-cp311-win32.whl", hash = "sha256:de2b47d748dc41cccb6b3b713d4d7dc9aa1046a82141c8665026908726426abc", size = 5820551 }, { url = "https://files.pythonhosted.org/packages/26/0c/b084e8839a117eaad8cb4fbaa81bbb24c6f183de0ee95c6c4e2770ab6f09/pyproj-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:38cba7c4c5679e40242dd959133e95b908d3b912dd66291094fd13510e8517ff", size = 6231788 }, - { url = "https://files.pythonhosted.org/packages/bd/19/be806b711e9ebfb80411c653054157db128fffdd7f8493e3064136c8d880/pyproj-3.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:8cbec92bdd6e9933ca08795c12717d1384e9b51cf4b1acf0d753db255a75c51e", size = 6261400 }, - { url = "https://files.pythonhosted.org/packages/99/3b/8497995e8cae0049d013679c6a7ac6c57b816d590c733a388748dafe5af5/pyproj-3.7.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8c4a8e4d3ba76c3adac3c087544cf92f7f9a19ea34946904a13fca48cc1c0106", size = 4637848 }, - { url = "https://files.pythonhosted.org/packages/ea/f7/2a5b46d6f8da913d58d44942ab06ca4803b5424b73259b15344cf90040f6/pyproj-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82624fb42aa31f6b1a860fbc0316babd07fd712642bc31022df4e9b4056bf463", size = 6324856 }, - { url = "https://files.pythonhosted.org/packages/36/83/c257771077bcf9da20d0e97abc834f9037c219986cc76d40183903a30464/pyproj-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1bbb3f89c68d4a6835c40b2da8b27680eec60e8cc7cdb08c09bcc725b2b62", size = 9525831 }, - { url = "https://files.pythonhosted.org/packages/d6/50/a635de79def69fe03cdef3a4bd3bec780c30987bce3a15dd7099afb2506f/pyproj-3.7.0-cp312-cp312-win32.whl", hash = "sha256:952515d5592167ad4436b355485f82acebed2a49b46722159e4584b75a763dd3", size = 5811864 }, - { url = "https://files.pythonhosted.org/packages/a1/8b/96bc8c8f3eca4eb7fa3758fde0b755d1df30a19f494376e3ee8de1ef4e79/pyproj-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0692f806224e8ed82fe4acfa57268ff444fdaf9f330689f24c0d96e59480cce1", size = 6224720 }, - { url = "https://files.pythonhosted.org/packages/bf/da/a17c452bea1ff4cd58d6dc573055b9c8fb6af114b7d2c694782aec770865/pyproj-3.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:94e8b903a9e83448fd2379c49dec3e8cd83c9ed36f54354e68b601cef56d5426", size = 6254898 }, - { url = "https://files.pythonhosted.org/packages/c2/31/ab07b389f2caa527c95ab2ea1940d28879bd2a19e67b2529cb3e94648d26/pyproj-3.7.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:64cb5c17d6f6305a8b978a40f95560c87c5b363fcac40632337955664437875a", size = 4628612 }, - { url = "https://files.pythonhosted.org/packages/1d/24/def3ded6529db3e3d8351ad73481730249ab57d8d876d502f86d7958ce06/pyproj-3.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c54e9bdda7ab9c4a5af50f9d6e6ee7704e05fafd504896b96ed1208c7aea098", size = 6315895 }, - { url = "https://files.pythonhosted.org/packages/dd/14/07314f78302105d199fb25e73376d723efe9c2ef3906463aae209913a6d3/pyproj-3.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24fa4e9e0abba875f9524808410cc520067eaf38fd5549ed0ef7c43ac39923c9", size = 9466144 }, - { url = "https://files.pythonhosted.org/packages/00/f2/2a116920db3496e3ff3c94d7d8d15da41374f35cfe1b9e79682eca500a61/pyproj-3.7.0-cp313-cp313-win32.whl", hash = "sha256:b9e8353fc3c79dc14d1f5ac758a1a6e4eee04102c3c0b138670f121f5ac52eb4", size = 5807180 }, - { url = "https://files.pythonhosted.org/packages/f8/33/3c8c6302717096b54aa14ccbb271045ba04629e21cbf348f2f2dc94f69b4/pyproj-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:10a8dc6ec61af97c89ff032647d743f8dc023645773da42ef43f7ae1125b3509", size = 6218036 }, ] [[package]] @@ -2004,7 +1601,6 @@ dependencies = [ { name = "graphviz" }, { name = "gurobipy" }, { name = "highspy" }, - { name = "kaleido" }, { name = "linopy" }, { name = "matplotlib" }, { name = "netcdf4" }, @@ -2063,7 +1659,6 @@ requires-dist = [ { name = "gurobipy", specifier = "==11.0.3" }, { name = "highspy", specifier = ">=1.9.0" }, { name = "ipython", marker = "extra == 'dev'" }, - { name = "kaleido", specifier = "==0.4.0rc5" }, { name = "linopy", specifier = "==0.3.14" }, { name = "matplotlib", specifier = "==3.8.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.11.0" }, @@ -2158,12 +1753,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/e2/02652007469263fe1466e98439831d65d4ca80ea1a2df29abecedf7e47b7/pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", size = 5928156 }, { url = "https://files.pythonhosted.org/packages/48/ef/f4fb45e2196bc7ffe09cad0542d9aff66b0e33f6c0954b43e49c33cad7bd/pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", size = 6559559 }, { url = "https://files.pythonhosted.org/packages/79/ef/68bb6aa865c5c9b11a35771329e95917b5559845bd75b65549407f9fc6b4/pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", size = 7972495 }, - { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, - { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, - { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, - { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, - { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, - { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, ] [[package]] @@ -2181,24 +1770,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, ] [[package]] @@ -2231,7 +1802,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } wheels = [ @@ -2281,45 +1852,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/08/696c9872cf56effdad9ed617ac072f6774a898d46b8b8964eab39ec562d2/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", size = 552105 }, { url = "https://files.pythonhosted.org/packages/18/1f/4df560be1e994f5adf56cabd6c117e02de7c88ee238bb4ce03ed50da9d56/rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", size = 220199 }, { url = "https://files.pythonhosted.org/packages/b8/1b/c29b570bc5db8237553002788dc734d6bd71443a2ceac2a58202ec06ef12/rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", size = 231775 }, - { url = "https://files.pythonhosted.org/packages/75/47/3383ee3bd787a2a5e65a9b9edc37ccf8505c0a00170e3a5e6ea5fbcd97f7/rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", size = 352334 }, - { url = "https://files.pythonhosted.org/packages/40/14/aa6400fa8158b90a5a250a77f2077c0d0cd8a76fce31d9f2b289f04c6dec/rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", size = 342111 }, - { url = "https://files.pythonhosted.org/packages/7d/06/395a13bfaa8a28b302fb433fb285a67ce0ea2004959a027aea8f9c52bad4/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", size = 384286 }, - { url = "https://files.pythonhosted.org/packages/43/52/d8eeaffab047e6b7b7ef7f00d5ead074a07973968ffa2d5820fa131d7852/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e", size = 391739 }, - { url = "https://files.pythonhosted.org/packages/83/31/52dc4bde85c60b63719610ed6f6d61877effdb5113a72007679b786377b8/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", size = 427306 }, - { url = "https://files.pythonhosted.org/packages/70/d5/1bab8e389c2261dba1764e9e793ed6830a63f830fdbec581a242c7c46bda/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", size = 442717 }, - { url = "https://files.pythonhosted.org/packages/82/a1/a45f3e30835b553379b3a56ea6c4eb622cf11e72008229af840e4596a8ea/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", size = 385721 }, - { url = "https://files.pythonhosted.org/packages/a6/27/780c942de3120bdd4d0e69583f9c96e179dfff082f6ecbb46b8d6488841f/rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", size = 415824 }, - { url = "https://files.pythonhosted.org/packages/94/0b/aa0542ca88ad20ea719b06520f925bae348ea5c1fdf201b7e7202d20871d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", size = 561227 }, - { url = "https://files.pythonhosted.org/packages/0d/92/3ed77d215f82c8f844d7f98929d56cc321bb0bcfaf8f166559b8ec56e5f1/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", size = 587424 }, - { url = "https://files.pythonhosted.org/packages/09/42/cacaeb047a22cab6241f107644f230e2935d4efecf6488859a7dd82fc47d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", size = 555953 }, - { url = "https://files.pythonhosted.org/packages/e6/52/c921dc6d5f5d45b212a456c1f5b17df1a471127e8037eb0972379e39dff4/rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", size = 221339 }, - { url = "https://files.pythonhosted.org/packages/f2/c7/f82b5be1e8456600395366f86104d1bd8d0faed3802ad511ef6d60c30d98/rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", size = 235786 }, - { url = "https://files.pythonhosted.org/packages/d0/bf/36d5cc1f2c609ae6e8bf0fc35949355ca9d8790eceb66e6385680c951e60/rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", size = 351657 }, - { url = "https://files.pythonhosted.org/packages/24/2a/f1e0fa124e300c26ea9382e59b2d582cba71cedd340f32d1447f4f29fa4e/rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", size = 341829 }, - { url = "https://files.pythonhosted.org/packages/cf/c2/0da1231dd16953845bed60d1a586fcd6b15ceaeb965f4d35cdc71f70f606/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", size = 384220 }, - { url = "https://files.pythonhosted.org/packages/c7/73/a4407f4e3a00a9d4b68c532bf2d873d6b562854a8eaff8faa6133b3588ec/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", size = 391009 }, - { url = "https://files.pythonhosted.org/packages/a9/c3/04b7353477ab360fe2563f5f0b176d2105982f97cd9ae80a9c5a18f1ae0f/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", size = 426989 }, - { url = "https://files.pythonhosted.org/packages/8d/e6/e4b85b722bcf11398e17d59c0f6049d19cd606d35363221951e6d625fcb0/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", size = 441544 }, - { url = "https://files.pythonhosted.org/packages/27/fc/403e65e56f65fff25f2973216974976d3f0a5c3f30e53758589b6dc9b79b/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", size = 385179 }, - { url = "https://files.pythonhosted.org/packages/57/9b/2be9ff9700d664d51fd96b33d6595791c496d2778cb0b2a634f048437a55/rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", size = 415103 }, - { url = "https://files.pythonhosted.org/packages/bb/a5/03c2ad8ca10994fcf22dd2150dd1d653bc974fa82d9a590494c84c10c641/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", size = 560916 }, - { url = "https://files.pythonhosted.org/packages/ba/2e/be4fdfc8b5b576e588782b56978c5b702c5a2307024120d8aeec1ab818f0/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", size = 587062 }, - { url = "https://files.pythonhosted.org/packages/67/e0/2034c221937709bf9c542603d25ad43a68b4b0a9a0c0b06a742f2756eb66/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", size = 555734 }, - { url = "https://files.pythonhosted.org/packages/ea/ce/240bae07b5401a22482b58e18cfbabaa392409b2797da60223cca10d7367/rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", size = 220663 }, - { url = "https://files.pythonhosted.org/packages/cb/f0/d330d08f51126330467edae2fa4efa5cec8923c87551a79299380fdea30d/rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", size = 235503 }, - { url = "https://files.pythonhosted.org/packages/f7/c4/dbe1cc03df013bf2feb5ad00615038050e7859f381e96fb5b7b4572cd814/rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", size = 347698 }, - { url = "https://files.pythonhosted.org/packages/a4/3a/684f66dd6b0f37499cad24cd1c0e523541fd768576fa5ce2d0a8799c3cba/rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", size = 337330 }, - { url = "https://files.pythonhosted.org/packages/82/eb/e022c08c2ce2e8f7683baa313476492c0e2c1ca97227fe8a75d9f0181e95/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", size = 380022 }, - { url = "https://files.pythonhosted.org/packages/e4/21/5a80e653e4c86aeb28eb4fea4add1f72e1787a3299687a9187105c3ee966/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", size = 390754 }, - { url = "https://files.pythonhosted.org/packages/37/a4/d320a04ae90f72d080b3d74597074e62be0a8ecad7d7321312dfe2dc5a6a/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", size = 423840 }, - { url = "https://files.pythonhosted.org/packages/87/70/674dc47d93db30a6624279284e5631be4c3a12a0340e8e4f349153546728/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", size = 438970 }, - { url = "https://files.pythonhosted.org/packages/3f/64/9500f4d66601d55cadd21e90784cfd5d5f4560e129d72e4339823129171c/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", size = 383146 }, - { url = "https://files.pythonhosted.org/packages/4d/45/630327addb1d17173adcf4af01336fd0ee030c04798027dfcb50106001e0/rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", size = 408294 }, - { url = "https://files.pythonhosted.org/packages/5f/ef/8efb3373cee54ea9d9980b772e5690a0c9e9214045a4e7fa35046e399fee/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", size = 556345 }, - { url = "https://files.pythonhosted.org/packages/54/01/151d3b9ef4925fc8f15bfb131086c12ec3c3d6dd4a4f7589c335bf8e85ba/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", size = 582292 }, - { url = "https://files.pythonhosted.org/packages/30/89/35fc7a6cdf3477d441c7aca5e9bbf5a14e0f25152aed7f63f4e0b141045d/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", size = 553855 }, - { url = "https://files.pythonhosted.org/packages/8f/e0/830c02b2457c4bd20a8c5bb394d31d81f57fbefce2dbdd2e31feff4f7003/rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", size = 219100 }, - { url = "https://files.pythonhosted.org/packages/f8/30/7ac943f69855c2db77407ae363484b915d861702dbba1aa82d68d57f42be/rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", size = 233794 }, ] [[package]] @@ -2364,20 +1896,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155 }, { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069 }, { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809 }, - { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516 }, - { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837 }, - { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728 }, - { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700 }, - { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613 }, - { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, - { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, - { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, - { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, - { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, - { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, - { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, - { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, - { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, ] [[package]] @@ -2395,12 +1913,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/1b/7538792254aec6850657d5b940fd05fe60582af829ffe40d6c054f065f34/scipy-1.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f290cf561a4b4edfe8d1001ee4be6da60c1c4ea712985b58bf6bc62badee221", size = 36401766 }, { url = "https://files.pythonhosted.org/packages/36/95/69e32b4691daa6a6fe625d2e3724a39dc9b910c7860e739b583798d3d127/scipy-1.11.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:91770cb3b1e81ae19463b3c235bf1e0e330767dca9eb4cd73ba3ded6c4151e4d", size = 36621150 }, { url = "https://files.pythonhosted.org/packages/81/d7/d2537d51efb692d0c411e64267ba349e7668d40f5bc73cefe78ccd650dcd/scipy-1.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1f97cd89c0fe1a0685f8f89d85fa305deb3067d0668151571ba50913e445820", size = 44095029 }, - { url = "https://files.pythonhosted.org/packages/e5/ee/c5bc0d4b66a9c38165adf86e8b57be6f76868edf5ea23b3bbee3680e7edf/scipy-1.11.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dfcc1552add7cb7c13fb70efcb2389d0624d571aaf2c80b04117e2755a0c5d15", size = 37051885 }, - { url = "https://files.pythonhosted.org/packages/cb/0e/7e2c614d4c892e7fc9f44f4bf16a4661c7f9112f856c3a14f444e43a6ad4/scipy-1.11.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0d3a136ae1ff0883fffbb1b05b0b2fea251cb1046a5077d0b435a1839b3e52b7", size = 29643878 }, - { url = "https://files.pythonhosted.org/packages/85/61/0b1298353028ae5080674a02a44ea2514decb7c87664007cd1e0c4822522/scipy-1.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bae66a2d7d5768eaa33008fa5a974389f167183c87bf39160d3fefe6664f8ddc", size = 32244516 }, - { url = "https://files.pythonhosted.org/packages/c8/ae/c1e5e9f7ace48e299d7eb05a4b7f1ca0c98961659945a0270a735e5a045a/scipy-1.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2f6dee6cbb0e263b8142ed587bc93e3ed5e777f1f75448d24fb923d9fd4dce6", size = 35743498 }, - { url = "https://files.pythonhosted.org/packages/dc/b2/c58eb0e021696c46719f90ab970d0c034445e36180445c431feed3290871/scipy-1.11.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:74e89dc5e00201e71dd94f5f382ab1c6a9f3ff806c7d24e4e90928bb1aafb280", size = 35936106 }, - { url = "https://files.pythonhosted.org/packages/f4/ce/be0b376ba6069f3f8ba240aa532a374733447453c93582d4c474effdde21/scipy-1.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:90271dbde4be191522b3903fc97334e3956d7cfb9cce3f0718d0ab4fd7d8bfd6", size = 43734377 }, ] [[package]] @@ -2442,61 +1954,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/47/05c8bb8322861113e72b903aebaaa4678ae6e44c886c189ad8fe297f2008/shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2", size = 2527925 }, { url = "https://files.pythonhosted.org/packages/48/2a/6e590f4f13b1bd8ee39626bab5c1643b8746a4e469c61c5e38571f6cccd9/shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd", size = 1290073 }, { url = "https://files.pythonhosted.org/packages/9e/39/029c441d8af32ab423b229c4525ce5ce6707318155b59634811a4c56f5c4/shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e", size = 1437690 }, - { url = "https://files.pythonhosted.org/packages/a7/e4/d0fdebc973ccfefe9feb8bca0995e0071c37aabb40448d9f170f94058a0b/shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c", size = 2498555 }, - { url = "https://files.pythonhosted.org/packages/cd/77/d36919327b4c6f5a92909ea194a1a4138bf0515bf0d6a5ca29f53cd0d879/shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2", size = 1430925 }, - { url = "https://files.pythonhosted.org/packages/5c/d9/d557c09d15406aad8252caeae181c2db2c562cc1b2ca6e6482b43274b25f/shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8", size = 1271480 }, - { url = "https://files.pythonhosted.org/packages/bc/e0/237b127f163737b3b4aa275943f4e75e56f50f0aa1c05fe997ddac6bd256/shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b", size = 2441270 }, - { url = "https://files.pythonhosted.org/packages/29/b7/754f971ebbcb747ef38147949f90b30d2232ee5b9af9daacd558cc1705e2/shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4", size = 2526036 }, - { url = "https://files.pythonhosted.org/packages/eb/5e/7aca7c3181f0bedbdb0931c825171b499c8a91b5aff667077efe81e75b4a/shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611", size = 1291039 }, - { url = "https://files.pythonhosted.org/packages/c7/d7/1bf4f48d83af09ce1af06c21752670fa8cdcafa72dbc452b809b215cda2a/shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033", size = 1438340 }, -] - -[[package]] -name = "simplejson" -version = "3.19.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3d/29/085111f19717f865eceaf0d4397bf3e76b08d60428b076b64e2a1903706d/simplejson-3.19.3.tar.gz", hash = "sha256:8e086896c36210ab6050f2f9f095a5f1e03c83fa0e7f296d6cba425411364680", size = 85237 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/bb/9ee3959e6929d228cf669b3f13f0edd43c5261b6cd69598640748b19ca35/simplejson-3.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e88abff510dcff903a18d11c2a75f9964e768d99c8d147839913886144b2065e", size = 91930 }, - { url = "https://files.pythonhosted.org/packages/ac/ae/a06523928af3a6783e2638cd4f6035c3e32de1c1063d563d9060c8d2f1ad/simplejson-3.19.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934a50a614fb831614db5dbfba35127ee277624dda4d15895c957d2f5d48610c", size = 74787 }, - { url = "https://files.pythonhosted.org/packages/c3/58/fea732e48a7540035fe46d39e6fd77679f5810311d31da8661ce7a18210a/simplejson-3.19.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:212fce86a22188b0c7f53533b0f693ea9605c1a0f02c84c475a30616f55a744d", size = 74612 }, - { url = "https://files.pythonhosted.org/packages/ab/4d/15718f20cb0e3875b8af9597d6bb3bfbcf1383834b82b6385ee9ac0b72a9/simplejson-3.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d9e8f836688a8fabe6a6b41b334aa550a6823f7b4ac3d3712fc0ad8655be9a8", size = 143550 }, - { url = "https://files.pythonhosted.org/packages/93/44/815a4343774760f7a82459c8f6a4d8268b4b6d23f81e7b922a5e2ca79171/simplejson-3.19.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23228037dc5d41c36666384062904d74409a62f52283d9858fa12f4c22cffad1", size = 153284 }, - { url = "https://files.pythonhosted.org/packages/9d/52/d3202d9bba95444090d1c98e43da3c10907875babf63ed3c134d1b9437e3/simplejson-3.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0791f64fed7d4abad639491f8a6b1ba56d3c604eb94b50f8697359b92d983f36", size = 141518 }, - { url = "https://files.pythonhosted.org/packages/b7/d4/850948bcbcfe0b4a6c69dfde10e245d3a1ea45252f16a1e2308a3b06b1da/simplejson-3.19.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f614581b61a26fbbba232a1391f6cee82bc26f2abbb6a0b44a9bba25c56a1c", size = 144688 }, - { url = "https://files.pythonhosted.org/packages/58/d2/b8dcb0a07d9cd54c47f9fe8733dbb83891d1efe4fc786d9dfc8781cc04f9/simplejson-3.19.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1df0aaf1cb787fdf34484ed4a1f0c545efd8811f6028623290fef1a53694e597", size = 144534 }, - { url = "https://files.pythonhosted.org/packages/a9/95/1e92d99039041f596e0923ec4f9153244acaf3830944dc69a7c11b23ceaa/simplejson-3.19.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:951095be8d4451a7182403354c22ec2de3e513e0cc40408b689af08d02611588", size = 146565 }, - { url = "https://files.pythonhosted.org/packages/21/04/c96aeb3a74031255e4cbcc0ca1b6ebfb5549902f0a065f06d65ce8447c0c/simplejson-3.19.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a954b30810988feeabde843e3263bf187697e0eb5037396276db3612434049b", size = 155014 }, - { url = "https://files.pythonhosted.org/packages/b7/41/e28a28593afc4a75d8999d057bfb7c73a103e35f927e66f4bb92571787ae/simplejson-3.19.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c40df31a75de98db2cdfead6074d4449cd009e79f54c1ebe5e5f1f153c68ad20", size = 148092 }, - { url = "https://files.pythonhosted.org/packages/2b/82/1c81a3af06f937afb6d2e9d74a465c0e0ae6db444d1bf2a436ea26de1965/simplejson-3.19.3-cp311-cp311-win32.whl", hash = "sha256:7e2a098c21ad8924076a12b6c178965d88a0ad75d1de67e1afa0a66878f277a5", size = 73942 }, - { url = "https://files.pythonhosted.org/packages/65/be/d8ab9717f471be3c114f16abd8be21d9a6a0a09b9b49177d93d64d3717d9/simplejson-3.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:c9bedebdc5fdad48af8783022bae307746d54006b783007d1d3c38e10872a2c6", size = 75469 }, - { url = "https://files.pythonhosted.org/packages/20/15/513fea93fafbdd4993eacfcb762965b2ff3d29e618c029e2956174d68c4b/simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99", size = 92921 }, - { url = "https://files.pythonhosted.org/packages/a4/4f/998a907ae1a6c104dc0ee48aa248c2478490152808d34d8e07af57f396c3/simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333", size = 75311 }, - { url = "https://files.pythonhosted.org/packages/db/44/acd6122201e927451869d45952b9ab1d3025cdb5e61548d286d08fbccc08/simplejson-3.19.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42e5acf80d4d971238d4df97811286a044d720693092b20a56d5e56b7dcc5d09", size = 74964 }, - { url = "https://files.pythonhosted.org/packages/27/ca/d0a1e8f16e1bbdc0b8c6d88166f45f565ed7285f53928cfef3b6ce78f14d/simplejson-3.19.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b0efc7279d768db7c74d3d07f0b5c81280d16ae3fb14e9081dc903e8360771", size = 150106 }, - { url = "https://files.pythonhosted.org/packages/63/59/0554b78cf26c98e2b9cae3f44723bd72c2394e2afec1a14eedc6211f7187/simplejson-3.19.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0552eb06e7234da892e1d02365cd2b7b2b1f8233aa5aabdb2981587b7cc92ea0", size = 158347 }, - { url = "https://files.pythonhosted.org/packages/b2/fe/9f30890352e431e8508cc569912d3322147d3e7e4f321e48c0adfcb4c97d/simplejson-3.19.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf6a3b9a7d7191471b464fe38f684df10eb491ec9ea454003edb45a011ab187", size = 148456 }, - { url = "https://files.pythonhosted.org/packages/37/e3/663a09542ee021d4131162f7a164cb2e7f04ef48433a67591738afbf12ea/simplejson-3.19.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7017329ca8d4dca94ad5e59f496e5fc77630aecfc39df381ffc1d37fb6b25832", size = 152190 }, - { url = "https://files.pythonhosted.org/packages/31/20/4e0c4d35e10ff6465003bec304316d822a559a1c38c66ef6892ca199c207/simplejson-3.19.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67a20641afebf4cfbcff50061f07daad1eace6e7b31d7622b6fa2c40d43900ba", size = 149846 }, - { url = "https://files.pythonhosted.org/packages/08/7a/46e2e072cac3987cbb05946f25167f0ad2fe536748e7405953fd6661a486/simplejson-3.19.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd6a7dabcc4c32daf601bc45e01b79175dde4b52548becea4f9545b0a4428169", size = 151714 }, - { url = "https://files.pythonhosted.org/packages/7f/7d/dbeeac10eb61d5d8858d0bb51121a21050d281dc83af4c557f86da28746c/simplejson-3.19.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08f9b443a94e72dd02c87098c96886d35790e79e46b24e67accafbf13b73d43b", size = 158777 }, - { url = "https://files.pythonhosted.org/packages/fc/8f/a98bdbb799c6a4a884b5823db31785a96ba895b4b0f4d8ac345d6fe98bbf/simplejson-3.19.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa97278ae6614346b5ca41a45a911f37a3261b57dbe4a00602048652c862c28b", size = 154230 }, - { url = "https://files.pythonhosted.org/packages/b1/db/852eebceb85f969ae40e06babed1a93d3bacb536f187d7a80ff5823a5979/simplejson-3.19.3-cp312-cp312-win32.whl", hash = "sha256:ef28c3b328d29b5e2756903aed888960bc5df39b4c2eab157ae212f70ed5bf74", size = 74002 }, - { url = "https://files.pythonhosted.org/packages/fe/68/9f0e5df0651cb79ef83cba1378765a00ee8038e6201cc82b8e7178a7778e/simplejson-3.19.3-cp312-cp312-win_amd64.whl", hash = "sha256:1e662336db50ad665777e6548b5076329a94a0c3d4a0472971c588b3ef27de3a", size = 75596 }, - { url = "https://files.pythonhosted.org/packages/93/3a/5896821ed543899fcb9c4256c7e71bb110048047349a00f42bc8b8fb379f/simplejson-3.19.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0959e6cb62e3994b5a40e31047ff97ef5c4138875fae31659bead691bed55896", size = 92931 }, - { url = "https://files.pythonhosted.org/packages/39/15/5d33d269440912ee40d856db0c8be2b91aba7a219690ab01f86cb0edd590/simplejson-3.19.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a7bfad839c624e139a4863007233a3f194e7c51551081f9789cba52e4da5167", size = 75318 }, - { url = "https://files.pythonhosted.org/packages/2a/8d/2e7483a2bf7ec53acf7e012bafbda79d7b34f90471dda8e424544a59d484/simplejson-3.19.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afab2f7f2486a866ff04d6d905e9386ca6a231379181a3838abce1f32fbdcc37", size = 74971 }, - { url = "https://files.pythonhosted.org/packages/4d/9d/9bdf34437c8834a7cf7246f85e9d5122e30579f512c10a0c2560e994294f/simplejson-3.19.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00313681015ac498e1736b304446ee6d1c72c5b287cd196996dad84369998f7", size = 150112 }, - { url = "https://files.pythonhosted.org/packages/a7/e2/1f2ae2d89eaf85f6163c82150180aae5eaa18085cfaf892f8a57d4c51cbd/simplejson-3.19.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d936ae682d5b878af9d9eb4d8bb1fdd5e41275c8eb59ceddb0aeed857bb264a2", size = 158354 }, - { url = "https://files.pythonhosted.org/packages/60/83/26f610adf234c8492b3f30501e12f2271e67790f946c6898fe0c58aefe99/simplejson-3.19.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c6657485393f2e9b8177c77a7634f13ebe70d5e6de150aae1677d91516ce6b", size = 148455 }, - { url = "https://files.pythonhosted.org/packages/b5/4b/109af50006af77133653c55b5b91b4bd2d579ff8254ce11216c0b75f911b/simplejson-3.19.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a6a750d3c7461b1c47cfc6bba8d9e57a455e7c5f80057d2a82f738040dd1129", size = 152191 }, - { url = "https://files.pythonhosted.org/packages/75/dc/108872a8825cbd99ae6f4334e0490ff1580367baf12198bcaf988f6820ba/simplejson-3.19.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea7a4a998c87c5674a27089e022110a1a08a7753f21af3baf09efe9915c23c3c", size = 149954 }, - { url = "https://files.pythonhosted.org/packages/eb/be/deec1d947a5d0472276ab4a4d1a9378dc5ee27f3dc9e54d4f62ffbad7a08/simplejson-3.19.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6300680d83a399be2b8f3b0ef7ef90b35d2a29fe6e9c21438097e0938bbc1564", size = 151812 }, - { url = "https://files.pythonhosted.org/packages/e9/58/4ee130702d36b1551ef66e7587eefe56651f3669255bf748cd71691e2434/simplejson-3.19.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ab69f811a660c362651ae395eba8ce84f84c944cea0df5718ea0ba9d1e4e7252", size = 158880 }, - { url = "https://files.pythonhosted.org/packages/0f/e1/59cc6a371b60f89e3498d9f4c8109f6b7359094d453f5fe80b2677b777b0/simplejson-3.19.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:256e09d0f94d9c3d177d9e95fd27a68c875a4baa2046633df387b86b652f5747", size = 154344 }, - { url = "https://files.pythonhosted.org/packages/79/45/1b36044670016f5cb25ebd92497427d2d1711ecb454d00f71eb9a00b77cc/simplejson-3.19.3-cp313-cp313-win32.whl", hash = "sha256:2c78293470313aefa9cfc5e3f75ca0635721fb016fb1121c1c5b0cb8cc74712a", size = 74002 }, - { url = "https://files.pythonhosted.org/packages/e2/58/b06226e6b0612f2b1fa13d5273551da259f894566b1eef32249ddfdcce44/simplejson-3.19.3-cp313-cp313-win_amd64.whl", hash = "sha256:3bbcdc438dc1683b35f7a8dc100960c721f922f9ede8127f63bed7dfded4c64c", size = 75599 }, - { url = "https://files.pythonhosted.org/packages/0d/e7/f9fafbd4f39793a20cc52e77bbd766f7384312526d402c382928dc7667f6/simplejson-3.19.3-py3-none-any.whl", hash = "sha256:49cc4c7b940d43bd12bf87ec63f28cbc4964fc4e12c031cc8cd01650f43eb94e", size = 57004 }, ] [[package]] @@ -2771,16 +2228,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/59/2f/c95e94423c463177b8a7d55a1dbbd524840fe6a684844ff728f238e71f68/tables-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e71f63ac67c583ac42943c99c2d33bcc9e361e94d1ab1a763dc0698bdd9ff815", size = 7117696 }, { url = "https://files.pythonhosted.org/packages/88/d5/71665919aa2a5a3d2a20eeef3c71dc7c2ebbd9f26d114a7808514aba24d6/tables-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154773f97763ccc91a29bcead6ab7b5ef164c2ed8c409cd79a2115aa9b4184c9", size = 7520921 }, { url = "https://files.pythonhosted.org/packages/46/96/b5023c1f7b9d560cac3e2c0daceebaeb88dd24c70c75db2d291abfa563e5/tables-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:96b5e945d275415e79ddb0578657ecc6ac77030dcc0632ab2c39f89390bb239d", size = 6407137 }, - { url = "https://files.pythonhosted.org/packages/ab/c4/1efbcc699db863d88874f3d111e5bb6dd2e0fbaca38f91c992e696324730/tables-3.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6ba58205d1f6a4e0e2212bc221e76cf104f22190f90c3f1683f3c1ab138f28f", size = 6734990 }, - { url = "https://files.pythonhosted.org/packages/4a/db/4c7facfc805ab764f2ee256011d20f96791d2426afa3389ca7ff2a8a4ea8/tables-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdb5c040aa43e5e96259d6f6bb9df5b66fef2b071a6eb035c21bf6508e865d40", size = 5483377 }, - { url = "https://files.pythonhosted.org/packages/93/0a/53815b516a2465b329e5dc2079c99a8b6b1a23f6b9ce5da8a7ebc7892bf4/tables-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e694123fa886d9be57f55fc7e1dcacac49f0b4ed4a931c795bd8f82f7111b5a8", size = 7081356 }, - { url = "https://files.pythonhosted.org/packages/d3/e1/3f4adfc83eb7390abb964682a7d1df0dbe451dd2cee99750b1c7ca8e2c9d/tables-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c12d0d04de89297763923ebeaddfd7e0b51f29041895db284fd4913e7448b7", size = 7483570 }, - { url = "https://files.pythonhosted.org/packages/9a/d4/0b9ba57a5a8d2d05d1108055a8d70a4b066db4ebed61921de34043a31bdb/tables-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:a406d5dbbcb6604bd1ca129af337e0790d4e02d29d06159ddb9f74e38d756d32", size = 6388443 }, - { url = "https://files.pythonhosted.org/packages/ab/02/8c7aeaa6c8aac8e0298d40dc5fc55477fddc30cb31e4dc7e5e473be4b464/tables-3.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7b8bc07c715bad3d447ed8f834388ef2e10265e2c4af6b1297fc61adb645948f", size = 6725764 }, - { url = "https://files.pythonhosted.org/packages/91/f4/8683395d294b9e4576fd7d888aa6cf5583c013c2c0a2e47f862c2842407f/tables-3.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28677ed8e1a371471495599078f48da0850f82457d6c852ca77959c974371140", size = 5442663 }, - { url = "https://files.pythonhosted.org/packages/72/9b/ea43159eed8f81bfa1ead8fa8201a3c352e84c7220e046bb548736833951/tables-3.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaaea478dcf27dd54679ef2643c26d3b8b15676ad81e4d80a88fd1682d23deb1", size = 7078747 }, - { url = "https://files.pythonhosted.org/packages/04/95/b3e88edc674e35d9011b168df0d7a9b1c3ab98733fa26e740ac7964edc2f/tables-3.10.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e67a9f901842f9a4b1f3d2307f4bdd94047514fe0d0c558ed19c11f53c402a", size = 7479985 }, - { url = "https://files.pythonhosted.org/packages/63/ca/eaa029a43d269bdda6985931d6cfd479e876cd8cf7c887d818bef05ef03b/tables-3.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:5637fdcded5ba5426aa24e0e42d6f990926a4da7f193830df131dfcb7e842900", size = 6385562 }, ] [[package]] @@ -3019,31 +2466,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/ae/8ce5f29e65d5fa5790e3c80c289819c55e12be2e1b9f5b6a0e55e169b97d/watchfiles-1.0.4-cp311-cp311-win32.whl", hash = "sha256:4ebbeca9360c830766b9f0df3640b791be569d988f4be6c06d6fae41f187f105", size = 271013 }, { url = "https://files.pythonhosted.org/packages/a4/c6/79dc4a7c598a978e5fafa135090aaf7bbb03b8dec7bada437dfbe578e7ed/watchfiles-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:05d341c71f3d7098920f8551d4df47f7b57ac5b8dad56558064c3431bdfc0b74", size = 284229 }, { url = "https://files.pythonhosted.org/packages/37/3d/928633723211753f3500bfb138434f080363b87a1b08ca188b1ce54d1e05/watchfiles-1.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:32b026a6ab64245b584acf4931fe21842374da82372d5c039cba6bf99ef722f3", size = 276824 }, - { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 }, - { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 }, - { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 }, - { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 }, - { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 }, - { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 }, - { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 }, - { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 }, - { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 }, - { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 }, - { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 }, - { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 }, - { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 }, - { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 }, - { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 }, - { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 }, - { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 }, - { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 }, - { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 }, - { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 }, - { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 }, - { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 }, - { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 }, - { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 }, - { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 }, ] [[package]] @@ -3072,28 +2494,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/22/e5f7c33db0cb2c1d03b79fd60d189a1da044e2661f5fd01d629451e1db89/websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d", size = 169583 }, { url = "https://files.pythonhosted.org/packages/aa/2e/2b4662237060063a22e5fc40d46300a07142afe30302b634b4eebd717c07/websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a", size = 163969 }, { url = "https://files.pythonhosted.org/packages/94/a5/0cda64e1851e73fc1ecdae6f42487babb06e55cb2f0dc8904b81d8ef6857/websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b", size = 164408 }, - { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 }, - { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 }, - { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 }, - { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 }, - { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 }, - { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 }, - { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 }, - { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 }, - { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 }, - { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 }, - { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 }, - { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 }, - { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 }, - { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 }, - { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 }, - { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 }, - { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 }, - { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 }, - { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 }, - { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 }, - { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 }, - { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 }, { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 }, ] @@ -3114,39 +2514,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, - { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, - { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, - { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, - { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, - { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, - { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, - { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, - { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, - { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, - { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, - { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, - { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, - { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, - { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, - { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, - { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, - { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, - { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, - { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, - { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, - { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, - { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, - { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, - { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, - { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, - { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, - { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, - { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, - { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, - { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, - { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, - { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, - { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, ] diff --git a/workflow/envs/dev.yaml b/workflow/envs/dev.yaml new file mode 100644 index 000000000..d7dc5e67c --- /dev/null +++ b/workflow/envs/dev.yaml @@ -0,0 +1,19 @@ +name: pypsa-usa-dev +channels: +- conda-forge + +dependencies: +- python==3.11.9 +- pip + +- pip: + - bump2version + - pudb + - mypy~=1.11.0 + - pre-commit + - ruff~=0.5.2 + - types-PyYAML + - sphinx-autobuild + - sphinx + - sphinx-book-theme + - myst-parser diff --git a/workflow/rules/postprocess_sector.smk b/workflow/rules/postprocess_sector.smk index 08d6fe0b3..c54453ef5 100644 --- a/workflow/rules/postprocess_sector.smk +++ b/workflow/rules/postprocess_sector.smk @@ -142,7 +142,7 @@ rule plot_sankey_energy: + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/", output: RESULTS - + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/energy.png", + + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/energy.html", log: "logs/plot_figures/{interconnect}_s{simpl}_c{clusters}_l{ll}_{opts}_{sector}_energy_sankey.log", threads: 1 @@ -161,7 +161,7 @@ rule plot_sankey_carbon: + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/", output: RESULTS - + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/carbon.png", + + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/carbon.html", log: "logs/plot_figures/{interconnect}_s{simpl}_c{clusters}_l{ll}_{opts}_{sector}_energy_sankey.log", threads: 1 From d3e850267d1692fad199f6fff77db168c35dabcd Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 17:24:13 -0800 Subject: [PATCH 68/75] update dev dependency --- pyproject.toml | 2 +- uv.lock | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 94eb340de..f4759fdd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ Documentation = "https://pypsa-usa.readthedocs.io/en/latest/" Issues = "https://github.com/PyPSA/pypsa-usa/issues" Source = "https://github.com/PyPSA/pypsa-usa" -[project.optional-dependencies] +[dependency-groups] dev = [ "bump2version", "ipython", diff --git a/uv.lock b/uv.lock index e1f8d7d35..1c7424730 100644 --- a/uv.lock +++ b/uv.lock @@ -1627,7 +1627,7 @@ dependencies = [ { name = "xlrd" }, ] -[package.optional-dependencies] +[package.dev-dependencies] dev = [ { name = "bump2version" }, { name = "ipython" }, @@ -1645,7 +1645,6 @@ dev = [ [package.metadata] requires-dist = [ { name = "atlite", specifier = "==0.3.0" }, - { name = "bump2version", marker = "extra == 'dev'" }, { name = "cartopy", specifier = "==0.23.0" }, { name = "dask", specifier = "==2024.12.0" }, { name = "dask-expr", specifier = "==1.1.20" }, @@ -1658,11 +1657,8 @@ requires-dist = [ { name = "graphviz", specifier = ">=0.20.3" }, { name = "gurobipy", specifier = "==11.0.3" }, { name = "highspy", specifier = ">=1.9.0" }, - { name = "ipython", marker = "extra == 'dev'" }, { name = "linopy", specifier = "==0.3.14" }, { name = "matplotlib", specifier = "==3.8.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.11.0" }, - { name = "myst-parser", marker = "extra == 'dev'" }, { name = "netcdf4", specifier = "==1.6.4" }, { name = "networkx", specifier = "==3.1" }, { name = "numpy", specifier = "==1.26.0" }, @@ -1670,9 +1666,7 @@ requires-dist = [ { name = "pandas", specifier = "==2.2.2" }, { name = "plotly", specifier = "==5.17.0" }, { name = "pre-commit", specifier = ">=4.1.0" }, - { name = "pre-commit", marker = "extra == 'dev'" }, { name = "progressbar2", specifier = "==4.3.2" }, - { name = "pudb", marker = "extra == 'dev'" }, { name = "pulp", specifier = "==2.7.0" }, { name = "pyarrow", specifier = "==16.1.0" }, { name = "pycountry", specifier = "==22.3.5" }, @@ -1680,20 +1674,30 @@ requires-dist = [ { name = "pypsa", specifier = "==0.30.2" }, { name = "pyyaml", specifier = ">=6.0.2" }, { name = "rasterio", specifier = "==1.3.8" }, - { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.5.2" }, { name = "scipy", specifier = "==1.11.3" }, { name = "seaborn", specifier = "==0.13.2" }, { name = "shapely", specifier = "==2.0.2" }, { name = "snakemake", specifier = "==7.32.4" }, - { name = "sphinx", marker = "extra == 'dev'" }, - { name = "sphinx-autobuild", marker = "extra == 'dev'" }, - { name = "sphinx-book-theme", marker = "extra == 'dev'" }, { name = "tsam", specifier = ">=2.3.6" }, - { name = "types-pyyaml", marker = "extra == 'dev'" }, { name = "xarray", specifier = "==2024.9.0" }, { name = "xlrd", specifier = "==2.0.1" }, ] +[package.metadata.requires-dev] +dev = [ + { name = "bump2version" }, + { name = "ipython" }, + { name = "mypy", specifier = "~=1.11.0" }, + { name = "myst-parser" }, + { name = "pre-commit" }, + { name = "pudb" }, + { name = "ruff", specifier = "~=0.5.2" }, + { name = "sphinx" }, + { name = "sphinx-autobuild" }, + { name = "sphinx-book-theme" }, + { name = "types-pyyaml" }, +] + [[package]] name = "pyreadline3" version = "3.5.4" From c36f9c45a7480851abca5743b58c3150a91b948e Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 17:28:17 -0800 Subject: [PATCH 69/75] minor fixes --- docs/source/about-install.md | 2 +- docs/source/config-co2-base.md | 11 -- docs/source/contributing.md | 6 +- docs/source/rules-retrieving-data.md | 155 --------------------------- 4 files changed, 4 insertions(+), 170 deletions(-) delete mode 100644 docs/source/config-co2-base.md delete mode 100644 docs/source/rules-retrieving-data.md diff --git a/docs/source/about-install.md b/docs/source/about-install.md index 3cb309ecd..661c1742f 100644 --- a/docs/source/about-install.md +++ b/docs/source/about-install.md @@ -47,7 +47,7 @@ source .venv/bin/activate ``` ```{warning} -If you are migrating from `mamba`/`conda`, you may need to install system level dependencies that conda has previously handeled. These include, `HDF5` and `GDAL>=3.1` libraries, and if running sector studies, . +If you are migrating from `mamba`/`conda`, you may need to install system level dependencies that conda has previously handeled. These include, `HDF5` and `GDAL>=3.1` libraries. ``` ### Step 3b: `mamba` Installation diff --git a/docs/source/config-co2-base.md b/docs/source/config-co2-base.md deleted file mode 100644 index c069f6bca..000000000 --- a/docs/source/config-co2-base.md +++ /dev/null @@ -1,11 +0,0 @@ -(base_emission_values)= -# Base Emission Values - -If using the `{opts}` wildcard to reduce emissions, the user must put in a `co2base` value. Provided below are historical yearly CO2 emission values for both the power sector and all sectors at an interconnect level. This data can be used as a starting point for users. **Note the units in this table are Million Metric Tons (MMT).** This data originates from the [EIA State Level CO2 database](https://www.eia.gov/opendata/browser/co2-emissions/co2-emissions-aggregates?frequency=annual&data=value;&sortColumn=period;&sortDirection=desc;), and is compiled by the script `workflow/notebooks/historical_emissions.ipynb` - -```{eval-rst} -.. csv-table:: - :header-rows: 1 - :widths: 22,7,22,33 - :file: configtables/emissions.csv -``` diff --git a/docs/source/contributing.md b/docs/source/contributing.md index a9a60ddac..ae0afb26f 100644 --- a/docs/source/contributing.md +++ b/docs/source/contributing.md @@ -74,7 +74,7 @@ copy of the code under your account on the repository service. If you plan on contributing to the respository, please install developer dependencies. -#### 3a. If using UV +#### 3a. Using UV Run the following command from the activated `pypsa-usa` conda environment. @@ -82,12 +82,12 @@ Run the following command from the activated `pypsa-usa` conda environment. uv sync ``` -#### 3b. If using conda/mamba +#### 3b. Using conda/mamba Run the following command from the activated `pypsa-usa` conda environment. ```console -conda env update --name my_env --file environment.yaml +conda env update --name pypsa-usa --file workflow/envs/dev.yaml --prune ``` ### 4. Install pre-commit hooks: diff --git a/docs/source/rules-retrieving-data.md b/docs/source/rules-retrieving-data.md deleted file mode 100644 index bcea23e21..000000000 --- a/docs/source/rules-retrieving-data.md +++ /dev/null @@ -1,155 +0,0 @@ -(retrieve-data)= -# Retrieve Data - -Numerous datasets used in PyPSA USA are large and are not stored on GitHub. Insted, data is stored on Zenodo or supplier websites, and the workflow will automatically download these datasets via the `retrieve` rules - -```{note} -If you recieve the follwing error while running a retrieve rule on Linux - - FileNotFoundError: [Errno 2] No such file or directory: 'unzip' - -Run the command `sudo apt install zip` -``` - -(databundle)= -## Rule `retrieve_zenodo_databundles` - -Data used to create the base electrical network is pulled from [Breakthrough Energy](https://breakthroughenergy.org/) (~4.3GB). This includes geolocated data on substations, power lines, generators, electrical demand, and resource potentials. - -[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.4538590.svg)](https://zenodo.org/record/4538590) - -Protected land area data for the USA is retrieved from [Protected Planet](https://www.protectedplanet.net/en) via the [PyPSA Meets-Earth](https://pypsa-meets-earth.github.io/) data deposit (`natura_global`) (~100MB). - -[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.10067222.svg)](https://zenodo.org/records/10067222) - -Baythymetry data via [GEBCO](https://www.gebco.net/) and a cutout of USA [Copernicus Global Land Service](https://land.copernicus.eu/global/products/lc) data are downloaded from a PyPSA USA Zenodo depost (~2GB). - -[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.10067222.svg)](https://zenodo.org/records/10067222) - -(databundle-sector)= -## Rule `retrieve_sector_databundle` -Retrives data for sector coupling - -[![DOI](https://sandbox.zenodo.org/badge/DOI/10.5072/zenodo.10019422.svg)](https://zenodo.org/records/10019422) - -**Geographic Data** - -Geographic boundaries of the United States counties are taken from the -United States Census Bureau. Note, these follow 2020 boundaries to match -census numbers - -[![URL](https://img.shields.io/badge/URL-Cartographic_Boundaries-blue)]() - -County level populations are taken from the United States Census Bureau. Filters applied: - - Geography: All Counties within United States and Puerto Rico - - Year: 2020 - - Surveys: Decennial Census, Demographic and Housing Characteristics - -Sheet Name: Decennial Census - P1 | Total Population - 2020: DEC Demographic and Housing Characteristics - -[![URL](https://img.shields.io/badge/URL-United_States_Census_Bureau-blue)]() - -County level urbanization rates are taken from the United States Census Bureau. Filters applied: - - Geography: All Counties within United States and Puerto Rico - - Year: 2020 - - Surveys: Decennial Census, Demographic and Housing Characteristics - -Sheet Name: Decennial Census - H1 | Housing Units - 2020: DEC Demographic and Housing Characteristics - -[![URL](https://img.shields.io/badge/URL-United_States_Census_Bureau-blue)]() - -**Natural Gas Data** - -Natural Gas infrastructure includes: -- State to State pipeline capacity -- State level tranmsission pipeline volume -- Natural gas processing facility locations -- Natural gas processing facility locations (via EIA API) -- Natural gas underground storage (via EIA API) -- Natural Gas imports/exports by point of entry (via EIA API) - -[![URL](https://img.shields.io/badge/URL-Pipeline_Capacity-blue)]() -[![URL](https://img.shields.io/badge/URL-Pipeline_Shape-blue)]() -[![URL](https://img.shields.io/badge/URL-Processing_Capacity-blue)]() - -(retrieve-gridemissions)= -## Rule `retrieve_gridemissions_data` -```{eval-rst} -.. automodule:: retrieve_gridemissions_data -``` - -(retrieve-efs)= -## Rule `retrieve_nrel_efs_data` - -The [Electrification Futures Study](https://www.nrel.gov/analysis/electrification-futures.html) (EFS) are a series of publications from the NREL that explore the impacts of electrification in all USA economic sectors. As part of this, study are the EFS hourly load profiles. These load profiles represent projected end-use electricity demand for various scenarios. Load profiles are provided for a subset of years (2018, 2020, 2024, 2030, 2040, 2050) and are aggregated to the state, sector, and select subsector level. See the [EFS Load Profile Data Catalog](https://data.nrel.gov/submissions/126) for full details. - -[![URL](https://img.shields.io/badge/URL-EFS_Load_Profiles-blue)]() - -(retrieve-cutout)= -## Rule `retrieve_cutout` - -Cutouts are spatio-temporal subsets of the USA weather data from the [ERA5 dataset](https://cds.climate.copernicus.eu/cdsapp#!/dataset/reanalysis-era5-single-levels?tab=overview). They have been prepared by and are for use with the [atlite](https://github.com/PyPSA/atlite) tool. You can either generate them yourself using the build_cutouts rule or retrieve them directly from zenodo through the rule `retrieve_cutout`. - -[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.10067222.svg)](https://zenodo.org/records/10067222) - -```{note} -Only the 2019 interconnects based on ERA5 have been prepared and saved to Zenodo for download -``` - -(costs)= -## Rule `retrieve_cost_data` - -This rule downloads economic assumptions from various sources. - -The [NREL](https://www.nrel.gov/) [Annual Technology Baseline](https://atb.nrel.gov/) provides economic parameters on capital costs, fixed operation costs, variable operating costs, fuel costs, technology specific discount rates, average capacity factors, and efficiencies. - -[![URL](https://img.shields.io/badge/URL-NREL_ATB-blue)]() - -[![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white)](https://data.openei.org/s3_viewer?bucket=oedi-data-lake&prefix=ATB%2F) - -State level capital cost supply side generator cost multipliers are pulled from the "Capital Cost and Performance -Characteristic Estimates for Utility Scale Electric Power Generating Technologies" by the [EIA](https://www.eia.gov/). Note, these have been saved as CSV's and come with the repository download - -[![URL](https://img.shields.io/badge/URL-CAPEX_Multipliers-blue)]() - -State level historial monthly **natural gas** fuel prices are taken from the [EIA](https://www.eia.gov/). This includes seperate prices for electrical power producers, industrial customers, commercial customers, and residential customers. - -[![URL](https://img.shields.io/badge/URL-EIA_Natural_Gas_Prices-blue)]() - -State level historical **coal** fuel prices are taken from the [EIA](https://www.eia.gov/). - -[![URL](https://img.shields.io/badge/URL-EIA_Coal_Prices-blue)]() - -The [Annual Technology Baseline](https://atb.nrel.gov/) also provides data on the [transportation sector](https://atb.nrel.gov/transportation/2020/index), including fuel usage and capital costs. - -[![URL](https://img.shields.io/badge/URL-NREL_ATB_Transportation-blue)]() - -To populate any missing data, the [PyPSA/technology-data](https://github.com/PyPSA/technology-data) project is used. Data from here is only used when no other sources can be found, as it is mostly European focused. - -[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/PyPSA/technology-data) - -**Relevant Settings** - -```yaml -enable: - retrieve_cost_data: - -costs: - year: - version: -``` - -```{seealso} -Documentation of the configuration file ``config/config.yaml`` at -:ref:`costs_cf` -``` - -**Outputs** - -- ``resources/costs.csv`` - -(retrieve-caiso-data)= -## Rule `retrieve_caiso_data` -```{eval-rst} -.. automodule:: retrieve_caiso_data -``` From 65a87fbecdafd578534d2555e3f694b516f77c77 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Wed, 12 Feb 2025 17:36:13 -0800 Subject: [PATCH 70/75] dev dependency update --- docs/source/contributing.md | 2 +- pyproject.toml | 2 +- uv.lock | 28 ++++++++++++---------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/docs/source/contributing.md b/docs/source/contributing.md index ae0afb26f..4245a7d4d 100644 --- a/docs/source/contributing.md +++ b/docs/source/contributing.md @@ -79,7 +79,7 @@ If you plan on contributing to the respository, please install developer depende Run the following command from the activated `pypsa-usa` conda environment. ```console -uv sync +uv pip install -r pyproject.toml --extra dev ``` #### 3b. Using conda/mamba diff --git a/pyproject.toml b/pyproject.toml index f4759fdd2..94eb340de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ Documentation = "https://pypsa-usa.readthedocs.io/en/latest/" Issues = "https://github.com/PyPSA/pypsa-usa/issues" Source = "https://github.com/PyPSA/pypsa-usa" -[dependency-groups] +[project.optional-dependencies] dev = [ "bump2version", "ipython", diff --git a/uv.lock b/uv.lock index 1c7424730..e1f8d7d35 100644 --- a/uv.lock +++ b/uv.lock @@ -1627,7 +1627,7 @@ dependencies = [ { name = "xlrd" }, ] -[package.dev-dependencies] +[package.optional-dependencies] dev = [ { name = "bump2version" }, { name = "ipython" }, @@ -1645,6 +1645,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "atlite", specifier = "==0.3.0" }, + { name = "bump2version", marker = "extra == 'dev'" }, { name = "cartopy", specifier = "==0.23.0" }, { name = "dask", specifier = "==2024.12.0" }, { name = "dask-expr", specifier = "==1.1.20" }, @@ -1657,8 +1658,11 @@ requires-dist = [ { name = "graphviz", specifier = ">=0.20.3" }, { name = "gurobipy", specifier = "==11.0.3" }, { name = "highspy", specifier = ">=1.9.0" }, + { name = "ipython", marker = "extra == 'dev'" }, { name = "linopy", specifier = "==0.3.14" }, { name = "matplotlib", specifier = "==3.8.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.11.0" }, + { name = "myst-parser", marker = "extra == 'dev'" }, { name = "netcdf4", specifier = "==1.6.4" }, { name = "networkx", specifier = "==3.1" }, { name = "numpy", specifier = "==1.26.0" }, @@ -1666,7 +1670,9 @@ requires-dist = [ { name = "pandas", specifier = "==2.2.2" }, { name = "plotly", specifier = "==5.17.0" }, { name = "pre-commit", specifier = ">=4.1.0" }, + { name = "pre-commit", marker = "extra == 'dev'" }, { name = "progressbar2", specifier = "==4.3.2" }, + { name = "pudb", marker = "extra == 'dev'" }, { name = "pulp", specifier = "==2.7.0" }, { name = "pyarrow", specifier = "==16.1.0" }, { name = "pycountry", specifier = "==22.3.5" }, @@ -1674,30 +1680,20 @@ requires-dist = [ { name = "pypsa", specifier = "==0.30.2" }, { name = "pyyaml", specifier = ">=6.0.2" }, { name = "rasterio", specifier = "==1.3.8" }, + { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.5.2" }, { name = "scipy", specifier = "==1.11.3" }, { name = "seaborn", specifier = "==0.13.2" }, { name = "shapely", specifier = "==2.0.2" }, { name = "snakemake", specifier = "==7.32.4" }, + { name = "sphinx", marker = "extra == 'dev'" }, + { name = "sphinx-autobuild", marker = "extra == 'dev'" }, + { name = "sphinx-book-theme", marker = "extra == 'dev'" }, { name = "tsam", specifier = ">=2.3.6" }, + { name = "types-pyyaml", marker = "extra == 'dev'" }, { name = "xarray", specifier = "==2024.9.0" }, { name = "xlrd", specifier = "==2.0.1" }, ] -[package.metadata.requires-dev] -dev = [ - { name = "bump2version" }, - { name = "ipython" }, - { name = "mypy", specifier = "~=1.11.0" }, - { name = "myst-parser" }, - { name = "pre-commit" }, - { name = "pudb" }, - { name = "ruff", specifier = "~=0.5.2" }, - { name = "sphinx" }, - { name = "sphinx-autobuild" }, - { name = "sphinx-book-theme" }, - { name = "types-pyyaml" }, -] - [[package]] name = "pyreadline3" version = "3.5.4" From c4a8d17ee67223c1c55bbce15a05d33aadc0894c Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Thu, 13 Feb 2025 13:50:30 -0800 Subject: [PATCH 71/75] fix sankey file extension --- workflow/Snakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/Snakefile b/workflow/Snakefile index e70fb2e98..e1818a452 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -206,7 +206,7 @@ def sector_figures(wildcards): figs.append( expand( RESULTS - + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/{figure}.png", + + "{interconnect}/figures/s{simpl}_c{clusters}/l{ll}_{opts}_{sector}/system/sankey/{figure}.html", **config["scenario"], figure=FIGURES_SECTOR_SANKEY, ) From 94991a5b8a254c7721a10408fcc4423a097cf183 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Sun, 16 Feb 2025 17:25:47 -0800 Subject: [PATCH 72/75] sector costs hotfix --- workflow/scripts/build_sector_costs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/build_sector_costs.py b/workflow/scripts/build_sector_costs.py index 05927d44c..0ccff0177 100644 --- a/workflow/scripts/build_sector_costs.py +++ b/workflow/scripts/build_sector_costs.py @@ -420,7 +420,7 @@ def _get_capex(self): return self._expand_data(df)[self.columns] def _get_lifetime(self): - df = self.get_capex() + df = self._get_capex() df["parameter"] = "lifetime" df["value"] = self.lifetime df["unit"] = "years" @@ -439,7 +439,7 @@ def _get_efficiency(self): # return self._correct_fom_units(df) def _get_fixed_costs(self): - df = self.get_capex() + df = self._get_capex() df["parameter"] = "FOM" df["unit"] = "%/year" df["value"] = df.technology.map(assign_vehicle_type) From 954644b2f09598266e73b46caae62526a9242052 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 17 Feb 2025 15:51:27 -0800 Subject: [PATCH 73/75] demand template --- workflow/rules/retrieve.smk | 15 + workflow/scripts/build_demand.py | 392 +++--------------------- workflow/scripts/demand_scalers.py | 328 ++++++++++++++++++++ workflow/scripts/eia.py | 4 +- workflow/scripts/retrieve_eia_demand.py | 142 +++++++++ 5 files changed, 536 insertions(+), 345 deletions(-) create mode 100644 workflow/scripts/demand_scalers.py create mode 100644 workflow/scripts/retrieve_eia_demand.py diff --git a/workflow/rules/retrieve.smk b/workflow/rules/retrieve.smk index d7ed375ad..887909f03 100644 --- a/workflow/rules/retrieve.smk +++ b/workflow/rules/retrieve.smk @@ -261,3 +261,18 @@ if "EGS" in config["electricity"]["extendable_carriers"]["Generator"]: LOGS + "retrieve_EGS_{interconnect}.log", script: "../scripts/retrieve_egs.py" + + +rule retrieve_eia_demand: + wildcard_constraints: + source="energy|transport|electricity", + params: + api=config["api"]["eia"], + output: + DATA + "eia/demand/{scenario}/{source}.csv", + resources: + mem_mb=5000, + log: + LOGS + "retrieve_eia_demand_{scenario}_{source}.log", + script: + "../retrieve_eia_demand.py" diff --git a/workflow/scripts/build_demand.py b/workflow/scripts/build_demand.py index 215debebb..ca7342c5c 100644 --- a/workflow/scripts/build_demand.py +++ b/workflow/scripts/build_demand.py @@ -1,11 +1,7 @@ """Builds the demand data for the PyPSA network.""" -# snakemake is not liking this futures import. Removing type hints in context class -# from __future__ import annotations - import calendar import logging -import sqlite3 import sys from abc import ABC, abstractmethod from pathlib import Path @@ -20,7 +16,15 @@ import xarray as xr from _helpers import configure_logging, get_multiindex_snapshots from constants_sector import FIPS_2_STATE, NAICS, VMT_UNIT_CONVERSION -from eia import EnergyDemand, TransportationDemand +from demand_scalers import ( + AeoElectricityScaler, + AeoEnergyScalerApi, + AeoScaler, + AeoVmtScalerApi, + DemandScaler, + EfsElectricityScalar, +) +from eia import TransportationDemand logger = logging.getLogger(__name__) @@ -1832,7 +1836,7 @@ def get_laf_per_bus( ### -# Formatters and Scalers +# Formatters ### @@ -1909,319 +1913,22 @@ def need_scaling(self, df: pd.DataFrame) -> bool: def assign_scaler(self): # type DemandScaler """Assign logic to scale demand with.""" if self.scaling_method == "aeo_energy": - assert self.api, "Must provide eia api key" - return AeoEnergyScaler(self.api) + assert self.filepath.endswith(".csv"), "Must provide formatted AEO.csv data" + return AeoScaler(self.filepath) elif self.scaling_method == "aeo_electricity": assert self.filepath.endswith(".sqlite"), "Must provide pudl.sqlite file" return AeoElectricityScaler(self.filepath) elif self.scaling_method == "efs": assert self.filepath.endswith(".csv"), "Must provide EFS.csv data" return EfsElectricityScalar(self.filepath) - elif self.scaling_method == "aeo_vmt": + elif self.scaling_method == "aeo_energy_api": assert self.api, "Must provide eia api key" - return AeoVmtScaler(self.api) - else: - raise NotImplementedError - - -class DemandScaler(ABC): - """Allow the scaling of input data bases on different energy projections.""" - - def __init__(self): - self.projection = self.get_projections() - - @abstractmethod - def get_projections(self) -> pd.DataFrame: - """Get implementation specific energy projections.""" - pass - - def get_growth(self, start_year: int, end_year: int, sector: str) -> float: - """Returns decimal change between two years.""" - min_year = self.projection.index.min() - max_year = self.projection.index.max() - - if start_year < min_year: - logger.warning(f"Setting base demand scaling year to {min_year}") - start_year = min_year - if end_year > max_year: - logger.warning(f"Setting final demand scaling year to {max_year}") - end_year = max_year - - start = self.projection.at[start_year, sector] - end = self.projection.at[end_year, sector] - - return end / start - - def scale( - self, - df: pd.DataFrame, - start_year: int, - end_year: int, - sector: str, - ) -> pd.DataFrame: - """Scales data.""" - growth = self.get_growth(start_year, end_year, sector) - new = df.mul(growth) - return self.reindex(new, end_year) - - @staticmethod - def reindex(df: pd.DataFrame, year: int) -> pd.DataFrame: - """ - Reindex a dataframe for a different planning horizon. - - Input dataframe will be... - - | | BusName_1 | BusName_2 | ... | BusName_n | - |---------------------|-----------|-----------|-----|-----------| - | 2019-01-01 00:00:00 | aaa | ddd | | ggg | - | 2019-01-01 01:00:00 | bbb | eee | | hhh | - | ... | ... | ... | | ... | - | 2019-02-28 23:00:00 | ccc | fff | | iii | - - Output dataframe will be... - - | | BusName_1 | BusName_2 | ... | BusName_n | - |---------------------|-----------|-----------|-----|-----------| - | 2030-01-01 00:00:00 | aaa | ddd | | ggg | - | 2030-01-01 01:00:00 | bbb | eee | | hhh | - | ... | ... | ... | | ... | - | 2030-02-28 23:00:00 | ccc | fff | | iii | - """ - new = df.copy() - new.index = new.index.map(lambda x: x.replace(year=year)) - return new - - -class AeoElectricityScaler(DemandScaler): - """Scales against EIA Annual Energy Outlook electricity projections.""" - - def __init__(self, pudl: str, scenario: str = "reference"): - self.pudl = pudl - self.scenario = scenario - self.region = "united_states" - super().__init__() - - def get_projections(self) -> pd.DataFrame: - """ - Get sector yearly END-USE ELECTRICITY growth rates from AEO. - - | | power | units | - |----- |-------|-------| - | 2021 | ### | ### | - | 2022 | ### | ### | - | 2023 | ### | ### | - | ... | | | - | 2049 | ### | ### | - | 2050 | ### | ### | - """ - con = sqlite3.connect(self.pudl) - df = pd.read_sql_query( - f""" - SELECT - projection_year, - technology_description_eiaaeo, - gross_generation_mwh - FROM - core_eiaaeo__yearly_projected_generation_in_electric_sector_by_technology - WHERE - electricity_market_module_region_eiaaeo = "{self.region}" AND - model_case_eiaaeo = "{self.scenario}" - """, - con, - ) - - df = ( - df.drop(columns=["technology_description_eiaaeo"]) - .rename( - columns={"projection_year": "year", "gross_generation_mwh": "power"}, - ) - .groupby("year") - .sum() - ) - df["units"] = "mwh" - return df - - -class AeoEnergyScaler(DemandScaler): - """Scales against EIA Annual Energy Outlook energy projections.""" - - def __init__(self, api: str, scenario: str = "reference"): - self.api = api - self.scenario = scenario - self.region = "united_states" - super().__init__() - - def get_sector_data(self, years: list[int], sector: str) -> pd.DataFrame: - """Function to piece togehter historical and projected values.""" - start_year = min(years) - end_year = max(years) - - data = [] - - if start_year < 2024: - data.append( - EnergyDemand(sector=sector, year=start_year, api=self.api).get_data(), - ) - if end_year >= 2024: - data.append( - EnergyDemand(sector=sector, year=end_year, api=self.api).get_data(), - ) - return pd.concat(data) - - def get_projections(self) -> pd.DataFrame: - """ - Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL - level. - - | | residential | commercial | industrial | transport | units | - |----- |-------------|-------------|-------------|------------|-------| - | 2018 | ### | ### | ### | ### | ### | - | 2019 | ### | ### | ### | ### | ### | - | 2020 | ### | ### | ### | ### | ### | - | ... | | | | | | - | 2049 | ### | ### | ### | ### | ### | - | 2050 | ### | ### | ### | ### | ### | - """ - years = range(2017, 2051) - - # sectors = ("residential", "commercial", "industry", "transport") - sectors = ("residential", "commercial", "industry") - - df = pd.DataFrame( - index=years, - ) - - for sector in sectors: - sector_data = self.get_sector_data(years, sector).sort_index() - df[sector] = sector_data.value - - df["units"] = "quads" - return df - - -class AeoVmtScaler(DemandScaler): - """Scales against EIA Annual Energy Outlook vehicle mile traveled projections.""" - - def __init__(self, api: str, scenario: str = "reference"): - self.api = api - self.scenario = scenario - self.region = "united_states" - super().__init__() - - def get_historical_value(self, year: int, sector: str) -> float: - """Returns single year value at a time.""" - return TransportationDemand(vehicle=sector, year=year, api=self.api).get_data(pivot=True).values[0][0] - - def get_future_values( - self, - year: int, - sector: str, - ) -> pd.DataFrame: - """Returns all values from 2024 onwards.""" - return TransportationDemand( - vehicle=sector, - year=year, - api=self.api, - scenario=self.scenario, - ).get_data() - - def get_projections(self) -> pd.DataFrame: - """ - Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL - level. - - | | light_duty | med_duty | heavy_duty | bus | units | - |----- |------------|-----------|-------------|------|-------| - | 2018 | ### | ### | ### | ### | ### | - | 2019 | ### | ### | ### | ### | ### | - | 2020 | ### | ### | ### | ### | ### | - | ... | | | | | | - | 2049 | ### | ### | ### | ### | ### | - | 2050 | ### | ### | ### | ### | ### | - """ - years = range(2017, 2051) - - vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") - - df = pd.DataFrame( - columns=["light_duty", "med_duty", "heavy_duty", "bus"], - index=years, - ) - - for year in sorted(years): - if year < 2024: - for vehicle in vehicles: - df.at[year, vehicle] = self.get_historical_value( - year, - vehicle, - ) - - for vehicle in vehicles: - aeo = self.get_future_values(max(years), vehicle) - for year in years: - if year < 2024: - continue - df.at[year, vehicle] = aeo.at[year, "value"] - - df["units"] = "thousand VMT" - return df - - -class EfsElectricityScalar(DemandScaler): - """Scales against NREL Electrification Futures Study electricity projections.""" - - def __init__(self, filepath: str): - self.efs = filepath - self.region = "united_states" - super().__init__() - - def read(self) -> pd.DataFrame: - """Read in raw EFS data.""" - df = pd.read_csv(self.efs, engine="pyarrow") - return ( - df.drop( - columns=[ - "Electrification", - "TechnologyAdvancement", - "LocalHourID", - "Sector", - "Subsector", - ], - ) - .groupby(["Year", "State"]) - .sum() - ) - - def interpolate(self, df: pd.DataFrame) -> pd.DataFrame: - """Function interpolates between provided demand data years.""" - efs_years = df.index - new_years = range(min(efs_years), max(efs_years) + 1) - df = df.reindex(new_years) - return df.interpolate() - - def get_projections(self) -> pd.DataFrame: - """ - Get sector yearly END-USE ELECTRICITY growth rates from EFS. Linear - interpolates missing values. - - | | power | units | - |----- |-------|-------| - | 2018 | ### | ### | - | 2019 | ### | ### | - | 2020 | ### | ### | - | ... | | | - | 2049 | ### | ### | - | 2050 | ### | ### | - """ - df = self.read().reset_index() - if self.region == "united_states": - df = df.drop(columns="State").groupby("Year").sum() + return AeoEnergyScalerApi(self.api) + elif self.scaling_method == "aeo_vmt_api": + assert self.api, "Must provide eia api key" + return AeoVmtScalerApi(self.api) else: raise NotImplementedError - df = self.interpolate(df) - df = df.rename(columns={"LoadMW": "power"}) - df["units"] = "MWh" - return df ### @@ -2235,42 +1942,41 @@ def get_demand_params( **kwargs, ) -> tuple: """Gets hard coded demand options.""" - match end_use: - case "power": # electricity only study - demand_profile = demand_params["profile"] + if end_use == "power": # electricity only study + demand_profile = demand_params["profile"] + demand_disaggregation = "pop" + if demand_profile == "efs": + scaling_method = "efs" + elif demand_profile == "eia": + scaling_method = "aeo_electricity" + elif demand_profile == "ferc": + scaling_method = "aeo_electricity" + else: + logger.warning( + f"No scaling method available for {demand_profile} profile. Setting to 'aeo_electricity'", + ) + elif end_use in ("residential", "commercial"): + demand_profile = "eulp" + demand_disaggregation = "pop" + scaling_method = "aeo_energy" + elif end_use == "industry": + demand_profile = "cliu" + demand_disaggregation = "cliu" + scaling_method = "aeo_energy" + elif end_use == "transport": + vehicle = kwargs.get("vehicle", None) + if not vehicle: # road transport + demand_profile = "transport_efs_aeo" demand_disaggregation = "pop" - if demand_profile == "efs": - scaling_method = "efs" - elif demand_profile == "eia": - scaling_method = "aeo_electricity" - elif demand_profile == "ferc": - scaling_method = "aeo_electricity" - else: - logger.warning( - f"No scaling method available for {demand_profile} profile. Setting to 'aeo_electricity'", - ) - case "residential" | "commercial": - demand_profile = "eulp" + scaling_method = "aeo_vmt" + elif vehicle.startswith(("air", "rail", "boat")): + demand_profile = "transport_aeo" demand_disaggregation = "pop" - scaling_method = "aeo_energy" - case "industry": - demand_profile = "cliu" - demand_disaggregation = "cliu" - scaling_method = "aeo_energy" - case "transport": - vehicle = kwargs.get("vehicle", None) - if not vehicle: # road transport - demand_profile = "transport_efs_aeo" - demand_disaggregation = "pop" - scaling_method = "aeo_vmt" - elif vehicle.startswith(("air", "rail", "boat")): - demand_profile = "transport_aeo" - demand_disaggregation = "pop" - scaling_method = None # will extract data for any year - else: - raise NotImplementedError - case _: + scaling_method = None # will extract data for any year + else: raise NotImplementedError + else: + raise ValueError(end_use) return demand_profile, demand_disaggregation, scaling_method diff --git a/workflow/scripts/demand_scalers.py b/workflow/scripts/demand_scalers.py new file mode 100644 index 000000000..80bc85c22 --- /dev/null +++ b/workflow/scripts/demand_scalers.py @@ -0,0 +1,328 @@ +"""Demand Scalers for electricity and sector studies.""" + +import logging +import sqlite3 +from abc import ABC, abstractmethod + +import pandas as pd +from eia import EnergyDemand, TransportationDemand + +logger = logging.getLogger(__name__) + + +class DemandScaler(ABC): + """Allow the scaling of input data bases on different energy projections.""" + + def __init__(self): + self.projection = self.get_projections() + + @abstractmethod + def get_projections(self) -> pd.DataFrame: + """Get implementation specific energy projections.""" + pass + + def get_growth(self, start_year: int, end_year: int, sector: str) -> float: + """Returns decimal change between two years.""" + min_year = self.projection.index.min() + max_year = self.projection.index.max() + + if start_year < min_year: + logger.warning(f"Setting base demand scaling year to {min_year}") + start_year = min_year + if end_year > max_year: + logger.warning(f"Setting final demand scaling year to {max_year}") + end_year = max_year + + start = self.projection.at[start_year, sector] + end = self.projection.at[end_year, sector] + + return end / start + + def scale( + self, + df: pd.DataFrame, + start_year: int, + end_year: int, + sector: str, + ) -> pd.DataFrame: + """Scales data.""" + growth = self.get_growth(start_year, end_year, sector) + new = df.mul(growth) + return self.reindex(new, end_year) + + @staticmethod + def reindex(df: pd.DataFrame, year: int) -> pd.DataFrame: + """ + Reindex a dataframe for a different planning horizon. + + Input dataframe will be... + + | | BusName_1 | BusName_2 | ... | BusName_n | + |---------------------|-----------|-----------|-----|-----------| + | 2019-01-01 00:00:00 | aaa | ddd | | ggg | + | 2019-01-01 01:00:00 | bbb | eee | | hhh | + | ... | ... | ... | | ... | + | 2019-02-28 23:00:00 | ccc | fff | | iii | + + Output dataframe will be... + + | | BusName_1 | BusName_2 | ... | BusName_n | + |---------------------|-----------|-----------|-----|-----------| + | 2030-01-01 00:00:00 | aaa | ddd | | ggg | + | 2030-01-01 01:00:00 | bbb | eee | | hhh | + | ... | ... | ... | | ... | + | 2030-02-28 23:00:00 | ccc | fff | | iii | + """ + new = df.copy() + new.index = new.index.map(lambda x: x.replace(year=year)) + return new + + +class AeoElectricityScaler(DemandScaler): + """Scales against EIA Annual Energy Outlook electricity projections.""" + + def __init__(self, pudl: str, scenario: str = "reference"): + self.pudl = pudl + self.scenario = scenario + self.region = "united_states" + super().__init__() + + def get_projections(self) -> pd.DataFrame: + """ + Get sector yearly END-USE ELECTRICITY growth rates from AEO. + + | | power | units | + |----- |-------|-------| + | 2021 | ### | ### | + | 2022 | ### | ### | + | 2023 | ### | ### | + | ... | | | + | 2049 | ### | ### | + | 2050 | ### | ### | + """ + con = sqlite3.connect(self.pudl) + df = pd.read_sql_query( + f""" + SELECT + projection_year, + technology_description_eiaaeo, + gross_generation_mwh + FROM + core_eiaaeo__yearly_projected_generation_in_electric_sector_by_technology + WHERE + electricity_market_module_region_eiaaeo = "{self.region}" AND + model_case_eiaaeo = "{self.scenario}" + """, + con, + ) + + df = ( + df.drop(columns=["technology_description_eiaaeo"]) + .rename( + columns={"projection_year": "year", "gross_generation_mwh": "power"}, + ) + .groupby("year") + .sum() + ) + df["units"] = "mwh" + return df + + +class EfsElectricityScalar(DemandScaler): + """Scales against NREL Electrification Futures Study electricity projections.""" + + def __init__(self, filepath: str): + self.efs = filepath + self.region = "united_states" + super().__init__() + + def read(self) -> pd.DataFrame: + """Read in raw EFS data.""" + df = pd.read_csv(self.efs, engine="pyarrow") + return ( + df.drop( + columns=[ + "Electrification", + "TechnologyAdvancement", + "LocalHourID", + "Sector", + "Subsector", + ], + ) + .groupby(["Year", "State"]) + .sum() + ) + + def interpolate(self, df: pd.DataFrame) -> pd.DataFrame: + """Function interpolates between provided demand data years.""" + efs_years = df.index + new_years = range(min(efs_years), max(efs_years) + 1) + df = df.reindex(new_years) + return df.interpolate() + + def get_projections(self) -> pd.DataFrame: + """ + Get sector yearly END-USE ELECTRICITY growth rates from EFS. Linear + interpolates missing values. + + | | power | units | + |----- |-------|-------| + | 2018 | ### | ### | + | 2019 | ### | ### | + | 2020 | ### | ### | + | ... | | | + | 2049 | ### | ### | + | 2050 | ### | ### | + """ + df = self.read().reset_index() + if self.region == "united_states": + df = df.drop(columns="State").groupby("Year").sum() + else: + raise NotImplementedError + df = self.interpolate(df) + df = df.rename(columns={"LoadMW": "power"}) + df["units"] = "MWh" + return df + + +class AeoScaler(DemandScaler): + """Scales according to AEO data previously extracted.""" + + def __init__(self, filepath: str): + self.filepath = filepath + super().__init__() + + def get_projections(self) -> pd.DataFrame: + """Get yearly END-USE growth rates at a national level.""" + return pd.read_csv(self.filepath, index_col=0) + + +""" +Deprecated in favour of processing data beforehand to avoid connection errors +keep until next release for testing purposes +""" + + +class AeoEnergyScalerApi(DemandScaler): + """Scales against EIA Annual Energy Outlook energy projections.""" + + def __init__(self, api: str, scenario: str = "reference"): + self.api = api + self.scenario = scenario + self.region = "united_states" + super().__init__() + + def get_sector_data(self, years: list[int], sector: str) -> pd.DataFrame: + """Function to piece togehter historical and projected values.""" + start_year = min(years) + end_year = max(years) + + data = [] + + if start_year < 2024: + data.append( + EnergyDemand(sector=sector, year=start_year, api=self.api).get_data(), + ) + if end_year >= 2024: + data.append( + EnergyDemand(sector=sector, year=end_year, api=self.api).get_data(), + ) + return pd.concat(data) + + def get_projections(self) -> pd.DataFrame: + """ + Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL + level. + + | | residential | commercial | industrial | transport | units | + |----- |-------------|-------------|-------------|------------|-------| + | 2018 | ### | ### | ### | ### | ### | + | 2019 | ### | ### | ### | ### | ### | + | 2020 | ### | ### | ### | ### | ### | + | ... | | | | | | + | 2049 | ### | ### | ### | ### | ### | + | 2050 | ### | ### | ### | ### | ### | + """ + years = range(2017, 2051) + + # sectors = ("residential", "commercial", "industry", "transport") + sectors = ("residential", "commercial", "industry") + + df = pd.DataFrame( + index=years, + ) + + for sector in sectors: + sector_data = self.get_sector_data(years, sector).sort_index() + df[sector] = sector_data.value + + df["units"] = "quads" + return df + + +class AeoVmtScalerApi(DemandScaler): + """Scales against EIA Annual Energy Outlook vehicle mile traveled projections.""" + + def __init__(self, api: str, scenario: str = "reference"): + self.api = api + self.scenario = scenario + self.region = "united_states" + super().__init__() + + def get_historical_value(self, year: int, sector: str) -> float: + """Returns single year value at a time.""" + return TransportationDemand(vehicle=sector, year=year, api=self.api).get_data(pivot=True).values[0][0] + + def get_future_values( + self, + year: int, + sector: str, + ) -> pd.DataFrame: + """Returns all values from 2024 onwards.""" + return TransportationDemand( + vehicle=sector, + year=year, + api=self.api, + scenario=self.scenario, + ).get_data() + + def get_projections(self) -> pd.DataFrame: + """ + Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL + level. + + | | light_duty | med_duty | heavy_duty | bus | units | + |----- |------------|-----------|-------------|------|-------| + | 2018 | ### | ### | ### | ### | ### | + | 2019 | ### | ### | ### | ### | ### | + | 2020 | ### | ### | ### | ### | ### | + | ... | | | | | | + | 2049 | ### | ### | ### | ### | ### | + | 2050 | ### | ### | ### | ### | ### | + """ + years = range(2017, 2051) + + vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") + + df = pd.DataFrame( + columns=["light_duty", "med_duty", "heavy_duty", "bus"], + index=years, + ) + + for year in sorted(years): + if year < 2024: + for vehicle in vehicles: + df.at[year, vehicle] = self.get_historical_value( + year, + vehicle, + ) + + for vehicle in vehicles: + aeo = self.get_future_values(max(years), vehicle) + for year in years: + if year < 2024: + continue + df.at[year, vehicle] = aeo.at[year, "value"] + + df["units"] = "thousand VMT" + return df diff --git a/workflow/scripts/eia.py b/workflow/scripts/eia.py index 9d70e3818..e25849a63 100644 --- a/workflow/scripts/eia.py +++ b/workflow/scripts/eia.py @@ -527,12 +527,12 @@ def _request_eia_data(url: str) -> dict[str, dict | str]: session = requests.Session() retries = Retry( total=3, - backoff_factor=0.1, + backoff_factor=0.5, status_forcelist=[500, 502, 503, 504], ) session.mount("https://", HTTPAdapter(max_retries=retries)) - response = session.get(url, timeout=30) + response = session.get(url, timeout=60) if response.status_code == 200: return response.json() # Assumes the response is in JSON format else: diff --git a/workflow/scripts/retrieve_eia_demand.py b/workflow/scripts/retrieve_eia_demand.py new file mode 100644 index 000000000..649fe8dce --- /dev/null +++ b/workflow/scripts/retrieve_eia_demand.py @@ -0,0 +1,142 @@ +"""Retrieves data from the EIA API.""" + +import pandas as pd +from _helpers import configure_logging +from eia import EnergyDemand, TransportationDemand + +# for projections from AEO +YEARS = range(2017, 2051) + + +def get_energy_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: + """ + Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL + level. + + | | residential | commercial | industrial | transport | units | + |----- |-------------|-------------|-------------|------------|-------| + | 2018 | ### | ### | ### | ### | ### | + | 2019 | ### | ### | ### | ### | ### | + | 2020 | ### | ### | ### | ### | ### | + | ... | | | | | | + | 2049 | ### | ### | ### | ### | ### | + | 2050 | ### | ### | ### | ### | ### | + """ + + def get_sector_data(years: list[int], sector: str) -> pd.DataFrame: + """Function to piece togehter historical and projected values.""" + start_year = min(years) + end_year = max(years) + + data = [] + + if start_year < 2024: + data.append( + EnergyDemand(sector=sector, year=start_year, api=api).get_data(), + ) + if end_year >= 2024: + data.append( + EnergyDemand(sector=sector, year=end_year, api=api, scenario=scenario).get_data(), + ) + return pd.concat(data) + + sectors = ("residential", "commercial", "industry", "transport") + + df = pd.DataFrame( + index=years, + ) + + for sector in sectors: + sector_data = get_sector_data(years, sector).sort_index() + df[sector] = sector_data.value + + df["units"] = "quads" + return df + + +def get_transport_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: + """ + Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL + level. + + | | light_duty | med_duty | heavy_duty | bus | units | + |----- |------------|-----------|-------------|------|-------| + | 2018 | ### | ### | ### | ### | ### | + | 2019 | ### | ### | ### | ### | ### | + | 2020 | ### | ### | ### | ### | ### | + | ... | | | | | | + | 2049 | ### | ### | ### | ### | ### | + | 2050 | ### | ### | ### | ### | ### | + """ + + def get_historical_value(year: int, sector: str) -> float: + """Returns single year value at a time.""" + return TransportationDemand(vehicle=sector, year=year, api=api).get_data(pivot=True).values[0][0] + + def get_future_values( + year: int, + sector: str, + scenario: str, + ) -> pd.DataFrame: + """Returns all values from 2024 onwards.""" + return TransportationDemand( + vehicle=sector, + year=year, + api=api, + scenario=scenario, + ).get_data() + + vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") + + df = pd.DataFrame( + columns=["light_duty", "med_duty", "heavy_duty", "bus"], + index=years, + ) + + for year in sorted(YEARS): + if year < 2024: + for vehicle in vehicles: + df.at[year, vehicle] = get_historical_value( + year, + vehicle, + ) + + for vehicle in vehicles: + aeo = get_future_values(max(YEARS), vehicle, scenario) + for year in YEARS: + if year < 2024: + continue + df.at[year, vehicle] = aeo.at[year, "value"] + + df["units"] = "thousand VMT" + return df + + +if __name__ == "__main__": + if "snakemake" not in globals(): + from _helpers import mock_snakemake + + snakemake = mock_snakemake( + "retrieve_eia_demand", + scenario="reference", + source="transport", + ) + configure_logging(snakemake) + + source = snakemake.wildcards.source + scenario = snakemake.wildcards.scenario + api = snakemake.params.api + save = snakemake.output + + if source == "energy": + df = get_energy_demand(api, YEARS, scenario) + elif source == "transport": + df = get_transport_demand(api, YEARS, scenario) + elif source == "electricity": + raise NotImplementedError + else: + raise ValueError + + df = df.reset_index(names="YEAR") + + df.to_csv(save, index=False) From 1f2fb7f75244e4e61bd732e0c4dce61203f82a25 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 17 Feb 2025 19:46:28 -0800 Subject: [PATCH 74/75] paths fixed --- workflow/rules/build_electricity.smk | 18 ++++++++++++++---- workflow/scripts/build_demand.py | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index f052ea7f2..45f3447c3 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -302,10 +302,20 @@ def demand_scaling_data(wildcards): "efs_speed" ].capitalize() return DATA + f"nrel_efs/EFSLoadProfile_{efs_case}_{efs_speed}.csv" - elif profile == "eia": - return DATA + "pudl/pudl.sqlite" elif profile == "ferc": return DATA + "pudl/pudl.sqlite" + elif profile == "eia": + if end_use == "power": + return DATA + "pudl/pudl.sqlite" + elif config["api"].get("cache", True): + aeo_scenario = config["electricity"]["demand"].get("eia", "reference") + if end_use == "transport": + # non-road transport will get this as well, but doesnt really matter + return DATA + f"eia/demand/{aeo_scenario}/transport.csv" + else: + return DATA + f"eia/demand/{aeo_scenario}/energy.csv" + else: + return "" # use eia api else: return "" @@ -362,8 +372,8 @@ rule build_sector_demand: benchmark: BENCHMARKS + "{interconnect}/demand/{end_use}_build_demand" threads: 2 - resources: - mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, + # resources: + # mem_mb=lambda wildcards, input, attempt: (input.size // 70000) * attempt * 2, script: "../scripts/build_demand.py" diff --git a/workflow/scripts/build_demand.py b/workflow/scripts/build_demand.py index ca7342c5c..d4c47ccdb 100644 --- a/workflow/scripts/build_demand.py +++ b/workflow/scripts/build_demand.py @@ -1968,7 +1968,7 @@ def get_demand_params( if not vehicle: # road transport demand_profile = "transport_efs_aeo" demand_disaggregation = "pop" - scaling_method = "aeo_vmt" + scaling_method = "aeo_energy" elif vehicle.startswith(("air", "rail", "boat")): demand_profile = "transport_aeo" demand_disaggregation = "pop" From 460bbde1adebee58f90c082dde4bfcf9cd0ce206 Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Mon, 17 Feb 2025 21:08:58 -0800 Subject: [PATCH 75/75] updates to demand --- workflow/rules/build_electricity.smk | 3 + workflow/scripts/build_demand.py | 39 +++-- workflow/scripts/demand_scalers.py | 8 +- workflow/scripts/retrieve_eia_demand.py | 218 ++++++++++++------------ 4 files changed, 138 insertions(+), 130 deletions(-) diff --git a/workflow/rules/build_electricity.smk b/workflow/rules/build_electricity.smk index 45f3447c3..e20e12a9b 100644 --- a/workflow/rules/build_electricity.smk +++ b/workflow/rules/build_electricity.smk @@ -354,6 +354,7 @@ rule build_sector_demand: profile_year=pd.to_datetime(config["snapshots"]["start"]).year, eia_api=config_provider("api", "eia"), snapshots=config_provider("snapshots"), + cache_eia=config_provider("api", "cache"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, @@ -386,6 +387,7 @@ rule build_transport_road_demand: profile_year=pd.to_datetime(config["snapshots"]["start"]).year, eia_api=config_provider("api", "eia"), snapshots=config_provider("snapshots"), + cache_eia=config_provider("api", "cache"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, @@ -422,6 +424,7 @@ rule build_transport_other_demand: planning_horizons=config_provider("scenario", "planning_horizons"), eia_api=config_provider("api", "eia"), snapshots=config_provider("snapshots"), + cache_eia=config_provider("api", "cache"), input: network=RESOURCES + "{interconnect}/elec_base_network.nc", demand_files=demand_raw_data, diff --git a/workflow/scripts/build_demand.py b/workflow/scripts/build_demand.py index d4c47ccdb..f482ca317 100644 --- a/workflow/scripts/build_demand.py +++ b/workflow/scripts/build_demand.py @@ -1354,18 +1354,16 @@ def zone(self): # noqa: D102 @staticmethod def _assign_vehicle_type(vehicle: str) -> str: """Coordinates vehicle names.""" - match vehicle: - case v if v.startswith(("Air", "air")): - return "air" - case "Domestic Shipping": - return "boat_shipping" - case "Rail": - return "rail_shipping" - case "Passenger Rail": - return "rail_passenger" - case _: - # logger.warning(f"Can not match {v}") - return v + if vehicle.startswith(("Air", "air")): + return "air" + elif vehicle == "Domestic Shipping": + return "boat_shipping" + elif vehicle == "Rail": + return "rail_shipping" + elif vehicle == "Passenger Rail": + return "rail_passenger" + else: + return vehicle def _read_data(self) -> pd.DataFrame: """ @@ -1956,23 +1954,26 @@ def get_demand_params( f"No scaling method available for {demand_profile} profile. Setting to 'aeo_electricity'", ) elif end_use in ("residential", "commercial"): + cache_eia = kwargs.get("cache_eia", False) demand_profile = "eulp" demand_disaggregation = "pop" - scaling_method = "aeo_energy" + scaling_method = "aeo_energy" if cache_eia else "aeo_energy_api" elif end_use == "industry": + cache_eia = kwargs.get("cache_eia", False) demand_profile = "cliu" demand_disaggregation = "cliu" - scaling_method = "aeo_energy" + scaling_method = "aeo_energy" if cache_eia else "aeo_energy_api" elif end_use == "transport": + cache_eia = kwargs.get("cache_eia", False) vehicle = kwargs.get("vehicle", None) if not vehicle: # road transport demand_profile = "transport_efs_aeo" demand_disaggregation = "pop" - scaling_method = "aeo_energy" + scaling_method = "aeo_energy" if cache_eia else "aeo_vmt_api" elif vehicle.startswith(("air", "rail", "boat")): demand_profile = "transport_aeo" demand_disaggregation = "pop" - scaling_method = None # will extract data for any year + scaling_method = "aeo_energy" if cache_eia else "aeo_vmt_api" else: raise NotImplementedError else: @@ -2039,6 +2040,11 @@ def _get_closest_efs_year(efs_years, investment_period): end_use = snakemake.wildcards.end_use eia_api = snakemake.params.eia_api + try: + cache_eia = snakemake.params.cache_eia + except KeyError: + cache_eia = None + vehicle = snakemake.wildcards.get("vehicle", None) planning_horizons = n.investment_periods.to_list() @@ -2048,6 +2054,7 @@ def _get_closest_efs_year(efs_years, investment_period): end_use, demand_params, vehicle=vehicle, + cache_eia=cache_eia, ) # set reading and writitng strategies diff --git a/workflow/scripts/demand_scalers.py b/workflow/scripts/demand_scalers.py index 80bc85c22..6a5089198 100644 --- a/workflow/scripts/demand_scalers.py +++ b/workflow/scripts/demand_scalers.py @@ -185,6 +185,11 @@ def get_projections(self) -> pd.DataFrame: return df +""" +Only used if caching is turned on. +""" + + class AeoScaler(DemandScaler): """Scales according to AEO data previously extracted.""" @@ -198,8 +203,7 @@ def get_projections(self) -> pd.DataFrame: """ -Deprecated in favour of processing data beforehand to avoid connection errors -keep until next release for testing purposes +Also used for caching purposes! """ diff --git a/workflow/scripts/retrieve_eia_demand.py b/workflow/scripts/retrieve_eia_demand.py index 649fe8dce..934f5afdb 100644 --- a/workflow/scripts/retrieve_eia_demand.py +++ b/workflow/scripts/retrieve_eia_demand.py @@ -1,116 +1,110 @@ """Retrieves data from the EIA API.""" -import pandas as pd from _helpers import configure_logging -from eia import EnergyDemand, TransportationDemand - -# for projections from AEO -YEARS = range(2017, 2051) - - -def get_energy_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: - """ - Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL - level. - - | | residential | commercial | industrial | transport | units | - |----- |-------------|-------------|-------------|------------|-------| - | 2018 | ### | ### | ### | ### | ### | - | 2019 | ### | ### | ### | ### | ### | - | 2020 | ### | ### | ### | ### | ### | - | ... | | | | | | - | 2049 | ### | ### | ### | ### | ### | - | 2050 | ### | ### | ### | ### | ### | - """ - - def get_sector_data(years: list[int], sector: str) -> pd.DataFrame: - """Function to piece togehter historical and projected values.""" - start_year = min(years) - end_year = max(years) - - data = [] - - if start_year < 2024: - data.append( - EnergyDemand(sector=sector, year=start_year, api=api).get_data(), - ) - if end_year >= 2024: - data.append( - EnergyDemand(sector=sector, year=end_year, api=api, scenario=scenario).get_data(), - ) - return pd.concat(data) - - sectors = ("residential", "commercial", "industry", "transport") - - df = pd.DataFrame( - index=years, - ) - - for sector in sectors: - sector_data = get_sector_data(years, sector).sort_index() - df[sector] = sector_data.value - - df["units"] = "quads" - return df - - -def get_transport_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: - """ - Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL - level. - - | | light_duty | med_duty | heavy_duty | bus | units | - |----- |------------|-----------|-------------|------|-------| - | 2018 | ### | ### | ### | ### | ### | - | 2019 | ### | ### | ### | ### | ### | - | 2020 | ### | ### | ### | ### | ### | - | ... | | | | | | - | 2049 | ### | ### | ### | ### | ### | - | 2050 | ### | ### | ### | ### | ### | - """ - - def get_historical_value(year: int, sector: str) -> float: - """Returns single year value at a time.""" - return TransportationDemand(vehicle=sector, year=year, api=api).get_data(pivot=True).values[0][0] - - def get_future_values( - year: int, - sector: str, - scenario: str, - ) -> pd.DataFrame: - """Returns all values from 2024 onwards.""" - return TransportationDemand( - vehicle=sector, - year=year, - api=api, - scenario=scenario, - ).get_data() - - vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") - - df = pd.DataFrame( - columns=["light_duty", "med_duty", "heavy_duty", "bus"], - index=years, - ) - - for year in sorted(YEARS): - if year < 2024: - for vehicle in vehicles: - df.at[year, vehicle] = get_historical_value( - year, - vehicle, - ) - - for vehicle in vehicles: - aeo = get_future_values(max(YEARS), vehicle, scenario) - for year in YEARS: - if year < 2024: - continue - df.at[year, vehicle] = aeo.at[year, "value"] - - df["units"] = "thousand VMT" - return df - +from demand_scalers import AeoEnergyScalerApi, AeoVmtScalerApi + +# def get_energy_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: +# """ +# Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL +# level. + +# | | residential | commercial | industrial | transport | units | +# |----- |-------------|-------------|-------------|------------|-------| +# | 2018 | ### | ### | ### | ### | ### | +# | 2019 | ### | ### | ### | ### | ### | +# | 2020 | ### | ### | ### | ### | ### | +# | ... | | | | | | +# | 2049 | ### | ### | ### | ### | ### | +# | 2050 | ### | ### | ### | ### | ### | +# """ + +# def get_sector_data(years: list[int], sector: str) -> pd.DataFrame: +# """Function to piece togehter historical and projected values.""" +# start_year = min(years) +# end_year = max(years) + +# data = [] + +# if start_year < 2024: +# data.append( +# EnergyDemand(sector=sector, year=start_year, api=api).get_data(), +# ) +# if end_year >= 2024: +# data.append( +# EnergyDemand(sector=sector, year=end_year, api=api, scenario=scenario).get_data(), +# ) +# return pd.concat(data) + +# sectors = ("residential", "commercial", "industry", "transport") + +# df = pd.DataFrame( +# index=years, +# ) + +# for sector in sectors: +# sector_data = get_sector_data(years, sector).sort_index() +# df[sector] = sector_data.value + +# df["units"] = "quads" +# return df + + +# def get_transport_demand(api: str, years: int, scenario: str = "reference") -> pd.DataFrame: +# """ +# Get sector yearly END-USE ENERGY growth rates from AEO at a NATIONAL +# level. + +# | | light_duty | med_duty | heavy_duty | bus | units | +# |----- |------------|-----------|-------------|------|-------| +# | 2018 | ### | ### | ### | ### | ### | +# | 2019 | ### | ### | ### | ### | ### | +# | 2020 | ### | ### | ### | ### | ### | +# | ... | | | | | | +# | 2049 | ### | ### | ### | ### | ### | +# | 2050 | ### | ### | ### | ### | ### | +# """ + +# def get_historical_value(year: int, sector: str) -> float: +# """Returns single year value at a time.""" +# return TransportationDemand(vehicle=sector, year=year, api=api).get_data(pivot=True).values[0][0] + +# def get_future_values( +# year: int, +# sector: str, +# scenario: str, +# ) -> pd.DataFrame: +# """Returns all values from 2024 onwards.""" +# return TransportationDemand( +# vehicle=sector, +# year=year, +# api=api, +# scenario=scenario, +# ).get_data() + +# vehicles = ("light_duty", "med_duty", "heavy_duty", "bus") + +# df = pd.DataFrame( +# columns=["light_duty", "med_duty", "heavy_duty", "bus"], +# index=years, +# ) + +# for year in sorted(YEARS): +# if year < 2024: +# for vehicle in vehicles: +# df.at[year, vehicle] = get_historical_value( +# year, +# vehicle, +# ) + +# for vehicle in vehicles: +# aeo = get_future_values(max(YEARS), vehicle, scenario) +# for year in YEARS: +# if year < 2024: +# continue +# df.at[year, vehicle] = aeo.at[year, "value"] + +# df["units"] = "thousand VMT" +# return df if __name__ == "__main__": if "snakemake" not in globals(): @@ -129,9 +123,9 @@ def get_future_values( save = snakemake.output if source == "energy": - df = get_energy_demand(api, YEARS, scenario) + df = AeoEnergyScalerApi(api, scenario).get_projections() elif source == "transport": - df = get_transport_demand(api, YEARS, scenario) + df = AeoVmtScalerApi(api, scenario).get_projections() elif source == "electricity": raise NotImplementedError else: