Chemistry · class

ParcelStore

Explained in Chemistry.

class ParcelStore
import { ParcelStore } from '@driftengine/chemistry';

Constructor

new

constructor(substances: SubstanceRegistry, species?: import("../index.ts").SpeciesRegistry)
ParameterTypeDescription
substancesSubstanceRegistry
species?import("../index.ts").SpeciesRegistry

Accessors

NameTypeDescription
countgetnumberHandles ever issued, live or not. A destroyed parcel still occupies its index.
substanceCountgetnumberHow many substances this store's registry holds. fingerprintChemistry covers all of them.

Methods

spawn

spawn(spec: ParcelSpec): number
ParameterTypeDescription
specParcelSpec

areaOf

areaOf(parcel: number): number

m², what the environment touches. Zero means the caller has not said, and conduction refuses.

ParameterTypeDescription
parcelnumber

destroy

destroy(parcel: number): void
ParameterTypeDescription
parcelnumber

alive

alive(parcel: number): boolean
ParameterTypeDescription
parcelnumber

substanceOf

substanceOf(parcel: number): number
ParameterTypeDescription
parcelnumber

massOf

massOf(parcel: number): number
ParameterTypeDescription
parcelnumber

volumeOf

volumeOf(parcel: number): number

m³, from mass and the substance's density. Shrinks as mass leaves, which is why a log burns down.

ParameterTypeDescription
parcelnumber

shellCount

shellCount(parcel: number): number

Shells resolved right now, which a level-of-detail tier may have reduced.

ParameterTypeDescription
parcelnumber
More

Everything that walks a parcel's depth reads this rather than the substance's declared count, so a Distant log is one shell everywhere at once and nothing has to be told which tier it is at.

declaredShellsOf

declaredShellsOf(parcel: number): number

What the substance asked for, which is the resolution Hero restores to.

ParameterTypeDescription
parcelnumber

tierOf

tierOf(parcel: number): Tier
ParameterTypeDescription
parcelnumber

cadenceOf

cadenceOf(parcel: number): number

Ticks between updates at this parcel's tier. §14's cadence column.

ParameterTypeDescription
parcelnumber

substepsOf

substepsOf(parcel: number): number

The most sub-steps a reaction solve will take at this parcel's tier.

ParameterTypeDescription
parcelnumber

setTier

setTier(parcel: number, tier: Tier): void

Move a parcel to a tier, conserving mass, every element and enthalpy exactly.

ParameterTypeDescription
parcelnumber
tierTier
More

§14: "a tier change does not lose or gain mass or energy... which is the property that makes the LOD safe to apply to a burning object mid-burn." Shells are equal-mass by construction, so a fold is a proportional redistribution — target j takes source shells [j·n/m, (j+1)·n/m), splitting one where the ratio is not an integer — and the targets come out equal-mass too.

What it loses is the profile inside a fold, which is the point of a tier: two shells at 400 K and 300 K become one at their mass-weighted mean, and going back up hands each of them the same mean. Resolution in depth is what a distant object gives away.

enthalpyOf

enthalpyOf(parcel: number): number

Total joules held across every shell, not joules per kilogram.

ParameterTypeDescription
parcelnumber

shellEnthalpyOf

shellEnthalpyOf(parcel: number, shell: number): number
ParameterTypeDescription
parcelnumber
shellnumber

shellTemperatureOf

shellTemperatureOf(parcel: number, shell: number): number

The temperature of one shell, K. Shell 0 is the surface and the last is the core.

ParameterTypeDescription
parcelnumber
shellnumber
More

surfaceTemperatureOf is what decides ignition and coreTemperatureOf is what decides doneness, and the gap between them is the whole reason shells exist.

surfaceTemperatureOf

surfaceTemperatureOf(parcel: number): number
ParameterTypeDescription
parcelnumber

coreTemperatureOf

coreTemperatureOf(parcel: number): number
ParameterTypeDescription
parcelnumber

positionX

positionX(parcel: number): number
ParameterTypeDescription
parcelnumber

positionY

positionY(parcel: number): number
ParameterTypeDescription
parcelnumber

positionZ

positionZ(parcel: number): number
ParameterTypeDescription
parcelnumber

move

move(parcel: number, x: number, y: number, z: number): void
ParameterTypeDescription
parcelnumber
xnumber
ynumber
znumber

temperatureOf

temperatureOf(parcel: number): number

The temperature this parcel would have if it were stirred, K.

ParameterTypeDescription
parcelnumber
More

A read rather than a stored field, and that is the design. Nothing writes a temperature except setTemperature, which writes the enthalpy that produces one — so a parcel on a plateau simply does not move, and no code anywhere had to know a plateau exists.

