code wiki / _hdl_build / nx_matstruct.nx

nx_matstruct.nx source

↩ module page · 259 lines · 15164 B

1// nx_matstruct.nx -- ★THE STRUCTURAL MATERIAL REGISTRY: what a part is MADE OF, and what that implies. 2// 3// ★THE GAP THIS CLOSES (seq1417, measured 2026-07-30): the ecosystem HAS a real structural primitive -- 4// nx_stress computes sigma = F/A in integer Pascals and returns SEALED verdicts SAFE/WORKING/YIELDED/ 5// FRACTURED -- but it imports nx_material_profile.nx and inherits ITS enum, which is the SLICER'S FILAMENT 6// REGISTRY (PLA, PETG, ABS, ASA, PC, TPU, NYLON, PA-CF, PEEK, carrying hotend temps and retraction). 7// So the entire structural-material universe was 3D-PRINTER FILAMENT. 'This bracket is 1018 steel' was 8// INEXPRESSIBLE, and an underbuilt verdict on a car was impossible no matter how good the geometry got. 9// 10// ★★WHY THIS IS A NEW ORGAN AND NOT SIX MORE ROWS IN NX_MAT_*: that enum is OWNED BY THE PRINT LANE and 11// is backed by a struct of hotend temperature, cooling and pressure advance. Adding steel to it would owe 12// a hotend temperature for steel. A filament class and a structural-material class are DIFFERENT THINGS 13// THAT SHARE A NAME; binding a general primitive to a domain-specific registry was the category error. 14// This registry keeps the print lane's data intact and simply stops being the only answer. 15// 16// ★★★NOTHING IS STRIPPED (rule 25 -- the answer is always better data, never fewer options): indices 0..8 17// ARE the nine filaments, in nx_stress's exact order, carrying its exact shipped yields and ultimates, so 18// this is a strict SUPERSET and a caller can migrate by index. T6 proves that correspondence value by 19// value against the literals nx_stress ships, so a transcription slip cannot pass. 20// 21// nx_matstruct selftest 22// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26). 23import "nx_gate_verdict.nx" 24const MS_MAGIC_1240: i64 = 1240 25const MS_MAGIC_3500: i64 = 3500 26const MS_MAGIC_1270: i64 = 1270 27const MS_MAGIC_2100: i64 = 2100 28const MS_MAGIC_1040: i64 = 1040 29const MS_MAGIC_2200: i64 = 2200 30const MS_MAGIC_1070: i64 = 1070 31const MS_MAGIC_2300: i64 = 2300 32const MS_MAGIC_1200: i64 = 1200 33const MS_MAGIC_1210: i64 = 1210 34const MS_MAGIC_1140: i64 = 1140 35const MS_MAGIC_2000: i64 = 2000 36const MS_MAGIC_1300: i64 = 1300 37const MS_MAGIC_7000: i64 = 7000 38const MS_MAGIC_1320: i64 = 1320 39const MS_MAGIC_3600: i64 = 3600 40const MS_MAGIC_7870: i64 = 7870 41const MS_MAGIC_205000: i64 = 205000 42const MS_MAGIC_7850: i64 = 7850 43const MS_MAGIC_4140: i64 = 4140 44const MS_MAGIC_2700: i64 = 2700 45const MS_MAGIC_68900: i64 = 68900 46const MS_MAGIC_6061: i64 = 6061 47const MS_MAGIC_2680: i64 = 2680 48const MS_MAGIC_70300: i64 = 70300 49const MS_MAGIC_5052: i64 = 5052 50const MS_MAGIC_7100: i64 = 7100 51const MS_MAGIC_168000: i64 = 168000 52const MS_MAGIC_1810: i64 = 1810 53const MS_MAGIC_45000: i64 = 45000 54const MS_MAGIC_4430: i64 = 4430 55const MS_MAGIC_114000: i64 = 114000 56const MS_MAGIC_1000000000: i64 = 1000000000 57const MS_MAGIC_7870000000: i64 = 7870000000 58const MS_MAGIC_2700000000: i64 = 2700000000 59 60// ---- families ------------------------------------------------------------------------------ 61const MS_FAM_POLYMER: i64 = 0 62const MS_FAM_STEEL: i64 = 1 63const MS_FAM_ALUMINIUM: i64 = 2 64const MS_FAM_IRON: i64 = 3 65const MS_FAM_MAGNESIUM: i64 = 4 66const MS_FAM_TITANIUM: i64 = 5 67 68// ---- verdicts: the SAME sealed values nx_stress already returns, so the two compose ---------- 69const MS_SAFE: i64 = 0 // below half yield -- ample margin 70const MS_WORKING: i64 = 1 // [yield/2, yield) -- the design range 71const MS_YIELDED: i64 = 2 // [yield, ultimate) -- permanent deformation 72const MS_FRACTURED: i64 = 3 // at or past ultimate 73const MS_UNKNOWN: i64 = 4 // not in the registry -- REFUSED, never guessed 74 75const MS_NM: i64 = 16 // materials modelled -- honest, and QUERYABLE 76const MS_S: i64 = 5 // row: family, density kg/m3, yield MPa, ultimate MPa, modulus MPa 77const MS_BAD: i64 = 0 - 1 78const MS_MPA: i64 = 1000000 // MPa -> Pa, applied at the accessor so the TABLE stays readable 79 80// ★ONE TABLE. Yields and ultimates in MPa; density kg/m3; modulus MPa. Engineering-handbook consensus 81// values for the common structural grades, and for 0..8 the EXACT figures nx_stress already ships. 82func ms_table(T: *i64) -> i64 { 83 var i: i64 = 0 84 // family dens yield ult E 85 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1240; T[i+2]=50; T[i+3]=65; T[i+4]=MS_MAGIC_3500; i=i+MS_S // 0 PLA 86 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1270; T[i+2]=45; T[i+3]=50; T[i+4]=MS_MAGIC_2100; i=i+MS_S // 1 PETG 87 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1040; T[i+2]=35; T[i+3]=45; T[i+4]=MS_MAGIC_2200; i=i+MS_S // 2 ABS 88 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1070; T[i+2]=35; T[i+3]=45; T[i+4]=MS_MAGIC_2300; i=i+MS_S // 3 ASA 89 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1200; T[i+2]=65; T[i+3]=70; T[i+4]=MS_MAGIC_2300; i=i+MS_S // 4 PC 90 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1210; T[i+2]=30; T[i+3]=40; T[i+4]=26; i=i+MS_S // 5 TPU 95A 91 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1140; T[i+2]=60; T[i+3]=80; T[i+4]=MS_MAGIC_2000; i=i+MS_S // 6 NYLON 92 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1300; T[i+2]=80; T[i+3]=120; T[i+4]=MS_MAGIC_7000; i=i+MS_S // 7 PA-CF 93 T[i]=MS_FAM_POLYMER; T[i+1]=MS_MAGIC_1320; T[i+2]=95; T[i+3]=100; T[i+4]=MS_MAGIC_3600; i=i+MS_S // 8 PEEK 94 // ★THE METALS A CAR IS ACTUALLY MADE OF -- the reason this organ exists. 95 T[i]=MS_FAM_STEEL; T[i+1]=MS_MAGIC_7870; T[i+2]=370; T[i+3]=440; T[i+4]=MS_MAGIC_205000; i=i+MS_S // 9 steel 1018 mild 96 T[i]=MS_FAM_STEEL; T[i+1]=MS_MAGIC_7850; T[i+2]=655; T[i+3]=1020; T[i+4]=MS_MAGIC_205000; i=i+MS_S // 10 steel MS_MAGIC_4140 alloy 97 T[i]=MS_FAM_ALUMINIUM; T[i+1]=MS_MAGIC_2700; T[i+2]=276; T[i+3]=310; T[i+4]=MS_MAGIC_68900; i=i+MS_S // 11 alu MS_MAGIC_6061-T6 98 T[i]=MS_FAM_ALUMINIUM; T[i+1]=MS_MAGIC_2680; T[i+2]=193; T[i+3]=228; T[i+4]=MS_MAGIC_70300; i=i+MS_S // 12 alu MS_MAGIC_5052-H32 99 T[i]=MS_FAM_IRON; T[i+1]=MS_MAGIC_7100; T[i+2]=310; T[i+3]=450; T[i+4]=MS_MAGIC_168000; i=i+MS_S // 13 ductile iron 65-45-12 100 T[i]=MS_FAM_MAGNESIUM; T[i+1]=MS_MAGIC_1810; T[i+2]=160; T[i+3]=230; T[i+4]=MS_MAGIC_45000; i=i+MS_S // 14 magnesium AZ91D 101 T[i]=MS_FAM_TITANIUM; T[i+1]=MS_MAGIC_4430; T[i+2]=880; T[i+3]=950; T[i+4]=MS_MAGIC_114000; i=i+MS_S // 15 Ti-6Al-4V 102 return 0 103} 104 105// ---- FAIL-CLOSED ACCESSORS (the nx_myotable law: an index outside the set is REFUSED, never answered 106// with the last row -- a phantom material that looks completely real is the bug that table cured) ------ 107func ms_count() -> i64 { return MS_NM } 108func ms_ok(m: i64) -> i64 { if m < 0 { return 0 } if m >= MS_NM { return 0 } return 1 } 109func ms_family(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S] } 110func ms_density(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+1] } 111func ms_yield_mpa(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+2] } 112func ms_ult_mpa(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+3] } 113func ms_mod_mpa(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+4] } 114// Pascals at the boundary, because nx_stress speaks Pa and the table stays readable in MPa 115func ms_yield_pa(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+2]*MS_MPA } 116func ms_ult_pa(T: *i64, m: i64) -> i64 { if ms_ok(m)==0 { return MS_BAD } return T[m*MS_S+3]*MS_MPA } 117 118// ★THE VERDICT, in the part's ACTUAL material. Same thresholds and same sealed values as nx_stress. 119func ms_verdict(T: *i64, m: i64, sigma_pa: i64) -> i64 { 120 if ms_ok(m) == 0 { return MS_UNKNOWN } 121 let y: i64 = ms_yield_pa(T,m) 122 let u: i64 = ms_ult_pa(T,m) 123 if sigma_pa < y/2 { return MS_SAFE } 124 if sigma_pa < y { return MS_WORKING } 125 if sigma_pa < u { return MS_YIELDED } 126 return MS_FRACTURED 127} 128 129// ★MASS FROM GEOMETRY -- the other half of 'material and everything'. Volume in mm3 (the twin's own unit 130// via nx_unitconv's fx256mm grid), result in MILLIGRAMS so it stays integer-exact: 131// kg/m3 x mm3 = kg/m3 x 1e-9 m3 = 1e-9 kg = 1e-6 g = 1e-3 mg => mg = density*vol/1000 132func ms_mass_mg(T: *i64, m: i64, vol_mm3: i64) -> i64 { 133 if ms_ok(m) == 0 { return MS_BAD } 134 if vol_mm3 < 0 { return MS_BAD } 135 return ms_density(T,m) * vol_mm3 / 1000 136} 137 138func ms_name(m: i64) -> *u8 { 139 if m == 0 { return "PLA" as *u8 } 140 if m == 1 { return "PETG" as *u8 } 141 if m == 2 { return "ABS" as *u8 } 142 if m == 3 { return "ASA" as *u8 } 143 if m == 4 { return "PC" as *u8 } 144 if m == 5 { return "TPU-95A" as *u8 } 145 if m == 6 { return "NYLON" as *u8 } 146 if m == 7 { return "PA-CF" as *u8 } 147 if m == 8 { return "PEEK" as *u8 } 148 if m == 9 { return "steel-1018-mild" as *u8 } 149 if m == 10 { return "steel-4140-alloy" as *u8 } 150 if m == 11 { return "aluminium-6061-T6" as *u8 } 151 if m == 12 { return "aluminium-5052-H32" as *u8 } 152 if m == 13 { return "ductile-iron-65-45-12" as *u8 } 153 if m == 14 { return "magnesium-AZ91D" as *u8 } 154 if m == 15 { return "titanium-Ti-6Al-4V" as *u8 } 155 return "REFUSED-UNKNOWN-MATERIAL" as *u8 156} 157 158func main(argc: i64, argv: *i64) -> i64 { 159 let ctr: *i64 = gv_ctr() 160 gv_head("nx_matstruct selftest -- structural materials as one fail-closed table; metals exist now" as *u8) 161 let T: *i64 = sys_mmap(MS_NM*MS_S*8) as *i64 162 ms_table(T) 163 164 // T1 the metals the car twin needs are actually present and in the right families 165 var t1: i64 = 1 166 if ms_family(T,9) != MS_FAM_STEEL { t1 = 0 } 167 if ms_family(T,11) != MS_FAM_ALUMINIUM { t1 = 0 } 168 if ms_family(T,13) != MS_FAM_IRON { t1 = 0 } 169 if ms_family(T,15) != MS_FAM_TITANIUM { t1 = 0 } 170 if ms_count() != MS_NM { t1 = 0 } 171 gv_check("T1 the structural metals EXIST and carry their family" as *u8, t1, ctr) 172 173 // T2 fail-closed: an unknown material is REFUSED, never answered with the last row 174 var t2: i64 = 1 175 if ms_density(T, MS_NM) != MS_BAD { t2 = 0 } 176 if ms_yield_mpa(T, 999) != MS_BAD { t2 = 0 } 177 if ms_family(T, 0-1) != MS_BAD { t2 = 0 } 178 if ms_mass_mg(T, 999, 1000) != MS_BAD { t2 = 0 } 179 if ms_verdict(T, 999, 1000) != MS_UNKNOWN { t2 = 0 } 180 gv_check("T2 FAIL-CLOSED: an unknown material is REFUSED and the verdict says UNKNOWN" as *u8, t2, ctr) 181 182 // ★★★T3 THE DISCRIMINATING TOOTH. The SAME stress in steel and in aluminium must give DIFFERENT 183 // verdicts, or the material is decoration and the whole registry is a lookup nobody needed. 184 // 300 MPa: comfortably inside 1018 steel's design range, PAST 6061-T6 aluminium's yield. 185 let sig: i64 = 300*MS_MPA 186 let vsteel: i64 = ms_verdict(T, 9, sig) 187 let valu: i64 = ms_verdict(T, 11, sig) 188 var t3: i64 = 0 189 if vsteel == MS_WORKING { if valu == MS_YIELDED { t3 = 1 } } 190 gv_check("T3 SAME STRESS, DIFFERENT MATERIAL, DIFFERENT VERDICT (steel WORKING, alu YIELDED)" as *u8, t3, ctr) 191 192 // T4 the verdict walks forward with rising stress and never backwards 193 var t4: i64 = 1 194 if ms_verdict(T, 9, 10*MS_MPA) != MS_SAFE { t4 = 0 } 195 if ms_verdict(T, 9, 300*MS_MPA) != MS_WORKING { t4 = 0 } 196 if ms_verdict(T, 9, 400*MS_MPA) != MS_YIELDED { t4 = 0 } 197 if ms_verdict(T, 9, 500*MS_MPA) != MS_FRACTURED { t4 = 0 } 198 gv_check("T4 MONOTONE: rising stress walks SAFE -> WORKING -> YIELDED -> FRACTURED" as *u8, t4, ctr) 199 200 // T5 physics ordering: a registry that let aluminium outrank steel would be worse than none 201 var t5: i64 = 1 202 if ms_yield_mpa(T,9) <= ms_yield_mpa(T,11) { t5 = 0 } // steel stronger than aluminium 203 if ms_yield_mpa(T,11) <= ms_yield_mpa(T,0) { t5 = 0 } // aluminium stronger than PLA 204 if ms_density(T,9) <= ms_density(T,11) { t5 = 0 } // steel denser than aluminium 205 if ms_density(T,11) <= ms_density(T,0) { t5 = 0 } // aluminium denser than PLA 206 if ms_mod_mpa(T,9) <= ms_mod_mpa(T,11) { t5 = 0 } // steel stiffer than aluminium 207 gv_check("T5 PHYSICS ORDER holds: steel > aluminium > polymer in strength, density and stiffness" as *u8, t5, ctr) 208 209 // ★★T6 NOTHING WAS STRIPPED. Indices 0..8 must reproduce nx_stress's NINE shipped filament values 210 // EXACTLY -- checked against the literals it ships, so a transcription slip cannot pass. This is what 211 // makes the registry a strict SUPERSET rather than a replacement that quietly loses the print lane. 212 var t6: i64 = 1 213 if ms_yield_mpa(T,0) != 50 { t6 = 0 } 214 if ms_ult_mpa(T,0) != 65 { t6 = 0 } 215 if ms_yield_mpa(T,1) != 45 { t6 = 0 } 216 if ms_ult_mpa(T,1) != 50 { t6 = 0 } 217 if ms_yield_mpa(T,2) != 35 { t6 = 0 } 218 if ms_yield_mpa(T,3) != 35 { t6 = 0 } 219 if ms_yield_mpa(T,4) != 65 { t6 = 0 } 220 if ms_ult_mpa(T,4) != 70 { t6 = 0 } 221 if ms_yield_mpa(T,5) != 30 { t6 = 0 } 222 if ms_yield_mpa(T,6) != 60 { t6 = 0 } 223 if ms_ult_mpa(T,6) != 80 { t6 = 0 } 224 if ms_yield_mpa(T,7) != 80 { t6 = 0 } 225 if ms_ult_mpa(T,7) != 120 { t6 = 0 } 226 if ms_yield_mpa(T,8) != 95 { t6 = 0 } 227 if ms_ult_mpa(T,8) != 100 { t6 = 0 } 228 gv_check("T6 SUPERSET: all NINE nx_stress filament yields reproduced EXACTLY, nothing stripped" as *u8, t6, ctr) 229 230 // ★★T7 ANCHORED TO AN ABSOLUTE PHYSICAL QUANTITY, not to another of my own readings. A cubic metre 231 // (1e9 mm3) of mild steel weighs 7870 kg and of 6061 aluminium 2700 kg. ★nx_facs' law: every 232 // instrument needs at least ONE tooth tied to a named external quantity, because a self-consistent 233 // comparison cannot catch a wrong field mapping -- seven of its eight teeth missed exactly that. 234 let cubic_m: i64 = MS_MAGIC_1000000000 235 let msteel: i64 = ms_mass_mg(T, 9, cubic_m) 236 let malu: i64 = ms_mass_mg(T, 11, cubic_m) 237 var t7: i64 = 0 238 if msteel == MS_MAGIC_7870000000 { if malu == MS_MAGIC_2700000000 { t7 = 1 } } 239 gv_check("T7 ABSOLUTE: 1 m3 of steel = 7870 kg and of aluminium = 2700 kg, computed not asserted" as *u8, t7, ctr) 240 241 gv_puts("\n A 2 mm wall carrying 300 MPa, by material:\n" as *u8) 242 var m: i64 = 9 243 while m < MS_NM { 244 gv_puts(" " as *u8) 245 gv_puts(ms_name(m)) 246 gv_puts(" yield " as *u8) 247 gv_num(ms_yield_mpa(T,m)) 248 gv_puts(" MPa verdict " as *u8) 249 gv_num(ms_verdict(T, m, sig)) 250 gv_puts(" (0 safe 1 working 2 yielded 3 fractured)\n" as *u8) 251 m = m + 1 252 } 253 gv_puts("\n HONEST COVERAGE: " as *u8) 254 gv_num(ms_count()) 255 gv_puts(" materials -- 9 polymers carried over from nx_stress plus 7 structural metals. That is the common automotive set, NOT a materials database: no tempers beyond those listed, no temperature dependence, no fatigue curve, no anisotropy. The FLIP CHAIN can now close -- nx_meshseg3d parts, nx_meshthick min wall, THIS verdict in the real alloy, joined to nx_underbuilt_board's NHTSA data -- and the wiring into nx_stress is a SEPARATE announced step because that primitive is shared with the print lane.\n" as *u8) 256 257 return gv_verdict("MATSTRUCT-GATE" as *u8, ctr, 258 "structural materials as one fail-closed table; the same stress in steel and aluminium now returns different verdicts" as *u8) 259}