Auditing Valve Operability and Turn-Count Records

An isolation trace returns valves, and a crew closes them. Everything between those two sentences assumes the valves can actually be turned — an assumption the network model rarely records and almost never checks. A valve that has not been exercised in a decade, one that seized after a main replacement re-graded the road above it, and one that closes eleven of its fourteen turns before meeting debris all read as ordinary open valves in the isolation layer. The last is the worst: it reports as closed after the crew turns it, and the main stays live. This guide builds the operability audit that keeps those valves out of isolation sets, using exercise dates, turn counts and a criticality-driven interval. It extends the barrier discipline in valve and isolator mapping strategies.

Environment Prerequisites

  • An isolation feature class carrying operability attribution: last exercise date, recorded turns, manufacturer turn specification, and an operable flag distinct from OPERATIONAL_STATUS.
  • An exercise interval per criticality class rather than one interval for the estate; a transmission isolation valve and a hydrant lateral valve do not need the same cadence.
  • Python 3.11 with pandas>=2.0; arcpy only where the exclusion is written back to the geodatabase rather than applied in the trace layer.
  • A work-management feed carrying completed exercise records, so the audit reads what the crews actually did rather than what was scheduled.
  • A defined shortfall tolerance — how far short of the specification a valve may stop before it is downgraded — agreed with operations rather than chosen in code.
  • A route into the isolation trace so an inoperable valve is excluded from results immediately, not at the next data load.

Schema-Aware Validation Protocol — Run Before the Audit

  1. Confirm operability is a separate attribute from status. An estate that encodes “seized” as a status value has made every trace that filters on status wrong in one direction or the other.
  2. Check the manufacturer turn specification is populated per valve class. Without it, a turn count is a number with nothing to compare against.
  3. Reconcile exercise records against work orders. An exercise date with no completed work order behind it is a date somebody typed, and it will keep a bad valve in service.
  4. Look for valves with no exercise record at all. These are not compliant-by-default; they are unknown, and unknown is closer to inoperable than to operable for planning purposes.
  5. Verify the criticality assignment. The interval is driven by it, so a valve whose criticality was never rolled up from the network is on the wrong cadence.

Minimal Reproducible Implementation

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from datetime import date, timedelta

import pandas as pd

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
LOG = logging.getLogger("valve-audit")

INTERVAL_DAYS = {"CRITICAL": 365, "HIGH": 730, "STANDARD": 1825}
SHORTFALL_TOLERANCE = 0.10          # 10% short of specification is still acceptable


@dataclass
class ValveFinding:
    valve_id: str
    state: str          # OPERABLE, DUE, RESTRICTED, INOPERABLE, UNKNOWN
    reason: str
    exclude_from_isolation: bool


@dataclass
class AuditResult:
    findings: list[ValveFinding] = field(default_factory=list)

    @property
    def excluded(self) -> list[str]:
        return [f.valve_id for f in self.findings if f.exclude_from_isolation]


def audit_valves(valves: pd.DataFrame, as_of: date) -> AuditResult:
    """Classify every valve by operability and decide which to exclude from isolation.

    ``valves`` carries valve_id, criticality, last_exercised, recorded_turns,
    spec_turns and operable_flag. The audit is deliberately conservative: a valve with
    no record is UNKNOWN and excluded, because planning a shutoff around a valve nobody
    has touched is the assumption this audit exists to remove.
    """
    result = AuditResult()

    for _, row in valves.iterrows():
        valve_id = str(row["valve_id"])
        crit = str(row.get("criticality", "STANDARD"))
        interval = timedelta(days=INTERVAL_DAYS.get(crit, 1825))
        last = row.get("last_exercised")
        spec = row.get("spec_turns")
        turns = row.get("recorded_turns")

        if pd.isna(last) or pd.isna(spec):
            result.findings.append(ValveFinding(
                valve_id, "UNKNOWN", "no exercise record or no turn specification", True))
            continue

        if row.get("operable_flag") is False:
            result.findings.append(ValveFinding(
                valve_id, "INOPERABLE", "flagged inoperable by the last exercise", True))
            continue

        if not pd.isna(turns) and float(turns) < float(spec) * (1 - SHORTFALL_TOLERANCE):
            shortfall = float(spec) - float(turns)
            state = "INOPERABLE" if float(turns) < float(spec) * 0.5 else "RESTRICTED"
            result.findings.append(ValveFinding(
                valve_id, state,
                f"stopped {shortfall:.0f} turn(s) short of {float(spec):.0f}", True))
            continue

        overdue = (as_of - pd.to_datetime(last).date()) > interval
        if overdue:
            result.findings.append(ValveFinding(
                valve_id, "DUE", f"last exercised beyond the {crit} interval", False))
        else:
            result.findings.append(ValveFinding(valve_id, "OPERABLE", "", False))

    LOG.info("%d valve(s): %d excluded from isolation", len(result.findings),
             len(result.excluded))
    return result
