Most cities just leave the pumps running.
What if the water went where it is needed, when it is needed?

The whole thing, in plain words

  1. In this city, the electricity goes off for hours every day. It goes off most often in the evening.
  2. No electricity means the pumps stop. When the pumps stop, the water tanks empty and the taps run dry - in the evening, exactly when everyone comes home and wants water.
  3. WIIGA decides, every hour, which pump runs and on what power, using sunshine, city electricity, or a diesel generator. Sunshine is free. The generator is expensive and dirty, and there is only a little of it.
  4. Its main trick: it fills the tanks at midday, on free sunshine, when nobody is thirsty yet. A full tank at midday is a battery for the evening - and it costs nothing, because the tank is already there.
  5. Its second trick: it can warn people to fill their jerrycans before the power goes. But if it warns and nothing happens, people stop believing it. So it has to learn to speak only when it is sure.
  6. And when it has nothing useful to add - at three in the morning, say - it hands the station back to the operator and says so.

25 February. 38.7 C. Hot dry season in Ouagadougou.

10 566 people

That is how many were left below the WHO survival threshold of 20 litres by the hand-written rulebook, and were not, on the same day, with the same outages, by this agent. 11 113 people against 546. The worst day of the year, and half a city.

Watch it decide

One simulated day, hour by hour. Press "Show me the day" and it walks you through the four hours that matter, in about twenty seconds. Then take the hour slider yourself, change the city - the same agent, weights trained on Ouagadougou and unchanged, running under a climate it never saw - or switch the operator to the rulebook and replay the same day without learning.

And set Demand to school + clinic +30%. The district drinks a third more than the forecast announced - an epidemic at the dispensary, a school reopening - and nothing tells the agent: its input carries the shape of the usual profile, not the litres actually drawn. At 1 p.m. its tank sits 13 points lower than it planned for, and the taps still do not run dry.

City
Season
Operator
Hour 00:00

Districts

Tank level through the day

Energy source, hour by hour

solar grid diesel generator pump idle grid down agent warns the city

Every scenario here was played in Python by the trained model and written into this page. The browser replays them - there is no model running in this tab, and saying otherwise would be a lie you could not check.

Your station, not ours

Everything above is one station with one set of constraints. Yours are different, and the honest question is not "is this impressive" but "would it do anything for me". Three constraints were swept independently, so pick the one that actually binds at your station and read the answer off a measurement rather than off a promise.

What binds you
Value -

Each axis was swept on its own, holding everything else at the values this repository publishes. The combinations are not measured, so the tool answers one question at a time and says which campaign each answer comes from. Turning three sliders at once and reading the corner would be inventing a number, and this page does not do that.

How it is wired

That console had three pumps, three tanks, three districts and three sources of power behind it, with a grid that goes out. Here is the whole machine on one page: what the agent is allowed to see, what it decides, and the two actions that touch no pump at all.

ONCE AN HOUR, THE AGENT READS 3 tank levels demand, 6 h ahead outage risk, 4 h ahead diesel left today what the city still believes all five are forecasts or states, never the truth of what is about to happen AND DECIDES a power level and a source, per pump ENERGY solar free, 7 h to 17 h grid out 8.6 h/day diesel 320 kWh/day PUMPS pump 1 0 to 100 % pump 2 0 to 100 % pump 3 0 to 100 % TANKS, THE BATTERY 120 m3 240 m3 80 m3 DISTRICTS THAT DRINK market 6 000 . peak 6h residential 12 000 . peak 19h school + clinic 4 000 . peak 13h the reward reads the WORST served district, never the average filled at midday on sunlight nobody pays for, drunk at 7 p.m. when the grid is down AN ACTION THAT TOUCHES NO PUMP "fill your jerrycans, the power is about to go" Demand moves forward. The day's volume is unchanged. 45 % of households respond when trust is full. A false alarm costs no fuel: it costs being listened to, and the break-even accuracy that follows is 78.9 %. AND THE OTHER ONE "not this hour, you take it" The agent hands the station back to the operator's own fixed setting, and pays 3.0 of reward every time it does. At 3, 6 and 7 a.m. it decides it has nothing to add, and takes the station back at 8 a.m. when the sun comes up.
Everything the agent sees is on the top row, and it is less than you would expect: forecasts rather than facts, and the shape of demand rather than the litres actually drawn. The two boxes at the bottom are the actions that touch no pump, and they are the part with no equivalent we could find.

