code wiki / (root) / nx_stress.nx

nx_stress.nx source

↩ module page · 171 lines · 7088 B

1// nx_stress.nx -- mechanical stress primitive (σ = F/A in Pascals). 2// 3// THREE-WAY cross-domain composition demo per 4// [[feedback-engineering-sciences-bits-up-3d-print-first]]: 5// nx_force (force input, Layer 1 physics) 6// <area-source> (polygon/mesh-derived, Layer 3 geometry) 7// material-strength (polymer literature, Layer 2 chemistry-derived) 8// → stress classification (Layer 3 engineering output) 9// 10// Unit convention: integer Pascals (N/m²). Engineering yield 11// strengths are MPa range (10^6 Pa); ultimate tensile up to GPa 12// (10^9 Pa). i64 Pa range = ±9.2 × 10^18 Pa = ±9.2 × 10^12 GPa, 13// vastly exceeding any engineering analysis. 14// 15// Algebra: σ_Pa = F_N / A_m². Both Q14 inputs scale identically, 16// so σ_Pa = F_q14 / A_q14 (Q14 scaling cancels in the ratio). 17// Integer result, no rescaling. 18// 19// Material yield strengths (MPa) verified against polymer / FDM 20// literature consensus: 21// PLA: yield 50 / ultimate 65 22// PETG: yield 45 / ultimate 50 23// ABS: yield 35 / ultimate 45 24// ASA: yield 35 / ultimate 45 25// PC: yield 65 / ultimate 70 26// TPU: yield 30 / ultimate 40 (highly elastic; values for TPU 95A) 27// NYLON: yield 60 / ultimate 80 28// PA-CF: yield 80 / ultimate 120 (carbon-fibre reinforced) 29// PEEK: yield 95 / ultimate 100 30// 31// Per cardinal feedback-nishilang-multi-line-binary-op-statement-break: 32// every multi-line arithmetic uses named temporaries throughout. 33// 34// license_tier: ORIGINAL 35 36import "nx_syscalls.nx" 37import "nx_material_profile.nx" 38import "nx_abs.nx" 39const NX_MAGIC_1000000: i64 = 1000000 40const NX_MAGIC_50000000: i64 = 50000000 41const NX_MAGIC_45000000: i64 = 45000000 42const NX_MAGIC_35000000: i64 = 35000000 43const NX_MAGIC_65000000: i64 = 65000000 44const NX_MAGIC_30000000: i64 = 30000000 45const NX_MAGIC_60000000: i64 = 60000000 46const NX_MAGIC_80000000: i64 = 80000000 47const NX_MAGIC_95000000: i64 = 95000000 48const NX_MAGIC_70000000: i64 = 70000000 49const NX_MAGIC_40000000: i64 = 40000000 50const NX_MAGIC_120000000: i64 = 120000000 51const NX_MAGIC_100000000: i64 = 100000000 52 53// ===== sealed-enum verdicts ======================================== 54 55const NX_STRESS_SAFE: i64 = 0 // < yield / 2 -- ample margin 56const NX_STRESS_WORKING: i64 = 1 // [yield/2, yield) -- design range 57const NX_STRESS_YIELDED: i64 = 2 // [yield, ultimate) -- permanent deformation 58const NX_STRESS_FRACTURED: i64 = 3 // >= ultimate -- catastrophic failure 59const NX_STRESS_UNKNOWN_MAT: i64 = 4 // material class not in registry 60const NX_STRESS_N: i64 = 5 61 62func nx_stress_verdict_name(v: i64) -> *u8 { 63 if v == NX_STRESS_SAFE { return "SAFE" } 64 if v == NX_STRESS_WORKING { return "WORKING" } 65 if v == NX_STRESS_YIELDED { return "YIELDED" } 66 if v == NX_STRESS_FRACTURED { return "FRACTURED" } 67 if v == NX_STRESS_UNKNOWN_MAT { return "UNKNOWN_MAT" } 68 return "UNKNOWN" 69} 70 71// ===== struct ====================================================== 72 73struct NxStress { 74 pa: i64, // Pascals (N/m²) 75} 76 77const NX_STRESS_BYTES: i64 = 8 78 79// ===== constructors ================================================ 80 81func nx_stress_new(pa: i64) -> *NxStress { 82 let s: *NxStress = (sys_mmap(NX_STRESS_BYTES)) as *NxStress 83 s.pa = pa 84 return s 85} 86 87func nx_stress_zero() -> *NxStress { 88 return nx_stress_new(0) 89} 90 91// ===== composition: F/A ============================================ 92// 93// Pure formula: σ = F/A. Both inputs Q14 (force in Q14 N, area in 94// Q14 m²); Q14 scaling cancels in the ratio so result is plain Pa. 95// 96// Defensive: zero area returns NULL (operator must check) -- this 97// matches the engineering reality that "stress on zero area" is 98// undefined (would be infinite). 99 100func nx_stress_from_force_area(force_n_q14: i64, area_m2_q14: i64) -> *NxStress { 101 if area_m2_q14 <= 0 { return 0 as *NxStress } 102 let pa: i64 = force_n_q14 / area_m2_q14 103 return nx_stress_new(pa) 104} 105 106// ===== MPa conversion for human display ============================ 107 108func nx_stress_to_mpa(s: *NxStress) -> i64 { 109 return s.pa / NX_MAGIC_1000000 110} 111 112func nx_stress_to_kpa(s: *NxStress) -> i64 { 113 return s.pa / 1000 114} 115 116// ===== material strength lookup ==================================== 117// 118// Yield strength in Pa for a given polymer class. Returns 0 for 119// unknown class. Caller can detect unknown via the 0 return. 120 121func nx_stress_yield_pa(material_class: i64) -> i64 { 122 if material_class == NX_MAT_PLA { return NX_MAGIC_50000000 } 123 if material_class == NX_MAT_PETG { return NX_MAGIC_45000000 } 124 if material_class == NX_MAT_ABS { return NX_MAGIC_35000000 } 125 if material_class == NX_MAT_ASA { return NX_MAGIC_35000000 } 126 if material_class == NX_MAT_PC { return NX_MAGIC_65000000 } 127 if material_class == NX_MAT_TPU { return NX_MAGIC_30000000 } 128 if material_class == NX_MAT_NYLON { return NX_MAGIC_60000000 } 129 if material_class == NX_MAT_PA_CF { return NX_MAGIC_80000000 } 130 if material_class == NX_MAT_PEEK { return NX_MAGIC_95000000 } 131 return 0 132} 133 134func nx_stress_ultimate_pa(material_class: i64) -> i64 { 135 if material_class == NX_MAT_PLA { return NX_MAGIC_65000000 } 136 if material_class == NX_MAT_PETG { return NX_MAGIC_50000000 } 137 if material_class == NX_MAT_ABS { return NX_MAGIC_45000000 } 138 if material_class == NX_MAT_ASA { return NX_MAGIC_45000000 } 139 if material_class == NX_MAT_PC { return NX_MAGIC_70000000 } 140 if material_class == NX_MAT_TPU { return NX_MAGIC_40000000 } 141 if material_class == NX_MAT_NYLON { return NX_MAGIC_80000000 } 142 if material_class == NX_MAT_PA_CF { return NX_MAGIC_120000000 } 143 if material_class == NX_MAT_PEEK { return NX_MAGIC_100000000 } 144 return 0 145} 146 147// ===== classifier ================================================== 148// 149// Maps a stress value against the material's yield/ultimate strengths 150// to a verdict: 151// SAFE : stress < yield / 2 (engineering safety factor of 2) 152// WORKING : yield/2 <= stress < yield 153// YIELDED : yield <= stress < ultimate (permanent deformation) 154// FRACTURED : stress >= ultimate (catastrophic failure) 155// UNKNOWN_MAT: material class not in strength registry 156 157func nx_stress_classify(s: *NxStress, material_class: i64) -> i64 { 158 let yield_pa: i64 = nx_stress_yield_pa(material_class) 159 let ult_pa: i64 = nx_stress_ultimate_pa(material_class) 160 if yield_pa <= 0 { return NX_STRESS_UNKNOWN_MAT } 161 if ult_pa <= 0 { return NX_STRESS_UNKNOWN_MAT } 162 // Compose nx_abs (already shipped) instead of inline `if x < 0` 163 // pattern. Per the four-pillar analysis: let-immutability is 164 // intentional design (Rust/Swift pattern), so the right path is 165 // composing existing pure helpers, NOT reaching for var on reflex. 166 let abs_pa: i64 = nx_abs(s.pa) 167 if abs_pa >= ult_pa { return NX_STRESS_FRACTURED } 168 if abs_pa >= yield_pa { return NX_STRESS_YIELDED } 169 if abs_pa >= (yield_pa / 2) { return NX_STRESS_WORKING } 170 return NX_STRESS_SAFE 171}