Step-by-Step Guide to Building Asset Hierarchies for Gas Networks
Fragmented gas distribution records, orphaned service laterals, and non-compliant containment models routinely trigger topology validation failures the moment a gas network is loaded into the ArcGIS Utility Network. Hand-building a hierarchy feature-by-feature does not survive contact with real distribution data: a single regulator station carries inlet, outlet, vent, and bypass terminals, every service tap must bind to the correct main, and PHMSA reporting, leak-survey routing, and pressure-zone isolation all depend on those associations being mathematically consistent. Ad-hoc construction guarantees attribute drift, mismatched terminal IDs, and silent trace breaks that surface only during an emergency isolation — exactly when the cost of failure is highest. This guide gives you a deterministic, schema-first procedure for modeling a gas network hierarchy in a geodatabase-backed Utility Network, with copy-paste-ready Python automation and a diagnostic protocol you can run before anything reaches production.
Environment Prerequisites
Validate every item below before instantiating a single feature. Most “the trace returns nothing” tickets trace back to a skipped prerequisite, not a bug in the procedure.
- ArcGIS Pro 3.2+ or ArcGIS Enterprise 11.2+ with the Utility Network extension licensed and enabled.
- A file or enterprise geodatabase containing a gas domain network (e.g.
GasDistribution) withAssetGroupandAssetTypecoded-value domains already published. - Pre-built coded-value domains for pressure class, pipe material, and
LifecycleStatus(Proposed,Active,Retired) — enforced at the schema level so invalid values cannot be ingested. - A normalized CRS across every feature class. Misaligned projections break containment centroid tests; resolve them first with the workflow in CRS Alignment & Geodetic Transformations.
- ArcGIS Pro conda Python environment with
arcpyinitialized against the active project andPyYAMLinstalled (conda install pyyaml). - A versioned editing workspace (branch versioning for enterprise geodatabases) so hierarchy edits are isolated from
sde.DEFAULTuntil validated.
Schema-Aware Validation Protocol (Run This First)
The single most common cause of a failed gas hierarchy build is schema misalignment, not a coding error. Run these checks in order — the most frequent failure is listed first — before invoking any automation.
- Confirm domain coverage. Every
AssetGroup/AssetTypepair you intend to create must resolve to an existing coded value. Usearcpy.da.ListDomains()and assert that each code in your YAML matrix exists. Orphaned codes are the number-one driver of downstream containment errors. - Audit terminal configurations. Inspect each junction feature class with
arcpy.Describe()and verify the terminal configuration. Gas regulators and valves need multi-terminal configs; a missingVentorBypassterminal silently collapses bypass-loop modeling. - Verify the containment matrix is acyclic. Walk your parent-child YAML with a depth-first search and reject any cycle before deployment. A regulator station that ultimately contains itself produces
ERROR 001569(containment loop) and infinite recursion during subnetwork updates. - Check nesting depth. Keep containment to three levels or fewer (e.g.
Regulator Station → Regulator Skid → Flow Meter). Deeper nesting degrades trace performance and complicates isolation logic. - Validate connectivity-rule compatibility. Confirm that service laterals are permitted to connect to distribution mains only, and that pressure-class compatibility rules exist, before the rules engine rejects associations at runtime.
This containment matrix is the contract your automation enforces. Define it explicitly so the deployment script can parse hierarchy rules deterministically:
- Containers: distribution mains, transmission mains, service laterals, regulator stations, meter sets, valve vaults.
- Structures: physical enclosures or mounting points that house multiple components (regulator skids, telemetry cabinets).
- Contained assets: pressure regulators, flow meters, relief valves, cathodic-protection test stations, SCADA nodes.
- Connectivity rules: terminal-to-terminal associations for service taps, regulator bypasses, and pressure-reducing-station inlets and outlets.
The structural model you are encoding here is the gas-specific application of the broader containment, attachment, and connectivity patterns covered in Asset Hierarchy Design for Water & Electric; reusing that association logic keeps a multi-commodity portfolio consistent.
TerminalID mapping must honour.Minimal Reproducible Implementation
This script reads a YAML hierarchy definition, batch-creates the container and junction feature classes, applies containment associations, and validates topology — all with structured logging and explicit exception handling so failures are diagnosable rather than silent. It is designed for an ArcGIS Pro Python 3.x environment.
import arcpy
import yaml
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
def deploy_gas_hierarchy(config_path: str, gdb_path: str, un_path: str) -> dict:
"""
Read a YAML hierarchy definition and build feature classes plus containment
associations in a Utility Network geodatabase.
Returns a structured summary of created classes, applied associations,
and any failures, so the caller (CI job or CMMS hook) can act on the result.
"""
summary = {"created": [], "associated": [], "failed": []}
with open(config_path, "r") as f:
schema = yaml.safe_load(f)
arcpy.env.workspace = gdb_path
arcpy.env.overwriteOutput = True
# 1. Batch-create container and junction feature classes from the schema.
for asset_class, rules in schema["containers"].items():
geom_type = "POLYGON" if rules["geometry"] == "structure" else "POLYLINE"
if not arcpy.Exists(asset_class):
arcpy.management.CreateFeatureclass(gdb_path, asset_class, geom_type)
summary["created"].append(asset_class)
logging.info("Created feature class: %s (%s)", asset_class, geom_type)
# 2. Apply containment associations defined in the parent-child matrix.
for parent, children in schema["containment_matrix"].items():
for child in children:
try:
arcpy.un.Associate(
in_utility_network=un_path,
association_type="CONTAINMENT",
from_feature=parent,
to_feature=child,
)
summary["associated"].append((parent, child))
logging.info("Created containment: %s -> %s", parent, child)
except arcpy.ExecuteError:
# ERROR 001568: invalid terminal mapping
# ERROR 001569: containment loop detected
msg = arcpy.GetMessages(2)
summary["failed"].append({"parent": parent, "child": child, "error": msg})
logging.error("Association failed (%s -> %s): %s", parent, child, msg)
# 3. Validate topology to surface terminal mismatches and orphaned associations.
logging.info("Running topology validation...")
arcpy.un.ValidateTopology(un_path)
logging.info("Hierarchy deployment complete.")
return summary
if __name__ == "__main__":
result = deploy_gas_hierarchy(
"gas_hierarchy.yaml",
r"C:\GIS\GasNetwork.gdb",
r"C:\GIS\GasNetwork.gdb\GasDistribution\GasNetwork",
)
if result["failed"]:
raise SystemExit(f"{len(result['failed'])} association(s) failed; see log.")
Capture validation errors before they reach production. The try/except around arcpy.un.Associate intercepts ERROR 001568 (invalid terminal mapping) and ERROR 001569 (containment loop), records the failing parent/child pair in the returned summary, and lets you cross-reference the offending feature’s GlobalID and AssetType against your YAML matrix. Returning structured output instead of crashing turns ingestion failures from an hours-long debugging session into a minutes-long lookup.
Configuring terminals and connectivity
Before the containment pass succeeds, the junction feature classes must carry the right terminal configurations. Map TerminalID values explicitly to physical ports — 1: Inlet, 2: Outlet, 3: Vent, 4: Bypass — so regulator bypass loops, pressure-sensing lines, and SCADA telemetry taps resolve correctly. Then restrict service-lateral connectivity to distribution mains and enforce pressure-class compatibility so cross-zone tracing cannot propagate through an invalid junction. Structural attachments (telemetry nodes, cathodic-protection rectifiers, valve actuators) are mounted with attachment rules rather than containment; they preserve maintenance lineage but do not participate in tracing.
Production Deployment Pattern
Run the build as a repeatable, gated pipeline rather than a one-time migration:
- Versioned workspace. Execute the deployment against a dedicated branch version, never
sde.DEFAULT. Reconcile and post only after validation passes, so a malformed hierarchy never reaches operational GIS or field-data collection. - CI/CD hook with structured output. Invoke
deploy_gas_hierarchyfrom a CI job and fail the build on any non-emptysummary["failed"]. The same containment-rule gating used in automating connectivity rule validation in CI pipelines keeps schema drift out of production. - Retry and backoff for lock contention. Enterprise geodatabase locks during peak editing cause
arcpy.un.Associateto fail transiently. Wrap the association loop in an exponential-backoff retry (for example, three attempts at 2s, 4s, 8s) before recording a hard failure. - Diagnostic resolution loop. When validation flags errors, work them in order: query laterals with a
NULLcontainment parent (SELECT ObjectID, AssetType FROM ServiceLaterals WHERE ParentGlobalID IS NULL) and reassign them; inspect regulator terminal IDs when traces stall at a station; break containment cycles by reassigning the deepest node to a valid container; then rebuild witharcpy.un.UpdateSubnetwork()and confirm theSubnetworkNameandStatusfields. - Nightly drift detection. Schedule the validation pass against a staging copy each night so schema drift, orphaned associations, and broken terminals are flagged before they affect PHMSA reporting or leak-survey routing.
- CMMS / REST integration. Push the structured
summaryto your asset-management system or a REST endpoint so every hierarchy change carries the audit metadata (GlobalID,AssetType, timestamp) that lifecycle and compliance reviews require.
Conclusion
You now have a deterministic path from a regulation-aligned schema to a validated gas network hierarchy: domains and a containment matrix defined up front, terminals and connectivity rules enforced at the junction level, a single arcpy routine that builds and validates the hierarchy with diagnosable output, and a gated pipeline that keeps drift out of production. The payoff is direct — reproducible PHMSA reporting, reliable pressure-zone isolation, and trace results you can trust during an emergency. The next logical step is to wire the validation pass into a scheduled batch job; see how to scale that out in the broader topology validation workflows.
Related
- Up to the parent guide: Asset Hierarchy Design for Water & Electric
- Up to the section: Core Utility GIS Fundamentals & Network Models
- Python Script for Validating CRS Alignment Across Utility Layers
- Best Practices for Handling Precision Drift in CAD-to-GIS Conversions
- How to Configure UN Network Datasets in ArcGIS Pro