Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linopy transition #796

Open
wants to merge 63 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
08c4643
Initial update comment
ekatef Jul 18, 2023
28e1168
Align with PyPSA-Eur approach
ekatef Jul 22, 2023
23dcbc4
Merge branch 'main' into dev_linopy
ekatef Nov 17, 2023
4bb5327
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 17, 2023
3dd5078
Merge branch 'pypsa-meets-earth:main' into dev_linopy
ekatef Jan 18, 2024
f2945c4
Merge branch 'main' into dev_linopy
ekatef Feb 15, 2024
7ca9ee5
Fix merge
ekatef Feb 15, 2024
bf4c5ab
Merge branch 'dev_linopy' of https://github.com/ekatef/pypsa-earth in…
ekatef Feb 15, 2024
f11d5da
Merge branch 'main' into dev_linopy
ekatef Feb 22, 2024
4c2d346
Replace aggregategenerators with aggregateoneport
ekatef Feb 25, 2024
97b24e6
Add aggregation strategies as a parameter
ekatef Feb 25, 2024
004a850
Re-define aggregation strategies
ekatef Feb 25, 2024
f19c69c
Update the environment
ekatef Feb 26, 2024
3d72eee
A temporal fix for merge_isolated
ekatef Feb 26, 2024
f94362b
Replace pyomo methods with linopy ones when clustering
ekatef Feb 26, 2024
6b302f8
Update aggregation strategies
ekatef Feb 26, 2024
28877d3
A temporal fix for the solver to keep the environment same
ekatef Feb 29, 2024
060b973
Update solving setup
ekatef Feb 29, 2024
e953d5a
Update solver settings in the config
ekatef Feb 29, 2024
5fd8060
Update environment
ekatef Mar 6, 2024
f86da66
Update a solver for clustering
ekatef Mar 6, 2024
0e7d145
Merge branch 'main' into dev_linopy
ekatef Apr 4, 2024
a2a48f7
Define bus aggregation strategies
ekatef Apr 4, 2024
2be7fe1
Update environment
ekatef Apr 4, 2024
70b6d04
Merge branch 'main' into dev_linopy
ekatef Apr 8, 2024
377455e
Get back merge of isolated nodes
ekatef Apr 16, 2024
a01f188
Add keys to aggregation strategies
ekatef Apr 16, 2024
644c2ea
Update the tutorial config
ekatef Apr 16, 2024
d210ce1
Lift version constraints for pypsa
ekatef Apr 16, 2024
c4016e2
Add Davide's suggestion for calling the solver log
ekatef May 9, 2024
5669f31
Merge branch 'main' into dev_linopy
ekatef May 13, 2024
5991ca7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 13, 2024
32674db
Remove unprecified columns from lines component
ekatef May 13, 2024
87ff6dd
Merge branch 'dev_linopy' of https://github.com/ekatef/pypsa-earth in…
ekatef May 13, 2024
7026712
Fix typo aggregation_strategies
davide-f May 16, 2024
c444005
Remove get_aggregation_strategies
davide-f May 16, 2024
a7db9bb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 16, 2024
ac4e5a5
Merge branch 'main' of https://github.com/pypsa-meets-earth/pypsa-ear…
ekatef May 18, 2024
2074e18
Update the default config
ekatef Jun 5, 2024
5998973
Merge remote-tracking branch 'origin/main' into dev_linopy
ekatef Jun 5, 2024
f932c6a
Remove debug parts
ekatef Jun 6, 2024
be6905d
Remove outdated comments
ekatef Jun 14, 2024
fef6951
Update implementation of the constraints
ekatef Jun 14, 2024
a6ee0a5
Maintain consistency in naming
ekatef Jun 14, 2024
577464c
Update doc-strings
ekatef Jun 14, 2024
417bf62
Update equity constraint
ekatef Jun 14, 2024
6b060d0
Fix typos
ekatef Jun 24, 2024
cfb251f
Merge branch 'main' into dev_linopy
ekatef Jun 27, 2024
26849ab
Remove hard-coding for renewable technologies
ekatef Jun 29, 2024
60faf63
Revise naming
ekatef Jun 29, 2024
f54d1f6
Move sign under the common code chunks
ekatef Jun 29, 2024
cbea30e
Add a missed efficiency
ekatef Jun 29, 2024
6a7e770
Select expressions
ekatef Jun 29, 2024
e7bd8bb
Put repetitions into a function
ekatef Jun 29, 2024
0e50477
Add a supplementary variable
ekatef Jun 29, 2024
d014acc
Replace get_var in add_res_constraints
ekatef Jun 29, 2024
56ce52e
Replace a constraint definition for add_res_constraints
ekatef Jun 29, 2024
c1155c3
Fix typo
ekatef Jun 29, 2024
a3cbd04
Add initialization of the model
ekatef Jul 1, 2024
671dbb5
Add a TODO
ekatef Jul 1, 2024
78ca7d0
Update linear expressions
ekatef Jul 1, 2024
564fd05
Update constraints
ekatef Jul 2, 2024
0ea2125
Add TODO
ekatef Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ rule add_electricity:

