pypsa-eur/scripts/solve_network.py

568 lines
19 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2023-02-16 10:50:55 +00:00
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
#
2021-09-14 14:37:41 +00:00
# SPDX-License-Identifier: MIT
"""
Solves linear optimal power flow for a network iteratively while updating
reactances.
2019-08-11 09:40:47 +00:00
Relevant Settings
-----------------
2019-08-11 11:17:36 +00:00
.. code:: yaml
solving:
tmpdir:
options:
formulation:
clip_p_max_pu:
load_shedding:
noisy_costs:
nhours:
min_iterations:
max_iterations:
skip_iterations:
track_iterations:
2019-08-11 11:17:36 +00:00
solver:
name:
2019-11-14 16:50:24 +00:00
.. seealso::
Documentation of the configuration file ``config.yaml`` at
:ref:`electricity_cf`, :ref:`solving_cf`, :ref:`plotting_cf`
2019-08-11 09:40:47 +00:00
Inputs
------
- ``networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc``: confer :ref:`prepare`
2019-08-11 20:34:18 +00:00
2019-08-11 09:40:47 +00:00
Outputs
-------
- ``results/networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc``: Solved PyPSA network including optimisation results
2019-08-11 20:34:18 +00:00
2023-03-09 12:28:42 +00:00
.. image:: img/results.png
2019-08-14 08:35:41 +00:00
:scale: 40 %
2019-08-11 09:40:47 +00:00
Description
-----------
2019-08-14 09:07:52 +00:00
Total annual system costs are minimised with PyPSA. The full formulation of the
linear optimal power flow (plus investment planning
is provided in the
`documentation of PyPSA <https://pypsa.readthedocs.io/en/latest/optimal_power_flow.html#linear-optimal-power-flow>`_.
The optimization is based on the ``pyomo=False`` setting in the :func:`network.lopf` and :func:`pypsa.linopf.ilopf` function.
Additionally, some extra constraints specified in :mod:`prepare_network` are added.
2019-08-14 09:07:52 +00:00
Solving the network in multiple iterations is motivated through the dependence of transmission line capacities and impedances.
As lines are expanded their electrical parameters change, which renders the optimisation bilinear even if the power flow
equations are linearized.
To retain the computational advantage of continuous linear programming, a sequential linear programming technique
is used, where in between iterations the line impedances are updated.
Details (and errors made through this heuristic) are discussed in the paper
- Fabian Neumann and Tom Brown. `Heuristics for Transmission Expansion Planning in Low-Carbon Energy System Models <https://arxiv.org/abs/1907.10548>`_), *16th International Conference on the European Energy Market*, 2019. `arXiv:1907.10548 <https://arxiv.org/abs/1907.10548>`_.
.. warning::
Capital costs of existing network components are not included in the objective function,
since for the optimisation problem they are just a constant term (no influence on optimal result).
Therefore, these capital costs are not included in ``network.objective``!
If you want to calculate the full total annual system costs add these to the objective value.
2019-08-11 20:34:18 +00:00
.. tip::
The rule :mod:`solve_all_networks` runs
2019-11-14 16:50:24 +00:00
for all ``scenario`` s in the configuration file
the rule :mod:`solve_network`.
"""
import logging
2023-03-09 13:24:25 +00:00
import os
import re
from pathlib import Path
Add logging to logfiles to all snakemake workflow scripts. (#102) * Add logging to logfiles to all snakemake workflow scripts. * Fix missing quotation marks in Snakefile. * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * doc: fix _ec_ filenames in docs * Allow logging message format to be specified in config.yaml. * Add logging for Snakemake rule 'retrieve_databundle '. * Add limited logging to STDERR only for retrieve_*.py scripts. * Import progressbar module only on demand. * Fix logging to file and enable concurrent printing to STDERR for most scripts. * Add new 'logging_format' option to Travis CI test config.yaml. * Add missing parenthesis (bug fix) and cross-os compatible paths. * Fix typos in messages. * Use correct log files for logging (bug fix). * doc: fix line references * config: logging_format in all configs * doc: add doc for logging_format * environment: update to powerplantmatching 0.4.3 * doc: update line references for tutorial.rst * Change logging configuration scheme for config.yaml. * Add helper function for doing basic logging configuration. * Add logpath for prepare_links_p_nom rule. * Outsource basic logging configuration for all scripts to _helper submodule. * Update documentation for changed config.yaml structure. Instead of 'logging_level' and 'logging_format', now 'logging' with subcategories is used. * _helpers: Change configure_logging signature.
2019-11-28 07:22:52 +00:00
import numpy as np
import pandas as pd
import pypsa
from _helpers import configure_logging
2023-03-09 13:24:25 +00:00
from linopy import merge
from pypsa.descriptors import get_switchable_as_dense as get_as_dense
2023-03-09 13:24:25 +00:00
from pypsa.optimization.abstract import optimize_transmission_expansion_iteratively
from pypsa.optimization.optimize import optimize
from vresutils.benchmark import memory_logger
2018-02-01 11:42:56 +00:00
logger = logging.getLogger(__name__)
def prepare_network(n, solve_opts):
if "clip_p_max_pu" in solve_opts:
for df in (n.generators_t.p_max_pu, n.storage_units_t.inflow):
df.where(df > solve_opts["clip_p_max_pu"], other=0.0, inplace=True)
load_shedding = solve_opts.get("load_shedding")
if load_shedding:
2022-03-03 22:08:29 +00:00
n.add("Carrier", "load", color="#dd2e23", nice_name="Load shedding")
buses_i = n.buses.query("carrier == 'AC'").index
if not np.isscalar(load_shedding):
load_shedding = 1e2 # Eur/kWh
# intersect between macroeconomic and surveybased
# willingness to pay
# http://journal.frontiersin.org/article/10.3389/fenrg.2015.00055/full)
n.madd(
"Generator",
buses_i,
" load",
bus=buses_i,
carrier="load",
sign=1e-3, # Adjust sign to measure p and p_nom in kW instead of MW
marginal_cost=load_shedding,
p_nom=1e9, # kW
)
if solve_opts.get("noisy_costs"):
for t in n.iterate_components(n.one_port_components):
# if 'capital_cost' in t.df:
# t.df['capital_cost'] += 1e1 + 2.*(np.random.random(len(t.df)) - 0.5)
if "marginal_cost" in t.df:
t.df["marginal_cost"] += 1e-2 + 2e-3 * (
np.random.random(len(t.df)) - 0.5
)
for t in n.iterate_components(["Line", "Link"]):
t.df["capital_cost"] += (
1e-1 + 2e-2 * (np.random.random(len(t.df)) - 0.5)
) * t.df["length"]
if solve_opts.get("nhours"):
nhours = solve_opts["nhours"]
n.set_snapshots(n.snapshots[:nhours])
n.snapshot_weightings[:] = 8760.0 / nhours
return n
def add_CCL_constraints(n, config):
2023-03-09 13:24:25 +00:00
"""
Add CCL (country & carrier limit) constraint to the network.
Add minimum and maximum levels of generator nominal capacity per carrier
for individual countries. Opts and path for agg_p_nom_minmax.csv must be defined
in config.yaml. Default file is available at data/agg_p_nom_minmax.csv.
Parameters
----------
n : pypsa.Network
config : dict
Example
-------
scenario:
opts: [Co2L-CCL-24H]
electricity:
agg_p_nom_limits: data/agg_p_nom_minmax.csv
"""
pypsa_eur_path = os.path.dirname(os.getcwd())
agg_p_nom_limits = os.path.join(
pypsa_eur_path, config["electricity"].get("agg_p_nom_limits")
)
try:
agg_p_nom_minmax = pd.read_csv(agg_p_nom_limits, index_col=list(range(2)))
except IOError:
logger.exception(
"Need to specify the path to a .csv file containing "
2023-03-09 13:24:25 +00:00
"aggregate capacity limits per country. "
"Path specified in config['electricity']['agg_p_nom_limit']. "
f"Currently read path is 'pypsa-eur/{agg_p_nom_limits}'."
)
logger.info(
"Adding per carrier generation capacity constraints for " "individual countries"
)
2023-03-09 13:24:25 +00:00
capacity_variable = n.model["Generator-p_nom"]
lhs = []
ext_carriers = n.generators.query("p_nom_extendable").carrier.unique()
for c in ext_carriers:
ext_carrier = n.generators.query("p_nom_extendable and carrier == @c")
country_grouper = (
ext_carrier.bus.map(n.buses.country)
.rename_axis("Generator-ext")
.rename("country")
)
2023-03-09 13:24:25 +00:00
ext_carrier_per_country = capacity_variable.loc[
country_grouper.index
].groupby_sum(country_grouper)
lhs.append(ext_carrier_per_country)
lhs = merge(lhs, dim=pd.Index(ext_carriers, name="carrier"))
min_matrix = agg_p_nom_minmax["min"].to_xarray().unstack().reindex_like(lhs)
max_matrix = agg_p_nom_minmax["max"].to_xarray().unstack().reindex_like(lhs)
n.model.add_constraints(
lhs >= min_matrix, name="agg_p_nom_min", mask=min_matrix.notnull()
)
n.model.add_constraints(
lhs <= max_matrix, name="agg_p_nom_max", mask=max_matrix.notnull()
)
def add_EQ_constraints(n, o, scaling=1e-1):
2023-03-09 13:24:25 +00:00
"""
Add equality constraints to the network.
Opts must be specified in the config.yaml.
Parameters
----------
n : pypsa.Network
o : str
Example
-------
scenario:
opts: [Co2L-EQ0.7-24H]
Require each country or node to on average produce a minimal share
of its total consumption itself. Example: EQ0.7c demands each country
to produce on average at least 70% of its consumption; EQ0.7 demands
each node to produce on average at least 70% of its consumption.
"""
float_regex = "[0-9]*\.?[0-9]+"
level = float(re.findall(float_regex, o)[0])
if o[-1] == "c":
ggrouper = n.generators.bus.map(n.buses.country)
lgrouper = n.loads.bus.map(n.buses.country)
sgrouper = n.storage_units.bus.map(n.buses.country)
else:
ggrouper = n.generators.bus
lgrouper = n.loads.bus
sgrouper = n.storage_units.bus
load = (
n.snapshot_weightings.generators
@ n.loads_t.p_set.groupby(lgrouper, axis=1).sum()
)
inflow = (
n.snapshot_weightings.stores
@ n.storage_units_t.inflow.groupby(sgrouper, axis=1).sum()
)
inflow = inflow.reindex(load.index).fillna(0.0)
rhs = scaling * (level * load - inflow)
2023-03-09 13:24:25 +00:00
dispatch_variable = n.model["Generator-p"].T
lhs_gen = (
2023-03-09 13:24:25 +00:00
(dispatch_variable * (n.snapshot_weightings.generators * scaling))
.groupby_sum(ggrouper)
.sum("snapshot")
)
if not n.storage_units_t.inflow.empty:
2023-03-09 13:24:25 +00:00
spillage_variable = n.model["StorageUnit-spill"]
lhs_spill = (
2023-03-09 13:24:25 +00:00
(spillage_variable * (-n.snapshot_weightings.stores * scaling))
.groupby_sum(sgrouper)
.sum("snapshot")
)
2023-03-09 13:24:25 +00:00
lhs = merge(lhs_gen, lhs_spill)
else:
lhs = lhs_gen
2023-03-09 13:24:25 +00:00
n.model.add_constraints(lhs >= rhs, name="equity_min")
def add_BAU_constraints(n, config):
2023-03-09 13:24:25 +00:00
"""
Add a per-carrier minimal overall capacity.
BAU_mincapacities and opts must be adjusted in the config.yaml.
Parameters
----------
n : pypsa.Network
config : dict
Example
-------
scenario:
opts: [Co2L-BAU-24H]
electricity:
BAU_mincapacities:
solar: 0
onwind: 0
OCGT: 100000
offwind-ac: 0
offwind-dc: 0
Which sets minimum expansion across all nodes e.g. in Europe to 100GW.
OCGT bus 1 + OCGT bus 2 + ... > 100000
"""
mincaps = pd.Series(config["electricity"]["BAU_mincapacities"])
2023-03-09 13:24:25 +00:00
capacity_variable = n.model["Generator-p_nom"]
ext_i = n.generators.query("p_nom_extendable")
ext_carrier_i = ext_i.carrier.rename_axis("Generator-ext")
lhs = capacity_variable.groupby_sum(ext_carrier_i)
rhs = mincaps[lhs.coords["carrier"].values].rename_axis("carrier")
n.model.add_constraints(lhs >= rhs, name="bau_mincaps")
def add_SAFE_constraints(n, config):
2023-03-09 13:24:25 +00:00
"""
Add a capacity reserve margin of a certain fraction above the peak demand.
Renewable generators and storage do not contribute. Ignores network.
Parameters
----------
n : pypsa.Network
config : dict
Example
-------
config.yaml requires to specify opts:
scenario:
opts: [Co2L-SAFE-24H]
electricity:
SAFE_reservemargin: 0.1
Which sets a reserve margin of 10% above the peak demand.
"""
peakdemand = n.loads_t.p_set.sum(axis=1).max()
margin = 1.0 + config["electricity"]["SAFE_reservemargin"]
reserve_margin = peakdemand * margin
conv_techs = config["plotting"]["conv_techs"]
2023-03-09 13:24:25 +00:00
ext_gens_i = n.generators.query("carrier in @conv_techs & p_nom_extendable").index
capacity_variable = n.model["Generator-p_nom"]
ext_cap_var = capacity_variable.sel({"Generator-ext": ext_gens_i})
lhs = ext_cap_var.sum()
exist_conv_caps = n.generators.query(
"~p_nom_extendable & carrier in @conv_techs"
).p_nom.sum()
2023-03-09 13:24:25 +00:00
rhs = reserve_margin - exist_conv_caps
n.model.add_constraints(lhs >= rhs, name="safe_mintotalcap")
2023-03-09 13:24:25 +00:00
def add_operational_reserve_margin_constraint(n, sns, config):
"""
Define minimum operational reserve margin for a given snapshot.
Parameters
----------
n : pypsa.Network
config : dict
Example:
--------
config.yaml requires to specify operational_reserve:
operational_reserve: # like https://genxproject.github.io/GenX/dev/core/#Reserves
activate: true
epsilon_load: 0.02 # percentage of load at each snapshot
epsilon_vres: 0.02 # percentage of VRES at each snapshot
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"]
# Reserve Variables
2023-03-09 13:24:25 +00:00
n.model.add_variables(
0, np.inf, coords=[sns, n.generators.index], name="Generator-r"
)
reserve = n.model["Generator-r"]
lhs = reserve.sum("Generator")
# Share of extendable renewable capacities
ext_i = n.generators.query("p_nom_extendable").index
vres_i = n.generators_t.p_max_pu.columns
if not ext_i.empty and not vres_i.empty:
capacity_factor = n.generators_t.p_max_pu[vres_i.intersection(ext_i)]
2023-03-09 13:24:25 +00:00
renewable_capacity_variables = (
n.model["Generator-p_nom"]
.sel({"Generator-ext": vres_i.intersection(ext_i)})
.rename({"Generator-ext": "Generator"})
)
lhs = merge(
lhs,
(renewable_capacity_variables * (-EPSILON_VRES * capacity_factor)).sum(
["Generator"]
),
)
# Total demand per t
demand = n.loads_t.p_set.sum(1)
# VRES potential of non extendable generators
capacity_factor = n.generators_t.p_max_pu[vres_i.difference(ext_i)]
renewable_capacity = n.generators.p_nom[vres_i.difference(ext_i)]
potential = (capacity_factor * renewable_capacity).sum(1)
# Right-hand-side
rhs = EPSILON_LOAD * demand + EPSILON_VRES * potential + CONTINGENCY
2023-03-09 13:24:25 +00:00
n.model.add_constraints(lhs >= rhs, name="reserve_margin")
def update_capacity_constraint(n):
2023-03-09 13:24:25 +00:00
"""
Update the capacity constraint to include the new capacity variables.
Parameters
----------
n : pypsa.Network
"""
gen_i = n.generators.index
ext_i = n.generators.query("p_nom_extendable").index
fix_i = n.generators.query("not p_nom_extendable").index
2023-03-09 13:24:25 +00:00
dispatch = n.model["Generator-p"]
reserve = n.model["Generator-r"]
p_max_pu = get_as_dense(n, "Generator", "p_max_pu")
2023-03-09 13:24:25 +00:00
capacity_fixed = n.generators.p_nom[fix_i]
2023-03-09 13:24:25 +00:00
lhs = merge(
dispatch * 1,
reserve * 1,
)
if not ext_i.empty:
2023-03-09 13:24:25 +00:00
capacity_variable = n.model["Generator-p_nom"]
lhs = merge(
lhs,
capacity_variable.rename({"Generator-ext": "Generator"}) * -p_max_pu[ext_i],
)
2023-03-09 13:24:25 +00:00
rhs = (p_max_pu[fix_i] * capacity_fixed).reindex(columns=gen_i)
n.model.add_constraints(
lhs <= rhs, name="gen_updated_capacity_constraint", mask=rhs.notnull()
)
def add_operational_reserve_margin(n, sns, config):
2022-06-23 14:04:49 +00:00
"""
Build reserve margin constraints based on the formulation given in
2022-06-23 14:04:49 +00:00
https://genxproject.github.io/GenX/dev/core/#Reserves.
2023-03-09 13:24:25 +00:00
Parameters
----------
n : pypsa.Network
sns: pd.DatetimeIndex
config : dict
"""
add_operational_reserve_margin_constraint(n, sns, config)
update_capacity_constraint(n)
def add_battery_constraints(n):
2023-03-09 13:24:25 +00:00
"""
Add constraints to ensure that the ratio between the charger and
discharger.
1 * charger_size - efficiency * discharger_size = 0
"""
nodes = n.buses.index[n.buses.carrier == "battery"]
2023-03-09 13:24:25 +00:00
if nodes.empty:
return
2023-03-09 13:24:25 +00:00
vars_link = n.model["Link-p_nom"]
eff = n.links.loc[nodes + " discharger", "efficiency"]
lhs = merge(
vars_link.sel({"Link-ext": nodes + " charger"}) * 1,
vars_link.sel({"Link-ext": nodes + " discharger"}) * -eff,
)
2023-03-09 13:24:25 +00:00
n.model.add_constraints(lhs == 0, name="link_charger_ratio")
def extra_functionality(n, snapshots):
"""
Collects supplementary constraints which will be passed to
``pypsa.linopf.network_lopf``.
If you want to enforce additional custom constraints, this is a good
location to add them. The arguments ``opts`` and
``snakemake.config`` are expected to be attached to the network.
"""
opts = n.opts
config = n.config
if "BAU" in opts and n.generators.p_nom_extendable.any():
add_BAU_constraints(n, config)
if "SAFE" in opts and n.generators.p_nom_extendable.any():
add_SAFE_constraints(n, config)
if "CCL" in opts and n.generators.p_nom_extendable.any():
add_CCL_constraints(n, config)
reserve = config["electricity"].get("operational_reserve", {})
if reserve.get("activate"):
add_operational_reserve_margin(n, snapshots, config)
for o in opts:
if "EQ" in o:
add_EQ_constraints(n, o)
add_battery_constraints(n)
def solve_network(n, config, opts="", **kwargs):
2023-03-09 13:24:25 +00:00
solver_options = config["solving"]["solver"].copy()
solver_name = solver_options.pop("name")
cf_solving = config["solving"]["options"]
track_iterations = cf_solving.get("track_iterations", False)
min_iterations = cf_solving.get("min_iterations", 4)
max_iterations = cf_solving.get("max_iterations", 6)
2020-02-10 15:47:11 +00:00
# add to network for extra_functionality
n.config = config
n.opts = opts
2020-02-10 15:47:11 +00:00
skip_iterations = cf_solving.get("skip_iterations", False)
if not n.lines.s_nom_extendable.any():
skip_iterations = True
logger.info("No expandable lines found. Skipping iterative solving.")
if skip_iterations:
2023-03-09 13:24:25 +00:00
optimize(
n,
solver_name=solver_name,
solver_options=solver_options,
extra_functionality=extra_functionality,
**kwargs,
)
else:
2023-03-09 13:24:25 +00:00
optimize_transmission_expansion_iteratively(
n,
solver_name=solver_name,
solver_options=solver_options,
track_iterations=track_iterations,
min_iterations=min_iterations,
max_iterations=max_iterations,
2023-03-09 13:24:25 +00:00
extra_functionality=extra_functionality,
**kwargs,
)
2023-03-09 13:24:25 +00:00
return n
if __name__ == "__main__":
if "snakemake" not in globals():
Introduce mocksnakemake which acutally parses Snakefile (#107) * rewrite mocksnakemake for parsing real Snakefile * continue add function to scripts * going through all scripts, setting new mocksnakemake * fix plotting scripts * fix build_country_flh * fix build_country_flh II * adjust config files * fix make_summary for tutorial network * create dir also for output * incorporate suggestions * consistent import of mocksnakemake * consistent import of mocksnakemake II * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/plot_network.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/plot_network.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/retrieve_databundle.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * use pathlib for mocksnakemake * rename mocksnakemake into mock_snakemake * revert change in data * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * remove setting logfile in mock_snakemake, use Path in configure_logging * fix fallback path and base_dir fix return type of make_io_accessable * reformulate mock_snakemake * incorporate suggestion, fix typos * mock_snakemake: apply absolute paths again, add assertion error *.py: make hard coded io path accessable for mock_snakemake * retrieve_natura_raster: use snakemake.output for fn_out * include suggestion * Apply suggestions from code review Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * linting, add return ad end of file * Update scripts/plot_p_nom_max.py Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * Update scripts/plot_p_nom_max.py fixes #112 Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * plot_p_nom_max: small correction * config.tutorial.yaml fix snapshots end * use techs instead of technology * revert try out from previous commit, complete replacing * change clusters -> clusts in plot_p_nom_max due to wildcard constraints of clusters * change clusters -> clusts in plot_p_nom_max due to wildcard constraints of clusters II
2019-12-09 20:29:15 +00:00
from _helpers import mock_snakemake
2023-03-09 13:24:25 +00:00
os.chdir(os.path.dirname(os.path.abspath(__file__)))
snakemake = mock_snakemake(
2023-03-09 13:24:25 +00:00
"solve_network",
simpl="",
clusters="5",
ll="copt",
opts="Co2L-BAU-24H",
)
Add logging to logfiles to all snakemake workflow scripts. (#102) * Add logging to logfiles to all snakemake workflow scripts. * Fix missing quotation marks in Snakefile. * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * doc: fix _ec_ filenames in docs * Allow logging message format to be specified in config.yaml. * Add logging for Snakemake rule 'retrieve_databundle '. * Add limited logging to STDERR only for retrieve_*.py scripts. * Import progressbar module only on demand. * Fix logging to file and enable concurrent printing to STDERR for most scripts. * Add new 'logging_format' option to Travis CI test config.yaml. * Add missing parenthesis (bug fix) and cross-os compatible paths. * Fix typos in messages. * Use correct log files for logging (bug fix). * doc: fix line references * config: logging_format in all configs * doc: add doc for logging_format * environment: update to powerplantmatching 0.4.3 * doc: update line references for tutorial.rst * Change logging configuration scheme for config.yaml. * Add helper function for doing basic logging configuration. * Add logpath for prepare_links_p_nom rule. * Outsource basic logging configuration for all scripts to _helper submodule. * Update documentation for changed config.yaml structure. Instead of 'logging_level' and 'logging_format', now 'logging' with subcategories is used. * _helpers: Change configure_logging signature.
2019-11-28 07:22:52 +00:00
configure_logging(snakemake)
tmpdir = snakemake.config["solving"].get("tmpdir")
2018-02-01 11:42:56 +00:00
if tmpdir is not None:
Path(tmpdir).mkdir(parents=True, exist_ok=True)
opts = snakemake.wildcards.opts.split("-")
solve_opts = snakemake.config["solving"]["options"]
2018-02-01 11:42:56 +00:00
fn = getattr(snakemake.log, "memory", None)
with memory_logger(filename=fn, interval=30.0) as mem:
n = pypsa.Network(snakemake.input[0])
n = prepare_network(n, solve_opts)
n = solve_network(
n,
snakemake.config,
opts,
solver_dir=tmpdir,
solver_logfile=snakemake.log.solver,
)
2022-06-30 06:39:03 +00:00
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
n.export_to_netcdf(snakemake.output[0])
2023-03-09 13:24:25 +00:00
logger.info("Maximum memory usage: {}".format(mem.mem_usage))