The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.
ionizationCount · src/physics/reference/photoelectric.ts · this site’s source, as built · sha256:b9faf7cd99d516b3082ef66c3793b0bc08fdc6c5c1b8fe35cd73b2da43f238cb
This function computes the listed outputs when it runs.
In words
The evaluator behind this laboratory. It checks the frequency against the ionization energy, resolves the incident and absorbed powers through the absorption efficiency, multiplies the absorbed power by the duration to get the absorbed energy, divides that by h nu for the number of absorbed quanta, and then lets the absorption mode decide what may be said about ionization. Every number it returns carries its own status, so the quanta can be a value on a page where the ionization count is a refusal.
Implementation
/**
* Ionization count and rate evaluator (Paper 1, §9, LQ-09).
* Computes absorbed light energy L = P_abs * t, absorbed quanta rate dot_N_abs = P_abs / (h*nu),
* and ionization counts/rates under the three epistemic absorption modes:
* - "all-absorbed-ionizes": j = L / (R*beta*nu) or N_ion = L / (h*nu)
* - "declared-fraction": N_ion = a * L / (h*nu)
* - "unknown": underdetermined with N_abs as an upper bound.
* Below threshold (h*nu < J_mol), ionization count and rate are strictly not-applicable.
*/
export function ionizationCount(input: IonizationCountInput): IonizationCountResult {
const {
nu,
ionizationEnergyJoules,
ionizationEnergyEv,
absorbedPowerWatts: rawAbsPower,
incidentPowerWatts: rawIncPower,
absorptionEfficiency: rawEta,
durationSeconds = 1.0,
absorptionMode = "all-absorbed-ionizes",
declaredFraction = 1.0,
gasCitation,
set,
} = input;
const bounds = ionizationBounds({
nu,
ionizationEnergyJoules,
ionizationEnergyEv,
gasCitation,
set,
});
if (bounds.status === "outside-domain") {
const refusal = outsideDomain(
bounds.refusalCode ?? "outside-domain",
"model",
bounds.verdictReason,
);
return Object.freeze({
status: "outside-domain",
bounds,
incidentPowerWatts: rawIncPower ?? 0,
absorbedPowerWatts: rawAbsPower ?? 0,
durationSeconds,
absorbedLightEnergyJoules: 0,
incidentQuantumRatePerSecond: refusal,
absorbedQuantumRatePerSecond: refusal,
absorbedQuantaCount: refusal,
ionizationRatePerSecond: refusal,
ionizationCountMolecules: refusal,
ionizedGramMolecules: refusal,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "outside-domain",
reason: bounds.verdictReason,
});
}
// Resolve powers
let pOpt = 0;
let pAbs = 0;
const eta = rawEta !== undefined ? rawEta : 1.0;
if (rawAbsPower !== undefined) {
pAbs = rawAbsPower;
pOpt = rawIncPower !== undefined ? rawIncPower : eta > 0 ? pAbs / eta : pAbs;
} else if (rawIncPower !== undefined) {
pOpt = rawIncPower;
pAbs = eta * pOpt;
}
if (pOpt < 0 || pAbs < 0 || !Number.isFinite(pOpt) || !Number.isFinite(pAbs)) {
const refusal = outsideDomain(
"negative-power",
"physical",
"Power values must be non-negative finite numbers.",
);
return Object.freeze({
status: "outside-domain",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: 0,
incidentQuantumRatePerSecond: refusal,
absorbedQuantumRatePerSecond: refusal,
absorbedQuantaCount: refusal,
ionizationRatePerSecond: refusal,
ionizationCountMolecules: refusal,
ionizedGramMolecules: refusal,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "outside-domain",
reason: "Power values must be non-negative finite numbers.",
});
}
if (eta < 0 || eta > 1 || !Number.isFinite(eta)) {
const refusal = outsideDomain(
"invalid-absorption-efficiency",
"model",
"Absorption efficiency must lie in [0, 1].",
);
return Object.freeze({
status: "outside-domain",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: 0,
incidentQuantumRatePerSecond: refusal,
absorbedQuantumRatePerSecond: refusal,
absorbedQuantaCount: refusal,
ionizationRatePerSecond: refusal,
ionizationCountMolecules: refusal,
ionizedGramMolecules: refusal,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "outside-domain",
reason: "Absorption efficiency must lie in [0, 1].",
});
}
if (durationSeconds < 0 || !Number.isFinite(durationSeconds)) {
const refusal = outsideDomain(
"negative-duration",
"physical",
"Duration must be non-negative.",
);
return Object.freeze({
status: "outside-domain",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: 0,
incidentQuantumRatePerSecond: refusal,
absorbedQuantumRatePerSecond: refusal,
absorbedQuantaCount: refusal,
ionizationRatePerSecond: refusal,
ionizationCountMolecules: refusal,
ionizedGramMolecules: refusal,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "outside-domain",
reason: "Duration must be non-negative.",
});
}
const h = getPlanckConstant(set);
const na = getAvogadroConstant(set);
const eqJ = h * nu;
const incQRate = eqJ > 0 ? pOpt / eqJ : 0;
const absQRate = eqJ > 0 ? pAbs / eqJ : 0;
const lAbs = pAbs * durationSeconds;
const absQCount = eqJ > 0 ? lAbs / eqJ : 0;
const incQRateRes = ok(incQRate, "s^-1");
const absQRateRes = ok(absQRate, "s^-1");
const absQCountRes = ok(absQCount);
// Sub-threshold check
if (!bounds.singleQuantumAllowed) {
const notApp = notApplicable("no single-quantum ionization under this hypothesis");
return Object.freeze({
status: "not-applicable",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: lAbs,
incidentQuantumRatePerSecond: incQRateRes,
absorbedQuantumRatePerSecond: absQRateRes,
absorbedQuantaCount: absQCountRes,
ionizationRatePerSecond: notApp,
ionizationCountMolecules: notApp,
ionizedGramMolecules: notApp,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "below-threshold",
reason: "Incident photon energy h*nu is below the ionization threshold J_mol.",
});
}
// Supra-threshold regimes
if (absorptionMode === "unknown") {
const underdet = underdetermined("sub-quantum-efficiency", [
"ionization-efficiency-fraction",
"non-ionizing-absorption-channels",
]);
return Object.freeze({
status: "underdetermined",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: lAbs,
incidentQuantumRatePerSecond: incQRateRes,
absorbedQuantumRatePerSecond: absQRateRes,
absorbedQuantaCount: absQCountRes,
ionizationRatePerSecond: underdet,
ionizationCountMolecules: underdet,
ionizedGramMolecules: underdet,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "unknown-absorption",
reason: `Unknown fraction of absorbed energy goes to non-ionizing channels; count is bounded above by absorbed quanta N_abs = ${absQCount.toExponential(4)}.`,
});
}
if (absorptionMode === "declared-fraction") {
if (declaredFraction < 0 || declaredFraction > 1 || !Number.isFinite(declaredFraction)) {
const refusal = outsideDomain(
"invalid-fraction",
"model",
"Declared ionization fraction must lie in [0, 1].",
);
return Object.freeze({
status: "outside-domain",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: lAbs,
incidentQuantumRatePerSecond: incQRateRes,
absorbedQuantumRatePerSecond: absQRateRes,
absorbedQuantaCount: absQCountRes,
ionizationRatePerSecond: refusal,
ionizationCountMolecules: refusal,
ionizedGramMolecules: refusal,
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "outside-domain",
reason: "Declared ionization fraction must lie in [0, 1].",
});
}
const ionRate = declaredFraction * absQRate;
const ionCount = declaredFraction * absQCount;
const jMol = na > 0 ? ionCount / na : 0;
return Object.freeze({
status: "value",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: lAbs,
incidentQuantumRatePerSecond: incQRateRes,
absorbedQuantumRatePerSecond: absQRateRes,
absorbedQuantaCount: absQCountRes,
ionizationRatePerSecond: ok(ionRate, "s^-1"),
ionizationCountMolecules: ok(ionCount),
ionizedGramMolecules: ok(jMol, "mol"),
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "declared-fraction",
reason: `Declared fraction a = ${declaredFraction.toFixed(4)} of absorbed light quanta produce ionization.`,
});
}
// Primary paper hypothesis: all absorbed light ionizes
const ionRate = absQRate;
const ionCount = absQCount;
const jMol = na > 0 ? ionCount / na : 0;
return Object.freeze({
status: "value",
bounds,
incidentPowerWatts: pOpt,
absorbedPowerWatts: pAbs,
durationSeconds,
absorbedLightEnergyJoules: lAbs,
incidentQuantumRatePerSecond: incQRateRes,
absorbedQuantumRatePerSecond: absQRateRes,
absorbedQuantaCount: absQCountRes,
ionizationRatePerSecond: ok(ionRate, "s^-1"),
ionizationCountMolecules: ok(ionCount),
ionizedGramMolecules: ok(jMol, "mol"),
ionsPerAbsorbedQuantumUpperBound: 1.0,
absorptionConditionLabel: "all-absorbed-ionizes",
reason:
"Under Einstein's §9 hypothesis that every absorbed light quantum ionizes one molecule: j = L / (R*beta*nu).",
});
}