Skip to content

How the arithmetic is checked

Every number this site computes is checked, automatically, every time the code changes. "Pinned" means the answer is written down in a test beside the code: if the code ever stops producing that answer, the change does not ship. This page lists what is written down, what it was checked against, and how close the two have to be — including the places where the check is weaker than it looks.

  • The first column is what was compared with what. Where the reference is another program it is named; where it is a field whose answer is known in closed form, it says so.
  • The middle column is the bound an automated test enforces. It is the real strength of each claim, and it is stated even where it is embarrassing — one bound on this page is a factor of three, and it says so rather than being left out.
  • The last column is what would slip through if that check were deleted. It is there because a tolerance on its own tells you nothing about which mistakes it would catch.
  • Under every comparison are the test files that assert it, by path. Those files are in the repository, and a build in which one of them stops containing the assertion named here fails rather than quietly going out of date.

Interpolation

Two ways of turning wells into a surface, both checked against arithmetic done somewhere else. The kriging numbers are pinned against two independent implementations at once, and the reference values they are pinned to are written by a script committed beside them rather than typed in by hand.

Inverse distance weighting, against the same formula in NumPy

Method.
Three synthetic well sets are interpolated at a grid of query points and at every well itself, and each value is compared with the same closed form evaluated in NumPy over the same double-precision numbers.
Reference.
NumPy, driven by the generator committed at src/engine/__tests__/fixtures/make_fixtures.py. There is no published worked example for an inverse-distance surface, and this does not pretend there is one: the reference is a second implementation of a formula both sides agree on.
What is comparedWhat the test enforcesWhat a failure would mean
Every interpolated value, against the NumPy evaluation1e-12 relativea wrong power, a wrong neighbour set, or a missing normalisation
A query sitting exactly on a wellthat well’s own value, exactlythe exact-hit rule quietly becoming an average
The weights anywhere away from a wellnever negative, and summing to 1 to 12 decimal placesa surface no longer bounded by the readings that made it
The two nearest wells’ share of the weighttheir distance ratio raised to the power, to 8 decimal placesthe power applied to the wrong quantity
A query with no well inside the search radiusnot a number, exactly as the reference reports itan empty neighbourhood filled in with something invented
The well sets are synthetic and generated from fixed seeds in that same script. Nothing on this page was computed from anybody’s site.
  • Agreement here says the formula was transcribed correctly. It says nothing about whether inverse distance weighting is the right choice for a water table, which is a judgement about the site and not about the code.

Asserted by src/engine/__tests__/idw.test.ts

Ordinary kriging, against PyKrige and against an independent solve

Method.
The same wells, the same variogram parameters and the same query points go through three different pieces of algebra — an explicit matrix inverse, a from-scratch solve, and this engine’s factorisation of the covariance form — and the three sets of answers are compared with each other.
Reference.
PyKrige 1.7.3, OrdinaryKriging, which forms an explicit inverse of the indefinite bordered system. · A from-scratch NumPy solve of the bordered system, written for this repository and run in the same script.
What is comparedWhat the test enforcesWhat a failure would mean
Every estimate, against both references1e-9 relativea sign error on the Lagrange multiplier, which moves an answer by about a hundredth
Every kriging variance1e-9 times the sill, as an absolute bounda variance that is right at the wells and wrong between them
The estimate at a well, with a nugget and without onethat well’s own value, to 1e-9 relativean interpolator that has stopped honouring its own data
The weights at any query pointsumming to 1 to 10 decimal placesthe unbiasedness constraint dropped somewhere in the solve
Kriging asked for with fewer than 8 wellsrefused in plain words, pointing at inverse distance weightinga variogram fitted to almost nothing, presented as a surface
Both estimates and kriging variances, at a grid of queries and at every well, for each model with a nugget and without one.
  • The three implementations are algebraically identical and numerically distinct, so this checks the transcription and the solve. All three would agree on the same wrong model, and none of them knows whether the variogram describes your aquifer.
  • Kriging here is global and isotropic. Every well enters every node’s system, and there is no anisotropy anywhere in the engine, so a site with a strong directional structure is not being modelled as one.

Asserted by src/engine/__tests__/kriging.test.ts

The reference values themselves, and where they come from

Method.
No reference value in the suite was written from memory: one committed Python script produces all of them, cross-checks the two kriging implementations against each other before writing anything, and refuses to write at all if they disagree.
Reference.
src/engine/__tests__/fixtures/make_fixtures.py, committed beside the reference file it wrote.
What is comparedWhat the test enforcesWhat a failure would mean
PyKrige against the from-scratch solve, before any value is written1e-8 relative, or the generator writes nothing at alla reference file that is wrong in exactly the same way as the code
Whether PyKrige was available on the run that wrote the filerecorded in the file, and asserted true by the suitea page claiming two references while pinning against one
The three well setsdrawn from the fixed seeds 20260815, 7771 and 31337fixtures that change under you, and a suite nobody can re-derive
The generator records the environment it ran in, and the suite reads that record back rather than trusting a note in a comment.
  • That guard is not decorative. It fired on the first run and caught a real defect: PyKrige’s list form of the variogram parameters subtracts the nugget from the sill, which its own docstring denies, and a five per cent error was already sitting in the fixtures when the cross-check refused them. The generator now passes a dictionary and asserts what was stored.

