nx_water_awg.nx source
↩ module page · 168 lines · 6292 B
1// nx_water_awg.nx -- WATER SYSTEMS / ATMOSPHERIC WATER HARVESTING. Models
2// the whole "get water from the air" landscape, not one dehumidifier, and
3// recommends the best harvester for a climate (temperature + relative
4// humidity):
5// CONDENSATION -- refrigeration / dehumidifier (Watergen-class): cool
6// air below its dew point. Needs humid air (RH >~40%),
7// grid energy (~300 Wh/L). Psychrometric yield modeled.
8// SORBENT -- desiccant / MOF (Berkeley MOF-303, SOURCE hydropanels):
9// adsorb at low RH, release with solar heat. Works into
10// the desert (RH >~15%), low grid energy.
11// PASSIVE_CAP -- passive capillary-condensation material (UPenn 2025
12// amphiphilic nanoporous polymer, sciencedaily 250521):
13// condenses in nanopores with NO energy input, at lower
14// humidity than conventional cooling.
15// FOG -- mesh fog harvesting (needs near-saturation / fog).
16// RADIATIVE -- passive night-sky radiative cooling condenser.
17//
18// INTEGER-EXACT. Temp in C; RH in %; saturation vapor density g/m^3 x10;
19// yield in mL; energy in Wh/L. Psychrometric condensation yield from a
20// saturation-vapor-density table.
21//
22// THE exceed: given a climate it PICKS the right harvesting technology
23// (desert -> sorbent; moderate -> passive capillary; humid -> condensation)
24// and models the psychrometric yield + specific energy -- a real
25// research-to-design step, sovereign, no hardware yet (honest).
26//
27// grounded: psychrometric_saturation_vapor_density + atmospheric_water_generator
28// + upenn_amphiphilic_nanoporous_passive_harvesting_2025
29// + berkeley_mof_303_sorbent_awh
30// genealogy_id: atmospheric_water_harvesting + nishi_water_systems
31
32import "nx_syscalls.nx"
33
34// ===== Harvester technology classes ===============================
35
36const WA_CONDENSATION: i64 = 0
37const WA_SORBENT: i64 = 1
38const WA_PASSIVE_CAP: i64 = 2
39const WA_FOG: i64 = 3
40const WA_RADIATIVE: i64 = 4
41const WA_CLASS_N: i64 = 5
42const WA_NONE: i64 = 0 - 1
43
44// Power kind.
45const WA_POWER_PASSIVE: i64 = 0
46const WA_POWER_SOLAR: i64 = 1
47const WA_POWER_GRID: i64 = 2
48
49// Minimum relative humidity (%) each class works at.
50func wa_class_min_rh(cls: i64) -> i64 {
51 if cls == WA_CONDENSATION { return 40 }
52 if cls == WA_SORBENT { return 15 }
53 if cls == WA_PASSIVE_CAP { return 30 }
54 if cls == WA_FOG { return 95 }
55 if cls == WA_RADIATIVE { return 60 }
56 return 101
57}
58
59// Approximate specific energy (Wh per litre); 0 = passive.
60func wa_class_energy_whl(cls: i64) -> i64 {
61 if cls == WA_CONDENSATION { return 300 }
62 if cls == WA_SORBENT { return 50 }
63 if cls == WA_PASSIVE_CAP { return 0 }
64 if cls == WA_FOG { return 0 }
65 if cls == WA_RADIATIVE { return 0 }
66 return 0
67}
68
69// Relative yield score (0-100), throughput potential in favourable conditions.
70func wa_class_yield_score(cls: i64) -> i64 {
71 if cls == WA_CONDENSATION { return 90 }
72 if cls == WA_SORBENT { return 30 }
73 if cls == WA_PASSIVE_CAP { return 40 }
74 if cls == WA_FOG { return 50 }
75 if cls == WA_RADIATIVE { return 20 }
76 return 0
77}
78
79func wa_class_power_kind(cls: i64) -> i64 {
80 if cls == WA_CONDENSATION { return WA_POWER_GRID }
81 if cls == WA_SORBENT { return WA_POWER_SOLAR }
82 return WA_POWER_PASSIVE
83}
84
85// Does a class work at a given RH?
86func wa_class_works(cls: i64, rh_pct: i64) -> i64 {
87 if rh_pct >= wa_class_min_rh(cls) { return 1 }
88 return 0
89}
90
91// Fitness = yield minus an energy penalty (favour passive/low-energy),
92// or -1 if the class cannot work at this RH.
93func wa_class_fitness(cls: i64, rh_pct: i64) -> i64 {
94 if wa_class_works(cls, rh_pct) == 0 { return 0 - 1 }
95 return wa_class_yield_score(cls) - wa_class_energy_whl(cls) / 10
96}
97
98// Recommend the best harvester technology for a climate (RH-driven).
99func wa_recommend(temp_c: i64, rh_pct: i64) -> i64 {
100 var best: i64 = WA_NONE
101 var best_fit: i64 = 0
102 var c: i64 = 0
103 while c < WA_CLASS_N {
104 let f: i64 = wa_class_fitness(c, rh_pct)
105 if f >= 0 {
106 if f > best_fit {
107 best_fit = f
108 best = c
109 }
110 }
111 c = c + 1
112 }
113 return best
114}
115
116// ===== Psychrometrics (condensation yield) ========================
117
118// Saturation vapor density (g/m^3, x10) at a temperature.
119func wa_sat_vapor_density_x10(temp_c: i64) -> i64 {
120 if temp_c == 0 { return 48 }
121 if temp_c == 5 { return 68 }
122 if temp_c == 10 { return 94 }
123 if temp_c == 15 { return 128 }
124 if temp_c == 20 { return 173 }
125 if temp_c == 25 { return 230 }
126 if temp_c == 30 { return 304 }
127 if temp_c == 35 { return 396 }
128 if temp_c == 40 { return 511 }
129 return 0 - 1
130}
131
132// Absolute humidity (g/m^3 x10) = saturation density * RH / 100.
133func wa_abs_humidity_x10(temp_c: i64, rh_pct: i64) -> i64 {
134 let sat: i64 = wa_sat_vapor_density_x10(temp_c)
135 if sat < 0 { return 0 - 1 }
136 return sat * rh_pct / 100
137}
138
139// Extractable water (g/m^3 x10) by condensation: intake absolute humidity
140// minus what the air still holds when leaving saturated at the coil temp.
141func wa_extractable_x10(temp_c: i64, rh_pct: i64, coil_temp_c: i64) -> i64 {
142 let intake: i64 = wa_abs_humidity_x10(temp_c, rh_pct)
143 let coil_sat: i64 = wa_sat_vapor_density_x10(coil_temp_c)
144 if intake < 0 { return 0 }
145 if coil_sat < 0 { return 0 }
146 if intake <= coil_sat { return 0 }
147 return intake - coil_sat
148}
149
150// Daily condensation yield (mL) for an airflow (m^3/h). extractable_x10 is
151// g/m^3 x10; 1 g water ~ 1 mL.
152func wa_daily_yield_ml(airflow_m3h: i64, extractable_x10: i64) -> i64 {
153 return airflow_m3h * 24 * extractable_x10 / 10
154}
155
156// First-order specific energy (Wh/L) for refrigeration condensation: the
157// drier the air, the more air (and energy) per litre. Anchored so ~10 g/m^3
158// extractable ~ 300 Wh/L.
159func wa_specific_energy_whl(extractable_x10: i64) -> i64 {
160 if extractable_x10 <= 0 { return 0 - 1 }
161 return 30000 / extractable_x10
162}
163
164// Fresh condensate is essentially distilled -> near-zero TDS (flat, and
165// unsafe until disinfected + remineralized).
166func wa_condensate_tds() -> i64 {
167 return 2
168}