@driftengine/chemistry 3.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +194 -0
- package/dist/conservation.d.ts +9 -0
- package/dist/conservation.js +38 -0
- package/dist/element/elements.d.ts +31 -0
- package/dist/element/elements.js +73 -0
- package/dist/element/matrix.d.ts +27 -0
- package/dist/element/matrix.js +80 -0
- package/dist/field/ambient.d.ts +49 -0
- package/dist/field/ambient.js +82 -0
- package/dist/field/atmosphere.d.ts +308 -0
- package/dist/field/atmosphere.js +1033 -0
- package/dist/ignition/criteria.d.ts +97 -0
- package/dist/ignition/criteria.js +135 -0
- package/dist/ignition/events.d.ts +56 -0
- package/dist/ignition/events.js +90 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +66 -0
- package/dist/library/biological.d.ts +6 -0
- package/dist/library/biological.js +229 -0
- package/dist/library/common.d.ts +26 -0
- package/dist/library/common.js +208 -0
- package/dist/library/food.d.ts +9 -0
- package/dist/library/food.js +272 -0
- package/dist/library/fuel.d.ts +19 -0
- package/dist/library/fuel.js +290 -0
- package/dist/library/library.d.ts +45 -0
- package/dist/library/library.js +29 -0
- package/dist/library/metal.d.ts +6 -0
- package/dist/library/metal.js +181 -0
- package/dist/library/mineral.d.ts +4 -0
- package/dist/library/mineral.js +177 -0
- package/dist/library/organic.d.ts +12 -0
- package/dist/library/organic.js +489 -0
- package/dist/library/polymer.d.ts +4 -0
- package/dist/library/polymer.js +237 -0
- package/dist/parcel/conduction.d.ts +20 -0
- package/dist/parcel/conduction.js +160 -0
- package/dist/parcel/shells.d.ts +49 -0
- package/dist/parcel/shells.js +73 -0
- package/dist/parcel/store.d.ts +395 -0
- package/dist/parcel/store.js +1152 -0
- package/dist/parcel/tier.d.ts +48 -0
- package/dist/parcel/tier.js +51 -0
- package/dist/present/audio.d.ts +41 -0
- package/dist/present/audio.js +50 -0
- package/dist/present/blackbody.d.ts +55 -0
- package/dist/present/blackbody.js +112 -0
- package/dist/present/flame.d.ts +34 -0
- package/dist/present/flame.js +103 -0
- package/dist/present/index.d.ts +25 -0
- package/dist/present/index.js +22 -0
- package/dist/present/install.d.ts +87 -0
- package/dist/present/install.js +157 -0
- package/dist/present/smoke.d.ts +63 -0
- package/dist/present/smoke.js +85 -0
- package/dist/present/surface.d.ts +40 -0
- package/dist/present/surface.js +76 -0
- package/dist/reaction/localise.d.ts +16 -0
- package/dist/reaction/localise.js +95 -0
- package/dist/reaction/rates.d.ts +59 -0
- package/dist/reaction/rates.js +101 -0
- package/dist/reaction/reaction.d.ts +178 -0
- package/dist/reaction/reaction.js +104 -0
- package/dist/reaction/registry.d.ts +47 -0
- package/dist/reaction/registry.js +215 -0
- package/dist/reaction/solver.d.ts +51 -0
- package/dist/reaction/solver.js +277 -0
- package/dist/species/registry.d.ts +26 -0
- package/dist/species/registry.js +97 -0
- package/dist/species/species.d.ts +48 -0
- package/dist/species/species.js +20 -0
- package/dist/species/standard.d.ts +30 -0
- package/dist/species/standard.js +436 -0
- package/dist/species/thermal.d.ts +18 -0
- package/dist/species/thermal.js +72 -0
- package/dist/substance/boiling.d.ts +29 -0
- package/dist/substance/boiling.js +54 -0
- package/dist/substance/composition.d.ts +34 -0
- package/dist/substance/composition.js +53 -0
- package/dist/substance/registry.d.ts +86 -0
- package/dist/substance/registry.js +187 -0
- package/dist/substance/substance.d.ts +94 -0
- package/dist/substance/substance.js +48 -0
- package/dist/testing/drift.d.ts +15 -0
- package/dist/testing/drift.js +29 -0
- package/dist/transport/contacts.d.ts +22 -0
- package/dist/transport/contacts.js +50 -0
- package/dist/transport/radiation.d.ts +101 -0
- package/dist/transport/radiation.js +199 -0
- package/dist/transport/world.d.ts +92 -0
- package/dist/transport/world.js +381 -0
- package/dist/world/fingerprint.d.ts +31 -0
- package/dist/world/fingerprint.js +70 -0
- package/package.json +100 -0
- package/src/conservation.ts +44 -0
- package/src/element/elements.ts +80 -0
- package/src/element/matrix.ts +89 -0
- package/src/field/ambient.ts +111 -0
- package/src/field/atmosphere.ts +1159 -0
- package/src/ignition/criteria.ts +177 -0
- package/src/ignition/events.ts +100 -0
- package/src/index.ts +213 -0
- package/src/library/biological.ts +252 -0
- package/src/library/common.ts +224 -0
- package/src/library/food.ts +302 -0
- package/src/library/fuel.ts +299 -0
- package/src/library/library.ts +62 -0
- package/src/library/metal.ts +186 -0
- package/src/library/mineral.ts +194 -0
- package/src/library/organic.ts +545 -0
- package/src/library/polymer.ts +243 -0
- package/src/parcel/conduction.ts +188 -0
- package/src/parcel/shells.ts +100 -0
- package/src/parcel/store.ts +1270 -0
- package/src/parcel/tier.ts +60 -0
- package/src/present/audio.ts +54 -0
- package/src/present/blackbody.ts +118 -0
- package/src/present/flame.ts +111 -0
- package/src/present/index.ts +25 -0
- package/src/present/install.ts +209 -0
- package/src/present/smoke.ts +139 -0
- package/src/present/surface.ts +107 -0
- package/src/reaction/localise.ts +126 -0
- package/src/reaction/rates.ts +153 -0
- package/src/reaction/reaction.ts +235 -0
- package/src/reaction/registry.ts +257 -0
- package/src/reaction/solver.ts +326 -0
- package/src/species/registry.ts +117 -0
- package/src/species/species.ts +53 -0
- package/src/species/standard.ts +449 -0
- package/src/species/thermal.ts +74 -0
- package/src/substance/boiling.ts +64 -0
- package/src/substance/composition.ts +81 -0
- package/src/substance/registry.ts +223 -0
- package/src/substance/substance.ts +148 -0
- package/src/testing/drift.ts +28 -0
- package/src/transport/contacts.ts +56 -0
- package/src/transport/radiation.ts +238 -0
- package/src/transport/world.ts +437 -0
- package/src/world/fingerprint.ts +106 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heat crossing empty space, which is how a fire reaches anything it is not touching.
|
|
3
|
+
*
|
|
4
|
+
* **Radiation dominates every fire beyond a few centimetres, and `§10` is the argument.** A flame
|
|
5
|
+
* at 1200 K radiates about 118 kW/m², and the fourth power means halving its temperature cuts that
|
|
6
|
+
* sixteenfold. A model without it cannot make a log ignite from across a hearth — which is the
|
|
7
|
+
* scenario the whole track exists for.
|
|
8
|
+
*
|
|
9
|
+
* **The view factor is a point source: `cos θ · A / (π r²)`.** It is wrong close in, where a flame
|
|
10
|
+
* is not a point, and it is right where it matters. What it buys is the single relationship this
|
|
11
|
+
* model is built on: **flux falls as `1/r²`**, so moving a log twice as far from a fire makes it
|
|
12
|
+
* take four times as long to catch. One line of arithmetic, and without it "leading a piece of wood
|
|
13
|
+
* near the fire" has no right answer at all.
|
|
14
|
+
*/
|
|
15
|
+
/** W/(m²·K⁴). */
|
|
16
|
+
export declare const STEFAN_BOLTZMANN = 5.670374419e-8;
|
|
17
|
+
/**
|
|
18
|
+
* Radiant flux onto a surface, W/m².
|
|
19
|
+
*
|
|
20
|
+
* @param sourceTemperature K
|
|
21
|
+
* @param sourceArea m² of radiating surface
|
|
22
|
+
* @param surfaceTemperature K of the receiver
|
|
23
|
+
* @param emissivity of the receiver, 0..1
|
|
24
|
+
* @param distance m
|
|
25
|
+
* @param cosine of the angle between the receiver's normal and the line to the source
|
|
26
|
+
*/
|
|
27
|
+
export declare function radiantFlux(sourceTemperature: number, sourceArea: number, surfaceTemperature: number, emissivity: number, distance: number, cosine: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* How far a source of a given power is worth considering, m.
|
|
30
|
+
*
|
|
31
|
+
* From `P / (4π r²) = q_min`, so a **big fire reaches further than a small one** rather than every
|
|
32
|
+
* source sharing a constant radius. That is what stops a candle being tested against every parcel
|
|
33
|
+
* in a scene and a bonfire being cut off at the same distance.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sourceRange(power: number, minimumFlux: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* Convective heat transfer coefficient from the local gas speed, W/(m²·K).
|
|
38
|
+
*
|
|
39
|
+
* Two to twenty-five in still air, and up to about two hundred and fifty in a gale. **Above a fire
|
|
40
|
+
* convection dominates radiation and beside one radiation dominates convection**, and the crossover
|
|
41
|
+
* is at the plume edge — which is why standing beside a bonfire is pleasant and standing in the
|
|
42
|
+
* smoke is not.
|
|
43
|
+
*/
|
|
44
|
+
export declare function convectiveCoefficient(gasSpeed: number): number;
|
|
45
|
+
/** Whether anything solid stands between two points. */
|
|
46
|
+
export type OcclusionTest = (ax: number, ay: number, az: number, bx: number, by: number, bz: number) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The radiating things in a scene this tick: flames and hot surfaces both.
|
|
49
|
+
*
|
|
50
|
+
* Gathered into columns once per tick and read many times, which is why it is a structure rather
|
|
51
|
+
* than a callback. Cleared and refilled rather than reallocated.
|
|
52
|
+
*/
|
|
53
|
+
export declare class RadiativeSources {
|
|
54
|
+
private x;
|
|
55
|
+
private y;
|
|
56
|
+
private z;
|
|
57
|
+
private temperature;
|
|
58
|
+
private area;
|
|
59
|
+
private range;
|
|
60
|
+
private used;
|
|
61
|
+
get count(): number;
|
|
62
|
+
clear(): void;
|
|
63
|
+
/** @param power W, which decides how far this source is worth testing against. */
|
|
64
|
+
add(x: number, y: number, z: number, temperature: number, area: number, power: number): void;
|
|
65
|
+
temperatureOf(source: number): number;
|
|
66
|
+
areaOf(source: number): number;
|
|
67
|
+
/** Whether a point is inside this source's own reach. */
|
|
68
|
+
reaches(source: number, x: number, y: number, z: number): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Total flux onto a point from every source that can see it, W/m².
|
|
71
|
+
*
|
|
72
|
+
* The receiver is treated as facing each source — a parcel is a lump rather than a plane, so
|
|
73
|
+
* there is no one normal to take a cosine against. What that costs is that a flat plate edge-on
|
|
74
|
+
* to a fire receives as much as one facing it; what it buys is not asking a consumer for a normal
|
|
75
|
+
* per parcel, which is a number most of them do not have.
|
|
76
|
+
*/
|
|
77
|
+
fluxAt(x: number, y: number, z: number, surfaceTemperature: number, emissivity: number, occluded: OcclusionTest | null): number;
|
|
78
|
+
/**
|
|
79
|
+
* The temperature of the hottest source that reaches a point, or zero where none does.
|
|
80
|
+
*
|
|
81
|
+
* **What a radiative exchange is bounded by**, and the reason it has to be bounded at all: the
|
|
82
|
+
* radiative term is explicit, so a parcel whose surface shell has a small heat capacity — a coin,
|
|
83
|
+
* a nail, a leaf — can absorb more in one step than would take it past the source that is heating
|
|
84
|
+
* it, and then radiate the excess back as `T⁴` and ring itself to pieces. It was found by putting
|
|
85
|
+
* a copper coin on a hearth in `demo/dev/chemistry.ts`, where it reached 4,000 K on the first step
|
|
86
|
+
* and 100 K a minute later.
|
|
87
|
+
*
|
|
88
|
+
* Nothing can be heated past the thing heating it, so this is the equilibrium the step may reach
|
|
89
|
+
* and not exceed — which is what an implicit solve would give and costs one comparison per source.
|
|
90
|
+
*/
|
|
91
|
+
hottestAt(x: number, y: number, z: number, occluded: OcclusionTest | null): number;
|
|
92
|
+
/**
|
|
93
|
+
* Whether **any** source reaches a point at all, which is the whole of the wake test.
|
|
94
|
+
*
|
|
95
|
+
* `§14`'s scan has to be coarse or sleeping saves nothing: this is a range comparison per source
|
|
96
|
+
* and no view factor, no temperature and no occlusion ray. A source's range already comes from its
|
|
97
|
+
* own power, so a bonfire wakes a parcel a candle would not.
|
|
98
|
+
*/
|
|
99
|
+
reachesPoint(x: number, y: number, z: number): boolean;
|
|
100
|
+
private grow;
|
|
101
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heat crossing empty space, which is how a fire reaches anything it is not touching.
|
|
3
|
+
*
|
|
4
|
+
* **Radiation dominates every fire beyond a few centimetres, and `§10` is the argument.** A flame
|
|
5
|
+
* at 1200 K radiates about 118 kW/m², and the fourth power means halving its temperature cuts that
|
|
6
|
+
* sixteenfold. A model without it cannot make a log ignite from across a hearth — which is the
|
|
7
|
+
* scenario the whole track exists for.
|
|
8
|
+
*
|
|
9
|
+
* **The view factor is a point source: `cos θ · A / (π r²)`.** It is wrong close in, where a flame
|
|
10
|
+
* is not a point, and it is right where it matters. What it buys is the single relationship this
|
|
11
|
+
* model is built on: **flux falls as `1/r²`**, so moving a log twice as far from a fire makes it
|
|
12
|
+
* take four times as long to catch. One line of arithmetic, and without it "leading a piece of wood
|
|
13
|
+
* near the fire" has no right answer at all.
|
|
14
|
+
*/
|
|
15
|
+
/** W/(m²·K⁴). */
|
|
16
|
+
export const STEFAN_BOLTZMANN = 5.670374419e-8;
|
|
17
|
+
/**
|
|
18
|
+
* How close a point source may be approached before the approximation is capped.
|
|
19
|
+
*
|
|
20
|
+
* A point source's flux is unbounded at zero distance, and the honest failure there is a large
|
|
21
|
+
* number rather than an infinite one. The clamp is the scale at which a real source stops looking
|
|
22
|
+
* like a point, which is its own size.
|
|
23
|
+
*/
|
|
24
|
+
const MIN_DISTANCE = 0.05;
|
|
25
|
+
/**
|
|
26
|
+
* Radiant flux onto a surface, W/m².
|
|
27
|
+
*
|
|
28
|
+
* @param sourceTemperature K
|
|
29
|
+
* @param sourceArea m² of radiating surface
|
|
30
|
+
* @param surfaceTemperature K of the receiver
|
|
31
|
+
* @param emissivity of the receiver, 0..1
|
|
32
|
+
* @param distance m
|
|
33
|
+
* @param cosine of the angle between the receiver's normal and the line to the source
|
|
34
|
+
*/
|
|
35
|
+
export function radiantFlux(sourceTemperature, sourceArea, surfaceTemperature, emissivity, distance, cosine) {
|
|
36
|
+
if (!(cosine > 0))
|
|
37
|
+
return 0;
|
|
38
|
+
const range = Math.max(distance, MIN_DISTANCE);
|
|
39
|
+
const viewFactor = (cosine * sourceArea) / (Math.PI * range * range);
|
|
40
|
+
const source = sourceTemperature * sourceTemperature * sourceTemperature * sourceTemperature;
|
|
41
|
+
const surface = surfaceTemperature * surfaceTemperature * surfaceTemperature * surfaceTemperature;
|
|
42
|
+
return emissivity * STEFAN_BOLTZMANN * viewFactor * (source - surface);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* How far a source of a given power is worth considering, m.
|
|
46
|
+
*
|
|
47
|
+
* From `P / (4π r²) = q_min`, so a **big fire reaches further than a small one** rather than every
|
|
48
|
+
* source sharing a constant radius. That is what stops a candle being tested against every parcel
|
|
49
|
+
* in a scene and a bonfire being cut off at the same distance.
|
|
50
|
+
*/
|
|
51
|
+
export function sourceRange(power, minimumFlux) {
|
|
52
|
+
if (!(power > 0) || !(minimumFlux > 0))
|
|
53
|
+
return 0;
|
|
54
|
+
return Math.sqrt(power / (4 * Math.PI * minimumFlux));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Convective heat transfer coefficient from the local gas speed, W/(m²·K).
|
|
58
|
+
*
|
|
59
|
+
* Two to twenty-five in still air, and up to about two hundred and fifty in a gale. **Above a fire
|
|
60
|
+
* convection dominates radiation and beside one radiation dominates convection**, and the crossover
|
|
61
|
+
* is at the plume edge — which is why standing beside a bonfire is pleasant and standing in the
|
|
62
|
+
* smoke is not.
|
|
63
|
+
*/
|
|
64
|
+
export function convectiveCoefficient(gasSpeed) {
|
|
65
|
+
const speed = gasSpeed > 0 ? gasSpeed : 0;
|
|
66
|
+
const h = 5 + 12 * Math.sqrt(speed);
|
|
67
|
+
return h > 250 ? 250 : h;
|
|
68
|
+
}
|
|
69
|
+
/** Below this a source is not worth testing against anything, W/m². */
|
|
70
|
+
const MINIMUM_USEFUL_FLUX = 200;
|
|
71
|
+
/**
|
|
72
|
+
* The radiating things in a scene this tick: flames and hot surfaces both.
|
|
73
|
+
*
|
|
74
|
+
* Gathered into columns once per tick and read many times, which is why it is a structure rather
|
|
75
|
+
* than a callback. Cleared and refilled rather than reallocated.
|
|
76
|
+
*/
|
|
77
|
+
export class RadiativeSources {
|
|
78
|
+
x = new Float64Array(32);
|
|
79
|
+
y = new Float64Array(32);
|
|
80
|
+
z = new Float64Array(32);
|
|
81
|
+
temperature = new Float64Array(32);
|
|
82
|
+
area = new Float64Array(32);
|
|
83
|
+
range = new Float64Array(32);
|
|
84
|
+
used = 0;
|
|
85
|
+
get count() {
|
|
86
|
+
return this.used;
|
|
87
|
+
}
|
|
88
|
+
clear() {
|
|
89
|
+
this.used = 0;
|
|
90
|
+
}
|
|
91
|
+
/** @param power W, which decides how far this source is worth testing against. */
|
|
92
|
+
add(x, y, z, temperature, area, power) {
|
|
93
|
+
if (this.used === this.x.length)
|
|
94
|
+
this.grow();
|
|
95
|
+
const at = this.used++;
|
|
96
|
+
this.x[at] = x;
|
|
97
|
+
this.y[at] = y;
|
|
98
|
+
this.z[at] = z;
|
|
99
|
+
this.temperature[at] = temperature;
|
|
100
|
+
this.area[at] = area;
|
|
101
|
+
this.range[at] = sourceRange(power, MINIMUM_USEFUL_FLUX);
|
|
102
|
+
}
|
|
103
|
+
temperatureOf(source) {
|
|
104
|
+
return this.temperature[source];
|
|
105
|
+
}
|
|
106
|
+
areaOf(source) {
|
|
107
|
+
return this.area[source];
|
|
108
|
+
}
|
|
109
|
+
/** Whether a point is inside this source's own reach. */
|
|
110
|
+
reaches(source, x, y, z) {
|
|
111
|
+
const dx = x - this.x[source];
|
|
112
|
+
const dy = y - this.y[source];
|
|
113
|
+
const dz = z - this.z[source];
|
|
114
|
+
const reach = this.range[source];
|
|
115
|
+
return dx * dx + dy * dy + dz * dz <= reach * reach;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Total flux onto a point from every source that can see it, W/m².
|
|
119
|
+
*
|
|
120
|
+
* The receiver is treated as facing each source — a parcel is a lump rather than a plane, so
|
|
121
|
+
* there is no one normal to take a cosine against. What that costs is that a flat plate edge-on
|
|
122
|
+
* to a fire receives as much as one facing it; what it buys is not asking a consumer for a normal
|
|
123
|
+
* per parcel, which is a number most of them do not have.
|
|
124
|
+
*/
|
|
125
|
+
fluxAt(x, y, z, surfaceTemperature, emissivity, occluded) {
|
|
126
|
+
let total = 0;
|
|
127
|
+
for (let source = 0; source < this.used; source++) {
|
|
128
|
+
if (!this.reaches(source, x, y, z))
|
|
129
|
+
continue;
|
|
130
|
+
const sx = this.x[source];
|
|
131
|
+
const sy = this.y[source];
|
|
132
|
+
const sz = this.z[source];
|
|
133
|
+
if (occluded !== null && occluded(sx, sy, sz, x, y, z))
|
|
134
|
+
continue;
|
|
135
|
+
const dx = x - sx;
|
|
136
|
+
const dy = y - sy;
|
|
137
|
+
const dz = z - sz;
|
|
138
|
+
total += radiantFlux(this.temperature[source], this.area[source], surfaceTemperature, emissivity, Math.sqrt(dx * dx + dy * dy + dz * dz), 1);
|
|
139
|
+
}
|
|
140
|
+
return total;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The temperature of the hottest source that reaches a point, or zero where none does.
|
|
144
|
+
*
|
|
145
|
+
* **What a radiative exchange is bounded by**, and the reason it has to be bounded at all: the
|
|
146
|
+
* radiative term is explicit, so a parcel whose surface shell has a small heat capacity — a coin,
|
|
147
|
+
* a nail, a leaf — can absorb more in one step than would take it past the source that is heating
|
|
148
|
+
* it, and then radiate the excess back as `T⁴` and ring itself to pieces. It was found by putting
|
|
149
|
+
* a copper coin on a hearth in `demo/dev/chemistry.ts`, where it reached 4,000 K on the first step
|
|
150
|
+
* and 100 K a minute later.
|
|
151
|
+
*
|
|
152
|
+
* Nothing can be heated past the thing heating it, so this is the equilibrium the step may reach
|
|
153
|
+
* and not exceed — which is what an implicit solve would give and costs one comparison per source.
|
|
154
|
+
*/
|
|
155
|
+
hottestAt(x, y, z, occluded) {
|
|
156
|
+
let hottest = 0;
|
|
157
|
+
for (let source = 0; source < this.used; source++) {
|
|
158
|
+
if (!this.reaches(source, x, y, z))
|
|
159
|
+
continue;
|
|
160
|
+
const sx = this.x[source];
|
|
161
|
+
const sy = this.y[source];
|
|
162
|
+
const sz = this.z[source];
|
|
163
|
+
if (occluded !== null && occluded(sx, sy, sz, x, y, z))
|
|
164
|
+
continue;
|
|
165
|
+
const temperature = this.temperature[source];
|
|
166
|
+
if (temperature > hottest)
|
|
167
|
+
hottest = temperature;
|
|
168
|
+
}
|
|
169
|
+
return hottest;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Whether **any** source reaches a point at all, which is the whole of the wake test.
|
|
173
|
+
*
|
|
174
|
+
* `§14`'s scan has to be coarse or sleeping saves nothing: this is a range comparison per source
|
|
175
|
+
* and no view factor, no temperature and no occlusion ray. A source's range already comes from its
|
|
176
|
+
* own power, so a bonfire wakes a parcel a candle would not.
|
|
177
|
+
*/
|
|
178
|
+
reachesPoint(x, y, z) {
|
|
179
|
+
for (let source = 0; source < this.used; source++) {
|
|
180
|
+
if (this.reaches(source, x, y, z))
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
grow() {
|
|
186
|
+
const size = this.x.length * 2;
|
|
187
|
+
const grow = (from) => {
|
|
188
|
+
const to = new Float64Array(size);
|
|
189
|
+
to.set(from);
|
|
190
|
+
return to;
|
|
191
|
+
};
|
|
192
|
+
this.x = grow(this.x);
|
|
193
|
+
this.y = grow(this.y);
|
|
194
|
+
this.z = grow(this.z);
|
|
195
|
+
this.temperature = grow(this.temperature);
|
|
196
|
+
this.area = grow(this.area);
|
|
197
|
+
this.range = grow(this.range);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { AtmosphereField } from '../field/atmosphere.ts';
|
|
2
|
+
import type { ParcelStore } from '../parcel/store.ts';
|
|
3
|
+
import { ContactSet } from './contacts.ts';
|
|
4
|
+
import { ChemistryEvents } from '../ignition/events.ts';
|
|
5
|
+
import { type OcclusionTest, RadiativeSources } from './radiation.ts';
|
|
6
|
+
export interface ChemistryWorldOptions {
|
|
7
|
+
/**
|
|
8
|
+
* How fast a porous parcel equalises its pore gas with the cell around it, per second.
|
|
9
|
+
*
|
|
10
|
+
* Scaled by porosity, so a substance nobody described as porous exchanges nothing. What this
|
|
11
|
+
* governs in practice: how quickly volatiles get out to burn, and how quickly oxygen gets in to
|
|
12
|
+
* burn them.
|
|
13
|
+
*/
|
|
14
|
+
readonly exchangeRate?: number;
|
|
15
|
+
/** Ambient gas speed where there is no field to ask, m/s. */
|
|
16
|
+
readonly stillAir?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class ChemistryWorld {
|
|
19
|
+
/** The parcels this world steps. Readable, because `fingerprintChemistry` hashes them. */
|
|
20
|
+
readonly parcels: ParcelStore;
|
|
21
|
+
/** The air, or `null` for a world with none. Readable for the same reason. */
|
|
22
|
+
readonly field: AtmosphereField | null;
|
|
23
|
+
/** Refilled by the caller each tick: what is radiating. */
|
|
24
|
+
readonly sources: RadiativeSources;
|
|
25
|
+
/** Refilled by the caller each tick: what is touching what. */
|
|
26
|
+
readonly contacts: ContactSet;
|
|
27
|
+
/** Drained by the caller each tick: what happened. Cleared at the top of every `simulate`. */
|
|
28
|
+
readonly events: ChemistryEvents;
|
|
29
|
+
/** Which slice of the sleeping set this tick's wake scan looks at. See `simulate`. */
|
|
30
|
+
private scanPhase;
|
|
31
|
+
/**
|
|
32
|
+
* Whether anything solid stands between two points.
|
|
33
|
+
*
|
|
34
|
+
* Absent, every source is visible — the cheap default, and wrong indoors. A consumer supplies a
|
|
35
|
+
* raycast; `§10` budgets and caches it, which is CH-6's work rather than this phase's.
|
|
36
|
+
*/
|
|
37
|
+
occluded: OcclusionTest | null;
|
|
38
|
+
private readonly exchangeRate;
|
|
39
|
+
private readonly stillAir;
|
|
40
|
+
private readonly scratch;
|
|
41
|
+
constructor(
|
|
42
|
+
/** The parcels this world steps. Readable, because `fingerprintChemistry` hashes them. */
|
|
43
|
+
parcels: ParcelStore,
|
|
44
|
+
/** The air, or `null` for a world with none. Readable for the same reason. */
|
|
45
|
+
field?: AtmosphereField | null, options?: ChemistryWorldOptions);
|
|
46
|
+
/**
|
|
47
|
+
* One fixed step, in the order `§13` gives.
|
|
48
|
+
*
|
|
49
|
+
* **Sleeping and the tier cadence are both here rather than inside the terms they skip**, because
|
|
50
|
+
* both are decisions about *whether* a parcel is stepped at all and a term that decided for itself
|
|
51
|
+
* would be four places deciding the same thing four ways.
|
|
52
|
+
*/
|
|
53
|
+
simulate(dt: number, windX?: number, windZ?: number): void;
|
|
54
|
+
/** What the fires can see of this parcel, into shell 0. */
|
|
55
|
+
private radiate;
|
|
56
|
+
/**
|
|
57
|
+
* Joules, clipped to what would bring the surface to `driving` and no further.
|
|
58
|
+
*
|
|
59
|
+
* Zero `driving` means nothing was found to be driving it, and then the joules pass through: a
|
|
60
|
+
* parcel radiating to a sky with no source in it is losing heat to the far field, and clipping
|
|
61
|
+
* that against zero kelvin would be clipping it against nothing.
|
|
62
|
+
*/
|
|
63
|
+
private bounded;
|
|
64
|
+
/** What the air around it is doing to it. */
|
|
65
|
+
private convect;
|
|
66
|
+
/** Heat across the faces the caller says are touching. */
|
|
67
|
+
private conductContacts;
|
|
68
|
+
/**
|
|
69
|
+
* The pore gas in a parcel relaxes toward the air around it.
|
|
70
|
+
*
|
|
71
|
+
* **One mechanism, both directions.** Volatiles a parcel's reactions made are above the cell's
|
|
72
|
+
* concentration and leave; oxygen its reactions consumed is below and comes in. Writing those as
|
|
73
|
+
* two rules would be two things to keep in step, and the second would be found wrong on the day a
|
|
74
|
+
* fire went out with oxygen still in the room.
|
|
75
|
+
*
|
|
76
|
+
* The target is what the pore volume would hold at the cell's own density, so a dense material
|
|
77
|
+
* with little void exchanges little — which is why a log smoulders and sawdust does not.
|
|
78
|
+
*/
|
|
79
|
+
private exchange;
|
|
80
|
+
/**
|
|
81
|
+
* Whether this parcel catches, keeps burning, or stops — and why.
|
|
82
|
+
*
|
|
83
|
+
* **Nothing here starts a fire.** Five conditions decide it, a pilot is one of them, and the
|
|
84
|
+
* pilot a caller supplied is spent at the end of the tick whether or not it was enough. That is
|
|
85
|
+
* `§11`, and it is why holding a match to wet wood does exactly nothing.
|
|
86
|
+
*/
|
|
87
|
+
private criteria;
|
|
88
|
+
/** How close a parcel is to catching, 0 to 1. A readout; nothing branches on it. */
|
|
89
|
+
ignitionProgressOf(parcel: number): number;
|
|
90
|
+
/** Moles of each element in the parcels and the air together. Writes into `out`. */
|
|
91
|
+
elementTotals(out: Float64Array): void;
|
|
92
|
+
}
|