Asserted by src/engine/__tests__/fixtures/make_fixtures.py, src/engine/__tests__/kriging.test.ts

Fitting the variogram

The fitted variogram is the artefact that goes in the appendix, so it is checked in three separate places — the model functions, the binning, and the fit — and the one place this engine deliberately does not match PyKrige is published as a measured divergence rather than hidden inside a generous tolerance.

The model functions, and the range convention

Method.
Spherical, exponential and gaussian semivariances are evaluated over a spread of separations and compared with the same expressions written in NumPy.
Reference.
PyKrige’s variogram_models.py, read rather than remembered. The range is the PRACTICAL range, so the exponential model uses a third of it as its length scale and the gaussian four sevenths — which is the convention this engine follows.
What is comparedWhat the test enforcesWhat a failure would mean
Every model value, against the NumPy expression1e-14 relativea range convention taken from a textbook instead — an error of several per cent that still looks plausible on a plot
Semivariance at zero separation, with a nugget in the modelexactly 0kriging quietly ceasing to be an exact interpolator
Every model at the practical rangeabove 0.94 of the silla range that means a different distance in each model
The spherical model at its rangeexactly the sillthe cap applied in the wrong place
  • A tolerance this tight is not a claim about precision. Both sides evaluate the same elementary expression, so anything but the last bit or two would be a mistake — and the mistake this row exists to catch is the length-scale convention, which is worth several per cent and would sail through a loose bound.

Asserted by src/engine/__tests__/variogram.test.ts

The empirical variogram, binned the way PyKrige bins it

Method.
Pair distances are binned twice — once with this engine’s own defaults, and once with a helper that reproduces PyKrige’s bin edges exactly — and the lag, the semivariance and the pair count of every bin are compared.
Reference.
PyKrige’s _initialize_variogram_model, read for the binning rules: equal-width bins, and a reported lag that is the mean pair distance in the bin rather than the bin centre.
What is comparedWhat the test enforcesWhat a failure would mean
Lag and semivariance, bin for bin1e-13 relativea binning or estimator mismatch — bin centre for mean lag, or a robust estimator for the plain one
The number of pairs in each binequal bin for bin, exactlya pair falling out of a bin on a last-bit difference
The automatic lag count5 bins for 10 pairs, 10 for 300, 15 for 100000bins holding too few pairs to mean anything
  • The closest pair used to fall out of the first bin, because that bin edge sits exactly on it and the two implementations round the distance differently in the last bit. The outer edges are now widened by a relative epsilon. The defect was real, and it is why the pair counts are asserted rather than assumed.

Asserted by src/engine/__tests__/variogram.test.ts

The fit, against SciPy attacking this engine’s own objective

Method.
For every combination of well set, binning and model, SciPy’s trust-region least squares is run on the same weighted residual over the same feasible set, and both the objective value and the fitted parameters are compared with what this engine found.
Reference.
SciPy least_squares with a linear loss, run in the fixture generator on the residual this engine minimises. An independent optimiser attacking OUR objective, which is a different and weaker claim than reproducing somebody else’s fit.
What is comparedWhat the test enforcesWhat a failure would mean
The weighted sum of squares both optimisers minimise2e-6 relativea convention or algebra error, which moves this by orders of magnitude
Our minimum against SciPy’snever worse by more than 2e-6a search that stops early and calls it an answer
Nugget, partial sill and range1e-3 relative, scaled by the silla fit landing in a different basin altogether
A model fitted to bins it generated itselfnugget 0.4, partial sill 3.0 and range 250 recovereda fitter that cannot find an answer it was handed
SciPy searches all three parameters at once; this engine solves two of them in closed form at a fixed range and searches the range alone. The two therefore stop at slightly different points, which is what the two bounds below are about.
  • The parameters are pinned an order looser than the objective, and that is deliberate rather than a concession. In a flat basin the argument of the minimum moves much further than the minimum does, so the objective value is the load-bearing assertion and the parameters are a sanity bound on it.

Asserted by src/engine/__tests__/variogram.test.ts

Where the fit deliberately does not match PyKrige