rule simplify_network:
params:
aggregation_strategies=config["cluster_options"]["aggregation_strategies"],
renewable=config["renewable"],
geo_crs=config["crs"]["geo_crs"],
cluster_options=config["cluster_options"],
Expand Down
68 changes: 57 additions & 11 deletions config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,6 @@ monte_carlo:
type: beta
args: [0.5, 2]



solving:
options:
formulation: kirchhoff
Expand All @@ -411,15 +409,63 @@ solving:
#nhours: 10
solver:
name: gurobi
threads: 4
method: 2 # barrier (=ipm)
crossover: 0
BarConvTol: 1.e-5
FeasibilityTol: 1.e-6
AggFill: 0
PreDual: 0
GURO_PAR_BARDENSETHRESH: 200

options: gurobi-default

solver_options:
highs-default:
# refer to https://ergo-code.github.io/HiGHS/options/definitions.html#solver
threads: 4
solver: "ipm"
run_crossover: "off"
small_matrix_value: 1e-6
large_matrix_value: 1e9
primal_feasibility_tolerance: 1e-5
dual_feasibility_tolerance: 1e-5
ipm_optimality_tolerance: 1e-4
parallel: "on"
random_seed: 123
gurobi-default:
threads: 4
method: 2 # barrier
crossover: 0
BarConvTol: 1.e-6
Seed: 123
AggFill: 0
PreDual: 0
GURO_PAR_BARDENSETHRESH: 200
gurobi-numeric-focus:
name: gurobi
NumericFocus: 3 # Favour numeric stability over speed
method: 2 # barrier
crossover: 0 # do not use crossover
BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge
BarConvTol: 1.e-5
FeasibilityTol: 1.e-4
OptimalityTol: 1.e-4
ObjScale: -0.5
threads: 8
Seed: 123
gurobi-fallback: # Use gurobi defaults
name: gurobi
crossover: 0
method: 2 # barrier
BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge
BarConvTol: 1.e-5
FeasibilityTol: 1.e-5
OptimalityTol: 1.e-5
Seed: 123
threads: 8
cplex-default:
threads: 4
lpmethod: 4 # barrier
solutiontype: 2 # non basic solution, ie no crossover
barrier.convergetol: 1.e-4 #5
feasopt.tolerance: 1.e-5 #6
cbc-default: {} # Used in CI
glpk-default: {} # Used in CI

mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2
walltime: "12:00:00"

plotting:
map:
Expand Down
57 changes: 57 additions & 0 deletions config.tutorial.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,63 @@ solving:
#nhours: 10
solver:
name: glpk
options: glpk-default

