NREL-erad 0.1.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- erad/__init__.py +1 -1
- erad/constants.py +20 -68
- erad/cypher_queries/load_data_v1.cypher +212 -0
- erad/data/World_Earthquakes_1960_2016.csv +23410 -0
- erad/db/__init__.py +0 -0
- erad/db/assets/__init__.py +0 -0
- erad/db/assets/critical_infras.py +171 -0
- erad/db/assets/distribution_lines.py +101 -0
- erad/db/credential_model.py +20 -0
- erad/db/disaster_input_model.py +23 -0
- erad/db/inject_earthquake.py +52 -0
- erad/db/inject_flooding.py +53 -0
- erad/db/neo4j_.py +162 -0
- erad/db/utils.py +14 -0
- erad/exceptions.py +68 -0
- erad/metrics/__init__.py +0 -0
- erad/metrics/check_microgrid.py +208 -0
- erad/metrics/metric.py +178 -0
- erad/programs/__init__.py +0 -0
- erad/programs/backup.py +62 -0
- erad/programs/microgrid.py +45 -0
- erad/scenarios/__init__.py +0 -0
- erad/scenarios/abstract_scenario.py +103 -0
- erad/scenarios/common.py +93 -0
- erad/scenarios/earthquake_scenario.py +161 -0
- erad/scenarios/fire_scenario.py +160 -0
- erad/scenarios/flood_scenario.py +494 -0
- erad/scenarios/flows.csv +671 -0
- erad/scenarios/utilities.py +76 -0
- erad/scenarios/wind_scenario.py +89 -0
- erad/utils/__init__.py +0 -0
- erad/utils/ditto_utils.py +252 -0
- erad/utils/hifld_utils.py +147 -0
- erad/utils/opendss_utils.py +357 -0
- erad/utils/overpass.py +76 -0
- erad/utils/util.py +178 -0
- erad/visualization/__init__.py +0 -0
- erad/visualization/plot_graph.py +218 -0
- {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/METADATA +39 -29
- nrel_erad-1.0.0.dist-info/RECORD +42 -0
- {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/WHEEL +1 -2
- {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/licenses/LICENSE.txt +28 -28
- erad/default_fragility_curves/__init__.py +0 -15
- erad/default_fragility_curves/default_fire_boundary_dist.py +0 -94
- erad/default_fragility_curves/default_flood_depth.py +0 -108
- erad/default_fragility_curves/default_flood_velocity.py +0 -101
- erad/default_fragility_curves/default_fragility_curves.py +0 -23
- erad/default_fragility_curves/default_peak_ground_acceleration.py +0 -163
- erad/default_fragility_curves/default_peak_ground_velocity.py +0 -94
- erad/default_fragility_curves/default_wind_speed.py +0 -94
- erad/enums.py +0 -40
- erad/gdm_mapping.py +0 -83
- erad/models/__init__.py +0 -1
- erad/models/asset.py +0 -287
- erad/models/asset_mapping.py +0 -20
- erad/models/fragility_curve.py +0 -116
- erad/models/hazard/__init__.py +0 -5
- erad/models/hazard/base_models.py +0 -12
- erad/models/hazard/common.py +0 -26
- erad/models/hazard/earthquake.py +0 -93
- erad/models/hazard/flood.py +0 -83
- erad/models/hazard/wild_fire.py +0 -121
- erad/models/hazard/wind.py +0 -143
- erad/models/probability.py +0 -73
- erad/probability_builder.py +0 -35
- erad/quantities.py +0 -25
- erad/runner.py +0 -122
- erad/systems/__init__.py +0 -2
- erad/systems/asset_system.py +0 -414
- erad/systems/hazard_system.py +0 -122
- nrel_erad-0.1.0.dist-info/RECORD +0 -35
- nrel_erad-0.1.0.dist-info/top_level.txt +0 -1
@@ -0,0 +1,161 @@
|
|
1
|
+
|
2
|
+
import matplotlib.pyplot as plt
|
3
|
+
from erad.scenarios.utilities import ProbabilityFunctionBuilder, GeoUtilities
|
4
|
+
from erad.constants import EARTHQUAKE_HISTORIC_CSV_PATH, DATA_FOLDER
|
5
|
+
from shapely.geometry import MultiPolygon, Point, LineString
|
6
|
+
from erad.scenarios.abstract_scenario import BaseScenario
|
7
|
+
from erad.scenarios.common import asset_list
|
8
|
+
from datetime import datetime
|
9
|
+
import pandas as pd
|
10
|
+
import numpy as np
|
11
|
+
import math
|
12
|
+
import os
|
13
|
+
|
14
|
+
from erad.scenarios.common import AssetTypes
|
15
|
+
from erad.scenarios.utilities import ProbabilityFunctionBuilder
|
16
|
+
|
17
|
+
class EarthquakeScenario(BaseScenario, GeoUtilities):
|
18
|
+
"""Base class for EarthquakeScenario. Extends BaseScenario and GeoUtilities
|
19
|
+
|
20
|
+
Attributes:
|
21
|
+
origin (Point): Earthquake origin point
|
22
|
+
probability_model (dict): Dictionary mapping asset types to probability funcitons
|
23
|
+
timestamp (datetime): Scenario occurance time
|
24
|
+
kwargs (dict): Additional parameters relevant for a particular scenario type
|
25
|
+
"""
|
26
|
+
fragility_curves = {
|
27
|
+
# Hierarchical seismic vulnerability assessment of power transmission systems: sensitivity analysis of fragility curves and clustering algorithms
|
28
|
+
AssetTypes.substation.name : ProbabilityFunctionBuilder("norm", [0.75, 0.20]),
|
29
|
+
# AssetTypes.solar_panels.name : ProbabilityFunctionBuilder("norm", [85, 10]),
|
30
|
+
# Seismic performance of buried electrical cables: evidence-based repair rates and fragility functions
|
31
|
+
# SEISMIC FRAGILITY OF UNDERGROUND ELECTRICAL CABLES IN THE 2010-11 CANTERBURY (NZ) EARTHQUAKES
|
32
|
+
# AssetTypes.buried_lines.name : ProbabilityFunctionBuilder("uniform", [0, 1]),
|
33
|
+
# Seismic Fragility Analysis of Monopile Offshore Wind Turbines under Different Operational Conditions
|
34
|
+
AssetTypes.wind_turbines.name : ProbabilityFunctionBuilder("norm", [0.62, 0.17]),
|
35
|
+
# AssetTypes.battery_storage.name : ProbabilityFunctionBuilder("norm", [85, 10]),
|
36
|
+
# Multi-hazard typhoon and earthquake collapse fragility models for transmission towers: An active learning reliability approach using gradient boosting classifiers
|
37
|
+
AssetTypes.transmission_poles.name : ProbabilityFunctionBuilder("norm", [1.5, 0.45]),
|
38
|
+
# Seismic performance and fragility analysis of power distribution concrete poles
|
39
|
+
# AssetTypes.distribution_poles.name : ProbabilityFunctionBuilder("norm", [0.73, 0.15]),
|
40
|
+
#Improving the resilience of distribution network in coming across seismic damage using mobile battery energy storage system
|
41
|
+
AssetTypes.distribution_poles.name : ProbabilityFunctionBuilder("norm", [0.6, 0.12]),
|
42
|
+
# https://www.researchgate.net/figure/Fragility-curves-that-show-the-probability-of-electric-power-lines-being-in-each-failure_fig3_339017910
|
43
|
+
AssetTypes.transmission_overhead_lines.name : ProbabilityFunctionBuilder("norm", [0.72, 0.1]),
|
44
|
+
# https://www.fema.gov/sites/default/files/2020-10/fema_hazus_earthquake_technical_manual_4-2.pdf
|
45
|
+
AssetTypes.distribution_overhead_lines.name : ProbabilityFunctionBuilder("lognorm", [0.3, 0.25,0.4]),
|
46
|
+
|
47
|
+
}
|
48
|
+
|
49
|
+
intensity_map = LineString([[1, 2.5], [2, 3.0], [3, 3.5], [4, 4.0], [5, 4.5], [6, 5.0], [7, 5.5], [8, 6.0], [9, 6.5], [10, 7.0], [11, 7.5], [12, 8.0]])
|
50
|
+
def __init__(self, origin : Point , probability_model : dict, timestamp : datetime, **kwargs) -> None:
|
51
|
+
"""Constructor for EarthquakeScenario.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
origin (Point): Earthquake origin point
|
55
|
+
probability_model (dict): Dictionary mapping asset types to probability funcitons
|
56
|
+
timestamp (datetime): Scenario occurance time
|
57
|
+
kwargs (dict): Additional parameters relevant for a particular scenario type
|
58
|
+
"""
|
59
|
+
|
60
|
+
super(EarthquakeScenario, self).__init__(origin, probability_model, timestamp, **kwargs)
|
61
|
+
self.kwargs = kwargs
|
62
|
+
return
|
63
|
+
|
64
|
+
@classmethod
|
65
|
+
def from_dynamic_model(cls, origin : Point , probability_function : dict, timestamp : datetime):
|
66
|
+
raise NotImplementedError("Method needs to be defined in derived classes")
|
67
|
+
|
68
|
+
@classmethod
|
69
|
+
def from_historical_earthquake_by_code(cls, earthquake_code : str, probability_function : dict= None):
|
70
|
+
"""Class method for EarthquakeScenario.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
earthquake_code (str): Code for a historic eqrthquake event
|
74
|
+
probability_function (dict): Dictionary mapping asset types to probability funcitons
|
75
|
+
"""
|
76
|
+
|
77
|
+
data_file = os.path.join(DATA_FOLDER, EARTHQUAKE_HISTORIC_CSV_PATH)
|
78
|
+
assert os.path.exists(data_file), f"The data file {data_file} not found"
|
79
|
+
earthquake_data = pd.read_csv(data_file, index_col=None)
|
80
|
+
earthquake_data['DateTime'] = pd.to_datetime(earthquake_data['Date'] + ' ' + earthquake_data['Time'], format='%m/%d/%Y %H:%M:%S')
|
81
|
+
earthquake_data_filtered = earthquake_data[earthquake_data['ID'] == earthquake_code]
|
82
|
+
|
83
|
+
timestamp = earthquake_data_filtered.DateTime.values[0]
|
84
|
+
kwargs = {
|
85
|
+
"Magnitude": earthquake_data_filtered.Magnitude.values[0],
|
86
|
+
"Depth" : earthquake_data_filtered.Depth.values[0],
|
87
|
+
}
|
88
|
+
long = earthquake_data_filtered.Longitude.values[0]
|
89
|
+
lat = earthquake_data_filtered.Latitude.values[0]
|
90
|
+
origin = Point(long, lat)
|
91
|
+
return cls(origin, probability_function, timestamp, **kwargs)
|
92
|
+
|
93
|
+
@property
|
94
|
+
def centroid(self):
|
95
|
+
"""Method to return the centroid of the affected region."""
|
96
|
+
return self.origin
|
97
|
+
|
98
|
+
def increment_time(self):
|
99
|
+
"""Method to increment simulation time for time evolviong scenarios."""
|
100
|
+
raise NotImplementedError("Method needs to be defined in derived classes")
|
101
|
+
|
102
|
+
def calculate_survival_probability(self, assets : dict, timestamp : datetime) -> dict:
|
103
|
+
"""Method to calculate survival probaility of asset types.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
assets (dict): The dictionary of all assets and their corresponding asset types
|
107
|
+
"""
|
108
|
+
for asset_type, asset_dict in assets.items():
|
109
|
+
# assert asset_type in self.probability_model, f"Survival probability for asset type '{asset_type}' not found in the passed probability_model"
|
110
|
+
if asset_type in self.probability_model:
|
111
|
+
|
112
|
+
probability_function = self.probability_model[asset_type]
|
113
|
+
for asset_name, asset_ppty in asset_dict.items():
|
114
|
+
coords = Point(asset_ppty["coordinates"][::-1])
|
115
|
+
|
116
|
+
epicenter_distance = self.distance_from_centroid(coords)
|
117
|
+
|
118
|
+
depth = self.kwargs["Depth"]
|
119
|
+
magnitude = self.kwargs["Magnitude"]
|
120
|
+
|
121
|
+
# Valutazione speditiva di sicurezza sismica degli edifici esistenti
|
122
|
+
hypocentral_distance = (depth**2 + epicenter_distance**2)**0.5
|
123
|
+
l = LineString([[0, magnitude], [100, magnitude]])
|
124
|
+
p = self.intensity_map.intersection(l)
|
125
|
+
Imcs = p.xy[0][0]
|
126
|
+
intensity = Imcs + 3 - 4.3 * math.log10(hypocentral_distance)
|
127
|
+
intensity = 0 if intensity < 0 else intensity
|
128
|
+
pga = 10**((intensity /3) - 1) / 9.81
|
129
|
+
probility = 1- probability_function.probability(pga)
|
130
|
+
assets[asset_type][asset_name]["survival_probability"] = probility
|
131
|
+
return assets
|
132
|
+
|
133
|
+
def plot(self, d : float):
|
134
|
+
"""Method to plot survival probaility of in the region of interest"""
|
135
|
+
m = range(2, 10)
|
136
|
+
h = np.linspace(14, 70, 100)
|
137
|
+
|
138
|
+
|
139
|
+
fig = plt.figure()
|
140
|
+
ax = fig.add_subplot(111)
|
141
|
+
for magnitude in m:
|
142
|
+
points = []
|
143
|
+
for depth in h:
|
144
|
+
hypocentral_distance = (depth**2 + d**2)**0.5
|
145
|
+
intensity = 0.66* magnitude - 1.13 * np.log(hypocentral_distance) -0.0072 * hypocentral_distance + 3.73
|
146
|
+
act_int = 10**intensity
|
147
|
+
survival_prob = 1 - act_int / 383641.228284058
|
148
|
+
if survival_prob > 1:
|
149
|
+
survival_prob = 1
|
150
|
+
elif survival_prob < 0:
|
151
|
+
survival_prob = 0
|
152
|
+
|
153
|
+
points.append(survival_prob)
|
154
|
+
|
155
|
+
print(points[0])
|
156
|
+
ax.plot(h, points, label=f"magnitude: {magnitude}")
|
157
|
+
ax.legend()
|
158
|
+
plt.show()
|
159
|
+
|
160
|
+
|
161
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
from erad.constants import FIRE_HISTORIC_GEODATAFRAME_PATH, DATA_FOLDER
|
2
|
+
from shapely.geometry import MultiPolygon, Point, LineString
|
3
|
+
from erad.scenarios.utilities import ProbabilityFunctionBuilder
|
4
|
+
from erad.scenarios.abstract_scenario import BaseScenario
|
5
|
+
from erad.exceptions import FeatureNotImplementedError
|
6
|
+
from erad.scenarios.utilities import GeoUtilities
|
7
|
+
import matplotlib.pyplot as plt
|
8
|
+
from datetime import datetime
|
9
|
+
import geopandas as gpd
|
10
|
+
import numpy as np
|
11
|
+
import random
|
12
|
+
import pyproj
|
13
|
+
import os
|
14
|
+
|
15
|
+
from erad.scenarios.common import AssetTypes
|
16
|
+
from erad.scenarios.utilities import ProbabilityFunctionBuilder
|
17
|
+
|
18
|
+
|
19
|
+
class FireScenario(BaseScenario, GeoUtilities):
|
20
|
+
"""Base class for FireScenario. Extends BaseScenario and GeoUtilities
|
21
|
+
|
22
|
+
Attributes:
|
23
|
+
multipolygon (MultiPolygon): MultiPolygon enclosing wild fire regions
|
24
|
+
probability_model (dict): Dictionary mapping asset types to probability funcitons
|
25
|
+
timestamp (datetime): Scenario occurance time
|
26
|
+
"""
|
27
|
+
|
28
|
+
fragility_curves = {
|
29
|
+
AssetTypes.substation.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
30
|
+
AssetTypes.solar_panels.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
31
|
+
AssetTypes.buried_lines.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
32
|
+
AssetTypes.wind_turbines.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
33
|
+
AssetTypes.battery_storage.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
34
|
+
AssetTypes.transmission_poles.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
35
|
+
AssetTypes.distribution_poles.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
36
|
+
AssetTypes.transmission_overhead_lines.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
37
|
+
AssetTypes.distribution_overhead_lines.name : ProbabilityFunctionBuilder("lognorm", [0.8, 10, 5]),
|
38
|
+
}
|
39
|
+
|
40
|
+
def __init__(self, multipolygon : MultiPolygon , probability_model : dict, timestamp : datetime) -> None:
|
41
|
+
"""Constructor for FireScenario.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
multipolygon (MultiPolygon): MultiPolygon enclosing wild fire regions
|
45
|
+
probability_model (dict): Dictionary mapping asset types to probability funcitons
|
46
|
+
timestamp (datetime): Scenario occurance time
|
47
|
+
"""
|
48
|
+
|
49
|
+
|
50
|
+
super(FireScenario, self).__init__(multipolygon, probability_model, timestamp)
|
51
|
+
return
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
def from_dynamic_model(cls, multipolygon : MultiPolygon , probability_function : dict, timestamp : datetime):
|
55
|
+
pass
|
56
|
+
|
57
|
+
@classmethod
|
58
|
+
def from_historical_fire_by_code(cls, fire_code : str, probability_function : dict = None):
|
59
|
+
"""Class method for FireScenario.
|
60
|
+
|
61
|
+
Args:
|
62
|
+
fire_code (str): Code for a historic wild fire event
|
63
|
+
probability_function (dict): Dictionary mapping asset types to probability funcitons
|
64
|
+
"""
|
65
|
+
|
66
|
+
|
67
|
+
data_file = os.path.join(DATA_FOLDER, FIRE_HISTORIC_GEODATAFRAME_PATH)
|
68
|
+
assert os.path.exists(data_file), f"The data file {data_file} not found"
|
69
|
+
fire_data = gpd.read_file(data_file)
|
70
|
+
fire_data_longlat = fire_data.to_crs('epsg:4326')
|
71
|
+
|
72
|
+
cls.fire_data_filtered = fire_data_longlat[fire_data_longlat['Combined_Fire_Code'] == fire_code]
|
73
|
+
assert not cls.fire_data_filtered.empty, f"Fire name '{fire_code}' not found in the database"
|
74
|
+
assert len(cls.fire_data_filtered) == 1, f"More than one record matched. Use additional filters for a unique match."
|
75
|
+
multipolygon = cls.fire_data_filtered["geometry"].values[0]
|
76
|
+
timestamp = cls.fire_data_filtered["Combined_Ignition_Date"].values[0]
|
77
|
+
if not np.datetime64:
|
78
|
+
timestamp = cls.fire_data_filtered["Combined_Controlled_Date"].values[0]
|
79
|
+
if not np.datetime64:
|
80
|
+
timestamp = cls.fire_data_filtered["Combined_Containment_Date"].values[0]
|
81
|
+
|
82
|
+
return cls(multipolygon, probability_function, timestamp)
|
83
|
+
|
84
|
+
|
85
|
+
@property
|
86
|
+
def area(self) -> float:
|
87
|
+
"""Method to calculate area of affected region."""
|
88
|
+
geod = pyproj.Geod(ellps="WGS84")
|
89
|
+
area = abs(geod.geometry_area_perimeter(self.polygon)[0])
|
90
|
+
return area
|
91
|
+
|
92
|
+
@property
|
93
|
+
def polygon(self) -> MultiPolygon:
|
94
|
+
"""Method to return polygon for the affected region."""
|
95
|
+
return self.multipolygon
|
96
|
+
|
97
|
+
@property
|
98
|
+
def boundary(self) -> LineString:
|
99
|
+
"""Method to return boundary for the affected region."""
|
100
|
+
return self.multipolygon.boundary
|
101
|
+
|
102
|
+
@property
|
103
|
+
def centroid(self) -> Point:
|
104
|
+
"""Method to return the centroid of the affected region."""
|
105
|
+
return self.polygon.centroid
|
106
|
+
|
107
|
+
def increment_time(self):
|
108
|
+
"""Method to increment simulation time for time evolviong scenarios."""
|
109
|
+
raise FeatureNotImplementedError()
|
110
|
+
|
111
|
+
def calculate_survival_probability(self, assets : dict, timestamp : datetime, plot: bool) -> dict:
|
112
|
+
"""Method to calculate survival probaility of asset types.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
assets (dict): The dictionary of all assets and their corresponding asset types
|
116
|
+
plot (bool): Set to true to plot the fire survival model
|
117
|
+
"""
|
118
|
+
if plot:
|
119
|
+
points = []
|
120
|
+
for asset_type, asset_dict in assets.items():
|
121
|
+
assert asset_type in self.probability_model, f"Survival probability for asset type '{asset_type}' not found in the passed probability_model"
|
122
|
+
probability_function = self.probability_model[asset_type]
|
123
|
+
for asset_name, asset_ppty in asset_dict.items():
|
124
|
+
coords = Point(asset_ppty["coordinates"])
|
125
|
+
coords_flipped = Point(coords.y, coords.x)
|
126
|
+
|
127
|
+
if self.in_polygon(coords_flipped):
|
128
|
+
distance = 0
|
129
|
+
survival_probability = 0
|
130
|
+
else:
|
131
|
+
try:
|
132
|
+
distance = self.distance_from_boundary(coords)
|
133
|
+
except:
|
134
|
+
distance = self.distance_from_boundary(Point(coords.y, coords.x))
|
135
|
+
survival_probability = probability_function.probability(distance*1000)
|
136
|
+
|
137
|
+
assets[asset_type][asset_name]["survival_probability"] = survival_probability
|
138
|
+
assets[asset_type][asset_name]["distance_to_boundary"] = distance
|
139
|
+
if plot:
|
140
|
+
points.append((coords.x, coords.y, survival_probability))
|
141
|
+
if plot:
|
142
|
+
points = np.array(points)
|
143
|
+
|
144
|
+
X = points[:,0]
|
145
|
+
Y = points[:,1]
|
146
|
+
Z = points[:,2]
|
147
|
+
|
148
|
+
fig = plt.figure()
|
149
|
+
ax = fig.add_subplot(111, projection='3d')
|
150
|
+
ax.plot_trisurf(X, Y, Z, color='white', edgecolors='grey', alpha=0.3)
|
151
|
+
#ax.scatter(X, Y, Z, c='red')
|
152
|
+
plt.show()
|
153
|
+
|
154
|
+
return assets
|
155
|
+
|
156
|
+
def plot(self):
|
157
|
+
self.fire_data_filtered.plot()
|
158
|
+
plt.show()
|
159
|
+
|
160
|
+
|