Method.
PyKrige fits its variogram with a robust loss and this engine fits plain weighted least squares, so the two are minimising different things and their fitted parameters genuinely differ; the suite therefore measures the gap instead of asserting it away.
Reference.
PyKrige’s _calculate_variogram_model, which passes a soft-L1 loss to SciPy. Read in the source, because the difference it makes is the whole reason this check exists.
What is comparedWhat the test enforcesWhat a failure would mean
Our total sill against PyKrige’sbetween 0.5 and 2.0 times ita fit that has stopped being about the same data
Our range against PyKrige’sbetween 0.33 and 3.0 times ita length scale that no longer describes the site
  • This is the loosest bound on the page and it is the honest one. A monitor is not an equality claim: no tolerance would make two different objectives agree, and widening one until they did would be this page lying about what was checked.
  • The closed-form step is what the choice buys. Keeping the objective linear in the nugget and the partial sill at a fixed range reduces fitting to a one-dimensional search with no optimiser, no seed and no dependency — which is also why the same file always fits the same variogram.

Asserted by src/engine/__tests__/variogram.test.ts

Drawing the contours

Marching squares is not pinned against another implementation at all. It is checked against fields whose isolines are known in closed form — a plane, a cone, a single ambiguous cell — with the closed form computed inside the test rather than remembered. There is nothing to transcribe, so there is nothing to transcribe wrongly.

Planes, where the isoline is a straight line

Method.
A plane is sampled onto a grid and contoured; because interpolating a linear field along a grid edge is exact, every vertex must satisfy the line equation to machine precision.
Reference.
No published source, and none is needed. The reference is the equation of the field, evaluated in the test beside the assertion.
What is comparedWhat the test enforcesWhat a failure would mean
Every vertex of a vertical isolinewithin 1e-12 of x = 5an interpolation along the wrong edge, or a wrong crossing parameter
Every vertex of a sloped plane, at three levelson the exact line to 1e-12the same defect in a case where a sign error would otherwise cancel
The direction each line is walkedthe high side always on the leftcontour labels and fills ending up on the wrong side of the line
A level above every value, and one belowno lines at allphantom contours outside the range of the data

Asserted by src/engine/__tests__/contours.test.ts

Cones, where the isoline is a circle of known radius

Method.
A cone is contoured at a radius the test chose, and the vertices are measured against that radius; because interpolating a curved field is not exact, the assertion is on how the error shrinks rather than on how big it is.
Reference.
No published source. The reference is the radius of the circle, which is an input to the test.
What is comparedWhat the test enforcesWhat a failure would mean
The worst radius error on a grid 101 nodes a sideunder 0.01a crossing computed on the wrong edge of a cell
Halving the cell sizecuts that error by more than 3.2 timesa method that is not second order, which no absolute bound would reveal
The area enclosed by the ringwithin 1e-3 of the circle’s own areasegments assembled into the wrong ring
  • The convergence row is the strong one. It needs no remembered constant, and a first-order defect fails it while a generous absolute bound would let it through.

Asserted by src/engine/__tests__/contours.test.ts

Saddles, where the answer is a choice and the choice is stated

Method.
The two ambiguous marching-squares cases are built as single cells, and the resulting segments are asserted coordinate by coordinate for the default rule and for each of the three overrides.
Reference.
No published source. The crossing parameters are worked out in the test’s own comment, from the corner values, and asserted against that working.
What is comparedWhat the test enforcesWhat a failure would mean
A cell whose centre the default rule calls hightwo named segments, to 3 decimal placesa saddle joined the wrong way, which merges two highs into one
A cell whose centre it calls lowcrossings at a third and two thirds of each edge, to 6 decimal placesthe same defect in the opposite direction
The asymptotic rule against the corner meana different topology, each matching its forced optionan option that is documented and does nothing
  • Marching squares skips any cell with a missing corner, so an isoline stops at the edge of the masked region rather than being clipped exactly to the hull polygon. The difference is drawn honestly — the fringe is hatched, legended and named in the method note — but it is not sub-cell clipping and this page will not call it that.

Asserted by src/engine/__tests__/contours.test.ts

The hydraulic gradient

The three-point problem is the one calculation on this site whose answer a reader can work out on paper, so it is asserted against the closed form as well as against an independent solve. Everything else about flow direction is checked against the analytic gradient of a field the test built.

The three-point problem, against a closed form worked by hand

Method.
Three wells are placed so that the fitted plane has an exact answer in binary floating point, and the gradient, the flow direction and the azimuth are asserted against that answer rather than against a stored number.
Reference.
No published source. The closed form is worked out in the test’s own comment, from the three heads, and the same cases are also compared with a NumPy solve of the same system.
What is comparedWhat the test enforcesWhat a failure would mean
A(0, 0, 100), B(100, 0, 99), C(0, 100, 100)gradient exactly 0.01 and azimuth exactly 90, to 15 and 12 placesa transposed system, or a gradient reported pointing uphill
A(0, 0, 100), B(100, 0, 98), C(0, 100, 96)azimuth exactly the arctangent of a half, to 12 placesan azimuth measured from the wrong axis
The same three heads put back through the fitted planeeach one recovered to 9 decimal placesa plane that does not pass through its own data
Three wells on a straight linerefused, with no jargon in the messagea determinant near zero turned into a confident answer
Coordinates and heads are the test’s own inputs; the middle column is what the plane through them must produce.

Asserted by src/engine/__tests__/flow.test.ts