solver_options:
highs-default:
# refer to https://ergo-code.github.io/HiGHS/options/definitions.html#solver
threads: 4
solver: "ipm"
run_crossover: "off"
small_matrix_value: 1e-6
large_matrix_value: 1e9
primal_feasibility_tolerance: 1e-5
dual_feasibility_tolerance: 1e-5
ipm_optimality_tolerance: 1e-4
parallel: "on"
random_seed: 123
gurobi-default:
threads: 4
method: 2 # barrier
crossover: 0
BarConvTol: 1.e-6
Seed: 123
AggFill: 0
PreDual: 0
GURO_PAR_BARDENSETHRESH: 200
gurobi-numeric-focus:
name: gurobi
NumericFocus: 3 # Favour numeric stability over speed
method: 2 # barrier
crossover: 0 # do not use crossover
BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge
BarConvTol: 1.e-5
FeasibilityTol: 1.e-4
OptimalityTol: 1.e-4
ObjScale: -0.5
threads: 8
Seed: 123
gurobi-fallback: # Use gurobi defaults
name: gurobi
crossover: 0
method: 2 # barrier
BarHomogeneous: 1 # Use homogeneous barrier if standard does not converge
BarConvTol: 1.e-5
FeasibilityTol: 1.e-5
OptimalityTol: 1.e-5
Seed: 123
threads: 8
cplex-default:
threads: 4
lpmethod: 4 # barrier
solutiontype: 2 # non basic solution, ie no crossover
barrier.convergetol: 1.e-4 #5
feasopt.tolerance: 1.e-5 #6
cbc-default: {} # Used in CI
glpk-default: {} # Used in CI

mem: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2
walltime: "12:00:00"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In pypsa-eur the option is named "runtime" and has a slightly different naming option, e.g. 12h, though it may be compatible.
Do you think we can adopt the same naming and writing convention?



plotting:
Expand Down
5 changes: 3 additions & 2 deletions envs/environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies:
- pip
- mamba # esp for windows build

- pypsa>=0.24, <0.25
- pypsa
# - atlite>=0.2.4 # until https://github.com/PyPSA/atlite/issues/244 is not merged
- dask
- powerplantmatching>=0.5.7
Expand All @@ -27,6 +27,7 @@ dependencies:
- memory_profiler
- ruamel.yaml<=0.17.26
- pytables
- pyscipopt # added to compy with the quadratic objective requirement of the clustering script
- lxml
- numpy
- pandas
Expand Down Expand Up @@ -77,7 +78,7 @@ dependencies:

# Default solver for tests (required for CI)
- glpk
- ipopt<3.13.3
- ipopt #<3.13.3
- gurobi

- pip:
Expand Down
25 changes: 0 additions & 25 deletions scripts/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,31 +431,6 @@ def dlProgress(count, blockSize, totalSize, roundto=roundto):
urllib.request.urlretrieve(url, file, reporthook=dlProgress, data=data)


def get_aggregation_strategies(aggregation_strategies):
"""
Default aggregation strategies that cannot be defined in .yaml format must
be specified within the function, otherwise (when defaults are passed in
the function's definition) they get lost when custom values are specified
in the config.
"""
import numpy as np

# to handle the new version of PyPSA.
try:
from pypsa.clustering.spatial import _make_consense
except Exception:
# TODO: remove after new release and update minimum pypsa version
from pypsa.clustering.spatial import _make_consense

bus_strategies = dict(country=_make_consense("Bus", "country"))
bus_strategies.update(aggregation_strategies.get("buses", {}))

generator_strategies = {"build_year": lambda x: 0, "lifetime": lambda x: np.inf}
generator_strategies.update(aggregation_strategies.get("generators", {}))

return bus_strategies, generator_strategies


def mock_snakemake(rulename, **wildcards):
"""
This function is expected to be executed from the "scripts"-directory of "
Expand Down
24 changes: 24 additions & 0 deletions scripts/base_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,18 @@ def base_network(

if hvdc_as_lines_config:
lines = pd.concat([lines_ac, lines_dc])
lines.drop(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, I'd recommend to do the converse:
keep the list of columns that we want and implicitly drop the others.
That should be more consistent. However, do you think it should be placed here or maybe in previous processing script?
If you consider that it is not suited for this PR, we can add an issue instead

labels=[
"bus0_lon",
"bus0_lat",
"bus1_lon",
"bus1_lat",
"bus_0_coors",
"bus_1_coors",
],
axis=1,
inplace=True,
)
n.import_components_from_dataframe(lines, "Line")
else:
lines_dc = _set_electrical_parameters_links(links_config, lines_dc)
Expand All @@ -522,6 +534,18 @@ def base_network(
axis=1,
result_type="reduce",
)
lines_ac.drop(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

labels=[
"bus0_lon",
"bus0_lat",
"bus1_lon",
"bus1_lat",
"bus_0_coors",
"bus_1_coors",
],
axis=1,
inplace=True,
)
n.import_components_from_dataframe(lines_ac, "Line")
# The columns which names starts with "bus" are mixed up with the third-bus specification
# when executing additional_linkports()
Expand Down
73 changes: 28 additions & 45 deletions scripts/cluster_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,14 @@
from functools import reduce

import geopandas as gpd
import linopy
import numpy as np
import pandas as pd
import pyomo.environ as po
import pypsa
from _helpers import (
REGION_COLS,
configure_logging,
create_logger,
get_aggregation_strategies,
sets_path_to_root,
update_p_nom_max,
)
Expand Down Expand Up @@ -336,47 +335,22 @@ def distribute_clusters(
distribution_factor.sum(), 1.0, rtol=1e-3
), f"Country weights L must sum up to 1.0 when distributing clusters. Is {distribution_factor.sum()}."

m = po.ConcreteModel()

def n_bounds(model, *n_id):
"""
Create a function that makes a bound pair for pyomo.

