code wiki / (root) / nx_water_quality.nx

nx_water_quality.nx source

↩ module page · 164 lines · 5365 B

1// nx_water_quality.nx -- WATER SYSTEMS / TASTE + HEALTH + SAFETY. Scores a 2// water for TASTE (WHO total-dissolved-solids palatability + mineral balance 3// + pH) and HEALTH (WHO desirable calcium + magnesium), and gates SAFETY by 4// construction: a NEVER-POISON refusal if any regulated contaminant exceeds 5// its EPA/WHO limit or bacteria are present. 6// 7// INTEGER-EXACT. TDS + minerals in mg/L; pH x10; trace contaminants in 8// their regulatory units (lead/arsenic ppb, nitrate mg/L-as-N, PFAS ppt, 9// THM ppb). Grounded limits: 10// lead <= 15 ppb (EPA action level, Lead and Copper Rule) 11// arsenic <= 10 ppb; nitrate-N <= 10 mg/L; THM(total) <= 80 ppb (EPA MCL) 12// PFOA+PFOS <= 4 ppt (EPA 2024 MCL); bacteria (E. coli/coliform) must be 0 13// Taste bands (WHO GDWQ): <300 excellent, 300-600 good, 600-900 fair, 14// 900-1200 poor, >1200 unacceptable; very low TDS (<50) tastes flat. 15// Health minerals (WHO desalination remineralization guidance): Ca >= 20, 16// Mg >= 10 mg/L desirable (optimum Ca 40-80, Mg 20-30). 17// 18// THE exceed: taste + health scored AND a never-poison safety gate that 19// STRUCTURALLY refuses lead/arsenic/nitrate/PFAS/bacteria/THM -- a TDS meter 20// reads a number, this decides drinkable / delicious / healthful. 21// 22// grounded: who_gdwq_tds_palatability + epa_national_primary_drinking_water 23// + epa_2024_pfas_mcl + who_remineralization_calcium_magnesium 24// genealogy_id: water_quality_taste_health + nishi_never_poison 25 26import "nx_syscalls.nx" 27 28// ===== Regulated safety limits ==================================== 29 30const WQ_LEAD_PPB: i64 = 15 31const WQ_ARSENIC_PPB: i64 = 10 32const WQ_NITRATE_MGL: i64 = 10 33const WQ_PFAS_PPT: i64 = 4 34const WQ_THM_PPB: i64 = 80 35 36// Health-desirable minerals (mg/L). 37const WQ_CA_MIN: i64 = 20 38const WQ_MG_MIN: i64 = 10 39 40// ===== Sealed enums =============================================== 41 42const WQ_INSIPID: i64 = 0 // TDS < 50 (flat) 43const WQ_EXCELLENT: i64 = 1 // < 300 44const WQ_GOOD: i64 = 2 // 300-600 45const WQ_FAIR: i64 = 3 // 600-900 46const WQ_POOR: i64 = 4 // 900-1200 47const WQ_UNACCEPTABLE: i64 = 5 // > 1200 48 49const WQ_SAFE: i64 = 0 50const WQ_REFUSED_LEAD: i64 = 1 51const WQ_REFUSED_ARSENIC: i64 = 2 52const WQ_REFUSED_NITRATE: i64 = 3 53const WQ_REFUSED_PFAS: i64 = 4 54const WQ_REFUSED_BACTERIA: i64 = 5 55const WQ_REFUSED_THM: i64 = 6 56 57// ===== Struct: NxWaterSample ====================================== 58 59struct NxWaterSample { 60 tds: i64, 61 ca: i64, 62 mg: i64, 63 na: i64, 64 hco3: i64, 65 sulfate: i64, 66 chloride: i64, 67 ph_x10: i64, 68 lead_ppb: i64, 69 arsenic_ppb: i64, 70 nitrate_mgl: i64, 71 pfas_ppt: i64, 72 bacteria: i64, 73 thm_ppb: i64, 74} 75 76func nx_water_sample_new() -> *NxWaterSample { 77 let s: *NxWaterSample = (sys_mmap(128)) as *NxWaterSample 78 s.tds = 0 79 s.ca = 0 80 s.mg = 0 81 s.na = 0 82 s.hco3 = 0 83 s.sulfate = 0 84 s.chloride = 0 85 s.ph_x10 = 70 86 s.lead_ppb = 0 87 s.arsenic_ppb = 0 88 s.nitrate_mgl = 0 89 s.pfas_ppt = 0 90 s.bacteria = 0 91 s.thm_ppb = 0 92 return s 93} 94 95// ===== Taste ====================================================== 96 97func wq_tds_taste_class(tds: i64) -> i64 { 98 if tds < 50 { return WQ_INSIPID } 99 if tds < 300 { return WQ_EXCELLENT } 100 if tds < 600 { return WQ_GOOD } 101 if tds < 900 { return WQ_FAIR } 102 if tds < 1200 { return WQ_POOR } 103 return WQ_UNACCEPTABLE 104} 105 106// TDS taste points (max 60): optimum band 150-400. 107func wq_tds_points(tds: i64) -> i64 { 108 if tds < 50 { return 10 } 109 if tds < 150 { return 40 } 110 if tds <= 400 { return 60 } 111 if tds <= 600 { return 45 } 112 if tds <= 900 { return 30 } 113 if tds <= 1200 { return 15 } 114 return 0 115} 116 117func wq_ca_points(ca: i64) -> i64 { 118 if ca >= 40 { if ca <= 100 { return 13 } return 8 } 119 if ca >= 20 { return 8 } 120 return 0 121} 122 123func wq_mg_points(mg: i64) -> i64 { 124 if mg >= 20 { if mg <= 50 { return 12 } return 7 } 125 if mg >= 10 { return 7 } 126 return 0 127} 128 129func wq_ph_points(ph_x10: i64) -> i64 { 130 if ph_x10 >= 70 { if ph_x10 <= 80 { return 15 } } 131 if ph_x10 >= 65 { if ph_x10 <= 85 { return 10 } } 132 if ph_x10 >= 60 { if ph_x10 <= 90 { return 5 } } 133 return 0 134} 135 136// Composite taste score 0-100. 137func wq_taste_score(s: *NxWaterSample) -> i64 { 138 return wq_tds_points(s.tds) + wq_ca_points(s.ca) + wq_mg_points(s.mg) + wq_ph_points(s.ph_x10) 139} 140 141// ===== Safety (never-poison) ====================================== 142 143func wq_safety_admit(s: *NxWaterSample) -> i64 { 144 if s.lead_ppb > WQ_LEAD_PPB { return WQ_REFUSED_LEAD } 145 if s.arsenic_ppb > WQ_ARSENIC_PPB { return WQ_REFUSED_ARSENIC } 146 if s.nitrate_mgl > WQ_NITRATE_MGL { return WQ_REFUSED_NITRATE } 147 if s.pfas_ppt > WQ_PFAS_PPT { return WQ_REFUSED_PFAS } 148 if s.bacteria != 0 { return WQ_REFUSED_BACTERIA } 149 if s.thm_ppb > WQ_THM_PPB { return WQ_REFUSED_THM } 150 return WQ_SAFE 151} 152 153func wq_is_potable(s: *NxWaterSample) -> i64 { 154 if wq_safety_admit(s) == WQ_SAFE { return 1 } 155 return 0 156} 157 158// Healthful = potable AND meets the WHO desirable mineral minimums. 159func wq_is_healthful(s: *NxWaterSample) -> i64 { 160 if wq_is_potable(s) == 0 { return 0 } 161 if s.ca < WQ_CA_MIN { return 0 } 162 if s.mg < WQ_MG_MIN { return 0 } 163 return 1 164}