The least-squares plane, and the gradient of a surface

Method.
The plane fit is checked against the three-point answer where three wells make them the same problem, against a plane it was built from, and against the analytic gradient of a planar head field sampled onto a grid.
Reference.
No published source. Every reference here is the field the test generated, differentiated in closed form.
What is comparedWhat the test enforcesWhat a failure would mean
The plane fit through exactly three wellsthe three-point answer, to 10 decimal placestwo calculations of the same thing disagreeing on one sheet
Thirty noiseless wells on a known planeslopes of −0.03 and 0.01 recovered to 10 placesa normal-equations error that averages itself away
Central differences over a linear surface−0.02 and 0.005 at every node, to 12 placesan edge case at the grid boundary or beside a hole
The gradient read off a planar head fieldthe analytic magnitude to 12 places, the azimuth to 9cancellation in the differencing, unnoticed
Compass bearings0 north, 90 east, 180 south, 270 westa figure whose flow arrows point the wrong way
  • Azimuths are reported as the direction water flows, not the direction the gradient vector points. Those are opposite, and a sheet that had them the wrong way round would look entirely reasonable to anybody who had not checked.

Asserted by src/engine/__tests__/flow.test.ts

The same file, twice

A figure that has to be defended two years later has to be reproducible two years later. The whole pipeline is run twice and compared as raw bytes, and every engine file is scanned for anything that could make it answer differently on a different day.

The same wells, the same figure

Method.
The default pipeline — grid, interpolation, clip, levels, contours, labels, arrows, gradient — is run twice over the same wells by both methods, and the grid buffers are compared byte for byte along with the serialised geometry.
Reference.
No published source. The reference is the previous run, which is the only reference a reproducibility claim can have.
What is comparedWhat the test enforcesWhat a failure would mean
The interpolated grid and the kriging variancesidentical, compared as raw bytesiteration over a set, or a hash order leaking into a number
Contours, labels, flow arrows and the gradient readoutidentical, compared as serialised texta tie broken differently on the second run
Flow arrows from a given seedthe same arrows, from seed 1 at a target of 40a figure whose arrows move when nothing else did
Every engine source fileno clock, and no unseeded random sourcea figure nobody can reproduce next quarter
Every import inside the enginerelative — nothing at all from outside the folderthe engine acquiring an opinion about the browser it is in
  • This is a claim about one machine and one build. It is what lets a reviewer re-run a quarterly figure and get the same drawing; it is not a claim that two different browsers produce identical bytes, which nobody here has measured.

Asserted by src/engine/__tests__/determinism.test.ts

Stiff diagrams

Every arm of every polygon on a Stiff map is one multiplication: a laboratory result in milligrams per litre, times the ion’s charge, divided by its formula weight. So the whole figure rests on a short table of atomic weights and on getting that arithmetic the right way up, and both are checked against sources that printed them.

The conversion factors, derived from IUPAC’s own table rather than remembered

Method.
The nine atomic weights a major-ion suite needs are asserted against the published table one by one, each formula weight is summed from them in code rather than written down, and the equivalent weights that fall out are compared with the divisors an independent author printed beside their own analysis.
Reference.
IUPAC’s Commission on Isotopic Abundances and Atomic Weights, Standard Atomic Weights of the Elements 2021, as tabulated with the Technical Report in Pure and Applied Chemistry. The conventional value is used for the six elements IUPAC publishes as an interval, which is the value IUPAC provides for a material of unknown provenance. · Claude E. Boyd, Anion-cation balance in water, Global Seafood Alliance, 2002, for the equivalent weights: the article prints the divisor it used for each ion, so a disagreement can be attributed to one ion rather than to the method.

IUPAC — Standard Atomic Weights of the Elements 2021

What is comparedWhat the test enforcesWhat a failure would mean
Each of the nine atomic weightsthe published value exactly — sodium 22.98976928, calcium 40.078a table copied from a textbook of a different vintage, wrong in the third figure
Bicarbonate’s formula weight, summed from those weights61.016, to 10 decimal placesa wrong subscript, which is a whole arm’s worth of error on every diagram
Every equivalent weight, against the published divisorswithin 0.005 relative of 20.04 for calcium, 12.16 for magnesium, 61 for bicarbonateone ion converted at the wrong weight, on a figure whose content is the shape
One milliequivalent per litre of alkalinity as calcium carbonate50.043 milligrams per litre, to 3 decimal placesthe number every water chemist would notice, quietly wrong
Alkalinity reported as calcium carbonate against alkalinity as bicarbonatea factor of 1.22, to 2 decimal placesa middle anion arm 22 per cent out, and a balance broken on an analysis that was fine
The table in the code is nine lines with the printed form beside each one, so checking it against the source is reading nine lines rather than parsing an expression.
  • The check that would catch the most damaging mistake is the second one, and it is worth saying why: sulfate written with three oxygens instead of four sums to 80.06, which is a plausible-looking number that draws every sulfate arm a fifth too long. Summing the formula in code from a checkable table is what makes that a test failure rather than a figure.
  • Agreement here is about transcription. It says nothing about whether the laboratory result you fed in is right, and a Stiff diagram drawn from a bad analysis is a bad diagram drawn correctly.

