0bf9c54d27
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 5s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 15s
CI / go-lint (push) Has been skipped
CI / test-go (push) Failing after 38s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
Fine-Kinney (Fine 1971 / Kinney-Wiruth 1976): Risk = Probability x Exposure x Consequence — a PUBLISHED, freely-usable method (not a DIN/Beuth/ISO standard), widely used incl. CE-marking. Gives the professional a second, US-recognized model alongside the EN-62061-style one; German exporters get both for free and adjust with their own licensed norm data. risk_fine_kinney.go: SuggestFineKinney derives justified P/E/C from public anchors (ESAW frequency -> P, lifecycle -> E, de-biased severity -> C on the Fine-Kinney consequence scale) + ComputeFineKinney(p,e,c) so the professional can override with his own values. No norm table stored. GT benchmark (rank concordance vs the professional): Fine-Kinney 75.4% — beats the EN-62061-style model (69.3%) and the raw engine (57%). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
141 lines
5.1 KiB
Go
141 lines
5.1 KiB
Go
package iace
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Fine-Kinney risk model — BreakPilot's deterministic, citable risk backbone.
|
|
//
|
|
// Method: W.T. Fine, "Mathematical Evaluations for Controlling Hazards"
|
|
// (1971, US Naval Ordnance Laboratory report) and G.F. Kinney & A.D. Wiruth,
|
|
// "Practical Risk Analysis for Safety Management" (1976). Risk = P x E x C.
|
|
// It is a PUBLISHED, freely-usable method — not a copyrighted DIN/Beuth/ISO
|
|
// standard — and is widely used in industry (incl. CE-marking risk analysis).
|
|
//
|
|
// We derive SUGGESTED P/E/C values deterministically from PUBLIC, permissively
|
|
// licensed sources (Eurostat ESAW frequencies, NIOSH/OSHA/MIL-STD-882 injury
|
|
// outcomes — see DATA_SOURCES.md), each with a plain-language justification.
|
|
// The professional then ADJUSTS them (e.g. from his own licensed DIN/Beuth
|
|
// data) — the tool only supplies the formula and computes. We never reproduce
|
|
// norm tables.
|
|
|
|
// FKParam is a suggested Fine-Kinney parameter value plus why we chose it.
|
|
type FKParam struct {
|
|
Value float64 `json:"value"`
|
|
Justification string `json:"justification"`
|
|
}
|
|
|
|
// FKAssessment is a full suggested Fine-Kinney assessment for one hazard.
|
|
type FKAssessment struct {
|
|
Probability FKParam `json:"probability"` // P: 0.1 .. 10
|
|
Exposure FKParam `json:"exposure"` // E: 0.5 .. 10
|
|
Consequence FKParam `json:"consequence"` // C: 1 .. 100
|
|
Score float64 `json:"score"` // R = P * E * C
|
|
Band string `json:"band"` // Fine-Kinney risk band label
|
|
Action string `json:"action"` // suggested urgency of action
|
|
}
|
|
|
|
// fkProbabilityByMode maps a contact mode to a Fine-Kinney probability value,
|
|
// anchored to the ESAW relative frequency of that injury mechanism.
|
|
var fkProbabilityByMode = map[string]float64{
|
|
"impact_stationary": 6, "crushing": 6, "struck_by": 6, "ergonomic": 6,
|
|
"cutting": 3, "entanglement": 3, "shearing": 3, "electrical": 3,
|
|
"thermal": 3, "fall": 3,
|
|
"chemical": 1, "pressure_burst": 1, "radiation": 0.5,
|
|
}
|
|
|
|
// fkConsequenceFromSeverity maps our pattern-specific, de-biased severity (1-5)
|
|
// onto the published Fine-Kinney consequence scale. Using the per-hazard
|
|
// severity (not a coarse mode constant) preserves the ranking signal.
|
|
// 1=first aid, 3=disability, 7=serious injury, 15=a fatality, 40=multiple.
|
|
func fkConsequenceFromSeverity(s int) float64 {
|
|
switch {
|
|
case s >= 5:
|
|
return 40
|
|
case s == 4:
|
|
return 15
|
|
case s == 3:
|
|
return 7
|
|
case s == 2:
|
|
return 3
|
|
default:
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// SuggestFineKinney builds a justified Fine-Kinney assessment from public-data
|
|
// anchors. Inputs are the hazard's categories, scenario text and the project's
|
|
// lifecycle phases. Values are SUGGESTIONS the professional adjusts.
|
|
func SuggestFineKinney(cats []string, scenario string, lifecyclePhases []string, defaultSeverity int) FKAssessment {
|
|
mode := DetectContactMode(cats, scenario)
|
|
|
|
p := 3.0
|
|
if v, ok := fkProbabilityByMode[mode]; ok {
|
|
p = v
|
|
}
|
|
s := EstimateSeverity(cats, scenario, defaultSeverity)
|
|
c := fkConsequenceFromSeverity(s)
|
|
e := fkExposure(lifecyclePhases)
|
|
|
|
modeLabel := mode
|
|
if modeLabel == "" {
|
|
modeLabel = "unbestimmt"
|
|
}
|
|
a := FKAssessment{
|
|
Probability: FKParam{p, "Eintrittswahrscheinlichkeit aus ESAW-Haeufigkeit der Kontaktart '" + modeLabel + "'"},
|
|
Exposure: FKParam{e.value, e.reason},
|
|
Consequence: FKParam{c, fmt.Sprintf("Konsequenz aus Schwere-Einstufung S%d (NIOSH/OSHA/MIL-STD-882-Verletzungsbild)", s)},
|
|
}
|
|
a.Score, a.Band, a.Action = ComputeFineKinney(p, e.value, c)
|
|
return a
|
|
}
|
|
|
|
type fkExp struct {
|
|
value float64
|
|
reason string
|
|
}
|
|
|
|
// fkExposure maps the active lifecycle phases to a Fine-Kinney exposure value
|
|
// (how often a person is exposed to the task).
|
|
func fkExposure(phases []string) fkExp {
|
|
has := func(needle string) bool {
|
|
for _, p := range phases {
|
|
if strings.Contains(p, needle) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
switch {
|
|
case has("normal_operation") || has("auto_operation") || has("manual_operation"):
|
|
return fkExp{6, "Exposition: Normalbetrieb (taeglich/dauernd)"}
|
|
case has("setup") || has("maintenance") || has("cleaning") || has("changeover"):
|
|
return fkExp{3, "Exposition: Einricht-/Wartungs-/Reinigungstaetigkeit (gelegentlich)"}
|
|
case len(phases) > 0:
|
|
return fkExp{1, "Exposition: seltene Lebensphase (wenige Male pro Jahr)"}
|
|
default:
|
|
return fkExp{3, "Exposition: angenommen gelegentlich (keine Lebensphase angegeben)"}
|
|
}
|
|
}
|
|
|
|
// ComputeFineKinney returns the Fine-Kinney risk score (P*E*C) and the
|
|
// published risk band + suggested action urgency. The professional may pass
|
|
// his own adjusted P/E/C here (e.g. derived from his licensed DIN/Beuth data) —
|
|
// the tool only computes; it stores no norm table.
|
|
func ComputeFineKinney(p, e, c float64) (score float64, band, action string) {
|
|
score = p * e * c
|
|
switch {
|
|
case score > 400:
|
|
return score, "sehr hoch", "Taetigkeit einstellen / sofortige Massnahmen"
|
|
case score > 200:
|
|
return score, "hoch", "sofortige Sanierung erforderlich"
|
|
case score > 70:
|
|
return score, "wesentlich", "Sanierung erforderlich"
|
|
case score > 20:
|
|
return score, "moeglich", "Aufmerksamkeit, Massnahmen planen"
|
|
default:
|
|
return score, "gering", "ggf. akzeptabel, beobachten"
|
|
}
|
|
}
|