Forecasting Pool Chemical Inventory from Logged Tank Levels

Chem Stock is a single-file, offline burn-rate forecaster for the chemical room. Log a tank or drum level whenever someone is in there; the tool derives the consumption rate from the history, projects the days remaining, and computes a reorder-by date against each supplier's lead time. Like the rest of the aquatics suite, it is one HTML file with no server and no external requests [1].

The problem

Chemical reordering at a pool tends to run on eyeball and memory: someone notices the chlorine tank looks low, an order goes in, and for the next several days the facility runs a race between the delivery truck and the tank. The failure is not that consumption is unknowable — it is very steady over weeks — it is that nobody is doing the division. Chem Stock does the division. Any level reading anyone bothers to log becomes signal, and the reorder date falls out of arithmetic instead of anxiety.

How it works

Each product shows as a card with a miniature tank fill and a status line: green (days left), amber (order by a named date), or red (ORDER NOW, with a hazard banner naming the product and its projected empty date). Selecting a product shows the detail view — a tank gauge drawn as SVG with a dashed red reorder line, the burn rate, the projected empty date, and the reorder-by date. Below that is the logging form (level plus an editable timestamp for backfilling) and the reading history.

Chem Stock detail view showing the tank gauge with dashed reorder line, burn rate, and projected dates
The detail view: tank gauge with the dashed reorder threshold, burn rate, and projected empty / reorder-by dates. Placeholder pending a real screenshot.

The reorder line on the gauge is drawn at the level where remaining supply equals lead time plus buffer — when the fill drops to the dashed line, it is time to order. Copy status formats a one-line-per-product summary for pasting into DigiQuatics; Export CSV writes the full reading history (with delivery flags) as a UTF-8 file for budget season.

The math

Burn rate

The rate is computed from consecutive level drops over a trailing 45-day window. Walking each adjacent pair of readings:

rate        = total_consumed / total_days      (over the trailing 45-day window)
days_to_empty = current_level / rate
reorder_by    = projected_empty − lead_days − 3-day safety buffer

Status turns amber inside a 7-day window ahead of the reorder-by date and red once the date has passed.

Guards

The tool refuses to project until there is real signal. A single reading, same-day pairs, or a history entirely outside the trailing window all report logging — need more readings instead of a fabricated date. A level entry more than 15% over tank capacity is rejected at input as a probable typo.

Editing the config

Products live in the PRODUCTS array at the top of the <script> block — name, unit, tank capacity, and supplier lead days:

const PRODUCTS = [
  { id:"naocl",   name:"Liquid Chlorine 12.5%", unit:"gal", capacity:500, leadDays:5 },
  { id:"ma",      name:"Muriatic Acid 31.45%",  unit:"gal", capacity:110, leadDays:5 },
  { id:"calhypo", name:"Cal-Hypo 65%",          unit:"lb",  capacity:400, leadDays:7 },
  { id:"cya",     name:"Cyanuric Acid",         unit:"lb",  capacity:200, leadDays:7 },
];

The behavior constants sit directly below: SAFETY_DAYS (3), SOON_DAYS (7), WINDOW_DAYS (45), and NOISE_FRAC (0.05, the delivery-detection threshold). The capacities shipped in the file are placeholders — they need to be replaced with the facility's actual tank and drum sizes before the percentages and delivery detection mean anything.

Limitations

References

  1. Chem Stock Forecaster