Asserted by src/tools/stiff-map/__tests__/ions.test.ts

The whole conversion, against an analysis somebody else converted

Method.
A published major-ion analysis of a saline aquifer, reported in milligrams per litre and converted to milliequivalents in the article itself, is put through this tool’s conversion again — ion by ion, and then as both of the sums the author arrived at.
Reference.
Claude E. Boyd, Anion-cation balance in water, Global Seafood Alliance, 1 April 2002. Used rather than a tidier textbook table because it covers all seven ions this map reads, it states the divisor used for each one, and it is a real water rather than a set of round numbers.

Boyd (2002) — Anion-cation balance in water

What is comparedWhat the test enforcesWhat a failure would mean
Each of the seven ions, against the printed meq/Lwithin 0.005, or half of the last digit the author printedone ion converted wrongly, which is one short arm on every diagram on the map
The sum of the cations66.74 meq/L, to 1 decimal placean error that cancels ion by ion and survives to the total
The sum of the anions66.29 meq/L, to 1 decimal placethe same, on the other side of the diagram
The charge balance those two sums giveinside 0.5 per cent by the formula this tool printsa balance figure that would mark a sound analysis as suspect
The direction of the multiplication120 mg/L of calcium is 6 meq/L, to 1 decimal placedividing by the charge instead of multiplying — every divalent arm halved, and the figure still looks like a figure
The water is dominated by sodium and chloride at tens of milliequivalents per litre, which is where a wrong factor shows up as a whole equivalent rather than as a rounding.
  • The two ions this tool differs from the article on at all are the two where the author rounded the equivalent weight rather than the answer — sodium at 23 and sulfate at 48 — and both land inside the tolerance above rather than being excused by it.

Asserted by src/tools/stiff-map/__tests__/ions.test.ts

The charge balance, in the form aqion and PHREEQC state it

Method.
The balance is the difference between the two sums over their total, in percent, and it is asserted on hand-worked pairs at both signs, on the symmetry that form implies, and on the one input for which there is no answer at all.
Reference.
aqion’s statement of the charge-balance error, which is the form PHREEQC uses: the difference over the SUM of the two sides rather than over one of them, which is the only version of the formula that gives the same magnitude when the two sums are swapped.

aqion — charge balance error

What is comparedWhat the test enforcesWhat a failure would mean
Two sums that agreeexactly 0, with no floating-point residuea formula that reports an error on a water that balances
Cations 6 against anions 420 per cent, to 10 decimal placesthe difference taken over one side rather than over the total
The same two sums, swappedthe same magnitude with the sign reversed, to 12 decimal placesa balance that depends on which side you write first
A water with nothing in itnot a number, rather than a division by zero reported as a balancean empty analysis presented as the best one on the map
Note what the convention implies: a ten percent difference by this formula is a twenty percent difference between the two sides, so the threshold reads tighter than it sounds. The figure prints the threshold it used.
  • The balance is a DISCLOSURE and never a filter. Nothing is dropped for failing it: the diagram is drawn either way, with a dashed outline, and the figure says how many analyses did not balance and against what threshold.

Asserted by src/tools/stiff-map/__tests__/ions.test.ts

Trends, well by well

Every arrow on a trend map is one Mann-Kendall test on one well’s own record, and every arrow length is one Sen slope. Both are pinned against a textbook that prints its intermediate values — the sign count, the tie-corrected variance, the standard deviation, the statistic and the confidence limits — so what is checked is the working and not just the answer.

The statistic and its variance, against four printed worked examples

