export function evaluateSr02(input: Sr02Input): Sr02Snapshot {
const row = (key: keyof typeof ROWS) => ROWS[key];
const pack = (
key: keyof typeof ROWS,
make: (id: string, unit: string, kind: string, owner: string) => ScientificResult,
) => {
const r = row(key);
return make(r[0], r[1], r[2], r[3]);
};
if (input.mode === "apparatus") {
const sym = (key: keyof typeof ROWS, symbol: string) =>
pack(key, (id, unit, kind, owner) => asSymbolic(id, unit, kind, owner, symbol));
return Object.freeze({
beta: 0,
magneticFieldMagnet: sym("Bm", "B"),
electricFieldMagnet: sym("Em", "E"),
magneticFieldConductor: sym("Bc", "B'"),
electricFieldConductor: sym("Ec", "E'"),
forceMagnet: sym("Fm", "F"),
forceConductor: sym("Fc", "F'"),
emfMagnet: sym("em", "EMF"),
emfConductor: sym("ec", "EMF'"),
emfExcess: sym("xs", "gamma-1"),
invariantDot: sym("id", "E·B"),
invariantDifference: sym("iv", "E²-c²B²"),
lorentzFactor: sym("g", "gamma"),
pathParallel: sym("pp", "n·v"),
endpointOffset: sym("off", "Delta t'"),
circuitCurrent: sym("I", "I"),
});
}
const c = C_SI;
const v = input.speed;
const refuseAll = (reason: string, condition: string): Sr02Snapshot => {
const out = (key: keyof typeof ROWS) =>
pack(key, (id, unit, kind, owner) => asOutside(id, unit, kind, owner, condition, reason));
return Object.freeze({
beta: v / c,
magneticFieldMagnet: out("Bm"),
electricFieldMagnet: out("Em"),
magneticFieldConductor: out("Bc"),
electricFieldConductor: out("Ec"),
forceMagnet: out("Fm"),
forceConductor: out("Fc"),
emfMagnet: out("em"),
emfConductor: out("ec"),
emfExcess: out("xs"),
invariantDot: out("id"),
invariantDifference: out("iv"),
lorentzFactor: out("g"),
pathParallel: out("pp"),
endpointOffset: out("off"),
circuitCurrent: out("I"),
});
};
if (![v, input.magneticField, input.segmentLength, input.testCharge].every(Number.isFinite)) {
return refuseAll("Speed, field, length and charge must be finite.", "finite inputs");
}
if (Math.abs(v) >= c) {
return refuseAll("No inertial observer at |v| >= c.", "|v| < c");
}
if (input.segmentLength <= 0) {
return refuseAll("The declared path must have positive length.", "ℓ > 0");
}
const beta = v / c;
const g = gamma(beta);
const gmo = gammaMinusOne(beta);
if (g.status !== "value" || gmo.status !== "value") {
return refuseAll("No inertial observer at |v| >= c.", "|v| < c");
}
const γ = g.value;
let Bz = input.magneticField;
if (input.fieldModel === "dipole") {
const r = input.testPointDistance;
const B = dipoleField({ x: 0, y: 0, z: input.dipoleMoment }, { x: r, y: 0, z: 0 });
Bz = -B.z;
}
const E0: Vec3 = Object.freeze({ x: 0, y: 0, z: 0 });
const B0: Vec3 = Object.freeze({ x: 0, y: 0, z: Bz });
const primed = transformSI({ E: E0, B: B0, boost: v, c });
const F = lorentzForce(input.testCharge, E0, B0, { x: v, y: 0, z: 0 });
const Fp = lorentzForce(input.testCharge, primed.E, primed.B, { x: 0, y: 0, z: 0 });
const inv0 = fieldInvariants(E0, B0, c);
const inv1 = fieldInvariants(primed.E, primed.B, c);
void inv1;
const nDotV = input.pathOrientation === "along-motion" ? 1 : 0;
const offset =
input.pathOrientation === "along-motion" ? (γ * v * input.segmentLength) / (c * c) : 0;
const emfMagnet = Math.abs(v * Bz * input.segmentLength) * (1 - nDotV);
const emfConductor = γ * emfMagnet;
const classification =
nDotV === 0 ? { sign: "zero" as const } : classifyWithTolerance(nDotV, { absolute: 1e-12 });
const val = (key: keyof typeof ROWS, value: number) =>
pack(key, (id, unit, kind, owner) => asValue(id, unit, kind, owner, value));
const na = (key: keyof typeof ROWS, reason: string) =>
pack(key, (id, unit, kind, owner) => asNotApplicable(id, unit, kind, owner, reason));
const und = (key: keyof typeof ROWS) =>
pack(key, (id, unit, kind, owner) => asUnderdetermined(id, unit, kind, owner));
const emfMagnetOut = val("em", emfMagnet);
let emfConductorOut = val("ec", emfConductor);
let excessOut = val("xs", gmo.value);
if (classification.sign === "indeterminate") {
emfConductorOut = und("ec");
excessOut = und("xs");
} else if (classification.sign !== "zero" && !input.sliceDeclared) {
emfConductorOut = na("ec", PATH_REFUSAL_REASON);
excessOut = na("xs", PATH_REFUSAL_REASON);
} else if (classification.sign !== "zero") {
excessOut = na("xs", ALONG_MOTION_ZERO_REASON);
}
return Object.freeze({
beta,
magneticFieldMagnet: val("Bm", Bz),
electricFieldMagnet: val("Em", 0),
magneticFieldConductor: val("Bc", primed.B.z),
electricFieldConductor: val("Ec", primed.E.y),
forceMagnet: val("Fm", F.y),
forceConductor: val("Fc", Fp.y),
emfMagnet: emfMagnetOut,
emfConductor: emfConductorOut,
emfExcess: excessOut,
invariantDot: val("id", inv0.eDotB),
invariantDifference: val("iv", inv0.e2MinusC2B2),
lorentzFactor: val("g", γ),
pathParallel: val("pp", nDotV),
endpointOffset: val("off", offset),
circuitCurrent: na("I", "Current in a real circuit is not modeled; this model has no circuit."),
});
}