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()
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[1], line 14
11 gdm_loader = SystemLoader()
12 gdm_loader.add_source(GCS_CASE_SOURCE)
---> 14 distribution_system: DistributionSystem = gdm_loader.load_dataset(
15 source_name=GCS_CASE_SOURCE.name,
16 system_type=DistributionSystem,
17 dataset_name="p1rhs7_1247",
18 version="2_1_2",
19 )
20 distribution_system.name = "p1rhs7_1247"
21 distribution_system.info()
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/gdmloader/source.py:134, in SystemLoader.load_dataset(self, system_type, source_name, dataset_name, version)
131 raise ValueError(msg)
133 system_file = list(dataset_folder.rglob("*.json"))[0]
--> 134 return system_type.from_json(system_file)
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/infrasys/system.py:272, in System.from_json(cls, filename, upgrade_handler, **kwargs)
270 data = orjson.loads(f_in.read())
271 time_series_parent_dir = Path(filename).parent
--> 272 return cls.from_dict(
273 data, time_series_parent_dir, upgrade_handler=upgrade_handler, **kwargs
274 )
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/infrasys/system.py:494, in System.from_dict(cls, data, time_series_parent_dir, upgrade_handler, **kwargs)
492 if component_needs_metadata_migration(system_data["components"][0]):
493 system_data["components"] = migrate_component_metadata(system_data["components"])
--> 494 system._deserialize_components(system_data["components"])
495 system._deserialize_supplemental_attributes(system_data["supplemental_attributes"])
496 logger.info("Deserialized system {}", system.label)
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/infrasys/system.py:1592, in System._deserialize_components(self, components)
1590 skipped_types = self._deserialize_components_first_pass(components, cached_types)
1591 if skipped_types:
-> 1592 self._deserialize_components_nested(skipped_types, cached_types)
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/infrasys/system.py:1626, in System._deserialize_components_nested(self, skipped_types, cached_types)
1624 if len(components) > 1:
1625 for component_dict in components[1:]:
-> 1626 component = self._try_deserialize_component(component_dict, cached_types)
1627 assert component is not None
1628 deserialized_types.add(component_type)
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/infrasys/system.py:1648, in System._try_deserialize_component(self, component, cached_types)
1646 metadata = SerializedTypeMetadata.validate_python(component[TYPE_METADATA])
1647 component_type = cached_types.get_type(metadata)
-> 1648 actual_component = component_type(**values)
1649 self._components.add(actual_component, deserialization_in_progress=True)
1650 return actual_component
File /opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/pydantic/main.py:250, in BaseModel.__init__(self, **data)
248 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
249 __tracebackhide__ = True
--> 250 validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
251 if self is not validated_self:
252 warnings.warn(
253 'A custom validator is returning a value other than `self`.\n'
254 "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
255 'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
256 stacklevel=2,
257 )
ValidationError: 1 validation error for MatrixImpedanceBranch
Value error, Length of matrix mat=<Quantity([[1.76096667 0.39596667]
[0.39596667 1.76096667]], 'ohm / kilometer')> did not match number of phases self.phases=[<Phase.S2: 'S2'>, <Phase.S1: 'S1'>, <Phase.N: 'N'>] [type=value_error, input_value={'uuid': '42a039ae-76dd-4...tity(115.0, 'ampere')>)}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.12/v/value_error
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