Full SOFA Score: Data Layer + Scoring Engine Glasgow Coma Scale: Data Layer + Scoring Engine
160 lines
5.5 KiB
C#
160 lines
5.5 KiB
C#
public static class SofaCalculator
|
|
{
|
|
public static readonly IReadOnlyList<string> SofaObservationCodes = new[]
|
|
{
|
|
"PAO2_MMHG", "FIO2_PCT", "PLATELET_K_UL", "BILIRUBIN_MG_DL",
|
|
"CREATININE_MG_DL", "URINE_OUTPUT_ML_H",
|
|
"SYSTOLIC_BP", "DIASTOLIC_BP", "SPO2", "SUPPLEMENTAL_O2"
|
|
};
|
|
|
|
public static readonly IReadOnlySet<string> SofaCodeSet =
|
|
new HashSet<string>(SofaObservationCodes);
|
|
|
|
public static bool IsSofaCode(string observationCode) =>
|
|
SofaCodeSet.Contains(observationCode);
|
|
|
|
// GCS components and gcs.scored also trigger SOFA re-score (CNS organ system)
|
|
public static bool TriggersRescore(string observationCode) =>
|
|
IsSofaCode(observationCode) || GcsCalculator.IsGcsCode(observationCode);
|
|
|
|
public static string CacheKey(Guid encounterId, string code) =>
|
|
$"sofa:{encounterId}:{code}";
|
|
|
|
// --- 1. Respiratory: PaO2/FiO2 ratio ---
|
|
public static int ScoreRespiratory(decimal? pao2, decimal? fio2, bool onMechanicalVent)
|
|
{
|
|
if (pao2 is null || fio2 is null || fio2 == 0) return 0;
|
|
var ratio = pao2.Value / (fio2.Value / 100m);
|
|
return ratio switch
|
|
{
|
|
>= 400 when onMechanicalVent => 0,
|
|
>= 400 => 0,
|
|
>= 300 => 1,
|
|
>= 200 => 2,
|
|
>= 100 when onMechanicalVent => 3,
|
|
>= 100 => 2,
|
|
_ when onMechanicalVent => 4,
|
|
_ => 3
|
|
};
|
|
}
|
|
|
|
// SpO2/FiO2 proxy when PaO2 unavailable (Rice et al. 2007)
|
|
public static int ScoreRespiratoryFromSpo2(decimal? spo2, decimal? fio2, bool onMechanicalVent)
|
|
{
|
|
if (spo2 is null || fio2 is null || fio2 == 0) return 0;
|
|
var sfRatio = spo2.Value / (fio2.Value / 100m);
|
|
return sfRatio switch
|
|
{
|
|
>= 315 => 0,
|
|
>= 235 => 1,
|
|
>= 150 => 2,
|
|
>= 67 when onMechanicalVent => 3,
|
|
>= 67 => 2,
|
|
_ when onMechanicalVent => 4,
|
|
_ => 3
|
|
};
|
|
}
|
|
|
|
// --- 2. Coagulation: Platelet count (k/µL) ---
|
|
public static int ScoreCoagulation(decimal? platelets)
|
|
{
|
|
if (platelets is null) return 0;
|
|
return platelets.Value switch
|
|
{
|
|
>= 150 => 0,
|
|
>= 100 => 1,
|
|
>= 50 => 2,
|
|
>= 20 => 3,
|
|
_ => 4
|
|
};
|
|
}
|
|
|
|
// --- 3. Liver: Bilirubin (mg/dL) ---
|
|
public static int ScoreLiver(decimal? bilirubin)
|
|
{
|
|
if (bilirubin is null) return 0;
|
|
return bilirubin.Value switch
|
|
{
|
|
< 1.2m => 0,
|
|
< 2.0m => 1,
|
|
< 6.0m => 2,
|
|
< 12.0m => 3,
|
|
_ => 4
|
|
};
|
|
}
|
|
|
|
// --- 4. Cardiovascular: MAP and vasopressor dose ---
|
|
public static int ScoreCardiovascular(decimal? map, VasopressorInfo? vasopressor)
|
|
{
|
|
if (vasopressor is not null)
|
|
{
|
|
return vasopressor.DrugName.ToUpperInvariant() switch
|
|
{
|
|
"DOPAMINE" when vasopressor.DoseUgKgMin > 15m => 4,
|
|
"EPINEPHRINE" when vasopressor.DoseUgKgMin > 0.1m => 4,
|
|
"NOREPINEPHRINE" when vasopressor.DoseUgKgMin > 0.1m => 4,
|
|
"DOPAMINE" when vasopressor.DoseUgKgMin > 5m => 3,
|
|
"EPINEPHRINE" => 3,
|
|
"NOREPINEPHRINE" => 3,
|
|
"DOPAMINE" => 2,
|
|
"DOBUTAMINE" => 2,
|
|
_ => 1
|
|
};
|
|
}
|
|
|
|
if (map is null) return 0;
|
|
return map.Value < 70m ? 1 : 0;
|
|
}
|
|
|
|
// --- 5. CNS: Glasgow Coma Scale (from Phase 25) ---
|
|
public static int ScoreCns(int? gcsTotal) =>
|
|
GcsCalculator.ToSofaCnsScore(gcsTotal ?? 15);
|
|
|
|
// --- 6. Renal: Creatinine (mg/dL) or urine output (mL/day) ---
|
|
public static int ScoreRenal(decimal? creatinine, decimal? urineOutputMlPerDay)
|
|
{
|
|
var creatScore = creatinine switch
|
|
{
|
|
null => 0,
|
|
< 1.2m => 0,
|
|
< 2.0m => 1,
|
|
< 3.5m => 2,
|
|
< 5.0m => 3,
|
|
_ => 4
|
|
};
|
|
|
|
var urineScore = urineOutputMlPerDay switch
|
|
{
|
|
null => 0,
|
|
< 200m => 4,
|
|
< 500m => 3,
|
|
_ => 0
|
|
};
|
|
|
|
return Math.Max(creatScore, urineScore);
|
|
}
|
|
|
|
public static SofaResult ComputeTotal(
|
|
int respiratory, int coagulation, int liver,
|
|
int cardiovascular, int cns, int renal) =>
|
|
new(
|
|
Total: respiratory + coagulation + liver + cardiovascular + cns + renal,
|
|
Respiratory: respiratory,
|
|
Coagulation: coagulation,
|
|
Liver: liver,
|
|
Cardiovascular: cardiovascular,
|
|
Cns: cns,
|
|
Renal: renal);
|
|
|
|
// Count organ systems with component data for baseline eligibility
|
|
public static int CountPopulatedOrganSystems(
|
|
bool hasRespiratory,
|
|
bool hasCoagulation,
|
|
bool hasLiver,
|
|
bool hasCardiovascular,
|
|
bool hasCns,
|
|
bool hasRenal) =>
|
|
new[] { hasRespiratory, hasCoagulation, hasLiver,
|
|
hasCardiovascular, hasCns, hasRenal }
|
|
.Count(hasData => hasData);
|
|
} |