The temperature of the total enthalpy, not the mean of the shell temperatures. The two differ wherever the curve bends, and only this one has the property that matters: heat in, temperature out, with nothing lost between them.

addHeat

addHeat(parcel: number, joules: number): void

Joules in, spread evenly through the depth. Negative cools. A microwave, or a stirred pot.

ParameterTypeDescription
parcelnumber
joulesnumber

addSurfaceHeat

addSurfaceHeat(parcel: number, joules: number): void

Joules into shell 0 alone, which is what a flux from outside actually does.

ParameterTypeDescription
parcelnumber
joulesnumber
More

Every radiative, convective and contact term in CH-5 lands here. The difference between this and addHeat is the difference between a log in front of a fire and a log in an oven.

conduct

conduct(parcel: number, dt: number): void

Conduct heat inward one step of dt seconds.

ParameterTypeDescription
parcelnumber
dtnumber
More

Conserves this parcel's total enthalpy exactly — the arithmetic is in conduction.ts and so is the reason.

setTemperature

setTemperature(parcel: number, temperature: number): void

Force a temperature by writing the enthalpy that produces it.

ParameterTypeDescription
parcelnumber
temperaturenumber
More

For authoring and for tests. Not for a tick: it discards whatever enthalpy the parcel held, so a boiling parcel set to its own boiling point loses however much of the plateau it had crossed, and the energy budget does not close across the call.

setShellTemperature

setShellTemperature(parcel: number, shell: number, temperature: number): void

Force one shell's temperature. Authoring and tests only, for the reason above.

ParameterTypeDescription
parcelnumber
shellnumber
temperaturenumber

speciesMassOf

speciesMassOf(parcel: number, shell: number, species: number): number

Kilograms of one species in one shell. Zero where the substance cannot hold it at all.

ParameterTypeDescription
parcelnumber
shellnumber
speciesnumber

parcelSpeciesMass

parcelSpeciesMass(parcel: number, species: number): number

The same, summed over every shell.

ParameterTypeDescription
parcelnumber
speciesnumber

shellMassOf

shellMassOf(parcel: number, shell: number): number

Kilograms in one shell, summed over its species. Invariant under reaction.

ParameterTypeDescription
parcelnumber
shellnumber

elementTotalsOf

elementTotalsOf(parcel: number, out: Float64Array): void

Moles of each element this parcel holds. Writes into out, allocates nothing.

ParameterTypeDescription
parcelnumber
outFloat64Array

chemicalEnergyOf

chemicalEnergyOf(parcel: number): number

Energy locked in this parcel's chemical bonds, J.

ParameterTypeDescription
parcelnumber
More

Neither this nor the thermal enthalpy is conserved on its own — a reaction moves energy between them. Their sum is, exactly, because the same number is added to one and taken from the other in reactShell.

react

react(parcel: number, dt: number, transportFactor?: number): void

Run this parcel's reactions in every shell for dt seconds.

ParameterTypeDescription
parcelnumber
dtnumber
transportFactor?number

sleeping

sleeping(parcel: number): boolean

Whether this parcel is being skipped entirely.

ParameterTypeDescription
parcelnumber
More

§14's thermal quiescence, and the vocabulary is deliberately Track B's. A cold stone floor is a thousand of these and costs a bounded scan.

wake

wake(parcel: number): void

Wake it, and reset the count that would put it back.

ParameterTypeDescription
parcelnumber
More

Called by everything that could change what a parcel is: heat arriving, water poured on, mass added, a contact, or a source coming within range. Cheap enough — a flag and an integer — that a caller never has to decide whether it is worth it.

settle

settle(parcel: number): boolean

Decide whether this parcel is still enough to sleep, and put it to sleep if it has been for long enough. Returns whether it is now asleep.

ParameterTypeDescription
parcelnumber
More

A burning or smouldering parcel never sleeps, whatever its numbers say. A steady flame is a steady parcel by every measure here, and sleeping one would put out a fire by optimising it.

accrue

accrue(parcel: number, dt: number): number

Seconds this parcel is owed by its cadence, and whether its turn has come.

ParameterTypeDescription
parcelnumber
dtnumber
More

A Far parcel is stepped every fourth tick with four ticks of dt, so the same seconds are integrated either way — which is what makes a cadence conserving for free rather than by care.

burning

burning(parcel: number): boolean

Whether a flame stands over this parcel.

ParameterTypeDescription
parcelnumber

smouldering

smouldering(parcel: number): boolean

Whether its char is glowing, which needs no flame and far less oxygen.

ParameterTypeDescription
parcelnumber

piloted