In the language of the field: a reinforcement-learning agent schedules three district pumps against a failing grid, on a simulator calibrated on three years of measured daily records. Solar first, always.

And the object is not really water. It is operating an essential service on infrastructure that cannot be relied on. Swap the tank for a vaccine fridge, a rural health battery or a motorbike charging point, and the same four horizons have to be held together by the same hour-by-hour decision. Water is simply where we could measure it.

Judging this? GRILLE-JURY.md in the repository maps each of the five criteria to its number and the one place that proves it. Everything below is regenerated by a command - no figure is typed by hand.

What it had to beat

Beating current practice is easy. Beating somebody who reads the same outage forecast is not - and that is the row that decides whether learning earned its place here.

The average hides the problem

Ouagadougou does not have one water problem. It has three.

April: 39.8 °C. Demand is up a quarter and the grid fails every evening - but the sky is clear, so the tank-as-battery works.

August: 30.1 °C and 8.7 mm of rain a day. Demand falls, and so does the sunshine the battery depends on.

The two seasons want opposite policies. That is why no fixed setting holds both.

The action that touches no pump

The agent can broadcast one message: fill your jerrycans, the power is about to go. In Europe this action would be meaningless - nobody stores water at home. Here every household has containers, and one sentence at 5 p.m. moves more water than an hour of diesel.

It is not a free button.

Warning moves demand forward. It does not remove it. The tank is under more strain in the following hour, less during the cut, and the day's volume is conserved exactly.

And a false alarm costs no fuel and no money. It costs being listened to. A correct warning buys +0.04 of the city's trust; a false one costs -0.15.

That asymmetry alone fixes a break-even accuracy of 78.9 %. No penalty term forbids chatter - it is simply a losing bet, and the agent has to work that out.

A city it has never seen

Scalable is a word everyone writes and nobody measures. Here it means one thing: the weights trained on Ouagadougou, replayed unchanged on climates that were not in training, against the same hand-written rule. Twelve temperatures, twelve solar sums and twelve rainfall figures are replaced - everything the model knows about geography - and we look.

How much concrete does it replace?

The simulator is a digital twin of the network: tanks, pumps, sources, a failing grid, and demand that follows heat and holidays - calibrated on three years of measured daily records.

You do not connect an agent to a city's water supply without one.

But a twin is useful on its own. It answers capital questions before anyone pours anything, so we asked the one a utility director actually asks:

How much bigger would the tanks have to be for the rulebook to serve the city as well as the agent does with the tanks we already have?

Physics is universal, money is local

A figure in West African francs reads in Ouagadougou and nowhere else. But converting everything to dollars is the wrong fix: an electricity tariff varies far more between countries than an exchange rate corrects for. So the primary units here are the ones that read everywhere - kilowatt-hours, litres of diesel, kilograms of CO2, and person-days above the WHO survival threshold. Cost is a presentation layer with the local tariff written on it.

This is also the one input the project cannot fetch by itself. Climate comes from Open-Meteo with no key and no account; there is no open, reliable equivalent for city-level electricity tariffs. The figures used are declared orders of magnitude, kept in one file so a utility that knows its real tariff replaces them in a minute - better a number labelled generic than a precise one that is wrong.

Check it yourself

Every number on this page comes from a JSON file, and every JSON file comes from a command. Nothing here was typed by hand.

pip install -r requirements.txt

python -m wiiga.resultats    --journees 365   # the tables above
python -m wiiga.graines      --journees 365   # three training seeds, mean and spread
python -m wiiga.transfert    --journees 365   # the city transfer
python -m wiiga.equivalence  --journees 365   # how much storage the agent replaces
python -m wiiga.ville        Chennai          # plug in any city, no API key
python -m wiiga.train        --pas 600000     # retrain, ~20 min on a laptop CPU

This page is a single static file with its data written inside it. No server, no serverless function, no network request - it cannot break in front of you because an API changed its mind.

What this is not