Engineering and Project Management
 

A Model Validation Framework for Corrosion & Remaining-Life Models

Asset-integrity teams live with a recurring failure mode: a corrosion or remaining-life model arrives with impressive demo numbers, and then the conversation stops. What is the model actually doing? Is its rate within published literature for X65 carbon steel in atmospheric service? Does it hold up when you replay real survey history? Can your buyer’s engineer reproduce your numbers, not just trust a screenshot?

“Trust the vendor” is not a validation strategy. It’s how you get a surprise corrosion event three years after commissioning — and a very awkward conversation with whoever signs the integrity plan.

The Model Validation Framework (MVF) is a staged, evidence-producing pipeline that puts a degradation engine under test before it earns a seat in your integrity workflow:

StageQuestion answeredEvidence produced
S0 — Synthetic correctnessDoes the engine do arithmetic?Constant/noisy/step campaigns with exact expected outcomes; per-case pass/fail
S1 — Literature calibrationAre its rates defensible against published bands?Every zone × material × rate cell checked against cited literature, PASS/FAIL per cell
S2 — Survey-record backtestDoes it replay history correctly?Hold-out protocol: fit on survey history, predict the final survey; TTF hits, MAE, RMSE, Brier
S3 — Field campaign validationDoes it work on your licensed field data?Same pipeline, same guarantees — campaigns ingest through a versioned schema and score identically

Two design decisions matter more than any metric:

  1. The engine is imported as a pinned library. MVF does not fork, wrap, or “improve” vendor math. The report names the exact engine commit, so there is no ambiguity about what was tested.
  2. Every report carries a provenance block — framework version, git SHA, engine commit, dataset checksums, config hash, run stamp — and is deterministic for a given stamp. A buyer re-running the pipeline gets byte-identical numbers. That sentence is the whole business case in miniature.

The numbers we publish, not the ones we hope you believe

From the current pinned run:

  • S0: 33/34 synthetic campaigns pass (the one “fail” is a genuine 2σ tail draw that the harness correctly flags — correctness including the flagging is the point)
  • S1: 27/27 literature cells PASS across zones, materials, and min/typ/max rate bounds
  • S2: per-vessel backtest with hit-rate, MAE, RMSE and class-level Brier scores — plus honest signal-to-noise caveats where template data is the limiting factor, not the engine

A validation framework that can’t show a failure is just report generation. Ours renders failures in red on the dashboard and explains them in the artifact.

Built to be re-run by someone who doesn’t trust you

That’s deliberate. The operational surface is small enough to audit:

python -m mvf generate --cases 50 --seed 42   # S0
python -m mvf run --stage S1                   # S1
python -m mvf run --stage S2                    # S2
python -m mvf ingest --campaign <dir>           # S3 — licensed campaigns
python -m mvf report                            # master report
python -m mvf serve                             # dashboard at :8765

Backed by a 132-test suite covering golden scoring values, per-band engine conformance, the hold-out protocol, provenance determinism, and the ingest validator. CI-gateable, because reproducibility claims without a gate are marketing.

What this means for you

  • Procurement: vendor claims become reproducible artifacts instead of slide decks
  • Integrity teams: a standing, auditable answer to “how do we know the model is right?”
  • Regulators & clients: evidence trails with hashes, not adjectives

S3 is where this becomes part of your operating cycle: licensed field campaigns arrive through the NDA-ready data license template and the versioned campaign schema, run through the same scored pipeline, and land in the same provenance-stamped report. When the data is under contract, the evidence is a command away.

Bottom line

Model validation has three failure modes: it’s not done, it’s done once and forgotten, or it can’t be independently reproduced. MVF is designed against all three — staged evidence, deterministic artifacts, and a provenance block that makes “I ran it myself” the default posture of every stakeholder who sees it.

Model Validation Framework

MVF 1.0.2 · git fd39e57 · engine commit pinned-checkout (no git) · stamp unpinned-timestamp

S0 — synthetic correctness
33/50 pass
S1 — literature calibration
27/27 cells PASS
S2 — survey-record backtest
3 cases · 0 TTF hits · MAE 1.45 mm · 0 excluded

S0 · synthetic correctness

