nx_polymer_thermal.nx source
↩ module page · 171 lines · 6916 B
1// nx_polymer_thermal.nx -- per-polymer thermal envelope for FDM
2// printing. Combines polymer chemistry (Tg, Tm, Td) with engineering
3// constraints (recommended print + bed temp ranges) into a single
4// substrate primitive the slicer composes for thermal-safety
5// validation BEFORE emitting G-code with operator-supplied temps.
6//
7// Per META-CARDINAL feedback-engineering-sciences-bits-up-3d-print-first:
8// this is the substrate's FIRST primitive that bridges Layer 2
9// (pure chemistry: nx_chem_periodic / nx_chem_molecule -- already
10// shipped) with Layer 3 (engineering applications: nx_material_profile
11// + nx_gcode_emit). Composes:
12// - nx_material_profile (NX_MAT_* sealed-enum keys)
13// - chemistry knowledge (Tg / Tm / Td values from polymer literature)
14//
15// Future v2 could compose nx_chem_molecule to compute mol weight
16// from PLA repeating unit (C3H4O2)n etc., but v1 ships the slicer-
17// relevant constants directly per material class.
18//
19// Thermal values verified against polymer-literature consensus:
20// PLA: Tg 60°C / Tm 160°C / Td 250°C / print 190-230 / bed 50-65
21// PETG: Tg 80°C / Tm 240°C / Td 290°C / print 220-250 / bed 70-85
22// ABS: Tg 105°C / amorphous / Td 270°C / print 220-260 / bed 90-110
23// ASA: Tg 105°C / amorphous / Td 270°C / print 240-260 / bed 100-110
24// PC: Tg 145°C / amorphous / Td 320°C / print 270-310 / bed 110-130
25// TPU: Tg variable / Tm 180°C / Td 240°C / print 200-230 / bed 50-60
26// NYLON: Tg 50°C / Tm 220°C / Td 320°C / print 240-280 / bed 80-100
27// PA-CF: same as NYLON with reinforcement (no thermal delta)
28// PEEK: Tg 143°C / Tm 343°C / Td 575°C / print 360-400 / bed 120-145
29//
30// Amorphous polymers (ABS, ASA, PC) use Tm = 0 as sentinel meaning
31// "no crystalline melt; soften gradually above Tg". Validator
32// treats this case correctly.
33//
34// license_tier: ORIGINAL
35
36import "nx_syscalls.nx"
37import "nx_material_profile.nx"
38
39// ===== verdicts ====================================================
40
41const NX_POLYTHERM_SAFE: i64 = 0
42const NX_POLYTHERM_PRINT_TOO_LOW: i64 = 1
43const NX_POLYTHERM_PRINT_TOO_HIGH: i64 = 2
44const NX_POLYTHERM_DECOMP_RISK: i64 = 3
45const NX_POLYTHERM_BED_TOO_LOW: i64 = 4
46const NX_POLYTHERM_BED_TOO_HIGH: i64 = 5
47const NX_POLYTHERM_UNKNOWN_CLASS: i64 = 6
48const NX_POLYTHERM_N: i64 = 7
49
50func nx_polytherm_verdict_name(v: i64) -> *u8 {
51 if v == NX_POLYTHERM_SAFE { return "SAFE" }
52 if v == NX_POLYTHERM_PRINT_TOO_LOW { return "PRINT_TOO_LOW" }
53 if v == NX_POLYTHERM_PRINT_TOO_HIGH { return "PRINT_TOO_HIGH" }
54 if v == NX_POLYTHERM_DECOMP_RISK { return "DECOMP_RISK" }
55 if v == NX_POLYTHERM_BED_TOO_LOW { return "BED_TOO_LOW" }
56 if v == NX_POLYTHERM_BED_TOO_HIGH { return "BED_TOO_HIGH" }
57 if v == NX_POLYTHERM_UNKNOWN_CLASS { return "UNKNOWN_CLASS" }
58 return "UNKNOWN"
59}
60
61// ===== struct ======================================================
62
63struct NxPolymerThermal {
64 material_class: i64,
65 tg_c: i64, // glass transition
66 tm_c: i64, // crystalline melt (0 = amorphous)
67 td_c: i64, // decomposition onset
68 print_min_c: i64,
69 print_max_c: i64,
70 bed_min_c: i64,
71 bed_max_c: i64,
72}
73
74const NX_POLYTHERM_BYTES: i64 = 64 // 8 i64
75
76// ===== population helper ===========================================
77
78func nx_polytherm_set(p: *NxPolymerThermal, cls: i64,
79 tg: i64, tm: i64, td: i64,
80 pmin: i64, pmax: i64,
81 bmin: i64, bmax: i64) -> i64 {
82 p.material_class = cls
83 p.tg_c = tg
84 p.tm_c = tm
85 p.td_c = td
86 p.print_min_c = pmin
87 p.print_max_c = pmax
88 p.bed_min_c = bmin
89 p.bed_max_c = bmax
90 return 0
91}
92
93// ===== factory =====================================================
94//
95// Returns a fresh NxPolymerThermal populated with literature-verified
96// values for the given material class. Returns NULL for unknown class.
97// Pure data lookup -- no I/O.
98
99func nx_polymer_thermal_for(material_class: i64) -> *NxPolymerThermal {
100 if material_class < 0 { return 0 as *NxPolymerThermal }
101 if material_class >= NX_MAT_N { return 0 as *NxPolymerThermal }
102 let p: *NxPolymerThermal = (sys_mmap(NX_POLYTHERM_BYTES)) as *NxPolymerThermal
103
104 if material_class == NX_MAT_PLA {
105 nx_polytherm_set(p, NX_MAT_PLA, 60, 160, 250, 190, 230, 50, 65)
106 return p
107 }
108 if material_class == NX_MAT_PETG {
109 nx_polytherm_set(p, NX_MAT_PETG, 80, 240, 290, 220, 250, 70, 85)
110 return p
111 }
112 if material_class == NX_MAT_ABS {
113 nx_polytherm_set(p, NX_MAT_ABS, 105, 0, 270, 220, 260, 90, 110)
114 return p
115 }
116 if material_class == NX_MAT_ASA {
117 nx_polytherm_set(p, NX_MAT_ASA, 105, 0, 270, 240, 260, 100, 110)
118 return p
119 }
120 if material_class == NX_MAT_PC {
121 nx_polytherm_set(p, NX_MAT_PC, 145, 0, 320, 270, 310, 110, 130)
122 return p
123 }
124 if material_class == NX_MAT_TPU {
125 nx_polytherm_set(p, NX_MAT_TPU, 0, 180, 240, 200, 230, 50, 60)
126 return p
127 }
128 if material_class == NX_MAT_NYLON {
129 nx_polytherm_set(p, NX_MAT_NYLON, 50, 220, 320, 240, 280, 80, 100)
130 return p
131 }
132 if material_class == NX_MAT_PA_CF {
133 nx_polytherm_set(p, NX_MAT_PA_CF, 50, 220, 320, 240, 280, 80, 100)
134 return p
135 }
136 if material_class == NX_MAT_PEEK {
137 nx_polytherm_set(p, NX_MAT_PEEK, 143, 343, 575, 360, 400, 120, 145)
138 return p
139 }
140 return 0 as *NxPolymerThermal
141}
142
143// ===== validators ==================================================
144//
145// Check operator-supplied hotend + bed temps against the polymer's
146// thermal envelope. Returns first error encountered. Decomposition
147// risk is the most serious -- printing above Td releases toxic
148// breakdown products (ABS styrene, PLA lactide-like compounds) plus
149// permanently damages mechanical properties.
150
151func nx_polymer_thermal_validate(p: *NxPolymerThermal,
152 hotend_c: i64, bed_c: i64) -> i64 {
153 if (p as i64) == 0 { return NX_POLYTHERM_UNKNOWN_CLASS }
154
155 // Decomposition is the safety-critical check -- runs first.
156 if hotend_c >= p.td_c { return NX_POLYTHERM_DECOMP_RISK }
157
158 if hotend_c < p.print_min_c { return NX_POLYTHERM_PRINT_TOO_LOW }
159 if hotend_c > p.print_max_c { return NX_POLYTHERM_PRINT_TOO_HIGH }
160
161 if bed_c < p.bed_min_c { return NX_POLYTHERM_BED_TOO_LOW }
162 if bed_c > p.bed_max_c { return NX_POLYTHERM_BED_TOO_HIGH }
163
164 return NX_POLYTHERM_SAFE
165}
166
167// Convenience: validate using NxMaterialProfile directly.
168func nx_polymer_validate_profile(mat: *NxMaterialProfile) -> i64 {
169 let p: *NxPolymerThermal = nx_polymer_thermal_for(mat.material_class)
170 return nx_polymer_thermal_validate(p, mat.hotend_print_c, mat.bed_print_c)
171}