piloted(parcel: number): boolean

Whether a pilot is present this tick.

ParameterTypeDescription
parcelnumber

ignite

ignite(parcel: number): void

Supply a pilot for one tick. This does not start a fire, and §11 is why.

ParameterTypeDescription
parcelnumber
More

Ignition is five conditions, and a pilot is one of them. Holding a match to wet wood does exactly nothing here, which is correct and is the API telling the truth about what a match is.

douse

douse(parcel: number): void

Put the flame out. Embers survive, which is the point — see §12.

ParameterTypeDescription
parcelnumber

setBurning

setBurning(parcel: number, burning: boolean): void
ParameterTypeDescription
parcelnumber
burningboolean

setSmouldering

setSmouldering(parcel: number, smouldering: boolean): void
ParameterTypeDescription
parcelnumber
smoulderingboolean

clearPilot

clearPilot(parcel: number): void
ParameterTypeDescription
parcelnumber

massFluxOf

massFluxOf(parcel: number): number

kg/(m²·s) of gas leaving the surface, all of it.

ParameterTypeDescription
parcelnumber

fuelFluxOf

fuelFluxOf(parcel: number): number

And of the part of it that can burn — which is the criterion ignition turns on.

ParameterTypeDescription
parcelnumber
More

The two differ by exactly what a wet log gives off. Steam leaving a surface counts toward diluting the mixture above it and not toward feeding it, which is why 60% moisture stops a log catching in front of a fire that lights a seasoned one in eighty seconds.

setMassFlux

setMassFlux(parcel: number, total: number, fuel: number): void
ParameterTypeDescription
parcelnumber
totalnumber
fuelnumber

heatReleaseOf

heatReleaseOf(parcel: number): number

Watts the reactions released last step: positive exothermic, negative endothermic.

ParameterTypeDescription
parcelnumber
More

A fire's size is its heat release rate, which is what every figure in the design is quoted against and is not the temperature. A pyrolysing surface reads negative here while it is gasifying, which is right and is why a flame has to keep feeding heat back to sustain one.

moistureOf

moistureOf(parcel: number): number

Kilograms of water per kilogram of dry matter — the dry basis, which is how it is quoted.

ParameterTypeDescription
parcelnumber
More

"12% moisture content" means twelve kilograms of water per hundred of dry wood, and wetComposition folded that into a wet-basis 0.107 to store it. Undoing the same conversion here is what makes a script comparing against the 0.25 that decides whether a log is worth burning read the quantity the author wrote.

Ice counts as water. A frozen log is not a dry one.

parcelPhaseOf

parcelPhaseOf(parcel: number): number

PHASE_SOLID, PHASE_LIQUID, PHASE_GAS, or PHASE_MIXED where no one of them dominates.

ParameterTypeDescription
parcelnumber
More

Mixed is a real answer rather than a failure to decide: wet wood is wood and water at once, and calling it either would be a lie a consumer would then draw.

wetnessOf

wetnessOf(parcel: number): number

Kilograms of free water on the surface, per square metre of it.

ParameterTypeDescription
parcelnumber
More

Free, meaning beyond what the material holds as its own moisture, and that distinction is the whole reading. A seasoned oak log carries 12% water on a dry basis and is not glossy; a log somebody threw a bucket over is. Reporting the shell's total water would make the two identical and every piece of wood in a scene look rained on.

Derived rather than a column, because wet puts real water in the surface shell rather than setting a flag — so §17's wet-film roughness reads a quantity the boiling reaction is simultaneously consuming, which is what makes drying visible.

wettable

wettable(parcel: number): boolean

Whether this parcel's substance can hold liquid water, which is what wet asks of it.

ParameterTypeDescription
parcelnumber
More

A caller pouring rain over everything asks first: wet refuses a material with no water in its model, and refusing is right, but a loop over every parcel in the rain had no way to tell an iron nail from a log before it was told.

wet

wet(parcel: number, kilograms: number): void

Pour water on it: liquid water into the surface shell, where the boiling reaction will find it.

ParameterTypeDescription
parcelnumber
kilogramsnumber
More

Not a flag, and that is the whole point. The heat sink §12 describes is the same 2.44 MJ/kg every other drop of water in this model carries, so a doused fire goes out for the reason a real one does and a light sprinkling on a hot ember does not.

Refused, naming the substance, where that substance cannot hold water at all — which means it declares no reaction that touches it. Silently doing nothing would make "I poured a bucket on it" and "this material has no water in its model" indistinguishable.

dry

dry(parcel: number, kilograms: number): void

Take surface water away: a cloth, a hot dry pan, or wind. Never below what is there.

