Skip to content

feat(geothermal): normalize OGC view temperatures to Celsius - #811

Merged
jirhiker merged 3 commits into
stagingfrom
feat/geothermal-temp-unit-normalization
Aug 7, 2026
Merged

feat(geothermal): normalize OGC view temperatures to Celsius#811
jirhiker merged 3 commits into
stagingfrom
feat/geothermal-temp-unit-normalization

Conversation

@jirhiker

@jirhiker jirhiker commented Aug 6, 2026

Copy link
Copy Markdown
Member

Problem

The per-well geothermal views created in d1e2f3a4b5c6 publish legacy temperatures unconverted and label them with max("TempUnit"). Two consequences:

  1. max() picks a unit lexically ('F' > 'C'), so a well holding both C and F readings is labelled 'F' while the values stay mixed.
  2. min("Temp") / max("Temp") aggregate across those mixed units — 100 F sorts above 40 C, so the published extremes are meaningless.

Nothing downstream compensates: pygeoapi serves the view columns as-is.

Change

New migration f3a1c2b4d5e6 (revises 2d3c3a268652, single head) adds two helpers and rebuilds the two temperature views.

Helpers:

  • public.nmw_temp_unit_code(text) -> text — canonicalizes a legacy unit to 'C' / 'F' / 'K', or NULL when unrecognized. NMW_GtTempDepths."TempUnit" is String(1) and NMW_GtBhtData."TempUnit" is String(5), so both single-letter and spelled-out forms are accepted.
  • public.nmw_temp_to_c(double precision, text) -> double precision — converts using that code. Unrecognized unit returns NULL rather than assuming a default, so bad rows are visible instead of silently wrong.

ogc_geothermal_wells_bht and ogc_geothermal_wells_temperature_profile gain:

  • min_bht_c / max_bht_c, min_temp_c / max_temp_c — aggregated over normalized values. These are the columns to chart.
  • temp_unit — now the constant 'C', describing the *_c columns.
  • temp_unit_source — the distinct source units actually present for the well (C, F, C,F, UNKNOWN, ...).
  • temp_unit_mixed — flags wells mixing units.
  • unconvertible_count — readings whose unit was not recognized.
  • profile series objects gain temp_c and temp_unit_source.

Raw columns (min_bht/max_bht, min_temp/max_temp, the series temp key) are unchanged for compatibility. They remain mixed-unit.

Verification

Ran upgrade + downgrade against a local dev database inside a rolled-back transaction — both clean, nothing persisted. Spot checks: 212 F -> 100, 40 C -> 40, 273.15 K -> 0, bogus unit -> NULL, NULL unit -> NULL. Sample wells (all F in dev data) convert correctly: max_bht 138.9 -> 59.39.

One bug surfaced during that run and is fixed here: materialized view population runs with a restricted search_path, so inlining nmw_temp_to_c failed on its unqualified nested call (ERROR: function nmw_temp_unit_code(text) does not exist). All function references are schema-qualified. No SET search_path on the functions, which would disable inlining across the ~370k-row source.

Reviewer notes

  • Breaking for anything parsing temp_unit — it now returns 'C' instead of the raw legacy code. Raw codes moved to temp_unit_source.
  • Applying this drops and recreates the materialized view, repopulating WITH DATA; expect the usual matview build cost against NMW_GtTempDepths.
  • Heat-flow units (HtFlowUnit, GradUnit, TCondUnit, Q_unit, Kpr_unit, Ka_unit) and depth units are not normalized — the summary and interval heat-flow views are untouched. Worth a follow-up.
  • SPEC.md lists these views but gains no invariant for unit normalization here.

🤖 Generated with Claude Code

jirhiker and others added 3 commits August 6, 2026 10:51
The per-well geothermal views from d1e2f3a4b5c6 published legacy
temperatures unconverted and labelled them with max("TempUnit"), which
picks a unit lexically. A well with both C and F readings was labelled
'F' while values stayed mixed, and min/max aggregated across units.

Add public.nmw_temp_unit_code() and public.nmw_temp_to_c(), then rebuild
ogc_geothermal_wells_bht and ogc_geothermal_wells_temperature_profile
with min/max_bht_c, min/max_temp_c, a constant 'C' temp_unit, a
temp_unit_source listing the distinct source units, a temp_unit_mixed
flag, and an unconvertible_count. Raw columns and the series 'temp' key
are unchanged for compatibility; series objects gain temp_c and
temp_unit_source.

Function references are schema-qualified because materialized view
population runs with a restricted search_path, which breaks inlining of
an unqualified nested call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds coverage for the f3a1c2b4d5e6 helpers and views: unit-code
canonicalization across single-letter and spelled-out legacy forms,
F/C/K conversion plus the NULL-on-unknown-unit contract, presence of the
Celsius columns on both temperature views, and that temp_unit publishes
the constant 'C'.

Matview columns are read from pg_attribute because materialized views do
not appear in information_schema.columns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jirhiker
jirhiker merged commit c6cfb7e into staging Aug 7, 2026
3 checks passed
@jirhiker
jirhiker deleted the feat/geothermal-temp-unit-normalization branch August 7, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant