A reaction's rate constant, live¶
Arrhenius's law, k = A exp(−Eₐ / (R T)): a reaction's rate constant from its prefactor A and its activation energy Eₐ at a temperature T, R the molar gas constant. Below, a reaction with a prefactor of 10¹³ per second and an activation energy of 80 kJ/mol is taken from 250 K to 600 K, 2.5 K a step, twenty steps a second, and at each the library's law (lusine.laws.arrhenius) gives its rate constant on your GPU as you read. What the law promises is read off its Arrhenius plot: ln k against 1 / T is a straight line whose slope is −Eₐ / R.
This page runs the program on your GPU through WebGPU, so it needs a browser with WebGPU (a recent Chrome, Edge, Safari or Firefox). Without it the program does not run and nothing is drawn below.
What it shows¶
- The strip above the controls is green while every check holds, and turns red for good once one fails.
- The plot: ln k (of k in s⁻¹) against 1 / T, a point a step over one whole sweep, and beside it the straight line through the coolest point with the slope −Eₐ / R, drawn from that one point and the slope alone: the two lie one over the other. Below it, the temperature and the rate constant there, which grows by seven orders of magnitude over the sweep.
- The checks: the slope from the coolest point to the temperature now, beside −Eₐ / R; a line, true while that slope has stayed within 0.01% of −Eₐ / R at every temperature; rises with T, while the rate has risen at every step of the sweep; no barrier, while a reaction with no activation energy has gone at its prefactor at every temperature.
Every number here is computed by the program on the GPU; the page only reads the values back and draws them. The prefactor, the activation energy and the sweep are the program's settings, which the host may write; this page offers no control to change them yet.
The program¶
The sweep, the law's answers and the checks, the panels the page draws, and the one procedure, main, run each step: the sweep moves on, the law is asked at the temperature, at the coolest temperature and with no barrier, and the checks are taken.
// Arrhenius's law's live page: a reaction's temperature swept from 250 K to 600 K, the library's rate constant at each,
// read off as Arrhenius's plot of ln k against 1 / T, with the page's checks computed here on the GPU and shown by the
// panels the program declares.
namespace arrhenius;
lusine.version version = { { number = 1 } };
// The reader's settings: the reaction's prefactor and activation energy, and the sweep: the temperature it starts at
// (also the reference the slope is taken from) and how far each step moves it.
host struct {
float<s^-1> prefactor = 10000000000000.0 s^-1; float<J/mol> activation = 80000.0 J/mol;
float<K> coolest = 250.0 K; float<K> stride = 2.5 K;
} settings;
// The sweep: the step it is at, from 0 to 140 and round again, the temperature there and its inverse.
struct { integer step; float<K> temperature; float<K^-1> inverse_temperature; } sweep;
// The rate constant at the temperature, its logarithm (of k in s^-1), the logarithm at the coolest temperature, the
// slope of the line from that point to this one on Arrhenius's plot, beside -Ea / R, the slope the law's plot has, and
// the straight line of that slope through the coolest point, at this temperature; and the last step's rate.
struct { float<s^-1> rate; float log_rate; float log_coolest; float<K> slope; float<K> expected; float line;
float<s^-1> before; } law;
// The checks: the plot a line of slope -Ea / R, within 0.01% at every temperature; the rate rising with the
// temperature at every step of the sweep; and with no barrier, the rate the prefactor at every temperature.
struct { bool line = true; bool rises = true; bool no_barrier = true; } checks;
// A strip of lamps the page draws, lit green while every check holds and red once one fails.
struct lamp { vector<float, 4> colour; };
lattice<lamp, 2> lamps(spacing = 1 m, boundary = wall);
vector<float, 4> lamp_colour(bool holds)
{
return holds ? vector<float, 4>(0.15, 0.6, 0.3, 1.0) : vector<float, 4>(0.75, 0.15, 0.15, 1.0);
}
output table<lusine.widget> panels = {
lusine.series { { panel = "the plot", chart = "ln k against 1 / T", quantity = law.log_rate, steps = 141,
against = sweep.inverse_temperature, label = "ln k" } },
lusine.series { { panel = "the plot", chart = "ln k against 1 / T", quantity = law.line, steps = 141,
against = sweep.inverse_temperature, label = "slope -Ea / R" } },
lusine.value { { panel = "the plot", quantity = sweep.temperature, label = "temperature" } },
lusine.value { { panel = "the plot", quantity = law.rate, label = "rate constant" } },
lusine.value { { panel = "the checks", quantity = law.slope, label = "slope" } },
lusine.value { { panel = "the checks", quantity = law.expected, label = "-Ea / R" } },
lusine.value { { panel = "the checks", quantity = checks.line, label = "a line" } },
lusine.value { { panel = "the checks", quantity = checks.rises, label = "rises with T" } },
lusine.value { { panel = "the checks", quantity = checks.no_barrier, label = "no barrier" } },
};
float<K> arrhenius_slope(float log_rate, float log_coolest, float<K^-1> inverse_temperature, float<K> coolest)
{
return (log_rate - log_coolest) / (inverse_temperature - 1.0 / coolest);
}
void main()
{
sweep.step = sweep.step >= 140 ? 0 : sweep.step + 1;
sweep.temperature = settings.coolest + float(sweep.step) * settings.stride;
sweep.inverse_temperature = 1.0 / sweep.temperature;
law.before = law.rate;
law.rate = lusine.laws.arrhenius(settings.prefactor, settings.activation, sweep.temperature);
law.log_rate = log(law.rate * 1.0 s);
law.log_coolest = log(lusine.laws.arrhenius(settings.prefactor, settings.activation,
temperature = settings.coolest) * 1.0 s);
law.slope = sweep.step == 0 ? law.slope : arrhenius_slope(law.log_rate, law.log_coolest, sweep.inverse_temperature,
coolest = settings.coolest);
law.expected = -settings.activation / lusine.constants.R;
law.line = law.log_coolest + law.expected * (sweep.inverse_temperature - 1.0 / settings.coolest);
checks.line = checks.line && (sweep.step == 0 || abs(law.slope / law.expected - 1.0) <= 0.0001);
checks.rises = checks.rises && (sweep.step == 0 || law.rate > law.before);
checks.no_barrier = checks.no_barrier
&& abs(lusine.laws.arrhenius(settings.prefactor, activation = 0.0 J/mol, sweep.temperature)
/ settings.prefactor - 1.0) <= 0.000001;
lamps.colour = lamp_colour(holds = checks.line && checks.rises && checks.no_barrier);
}
The law's other proof (the rate against a reference over reactions from cool to hot) is on its proof page.