Skip to content

Commit ab19951

Browse files
authored
Merge pull request #619 from openego/project/411_LoMa_14aOptimization_EV
Project/411 LoMa 14a optimization ev
2 parents d6c51bc + aa0c112 commit ab19951

5 files changed

Lines changed: 1201 additions & 5 deletions

File tree

edisgo/edisgo.py

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@
3535
from edisgo.io.ding0_import import import_ding0_grid
3636
from edisgo.io.electromobility_import import (
3737
distribute_charging_demand,
38+
distribute_charging_demand_14a, #engine
3839
import_electromobility_from_dir,
3940
import_electromobility_from_oedb,
41+
import_electromobility_from_oedb_14a,
4042
integrate_charging_parks,
43+
integrate_charging_parks_14a,
4144
)
4245
from edisgo.io.heat_pump_import import oedb as import_heat_pumps_oedb
4346
from edisgo.io.storage_import import home_batteries_oedb
@@ -53,7 +56,7 @@
5356
from edisgo.opf.results.opf_result_class import OPFResults
5457
from edisgo.tools import plots, tools
5558
from edisgo.tools.config import Config
56-
from edisgo.tools.geo import find_nearest_bus
59+
from edisgo.tools.geo import find_nearest_bus, find_nearest_bus_14a
5760
from edisgo.tools.spatial_complexity_reduction import spatial_complexity_reduction
5861
from edisgo.tools.tools import determine_grid_integration_voltage_level
5962

@@ -1800,6 +1803,91 @@ def integrate_component_based_on_geolocation(
18001803
)
18011804

18021805
return comp_name
1806+
1807+
def integrate_component_based_on_geolocation_14a(
1808+
self,
1809+
comp_type,
1810+
geolocation,
1811+
voltage_level=None,
1812+
add_ts=True,
1813+
ts_active_power=None,
1814+
ts_reactive_power=None,
1815+
**kwargs,
1816+
):
1817+
"""
1818+
14a variant of integrate_component_based_on_geolocation.
1819+
1820+
Uses explicit _14a helper functions where custom behavior was introduced,
1821+
while leaving the original integrate_component_based_on_geolocation
1822+
unchanged for standard users.
1823+
"""
1824+
supported_voltage_levels = {4, 5, 6, 7}
1825+
p_nom = kwargs.get("p_nom", None)
1826+
p_set = kwargs.get("p_set", None)
1827+
1828+
p = p_nom if p_set is None else p_set
1829+
kwargs["p"] = p
1830+
1831+
if voltage_level not in supported_voltage_levels:
1832+
if p is None:
1833+
raise ValueError(
1834+
"Neither appropriate voltage level nor nominal power were supplied."
1835+
)
1836+
voltage_level = determine_grid_integration_voltage_level(self, p)
1837+
1838+
# convert geolocation to shapely Point if needed
1839+
if type(geolocation) is not Point:
1840+
geolocation = Point(geolocation)
1841+
1842+
kwargs["geom"] = geolocation
1843+
kwargs["voltage_level"] = voltage_level
1844+
1845+
# -------------------------
1846+
# Connect in MV
1847+
# -------------------------
1848+
if voltage_level in [4, 5]:
1849+
comp_name = self.topology.connect_to_mv_14a(self, kwargs, comp_type)
1850+
1851+
# -------------------------
1852+
# Connect in LV
1853+
# -------------------------
1854+
else:
1855+
lv_buses = self.topology.buses_df.drop(self.topology.mv_grid.buses_df.index)
1856+
lv_buses_dropna = lv_buses.dropna(axis=0, subset=["x", "y"])
1857+
1858+
# fallback path for non-georeferenced LV grids
1859+
if len(lv_buses_dropna) < len(lv_buses):
1860+
if kwargs.get("mvlv_subst_id", None) is None:
1861+
substations = self.topology.buses_df.loc[
1862+
self.topology.transformers_df.bus1.unique()
1863+
]
1864+
nearest_substation, _ = find_nearest_bus_14a(geolocation, substations)
1865+
kwargs["mvlv_subst_id"] = int(nearest_substation.split("_")[-2])
1866+
1867+
comp_name = self.topology.connect_to_lv_14a(self, kwargs, comp_type)
1868+
1869+
else:
1870+
max_distance_from_target_bus = kwargs.pop(
1871+
"max_distance_from_target_bus", 0.3 #CHANGED #14a
1872+
)
1873+
comp_name = self.topology.connect_to_lv_based_on_geolocation_14a(
1874+
self, kwargs, comp_type, max_distance_from_target_bus
1875+
)
1876+
1877+
if add_ts:
1878+
if comp_type == "generator":
1879+
self.set_time_series_manual(
1880+
generators_p=pd.DataFrame({comp_name: ts_active_power}),
1881+
generators_q=pd.DataFrame({comp_name: ts_reactive_power}),
1882+
)
1883+
else:
1884+
self.set_time_series_manual(
1885+
loads_p=pd.DataFrame({comp_name: ts_active_power}),
1886+
loads_q=pd.DataFrame({comp_name: ts_reactive_power}),
1887+
)
1888+
1889+
return comp_name
1890+
18031891

18041892
def remove_component(self, comp_type, comp_name, drop_ts=True):
18051893
"""
@@ -2107,6 +2195,58 @@ def import_electromobility(
21072195

21082196
integrate_charging_parks(self)
21092197

2198+
def import_electromobility_14a(
2199+
self,
2200+
data_source: str = "oedb",
2201+
scenario: str = None,
2202+
import_electromobility_data_kwds=None,
2203+
allocate_charging_demand_kwds=None,
2204+
):
2205+
"""
2206+
14a variant of import_electromobility.
2207+
2208+
This method uses the specific 14a EV integration path.
2209+
"""
2210+
if data_source != "oedb":
2211+
raise ValueError(
2212+
"Invalid input for parameter 'data_source'. Currently only 'oedb' is supported."
2213+
)
2214+
2215+
valid_scenarios = {"eGon2035", "eGon100RE"}
2216+
if scenario not in valid_scenarios:
2217+
raise ValueError(
2218+
f"Invalid scenario '{scenario}'. Possible options are {sorted(valid_scenarios)}."
2219+
)
2220+
2221+
if self.engine is None:
2222+
raise ValueError(
2223+
"No database engine available. Please set 'self.engine' before calling "
2224+
"import_electromobility_14a()."
2225+
)
2226+
2227+
if import_electromobility_data_kwds is None:
2228+
import_electromobility_data_kwds = {}
2229+
2230+
if "shapefile_path" not in import_electromobility_data_kwds:
2231+
raise ValueError(
2232+
"For import_electromobility_14a with data_source='oedb', "
2233+
"'shapefile_path' must be provided in import_electromobility_data_kwds."
2234+
)
2235+
2236+
if allocate_charging_demand_kwds is None:
2237+
allocate_charging_demand_kwds = {}
2238+
2239+
import_electromobility_from_oedb_14a(
2240+
self,
2241+
scenario=scenario,
2242+
engine=self.engine,
2243+
**import_electromobility_data_kwds,
2244+
)
2245+
2246+
distribute_charging_demand_14a(self, **allocate_charging_demand_kwds)
2247+
2248+
integrate_charging_parks_14a(self)
2249+
21102250
def apply_charging_strategy(self, strategy="dumb", **kwargs):
21112251
"""
21122252
Applies charging strategy to set EV charging time series at charging parks.

0 commit comments

Comments
 (0)