The operability states a valve moves through, and which of them a trace may use A valve is only a barrier a crew can rely on while it is both correctly positioned and physically turnable. Exercising confirms operability and resets the clock. An exercise that fails to reach the expected turn count downgrades the valve to restricted; one that will not move at all marks it inoperable, and an inoperable valve must be excluded from every isolation set even though its status still reads OPEN. OPERABLE exercised recently DUE past its interval RESTRICTED partial turns INOPERABLE excluded from traces interval elapses partial exercise will not move repaired and re-exercised An inoperable valve reading OPEN is the most dangerous record in the isolation layer. Status says where the valve is; operability says whether anyone can change it.

DUE is deliberately not excluded. A valve past its interval is a maintenance finding, not evidence of a defect, and excluding every overdue valve from isolation planning would empty the isolation layer in most estates. RESTRICTED and UNKNOWN are excluded, because in both cases there is positive reason to doubt the valve.

Recorded turns against the manufacturer count, and what each shortfall means A gate valve has a known number of turns from fully open to fully closed. An exercise that reaches the full count confirms the valve travels; one that stops short has met an obstruction, and the shortfall is diagnostic. A valve that closes most of the way still passes water, which is the case that produces an isolation set that looks complete and does not hold. against a 14-turn manufacturer specification Full travel 14 turns confirms operability Slight shortfall 12 turns debris, re-exercise Half travel 7 turns still passing — not a barrier Seized 1 turns inoperable Partial closure is the failure that still reads as a closed valve.

Production Deployment Pattern

  1. Feed the exclusion list into the isolation trace, not into the data. The trace should consult the exclusion at query time, so a repaired valve returns to service as soon as the exercise record arrives.
  2. Drive exercise scheduling from the audit. The DUE list is the work programme, ordered by criticality, and it belongs in the maintenance system rather than in a spreadsheet.
  3. Record turns on every exercise, not just success or failure. The turn count is what distinguishes a valve that is fine from one that is on its way to seizing.
  4. Treat a partial closure as an incident. A valve that reads closed and passes water has already invalidated at least one isolation plan; the finding should reach whoever planned it.
  5. Publish operability alongside status in every consumer. A dispatch console showing status without operability is showing half the picture during the event where the difference matters.
  6. Persist the audit. Findings, intervals, tolerance and the as-of date, so a later review can see what was known about a valve on the day a shutoff was planned around it.
  7. Report coverage, not just findings. The share of the isolation layer with a current exercise record is the number that describes the estate; a finding count says nothing about how much of the network was assessable in the first place.
The audit that keeps the isolation layer honest between exercise programmes Every valve carries an exercise date, a recorded turn count and an operable flag. The audit compares the date against the interval for its criticality, compares the turns against the manufacturer specification, and cross-checks the operable flag against both. Valves that fail are excluded from isolation traces immediately and queued for exercise, so a shutoff plan never includes a valve nobody can turn. READ exercise date turns, flag INTERVAL by criticality not one number TRAVEL turns vs spec shortfall classified EXCLUDE from isolation queue for exercise the quiet failure PARTIAL CLOSURE reads CLOSED, passes water — the isolation set is wrong Exclusion is immediate; the exercise can be scheduled.

Conclusion

Operability is a second dimension the isolation layer needs and usually lacks. Auditing exercise dates against criticality-driven intervals finds the valves nobody has touched; comparing recorded turns against the manufacturer specification finds the ones that no longer travel; and excluding restricted and unknown valves from isolation results keeps a shutoff plan honest. The exercise programme that follows is ordinary maintenance work — but it is prioritised by what the network actually depends on rather than by what is easy to reach.

For authoritative reference, consult the AWWA standards catalogue and the pandas documentation.