Measured Behaviour

Does obfuscation break disinfection contact time?

Every area in this pass so far loses a statistic, a grouping, an adjacency or a classification. This one loses a NUMBER - and not a number that goes missing or turns into NaN, which we have shown repeatedly fails loudly. It loses a number that is replaced by a plausible, finite, documented default which happens to be the one value it can never correctly have.

What a baffle factor is, and why 1.0 is the wrong default

The disinfection credit a contact tank earns is the concentration of disinfectant multiplied by the time the water spends in contact with it. The concentration is measured. The time is not - it is computed, and how you compute it is the whole subject of this article.

The naive figure is the tank's volume divided by the flow through it: the theoretical detention time. No real tank achieves it, because water does not move through a tank in a neat block. Some of it short-circuits from inlet to outlet along the quickest path and leaves early. The BAFFLE FACTOR is the dimensionless fraction of the theoretical time you are actually allowed to claim. A well-baffled serpentine contactor earns about 0.7. A rectangular tank with the inlet and outlet on the same wall and no baffle curtain earns 0.3. Only a pipe earns 1.0.

That last sentence is the finding. The generic library we handed the calculation to defaults the baffle factor to 1.0, which is not an unreasonable default for a library that computes residence times in general - it is the identity. It is simply the one value that is never correct for a tank.

Five events, and the one that is correctly refused

Five disinfection events across three contact zones at one works, each a set of instrument readings taken at the same moment. Four of them are comfortable. Two are the article.

E-1 is the marginal one. The water is 16.5 degrees at pH 6.9. The requirement table is read DOWN in temperature and UP in pH - toward the harder cell, because disinfection is slower in cold water and at high pH - so 16.5 degrees bands to the ten-degree row and pH 6.9 bands to the 7.5 column, which needs 118 mg-min/L. Nine hundred cubic metres at three tenths, divided by twelve megalitres a day, is 32.4 minutes; times 2.4 mg/L is 77.8. Short, and correctly refused.

E-5 is the one the residual floor exists for. A very large reservoir at a very low flow gives an enormous contact time - 864 minutes - and CT is a PRODUCT, so a residual of 0.15 mg/L multiplies out to 129.6 and clears the table comfortably. It is refused on the residual, not on the CT, and that is the entire reason there is a separate minimum residual at all.

The correct run: five events, three credited, two refused, and no water credited that did not earn its credit.

The multiplier that reverts to the identity

Rename the baffle factor and the library substitutes 1.0. E-1's contact time goes from 32.4 minutes to 108.0, and its achieved CT from 77.8 to 259.2 against a requirement of 118. It is credited. Twelve megalitres a day of water is credited with a disinfection it did not receive.

Now look at what a reviewer has to work with. The residual is 2.4 mg/L, which is what the analyser read. The volume is 900 cubic metres, which is what the tank holds. The flow is 12.0 megalitres a day, which is what the meter said. The temperature is 16.5 and the pH is 6.9, both correct. The requirement is 118, correctly banded out of a correct table. Every quantity on the line survives, because the thing that moved was not a quantity - it was a dimensionless fraction that says how much of one of them counts.

Rename the option choosing PEAK flow instead and the calculation sizes the contact time on average flow, 7.5 megalitres a day rather than 12.0. Contact time 51.8 minutes, CT 124.4, requirement 118: credited by six. The requirement to use peak flow exists because contact time is shortest when flow is highest, and that is precisely when the credit is least deserved.

Rename both and E-1's CT reads 414.7 against a requirement of 118. Two dimensionless choices, one options object, both pointing the same way.

The arm where the answer does not move at all

This is the sharpest cell in the area and it took one line to produce.

Rename the option that says the banding is conservative and the library bands to the NEAREST cell instead. E-1's 16.5 degrees is nearer to twenty than to ten. Its pH 6.9 is nearer to 6.5 than to 7.5. The requirement it is compared against goes from 118 to 42.

The achieved CT does not move. It is 77.8 in the correct run and 77.8 in this one, printed identically. The contact time is 32.4 minutes in both. Every measured quantity is identical. Every cell in the requirement table is a genuine published figure - 42 mg-min/L really is the requirement at twenty degrees and pH 6.5, and it is right there in the table any reviewer would check. What moved is which of nine correct numbers this event was measured against, and the event is credited.