Method.
Four examples from one chapter are reproduced: the four-point sequence of its Table 16.2, the twenty-two monthly readings of its Example 16.2, the doubly-tied Example 16.3, and the tie groups the chapter works out for a sequence containing traces.
Reference.
R. O. Gilbert, Statistical Methods for Environmental Pollution Monitoring, Van Nostrand Reinhold, 1987, chapter 16. Chosen because it prints its intermediate arithmetic rather than only its conclusions, which is what makes the variance and the continuity correction pinnable rather than merely plausible. · HydroGeoLogic, Inc., Mann-Kendall Analysis for the Fort Ord Site, 2005, appendix D of an annual groundwater monitoring report — an independent statement of the same formulae in a document a regulator accepted, walking the same arithmetic to the same answer.
What is comparedWhat the test enforcesWhat a failure would mean
S for the sequence 10, 15, 14, 20exactly 4, as Table 16.2 works it out term by terma sign count that disagrees with every published implementation
The one-sided probability of that S0.167, which is 4/24, to 12 decimal placesa tabled probability read off the wrong tail
The tie-corrected variance at twenty-two readings1227.33 from Eq. 16.3, to 2 decimal placesthe tie correction omitted, which is a conservative p that will not match ProUCL
The statistic, with the continuity correction3.1, to 1 decimal place, from an unrounded 3.0545the correction dropped, or applied in the wrong direction
S where three readings share one time period and two share another19, scoring 24 of the 28 possible pairssame-period pairs scored as though they were ordered in time
The variance from Eq. 16.5, and the statistic it gives58.1 and 2.4, each to 1 decimal placea time-tie formula that does not reduce to the untied one
The chapter’s own tie-group instruction is the citation this map’s non-detect rule rests on: traces enter as a tied group rather than being substituted or dropped, and the figure names the source on the sheet.
  • The concentrations behind Example 16.2 are printed only as a scatter plot, so what is pinned there is the arithmetic the book prints beside it — the tie groups, the variance, the standard deviation and the statistic — rather than twenty-two values read off a scanned figure. Reading them off would be pinning an act of eyesight.
  • The tie correction subtracts from the variance, so for a fixed S it makes the statistic larger rather than smaller. That is the opposite of the usual intuition and it is asserted as a direction as well as as a value, because an implementation that omitted it would still produce a plausible p-value.

Asserted by src/tools/trend-map/__tests__/mk.test.ts

The Sen slope and its confidence interval, against the same chapter

Method.
The eight observations of that same example go through the slope estimator: every pairwise slope that spans two different time periods, the median of them, and the two interpolated ranks that bound it — which is the assertion that earns the estimator its interval rather than just its point.
Reference.
R. O. Gilbert, Statistical Methods for Environmental Pollution Monitoring, 1987, Example 16.5, which tabulates the pairwise slopes, states the rank arithmetic and prints both confidence limits — so the interpolation between ranks is checkable and not merely asserted.
What is comparedWhat the test enforcesWhat a failure would mean
How many pairwise slopes the record gives24, the same set the chapter tabulatespairs within one time period counted, which would bias the median
The median of them5.5 units per time period, to 10 decimal placesa mean quietly substituted for the median, which one bad result would then move
The lower and upper confidence limits2.6 and 9.3, each to 1 decimal placeranks rounded instead of interpolated, which is a different interval
The normal quantile the chapter read off its own table1.645, to 3 decimal placesan interval built on the wrong critical value
Where the fitted line sitsthrough the median time and the median value, to 10 decimal placesa slope that is right and a line drawn somewhere else
  • Rounding the ranks rather than interpolating between them gives 3.3 and 9.0, which are also perfectly plausible-looking numbers. Matching the printed limits to one decimal place is only possible if both the rank arithmetic and the interpolation are right, which is why the interval is pinned and not only the slope.
  • The slope is what this map draws as the arrow’s LENGTH, so the estimator’s resistance to a single wild reading is a property of the drawing and not only of the number. It is asserted separately, against a record with one result inflated by a factor of a hundred.

Asserted by src/tools/trend-map/__tests__/mk.test.ts

Which p-value a record gets, and the floor a short one cannot get under

Method.
The exact permutation distribution is used where it is defined and the normal approximation where it is not; the boundary between them is the one the regulator’s own software draws, and the smallest p a record of a given length can reach is read off that same distribution rather than assumed.
Reference.
USEPA, ProUCL 5.1 Technical Guide, section 10.2, equations 10-14 to 10-18, cross-checked as formulae and as the boundary at which the exact tail gives way to the approximation. Its own worked example prints its results only inside a screenshot, so there is no number in it to pin. · Kendall’s published tail probabilities, which appear in Gilbert (1987)’s own table and in every rank-correlation table since — the reference for the exact distribution at small sample sizes, checked value for value.

USEPA — ProUCL software and documentation

What is comparedWhat the test enforcesWhat a failure would mean
The published tail probabilities at five readings0.008, 0.042 and 0.117, each to 3 decimal placesa distribution that agrees with nothing anybody has tabulated
The exact distribution as a distributionsums to 1 to 10 decimal places, and is symmetric about zero to 12a rising well and its mirror image getting different p-values, which on a map is two different arrows
The largest attainable statisticexactly 1 over n factorial, to 12 decimal places, at every length up to 10a tail computed one step out, which nothing else would reveal
Which branch a record getsexact up to 22 readings and untied, the approximation above 22 or with any tiea boundary that disagrees with the software a regulator is holding the figure to
The two branches at that boundaryagreeing to 2 decimal places at 22 readingsa p-value that visibly jumps when one more quarterly round is added to the file
The smallest p a four-round record can reach0.083, so four rounds cannot clear 0.05 however perfect the trendan arrow drawn on evidence the test never produced
The far tail of the normal approximation9.865876e-10 at −6, to 16 decimal placesa p reported as zero where it should be a small number, which no statistician would sign
The exact branch is refused the moment anything is tied, because a tied record’s null distribution is not the permutation distribution of distinct values, and using it anyway would be a p-value with no argument behind it.
  • The floor is a fact about the test and not about the well. A four-round record that rises perfectly still cannot produce a p below 0.083, so this map draws no arrow there at the usual level and says on the figure that the level could not be reached even in principle — rather than leaving it to be discovered by whoever checks.
  • Non-detects are carried as a tied group below the highest reporting limit, after the chapter cited above, and nothing is substituted. That is a defensible choice rather than the only one, and the figure names it; a reader whose programme requires a different treatment is not being offered one here.