Use n_bounds(model, n_id) if N is Single-Index
Use n_bounds(model, *n_id) if N is Multi-Index
Example: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Variables.html

Returns
-------
bounds = A function (or Python object) that gives a (lower,upper) bound pair i.e.(1,10) for the variable
"""
return (1, N[n_id])

m.n = po.Var(list(distribution_factor.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] - distribution_factor.loc[i] * n_clusters) ** 2
for i in distribution_factor.index
),
sense=po.minimize,
m = linopy.Model()
clusters = m.add_variables(
lower=1, upper=N, coords=[L.index], name="n", integer=True
)

opt = po.SolverFactory(solver_name)
if not opt.has_capability("quadratic_objective"):
logger.warning(
f"The configured solver `{solver_name}` does not support quadratic objectives. Falling back to `ipopt`."
m.add_constraints(clusters.sum() == n_clusters, name="tot")
# leave out constant in objective (L * n_clusters) ** 2
m.objective = (clusters * clusters - 2 * clusters * L * n_clusters).sum()
if solver_name == "gurobi":
logging.getLogger("gurobipy").propagate = False
elif solver_name != "scip":
logger.info(
f"The configured solver `{solver_name}` does not support quadratic objectives. Falling back to `scip`."
)
opt = po.SolverFactory("ipopt")

results = opt.solve(m)
assert (
results["Solver"][0]["Status"] == "ok"
), f"Solver returned non-optimally: {results}"

return (
pd.Series(m.n.get_values(), index=distribution_factor.index).round().astype(int)
)
solver_name = "scip"
m.solve(solver_name=solver_name)
return m.solution["n"].to_series().astype(int)


def busmap_for_gadm_clusters(inputs, n, gadm_level, geo_crs, country_list):
Expand Down Expand Up @@ -577,9 +551,10 @@ def clustering_for_n_clusters(
extended_link_costs=0,
focus_weights=None,
):
bus_strategies, generator_strategies = get_aggregation_strategies(
aggregation_strategies
)
line_strategies = aggregation_strategies.get("lines", dict())
line_strategies.update({"geometry": "first", "bounds": "first"})
generator_strategies = aggregation_strategies.get("generators", dict())
one_port_strategies = aggregation_strategies.get("one_ports", dict())

if not isinstance(custom_busmap, pd.Series):
if alternative_clustering:
Expand All @@ -605,12 +580,20 @@ def clustering_for_n_clusters(
clustering = get_clustering_from_busmap(
n,
busmap,
bus_strategies=bus_strategies,
aggregate_generators_weighted=True,
aggregate_generators_carriers=aggregate_carriers,
aggregate_one_ports=["Load", "StorageUnit"],
line_length_factor=line_length_factor,
line_strategies=line_strategies,
generator_strategies=generator_strategies,
bus_strategies={
"lat": "mean",
"lon": "mean",
"tag_substation": "first",
"tag_area": "first",
"country": "first",
},
Comment on lines +589 to +595
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit widely duplicated across scripts. v_nom is also not here, may this be somewhat linked to the issue by Emmanuel?

one_port_strategies=one_port_strategies,
scale_link_capital_costs=False,
)

Expand Down
Loading
Loading