A plausibility check on the requirement cannot see this, by construction rather than by luck: it asks whether the value COULD be real, and every value in a published table IS real. The assertion that helps is about WHICH CELL was applied - the temperature and pH the operator recorded, banded the way the regulation says - not about whether the figure it produced looks sensible.

The record side fails closed, and loudly

The contrast this series keeps measuring holds here too, and it is worth stating plainly because it is the good news in the article.

Rename the temperature field, or the pH field, or both, and the band lookup misses. A missed lookup falls to the catch-all, which this utility set to 346 mg-min/L - the hardest cell in the table. Nothing in the fixture reaches it. All five events are refused, including the three that are genuinely fine. That is a works that cannot sign off any disinfection at all, and somebody finds out within a shift.

Rename the residual, the contact volume or the peak flow and the arithmetic produces NaN and the run refuses before any credit is computed. Rename the requirement table itself and every event is measured against the 346 catch-all and refused.

So losing a VALUE fails closed and loud, and losing the MEANING of a value - what fraction of it counts, which flow it is divided by, which cell it is compared against - fails open and silent. That is the eleventh area in this series running where that contrast has held, and it is the most useful single thing to know when deciding what to protect.

One more cell makes the point from the other side. The event records carry BOTH a peak flow and an average flow, both measured, both true. An option decides which one is read. Renaming the average flow is inert on every counter, because with peak flow selected it is never consulted - it is decoration. Flip the option and the roles swap. Nothing on the record says which of the two is load-bearing.

What to do about it

The narrow fix is the one this whole series keeps arriving at: scope the member-renaming regexp to members your own classes own, and keep it away from the options object you hand to a calculation library. Everything above is a key on that literal or a field on the records it reads.

The design fixes are worth having regardless of obfuscation, because a hand-edited configuration file produces every one of these symptoms. Do not let a dimensionless multiplier live as a bare, optional key whose absence means one: require it, and refuse to compute a credit without it. A baffle factor of 1.0 should be something a plant declares deliberately, not something it inherits by omission.

Assert the CELL, not the value. Log which temperature band and which pH band the event resolved to, and check that pair against the recorded temperature and pH under the regulation's own banding rule. That single assertion catches the banding arm, which nothing about the numbers can.

And keep the residual floor as a separate check rather than folding it into the CT. Ours is what refused E-5, and it refused it for a reason the product could never express: a very long contact time will multiply almost any residual up past the table, and a floor is the only thing that says a minimum concentration is required in its own right.

Frequently asked questions

What is a baffle factor?

The dimensionless fraction of a contact tank's theoretical detention time that may be credited, given that some water short-circuits from inlet to outlet. A well-baffled contactor earns about 0.7 and an unbaffled tank about 0.3; 1.0 corresponds to plug flow, which a tank does not achieve.

What did renaming the baffle factor do in your measurements?

The library substituted 1.0. Contact time went from 32.4 minutes to 108.0 and the achieved CT from 77.8 to 259.2 against a requirement of 118, so an event that was correctly refused was credited - with every measured quantity on the line unchanged.

Can a rename leave the computed answer completely unchanged?

Yes, and we measured one. Renaming the option that bands conservatively left the achieved CT at 77.8 and the contact time at 32.4 minutes, and moved only which cell of the requirement table the event was compared against - from 118 to 42.

Why does a plausibility check not catch the banding case?

Because it asks whether the requirement looks real, and every cell in a published table is real. 42 mg-min/L is the correct requirement at twenty degrees and pH 6.5. The useful assertion is about which cell was applied, not about whether the number is sensible.

What happens if a temperature or pH field is renamed instead?

The band lookup misses and falls to the catch-all, which our utility set to the hardest cell in the table. All five events were refused, including the three that were genuinely fine - a loud failure that surfaces within a shift.

Why is there a separate minimum residual if CT already exists?

Because CT is a product. A very long contact time multiplies almost any residual up past the table: our reservoir event reached 129.6 on a residual of 0.15 mg/L. The floor is what says a minimum concentration is required in its own right, and renaming it credited that event.

How do you tell which of two measured values is load-bearing?

Not from the record. Our event records carry both a peak flow and an average flow, both measured and both true, and an option decides which is read. Renaming the unread one is inert on every counter; flip the option and the roles swap.

Related reading