feat(iace): FMEA P1 — open methodology anchors + bp_iace_fmea_kb
P1 of the auto-FMEA build plan: establish the public-domain methodology foundation (no AIAG-VDA/SAE/IEC tables reproduced). - fmea_data_sources.go: MIL-STD-882E severity (Cat I-IV→1-10) + probability (A-F→1-10 with per-hour λ bands), OccurrenceFromRate(λp·α), SeverityForCategory, MIL-STD-1629A CriticalityCm = λp·α·β·t. Own 1-10 projection, government-anchored. - 4 versioned source docs (MIL-STD-1629A, MIL-STD-882E, NASA RCM, FMD-91/NPRD-91) ingested into the new RAG collection bp_iace_fmea_kb (whitelisted). - Tests for all scales/mappings/criticality (green). Next (P1 step 2): fetch FMD-91/NPRD-91 bulk λ/α tables from DTIC. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ var AllowedCollections = map[string]bool{
|
||||
"bp_iace_libraries": true,
|
||||
"bp_iace_accident_stats": true,
|
||||
"bp_iace_safety_kb": true,
|
||||
"bp_iace_fmea_kb": true,
|
||||
}
|
||||
|
||||
// SearchRequest represents a RAG search request.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Failure-mode taxonomy & reliability data — FMD-91 / NPRD-91 (public domain)
|
||||
|
||||
Open component→failure-mode taxonomy and failure-rate (λ) / mode-ratio (α)
|
||||
anchor for the IACE auto-FMEA. Ingested into `bp_iace_fmea_kb`. The bulk numeric
|
||||
tables are fetched from the DTIC PDFs in a follow-up P1 step; this doc fixes the
|
||||
sources + the per-component mode lists used to seed the gated library (P2).
|
||||
|
||||
## Sources
|
||||
- **FMD-91** (RAC/DTIC) — *Failure Mode / Mechanism Distributions* — modes +
|
||||
mechanisms with **% distribution (α)** per part class. **Public domain**
|
||||
("Approved for Public Release; Distribution Unlimited"). DTIC ADA259655.
|
||||
- **NPRD-91 / NPRD-95** (RAC/DTIC) — *Nonelectronic Parts Reliability Data* —
|
||||
field **failure rates λ** for mechanical / electromechanical parts. **Public
|
||||
domain.** DTIC ADA242083.
|
||||
- **MIL-HDBK-217F** (US DoD) — electronic-part failure-rate models λ. Public
|
||||
domain (obsolete 1995 but legally free).
|
||||
- **Attribution:** `Source: FMD-91 / NPRD-91 (RAC, DTIC), public domain`
|
||||
- **AVOID:** Quanterion NPRD-2023 / FMD-2016 / EPRD-2024 (proprietary reissues),
|
||||
OREDA (commercial), FIDES tables (© DGA — read-only), ISO 14224 (copyrighted).
|
||||
|
||||
## Failure modes by component class (open — FMD-91 + CC-BY-SA Wikipedia)
|
||||
| Component class | Typical failure modes |
|
||||
|---|---|
|
||||
| Bearings | fatigue spalling/pitting, wear, brinelling, seizure, contamination |
|
||||
| Gears | pitting, scuffing/scoring, tooth-fatigue fracture, wear |
|
||||
| Shafts / fasteners / springs | fatigue fracture, fretting, loosening, set/relaxation |
|
||||
| Seals / hoses / cylinders | leakage (external/internal), extrusion, hardening, rupture |
|
||||
| Valves / pumps | stuck-open, stuck-closed, leakage, fail-to-open/close, degraded flow |
|
||||
| Motors / contactors | winding short, open circuit, insulation breakdown, contact weld/stick |
|
||||
| Sensors / cabling | drift/bias, loss of signal, intermittent, open/short |
|
||||
|
||||
FMD-91 additionally gives the **% weighting (α)** of each mode per class — that
|
||||
is the mode-ratio in `Cm = λp·α·β·t` and lets the engine RANK modes rather than
|
||||
enumerate them flatly.
|
||||
|
||||
## How used in IACE
|
||||
- Taxonomy → seeds/expands the gated failure-mode library (P2).
|
||||
- λ (NPRD/217) × α (FMD-91) → mode rate → `OccurrenceFromRate` → 1–10 occurrence.
|
||||
- Each `source`/`license` tagged; allowlist rejects copyrighted/proprietary data.
|
||||
@@ -0,0 +1,39 @@
|
||||
# FMECA methodology — MIL-STD-1629A (US public domain)
|
||||
|
||||
Canonical, citable methodology source for the IACE auto-FMEA. Ingested into the
|
||||
core RAG collection `bp_iace_fmea_kb`.
|
||||
|
||||
## Source
|
||||
- **Source:** US DoD — MIL-STD-1629A, *Procedures for Performing a Failure Mode, Effects and Criticality Analysis* (cancelled 1998, still the de-facto FMECA reference)
|
||||
- **License:** US Government work — **public domain** (17 U.S.C. §105)
|
||||
- **Attribution:** `Source: MIL-STD-1629A (US DoD), public domain`
|
||||
- **Retrieved:** 2026-06 · **Ref:** everyspec / Wikipedia FMECA
|
||||
|
||||
## Procedure (qualitative + quantitative)
|
||||
1. Define the system, indenture levels and ground rules.
|
||||
2. For each item/function: list **failure modes** → **causes** → **local / next-higher / end effects**.
|
||||
3. Assign a **severity category** (I–IV, below).
|
||||
4. **Quantitative path (when failure-rate data exists):** compute the **modal
|
||||
criticality** `Cm = λp · α · β · t` — λp part failure rate, α failure-mode
|
||||
ratio (fraction of part failures in this mode), β conditional probability the
|
||||
mode causes the analysed effect, t operating time. Item criticality `Cr = Σ Cm`.
|
||||
5. **Qualitative path (Task 101, no λ data):** assign probability-of-occurrence
|
||||
levels A–E (Frequent → Extremely unlikely) instead of Cm.
|
||||
6. Plot items on a **severity × criticality matrix** — the open analogue of an
|
||||
action-priority ranking (no copyrighted S×O×D RPN table needed).
|
||||
|
||||
## Severity categories (MIL-STD-1629A / MIL-STD-882 lineage)
|
||||
| Cat | Name | Meaning |
|
||||
|---|---|---|
|
||||
| I | Catastrophic | May cause death or system loss |
|
||||
| II | Critical | Severe injury / major damage |
|
||||
| III | Marginal | Minor injury / minor damage |
|
||||
| IV | Minor (Negligible) | No injury; unscheduled maintenance/repair |
|
||||
|
||||
## How used in IACE
|
||||
- The **Cm formula** and the **severity × criticality matrix** are the public-domain
|
||||
criticality backbone (`CriticalityCm` in fmea_data_sources.go).
|
||||
- λp/α come from NPRD-91 / FMD-91 (public domain); β and t from the project context.
|
||||
- Severity resolves through the linked EN ISO 12100 hazard (safety-FMEA bridge).
|
||||
|
||||
No AIAG-VDA / SAE J1739 / IEC 60812 content is reproduced.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Severity & probability scales — MIL-STD-882E (US public domain)
|
||||
|
||||
Open S/O anchor for the IACE auto-FMEA. The 1–10 projection used in code is
|
||||
BreakPilot's own; these public-domain categories provide the defensible anchor
|
||||
(no copyrighted AIAG-VDA/SAE 1–10 table is used). Ingested into `bp_iace_fmea_kb`.
|
||||
|
||||
## Source
|
||||
- **Source:** US DoD — MIL-STD-882E, *System Safety* (Distribution Statement A, approved for public release)
|
||||
- **License:** US Government work — **public domain**
|
||||
- **Attribution:** `Source: MIL-STD-882E (US DoD), public domain`
|
||||
- **Retrieved:** 2026-06 · **Ref:** DLA QuickSearch (ident 36027)
|
||||
|
||||
## Table I — Severity (→ our 1–10 S)
|
||||
| Cat | Severity | Threshold | our S |
|
||||
|---|---|---|---|
|
||||
| I | Catastrophic | Death, permanent total disability, irreversible severe environmental damage | 10 |
|
||||
| II | Critical | Permanent partial disability; hospitalisation of multiple persons | 7 |
|
||||
| III | Marginal | Injury/illness with lost work day(s) | 4 |
|
||||
| IV | Negligible | Minor injury, no lost work day | 2 |
|
||||
|
||||
## Table II — Probability (individual item) (→ our 1–10 O)
|
||||
| Lvl | Name | Qualitative | rate band (/h, our calibration) | our O |
|
||||
|---|---|---|---|---|
|
||||
| A | Frequent | likely to occur often | ≥ 1e-1 | 10 |
|
||||
| B | Probable | will occur several times | 1e-2 … 1e-1 | 8 |
|
||||
| C | Occasional | likely to occur sometime | 1e-3 … 1e-2 | 6 |
|
||||
| D | Remote | unlikely but possible | 1e-6 … 1e-3 | 4 |
|
||||
| E | Improbable | so unlikely ~ may not occur | < 1e-6 | 2 |
|
||||
| F | Eliminated | incapable of occurrence | 0 | 1 |
|
||||
|
||||
## Detection (no government scale)
|
||||
Detection is an AIAG/SAE concept with no public-domain table. IACE defines its
|
||||
OWN 1–10 detection scale grounded on the generic **diagnostic-coverage** concept
|
||||
(1 = built-in diagnostic always catches it … 10 = latent, no detection). The
|
||||
IEC-61508/13849 DC tables are copyrighted and are NOT reproduced — only the
|
||||
concept (in our own words) is used.
|
||||
|
||||
## How used in IACE
|
||||
`MILStd882Severity` / `MILStd882Probability` / `OccurrenceFromRate` /
|
||||
`SeverityForCategory` in fmea_data_sources.go implement these bands. Severity is
|
||||
preferably resolved through the linked ISO 12100 hazard; occurrence from λp·α.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Failure-consequence & maintenance decision logic — NASA RCM (public domain)
|
||||
|
||||
Drives the IACE auto-FMEA "recommended action" layer and the safety-priority
|
||||
rule. Ingested into `bp_iace_fmea_kb`.
|
||||
|
||||
## Source
|
||||
- **Source:** NASA — NPR 8831.2F Ch.7 (Reliability-Centered Maintenance) and NASA GSFC-HDBK-8004 (FMEA & Risk Assessment)
|
||||
- **License:** US Government work — **public domain** ("cleared for public accessibility")
|
||||
- **Attribution:** `Source: NASA NPR 8831.2F / GSFC-HDBK-8004, public domain`
|
||||
- **Retrieved:** 2026-06 · **Ref:** nodis3.gsfc.nasa.gov / standards.nasa.gov
|
||||
|
||||
## Safety-consequence priority (key rule)
|
||||
A failure with a **safety** consequence is prioritised **regardless of how rare
|
||||
it is** — "safety shall be ensured at any cost; thereafter cost-effectiveness
|
||||
becomes the criterion." So in a SAFETY FMEA a single Catastrophic mode matters
|
||||
even at low occurrence — severity dominates the ranking.
|
||||
|
||||
## Failure definition (broad)
|
||||
"Any unsatisfactory condition" — loss of FUNCTION **or** loss of QUALITY/
|
||||
acceptable performance — not only complete breakdown. (Captures drift,
|
||||
degradation, intermittent.)
|
||||
|
||||
## Recommended-action decision logic (RCM)
|
||||
| Failure character | Recommended task |
|
||||
|---|---|
|
||||
| Gives advance warning / measurable degradation | **Condition-based / predictive** monitoring |
|
||||
| Age/wear-related, predictable | **Preventive / scheduled** replacement |
|
||||
| Random, low consequence | **Run-to-failure** (corrective) |
|
||||
| Hidden / no effective task | **Redesign** or add **redundancy / detection** |
|
||||
|
||||
This maps onto the IACE 3-step measure hierarchy (inherently safe design →
|
||||
safeguarding → information) for the FMEA's control/action column.
|
||||
|
||||
## How used in IACE
|
||||
- Safety-priority rule overrides pure Cm ranking when the linked ISO 12100 hazard
|
||||
is high-severity (the safety-FMEA bridge).
|
||||
- The decision table seeds the recommended-action suggestions per failure mode.
|
||||
|
||||
Concepts only — IEC 61508/ISO 13849 (SIL/PL, DC, β-factor tables) are copyrighted
|
||||
and NOT reproduced; diagnostic-coverage / common-cause are used as generic ideas.
|
||||
@@ -0,0 +1,99 @@
|
||||
package iace
|
||||
|
||||
// Open, public-domain methodology anchors for the auto-FMEA (P1 of the FMEA
|
||||
// build plan). Every scale here is anchored on US-Government PUBLIC-DOMAIN
|
||||
// sources — MIL-STD-882E (system-safety severity/probability) and MIL-STD-1629A
|
||||
// (FMECA criticality). The 1-10 projection is BreakPilot's OWN scale; NO
|
||||
// AIAG-VDA / SAE J1739 / IEC 60812 table is reproduced (those are copyrighted).
|
||||
// Failure-rate (λ) and failure-mode-ratio (α) values come later from NPRD-91 /
|
||||
// FMD-91 (RAC, public domain). See project_fmea_build_plan / opensource-fmea-sources.
|
||||
|
||||
// FMEADataSourcesNote is the audit/attribution line for the FMEA methodology.
|
||||
const FMEADataSourcesNote = "FMEA-Methodik + Skalen verankert an US-Gov Public Domain " +
|
||||
"(MIL-STD-1629A FMECA-Kritikalität, MIL-STD-882E Severity/Probability, NASA RCM). " +
|
||||
"Ausfallraten/Mode-Verteilungen aus NPRD-91/FMD-91 (RAC, Public Domain). " +
|
||||
"Das 1-10-Mapping ist BreakPilot-eigen; KEINE AIAG-VDA/SAE/IEC-Tabelle reproduziert."
|
||||
|
||||
// FMEASeverityBand maps a MIL-STD-882E Table-I severity CATEGORY to our 1-10 S.
|
||||
type FMEASeverityBand struct {
|
||||
Category string `json:"category"` // "I".."IV"
|
||||
LabelDE string `json:"label_de"`
|
||||
Desc string `json:"desc"` // public-domain threshold (injury/$)
|
||||
Severity int `json:"severity"` // our 1-10 projection
|
||||
}
|
||||
|
||||
// MILStd882Severity returns the four MIL-STD-882E severity categories, ordered
|
||||
// most→least severe, with our 1-10 projection. (882E is US-Gov public domain.)
|
||||
func MILStd882Severity() []FMEASeverityBand {
|
||||
return []FMEASeverityBand{
|
||||
{Category: "I", LabelDE: "Katastrophal", Desc: "Tod, dauerhafte Vollinvalidität oder irreversibler schwerer Umweltschaden", Severity: 10},
|
||||
{Category: "II", LabelDE: "Kritisch", Desc: "Dauerhafte Teilinvalidität oder Krankenhausaufenthalt mehrerer Personen", Severity: 7},
|
||||
{Category: "III", LabelDE: "Marginal", Desc: "Verletzung/Erkrankung mit Ausfalltag(en)", Severity: 4},
|
||||
{Category: "IV", LabelDE: "Vernachlässigbar", Desc: "Geringfügige Verletzung ohne Ausfalltag", Severity: 2},
|
||||
}
|
||||
}
|
||||
|
||||
// FMEAOccurrenceBand maps a MIL-STD-882E Table-II probability LEVEL to our 1-10
|
||||
// O. LambdaMax is the upper bound (failures per operating hour) we use to bin a
|
||||
// computed mode rate; the per-hour calibration is ours, anchored on 882E order.
|
||||
type FMEAOccurrenceBand struct {
|
||||
Level string `json:"level"` // "A".."F"
|
||||
LabelDE string `json:"label_de"`
|
||||
LambdaMax float64 `json:"lambda_max"` // inclusive upper bound, /h (0 ⇒ eliminated)
|
||||
Occurrence int `json:"occurrence"` // our 1-10 projection
|
||||
}
|
||||
|
||||
// MILStd882Probability returns the six MIL-STD-882E probability levels (individual
|
||||
// item), ordered most→least frequent, with our 1-10 projection + a per-hour
|
||||
// failure-rate band used by OccurrenceFromRate.
|
||||
func MILStd882Probability() []FMEAOccurrenceBand {
|
||||
return []FMEAOccurrenceBand{
|
||||
{Level: "A", LabelDE: "Häufig", LambdaMax: 1, Occurrence: 10}, // ≥ 1e-1
|
||||
{Level: "B", LabelDE: "Wahrscheinlich", LambdaMax: 1e-1, Occurrence: 8}, // 1e-2 .. 1e-1
|
||||
{Level: "C", LabelDE: "Gelegentlich", LambdaMax: 1e-2, Occurrence: 6}, // 1e-3 .. 1e-2
|
||||
{Level: "D", LabelDE: "Selten", LambdaMax: 1e-3, Occurrence: 4}, // 1e-6 .. 1e-3
|
||||
{Level: "E", LabelDE: "Unwahrscheinlich", LambdaMax: 1e-6, Occurrence: 2},
|
||||
{Level: "F", LabelDE: "Ausgeschlossen", LambdaMax: 0, Occurrence: 1},
|
||||
}
|
||||
}
|
||||
|
||||
// OccurrenceFromRate maps a mode failure rate (λp·α, failures per operating hour)
|
||||
// onto the 1-10 occurrence axis via the MIL-STD-882E probability bands. A rate
|
||||
// ≤ 0 is "eliminated" (1); anything ≥ 0.1/h saturates at "frequent" (10).
|
||||
func OccurrenceFromRate(modeRatePerHour float64) int {
|
||||
if modeRatePerHour <= 0 {
|
||||
return 1
|
||||
}
|
||||
bands := MILStd882Probability()
|
||||
// bands[0] (A) has the highest threshold; walk most→least frequent and take
|
||||
// the first band whose lower edge the rate clears.
|
||||
for i := 0; i < len(bands)-1; i++ {
|
||||
lowerEdge := bands[i+1].LambdaMax // edge between band i and i+1
|
||||
if modeRatePerHour > lowerEdge {
|
||||
return bands[i].Occurrence
|
||||
}
|
||||
}
|
||||
return bands[len(bands)-2].Occurrence // E (improbable) for tiny positive rates
|
||||
}
|
||||
|
||||
// SeverityForCategory returns our 1-10 severity for a MIL-STD-882E category
|
||||
// ("I".."IV"); 0 if unknown.
|
||||
func SeverityForCategory(category string) int {
|
||||
for _, b := range MILStd882Severity() {
|
||||
if b.Category == category {
|
||||
return b.Severity
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CriticalityCm computes the MIL-STD-1629A modal criticality
|
||||
// Cm = λp · α · β · t, where λp = part failure rate (/h), α = failure-mode ratio
|
||||
// (fraction of part failures in this mode), β = conditional probability the mode
|
||||
// produces the analysed effect, t = operating hours. Fully public-domain method.
|
||||
func CriticalityCm(lambdaP, alpha, beta, operatingHours float64) float64 {
|
||||
if lambdaP < 0 || alpha < 0 || beta < 0 || operatingHours < 0 {
|
||||
return 0
|
||||
}
|
||||
return lambdaP * alpha * beta * operatingHours
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package iace
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMILStd882Severity_OrderedAndComplete(t *testing.T) {
|
||||
bands := MILStd882Severity()
|
||||
if len(bands) != 4 {
|
||||
t.Fatalf("expected 4 severity categories, got %d", len(bands))
|
||||
}
|
||||
wantCat := []string{"I", "II", "III", "IV"}
|
||||
prev := 11
|
||||
for i, b := range bands {
|
||||
if b.Category != wantCat[i] {
|
||||
t.Errorf("band %d: category %q, want %q", i, b.Category, wantCat[i])
|
||||
}
|
||||
if b.LabelDE == "" || b.Desc == "" {
|
||||
t.Errorf("band %s: empty label/desc", b.Category)
|
||||
}
|
||||
if b.Severity < 1 || b.Severity > 10 {
|
||||
t.Errorf("band %s: severity %d out of 1-10", b.Category, b.Severity)
|
||||
}
|
||||
if b.Severity >= prev {
|
||||
t.Errorf("severity not strictly descending at %s (%d >= %d)", b.Category, b.Severity, prev)
|
||||
}
|
||||
prev = b.Severity
|
||||
}
|
||||
}
|
||||
|
||||
func TestMILStd882Probability_OrderedBands(t *testing.T) {
|
||||
bands := MILStd882Probability()
|
||||
if len(bands) != 6 {
|
||||
t.Fatalf("expected 6 probability levels, got %d", len(bands))
|
||||
}
|
||||
prevOcc, prevLambda := 11, math.Inf(1)
|
||||
for _, b := range bands {
|
||||
if b.Occurrence < 1 || b.Occurrence > 10 {
|
||||
t.Errorf("%s: occurrence %d out of 1-10", b.Level, b.Occurrence)
|
||||
}
|
||||
if b.Occurrence >= prevOcc {
|
||||
t.Errorf("occurrence not descending at %s", b.Level)
|
||||
}
|
||||
if b.LambdaMax >= prevLambda {
|
||||
t.Errorf("lambda_max not descending at %s", b.Level)
|
||||
}
|
||||
prevOcc, prevLambda = b.Occurrence, b.LambdaMax
|
||||
}
|
||||
}
|
||||
|
||||
func TestOccurrenceFromRate(t *testing.T) {
|
||||
cases := []struct {
|
||||
rate float64
|
||||
want int
|
||||
}{
|
||||
{1.0, 10}, // A frequent
|
||||
{0.05, 8}, // B probable (1e-2..1e-1)
|
||||
{0.005, 6}, // C occasional (1e-3..1e-2)
|
||||
{1e-4, 4}, // D remote (1e-6..1e-3)
|
||||
{1e-8, 2}, // E improbable (<1e-6)
|
||||
{0, 1}, // F eliminated
|
||||
{-5, 1}, // guard
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := OccurrenceFromRate(c.rate); got != c.want {
|
||||
t.Errorf("OccurrenceFromRate(%g) = %d, want %d", c.rate, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeverityForCategory(t *testing.T) {
|
||||
if got := SeverityForCategory("I"); got != 10 {
|
||||
t.Errorf("Cat I = %d, want 10", got)
|
||||
}
|
||||
if got := SeverityForCategory("IV"); got != 2 {
|
||||
t.Errorf("Cat IV = %d, want 2", got)
|
||||
}
|
||||
if got := SeverityForCategory("Z"); got != 0 {
|
||||
t.Errorf("unknown cat = %d, want 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCriticalityCm(t *testing.T) {
|
||||
// Cm = λp·α·β·t = 1e-5 · 0.3 · 1.0 · 1000 = 3e-3
|
||||
got := CriticalityCm(1e-5, 0.3, 1.0, 1000)
|
||||
if math.Abs(got-3e-3) > 1e-9 {
|
||||
t.Errorf("Cm = %g, want 3e-3", got)
|
||||
}
|
||||
if CriticalityCm(-1, 0.3, 1, 1000) != 0 {
|
||||
t.Error("negative input should yield 0")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user