Working with GDM models#

ERAD’s AssetSystem provides a class method for users working with a GDM DistributionSystem. Distribution system components are automatically mapped to the asset models and added to the AssetSystem. A hazard simulation can then be set up using the steps listed above. We start by loading a GDM model using the gdmloader package. This package can be installed using the command

pip install gdmloader
from IPython.display import display, HTML
import plotly.graph_objects as go
import plotly.io as pio

pio.renderers.default = "notebook_connected"

from gdm.distribution import DistributionSystem
from gdmloader.constants import GCS_CASE_SOURCE
from gdmloader.source import SystemLoader

gdm_loader = SystemLoader()
gdm_loader.add_source(GCS_CASE_SOURCE)

distribution_system: DistributionSystem = gdm_loader.load_dataset(
    source_name=GCS_CASE_SOURCE.name,
    system_type=DistributionSystem,
    dataset_name="p1rhs7_1247",
    version="2_1_2",
)
distribution_system.name = "p1rhs7_1247"
distribution_system.info()
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
Cell In[1], line 8
      5 pio.renderers.default = "notebook_connected"
      7 from gdm.distribution import DistributionSystem
----> 8 from gdmloader.constants import GCS_CASE_SOURCE
      9 from gdmloader.source import SystemLoader
     11 gdm_loader = SystemLoader()

File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/gdmloader/constants.py:6
      2 from gdmloader.source import SourceModel
      3 import fsspec
      5 GDM_CASE_SOURCE = SourceModel(
----> 6     fs=fsspec.filesystem("github", org="NREL-Distribution-Suites", repo="gdm-cases", branch="main"),
      7     name="gdm-cases",
      8     url="https://github.com/NREL-Distribution-Suites/gdm-cases",
      9     folder="data",
     10 )
     12 GCS_CASE_SOURCE = SourceModel(
     13     fs=fsspec.filesystem("gcs"),
     14     name="gdm_data",
     15     url="https://storage.googleapis.com/gdm_data",
     16     folder="data",
     17 )

File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/fsspec/registry.py:310, in filesystem(protocol, **storage_options)
    303     warnings.warn(
    304         "The 'arrow_hdfs' protocol has been deprecated and will be "
    305         "removed in the future. Specify it as 'hdfs'.",
    306         DeprecationWarning,
    307     )
    309 cls = get_filesystem_class(protocol)
--> 310 return cls(**storage_options)

File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/fsspec/spec.py:81, in _Cached.__call__(cls, *args, **kwargs)
     79     return cls._cache[token]
     80 else:
---> 81     obj = super().__call__(*args, **kwargs)
     82     # Setting _fs_token here causes some static linters to complain.
     83     obj._fs_token_ = token

File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/fsspec/implementations/github.py:63, in GithubFileSystem.__init__(self, org, repo, sha, username, token, timeout, **kwargs)
     59     u = "https://api.github.com/repos/{org}/{repo}"
     60     r = requests.get(
     61         u.format(org=org, repo=repo), timeout=self.timeout, **self.kw
     62     )
---> 63     r.raise_for_status()
     64     sha = r.json()["default_branch"]
     66 self.root = sha

File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/requests/models.py:1026, in Response.raise_for_status(self)
   1021     http_error_msg = (
   1022         f"{self.status_code} Server Error: {reason} for url: {self.url}"
   1023     )
   1025 if http_error_msg:
-> 1026     raise HTTPError(http_error_msg, response=self)

HTTPError: 403 Client Error: rate limit exceeded for url: https://api.github.com/repos/NREL-Distribution-Suites/gdm-cases

Next, we built the asset system from the gdm DistributionSystem using the from_gdm method.

from erad.systems import AssetSystem

asset_system = AssetSystem.from_gdm(distribution_system)
asset_system.info()

for a in asset_system.iter_all_components():
    a.pprint()
    break
System                          
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Property              Value ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ System name          │       │
│ Data format version  │       │
│ Components attached  │  4987 │
│ Time Series attached │     0 │
│ Description          │       │
└──────────────────────┴───────┘
Component Information           
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Type                  Count ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ Asset                │  4987 │
└──────────────────────┴───────┘
Asset(
    name='sb10_p1rhs7_1247_bus_10_6',
    distribution_asset=UUID('1f95b063-9b69-4331-8f56-6a718161087f'),
    connections=[],
    devices=[],
    asset_type=<AssetTypes.distribution_poles: 6>,
    height=<Quantity(3, 'meter')>,
    latitude=38.64000605387684,
    longitude=-122.50715315149552,
    asset_state=[],
    elevation=<Quantity(460.0, 'meter')>
)

Plotting an AssetSystem#

fig = asset_system.plot(show=False)
display(HTML(pio.to_html(fig, include_plotlyjs="cdn", full_html=False)))
Asset type:  substation
Asset type:  distribution_poles
Asset type:  distribution_junction_box

Building a HazardModel#

In this section, we built a hazard model and apply the model the asset system.

from datetime import datetime

from shapely.geometry import Polygon
from gdm.quantities import Distance

from erad.models.hazard import FloodModelArea, FloodModel
from erad.systems import HazardSystem
from erad.quantities import Speed

flood_area = FloodModelArea(
    affected_area=Polygon(
        [
            (-122.38, 38.70),
            (-122.35, 38.68),
            (-122.343, 38.69),
            (-122.37, 38.7035),
        ]
    ),
    water_velocity=Speed(0, "meter/second"),
    water_elevation=Distance(160, "meter"),
)

flood = FloodModel(
    name="flood 1",
    timestamp=datetime.now(),
    affected_areas=[flood_area],
)

user_defined_flood_event = HazardSystem(auto_add_composed_components=True)
user_defined_flood_event.add_component(flood)

Overlaying the HazardModel#

We can overlay the hazard model on the same plot using the add_trace method. The show method can be used to render the image again.

polygon = flood.affected_areas[0].affected_area
lon, lat = polygon.exterior.xy  # returns x and y sequences

fig.add_trace(
    go.Scattermap(
        fill="toself",
        lon=lon.tolist(),
        lat=lat.tolist(),
        marker={"size": 10, "color": flood.affected_areas[0].water_velocity.magnitude},
    )
)

fig.show()

Finally, we can run the actual simulation using the HazardSimulator class from erad.runner.

from erad.runner import HazardSimulator

user_defined_flood_event.info()

hazard_scenario = HazardSimulator(asset_system=asset_system)
hazard_scenario.run(hazard_system=user_defined_flood_event)
System                          
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Property              Value ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ System name          │       │
│ Data format version  │       │
│ Components attached  │     2 │
│ Time Series attached │     0 │
│ Description          │       │
└──────────────────────┴───────┘
Component Information           
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Type                  Count ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ FloodModel           │     1 │
│ FloodModelArea       │     1 │
└──────────────────────┴───────┘
2025-07-18 16:06:49.885 | WARNING  | erad.runner:run:51 - No HazardFragilityCurves definations found in the passed HazardSystem using default curve definations

Once the simulation is complete, we can visualize the results by plotting the survival_prob from the updated gdf dataframe.

fig = asset_system.plot(show=False)
display(HTML(pio.to_html(fig, include_plotlyjs="cdn", full_html=False)))
Asset type:  substation
Asset type:  distribution_poles
Asset type:  distribution_junction_box