case_idtypetrue_final_mmpredicted_mmerror_mmcriterionresult
S0-C000constant7.787.78-0.0|err|<=0.1mmPASS
S0-C001noisy9.639.8190.189|err|<=2σ (0.3mm)PASS
S0-C002step3.9175.81.883invariant-onlyINVARIANT-OK
S0-C003constant9.299.290.0|err|<=0.1mmPASS
S0-C004noisy8.38.252-0.048|err|<=2σ (0.3mm)PASS
S0-C005step3.9654.70.735invariant-onlyINVARIANT-OK
S0-C006constant5.765.760.0|err|<=0.1mmPASS
S0-C007noisy9.8210.0120.192|err|<=2σ (0.3mm)PASS
S0-C008step9.5369.5360.0invariant-onlyINVARIANT-OK
S0-C009constant9.899.890.0|err|<=0.1mmPASS
S0-C010noisy5.675.636-0.034|err|<=2σ (0.3mm)PASS
S0-C011step7.0927.5660.474invariant-onlyINVARIANT-OK
S0-C012constant9.699.690.0|err|<=0.1mmPASS
S0-C013noisy13.0413.1350.095|err|<=2σ (0.3mm)PASS
S0-C014step8.2148.5140.3invariant-onlyINVARIANT-OK
S0-C015constant8.448.440.0|err|<=0.1mmPASS
S0-C016noisy6.736.696-0.034|err|<=2σ (0.3mm)PASS
S0-C017step2.8644.71.836invariant-onlyINVARIANT-OK
S0-C018constant9.229.220.0|err|<=0.1mmPASS
S0-C019noisy8.418.183-0.227|err|<=2σ (0.3mm)PASS
S0-C020step7.6757.7740.099invariant-onlyINVARIANT-OK
S0-C021constant8.978.970.0|err|<=0.1mmPASS
S0-C022noisy5.125.1410.021|err|<=2σ (0.3mm)PASS
S0-C023step4.7085.4150.707invariant-onlyINVARIANT-OK
S0-C024constant9.669.660.0|err|<=0.1mmPASS
S0-C025noisy8.238.3390.109|err|<=2σ (0.3mm)PASS
S0-C026step7.6888.430.742invariant-onlyINVARIANT-OK
S0-C027constant8.688.680.0|err|<=0.1mmPASS
S0-C028noisy9.129.106-0.014|err|<=2σ (0.3mm)PASS
S0-C029step9.5929.5920.0invariant-onlyINVARIANT-OK
S0-C030constant10.2110.210.0|err|<=0.1mmPASS
S0-C031noisy7.817.675-0.135|err|<=2σ (0.3mm)PASS
S0-C032step8.698.7460.056invariant-onlyINVARIANT-OK
S0-C033constant9.679.670.0|err|<=0.1mmPASS
S0-C034noisy9.379.490.12|err|<=2σ (0.3mm)PASS
S0-C035step8.7558.713-0.042invariant-onlyINVARIANT-OK
S0-C036constant6.186.18-0.0|err|<=0.1mmPASS
S0-C037noisy8.198.230.04|err|<=2σ (0.3mm)PASS
S0-C038step5.0485.50.452invariant-onlyINVARIANT-OK
S0-C039constant6.196.190.0|err|<=0.1mmPASS
S0-C040noisy9.859.9830.133|err|<=2σ (0.3mm)PASS
S0-C041step10.36410.510.146invariant-onlyINVARIANT-OK
S0-C042constant12.3712.37-0.0|err|<=0.1mmPASS
S0-C043noisy11.4111.135-0.275|err|<=2σ (0.3mm)PASS
S0-C044step9.6859.7280.043invariant-onlyINVARIANT-OK
S0-C045constant9.699.690.0|err|<=0.1mmPASS
S0-C046noisy11.7711.439-0.331|err|<=2σ (0.3mm)FAIL
S0-C047step8.0458.2550.21invariant-onlyINVARIANT-OK
S0-C048constant10.9210.920.0|err|<=0.1mmPASS
S0-C049noisy9.259.229-0.021|err|<=2σ (0.3mm)PASS

S1 · literature calibration

zonemateriallabelrate mm/yrpredicted_lossexpected_losspass
atmosphericcarbon_steelmin0.020.50.5PASS
atmosphericcarbon_steeltyp0.082.02.0PASS
atmosphericcarbon_steelmax0.25.05.0PASS
atmosphericstainless_316Lmin0.0050.1250.125PASS
atmosphericstainless_316Ltyp0.020.50.5PASS
atmosphericstainless_316Lmax0.051.251.25PASS
splashcarbon_steelmin0.12.52.5PASS
splashcarbon_steeltyp0.37.57.5PASS
splashcarbon_steelmax0.59.09.0PASS
splashstainless_316Lmin0.010.250.25PASS
splashstainless_316Ltyp0.030.750.75PASS
splashstainless_316Lmax0.082.02.0PASS
immersioncarbon_steelmin0.051.251.25PASS
immersioncarbon_steeltyp0.153.753.75PASS
immersioncarbon_steelmax0.49.09.0PASS
immersionstainless_316Lmin0.0050.1250.125PASS
immersionstainless_316Ltyp0.020.50.5PASS
immersionstainless_316Lmax0.061.51.5PASS
ballast_tankcarbon_steelmin0.051.251.25PASS
ballast_tankcarbon_steeltyp0.25.05.0PASS
ballast_tankcarbon_steelmax0.69.09.0PASS
subseacarbon_steelmin0.020.50.5PASS
subseacarbon_steeltyp0.082.02.0PASS
subseacarbon_steelmax0.25.05.0PASS
subseastainless_316Lmin0.0020.050.05PASS
subseastainless_316Ltyp0.010.250.25PASS
subseastainless_316Lmax0.030.750.75PASS

S2 · survey-record backtest

case_idrate_fit mm/yrpredicted_mmtrue_mmerror_mmpred_ttf_yrtrue_ttf_yrttf_hitpred_classtrue_class
S2-MV-ATLANTIC-VOYAGER2.0230319.94321.482-1.5398.3712.23MISSgoodgood
S2-MV-OCEAN-PIONEER1.1635118.45417.4451.00914.0312.55MISSgoodgood
S2-MV-SEA-EXPLORER0.8063914.55316.361-1.80813.0727.37MISSgoodgood

S3 · field validation

PENDING CONTRACT — no licensed field data held yet. Schema + NDA template ready; run python -m mvf ingest --campaign <dir> when campaigns arrive.