# ============================================================================= # operations.yml — pump-down operations. # # THIS IS THE FILE THAT ANSWERS THE ADVISORY QUESTION WITH EVIDENCE RATHER THAN # OPINION. When an operator asks what rate to run the station at, the honest # answer is a table of what has actually been run, what happened each time, and # what the documented limits are - then a deferral. Everything needed for that # is a measure here. # # REMOVING THE STAND-IN: db/README-standin-historian.md. The view below is # CONTRACT - repoint it at imh, do not edit this file to absorb a difference. # # SOURCE: fixture.operation_history while USE_FIXTURES=true. It is a # MATERIALIZED VIEW and it is DERIVED, not stored - confirmed now rather than # assumed: CI Server has no operations concept, and the WRPS item list is 49 # points with nothing resembling a batch or campaign record. # # The heuristic, kept deliberately simple so it can be explained to the person # checking the number, and implemented in db/002_fixtures.sql exactly as # written here: # # A PUMP-DOWN starts at the sample where AID.WRPS.STN.PUMPS_RUNNING goes from # 0 to non-zero, and ends at the next sample where it returns to 0. Its max # level is the maximum AID.WRPS.STN.LEVEL over that span plus the 10 minutes # before it, because the peak is usually just before the pumps catch up. # Operations shorter than 5 minutes are discarded as start/stop noise. # # Do not make this cleverer. A heuristic nobody can explain is not evidence, # and this cube's whole job is to produce evidence. # # THE TWO SOURCE ITEMS ARE SAMPLED AT DIFFERENT RATES - PUMPS_RUNNING every 30 # seconds, LEVEL every 5 - and the boundaries line up only because 30 is a # multiple of 5. If either group's rate is retuned on the server, check that # assumption before trusting start_level_pct or end_level_pct. # # THERE IS NO equipment_id. A pump-down belongs to the station and to nothing # else; duty_pump names the unit that led it. The column used to exist, carried # the literal 'STN-001', and was part of the same denormalisation that made # alarm equipment disagree with the tag seed. # # ON THE WORD "FILL": the generic spec calls these fills. WRPS is a pump # station, so the operation is a pump-down - the well fills passively on inflow # and the station draws it back down. Same shape, opposite sign. The measures # keep the pump-down naming because that is what an operator here would say. # ============================================================================= cubes: - name: operations sql_table: fixture.operation_history # -> derived from imh at Phase 4 description: > One row per pump-down at the Waterloo Road Pump Station: when it ran, how high the well got, how much came in, how much was pumped, which unit was duty, and whether it alarmed or spilled. dimensions: - name: operation_id sql: operation_id type: number primary_key: true - name: start_time sql: start_time type: time description: Stored UTC, presented in SITE_TIMEZONE. - name: end_time sql: end_time type: time - name: operation_type sql: operation_type type: string description: PUMP_DOWN. Reserved for future manual or wash-down operations. - name: duty_pump sql: duty_pump type: string description: > The unit that led the operation. Duty rotates on lowest accumulated run hours, service-due units ranked last, ties by ascending pump number - so an uneven distribution over a long window is a finding, not a rotation fault. - name: peak_pumps_running sql: peak_pumps_running type: number - name: high_level_alarm sql: high_level_alarm type: boolean description: Did this pump-down reach the high level alarm setpoint. - name: spill sql: spill type: boolean description: > Did the well go over the weir crest. A spill is an environmental reportable event; report the count plainly and never round it. - name: is_fixture sql: is_fixture type: boolean measures: - name: pump_down_count type: count description: Number of pump-down operations in the window. - name: avg_discharge_rate sql: avg_discharge_m3h type: avg description: > Mean discharge rate across operations, m3/h. Evidence of what has been run - NOT a recommendation of what to run. - name: min_discharge_rate sql: avg_discharge_m3h type: min - name: max_discharge_rate sql: avg_discharge_m3h type: max - name: avg_inflow_rate sql: avg_inflow_m3h type: avg - name: max_level_reached sql: max_level_pct type: max description: Highest wet well level reached, percent of the weir crest. - name: avg_max_level sql: max_level_pct type: avg - name: high_alarm_count type: count filters: - sql: "{CUBE}.high_level_alarm = TRUE" - name: high_alarm_rate sql: > COUNT(*) FILTER (WHERE {CUBE}.high_level_alarm)::float / NULLIF(COUNT(*), 0) type: number description: > Fraction of pump-downs that reached the high level alarm. Pair it with pump_down_count in the answer - 1 in 2 and 50 in 100 are not the same evidence, and a rate quoted without its denominator invites the reader to treat a small sample as a trend. - name: spill_count type: count filters: - sql: "{CUBE}.spill = TRUE" - name: avg_duration_minutes sql: "EXTRACT(EPOCH FROM ({CUBE}.end_time - {CUBE}.start_time)) / 60" type: avg pre_aggregations: - name: ops_by_day measures: - pump_down_count - avg_discharge_rate - max_level_reached - high_alarm_count - spill_count dimensions: [duty_pump, operation_type, peak_pumps_running] time_dimension: start_time granularity: day partition_granularity: month refresh_key: # 24h, not minutes: the fixtures are static, so a shorter # interval rebuilds a byte-identical result on a 2-vCPU host # and was a standing CPU load for no gain. Tune this back # deliberately when imh makes the data genuinely live. every: 24 hours build_range_start: # Eight days, not 365. The historian retains one week, so a pump-down # older than that does not exist to be aggregated. sql: "SELECT now() - interval '8 days'" build_range_end: sql: "SELECT now()"