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.
Files changed (72) hide show
  1. erad/__init__.py +1 -1
  2. erad/constants.py +20 -68
  3. erad/cypher_queries/load_data_v1.cypher +212 -0
  4. erad/data/World_Earthquakes_1960_2016.csv +23410 -0
  5. erad/db/__init__.py +0 -0
  6. erad/db/assets/__init__.py +0 -0
  7. erad/db/assets/critical_infras.py +171 -0
  8. erad/db/assets/distribution_lines.py +101 -0
  9. erad/db/credential_model.py +20 -0
  10. erad/db/disaster_input_model.py +23 -0
  11. erad/db/inject_earthquake.py +52 -0
  12. erad/db/inject_flooding.py +53 -0
  13. erad/db/neo4j_.py +162 -0
  14. erad/db/utils.py +14 -0
  15. erad/exceptions.py +68 -0
  16. erad/metrics/__init__.py +0 -0
  17. erad/metrics/check_microgrid.py +208 -0
  18. erad/metrics/metric.py +178 -0
  19. erad/programs/__init__.py +0 -0
  20. erad/programs/backup.py +62 -0
  21. erad/programs/microgrid.py +45 -0
  22. erad/scenarios/__init__.py +0 -0
  23. erad/scenarios/abstract_scenario.py +103 -0
  24. erad/scenarios/common.py +93 -0
  25. erad/scenarios/earthquake_scenario.py +161 -0
  26. erad/scenarios/fire_scenario.py +160 -0
  27. erad/scenarios/flood_scenario.py +494 -0
  28. erad/scenarios/flows.csv +671 -0
  29. erad/scenarios/utilities.py +76 -0
  30. erad/scenarios/wind_scenario.py +89 -0
  31. erad/utils/__init__.py +0 -0
  32. erad/utils/ditto_utils.py +252 -0
  33. erad/utils/hifld_utils.py +147 -0
  34. erad/utils/opendss_utils.py +357 -0
  35. erad/utils/overpass.py +76 -0
  36. erad/utils/util.py +178 -0
  37. erad/visualization/__init__.py +0 -0
  38. erad/visualization/plot_graph.py +218 -0
  39. {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/METADATA +39 -29
  40. nrel_erad-1.0.0.dist-info/RECORD +42 -0
  41. {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/WHEEL +1 -2
  42. {nrel_erad-0.1.0.dist-info → nrel_erad-1.0.0.dist-info}/licenses/LICENSE.txt +28 -28
  43. erad/default_fragility_curves/__init__.py +0 -15
  44. erad/default_fragility_curves/default_fire_boundary_dist.py +0 -94
  45. erad/default_fragility_curves/default_flood_depth.py +0 -108
  46. erad/default_fragility_curves/default_flood_velocity.py +0 -101
  47. erad/default_fragility_curves/default_fragility_curves.py +0 -23
  48. erad/default_fragility_curves/default_peak_ground_acceleration.py +0 -163
  49. erad/default_fragility_curves/default_peak_ground_velocity.py +0 -94
  50. erad/default_fragility_curves/default_wind_speed.py +0 -94
  51. erad/enums.py +0 -40
  52. erad/gdm_mapping.py +0 -83
  53. erad/models/__init__.py +0 -1
  54. erad/models/asset.py +0 -287
  55. erad/models/asset_mapping.py +0 -20
  56. erad/models/fragility_curve.py +0 -116
  57. erad/models/hazard/__init__.py +0 -5
  58. erad/models/hazard/base_models.py +0 -12
  59. erad/models/hazard/common.py +0 -26
  60. erad/models/hazard/earthquake.py +0 -93
  61. erad/models/hazard/flood.py +0 -83
  62. erad/models/hazard/wild_fire.py +0 -121
  63. erad/models/hazard/wind.py +0 -143
  64. erad/models/probability.py +0 -73
  65. erad/probability_builder.py +0 -35
  66. erad/quantities.py +0 -25
  67. erad/runner.py +0 -122
  68. erad/systems/__init__.py +0 -2
  69. erad/systems/asset_system.py +0 -414
  70. erad/systems/hazard_system.py +0 -122
  71. nrel_erad-0.1.0.dist-info/RECORD +0 -35
  72. 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
+