Asserted by src/tools/trend-map/__tests__/compute.test.ts, src/tools/trend-map/__tests__/mk.test.ts

What comes out of the export menu

Two exports, checked two different ways. The picture is checked by rendering every finished map twice and comparing the markup; the drawing is checked by reading it back as group-code pairs and asserting the structure an R12 reader requires. Both are checks on the file, not on how it looks once somebody opens it.

The SVG is the same file with the editor above it and without it

Method.
Every finished map is rendered over its own sample site, once with the click-to-edit layer mounted above it and once without, and the markup the download button would write is compared.
Reference.
No published source. The claim is about this product rather than about a format: the svg element is the deliverable, so nothing that exists to help somebody edit it may travel into it.
What is comparedWhat the test enforcesWhat a failure would mean
The serialised figure, with the editor and without itidentical, byte for byteediting state exported into a client’s deliverable
Every attribute the editing layer addsabsent from the filethe same defect in a form nobody would notice
The hit boxes, the dotted underline and the inputabsent from the filebrowser affordances baked into a drawing
A title-block cell the reader rewrotein the file, and the wording it replaced is nota reader’s own words silently dropped at the point of export
Every map that ships is covered, because the suite walks the registry rather than a list somebody has to remember to extend.
  • One thing legitimately differs between two renders and is normalised before the comparison: the clip path’s identifier, which is numbered per sheet so two figures on one page cannot capture each other. Everything else is compared exactly as it stands.
  • The exported file does not embed its fonts. The type stack is deliberately web-safe and the layout is computed from estimated metrics, so a substitution shifts glyphs by a fraction and never reflows the sheet — but a print system with no sans-serif at all would render it wrongly, and nobody here has tested that case.

Asserted by src/figure/__tests__/figureTextExport.test.tsx

The DXF is a drawing an R12 reader can open

Method.
The exported file is read back into group-code pairs by a strict reader written inside the test, and the sections, the layer table, the entities and every coordinate are asserted against what the R12 specification requires.
Reference.
Autodesk’s own R12 DXF reference — the acad_r12_dxf archive PDF, which Autodesk no longer hosts at a stable address. It is what the exporter was written against, and the file header in src/figure/exportDxf.ts says so. · ezdxf’s notes on DXF file structure, used as a second reading of the same rules.

ezdxf — DXF file structure

What is comparedWhat the test enforcesWhat a failure would mean
The version in the headerAC1009, which is R12 and nothing latera file that declares one version and uses another’s entities
The polyline entity usedthe old form with its own vertices, never the lightweight one R12 has no word foran entity an older importer silently drops
The sectionsheader, tables and entities in that order, ending at the end-of-file markera file a strict reader rejects at the first section
The layer table5 layers on a contour drawing, counted honestly in the table headera drawing whose layers the importer has to invent
A published line that is not a contouron a layer named for what it is, and declared — 6 layers on a Stiff drawinga Stiff polygon filed on the contour layer, so turning the contours off empties the sheet
Every group code and its valuean even number of lines, and every code an integerone lost half of a pair, which turns the rest of the file into nonsense
Every coordinate in the entities sectioninside the site’s own extent — −1 to 21 on the fixturesheet points exported as though they were survey coordinates
A non-detect printed beside a wellfolded to plain ASCII, still a limit and not a readinga reporting limit arriving in somebody’s CAD as a measured number
Two exports of an unchanged figureidentical, with no date anywhere in the filea diff between two quarters that shows every line as changed
Any string the file carriestrimmed to 250 characters, which is the R12 limita method note that overruns what a reader will accept
The reader refuses an odd number of lines and any group code that is not an integer, so no assertion below can pass on a file a real reader could not parse.
  • Structure is not appearance. These assertions say the file is a valid R12 drawing carrying the geometry the figure drew, in the site’s own coordinates. Nobody has opened one in a civil package and looked at it, and that is a gap only a person can close.
  • The drawing unit is whatever unit the file arrived in, and the height of a contour is its own value rather than an elevation — so a head in feet contoured over a grid in metres puts two units in one drawing. That is the convention every contour DXF follows, and the file says which is which in its comment block rather than leaving it to be found out.

Asserted by src/figure/__tests__/exportDxf.test.ts

The shapefile is a shapefile, read back file by file