ParameterTypeDescription
parcelnumber
kilogramsnumber

charDepthOf

charDepthOf(parcel: number): number

Metres of char measured inward from the surface.

ParameterTypeDescription
parcelnumber
More

The shells that are mostly carbon, summed by their own thickness. A shell counts once it is past half char, which is a threshold on a continuum and is said out loud: a finer answer needs a char front within a shell, which is a sub-shell quantity this model does not carry.

structuralIntegrityOf

structuralIntegrityOf(parcel: number): number

How much of this parcel is still load-bearing, 1 down to 0.

ParameterTypeDescription
parcelnumber
More

§18 refuses structural failure in writing and this is the refusal reporting. A beam whose char depth has eaten a fraction of its thickness has lost that fraction of its strength, and what happens next — a joint releasing, a body splitting, a building coming down — belongs to whoever owns the solver. This package writes the scalar, emits an event at a threshold, and breaks nothing.

mix

mix(from: number, into: number): void

Pour from into into; from is consumed. Mass, elements and enthalpy all conserved exactly.

ParameterTypeDescription
fromnumber
intonumber
More

Both must be the same substance, and two that are not are refused naming both. Two substances are two species sets and two shell counts, so mixing them would silently discard whichever set was narrower — and a stew is not a substance change. What a consumer combining two genuinely different materials wants is a third substance that is the mixture, authored as one.

addShellHeat

addShellHeat(parcel: number, shell: number, joules: number): void

Joules into one named shell.

ParameterTypeDescription
parcelnumber
shellnumber
joulesnumber
More

The difference between this and addSurfaceHeat is a microwave and an oven: one deposits where the field penetrates to, the other at the surface. Nothing here decides which is physical.

volumeShareOf

volumeShareOf(parcel: number): number

What fraction of its original volume is left, as mass leaves it.

ParameterTypeDescription
parcelnumber
More

The number a shrinking log is drawn from. A parcel's volume is its mass over its substance's density, and the density does not change — so this is simply what share of the mass is still here, which falls as volatiles leave and rises for nothing.

surfaceHeatCapacityOf

surfaceHeatCapacityOf(parcel: number): number

Joules per kelvin of the surface shell, from what it is made of right now.

ParameterTypeDescription
parcelnumber
More

What every explicit surface term has to be bounded against. A flux times an area times a step is joules, and joules into a small enough shell is an arbitrarily large temperature change — so a term that does not know this number cannot tell the difference between heating something and destroying it. ChemistryWorld uses it to stop a radiative or convective step carrying a surface past the thing driving it.

appearanceOf

appearanceOf(parcel: number): AppearanceModel | null

What this parcel's substance looks like, or null where nobody said.

ParameterTypeDescription
parcelnumber

charFractionOf

charFractionOf(parcel: number): number

Share of this parcel's mass that is char, 0..1.

ParameterTypeDescription
parcelnumber

addSpeciesMass

addSpeciesMass(parcel: number, shell: number, species: number, kilograms: number): void

Add (or with a negative value, remove) mass of one species in one shell.

ParameterTypeDescription
parcelnumber
shellnumber
speciesnumber
kilogramsnumber

substanceDigestOf

substanceDigestOf(substance: number): number

A number standing for what a registered substance is, for the fingerprint.

ParameterTypeDescription
substancenumber
More

Its density, porosity, emissivity, shell count and the species it can hold — enough that two libraries differing anywhere disagree here, and cheap enough to compute once a tick. §15 asks for the registry to be covered because two worlds with different substances are different worlds, and a replay crossing them should say so rather than diverge later.

speciesSetOf

speciesSetOf(substance: number): Int32Array

Global species indices this parcel's substance can hold.

ParameterTypeDescription
substancenumber

idOfSpecies

idOfSpecies(species: number): string
ParameterTypeDescription
speciesnumber

phaseOf

phaseOf(species: number): number
ParameterTypeDescription
speciesnumber

emissivityOf

emissivityOf(parcel: number): number
ParameterTypeDescription
parcelnumber

ignitionOf

ignitionOf(substance: number): IgnitionModel | null

When this parcel's substance catches, or null where nobody said it does.

ParameterTypeDescription
substancenumber

porosityOf

porosityOf(parcel: number): number
ParameterTypeDescription
parcelnumber

conductivityOf

conductivityOf(parcel: number, temperature: number): number

W/(m·K) at a temperature, or zero where the substance never said.

ParameterTypeDescription
parcelnumber
temperaturenumber

shellDepthOf

shellDepthOf(parcel: number, shell: number): number

Metres from the outside of shell shell to the inside of it.

ParameterTypeDescription
parcelnumber
shellnumber