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) with AssetGroup and AssetType coded-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 arcpy initialized against the active project and PyYAML installed (conda install pyyaml).
  • A versioned editing workspace (branch versioning for enterprise geodatabases) so hierarchy edits are isolated from sde.DEFAULT until 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.

  1. Confirm domain coverage. Every AssetGroup / AssetType pair you intend to create must resolve to an existing coded value. Use arcpy.da.ListDomains() and assert that each code in your YAML matrix exists. Orphaned codes are the number-one driver of downstream containment errors.
  2. 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 missing Vent or Bypass terminal silently collapses bypass-loop modeling.
  3. 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.
  4. 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.
  5. 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.

Gas network containment hierarchy and regulator terminals A regulation distribution main carries a service lateral that taps into a regulator station. The regulator station (level one) contains a regulator skid (level two), which contains a flow meter, a pressure regulator, and a relief valve (level three). The pressure regulator exposes four terminals: inlet, outlet, vent, and bypass. A meter set contained by the lateral is shown as a separate branch. Container Contained asset Container edge (main) Terminal Distribution main transmission main upstream Service lateral (tap) Meter set level 1 container Regulator station level 1 — container contains Regulator skid level 2 — container (structure) Flow meter level 3 asset Relief valve level 3 asset Pressure regulator level 3 asset · 4 terminals Inlet (1) Outlet (2) Vent (3) Bypass (4) meter→reg
Three containment levels for a gas station — station → skid → metering and regulation assets — with the four physical terminals every 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:

  1. 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.
  2. CI/CD hook with structured output. Invoke deploy_gas_hierarchy from a CI job and fail the build on any non-empty summary["failed"]. The same containment-rule gating used in automating connectivity rule validation in CI pipelines keeps schema drift out of production.
  3. Retry and backoff for lock contention. Enterprise geodatabase locks during peak editing cause arcpy.un.Associate to fail transiently. Wrap the association loop in an exponential-backoff retry (for example, three attempts at 2s, 4s, 8s) before recording a hard failure.
  4. Diagnostic resolution loop. When validation flags errors, work them in order: query laterals with a NULL containment 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 with arcpy.un.UpdateSubnetwork() and confirm the SubnetworkName and Status fields.
  5. 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.
  6. CMMS / REST integration. Push the structured summary to 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.