Method.
The archive is opened with this project’s own zip reader — the one written to open Excel workbooks, which knows nothing about the writer — and every file inside it is parsed back by a reader written in the test from the published specification rather than from the code it checks.
Reference.
ESRI Shapefile Technical Description, an ESRI White Paper, July 1998. It is the specification the writer was built from and the one the readers in the test were written from, independently. · dBASE III for the attribute table, which the ESRI paper leaves as “a standard DBF file”. The layout was taken from three published descriptions that agree on every offset used — dbase.com’s own file-format note, the dBase III structure published by Promotic, and Erik Bachmann’s Xbase file format description.

ESRI Shapefile Technical Description

What is comparedWhat the test enforcesWhat a failure would mean
The main file headerthe file code 9994 big endian and the version 1000 little endian, in one 100-byte headera file every reader rejects on its first four bytes
Every length in the filecounted in 16-bit words rather than in bytesa length field that looks plausible and is out by a factor of two
The first record’s offset in the index50 words, which is where the 100-byte header endsan index that seeks into the middle of a record
A point record’s contents10 words — a shape type and two doublesa record whose declared length walks the reader off the end of the file
Every offset the index publishesfollowed back into the main file, where the record number found has to be the one expectedan index out by the size of a record header, which no round trip through the writer would notice
The bounding boxrecomputed from the records themselves, in the order the specification givesa box written as two corners rather than interleaved — accepted by every reader, drawn in the wrong place
The attribute tableone record per shape, in the same order, with the declared header and record lengths adding upa table read as a wall of shifted characters
The date in the attribute table80 — the year since 1900, which the specification requiresa header a strict dBASE reader refuses
A well the figure posted no reading besideblanks, never a zeroa measurement that never happened, on every row of a well location map
The projection filewritten only where the coordinate system is genuinely known, and byte-identical to what ArcGIS writes for the same zonea site placed somewhere else on the planet, silently, by a projection file that guessed
Two exports of an unchanged figureidentical, with no date in the archive or in the attribute tablea diff between two quarters that shows every byte as changed
Every offset is hand-computed from the specification and then found in the bytes: a two-well layer comes out at 156 bytes, its second record starts at word 64, and that well’s easting is at byte 140.
  • Structure is not appearance, the same caveat the drawing carries. These assertions say the archive holds valid shapefiles carrying the geometry the figure drew, with the attributes the figure printed. Nobody has opened one in ArcGIS or in a state agency’s submittal portal, and that is a gap only a person can close.
  • The convex hull of the wells is not in the archive. A shapefile holds one geometry type, so it would be a third layer of a single feature; the DXF and the GeoJSON both carry it, and the README inside the archive says where it went.
  • Attribute text is folded to plain ASCII, because dBASE III has no way to declare an encoding. The fold is lossless for everything this product prints — a non-detect stays a limit and never becomes a bare number — and it would drop an accent from a well name nobody here has ever seen in a file.

Asserted by src/figure/__tests__/exportShapefile.test.ts

Where the reference values came from

The reference values are not stored answers somebody once printed. They are produced by a script committed beside them, which records the versions it ran under, cross-checks its two independent kriging implementations against each other, and refuses to write anything if they disagree by more than the guard below. The suite reads that record back, so these versions cannot drift away from the numbers they produced.

Python
3.13.13
NumPy
2.4.6
SciPy
1.17.1
PyKrige
1.7.3
The two references, against each other
2.83e-14
What the generator would have refused at
1e-8

What is NOT validated

Nothing here has been read by a hydrogeologist. Every claim on every figure is checked against a computation; none of it has been reviewed by somebody who signs these drawings, and no figure from this site is known to have survived a regulator.

There is no anisotropy anywhere. Variograms and kriging are isotropic and there are no directional variograms, so a site whose structure runs along a valley or a fracture set is being modelled as though it did not.

Kriging is global. Every well enters every grid node’s system, which is correct and slow and fine to a few hundred wells; there is no moving neighbourhood, so nothing here is validated at thousands.

This engine does not reproduce PyKrige’s fitted variogram, and no tolerance would make it. PyKrige minimises a robust loss and this fits plain weighted least squares. The fit is pinned against an independent optimiser attacking OUR objective, and the distance to PyKrige’s answer is monitored rather than asserted away.

Contours are not clipped exactly to the hull. Any cell with a missing corner is skipped, so an isoline stops at the edge of the masked region; the fringe is hatched and named on the sheet, which is honest, but it is not sub-cell clipping.

How the pages and the pictures LOOK is checked by a person in a browser. There is no screenshot pass. The small figures on the landing page are baked by a script and tested for the things a test can see — a missing one, lettering, a colour that is not the sheet’s ink, a size budget — but whether any of them is a good picture of that map is nobody’s assertion, and a stale-yet-valid one would pass.

The number of flow arrows is approximate by design: the packing that places them depends on the shape of the domain, so asking for a count gets you roughly that many.

Agreement with another implementation is evidence that the equations were transcribed and wired up correctly. It is not evidence that they are the right equations for your site, that the data you gave them is defensible, or that the answer means what you would like it to. All of this is a calculation aid and none of it is a substitute for professional judgement.