A reservoir and a farm · Водохранилище и поле · Suv ombori va dala
Step 3 of 8
A reservoir trade-off in four TaqSim blocks
hydrosolutions GmbH · Venusstrasse 29, 8050 Zürich, Switzerland · www.hydrosolutions.ch · hs@hydrosolutions.ch · +41 43 535 05 80
1 The question
The two knobs are the storage trigger and the fraction of summer demand to release. The two goals are average annual shortage (Mm³/year) and the worst monthly shortage (% of demand). Years with shortage is a third diagnostic, not an optimization goal. Pareto front means the outcomes of unbeaten plans: no other plan is at least as good on both goals and strictly better on one.
Две настройки — порог запаса и доля летней потребности для выпуска. Две цели — средний годовой дефицит (млн м³/год) и наибольший месячный дефицит (% потребности). Число дефицитных лет — дополнительный показатель, не цель оптимизации. Фронт Парето — результаты планов, для которых нет другого плана не хуже по обеим целям и строго лучше хотя бы по одной.
Ikki sozlama — zaxira chegarasi va yozgi talabning chiqariladigan ulushi. Ikki maqsad — oʻrtacha yillik taqchillik (mln m³/yil) va eng katta oylik taqchillik (talabning %). Taqchil yillar soni qoʻshimcha koʻrsatkich, optimallashtirish maqsadi emas. Pareto fronti — ikkala maqsadda kamida teng va bittasida qatʼiy yaxshiroq boshqa rejasi boʻlmagan rejalarning natijalari.
A reservoir fills in winter. A farm takes water from it in summer. In most years there is enough. In a dry year there is not, and the operator has two ways to run the reservoir:
- Deliver in full until the reservoir is empty. The farm gets everything it asks for, for as long as possible. If the water runs out, the last weeks of summer get almost nothing.
- Ration early. When the reservoir is getting low, deliver only part of what the farm asks for. The water lasts longer, but the farm is short more often, including in years when the water would have lasted anyway.
Which is better? It depends on what you count. This notebook builds the smallest model that shows it: four TaqSim blocks, two knobs, two goals. Then it lets the optimizer search.
What you will take away
- How to build and run a small water system in TaqSim, block by block.
- Why two reasonable goals can pull in opposite directions, and how to see every sensible compromise at once.
- How to hand the search to the optimizer, and how to read what it returns.
The technical words. Engineers call “deliver in full until empty” the standard operating policy and “ration early” hedging. A plan that no other plan beats on every goal is called Pareto-optimal, and the set of such plans is the Pareto front. This notebook says “unbeaten plans”.
Suv ombori qishda toʻladi. Yozda xoʻjalik undan suv oladi. Koʻp yillarda suv yetarli. Qurgʻoqchil yilda esa yetmaydi, va operatorning omborni boshqarish uchun ikki yoʻli bor:
- Ombor boʻshaguncha toʻliq berish. Xoʻjalik soʻraganini iloji boricha uzoq vaqt toʻliq oladi. Suv tugasa, yozning oxirgi haftalarida deyarli hech narsa qolmaydi.
- Erta cheklash. Ombor kamayib qolganda xoʻjalikka soʻraganining faqat bir qismini berish. Suv uzoqroqqa yetadi, lekin xoʻjalik koʻproq hollarda, hatto suv baribir yetgan boʻladigan yillarda ham, taqchillik koʻradi.
Qaysi biri yaxshiroq? Bu nimani hisoblashingizga bogʻliq. Ushbu daftar buni koʻrsatadigan eng kichik modelni quradi: toʻrtta TaqSim bloki, ikkita dastak, ikkita maqsad. Soʻngra qidiruvni optimallashtirgichga topshiradi.
Siz nimani oʻrganasiz
- TaqSimda kichik suv tizimini blokma-blok qurish va ishga tushirishni.
- Nima uchun ikki oqilona maqsad qarama-qarshi tomonga tortishi mumkinligini va barcha maʼqul murosalarni birdaniga qanday koʻrishni.
- Qidiruvni optimallashtirgichga qanday topshirish va u qaytargan natijani qanday oʻqishni.
Atamalar. Muhandislar “ombor boʻshaguncha toʻliq berish”ni standart boshqaruv siyosati, “erta cheklash”ni esa xejirlash (hedging) deb ataydi. Hech bir boshqa reja barcha maqsadlar boʻyicha undan ustun kelmaydigan reja Pareto-optimal, bunday rejalar toʻplami esa Pareto fronti deyiladi. Bu daftarda ular “ustun kelib boʻlmaydigan rejalar” deb yuritiladi.
Водохранилище наполняется зимой. Летом из него берёт воду хозяйство. В большинстве лет воды хватает. В сухой год её не хватает, и у оператора есть два способа управлять водохранилищем:
- Подавать полностью, пока водохранилище не опустеет. Хозяйство получает всё, что просит, так долго, как возможно. Если вода кончается, последние недели лета остаются почти ни с чем.
- Лимитировать заранее. Когда воды в водохранилище становится мало, подавать лишь часть того, что просит хозяйство. Воды хватает дольше, но дефицит случается чаще, в том числе в годы, когда воды и так хватило бы.
Что лучше? Это зависит от того, что считать. В этом блокноте собрана самая маленькая модель, которая это показывает: четыре блока TaqSim, два рычага, две цели. Затем поиск поручается оптимизатору.
Что вы вынесете из блокнота
- Как собрать и запустить небольшую водную систему в TaqSim, блок за блоком.
- Почему две разумные цели могут тянуть в разные стороны и как увидеть все разумные компромиссы сразу.
- Как поручить поиск оптимизатору и как читать то, что он возвращает.
Термины. Инженеры называют «подавать полностью, пока не опустеет» стандартной политикой управления, а «лимитировать заранее» — хеджированием. План, который ни один другой план не превосходит по всем целям, называется Парето-оптимальным, а множество таких планов — фронтом Парето. В этом блокноте они называются «непревзойдённые планы».
2 The system, block by block
The reservoir settings are capacity (40 Mm³) and initial storage (20 Mm³). A synthetic river is a generated inflow sequence, not observations from the Zarafshan. A random seed fixes the random draws: repeating it in the same environment repeats the river; changing it gives a different sample from the same target distribution. To change settings, open the step 3 Colab notebook; this coursebook is a static reading copy.
Настройки водохранилища — ёмкость (40 млн м³) и начальный запас (20 млн м³). Синтетическая река — сгенерированная последовательность притоков, не наблюдения Зарафшана. Seed задаёт случайные числа: то же значение в той же среде повторяет реку; другое даёт новую выборку из того же распределения. Менять настройки можно в тетради Colab шага 3; веб-учебник служит для чтения.
Ombor sozlamalari — sigʻim (40 mln m³) va boshlangʻich zaxira (20 mln m³). Sunʼiy daryo — yaratilgan oqimlar ketma-ketligi, Zarafshon kuzatuvlari emas. Seed tasodifiy sonlarni belgilaydi: bir xil muhitda bir xil seed oqimni takrorlaydi; boshqasi oʻsha taqsimotdan yangi namuna beradi. Sozlamalarni 3-qadam Colab daftarida oʻzgartiring; veb-darslik oʻqish uchun.
TaqSim describes a water system as a network: nodes that hold, use or pass on water, joined by edges along which water flows. Ours will have four nodes and three edges. We start with three, the river, the farm and the outlet, and add the reservoir once we have seen what goes wrong without it.
TaqSim suv tizimini tarmoq sifatida tasvirlaydi: suvni saqlaydigan, ishlatadigan yoki uzatadigan tugunlar va ular orasida suv oqadigan qirralar. Bizning tarmoqda toʻrtta tugun va uchta qirra boʻladi. Uchtasidan boshlaymiz: daryo, xoʻjalik va quyi oqim; suv omborisiz nima notoʻgʻri boʻlishini koʻrgach, uni qoʻshamiz.
TaqSim описывает водную систему как сеть: узлы, которые хранят, используют или передают воду, соединённые рёбрами, по которым вода течёт. В нашей сети будет четыре узла и три ребра. Начнём с трёх, реки, хозяйства и нижнего бьефа, и добавим водохранилище, когда увидим, что без него идёт не так.
Show the code
# Standard tools
import inspect # to show the source code of a function or class inside this notebook
import logging
import pandas as pd # tables
from IPython.display import HTML, Markdown # to show formatted text and HTML as cell output
# TaqSim itself: the four kinds of node we use, the edge that joins them, and the system that holds them
from taqsim import Demand, Edge, Sink, Source, Storage, TimeSeries, WaterSystem
from taqsim.node import NoLoss # a loss rule that loses nothing (reservoirs need one)
from taqsim.node.events import DeficitRecorded, WaterSpilled # the events a short Demand and a full Storage record
from taqsim.time import Frequency # the length of one simulation step (monthly here)
# The helpers written for this coursebook (src/zarafshan_taqsim/rationing_coursebook.py)
from zarafshan_taqsim.rationing_coursebook import (
CAPACITY_MM3,
EVAPORATION_SHARE_EXAMPLE,
MONTHLY_NEED_MM3,
NO_RATIONING,
PAPER_REGIME,
START_STORAGE_MM3,
SUMMER_NEED_MM3,
WINTER_NEED_MM3,
YEARS,
Plan,
RationingRelease,
SUMMER_PLAN_FOR_WINTER,
WINTER_FIELDS_TOTAL_MM3,
WINTER_IN_FULL,
SummerEvaporation,
WinterPlan,
WinterRelease,
configure,
drought_window_start,
explorer_html,
explorer_payload,
front_of,
objectives,
optimise_plans,
plan_grid,
forecast_value,
least_winter_shortage,
plot_forecast_skill,
plot_forecast_value,
plot_front_shift,
plot_inflows,
plot_network,
plot_regime_comparison,
plot_runs,
plot_score_maps,
plot_shortage_years,
plot_system_sketch,
plot_tradeoff,
plot_winter_decisions,
plot_winter_fronts,
score_by_skill,
score_plans,
score_winter_plans,
season_statistics,
season_totals,
seasonal_forecast,
simulate_plan,
simulate_winter,
simulate_without_reservoir,
synthetic_inflows,
winter_grid,
winter_plan_within,
)
logging.basicConfig(level=logging.WARNING) # only show warnings and errors, not progress messages
pd.set_option("display.precision", 2) # two decimals in tables
LANG = None # the web page shows all three languages and lets you switch; a notebook is built for one language
def say(en: str, uz: str, ru: str) -> Markdown:
"""Text in three languages: all three on the web page (the switcher picks one), one in a notebook."""
if LANG:
return Markdown({"en": en, "uz": uz, "ru": ru}[LANG])
blocks = (("en", en), ("uz", uz), ("ru", ru))
return Markdown("\n\n".join(f"::: {{.lang-{code}}}\n{text}\n:::" for code, text in blocks))
def show_source(thing) -> Markdown:
"""Show the source code of a function or class as a code block."""
return Markdown(f"```python\n{inspect.getsource(thing)}```")- river is a
Source: it puts water into the network each month, from a list of numbers. - farm is a
Demand: it asks for a given amount each month and records how much it did not get. - downstream is a
Sink: whatever the farm does not use leaves the network here.
- river (daryo) —
Source: har oy sonlar roʻyxatidan tarmoqqa suv kiritadi. - farm (xoʻjalik) —
Demand: har oy maʼlum miqdorni soʻraydi va qanchasini olmaganini yozib boradi. - downstream (quyi oqim) —
Sink: xoʻjalik ishlatmagan suv tarmoqni shu yerdan tark etadi.
- river (река) —
Source: каждый месяц подаёт воду в сеть по списку чисел. - farm (хозяйство) —
Demand: каждый месяц запрашивает заданный объём и записывает, сколько не получило. - downstream (нижний бьеф) —
Sink: всё, что хозяйство не использовало, покидает сеть здесь.
The numbers come from a classic paper on reservoir performance (Hashimoto, Stedinger and Loucks, 1982, p. 17 and Table 1), with one change. The paper’s river peaks in winter. Rivers in Central Asia are fed by snow and glacier melt: they peak in summer and are lowest in winter. So we swap the paper’s two seasons and keep everything else. The comparison section runs the paper’s own river alongside ours. Volumes are in million cubic metres (Mm³).
Raqamlar suv omborlari samaradorligi haqidagi klassik maqoladan olingan (Hashimoto, Stedinger va Loucks, 1982, 17-bet va 1-jadval), bitta oʻzgarish bilan. Maqoladagi daryo qishda toʻlib oqadi. Markaziy Osiyo daryolari esa qor va muzliklar erishidan toʻyinadi: ular yozda toʻlib oqadi, qishda esa eng kam suvli. Shuning uchun biz maqoladagi ikki faslni oʻrin almashtirdik, qolgan hamma narsani saqlab qoldik. Taqqoslash boʻlimi maqoladagi daryoni biznikining yonida hisoblaydi. Hajmlar million kub metrda (mln m³).
Числа взяты из классической статьи о надёжности водохранилищ (Hashimoto, Stedinger и Loucks, 1982, с. 17 и таблица 1) с одним изменением. Река в статье многоводна зимой. Реки Центральной Азии питаются талыми водами снега и ледников: пик стока приходится на лето, а зимой сток минимален. Поэтому мы меняем местами два сезона из статьи, а всё остальное оставляем. Раздел сравнения прогоняет реку из статьи рядом с нашей. Объёмы в миллионах кубометров (млн м³).
Bu raqamlar qatʼiy emas, sozlamalar. Quyidagi katak ularni ushbu daftar uchun belgilaydi: daryo, xoʻjalik va shu yerdayoq suv omborining ikki raqami; ombor qurish boʻlimida kiradi. Qiymatni oʻzgartiring, daftarni shu yerdan qayta ishga tushiring, va har bir hisob, jadval va rasm unga ergashadi. Matnda standart qiymatlar keltirilgan.
Show the code
settings = configure(
winter_inflow=(25.0, 10.0), # winter inflow: mean and standard deviation, Mm³ per season (the paper: 40 and 15)
summer_inflow=(40.0, 15.0), # summer inflow: mean and standard deviation (the paper: 25 and 10)
summer_need=45.0, # what the farm needs over the six summer months, Mm³
winter_need=5.0, # and over the six winter months: a small off-season demand, taken from the paper
capacity=40.0, # the reservoir, Mm³
start_storage=20.0, # water in the reservoir when a simulation starts, Mm³
years=100, # length of the synthetic river
seed=72, # the random seed of the river: another seed, another river with the same statistics
)
# The names the text uses follow the settings
from zarafshan_taqsim.rationing_coursebook import CAPACITY_MM3, MONTHLY_NEED_MM3, START_STORAGE_MM3, SUMMER_NEED_MM3, WINTER_NEED_MM3, YEARS
settings.style.hide(axis="index")| setting | value |
|---|---|
| Reservoir capacity | 40 Mm³ |
| What the farm needs in summer | 45 Mm³ (7.50 a month) |
| What the farm needs in winter | 5 Mm³ (0.83 a month) |
| River inflow in winter | 25 Mm³ on average, standard deviation 10 |
| River inflow in summer | 40 Mm³ on average, standard deviation 15 |
| Water in the reservoir at the start | 20 Mm³ |
| Years in the synthetic river | 100 |
| Random seed of the river | 72 |
The summer need (45) is larger than the average summer inflow (40), so the farm depends on water stored in winter.
A model year is six winter months followed by six summer months; no calendar dates are attached. Read it as the hydrological year used in the Zarafshan basin: winter is October to March, summer (the irrigation season) April to September. Within a season nothing changes from month to month: each season’s inflow is spread evenly over its six months, and so is the farm’s need. The only seasonality is winter against summer, as in the paper. The real monthly shape of the Zarafshan, with its July peak, appears in the sixteen-blocks game and in the simulation coursebook.
Yozgi ehtiyoj (45) oʻrtacha yozgi oqimdan (40) katta, shuning uchun xoʻjalik qishda saqlangan suvga bogʻliq.
Model yili — oltita qishki oy, soʻngra oltita yozgi oy; kalendar sanalari biriktirilmagan. Uni Zarafshon havzasida qoʻllaniladigan gidrologik yil deb oʻqing: qish — oktyabrdan martgacha, yoz (sugʻorish mavsumi) — apreldan sentyabrgacha. Mavsum ichida oydan oyga hech narsa oʻzgarmaydi: har bir mavsumning oqimi oltita oyga teng taqsimlanadi, xoʻjalikning talabi ham shunday. Yagona mavsumiylik — maqoladagi kabi qish va yoz orasidagi farq. Zarafshonning iyuldagi choʻqqisi bilan haqiqiy oylik shakli oʻn olti blokli oʻyinda va modellashtirish darsligida koʻrsatilgan.
Летняя потребность (45) больше среднего летнего притока (40), поэтому хозяйство зависит от воды, накопленной зимой.
Модельный год — шесть зимних месяцев, затем шесть летних; календарные даты не заданы. Читайте его как гидрологический год, принятый в бассейне Зарафшана: зима — с октября по март, лето (вегетационный период) — с апреля по сентябрь. Внутри сезона от месяца к месяцу ничего не меняется: приток каждого сезона распределён равномерно по его шести месяцам, как и потребность хозяйства. Единственная сезонность — зима против лета, как в статье. Настоящий месячный ход Зарафшана с июльским пиком показан в игре «Зарафшан в шестнадцати блоках» и в учебнике по моделированию.
2.1 The river
Every year has six equal winter months and six equal summer months, but the two seasonal totals vary between years. “Target” describes the generator’s distribution, from the paper with seasons swapped; “this river” describes one finite sample of 100 years. Sample statistics generally differ from their targets. These are not measured Zarafshan statistics.
В каждом году шесть одинаковых зимних и шесть одинаковых летних месяцев, но сезонные объёмы меняются между годами. «Target» — параметры распределения из статьи с переставленными сезонами; «this river» — одна выборка из 100 лет. Статистики выборки обычно отличаются от целевых. Это не измеренные статистики Зарафшана.
Har yili oltita teng qishki va oltita teng yozgi oy bor, ammo mavsumiy hajmlar yildan yilga oʻzgaradi. “Target” — maqoladagi mavsumlari almashtirilgan taqsimot parametrlari; “this river” — 100 yillik bitta namuna. Namuna statistikasi odatda maqsaddan farq qiladi. Bular Zarafshon oʻlchovlari emas.
We do not have a hundred years of measurements, so we make a river: one hundred years of winter and summer inflows with the averages and spread from the table above. A dry season tends to be followed by another dry season, as in real rivers.
Bizda yuz yillik oʻlchovlar yoʻq, shuning uchun daryoni oʻzimiz yaratamiz: yuqoridagi jadvaldagi oʻrtacha qiymatlar va tarqalish bilan yuz yillik qishki va yozgi oqimlar. Haqiqiy daryolardagidek, qurgʻoqchil fasldan keyin koʻpincha yana qurgʻoqchil fasl keladi.
Ста лет измерений у нас нет, поэтому мы создаём реку сами: сто лет зимних и летних притоков со средними и разбросом из таблицы выше. За сухим сезоном, как и в настоящих реках, чаще следует ещё один сухой сезон.
Show the code
# The river is drawn from the settings cell: its winter and summer inflow (mean and standard deviation),
# its years and its seed. 100 years x 12 months = 1 200 numbers, each a month's inflow in Mm³.
inflows = synthetic_inflows()
# Check the river against the numbers we asked for: averages, spread and the link from one season to the next
season_statistics(inflows).style.hide(axis="index").format(precision=2)| statistic | target | this river |
|---|---|---|
| Winter inflow, mean (Mm³) | 25.00 | 24.81 |
| Winter inflow, standard deviation (Mm³) | 10.00 | 10.15 |
| Summer inflow, mean (Mm³) | 40.00 | 38.73 |
| Summer inflow, standard deviation (Mm³) | 15.00 | 14.91 |
| Link winter → next summer (correlation) | 0.65 | 0.66 |
| Link summer → next winter (correlation) | 0.60 | 0.57 |
synthetic_inflows() always gives the same river (it uses a fixed random seed), so every result in this notebook can be reproduced. The table compares the river’s statistics with the ones we asked for; they agree to within a few percent.
synthetic_inflows() har doim bir xil daryoni beradi (u qatʼiy tasodifiy urugʻdan foydalanadi), shuning uchun bu daftardagi har bir natijani takrorlash mumkin. Jadval daryoning statistikasini biz soʻragan qiymatlar bilan taqqoslaydi; ular bir necha foiz aniqlikda mos keladi.
synthetic_inflows() всегда даёт одну и ту же реку (используется фиксированное зерно генератора случайных чисел), поэтому любой результат в этом блокноте воспроизводим. Таблица сравнивает статистику реки с заданными значениями; они совпадают с точностью до нескольких процентов.
Where this differs from the paper. (1) The seasons are swapped, as explained above. (2) Each season’s inflow is spread evenly over six monthly steps. (3) The paper derives its operating rules mathematically; we use a simple rule with two knobs. (4) The link between seasons is applied to the logarithms of the flows, which is an assumption. The numbers in this notebook are therefore our own, not a reproduction of the paper’s.
Maqoladan farqlar. (1) Yuqorida tushuntirilganidek, fasllar oʻrin almashtirilgan. (2) Har bir faslning oqimi oltita oylik qadamga teng taqsimlangan. (3) Maqola boshqaruv qoidalarini matematik yoʻl bilan keltirib chiqaradi; biz ikki dastakli oddiy qoidadan foydalanamiz. (4) Fasllar orasidagi bogʻliqlik oqimlarning logarifmlariga qoʻllangan, bu — taxmin. Shu bois bu daftardagi raqamlar bizniki, maqoladagilarning takrori emas.
Чем это отличается от статьи. (1) Сезоны поменяны местами, как объяснено выше. (2) Приток каждого сезона равномерно распределён по шести месячным шагам. (3) В статье правила управления выводятся математически; мы используем простое правило с двумя рычагами. (4) Связь между сезонами задана для логарифмов стока — это допущение. Поэтому числа в этом блокноте наши собственные, а не воспроизведение чисел статьи.
3 Before the reservoir: a river and a farm
We first run the river and farm alone, then compare with a 40 Mm³ reservoir that releases full demand while water is available. The two panels show these two systems. Each bar is one year’s worst monthly shortage, as a percentage of that month’s demand; 72% short means 28% delivered. This percentage is different from the number of years with any shortage. Annual demand is 45 in summer + 5 in winter = 50 Mm³.
Сначала моделируем реку и поле без водохранилища, затем сравниваем с водохранилищем 40 млн м³, подающим полную потребность при наличии воды. Две панели показывают эти системы. Столбик — худший месячный дефицит года в процентах потребности: 72% дефицита означает 28% подачи. Это не число дефицитных лет. Годовая потребность: 45 летом + 5 зимой = 50 млн м³.
Avval daryo va dalani omborsiz, keyin suv borida toʻliq talabni chiqaradigan 40 mln m³ ombor bilan solishtiramiz. Ikki panel shu tizimlarni koʻrsatadi. Har ustun yilning eng yomon oylik taqchilligi, talabga nisbatan foizda: 72% taqchillik — 28% yetkazish. Bu taqchil yillar soni emas. Yillik talab: yozda 45 + qishda 5 = 50 mln m³.
Start without the reservoir. The farm sits on the river and takes what the river brings; what it does not use flows on. Three blocks, two edges, and nothing to decide: there is no rule, because there is nothing to store.
Suv omborisiz boshlaymiz. Xoʻjalik daryo boʻyida joylashgan va daryo keltirganini oladi; ishlatmaganini oʻtkazib yuboradi. Uchta blok, ikkita qirra va hech qanday qaror yoʻq: qoida yoʻq, chunki saqlaydigan narsa yoʻq.
Начнём без водохранилища. Хозяйство стоит на реке и берёт то, что река приносит; что оно не использует, течёт дальше. Три блока, два ребра и никаких решений: правила нет, потому что нечего хранить.
Building a TaqSim model always has the same four steps: create an empty system, add the nodes, add the edges, check and run. Here are all four for the three blocks.
TaqSim modelini qurish har doim bir xil toʻrt qadamdan iborat: boʻsh tizim yaratish, tugunlarni qoʻshish, qirralarni qoʻshish, tekshirish va ishga tushirish. Mana uchta blok uchun toʻrttala qadam.
Сборка модели TaqSim всегда состоит из одних и тех же четырёх шагов: создать пустую систему, добавить узлы, добавить рёбра, проверить и запустить. Вот все четыре для трёх блоков.
Show the code
# 1. An empty system. One simulation step is one month.
river_farm = WaterSystem(frequency=Frequency.MONTHLY)
# 2. Three nodes: the river gives water, the farm asks for it, the outlet takes what is left.
river_farm.add_node(Source(id="river", inflow=TimeSeries(values=list(inflows))))
river_farm.add_node(Demand(id="farm", requirement=TimeSeries(values=list(MONTHLY_NEED_MM3) * YEARS)))
river_farm.add_node(Sink(id="downstream"))
# 3. Two edges, each named after the nodes it joins.
for source, target in [("river", "farm"), ("farm", "downstream")]:
river_farm.add_edge(Edge(id=f"{source}_to_{target}", source=source, target=target))
# 4. Check the network, then run all 1 200 months.
river_farm.validate()
river_farm.simulate(len(inflows))
# The farm records a DeficitRecorded event in every month it gets less than it needs; add up the missing water
not_delivered = sum(event.deficit for event in river_farm.nodes["farm"].events_of_type(DeficitRecorded))
print(f"Water not delivered: {not_delivered / YEARS:.1f} Mm³ per year, on average")Water not delivered: 9.6 Mm³ per year, on average
That is a lot: the farm asks for 50 Mm³ a year and misses 9.6 of them, and it is short in 72 of 100 years. The reason is not a lack of water but its timing: the farm needs 45 Mm³ in summer, the river brings 40 on average, and the 25 Mm³ that arrive in winter flow past the farm unused. The lower panel shows what a reservoir does. A reservoir of 40 Mm³ carries winter water into summer: the short years fall from 72 to 10, the water missed from 9.6 to 1.1 Mm³ a year. This is what reservoirs are for.
The reservoir has a limit and introduces a decision. It does not change the single worst month (72 % short in both panels): in the driest summer the reservoir is already empty, because it holds only 40 Mm³ and can only fill from the low winter flow. And it brings a decision with it, how to release the water it holds, which the river alone never asked of us. The rest of this coursebook is about that decision.
Bu koʻp: xoʻjalik yiliga 50 mln m³ soʻraydi va shundan 9.6 ini olmaydi, 100 yildan 72 tasida taqchillik koʻradi. Sabab suv yetishmasligida emas, uning vaqtida: xoʻjalikka yozda 45 mln m³ kerak, daryo oʻrtacha 40 keltiradi, qishda kelgan 25 mln m³ esa xoʻjalik yonidan foydalanilmay oqib oʻtadi. Pastki panel suv ombori nima qilishini koʻrsatadi. 40 mln m³ li suv ombori qishki suvni yozga olib oʻtadi: taqchil yillar 72 tadan 10 taga, olinmagan suv yiliga 9.6 dan 1.1 mln m³ ga tushadi. Suv omborlari ana shu uchun quriladi.
Omborning chegarasi va yangi qaror bor. U eng yomon oyni oʻzgartirmaydi (ikkala panelda ham 72 % taqchillik): eng qurgʻoqchil yozda ombor allaqachon boʻsh, chunki u bor-yoʻgʻi 40 mln m³ sigʻdiradi va faqat kam suvli qishki oqimdan toʻlishi mumkin. Va u oʻzi bilan bir qarorni olib keladi: saqlagan suvini qanday chiqarish kerak, buni daryoning oʻzi hech qachon bizdan soʻramagan edi. Bu darslikning qolgan qismi ana shu qaror haqida.
Это много: хозяйство просит 50 млн м³ в год и недополучает 9.6 из них, а дефицит испытывает в 72 из 100 лет. Причина не в нехватке воды, а в её сроках: хозяйству нужно 45 млн м³ летом, река приносит в среднем 40, а 25 млн м³, приходящие зимой, протекают мимо хозяйства без пользы. Нижняя панель показывает, что делает водохранилище. Водохранилище на 40 млн м³ переносит зимнюю воду в лето: число дефицитных лет падает с 72 до 10, недополученная вода с 9.6 до 1.1 млн м³ в год. Для этого водохранилища и строят.
У водохранилища есть ограничение и новый вопрос. Оно не меняет самый худший месяц (72 % дефицита на обеих панелях): в самое сухое лето водохранилище уже пусто, потому что вмещает лишь 40 млн м³ и наполняется только из скудного зимнего стока. И оно приносит с собой решение, как выпускать накопленную воду, которого река сама по себе от нас никогда не требовала. Остальная часть этого учебника об этом решении.
4 Build it in TaqSim
The reservoir is the fourth block. It goes between the river and the farm, and it brings two numbers of its own, set in the settings cell above: how much it holds, and how much is in it when a simulation starts.
- reservoir is a
Storage: it keeps water up to its capacity, loses some (if we tell it to), and releases some according to a rule. This is where the decisions are made.
The reservoir (40 Mm³) is smaller than the summer need (45), and it can only be filled from the low winter flow, so there is little room for error. The example plans below start rationing at 16 Mm³; if you change the capacity, keep it above that.
Suv ombori — toʻrtinchi blok. U daryo bilan xoʻjalik orasiga kiradi va yuqoridagi sozlamalar katagida belgilangan ikkita oʻz raqamini olib keladi: u qancha suv sigʻdiradi va simulyatsiya boshlanganda unda qancha suv bor.
- reservoir (suv ombori) —
Storage: suvni sigʻimigacha saqlaydi, bir qismini yoʻqotadi (agar shunday buyursak) va bir qismini qoida boʻyicha chiqaradi. Qarorlar shu yerda qabul qilinadi.
Suv ombori (40 mln m³) yozgi ehtiyojdan (45) kichik va uni faqat kam suvli qishki oqimdan toʻldirish mumkin, shu bois xatoga oʻrin kam. Quyidagi namunaviy rejalar cheklashni 16 mln m³ da boshlaydi; sigʻimni oʻzgartirsangiz, uni undan yuqori qoldiring.
Водохранилище — четвёртый блок. Оно встаёт между рекой и хозяйством и приносит два своих числа, заданных в ячейке настроек выше: сколько оно вмещает и сколько воды в нём в начале расчёта.
- reservoir (водохранилище) —
Storage: хранит воду до своей ёмкости, часть теряет (если мы так зададим) и часть сбрасывает по правилу. Здесь принимаются решения.
Водохранилище (40 млн м³) меньше летней потребности (45), а наполнить его можно только из скудного зимнего стока, так что запаса на ошибку почти нет. Примерные планы ниже начинают лимитирование при 16 млн м³; меняя ёмкость, оставьте её выше.
4.1 The rule with two knobs
A TaqSim reservoir needs a release rule: a small class that says how much water to let out each month. Ours has two knobs.
- Trigger (Mm³): rationing starts when the reservoir holds less than this.
- Ration (0 to 1): the share of the summer need that is then delivered.
What a class is, in plain words. A program like TaqSim is built from objects: a reservoir, a farm, a rule. A class describes one kind of object: what it remembers and what it can do. Think of a printed form with a few fields to fill in and a short instruction on the back. The class is the blank form; every object made from it is one filled-in copy. Our rule class remembers two numbers, the trigger and the ration (its fields), and knows how to do one thing, release(...), which TaqSim asks it to do every month (its method). Writing RationingRelease(trigger=16, ration=0.8) fills in one copy of the form. Plan, Storage and Demand below are other forms of the same kind. That is all “object-oriented” means here: the model is a handful of such forms that pass water to each other. You need nothing more to read or change this notebook.
TaqSim suv omboriga chiqarish qoidasi kerak: har oy qancha suv chiqarishni aytadigan kichik klass. Bizning qoidada ikkita dastak bor.
- Chegara (trigger, mln m³): omborda suv shundan kam boʻlganda cheklash boshlanadi.
- Ulush (ration, 0 dan 1 gacha): shunda yozgi ehtiyojning qancha qismi beriladi.
Klass nima, oddiy soʻzlar bilan. TaqSim kabi dastur obyektlardan qurilgan: suv ombori, xoʻjalik, qoida. Klass bir turdagi obyektni tasvirlaydi: u nimani eslab qoladi va nima qila oladi. Bir nechta toʻldiriladigan katagi va orqasida qisqa koʻrsatmasi bor bosma blankni tasavvur qiling. Klass — boʻsh blank; undan yasalgan har bir obyekt — toʻldirilgan bitta nusxa. Bizning qoida klassimiz ikkita raqamni eslab qoladi, chegara va cheklash (uning maydonlari), va bitta ishni qilishni biladi, release(...), TaqSim buni har oy undan soʻraydi (uning metodi). RationingRelease(trigger=16, ration=0.8) deb yozish blankning bitta nusxasini toʻldiradi. Quyidagi Plan, Storage va Demand — oʻsha turdagi boshqa blanklar. Bu yerda “obyektga yoʻnaltirilgan” degani shu, xolos: model — bir-biriga suv uzatadigan bir nechta shunday blank. Bu daftarni oʻqish yoki oʻzgartirish uchun boshqa hech narsa kerak emas.
Водохранилищу в TaqSim нужно правило сброса: небольшой класс, который говорит, сколько воды выпускать каждый месяц. У нашего правила два рычага.
- Порог (trigger, млн м³): лимитирование начинается, когда в водохранилище меньше этого объёма.
- Доля (ration, от 0 до 1): какая часть летней потребности тогда подаётся.
Что такое класс, простыми словами. Программа вроде TaqSim собрана из объектов: водохранилище, хозяйство, правило. Класс описывает один вид объекта: что он помнит и что умеет делать. Представьте печатный бланк с несколькими полями и короткой инструкцией на обороте. Класс — это чистый бланк; каждый объект, созданный из него, — один заполненный экземпляр. Наш класс правила помнит два числа, порог и долю подачи (его поля), и умеет делать одно, release(...), о чём TaqSim просит его каждый месяц (его метод). Запись RationingRelease(trigger=16, ration=0.8) заполняет один экземпляр бланка. Plan, Storage и Demand ниже — другие бланки того же рода. Вот и всё, что здесь значит «объектно-ориентированный»: модель — это несколько таких бланков, передающих друг другу воду. Больше ничего не нужно, чтобы читать или менять эту тетрадь.
@dataclass(frozen=True)
class RationingRelease(Strategy):
"""Release what the farm needs.
In summer, when the reservoir holds less than the trigger, release only the ration of it.
"""
__params__: ClassVar[tuple[str, ...]] = ("trigger", "ration")
__bounds__: ClassVar[dict[str, tuple[float, float]]] = {
"trigger": (0.0, CAPACITY_MM3),
"ration": (0.0, 1.0),
}
trigger: float = 0.0
ration: float = 1.0
def release(self, node: Storage, inflow: float, t: Timestep) -> float:
month = t.index % 12 # 0-5 = winter, 6-11 = summer
need = MONTHLY_NEED_MM3[month] # what the farm asks for this month, Mm³
if month in SUMMER_MONTHS and node.storage < self.trigger:
need *= self.ration # rationing: deliver only this share of the need
return min(need, node.storage) # never release more than is in the reservoirReading the class from the top:
@dataclass(frozen=True)makes it a small, unchangeable record: it holds its two knobs and nothing else.__params__names the knobs the optimizer may turn, and__bounds__says between which values.triggerandrationare the knobs, with their defaults (0 and 1: no rationing).release(...)is called by TaqSim once per month. It gets the reservoir (node, from which it reads the current storage), this month’s inflow and the time stept, and returns the volume to release in Mm³.
A trigger of 0 (or a ration of 1) means no rationing at all.
Klassni yuqoridan oʻqiymiz:
@dataclass(frozen=True)uni kichik, oʻzgarmas yozuvga aylantiradi: unda ikki dastakdan boshqa hech narsa yoʻq.__params__optimallashtirgich burashi mumkin boʻlgan dastaklarni nomlaydi,__bounds__esa qaysi qiymatlar orasida ekanini aytadi.triggervaration— dastaklar, standart qiymatlari bilan (0 va 1: cheklash yoʻq).release(...)ni TaqSim har oy bir marta chaqiradi. U suv omborini (node, undan joriy suv hajmini oʻqiydi), shu oyning oqimini vatvaqt qadamini oladi va chiqariladigan hajmni mln m³ da qaytaradi.
Chegara 0 (yoki ulush 1) — umuman cheklash yoʻq degani.
Читаем класс сверху вниз:
@dataclass(frozen=True)делает его маленькой неизменяемой записью: в ней два рычага и больше ничего.__params__называет рычаги, которые может крутить оптимизатор, а__bounds__— в каких пределах.triggerиration— сами рычаги со значениями по умолчанию (0 и 1: без лимитирования).release(...)TaqSim вызывает раз в месяц. Метод получает водохранилище (node, из которого читает текущий объём), приток этого месяца и шаг времениtи возвращает объём сброса в млн м³.
Порог 0 (или доля 1) означает полное отсутствие лимитирования.
4.2 Wire the four blocks
The same four steps as for the three blocks: create an empty system, add the nodes, add the edges, check and run. The reservoir goes between the river and the farm, and it needs the rule from the section above.
Uchta blokdagi kabi toʻrt qadam: boʻsh tizim yaratish, tugunlarni qoʻshish, qirralarni qoʻshish, tekshirish va ishga tushirish. Suv ombori daryo bilan xoʻjalik orasiga kiradi, va unga yuqoridagi boʻlimdagi qoida kerak.
Те же четыре шага, что и для трёх блоков: создать пустую систему, добавить узлы, добавить рёбра, проверить и запустить. Водохранилище встаёт между рекой и хозяйством, и ему нужно правило из раздела выше.
Show the code
# 1. An empty system. One simulation step is one month.
system = WaterSystem(frequency=Frequency.MONTHLY)
# 2. The four nodes. Each has an id (its name in the network) and the settings its kind needs.
system.add_node(
Source(
id="river",
inflow=TimeSeries(values=list(inflows)), # one value per month, Mm³
)
)
system.add_node(
Storage(
id="reservoir",
capacity=CAPACITY_MM3, # 40 Mm³; more than this spills
initial_storage=START_STORAGE_MM3, # 20 Mm³ in store when the simulation starts
release_policy=RationingRelease(trigger=0.0, ration=1.0), # our rule; these knobs mean no rationing
loss_rule=NoLoss(), # no evaporation for now
)
)
system.add_node(
Demand(
id="farm",
requirement=TimeSeries(values=list(MONTHLY_NEED_MM3) * YEARS), # the 12 monthly needs, repeated 100 times
)
)
system.add_node(Sink(id="downstream"))
# 3. The three edges. An edge is named after the two nodes it joins and carries whatever its source lets out.
for source, target in [("river", "reservoir"), ("reservoir", "farm"), ("farm", "downstream")]:
system.add_edge(Edge(id=f"{source}_to_{target}", source=source, target=target))
# 4. Check that the network is complete (every node connected, every setting valid), then run all 1 200 months.
system.validate()
system.simulate(len(inflows))Running the system produces no printout. Instead, every node keeps a record of events: what it received, stored, released, lost, or failed to deliver, month by month. The farm records a DeficitRecorded event in every month it gets less than it needs, and each such event carries the missing volume:
Tizimni ishga tushirish hech narsa chop etmaydi. Buning oʻrniga har bir tugun hodisalar yozuvini yuritadi: oyma-oy nima qabul qilgani, saqlagani, chiqargani, yoʻqotgani yoki yetkazib bera olmagani. Xoʻjalik kerakligidan kam olgan har bir oyda DeficitRecorded hodisasini yozadi, va har bir bunday hodisa yetishmagan hajmni oʻz ichida saqlaydi:
Запуск системы ничего не печатает. Вместо этого каждый узел ведёт журнал событий: что он получил, сохранил, сбросил, потерял или не смог подать, месяц за месяцем. Хозяйство записывает событие DeficitRecorded в каждом месяце, когда получает меньше, чем нужно, и каждое такое событие хранит недостающий объём:
Show the code
farm = system.nodes["farm"] # look up a node by its id
# Add up the missing volume over all deficit events, then divide by the number of years
not_delivered = sum(event.deficit for event in farm.events_of_type(DeficitRecorded))
print(f"Water not delivered: {not_delivered / YEARS:.2f} Mm³ per year, on average")Water not delivered: 1.11 Mm³ per year, on average
From here on, simulate_plan(inflows, plan) does exactly these steps (build, validate, simulate, read the events) and hands back the results in a tidy form: the storage month by month, the shortage month by month, and the three scores introduced next.
Bundan keyin simulate_plan(inflows, plan) aynan shu qadamlarni bajaradi (qurish, tekshirish, hisoblash, hodisalarni oʻqish) va natijalarni tartibli koʻrinishda qaytaradi: oyma-oy suv hajmi, oyma-oy taqchillik va keyingi boʻlimda kiritiladigan uchta koʻrsatkich.
Далее simulate_plan(inflows, plan) выполняет ровно эти шаги (собрать, проверить, рассчитать, прочитать события) и возвращает результат в удобном виде: объём в водохранилище по месяцам, дефицит по месяцам и три показателя, которые вводятся ниже.
5 First run: deliver in full until empty
Show the code
no_rationing = simulate_plan(inflows, NO_RATIONING) # NO_RATIONING is Plan(trigger=0, ration=1)
first_year = drought_window_start(no_rationing) # a 20-year window that contains the worst drought
# One row per plan; the right panel shows the 20 years starting in first_year, shaded in the left panel
_ = plot_runs({"Deliver in full until empty": no_rationing}, first_year=first_year)The left panel shows all 100 years. For each year it draws one bar: the worst month of that year, as the share of that month’s need the farm did not get. A bar of 72 % means that in its worst month the farm received only 28 % of what it asked for; most years have no bar at all. The right panel zooms into the shaded 20 years around the worst drought: the reservoir fills every winter (the rising lines) and is drawn down every summer, and in the driest years it reaches zero.
We score a plan with three numbers:
- Average shortage: the water the farm needed but did not get, in Mm³ per year.
- Worst month: the share of its need the farm did not get in the single worst month of the hundred years.
- Years short: the number of years with at least one short month.
Chap panelda barcha 100 yil koʻrsatilgan. Har bir yil uchun bitta ustun: oʻsha yilning eng yomon oyi, yaʼni xoʻjalik oʻsha oy talabining qancha ulushini olmagani. 72 % lik ustun — eng yomon oyda xoʻjalik soʻraganining faqat 28 % ini olgan degani; koʻp yillarda umuman ustun yoʻq. Oʻng panel eng qattiq qurgʻoqchilik atrofidagi belgilangan 20 yilni yaqindan koʻrsatadi: suv ombori har qish toʻladi (koʻtarilayotgan chiziqlar) va har yoz boʻshatiladi, eng qurgʻoqchil yillarda esa nolga yetadi.
Rejani uchta raqam bilan baholaymiz:
- Oʻrtacha taqchillik: xoʻjalikka kerak boʻlgan, lekin olmagan suv, yiliga mln m³.
- Eng yomon oy: yuz yil ichidagi eng yomon oyda xoʻjalik ehtiyojining olmagan ulushi.
- Taqchil yillar: kamida bitta taqchil oyi boʻlgan yillar soni.
Левая панель показывает все 100 лет. Для каждого года один столбик: худший месяц этого года, то есть доля потребности этого месяца, которую хозяйство не получило. Столбик 72 % означает, что в худшем месяце хозяйство получило лишь 28 % того, что просило; в большинстве лет столбика нет вовсе. Правая панель показывает крупно выделенные 20 лет вокруг самой сильной засухи: водохранилище наполняется каждую зиму (восходящие линии) и срабатывается каждое лето, а в самые сухие годы опустошается до нуля.
План оцениваем тремя числами:
- Средний дефицит: вода, которая была нужна хозяйству, но не подана, в млн м³ в год.
- Худший месяц: доля потребности, не поданная хозяйству в самом худшем месяце за сто лет.
- Дефицитные годы: число лет хотя бы с одним дефицитным месяцем.
Without rationing the farm is short in 10 of 100 years. On average 1.1 Mm³ per year is not delivered, which is little. But when it goes wrong, it goes badly wrong: in the worst month the farm gets 28 % of what it needs (72 % short).
Cheklashsiz xoʻjalik 100 yildan 10 tasida taqchillik koʻradi. Oʻrtacha yiliga 1.1 mln m³ yetkazib berilmaydi, bu kam. Lekin ishkal chiqqanda, qattiq chiqadi: eng yomon oyda xoʻjalik keragining 28 % ini oladi (72 % taqchillik).
Без лимитирования хозяйство испытывает дефицит в 10 из 100 лет. В среднем не подаётся 1.1 млн м³ в год — немного. Но когда дело идёт плохо, оно идёт очень плохо: в худшем месяце хозяйство получает 28 % потребности (дефицит 72 %).
Where does the water go when the reservoir is full? It spills. A Storage block passes on whatever does not fit and records it as a WaterSpilled event; the spill flows to the farm like a release, and what the farm does not need leaves at the outlet. The events of the run above say how much:
Suv ombori toʻla boʻlganda suv qayerga ketadi? U toshib ketadi. Storage bloki sigʻmagan suvni oʻtkazib yuboradi va buni WaterSpilled hodisasi sifatida yozadi; toshgan suv xoʻjalikka xuddi chiqarilgan suv kabi oqib boradi, xoʻjalikka kerak boʻlmagani esa quyi oqimga chiqib ketadi. Yuqoridagi hisobning hodisalari buning qanchaligini aytadi:
Куда уходит вода, когда водохранилище полно? Она сбрасывается. Блок Storage пропускает всё, что не помещается, и записывает это как событие WaterSpilled; сброс течёт к хозяйству так же, как попуск, а то, что хозяйству не нужно, уходит в нижний бьеф. События расчёта выше говорят, сколько это:
Show the code
spills = system.nodes["reservoir"].events_of_type(WaterSpilled) # the four-block run from the section above
spilled = sum(event.amount for event in spills) / YEARS # Mm³ per year
spill_months = len({event.t for event in spills})
winter_share = sum(event.amount for event in spills if event.t % 12 < 6) / (spilled * YEARS)
print(f"Spilled: {spilled:.1f} Mm³ per year, in {spill_months} of {12 * YEARS} months; {winter_share:.0%} of it in winter")Spilled: 14.6 Mm³ per year, in 387 of 1200 months; 56% of it in winter
So 23% of the river never finds room in the reservoir: 56% of it in winter, when the farm needs almost nothing, the rest in wet summer months, when the release already covers the farm. Either way the water runs through to the outlet unused. Two things follow. In reality that water is not wasted: it is the river downstream, for nature and for the next user, which this book simply does not count; the game River flow for nature is about exactly that. And the spill is the reservoir’s size at work: a larger reservoir would turn part of it into summer water, which is the classic question of how big to build.
Demak, daryo suvining 23% i suv omboriga sigʻmaydi: uning 56% i qishda, xoʻjalikka deyarli suv kerak boʻlmaganda, qolgani esa sersuv yoz oylarida, chiqarilgan suv xoʻjalik ehtiyojini allaqachon qoplaganda. Har ikki holda ham suv foydalanilmay quyi oqimga oqib oʻtadi. Bundan ikki xulosa kelib chiqadi. Aslida bu suv behuda ketmaydi: bu quyi oqimdagi daryo, tabiat va keyingi isteʼmolchi uchun suv — bu daftar uni shunchaki hisobga olmaydi; Tabiat uchun daryo oqimi oʻyini aynan shu haqda. Toshib ketish esa suv ombori hajmining natijasi: kattaroq ombor uning bir qismini yozgi suvga aylantirgan boʻlardi — bu qanday kattalikda qurish kerak degan klassik savol.
Итак, 23% стока реки не находит места в водохранилище: 56% этого объёма сбрасывается зимой, когда хозяйству почти ничего не нужно, остальное в многоводные летние месяцы, когда попуск уже покрывает потребность хозяйства. В обоих случаях вода уходит в нижний бьеф без пользы. Из этого следуют две вещи. В действительности эта вода не пропадает: это река ниже по течению, для природы и для следующего водопользователя, просто этот учебник её не считает; игра Вода для реки как раз об этом. А сброс — это следствие размера водохранилища: более крупное превратило бы часть его в летнюю воду; это классический вопрос о том, какого размера строить.
6 Second run: ration
Below 16 Mm³, the summer release target is 80% of farm demand for “ration lightly” and 50% for “cut deep”. Winter demand is requested in full, but actual delivery is always limited by available water. In the comparison figure, yearly shortage bars are on the left and reservoir-storage panels on the right of each row.
При запасе ниже 16 млн м³ целевой летний выпуск — 80% потребности при мягком и 50% при сильном ограничении. Зимой запрашивается полная потребность, но фактическая подача ограничена наличием воды. В каждой строке рисунка слева годовые столбики дефицита, справа запас водохранилища.
Zaxira 16 mln m³ dan past boʻlsa, yozgi chiqarish maqsadi yengil cheklashda talabning 80%, keskin cheklashda 50%. Qishda toʻliq talab soʻraladi, lekin yetkazish mavjud suv bilan cheklanadi. Har qatorda chapda yillik taqchillik, oʻngda ombor zaxirasi.
Two rationing plans. Both start rationing when the reservoir holds less than 16 Mm³. One cuts deliveries a little (to 80 %), the other cuts deep (to 50 %).
Ikkita cheklash rejasi. Ikkalasi ham omborda 16 mln m³ dan kam suv qolganda cheklashni boshlaydi. Biri suv berishni ozgina kamaytiradi (80 % gacha), ikkinchisi keskin kesadi (50 % gacha).
Два плана лимитирования. Оба начинают лимитировать, когда в водохранилище меньше 16 млн м³. Один урезает подачу немного (до 80 %), другой — сильно (до 50 %).
Show the code
# A Plan holds the two knobs. Below 16 Mm³ in store, deliver 80 % (lightly) or 50 % (deep) of the summer need.
ration_lightly = simulate_plan(inflows, Plan(trigger=16, ration=0.8))
cut_deep = simulate_plan(inflows, Plan(trigger=16, ration=0.5))
# A dictionary of name -> run; the names become the panel titles
runs = {
"Deliver in full until empty": no_rationing,
"Ration lightly (below 16 Mm³, deliver 80 %)": ration_lightly,
"Cut deep (below 16 Mm³, deliver 50 %)": cut_deep,
}
_ = plot_runs(runs, first_year=first_year)Look at the bars on the left. “Ration lightly” turns the few tall bars into many short ones of 20 %, but the tallest bar in the drought stays. Under “Cut deep” no bar is taller than 50 %: the ration itself is the worst that happens, because the reservoir never runs dry. The price shows in the other two scores.
Chapdagi ustunlarga qarang. “Ozgina cheklash” bir nechta baland ustunni 20 % lik koʻplab past ustunlarga aylantiradi, lekin qurgʻoqchilikdagi eng baland ustun qoladi. “Keskin kesish” ostida hech bir ustun 50 % dan baland emas: eng yomon holat — cheklashning oʻzi, chunki suv ombori hech qachon butunlay boʻshamaydi. Buning bahosi qolgan ikki koʻrsatkichda koʻrinadi.
Посмотрите на столбики слева. «Лёгкое лимитирование» превращает несколько высоких столбиков во множество низких по 20 %, но самый высокий столбик в засуху остаётся. При «сильном урезании» ни один столбик не выше 50 %: худшее, что случается, — само урезание, потому что водохранилище никогда не опустошается полностью. Цена видна в двух других оценках.
| Plan | Average shortage (Mm³/yr) | Worst month (% short) | Years short (of 100) |
|---|---|---|---|
| Deliver in full until empty | 1.1 | 72 | 10 |
| Ration lightly (below 16 Mm³, deliver 80 %) | 1.2 | 72 | 14 |
| Cut deep (below 16 Mm³, deliver 50 %) | 1.4 | 50 | 12 |
- How to read the reservoir panels on the right of each row. The grey line is the trigger, 16 Mm³. Every summer month in which the blue line is below it is a rationed month. Without rationing the line hits zero in the drought; under “Cut deep” it never falls below 2.4 Mm³, because halving the deliveries keeps water back.
- How to read the bars. Under “Ration lightly” most bars are 20 % tall: that is the ration itself, in every year the trigger is reached. There are more of them (14 years instead of 10), and the tallest one, 72 %, is still there. Under “Cut deep” the bars are 50 % at most and there are 12 of them.
- Rationing lightly does not help here. The worst month is still 72 % short: in the worst drought the reservoir runs dry anyway. The farm is simply short more often (14 years instead of 10).
- Cutting deep works, at a price. The worst month improves from 72 % to 50 % short. But 1.4 Mm³ per year is not delivered instead of 1.1, and the farm is short in 12 years instead of 10.
So there is no free lunch: a milder worst month costs water. How much, exactly?
- Suv hajmi panelini qanday oʻqish. Kulrang chiziq — chegara, 16 mln m³. Koʻk chiziq undan pastda boʻlgan har bir yoz oyi — cheklangan oy. Cheklashsiz chiziq qurgʻoqchilikda nolga tushadi; “Keskin kesish” ostida u hech qachon 2.4 mln m³ dan pastga tushmaydi, chunki suv berishni yarmiga kamaytirish suvni saqlab qoladi.
- Ustunlarni qanday oʻqish. “Ozgina cheklash” ostida koʻp ustunlar 20 % balandlikda: bu cheklashning oʻzi, chegaraga yetilgan har bir yilda. Ular koʻproq (10 oʻrniga 14 yil), eng balandi esa, 72 %, hali ham turibdi. “Keskin kesish” ostida ustunlar koʻpi bilan 50 % va ular 12 ta.
- Ozgina cheklash bu yerda yordam bermaydi. Eng yomon oy hali ham 72 % taqchil: eng qattiq qurgʻoqchilikda ombor baribir quriydi. Xoʻjalik shunchaki koʻproq taqchillik koʻradi (10 oʻrniga 14 yil).
- Keskin kesish ishlaydi, lekin bahosi bor. Eng yomon oy 72 % dan 50 % taqchillikka yaxshilanadi. Lekin 1.1 oʻrniga yiliga 1.4 mln m³ yetkazilmaydi, va xoʻjalik 10 oʻrniga 12 yilda taqchillik koʻradi.
Demak, tekin tushlik yoʻq: yumshoqroq eng yomon oy suvga tushadi. Aniq qanchaga?
- Как читать панель с запасом. Серая линия — порог, 16 млн м³. Каждый летний месяц, в который синяя линия ниже порога, — месяц с лимитированием. Без лимитирования линия в засуху доходит до нуля; при «сильном урезании» она никогда не опускается ниже 2.4 млн м³, потому что половинная подача сберегает воду.
- Как читать столбики. При «лёгком лимитировании» большинство столбиков высотой 20 %: это само урезание, в каждый год, когда достигнут порог. Их больше (14 лет вместо 10), а самый высокий, 72 %, никуда не делся. При «сильном урезании» столбики не выше 50 %, и их 12.
- Лёгкое лимитирование здесь не помогает. Худший месяц по-прежнему с дефицитом 72 %: в самую сильную засуху водохранилище всё равно пустеет. Хозяйство просто чаще испытывает дефицит (14 лет вместо 10).
- Сильное урезание работает, но имеет цену. Худший месяц улучшается с 72 % до 50 % дефицита. Но не подаётся 1.4 млн м³ в год вместо 1.1, и хозяйство испытывает дефицит в 12 годах вместо 10.
Бесплатных обедов нет: более мягкий худший месяц стоит воды. Сколько именно?
6.1 Try your own plan
Change trigger (0–40 Mm³) and ration (0–1) in the open cell below. Run it with ▶ or Shift+Enter. Compare your plan with full delivery in the figure and score table. Reset with trigger=0, ration=1. In this exercise compare the two optimization goals; years short is additional context.
Измените trigger (0–40 млн м³) и ration (0–1) в открытой ячейке ниже и нажмите ▶ или Shift+Enter. Сравните свой план с полной подачей на рисунке и в таблице. Сброс: trigger=0, ration=1. Сравнивайте две цели оптимизации; число дефицитных лет — дополнительная информация.
Ochiq katakdagi trigger (0–40 mln m³) va ration (0–1) ni oʻzgartirib, ▶ yoki Shift+Enter ni bosing. Rejangizni rasm va jadvalda toʻliq yetkazish bilan solishtiring. Tiklash: trigger=0, ration=1. Ikki optimallashtirish maqsadini solishtiring; taqchil yillar soni qoʻshimcha maʼlumot.
Show the code
my_plan = Plan(trigger=22, ration=0.55) # change these two numbers and run the cell again
mine = simulate_plan(inflows, my_plan)
_ = plot_runs({"Deliver in full until empty": no_rationing, "My plan": mine}, first_year=first_year)
print(mine.scores) # the three scores of your plan| Plan | Average shortage (Mm³/year) | Worst month (%) | Years short (of 100) |
|---|---|---|---|
| Deliver in full | 1.1 | 72.0 | 10 |
| My plan | 2.0 | 45.0 | 24 |
The trigger can be anything from 0 to 40 Mm³ (the capacity), the ration anything from 0 to 1. A trigger of 40 means “always ration in summer”.
Chegara 0 dan 40 mln m³ gacha (sigʻim), ulush 0 dan 1 gacha har qanday qiymat boʻlishi mumkin. Chegara 40 — “yozda har doim chekla” degani.
Порог может быть любым от 0 до 40 млн м³ (ёмкость), доля — любой от 0 до 1. Порог 40 означает «летом лимитировать всегда».
What to look for: the two rows share their axes, so compare them directly. Your plan’s three scores are printed under the figure. A plan is better only if it improves one score without worsening the other two; most changes trade one for another. Two useful extremes: trigger 0 is “never ration” (the first row again), trigger 40 is “ration in every summer month”.
Nimaga qarash kerak: ikkala qator bir xil oʻqlarga ega, shuning uchun ularni toʻgʻridan-toʻgʻri solishtiring. Rejangizning uchta koʻrsatkichi rasm ostida chop etiladi. Reja faqat bitta koʻrsatkichni qolgan ikkitasini yomonlashtirmasdan yaxshilasagina yaxshiroq; koʻp oʻzgarishlar birini ikkinchisiga almashtiradi. Ikkita foydali chegara: chegara 0 — “hech qachon cheklamaslik” (yana birinchi qator), chegara 40 — “har bir yoz oyida cheklash”.
На что смотреть: обе строки имеют одни и те же оси, так что сравнивайте их напрямую. Три оценки вашего плана печатаются под рисунком. План лучше только тогда, когда он улучшает одну оценку, не ухудшая две другие; большинство изменений меняют одно на другое. Две полезные крайности: порог 0 — «никогда не лимитировать» (снова первая строка), порог 40 — «лимитировать в каждый летний месяц».
7 Every plan at once
| Symbol | Maʼnosi | Unit |
|---|---|---|
| t; N | oy raqami; yillar | – |
| m(t); S | yildagi oy; yoz oylari | – |
| sₜ; qₜ; K | boshlangʻich zaxira; oqim; sigʻim | Mm³ |
| ℓₜ; ŝₜ | yoʻqotish; oqim, toshish va yoʻqotishdan keyingi zaxira | Mm³ |
| τ; ρ; ρₜ | chegara; ulush sozlamasi; shu oy ulushi | Mm³; 0–1; 0–1 |
| dₘ; rₜ; δₜ | talab; chiqarish; taqchillik | Mm³ |
| f₁; f₂ | oʻrtacha yillik taqchillik; eng yomon oylik taqchillik | Mm³/year; % |
| Symbol | Значение | Unit |
|---|---|---|
| t; N | номер месяца; годы | – |
| m(t); S | месяц года; летние месяцы | – |
| sₜ; qₜ; K | начальный запас; приток; ёмкость | Mm³ |
| ℓₜ; ŝₜ | потери; запас после притока, перелива и потерь | Mm³ |
| τ; ρ; ρₜ | порог; доля подачи; доля в этом месяце | Mm³; 0–1; 0–1 |
| dₘ; rₜ; δₜ | потребность; выпуск; дефицит | Mm³ |
| f₁; f₂ | средний годовой дефицит; худший месячный дефицит | Mm³/year; % |
| Symbol | Meaning | Unit |
|---|---|---|
| t; N | month index; years | – |
| m(t); S | month within year; summer months | – |
| sₜ; qₜ; K | starting storage; inflow; capacity | Mm³ |
| ℓₜ; ŝₜ | loss; storage after inflow, spill and loss | Mm³ |
| τ; ρ; ρₜ | trigger; ration setting; ration applied this month | Mm³; 0–1; 0–1 |
| dₘ; rₜ; δₜ | demand; release; shortage | Mm³ |
| f₁; f₂ | average annual shortage; worst monthly shortage | Mm³/year; % |
The problem in formulas. Months are numbered \(t = 1, \dots, 12N\) over \(N = 100\) years; \(m(t)\) is the month within its year and \(S\) the six summer months. At the start of month \(t\) the reservoir holds \(s_t\) and receives the inflow \(q_t\); what exceeds the capacity \(K = 40\) Mm³ spills, and the loss \(\ell_t\) (zero until the evaporation section) is taken. The water the rule sees is \(\hat s_t\). A plan is the pair \(\theta = (\tau, \rho)\), trigger and ration, and the farm needs \(d_m\) (0.83 Mm³ in a winter month, 7.5 in a summer month). The release and the water balance are
\[ r_t(\theta) = \min\{\hat s_t,\; \rho_t\, d_{m(t)}\}, \qquad \rho_t = \begin{cases} \rho & \text{if } m(t) \in S \text{ and } \hat s_t < \tau \\ 1 & \text{otherwise,} \end{cases} \]
\[ \hat s_t = \min\{K,\; s_t + q_t\} - \ell_t, \qquad s_{t+1} = \hat s_t - r_t, \]
and the shortage of month \(t\) is \(\delta_t = d_{m(t)} - r_t\). The two scores, in Mm³ per year and in percent, are
\[ f_1(\theta) = \frac{1}{N}\sum_{t=1}^{12N} \delta_t(\theta), \qquad f_2(\theta) = 100 \max_t \frac{\delta_t(\theta)}{d_{m(t)}}, \]
and the problem is to make both small at once:
\[ \min_{\theta \in [0,K] \times [0,1]} \big(f_1(\theta),\, f_2(\theta)\big), \]
Two numbers cannot both be smallest at once, so “solve” means: find the plans no other plan beats. A plan \(\theta\) is unbeaten if there is no \(\theta'\) with \(f_1(\theta') \le f_1(\theta)\) and \(f_2(\theta') \le f_2(\theta)\), one of them strictly; the set of unbeaten plans is the Pareto front. The grid evaluates \(f\) at 21 × 21 values of \(\theta\); the optimizer searches the whole rectangle.
Masala formulalarda. Oylar \(N = 100\) yil boʻylab \(t = 1, \dots, 12N\) deb raqamlanadi; \(m(t)\) — yil ichidagi oy, \(S\) — oltita yoz oyi. \(t\) oy boshida omborda \(s_t\) suv bor va unga \(q_t\) oqim keladi; \(K = 40\) mln m³ sigʻimdan ortiqchasi toshib ketadi, \(\ell_t\) yoʻqotish (bugʻlanish boʻlimigacha nol) ayiriladi. Qoida koʻradigan suv — \(\hat s_t\). Reja — \(\theta = (\tau, \rho)\) juftligi, chegara va ulush; xoʻjalik talabi \(d_m\) (qish oyida 0,83 mln m³, yoz oyida 7,5). Chiqarish va suv balansi:
\[ r_t(\theta) = \min\{\hat s_t,\; \rho_t\, d_{m(t)}\}, \qquad \rho_t = \begin{cases} \rho & \text{if } m(t) \in S \text{ and } \hat s_t < \tau \\ 1 & \text{otherwise,} \end{cases} \]
\[ \hat s_t = \min\{K,\; s_t + q_t\} - \ell_t, \qquad s_{t+1} = \hat s_t - r_t, \]
\(t\) oyning taqchilligi — \(\delta_t = d_{m(t)} - r_t\). Ikki koʻrsatkich, yiliga mln m³ da va foizda:
\[ f_1(\theta) = \frac{1}{N}\sum_{t=1}^{12N} \delta_t(\theta), \qquad f_2(\theta) = 100 \max_t \frac{\delta_t(\theta)}{d_{m(t)}}, \]
masala esa ikkalasini birdaniga kichik qilish:
\[ \min_{\theta \in [0,K] \times [0,1]} \big(f_1(\theta),\, f_2(\theta)\big), \]
Ikki son bir vaqtda eng kichik boʻla olmaydi, shuning uchun “yechish” degani — boshqa hech bir reja ustun kela olmaydigan rejalarni topish. \(\theta\) reja yengilmagan, agar \(f_1(\theta') \le f_1(\theta)\) va \(f_2(\theta') \le f_2(\theta)\) (biri qatʼiy) boʻlgan \(\theta'\) mavjud boʻlmasa; yengilmagan rejalar toʻplami — Pareto fronti. Toʻr \(f\) ni \(\theta\) ning 21 × 21 qiymatida hisoblaydi; optimallashtirgich butun toʻrtburchakni qidiradi.
Задача в формулах. Месяцы нумеруются \(t = 1, \dots, 12N\) за \(N = 100\) лет; \(m(t)\) — месяц внутри своего года, \(S\) — шесть летних месяцев. В начале месяца \(t\) в водохранилище \(s_t\) воды, и в него приходит приток \(q_t\); всё сверх ёмкости \(K = 40\) млн м³ сбрасывается, затем вычитается потеря \(\ell_t\) (нуль до раздела об испарении). Вода, которую видит правило, — \(\hat s_t\). План — пара \(\theta = (\tau, \rho)\), порог и доля; потребность хозяйства \(d_m\) (0,83 млн м³ в зимний месяц, 7,5 в летний). Сброс и водный баланс:
\[ r_t(\theta) = \min\{\hat s_t,\; \rho_t\, d_{m(t)}\}, \qquad \rho_t = \begin{cases} \rho & \text{if } m(t) \in S \text{ and } \hat s_t < \tau \\ 1 & \text{otherwise,} \end{cases} \]
\[ \hat s_t = \min\{K,\; s_t + q_t\} - \ell_t, \qquad s_{t+1} = \hat s_t - r_t, \]
а дефицит месяца \(t\) — \(\delta_t = d_{m(t)} - r_t\). Два показателя, в млн м³ в год и в процентах:
\[ f_1(\theta) = \frac{1}{N}\sum_{t=1}^{12N} \delta_t(\theta), \qquad f_2(\theta) = 100 \max_t \frac{\delta_t(\theta)}{d_{m(t)}}, \]
и задача — сделать оба малыми одновременно:
\[ \min_{\theta \in [0,K] \times [0,1]} \big(f_1(\theta),\, f_2(\theta)\big), \]
Два числа не могут быть наименьшими одновременно, поэтому «решить» значит: найти планы, которые не превосходит никакой другой. План \(\theta\) непревзойдён, если нет \(\theta'\) с \(f_1(\theta') \le f_1(\theta)\) и \(f_2(\theta') \le f_2(\theta)\), причём хотя бы одно строго; множество непревзойдённых планов — фронт Парето. Перебор вычисляет \(f\) в 21 × 21 значениях \(\theta\); оптимизатор ищет по всему прямоугольнику.
Two knobs are few enough to simply try everything: 21 triggers × 21 rations = 441 plans, 441 TaqSim runs.
Ikki dastak uchun hammasini shunchaki sinab koʻrish mumkin: 21 chegara × 21 ulush = 441 reja, 441 ta TaqSim hisobi.
Два рычага — достаточно мало, чтобы просто перебрать всё: 21 порог × 21 доля = 441 план, 441 расчёт в TaqSim.
Show the code
plans = plan_grid() # 441 Plans: triggers 0, 2, 4, ... 40 Mm³ combined with rations 0, 0.05, 0.10, ... 1
table = score_plans(inflows, plans) # runs each plan through TaqSim; one row per plan with its three scores
table.sample(5, random_state=1).sort_index().style.hide(axis="index").format(precision=2) # five rows, as a taste| trigger | ration | mean_shortage | worst_month | years_short | unbeaten |
|---|---|---|---|---|---|
| 16.00 | 0.60 | 1.33 | 63.65 | 13 | False |
| 22.00 | 0.65 | 1.87 | 63.65 | 25 | False |
| 24.00 | 0.90 | 1.39 | 72.05 | 32 | False |
| 32.00 | 0.50 | 4.35 | 50.00 | 47 | False |
| 40.00 | 0.80 | 5.44 | 63.65 | 73 | False |
Each row is one plan: its two knobs, its three scores, and an unbeaten flag explained below. All 441 runs take a few seconds.
Har bir qator — bitta reja: ikki dastagi, uchta koʻrsatkichi va quyida tushuntiriladigan unbeaten belgisi. Barcha 441 hisob bir necha soniya oladi.
Каждая строка — один план: два рычага, три показателя и флаг unbeaten, объяснённый ниже. Все 441 расчёт занимают несколько секунд.
The next figure shows all 441 plans on three maps, one per score. Each map is a chessboard: trigger across, ration up, one square per plan, darker is worse. Left map: the further right and down (ration earlier, deliver less), the more water is held back and never delivered. Middle map: the lightest squares, the mildest worst months, sit at the top right, a high trigger with a ration around 60 to 75 %; the dark band at the bottom is “deliver almost nothing once rationing starts”. Right map: the number of short years grows with the trigger, because rationing is switched on in more years. No square is light on all three maps. That is the trade-off.
Keyingi rasm barcha 441 rejani uchta xaritada koʻrsatadi, har bir koʻrsatkich uchun bittadan. Har bir xarita — shaxmat taxtasi: chegara — koʻndalangiga, cheklash — yuqoriga, har bir katak bitta reja, qoramtiri yomonroq. Chap xarita: qanchalik oʻngga va pastga (erta cheklash, kam berish), shunchalik koʻp suv ushlab qolinadi va hech qachon yetkazilmaydi. Oʻrta xarita: eng och kataklar, eng yumshoq eng yomon oylar, yuqori oʻngda — baland chegara va 60 dan 75 % gacha cheklash; pastdagi qora yoʻl — “cheklash boshlangach deyarli hech narsa bermaslik”. Oʻng xarita: taqchil yillar soni chegara bilan oʻsadi, chunki cheklash koʻproq yilda yoqiladi. Hech bir katak uchala xaritada ham och emas. Murosa shu.
Следующий рисунок показывает все 441 план на трёх картах, по одной на оценку. Каждая карта — шахматная доска: порог по горизонтали, доля подачи по вертикали, одна клетка — один план, темнее — хуже. Левая карта: чем правее и ниже (лимитировать раньше, подавать меньше), тем больше воды удерживается и так и не подаётся. Средняя карта: самые светлые клетки, самые мягкие худшие месяцы, находятся справа вверху — высокий порог и подача около 60–75 %; тёмная полоса внизу — «почти ничего не подавать, как только началось лимитирование». Правая карта: число дефицитных лет растёт с порогом, потому что лимитирование включается в большем числе лет. Ни одна клетка не светлая на всех трёх картах. В этом и есть компромисс.
Now put two of the scores against each other. Every plan becomes one dot.
Endi ikkita koʻrsatkichni bir-biriga qarshi qoʻyamiz. Har bir reja bitta nuqtaga aylanadi.
Теперь сопоставим два показателя. Каждый план становится одной точкой.
Show the code
# Every row of the table becomes a dot; `named` runs are circled and labelled
_ = plot_tradeoff(table, named={"deliver in full": no_rationing, "ration lightly": ration_lightly, "cut deep": cut_deep})The black dots are the unbeaten plans. For each of them, the only way to get a milder worst month is to leave more water undelivered. They are the only plans worth discussing; which one to pick is a judgement, not a calculation.
Qora nuqtalar — ustun kelib boʻlmaydigan rejalar. Ularning har biri uchun eng yomon oyni yumshatishning yagona yoʻli — koʻproq suvni yetkazmay qoldirish. Muhokamaga arziydigan rejalar faqat shular; qaysi birini tanlash — hisob emas, mulohaza.
Чёрные точки — непревзойдённые планы. Для каждого из них единственный способ смягчить худший месяц — оставить больше воды неподанной. Только эти планы стоит обсуждать; какой из них выбрать — вопрос суждения, а не расчёта.
How to read the chart. Each dot is a plan. Left means less average shortage; down means a smaller worst-month shortage. Black dots are unbeaten outcomes, the Pareto front; the staircase joins them. Grey dots have another plan at least as good on both goals and strictly better on one. Circled points identify the three example plans: “cut deep” is on the front; “ration lightly” is dominated. The label “plans lie further right” identifies outcomes beyond the cropped x-axis. These descriptions refer to the default settings.
Grafikni oʻqish. Har nuqta — reja. Chaproqda oʻrtacha taqchillik, pastroqda eng yomon oydagi taqchillik kamroq. Qora nuqtalar — zinapoya bilan ulangan Pareto fronti. Kulrang nuqtalarda ikkala maqsadda kamida teng, bittasida yaxshiroq boshqa reja bor. Doiralar uch misolni koʻrsatadi: keskin cheklash frontda, yengil cheklash yengilgan. Oʻngdagi rejalar yozuvi kesilgan oʻq qismini bildiradi. Bu izohlar boshlangʻich sozlamalar uchun.
Как читать график. Точка — план. Левее — меньше средний дефицит, ниже — меньше дефицит худшего месяца. Чёрные точки — фронт Парето, соединённый лестницей. Для серых точек есть план не хуже по обеим целям и строго лучше по одной. Кружки — три примера: сильное ограничение на фронте, мягкое — доминируется. Надпись о планах правее обозначает обрезанную часть оси. Эти описания относятся к исходным настройкам.
The table below lists the staircase from its top-left end to its bottom-right end: from no rationing (1.11 Mm³ per year undelivered, worst month 72 % short, 10 years short) to the deepest plan still worth having (trigger 32 Mm³, ration 0.65: 3.73 Mm³ per year, 35 %, 48 years short). Every step down the table buys a milder worst month with more water left undelivered and more years with some shortage.
Quyidagi jadval zinapoyani yuqori chap uchidan pastki oʻng uchigacha sanab oʻtadi: cheklashsizdan (yiliga 1.11 mln m³ yetkazilmagan, eng yomon oy 72 % taqchil, 10 taqchil yil) hali ham arziydigan eng chuqur rejagacha (chegara 32 mln m³, cheklash 0.65: yiliga 3.73 mln m³, 35 %, 48 taqchil yil). Jadvalda har bir qadam pastga yumshoqroq eng yomon oyni koʻproq yetkazilmagan suv va koʻproq taqchil yil evaziga sotib oladi.
Таблица ниже перечисляет лестницу от её левого верхнего конца до правого нижнего: от отсутствия лимитирования (1.11 млн м³ в год не подано, худший месяц 72 % дефицита, 10 дефицитных лет) до самого глубокого плана, который ещё имеет смысл (порог 32 млн м³, подача 0.65: 3.73 млн м³ в год, 35 %, 48 дефицитных лет). Каждый шаг вниз по таблице покупает более мягкий худший месяц ценой большего объёма неподанной воды и большего числа лет с дефицитом.
| Trigger (Mm³) | Ration | Average shortage (Mm³/yr) | Worst month (% short) | Years short (of 100) |
|---|---|---|---|---|
| 0 | 1.00 | 1.11 | 72 | 10 |
| 6 | 0.30 | 1.12 | 70 | 9 |
| 6 | 0.35 | 1.14 | 65 | 10 |
| 12 | 0.60 | 1.18 | 64 | 10 |
| 8 | 0.40 | 1.22 | 60 | 9 |
| 10 | 0.45 | 1.24 | 57 | 9 |
| 14 | 0.45 | 1.40 | 55 | 11 |
| 16 | 0.50 | 1.43 | 50 | 12 |
| 20 | 0.55 | 1.65 | 45 | 17 |
| 24 | 0.60 | 2.16 | 40 | 26 |
| 30 | 0.65 | 3.18 | 38 | 42 |
| 32 | 0.65 | 3.73 | 35 | 48 |
8 Let the optimizer search
What this search shows. We (1) define the two
objectives, (2) search, and (3) compare the result with the 441-plan
grid. With two knobs the grid is cheaper. NSGA-II works with a
population of 40 plans over 40 generations, selecting by dominance and
diversity. The nominal search budget is 1,600 evaluations, plus this
helper’s re-runs for scoring returned plans. The comparison checks
agreement with the grid; it does not prove a global optimum. Step 7 has
124 knobs, where a full grid is impractical. A fixed seed controls
randomness; software versions also matter. The printed distinct count
refers to unbeaten score pairs, not necessarily distinct parameter
settings. Objective is imported from taqsim in
the course helper; objectives() is the helper shown
here.
Что показывает поиск. (1) Задаём две цели, (2)
запускаем поиск, (3) сравниваем результат с сеткой из 441 плана. При
двух настройках сетка дешевле. NSGA-II работает с популяцией 40 планов в
течение 40 поколений, учитывая доминирование и разнообразие. Бюджет
поиска — 1600 оценок плюс повторные расчёты возвращённых планов.
Сравнение не доказывает глобальный оптимум. На шаге 7 уже 124 настройки.
Seed управляет случайностью; версии программ тоже влияют. Число
различных результатов означает пары недоминируемых оценок, не настройки.
Objective импортирован из taqsim в модуле
курса; objectives() — функция этого модуля.
Qidiruv nimani koʻrsatadi. (1) Ikki maqsadni
belgilaymiz, (2) qidiramiz, (3) 441 rejali toʻr bilan solishtiramiz.
Ikki sozlamada toʻr arzonroq. NSGA-II 40 rejali populyatsiya va 40 avlod
bilan, ustunlik hamda xilma-xillikni hisobga olib ishlaydi. Qidiruv
budjeti 1600 baholash, qaytgan rejalarni baholash uchun qoʻshimcha
hisoblar ham bor. Taqqoslash global optimumni isbotlamaydi. 7-qadamda
124 sozlama bor. Seed tasodifiylikni boshqaradi; dastur versiyalari ham
muhim. Turli natijalar soni sozlamalar emas, yengilmagan baholar
juftlarini bildiradi. Objective kurs modulida
taqsim dan import qilingan; objectives() shu
modul funksiyasi.
Trying everything worked because there are only two knobs. A real reservoir rule has dozens (different triggers and rations for each month, several reservoirs, several users). With ten knobs and 21 values each, “everything” is more runs than anyone can wait for.
The optimizer (TaqSim uses a method called NSGA-II) searches instead. It starts with random plans, keeps the unbeaten ones, makes new plans by mixing and nudging them, and repeats. We give it the system and the two goals:
Hammasini sinab koʻrish faqat ikki dastak boʻlgani uchun ishladi. Haqiqiy suv ombori qoidasida oʻnlab dastak bor (har oy uchun alohida chegara va ulush, bir nechta ombor, bir nechta suv isteʼmolchisi). Oʻnta dastak va har birida 21 qiymat bilan “hammasi” — hech kim kutib oʻtira olmaydigan darajada koʻp hisob.
Buning oʻrniga optimallashtirgich qidiradi (TaqSim NSGA-II deb ataladigan usuldan foydalanadi). U tasodifiy rejalardan boshlaydi, ustun kelib boʻlmaydiganlarini saqlab qoladi, ularni aralashtirib va ozgina oʻzgartirib yangi rejalar yasaydi, va buni takrorlaydi. Biz unga tizimni va ikki maqsadni beramiz:
Перебор сработал, потому что рычагов всего два. У настоящего правила управления водохранилищем их десятки (свои порог и доля для каждого месяца, несколько водохранилищ, несколько водопользователей). При десяти рычагах по 21 значению «всё» — это больше расчётов, чем кто-либо готов ждать.
Вместо перебора ищет оптимизатор (TaqSim использует метод NSGA-II). Он начинает со случайных планов, оставляет непревзойдённые, создаёт новые планы, смешивая и слегка изменяя их, и повторяет. Мы даём ему систему и две цели:
def objectives() -> list[Objective]:
"""The two goals the optimizer weighs: little water not delivered, and a mild worst month."""
# Each goal is a function of the simulated system that returns one number to make as small as possible.
return [
Objective(
name="mean_shortage", # Mm³ per year the farm did not get
direction="minimize",
evaluate=lambda system: _scores(shortage_by_month(system)).mean_shortage,
),
Objective(
name="worst_month", # % of its need the farm did not get in the worst month
direction="minimize",
evaluate=lambda system: _scores(shortage_by_month(system)).worst_month,
),
]Each Objective has a name, a direction, and a function that turns a simulated system into one number.
Har bir Objective da nom, yoʻnalish va hisoblangan tizimni bitta raqamga aylantiradigan funksiya bor.
У каждой Objective есть имя, направление и функция, превращающая рассчитанную систему в одно число.
Show the code
found = optimise_plans(
inflows,
pop_size=40, # the optimizer works with 40 plans at a time
generations=40, # and improves them 40 times: 1 600 TaqSim runs in all
seed=42, # the starting plans are random; a fixed seed makes the result reproducible
)
print(f"The optimizer returned {len(found)} plans, {len(front_of(found))} of them different.")Saved coursebook run: 40 returned plans; 32 distinct unbeaten score pairs. Counts can differ with software versions; run the notebook to compute your result.
Inside, optimise_plans is one call to TaqSim, optimize(system, objectives, timesteps, pop_size, generations, seed), followed by a re-run of each returned plan to read off its scores. The result table has the same columns as the grid table above.
Ichkarida optimise_plans — TaqSimga bitta chaqiruv, optimize(system, objectives, timesteps, pop_size, generations, seed), soʻngra qaytarilgan har bir rejani koʻrsatkichlarini oʻqish uchun qayta hisoblash. Natija jadvali yuqoridagi toʻr jadvali bilan bir xil ustunlarga ega.
Внутри optimise_plans — один вызов TaqSim, optimize(system, objectives, timesteps, pop_size, generations, seed), после чего каждый возвращённый план пересчитывается, чтобы снять его показатели. Таблица результата имеет те же столбцы, что и таблица перебора выше.
Show the code
_ = plot_tradeoff(table, found=found)How to read it: the blue dots are the 40 plans the optimizer finished with, 32 of them different. Most sit on or just beside the black staircase, and several lie between its steps: the optimizer may choose any trigger and any ration, not only the grid’s steps of 2 Mm³ and 5 %. A few blue dots reach further right than the last black one: the optimizer also found plans with a worst month below 35 %, at a high price in water. It did this with 1 600 runs and without ever seeing the grid.
Buni qanday oʻqish: koʻk nuqtalar — optimallashtiruvchi yakunlagan 40 reja, ulardan 32 tasi har xil. Koʻpchiligi qora zinapoyaning ustida yoki yonida, bir nechtasi esa uning pogʻonalari orasida: optimallashtiruvchi istalgan chegara va istalgan cheklashni tanlashi mumkin, toʻrning 2 mln m³ va 5 % lik qadamlarini emas. Bir nechta koʻk nuqta oxirgi qora nuqtadan oʻngroqqa yetadi: optimallashtiruvchi eng yomon oyi 35 % dan past rejalarni ham topdi, suv boʻyicha qimmat narxda. U buni 1 600 ta hisob bilan va toʻrni umuman koʻrmasdan qildi.
Как это читать: синие точки — 40 планов, с которыми оптимизатор закончил, 32 из них разные. Большинство лежат на чёрной лестнице или рядом с ней, а несколько — между её ступенями: оптимизатор может выбрать любой порог и любую долю подачи, а не только шаги сетки в 2 млн м³ и 5 %. Несколько синих точек уходят правее последней чёрной: оптимизатор нашёл и планы с худшим месяцем ниже 35 %, по высокой цене в воде. Он сделал это за 1 600 прогонов, ни разу не увидев сетку.
9 What to notice
1. What you count decides the answer. If the only goal is to deliver as much water as possible, there is one best plan and it is no rationing (average shortage 1.11 Mm³ per year, the lowest of all 441 plans). The trade-off only appears once you also care about the worst month. Choosing the goals is the most important modelling decision, and it is not a technical one.
2. The curve bends. Going from 72 % to 50 % short in the worst month costs 0.3 Mm³ per year. Going on from 50 % to 35 % costs another 2.3 Mm³ per year. The first steps are cheap, the last ones very expensive. A plan near the bend is the natural place to start the discussion.
3. Being short more often is part of the price. Along the curve the number of short years rises from 10 to 48 of 100.
4. Most plans are simply bad. 336 of the 441 plans are beaten, including the reasonable-sounding “ration lightly”. Intuition is a poor guide here; the search is not.
1. Nimani hisoblashingiz javobni belgilaydi. Agar yagona maqsad iloji boricha koʻp suv yetkazish boʻlsa, bitta eng yaxshi reja bor va bu — cheklash yoʻq (oʻrtacha taqchillik yiliga 1.11 mln m³, barcha 441 reja ichida eng kami). Murosa faqat eng yomon oy ham sizga muhim boʻlgandagina paydo boʻladi. Maqsadlarni tanlash — modellashtirishdagi eng muhim qaror, va u texnik qaror emas.
2. Egri chiziq bukiladi. Eng yomon oyda 72 % dan 50 % taqchillikka oʻtish yiliga 0.3 mln m³ ga tushadi. 50 % dan 35 % gacha davom etish yana yiliga 2.3 mln m³ ga tushadi. Birinchi qadamlar arzon, oxirgilari juda qimmat. Muhokamani bukilish yaqinidagi rejadan boshlash tabiiy.
3. Koʻproq taqchillik koʻrish — bahoning bir qismi. Egri chiziq boʻylab taqchil yillar soni 100 dan 10 tadan 48 tagacha oshadi.
4. Koʻpchilik rejalar shunchaki yomon. 441 rejadan 336 tasi ustun kelingan, shu jumladan oqilona tuyulgan “ozgina cheklash” ham. Bu yerda sezgi yomon yoʻlboshchi; qidiruv esa yaxshi.
1. Ответ определяет то, что вы считаете. Если единственная цель — подать как можно больше воды, есть один лучший план, и это без лимитирования (средний дефицит 1.11 млн м³ в год, наименьший из всех 441 планов). Компромисс появляется только тогда, когда вам важен и худший месяц. Выбор целей — самое важное решение при моделировании, и оно не техническое.
2. Кривая изгибается. Переход от 72 % к 50 % дефицита в худшем месяце стоит 0.3 млн м³ в год. Дальнейший переход от 50 % к 35 % стоит ещё 2.3 млн м³ в год. Первые шаги дёшевы, последние очень дороги. План возле изгиба — естественная отправная точка для обсуждения.
3. Более частый дефицит — часть цены. Вдоль кривой число дефицитных лет растёт с 10 до 48 из 100.
4. Большинство планов просто плохи. 336 из 441 планов превзойдены, включая разумное на слух «лёгкое лимитирование». Интуиция здесь плохой советчик; поиск — хороший.
10 Explore the plans yourself
Drag the two knobs, or click a dot. Every number and line below was computed by TaqSim for one of the 441 grid plans; nothing is recalculated in the browser. The explorer itself is in English.
Ikki dastakni suring yoki nuqtani bosing. Quyidagi har bir raqam va chiziq TaqSim tomonidan 441 ta toʻr rejasidan biri uchun hisoblangan; brauzerda hech narsa qayta hisoblanmaydi. Tadqiqotchi vositasining oʻzi ingliz tilida.
Перемещайте два ползунка или щёлкните по точке. Каждое число и каждая линия ниже рассчитаны TaqSim для одного из 441 планов перебора; в браузере ничего не пересчитывается. Сам инструмент на английском языке.
11 Another river: the paper’s regime
Havza nomlari. Ravatxoʻja — sugʻoriladigan vohaga kirishdagi gidrouzel; Navoiy quyi oqimda. Kattaqoʻrgʻon suv ombori va sugʻorish tumani alohida qismlar. Havza modeli Kattaqoʻrgʻon va Oqdaryo omborlarini oʻz ichiga oladi. 2-qadamdagi 667 mln m³ Kattaqoʻrgʻonga tegishli; 3-qadamdagi 40 mln m³ esa Hashimoto va hammualliflar asosidagi oʻquv misoli, Kattaqoʻrgʻonning kalibrlangan modeli emas.
Step 4 · River for nature · Step 5 · Sixteen blocks · Step 6 · Basin model
Названия бассейна. Раватхожа — гидроузел у входа в орошаемый оазис; Навои расположен ниже по течению. Каттакурганское водохранилище и Каттакурганский ирригационный район — разные элементы. Модель бассейна включает Каттакурганское и Акдарьинское водохранилища. На шаге 2 ёмкость 667 млн м³ относится к Каттакурганскому водохранилищу; на шаге 3 используется учебный пример 40 млн м³ из Hashimoto и соавт., а не калиброванная модель Каттакургана.
Step 4 · River for nature · Step 5 · Sixteen blocks · Step 6 · Basin model
Basin names. Ravatkhoja is the headworks at the entrance to the irrigated oasis; Navoi is downstream. Kattakurgan reservoir and Kattakurgan irrigation district are different parts of the system. The basin model includes Kattakurgan and Akdarya reservoirs. The 667 Mm³ reservoir in step 2 represents Kattakurgan; step 3 instead uses a 40 Mm³ teaching reservoir based on Hashimoto et al., not a calibrated Kattakurgan model.
Step 4 · River for nature · Step 5 · Sixteen blocks · Step 6 · Basin model
The paper’s river peaks in winter and is lowest in summer, the opposite of ours. Same reservoir, same farm, same hundred random draws; only the seasons are swapped. Does the trade-off look the same?
Maqoladagi daryo qishda toʻlib oqadi va yozda eng kam suvli — biznikining aksi. Bir xil ombor, bir xil xoʻjalik, bir xil yuzta tasodifiy qiymat; faqat fasllar oʻrin almashgan. Murosa bir xil koʻrinadimi?
Река из статьи многоводна зимой и маловодна летом — противоположность нашей. То же водохранилище, то же хозяйство, те же сто случайных значений; только сезоны поменяны местами. Выглядит ли компромисс так же?
Show the code
paper_inflows = synthetic_inflows(regime=PAPER_REGIME) # the paper's seasons: winter 40 Mm³, summer 25 Mm³
paper_table = score_plans(paper_inflows, plan_grid()) # the same 441 plans on that river
_ = plot_regime_comparison(
{"Central Asian regime (this notebook)": (inflows, table), "Paper regime (winter peak)": (paper_inflows, paper_table)}
)- How to read the figure. Left: the average inflow of each month, blue for our river, orange for the paper’s; the black step is the farm’s need, 0.83 Mm³ a month in winter and 7.5 in summer. Our summer bars reach almost up to the need; the paper’s summer bars stay far below it, so its reservoir has to carry most of the summer water over from winter. Right: the unbeaten plans of both rivers, on the chart you know.
- Same shape. Both curves bend the same way: cheap at first, expensive at the end. The example works for either river.
- The paper’s river is the harder case. Without rationing it is 84 % short in its worst month (ours: 72 %), and holding the worst month to 50 % costs 0.9 Mm³ per year there against 0.3 here. With a summer peak the water arrives when the farm needs it, and the reservoir only has to cover the gap.
- What runs our reservoir dry is a poor winter (little to store) followed by a weak melt. That is the Central Asian pattern: the reservoir is filled from the low winter flow, as Kattakurgan is.
- Rasmni qanday oʻqish. Chapda: har bir oyning oʻrtacha oqimi, koʻk — bizning daryo, toʻq sariq — maqoladagi daryo; qora pogʻona — xoʻjalik talabi, qishda oyiga 0,83 mln m³, yozda 7,5. Bizning yoz ustunlari deyarli talabgacha yetadi; maqoladagi daryoning yoz ustunlari undan ancha pastda, shuning uchun uning ombori yoz suvining koʻp qismini qishdan olib oʻtishi kerak. Oʻngda: ikkala daryoning yengilmagan rejalari, sizga tanish diagrammada.
- Shakli bir xil. Ikkala egri chiziq ham bir xil bukiladi: avval arzon, oxirida qimmat. Misol ikkala daryo uchun ham ishlaydi.
- Maqoladagi daryo — qiyinroq holat. Cheklashsiz uning eng yomon oyi 84 % taqchil (bizniki: 72 %), va eng yomon oyni 50 % da ushlab turish u yerda yiliga 0.9 mln m³, bizda esa 0.3 mln m³ ga tushadi. Yozgi toshqin bilan suv xoʻjalikka kerak paytda keladi, va ombor faqat farqni qoplashi kerak.
- Bizning omborni quritadigan narsa — kam suvli qish (saqlashga kam suv) va undan keyingi kuchsiz erish. Bu Markaziy Osiyo manzarasi: ombor, Kattaqoʻrgʻon suv ombori kabi, kam suvli qishki oqimdan toʻldiriladi.
- Как читать рисунок. Слева: средний приток каждого месяца, синий — наша река, оранжевый — река из статьи; чёрная ступенька — потребность хозяйства, 0,83 млн м³ в месяц зимой и 7,5 летом. Наши летние столбики почти дотягивают до потребности; летние столбики реки из статьи остаются далеко ниже, так что её водохранилищу приходится переносить большую часть летней воды с зимы. Справа: непревзойдённые планы обеих рек на уже знакомом графике.
- Та же форма. Обе кривые изгибаются одинаково: сначала дёшево, в конце дорого. Пример работает для обеих рек.
- Река из статьи — более трудный случай. Без лимитирования её худший месяц имеет дефицит 84 % (у нашей: 72 %), а удержание худшего месяца на уровне 50 % стоит там 0.9 млн м³ в год против 0.3 у нас. При летнем пике вода приходит тогда, когда она нужна хозяйству, и водохранилищу остаётся лишь покрыть разницу.
- Наше водохранилище опустошает скудная зима (мало что накопить), за которой следует слабое таяние. Это центральноазиатская картина: водохранилище наполняется из скудного зимнего стока, как Каттакурганское.
12 One more block: evaporation
So far, water saved in the reservoir waits there for free. In a hot climate it does not: some of it evaporates. In TaqSim that is one more small class, a loss rule, plugged into the reservoir:
Hozirgacha omborda saqlangan suv u yerda tekin kutib turardi. Issiq iqlimda bunday emas: uning bir qismi bugʻlanadi. TaqSimda bu — suv omboriga ulanadigan yana bitta kichik klass, yoʻqotish qoidasi:
До сих пор вода, сбережённая в водохранилище, ждала там бесплатно. В жарком климате так не бывает: часть её испаряется. В TaqSim это ещё один небольшой класс, правило потерь, подключаемый к водохранилищу:
@dataclass(frozen=True)
class SummerEvaporation:
"""Each summer month a fixed share of the stored water evaporates."""
share: float
def calculate(self, node: Storage, t: Timestep) -> dict:
if t.index % 12 not in SUMMER_MONTHS:
return {} # no loss in winter
return {EVAPORATION: self.share * node.storage} # Mm³ lost this month, booked as evaporationcalculate(...) is called by TaqSim once per month, after the inflow has been stored and before the release. It returns what was lost and why; TaqSim subtracts it from the storage and records a WaterLost event.
calculate(...) ni TaqSim har oy bir marta, oqim saqlangandan keyin va chiqarishdan oldin chaqiradi. U nima yoʻqolgani va nima sababdan yoʻqolganini qaytaradi; TaqSim buni suv hajmidan ayiradi va WaterLost hodisasini yozadi.
calculate(...) TaqSim вызывает раз в месяц, после того как приток сохранён, и перед сбросом. Метод возвращает, что потеряно и почему; TaqSim вычитает это из объёма и записывает событие WaterLost.
Show the code
# Same 441 plans, same river, but now the reservoir loses 3 % of its content in each summer month
with_evaporation = score_plans(inflows, plan_grid(), evaporation_share=EVAPORATION_SHARE_EXAMPLE)
# Draw the unbeaten plans of both tables on one chart
_ = plot_front_shift({"no evaporation": table, "3 % of the stored water evaporates each summer month": with_evaporation})The two staircases are the unbeaten plans without (blue) and with (orange) evaporation. The orange one lies to the right of and above the blue one: every plan delivers less, and the same worst month costs more water. It also stops higher: with evaporation no plan gets the worst month below 40 %, against 35 % without. The 3 % is an illustrative figure, not a measurement. The effect is large: holding the worst month to 50 % short costs 1.4 Mm³ per year without evaporation and 3.3 Mm³ per year with it. Saving water for later is worth less when the saved water does not keep.
Ikkita zinapoya — bugʻlanishsiz (koʻk) va bugʻlanish bilan (toʻq sariq) yengilmagan rejalar. Toʻq sariq zinapoya koʻkning oʻngida va yuqorisida: har bir reja kamroq yetkazadi, va bir xil eng yomon oy koʻproq suvga tushadi. U yuqoriroqda ham toʻxtaydi: bugʻlanish bilan hech bir reja eng yomon oyni 40 % dan pastga tushira olmaydi, bugʻlanishsiz esa 35 % gacha. 3 % — oʻlchov emas, tushuntirish uchun olingan raqam. Taʼsiri katta: eng yomon oyni 50 % taqchillikda ushlab turish bugʻlanishsiz yiliga 1.4 mln m³, bugʻlanish bilan esa yiliga 3.3 mln m³ ga tushadi. Saqlangan suv saqlanmasa, uni keyinga asrash kamroq qadrli.
Две лестницы — непревзойдённые планы без испарения (синяя) и с испарением (оранжевая). Оранжевая лежит правее и выше синей: каждый план подаёт меньше, а тот же худший месяц стоит больше воды. Она и обрывается выше: с испарением ни один план не опускает худший месяц ниже 40 %, без испарения — до 35 %. 3 % — иллюстративное число, не измерение. Эффект велик: удержать худший месяц на уровне 50 % дефицита стоит 1.4 млн м³ в год без испарения и 3.3 млн м³ в год с ним. Беречь воду на потом менее выгодно, когда сбережённая вода не сохраняется.
13 One more user: the winter fields
Roadmap. Add winter fields as a second user; keep the summer plan fixed. The goals now become the farm’s summer shortage and the winter fields’ winter shortage, both Mm³/year. Compare A: request the winter fields’ full demand; B: request zero for them below 24 Mm³, otherwise full demand. Then compare 861 winter plans. Overflow can still reach the fields under plan B. The rule includes forecast lines for section 14; here the forecast is empty, so only storage is used. Holding water back can prove unnecessary when summer inflow is sufficient. Differences below are calculated before rounding, so subtracting rounded endpoints may differ by 0.1.
План раздела. Добавляем зимние поля, сохраняя летний план. Новые цели — летний дефицит хозяйства и зимний дефицит полей, в млн м³/год. План A запрашивает полную зимнюю потребность; B — ноль при запасе ниже 24 млн м³, иначе полную потребность. Затем сравниваем 861 план. Перелив всё равно может достичь полей. Строки прогноза нужны в разделе 14; здесь прогноз пуст и используется только запас. Сохранение воды может оказаться ненужным при достаточном летнем притоке. Разности вычисляются до округления; вычитание округлённых чисел может отличаться на 0,1.
Boʻlim rejasi. Yozgi rejani saqlab, qishki dalalarni qoʻshamiz. Yangi maqsadlar — xoʻjalikning yozgi va dalalarning qishki taqchilligi, mln m³/yil. A reja qishki toʻliq talabni soʻraydi; B zaxira 24 mln m³ dan pastda nol, aks holda toʻliq talabni soʻraydi. Keyin 861 rejani solishtiramiz. Toshgan suv baribir dalaga yetishi mumkin. Prognoz satrlari 14-boʻlim uchun; hozir prognoz boʻsh, faqat zaxira ishlatiladi. Yozgi oqim yetarli boʻlsa, suvni ushlab qolish ortiqcha boʻlishi mumkin. Farqlar yaxlitlashdan oldin hisoblanadi; yaxlitlangan sonlar ayirmasi 0,1 ga farq qilishi mumkin.
The reservoir fills in winter, and in the Zarafshan oasis winter is not idle. Winter wheat is watered, and before the spring sowing the saline fields are leached: flooded, so that the salt is washed down below the roots. Both take water from the same reservoir in the months when it should be filling. We add that user as a fifth block, on the same canal below the farm: the winter fields. They ask for 2.5 Mm³ in each winter month, 15 Mm³ a winter, and nothing in summer. The number is illustrative, like the evaporation share.
Ombor qishda toʻladi, Zarafshon vohasida esa qish boʻsh oʻtmaydi. Kuzgi bugʻdoy sugʻoriladi, bahorgi ekishdan oldin shoʻrlangan dalalar yuviladi: tuz ildizlardan pastga yuvilib ketishi uchun dalaga suv bostiriladi. Ikkalasi ham xuddi oʻsha ombordan, u toʻlishi kerak boʻlgan oylarda suv oladi. Bu isteʼmolchini beshinchi blok sifatida, xoʻjalikdan pastda, oʻsha kanalda qoʻshamiz: qishki dalalar. Ular har qish oyida 2,5 mln m³, bir qishda 15 mln m³ soʻraydi, yozda esa hech narsa soʻramaydi. Bu raqam, bugʻlanish ulushi kabi, tushuntirish uchun olingan.
Водохранилище наполняется зимой, а зима в Зарафшанском оазисе не пустует. Поливают озимую пшеницу, а перед весенним севом промывают засолённые поля: их затапливают, чтобы соль ушла ниже корней. И то и другое берёт воду из того же водохранилища в те месяцы, когда оно должно наполняться. Добавим этого водопользователя пятым блоком, на том же канале ниже хозяйства: зимние поля. Они просят 2,5 млн м³ в каждый зимний месяц, 15 млн м³ за зиму, и ничего летом. Число иллюстративное, как и доля испарения.
Show the code
_ = plot_network(winter=True)Every winter the operator faces a new question. Water given to the winter fields is gone before anyone knows how big the summer melt will be. Give it in full, and a weak melt leaves the farm short in July. Hold it back, and in most winters the water was not needed. The rule gets two more knobs, for winter:
- Winter trigger (Mm³): when the water in sight is below this, the winter fields are rationed. For now the water in sight is simply the storage.
- Winter ration (0 to 1): the share of their need the winter fields then get.
The summer knobs stay as they were. For the rest of this notebook we fix them at “cut deep” (trigger 16 Mm³, ration 0.5), the plan from the second run, so that only the winter decision changes.
Har qishda operator oldida yangi savol turadi. Qishki dalalarga berilgan suv yozgi erish qanchalik katta boʻlishini hech kim bilmasidan oldin ketib boʻladi. Toʻliq bering — kuchsiz erish xoʻjalikni iyulda taqchil qoldiradi. Ushlab turing — koʻp qishlarda bu suv kerak boʻlmagan boʻlib chiqadi. Qoida qish uchun yana ikki dastak oladi:
- Qishki chegara (mln m³): koʻrinib turgan suv bundan kam boʻlsa, qishki dalalar cheklanadi. Hozircha koʻrinib turgan suv — bu shunchaki ombordagi suv.
- Qishki ulush (0 dan 1 gacha): qishki dalalar oʻshanda oladigan talab ulushi.
Yozgi dastaklar avvalgidek qoladi. Daftarning qolgan qismida biz ularni “keskin kesish” (chegara 16 mln m³, ulush 0,5), yaʼni ikkinchi hisobdagi rejada qotirib qoʻyamiz, shunda faqat qishki qaror oʻzgaradi.
Каждую зиму перед оператором встаёт новый вопрос. Вода, отданная зимним полям, уходит раньше, чем кто-либо узнает, каким будет летнее таяние. Отдать полностью — и слабое таяние оставит хозяйство без воды в июле. Придержать — и в большинстве зим окажется, что вода была не нужна. Правило получает ещё два рычага, для зимы:
- Зимний порог (млн м³): когда вода на горизонте ниже него, зимние поля лимитируются. Пока вода на горизонте — это просто запас в водохранилище.
- Зимняя доля (от 0 до 1): какую часть своей потребности зимние поля тогда получают.
Летние рычаги остаются прежними. До конца этой тетради мы фиксируем их на «сильном урезании» (порог 16 млн м³, доля 0,5), плане из второго расчёта, чтобы менялось только зимнее решение.
@dataclass(frozen=True)
class WinterRelease(Strategy):
"""The summer rule with its two knobs, plus two knobs for winter.
In winter, deliver what the winter fields ask for, unless the water in sight (the storage, plus the forecast
of the coming summer if there is one) is below the winter trigger: then deliver only the winter ration of it.
"""
__params__: ClassVar[tuple[str, ...]] = ("trigger", "ration", "winter_trigger", "winter_ration")
__bounds__: ClassVar[dict[str, tuple[float, float]]] = {
"trigger": (0.0, CAPACITY_MM3),
"ration": (0.0, 1.0),
"winter_trigger": (0.0, WINTER_TRIGGER_MAX_MM3),
"winter_ration": (0.0, 1.0),
}
trigger: float = 0.0
ration: float = 1.0
winter_trigger: float = 0.0
winter_ration: float = 1.0
forecast: tuple[float, ...] = field(default=(), compare=False) # one forecast per year, Mm³; empty: none
def release(self, node: Storage, inflow: float, t: Timestep) -> float:
month = t.index % 12
need = MONTHLY_NEED_MM3[month] # what the farm asks for this month
if month in SUMMER_MONTHS:
if node.storage < self.trigger:
need *= self.ration
return min(need, node.storage)
in_sight = node.storage + (self.forecast[t.index // 12] if self.forecast else 0.0)
winter_need = WINTER_FIELDS_NEED_MM3[month] # what the winter fields ask for
if in_sight < self.winter_trigger:
winter_need *= self.winter_ration
return min(need + winter_need, node.storage)Show the code
# Summer "cut deep" in both; the winter fields served in full, or given nothing when the store is below 24 Mm³
winter_in_full = simulate_winter(inflows, WINTER_IN_FULL)
hold_back = simulate_winter(inflows, WinterPlan(trigger=24.0, ration=0.0))
for name, run in {"Winter fields served in full": winter_in_full, "Hold back below 24 Mm³": hold_back}.items():
print(
f"{name:30s} farm short {run.scores.summer_shortage:.1f} Mm³/yr, "
f"winter fields short {run.scores.winter_shortage:.1f} Mm³/yr"
)Winter fields served in full farm short 6.9 Mm³/yr, winter fields short 0.4 Mm³/yr
Hold back below 24 Mm³ farm short 2.7 Mm³/yr, winter fields short 5.3 Mm³/yr
Served in full, the winter fields get almost all of their 15 Mm³, and the farm’s shortage rises from 1.4 Mm³ a year (the same summer plan without the winter fields) to 6.9. Holding back below 24 Mm³ gives the farm back most of that, 4.3 Mm³ a year, at a price of 5.0 Mm³ a year for the winter fields. Even “nothing” is not quite nothing: in wet winters the reservoir spills, and the spilled water reaches the fields anyway.
The two goals are now the two users: the water the farm did not get in summer, and the water the winter fields did not get in winter, both in Mm³ a year, both less is better. As before, we try every winter plan: triggers from 0 to 40 Mm³ in steps of 1, rations in steps of 0.05, 861 plans.
Toʻliq berilganda qishki dalalar oʻzining 15 mln m³ ining deyarli hammasini oladi, xoʻjalikning taqchilligi esa yiliga 1.4 mln m³ dan (qishki dalalarsiz oʻsha yozgi reja) 6.9 ga koʻtariladi. 24 mln m³ dan pastda ushlab turish xoʻjalikka buning koʻp qismini, yiliga 4.3 mln m³ ni qaytaradi, qishki dalalar uchun esa yiliga 5.0 mln m³ ga tushadi. Hatto “hech narsa” ham toʻliq hech narsa emas: sersuv qishlarda ombor toshadi va toshgan suv baribir dalalarga yetib boradi.
Endi ikki maqsad — ikki isteʼmolchi: xoʻjalik yozda olmagan suv va qishki dalalar qishda olmagan suv, ikkalasi ham yiliga mln m³ da, ikkalasida ham kam boʻlgani yaxshi. Avvalgidek, har bir qishki rejani sinab koʻramiz: chegaralar 0 dan 40 mln m³ gacha 1 qadam bilan, ulushlar 0,05 qadam bilan, 861 ta reja.
При полной подаче зимние поля получают почти все свои 15 млн м³, а дефицит хозяйства растёт с 1.4 млн м³ в год (тот же летний план без зимних полей) до 6.9. Придерживание ниже 24 млн м³ возвращает хозяйству большую часть этого, 4.3 млн м³ в год, ценой 5.0 млн м³ в год для зимних полей. Даже «ничего» — не совсем ничего: в многоводные зимы водохранилище сбрасывает излишек, и эта вода всё равно доходит до полей.
Две цели — теперь два водопользователя: вода, которую хозяйство не получило летом, и вода, которую зимние поля не получили зимой, обе в млн м³ в год, обе — чем меньше, тем лучше. Как и раньше, перебираем все зимние планы: пороги от 0 до 40 млн м³ с шагом 1, доли с шагом 0,05, 861 план.
The winter problem in formulas. The summer plan is fixed, \(\theta^* = (16, 0.5)\); the winter plan is \(\psi = (\tau_w, \rho_w)\). The winter fields need \(w_m = 2.5\) Mm³ in a winter month and nothing in summer. In a winter month the rule compares the water in sight \(v_t\) with the winter trigger: the storage alone for now, the storage plus the forecast \(F_y\) of the coming summer in the next section. The farm is served first and the winter fields get the rest:
\[ r_t = \min\{\hat s_t,\; d_{m(t)} + \rho^{w}_t\, w_{m(t)}\}, \qquad \rho^{w}_t = \begin{cases} \rho_w & \text{if } v_t < \tau_w \\ 1 & \text{otherwise,} \end{cases} \qquad v_t = \hat s_t \;\text{ or }\; \hat s_t + F_{y(t)}, \]
Summer months follow the summer rule above. The two scores are the two users’ shortages, in Mm³ per year, and the problem is the same kind as before, over \(\tau_w \in [0, 40]\) (or \([0, 120]\) with a forecast) and \(\rho_w \in [0, 1]\):
\[ g_1(\psi) = \frac{1}{N}\sum_{t:\, m(t) \in S} \delta^{\text{farm}}_t, \qquad g_2(\psi) = \frac{1}{N}\sum_{t:\, m(t) \notin S} \delta^{\text{fields}}_t, \qquad \min_{\psi} \big(g_1(\psi),\, g_2(\psi)\big). \]
Qishki masala formulalarda. Yozgi reja qotirilgan: \(\theta^* = (16;\ 0{,}5)\); qishki reja — \(\psi = (\tau_w, \rho_w)\). Qishki dalalar qish oyida \(w_m = 2{,}5\) mln m³ soʻraydi, yozda hech narsa. Qish oyida qoida koʻrinib turgan suv \(v_t\) ni qishki chegara bilan solishtiradi: hozircha faqat ombordagi suv, keyingi boʻlimda ombordagi suv plyus kelayotgan yozning \(F_y\) prognozi. Avval xoʻjalikka beriladi, qolgani qishki dalalarga:
\[ r_t = \min\{\hat s_t,\; d_{m(t)} + \rho^{w}_t\, w_{m(t)}\}, \qquad \rho^{w}_t = \begin{cases} \rho_w & \text{if } v_t < \tau_w \\ 1 & \text{otherwise,} \end{cases} \qquad v_t = \hat s_t \;\text{ or }\; \hat s_t + F_{y(t)}, \]
Yoz oylari yuqoridagi yozgi qoidaga boʻysunadi. Ikki koʻrsatkich — ikki isteʼmolchining taqchilligi, yiliga mln m³ da; masala avvalgi turda, \(\tau_w \in [0, 40]\) (prognoz bilan \([0, 120]\)) va \(\rho_w \in [0, 1]\) boʻyicha:
\[ g_1(\psi) = \frac{1}{N}\sum_{t:\, m(t) \in S} \delta^{\text{farm}}_t, \qquad g_2(\psi) = \frac{1}{N}\sum_{t:\, m(t) \notin S} \delta^{\text{fields}}_t, \qquad \min_{\psi} \big(g_1(\psi),\, g_2(\psi)\big). \]
Зимняя задача в формулах. Летний план зафиксирован: \(\theta^* = (16;\ 0{,}5)\); зимний план — \(\psi = (\tau_w, \rho_w)\). Зимние поля просят \(w_m = 2{,}5\) млн м³ в зимний месяц и ничего летом. В зимний месяц правило сравнивает воду на горизонте \(v_t\) с зимним порогом: пока только запас, в следующем разделе — запас плюс прогноз \(F_y\) предстоящего лета. Сначала обслуживается хозяйство, остаток получают зимние поля:
\[ r_t = \min\{\hat s_t,\; d_{m(t)} + \rho^{w}_t\, w_{m(t)}\}, \qquad \rho^{w}_t = \begin{cases} \rho_w & \text{if } v_t < \tau_w \\ 1 & \text{otherwise,} \end{cases} \qquad v_t = \hat s_t \;\text{ or }\; \hat s_t + F_{y(t)}, \]
Летние месяцы подчиняются летнему правилу выше. Два показателя — дефициты двух водопользователей, в млн м³ в год; задача того же рода, что и раньше, по \(\tau_w \in [0, 40]\) (с прогнозом \([0, 120]\)) и \(\rho_w \in [0, 1]\):
\[ g_1(\psi) = \frac{1}{N}\sum_{t:\, m(t) \in S} \delta^{\text{farm}}_t, \qquad g_2(\psi) = \frac{1}{N}\sum_{t:\, m(t) \notin S} \delta^{\text{fields}}_t, \qquad \min_{\psi} \big(g_1(\psi),\, g_2(\psi)\big). \]
Show the code
# 861 winter plans, summer fixed at "cut deep"; the water in sight is the storage, so triggers run to 40 Mm³
storage_only = score_winter_plans(inflows, winter_grid(n_trigger=41, trigger_max=CAPACITY_MM3))
_ = plot_winter_fronts({"winter decided on the storage alone": storage_only})The same bent curve as in summer. Its bottom-right end is “serve in full”: the winter fields lack almost nothing, the farm is short 6.9 Mm³ a year. Moving up and left, each step gives the farm back some water and takes more from the winter fields. To hold the farm’s shortage to 3 Mm³ a year, the winter fields must give up 4.9 Mm³ a year; to hold it to 2, 6.7. The operator who only watches the storage pays this price in every winter the reservoir is low, including the many that a strong melt would have rescued.
Yozdagi kabi bukilgan egri chiziq. Uning pastki oʻng uchi — “toʻliq berish”: qishki dalalarga deyarli hech narsa yetishmaydi, xoʻjalik esa yiliga 6.9 mln m³ taqchil. Yuqoriga va chapga yurganda har bir qadam xoʻjalikka bir oz suv qaytaradi va qishki dalalardan koʻproq oladi. Xoʻjalik taqchilligini yiliga 3 mln m³ da ushlab turish uchun qishki dalalar yiliga 4.9 mln m³ dan voz kechishi kerak; 2 da ushlab turish uchun — 6.7. Faqat ombordagi suvga qaraydigan operator bu narxni ombor kam suvli boʻlgan har bir qishda toʻlaydi, shu jumladan kuchli erish qutqarib qoladigan koʻplab qishlarda ham.
Та же изогнутая кривая, что и летом. Её нижний правый конец — «подавать полностью»: зимним полям почти ничего не не хватает, хозяйству не хватает 6.9 млн м³ в год. Двигаясь вверх и влево, каждый шаг возвращает хозяйству немного воды и отнимает больше у зимних полей. Чтобы удержать дефицит хозяйства на 3 млн м³ в год, зимние поля должны отдать 4.9 млн м³ в год; чтобы удержать на 2 — 6.7. Оператор, который смотрит только на запас, платит эту цену каждую зиму, когда водохранилище маловодно, включая те многие зимы, которые сильное таяние всё равно бы спасло.
14 A forecast of the summer, decided in winter
The operator holds back because the summer is unknown. But it is not entirely unknown. By late winter most of the snow that will melt in summer has fallen, and the hydrometeorological services of Central Asia forecast the April–September flow of their rivers every spring, from the snow cover, the winter precipitation and the flow of the past months (Apel et al., 2018). Suppose our operator has such a forecast at the start of every winter: one number, the inflow of the coming summer. In reality it arrives late in the winter, once the snow has fallen; here the operator gets it at the start of winter, which is the best case, and exercise 6 asks what a later forecast is still worth.
The rule needs one change. The water in sight is no longer the storage alone but the storage plus the forecast, and the winter trigger is compared with that. Nothing else changes: same knobs, same summer plan, same river. Because the water in sight now includes a summer, the trigger runs up to 120 Mm³ instead of 40.
We start with the best case, a forecast that is always right. It tells us the most a forecast could ever be worth here.
Operator suvni yoz nomaʼlum boʻlgani uchun ushlab turadi. Lekin u butunlay nomaʼlum emas. Qish oxiriga kelib yozda eriydigan qorning koʻp qismi yogʻib boʻlgan boʻladi, va Markaziy Osiyo gidrometeorologiya xizmatlari har bahorda oʻz daryolarining aprel–sentyabr oqimini qor qoplami, qishki yogʻin va oʻtgan oylar oqimi asosida prognoz qiladi (Apel va boshq., 2018). Faraz qilaylik, bizning operatorda har qish boshida shunday prognoz bor: bitta raqam, kelayotgan yozning oqimi. Aslida u qish oxirida, qor yogʻib boʻlgach keladi; bu yerda operator uni qish boshida oladi — bu eng yaxshi holat, kechroq kelgan prognoz qanchaga arzishini esa 6-mashq soʻraydi.
Qoidaga bitta oʻzgartirish kerak. Koʻrinib turgan suv endi faqat ombordagi suv emas, balki ombordagi suv plyus prognoz, va qishki chegara shu bilan solishtiriladi. Boshqa hech narsa oʻzgarmaydi: oʻsha dastaklar, oʻsha yozgi reja, oʻsha daryo. Koʻrinib turgan suvga endi bir yoz ham kirgani uchun chegara 40 emas, 120 mln m³ gacha boradi.
Eng yaxshi holatdan boshlaymiz: hamisha toʻgʻri chiqadigan prognoz. U bizga bu yerda prognoz eng koʻpi bilan qanchalik qadrli boʻlishi mumkinligini koʻrsatadi.
Оператор придерживает воду, потому что лето неизвестно. Но оно неизвестно не полностью. К концу зимы большая часть снега, который растает летом, уже выпала, и гидрометеорологические службы Центральной Азии каждую весну прогнозируют сток своих рек за апрель–сентябрь по снежному покрову, зимним осадкам и стоку прошедших месяцев (Apel et al., 2018). Предположим, что у нашего оператора в начале каждой зимы есть такой прогноз: одно число, приток предстоящего лета. В действительности он приходит в конце зимы, когда снег уже выпал; здесь оператор получает его в начале зимы — это лучший случай, а упражнение 6 спрашивает, чего стоит более поздний прогноз.
Правилу нужно одно изменение. Вода на горизонте — это теперь не только запас, а запас плюс прогноз, и с ним сравнивается зимний порог. Больше ничего не меняется: те же рычаги, тот же летний план, та же река. Поскольку в воду на горизонте теперь входит целое лето, порог доходит до 120 млн м³ вместо 40.
Начнём с лучшего случая — прогноза, который всегда верен. Он показывает, сколько прогноз вообще может здесь стоить.
Show the code
perfect = seasonal_forecast(inflows, skill=1.0) # one number per year: the coming summer's inflow, exactly
with_perfect = score_winter_plans(inflows, winter_grid(n_trigger=41), forecast=perfect) # 861 plans, triggers to 120
_ = plot_winter_fronts({"storage plus a perfect forecast": with_perfect, "storage alone": storage_only})Show the code
# From each curve, the plan that holds the farm's shortage to 2 Mm³ a year with the least loss for the winter fields
chosen = {
"storage plus a perfect forecast": winter_plan_within(with_perfect, 2.0),
"storage alone": winter_plan_within(storage_only, 2.0),
}
runs = {
label: simulate_winter(inflows, winter, forecast=perfect if "forecast" in label else None)
for label, winter in chosen.items()
}
_ = plot_winter_decisions(runs, inflows, first_year=first_year)- The blue curve lies below the orange one. At every summer shortage the farm accepts, the winter fields lose less. Holding the farm to 2 Mm³ a year costs the winter fields 6.7 Mm³ a year without the forecast and 5.5 with it, 17 % less; at 3 Mm³ a year, 4.9 against 4.3.
- Why. Look at the two plans in the second figure. The plan without the forecast holds back whenever the reservoir is low, which is mostly the winter after a dry summer. The plan with the forecast holds back in the winters before a weak summer and serves in full before a strong one. It rations fewer winters, and the right ones.
- This gap is the ceiling. No real forecast can do better than one that is always right. Whatever a forecast service can offer is worth at most this much here: 1.1 Mm³ a year for the winter fields at a summer shortage of 2. In a system where more decisions are taken before the melt, the ceiling is higher.
- Koʻk egri chiziq toʻq sarigʻidan pastda yotadi. Xoʻjalik rozi boʻlgan har bir yozgi taqchillikda qishki dalalar kamroq yoʻqotadi. Xoʻjalikni yiliga 2 mln m³ da ushlab turish qishki dalalarga prognozsiz yiliga 6.7 mln m³, prognoz bilan 5.5 ga tushadi, yaʼni 17 % kam; yiliga 3 mln m³ da — 4.9 ga qarshi 4.3.
- Nima uchun. Ikkinchi rasmdagi ikki rejaga qarang. Prognozsiz reja ombor kam suvli boʻlganda ushlab turadi, bu esa asosan quruq yozdan keyingi qish. Prognozli reja kuchsiz yozdan oldingi qishlarda ushlab turadi va kuchli yozdan oldin toʻliq beradi. U kamroq qishni cheklaydi, va aynan keraklilarini.
- Bu farq — shift. Hech bir haqiqiy prognoz hamisha toʻgʻri chiqadiganidan yaxshiroq boʻla olmaydi. Prognoz xizmati taklif qila oladigan narsa bu yerda eng koʻpi bilan shuncha turadi: yozgi taqchillik 2 boʻlganda qishki dalalar uchun yiliga 1.1 mln m³. Erishdan oldin koʻproq qaror qabul qilinadigan tizimda shift balandroq.
- Синяя кривая лежит ниже оранжевой. При любом летнем дефиците, на который соглашается хозяйство, зимние поля теряют меньше. Удержать хозяйство на 2 млн м³ в год стоит зимним полям 6.7 млн м³ в год без прогноза и 5.5 с ним, на 17 % меньше; при 3 млн м³ в год — 4.9 против 4.3.
- Почему. Посмотрите на два плана на второй рисунке. План без прогноза придерживает воду всякий раз, когда водохранилище маловодно, а это в основном зима после сухого лета. План с прогнозом придерживает воду в зимы перед слабым летом и подаёт полностью перед сильным. Он лимитирует меньше зим, и именно те, что нужно.
- Этот разрыв — потолок. Ни один реальный прогноз не может быть лучше того, что всегда верен. Что бы ни предложила прогностическая служба, здесь это стоит самое большее столько: 1.1 млн м³ в год для зимних полей при летнем дефиците 2. В системе, где больше решений принимается до таяния, потолок выше.
15 A forecast that can be wrong
The idea. The last section gave the operator a forecast that was always right. No real forecast is. The question of this section is the one a water authority actually faces: a forecast that is sometimes wrong, is it still worth acting on, and how much? The everyday version is familiar. A weather forecast that is right seven times in ten is far from perfect, yet you still take the umbrella when it says rain, and you are right to: you act on it differently than on a perfect one, with less trust, but you do not throw it away. We want to see the same thing for the reservoir, with numbers.
How we build it. We cannot buy a hundred years of real forecasts for a synthetic river, so we manufacture forecasts of known quality. We take the summer that will actually come, blur it with random noise, and let a sensible forecaster make the best guess it can from the blurred picture. One knob sets how much blur: we call it the forecast’s skill, from 0 to 1.
- Skill 1: no blur. The forecaster sees the summer exactly; this is the perfect forecast of the last section.
- Skill 0: all blur. The forecaster sees nothing useful and can do no better than say “an average summer” every year.
- In between: the forecaster sees something, and the forecasts follow the real summers partly. The technical name for skill is the correlation coefficient between forecasts and outcomes.
seasonal_forecast makes such forecasts. One property matters: the forecasts are honest. A forecaster who knows little says something close to the average; one who knows a lot dares to say “a very dry summer”. On average the guess is neither too bold nor too timid, which is how a statistical forecast, fitted to past years, behaves. The operator’s rule is the one you know: it adds the forecast to the water in sight and compares the sum with the winter trigger. Two examples of what the forecasts look like:
Gʻoya. Oʻtgan boʻlimda operatorga hamisha toʻgʻri chiqadigan prognoz berildi. Hech bir haqiqiy prognoz bunday emas. Bu boʻlimning savoli — suv xoʻjaligi idorasi aslida duch keladigan savol: baʼzan xato qiladigan prognozga tayanish arziydimi, va qanchaga? Kundalik varianti hammaga tanish. Oʻndan yetti marta toʻgʻri chiqadigan ob-havo prognozi mukammallikdan uzoq, lekin u yomgʻir deganda siz baribir soyabon olasiz, va toʻgʻri qilasiz: unga mukammal prognozdan koʻra kamroq ishonib, boshqacha harakat qilasiz, lekin uni tashlab yubormaysiz. Biz xuddi shuni suv ombori uchun, raqamlar bilan koʻrmoqchimiz.
Qanday quramiz. Sunʼiy daryo uchun yuz yillik haqiqiy prognozlarni sotib olib boʻlmaydi, shuning uchun maʼlum sifatli prognozlarni oʻzimiz yasaymiz. Haqiqatda keladigan yozni olamiz, uni tasodifiy shovqin bilan xiralashtiramiz, va oqilona prognozchiga xira rasmdan qoʻlidan kelgan eng yaxshi taxminni qildiramiz. Bitta dastak xiralik darajasini belgilaydi: biz uni prognozning sifati deymiz, 0 dan 1 gacha.
- Sifat 1: xiralik yoʻq. Prognozchi yozni aynan koʻradi; bu oʻtgan boʻlimdagi mukammal prognoz.
- Sifat 0: butunlay xira. Prognozchi foydali hech narsa koʻrmaydi va har yili “oʻrtacha yoz” deyishdan yaxshiroq hech narsa qila olmaydi.
- Oraliqda: prognozchi nimanidir koʻradi, prognozlar haqiqiy yozlarga qisman ergashadi. Sifatning texnik nomi — prognozlar bilan natijalar orasidagi korrelyatsiya koeffitsiyenti.
seasonal_forecast shunday prognozlar yasaydi. Bitta xususiyat muhim: prognozlar halol. Kam biladigan prognozchi oʻrtachaga yaqin narsa aytadi; koʻp biladigani “juda quruq yoz” deyishga jurʼat qiladi. Oʻrtacha olganda taxmin na haddan tashqari dadil, na haddan tashqari ehtiyotkor — oʻtgan yillarga moslangan statistik prognoz aynan shunday ishlaydi. Operatorning qoidasi sizga tanish: u prognozni koʻrinib turgan suvga qoʻshadi va yigʻindini qishki chegara bilan solishtiradi. Prognozlar qanday koʻrinishiga ikki misol:
Идея. В прошлом разделе оператор получил прогноз, который всегда верен. Ни один настоящий прогноз таким не бывает. Вопрос этого раздела — тот, с которым на деле сталкивается водное ведомство: прогноз, который иногда ошибается, — стоит ли на него опираться, и насколько? Житейский вариант знаком каждому. Прогноз погоды, который сбывается семь раз из десяти, далёк от идеала, и всё же вы берёте зонт, когда он обещает дождь, и правильно делаете: вы действуете по нему иначе, чем по идеальному, с меньшим доверием, но не выбрасываете его. Мы хотим увидеть то же самое для водохранилища, в числах.
Как мы это строим. Купить сто лет настоящих прогнозов для синтетической реки нельзя, поэтому мы изготавливаем прогнозы известного качества. Берём лето, которое действительно наступит, размываем его случайным шумом и даём разумному прогнозисту сделать лучшую оценку, на какую он способен по размытой картине. Один рычаг задаёт степень размытия: мы называем его качеством прогноза, от 0 до 1.
- Качество 1: размытия нет. Прогнозист видит лето в точности; это идеальный прогноз прошлого раздела.
- Качество 0: сплошное размытие. Прогнозист не видит ничего полезного и не может сделать ничего лучше, чем каждый год говорить «среднее лето».
- Между ними: прогнозист что-то видит, и прогнозы отчасти следуют за настоящими лётами. Техническое название качества — коэффициент корреляции между прогнозами и исходами.
seasonal_forecast строит такие прогнозы. Важно одно свойство: прогнозы честны. Прогнозист, который знает мало, говорит что-то близкое к среднему; тот, кто знает много, решается сказать «очень сухое лето». В среднем оценка не слишком смела и не слишком робка — именно так ведёт себя статистический прогноз, подобранный по прошлым годам. Правило оператора — то, что вы знаете: оно прибавляет прогноз к воде на горизонте и сравнивает сумму с зимним порогом. Два примера того, как выглядят прогнозы:
The forecast in formulas. The summer inflow of year \(y\) is \(Q_y = e^{\mu + \sigma z_y}\) with \(z_y\) a standard score (the river is log-normal). The forecaster sees the coming summer through noise \(\varepsilon_y\), and issues the best guess given what it saw:
\[ \tilde z_y = \kappa\, z_y + \sqrt{1-\kappa^2}\; \varepsilon_y, \qquad F_y = \mathbb{E}\big[Q_y \mid \tilde z_y\big] = \exp\!\Big(\mu + \sigma \kappa \tilde z_y + \tfrac{1}{2}\sigma^2 (1-\kappa^2)\Big), \]
so that the correlation between what it sees and the truth is the skill \(\kappa\), the forecast is the long-run mean for \(\kappa = 0\) and the summer itself for \(\kappa = 1\). The value of a forecast is measured at a fixed summer shortage \(\bar c\) (2 Mm³ a year below): the winter water it saves without costing the farm anything,
\[ V(\kappa) = g_2^{*}(\text{no forecast}) - g_2^{*}(\kappa), \qquad g_2^{*}(\cdot) = \min\big\{\, g_2(\psi) : g_1(\psi) \le \bar c \,\big\}. \]
\(V(1)\), the value of the perfect forecast, is the ceiling.
Prognoz formulalarda. \(y\) yilning yozgi oqimi \(Q_y = e^{\mu + \sigma z_y}\), bunda \(z_y\) — standart baho (daryo lognormal). Prognozchi kelayotgan yozni \(\varepsilon_y\) shovqin orqali koʻradi va koʻrganiga koʻra eng yaxshi taxminni beradi:
\[ \tilde z_y = \kappa\, z_y + \sqrt{1-\kappa^2}\; \varepsilon_y, \qquad F_y = \mathbb{E}\big[Q_y \mid \tilde z_y\big] = \exp\!\Big(\mu + \sigma \kappa \tilde z_y + \tfrac{1}{2}\sigma^2 (1-\kappa^2)\Big), \]
shunda u koʻrgan narsa bilan haqiqat orasidagi korrelyatsiya — \(\kappa\) sifat, prognoz \(\kappa = 0\) da uzoq muddatli oʻrtacha, \(\kappa = 1\) da yozning oʻzi. Prognozning qadri qotirilgan yozgi taqchillik \(\bar c\) da (quyida yiliga 2 mln m³) oʻlchanadi: xoʻjalikka hech narsaga tushmasdan u tejab qolgan qishki suv,
\[ V(\kappa) = g_2^{*}(\text{no forecast}) - g_2^{*}(\kappa), \qquad g_2^{*}(\cdot) = \min\big\{\, g_2(\psi) : g_1(\psi) \le \bar c \,\big\}. \]
\(V(1)\), mukammal prognozning qadri — shift.
Прогноз в формулах. Летний приток года \(y\) — \(Q_y = e^{\mu + \sigma z_y}\), где \(z_y\) — стандартизованная величина (река логнормальна). Прогнозист видит предстоящее лето сквозь шум \(\varepsilon_y\) и выдаёт лучшую оценку по тому, что увидел:
\[ \tilde z_y = \kappa\, z_y + \sqrt{1-\kappa^2}\; \varepsilon_y, \qquad F_y = \mathbb{E}\big[Q_y \mid \tilde z_y\big] = \exp\!\Big(\mu + \sigma \kappa \tilde z_y + \tfrac{1}{2}\sigma^2 (1-\kappa^2)\Big), \]
так что корреляция между увиденным и истиной — это качество \(\kappa\), прогноз при \(\kappa = 0\) — многолетнее среднее, при \(\kappa = 1\) — само лето. Ценность прогноза измеряется при фиксированном летнем дефиците \(\bar c\) (ниже 2 млн м³ в год): зимняя вода, которую он сберегает, ничего не стоя хозяйству,
\[ V(\kappa) = g_2^{*}(\text{no forecast}) - g_2^{*}(\kappa), \qquad g_2^{*}(\cdot) = \min\big\{\, g_2(\psi) : g_1(\psi) \le \bar c \,\big\}. \]
\(V(1)\), ценность идеального прогноза, — потолок.
Show the code
_ = plot_forecast_skill(inflows, skills=(0.5, 0.9))- How to read the figure. One dot per year: across, the summer that came; up, what the forecast said. A dot on the grey line is a forecast that was exactly right; the further from the line, the larger the error. With skill 0.9 the dots hug the line; with skill 0.5 they form a cloud. The typical error (the average distance from the line) is 5 Mm³ at skill 0.9 and 9 Mm³ at skill 0.5, against 11 Mm³ for “an average summer” every year.
- A weak forecast stays close to the average. Look at the skill-0.5 cloud: it is flatter than the line, the forecasts hardly ever say 20 or 70 even when the summer turned out so. That is not timidity, it is honesty: a forecaster who knows little should say something close to the average, because a bold forecast from weak evidence would be wrong more often and by more. The formula above does exactly that: it pulls the guess towards the long-run mean in proportion to what the forecaster does not know.
- Two ways to be wrong. A forecast that is too rosy says the summer will be fine, the operator gives the winter fields their water, and the farm is short in July. One that is too gloomy makes the operator hold back water that was not needed. With skill 0.5 the forecast was more than 10 Mm³ too rosy in 19 of the hundred years and more than 10 Mm³ too gloomy in 13; with skill 0.9, in 5 and 7. The two errors are not equally bad for the two users, which is why the operator cannot simply take a weak forecast at its word.
- Rasmni qanday oʻqish. Har bir yil — bitta nuqta: koʻndalangiga kelgan yoz, tikka prognoz aytgani. Kulrang chiziq ustidagi nuqta — aynan toʻgʻri chiqqan prognoz; chiziqdan qancha uzoq boʻlsa, xato shuncha katta. Sifat 0,9 da nuqtalar chiziqqa yopishib turadi; 0,5 da bulut hosil qiladi. Odatiy xato (chiziqdan oʻrtacha masofa) sifat 0,9 da 5 mln m³, 0,5 da 9 mln m³, har yili “oʻrtacha yoz” deyishda esa 11 mln m³.
- Kuchsiz prognoz oʻrtachaga yaqin turadi. Sifat 0,5 bulutiga qarang: u chiziqdan yotiqroq, yoz 20 yoki 70 chiqqanda ham prognoz deyarli hech qachon 20 yoki 70 demaydi. Bu qoʻrqoqlik emas, halollik: kam biladigan prognozchi oʻrtachaga yaqin narsa aytishi kerak, chunki kuchsiz dalillardan dadil prognoz tez-tez va koʻproq xato boʻladi. Yuqoridagi formula aynan shuni qiladi: taxminni prognozchi bilmagan narsaga mutanosib ravishda uzoq muddatli oʻrtacha tomon tortadi.
- Xato qilishning ikki yoʻli. Haddan tashqari umidvor prognoz yoz yaxshi boʻladi deydi, operator qishki dalalarga suvini beradi, xoʻjalik esa iyulda taqchil qoladi. Haddan tashqari tushkun prognoz operatorni kerak boʻlmagan suvni ushlab turishga majbur qiladi. Sifat 0,5 da prognoz yuz yildan 19 tasida 10 mln m³ dan ortiq umidvor, 13 tasida 10 mln m³ dan ortiq tushkun boʻldi; sifat 0,9 da — 5 va 7 tasida. Bu ikki xato ikki isteʼmolchi uchun bir xil yomon emas, shuning uchun operator kuchsiz prognozga shunchaki soʻzsiz ishona olmaydi.
- Как читать рисунок. Одна точка — один год: по горизонтали — какое лето наступило, по вертикали — что говорил прогноз. Точка на серой линии — прогноз, который сбылся в точности; чем дальше от линии, тем больше ошибка. При качестве 0,9 точки жмутся к линии; при 0,5 образуют облако. Типичная ошибка (среднее расстояние от линии) — 5 млн м³ при качестве 0,9 и 9 млн м³ при 0,5, против 11 млн м³, если каждый год говорить «среднее лето».
- Слабый прогноз держится около среднего. Посмотрите на облако при качестве 0,5: оно положе линии, прогноз почти никогда не говорит 20 или 70, даже когда лето оказалось таким. Это не робость, а честность: прогнозист, который знает мало, должен говорить что-то близкое к среднему, потому что смелый прогноз по слабым данным ошибался бы чаще и сильнее. Формула выше делает ровно это: она притягивает оценку к многолетнему среднему пропорционально тому, чего прогнозист не знает.
- Два способа ошибиться. Слишком радужный прогноз обещает хорошее лето, оператор отдаёт зимним полям их воду, и хозяйству не хватает воды в июле. Слишком мрачный заставляет оператора придержать воду, которая была не нужна. При качестве 0,5 прогноз был более чем на 10 млн м³ радужнее истины в 19 из ста лет и более чем на 10 млн м³ мрачнее — в 13; при качестве 0,9 — в 5 и 7. Эти две ошибки не одинаково плохи для двух водопользователей, и потому оператор не может просто верить слабому прогнозу на слово.
Now the operator uses these forecasts. The rule is unchanged: it takes the forecast at its word and adds it to the water in sight. What changes is the plan: for each skill we try the 861 winter plans again, so that the trigger and the ration are chosen knowing how good the forecast is.
Endi operator shu prognozlardan foydalanadi. Qoida oʻzgarmagan: u prognozga soʻzsiz ishonadi va uni koʻrinib turgan suvga qoʻshadi. Oʻzgaradigani — reja: har bir sifat uchun 861 ta qishki rejani qayta sinab koʻramiz, shunda chegara va ulush prognoz qanchalik yaxshi ekanini bilgan holda tanlanadi.
Теперь оператор пользуется этими прогнозами. Правило не изменилось: оно верит прогнозу на слово и прибавляет его к воде на горизонте. Меняется план: для каждого качества мы снова перебираем 861 зимний план, так что порог и доля выбираются с учётом того, насколько хорош прогноз.
Show the code
# Six more forecasts of growing skill, 861 winter plans each (this cell runs for a few minutes)
tables = {1.0: with_perfect, **score_by_skill(inflows, (0.0, 0.3, 0.5, 0.65, 0.8, 0.9), winter_grid(n_trigger=41))}
_ = plot_winter_fronts(
{
"perfect forecast": with_perfect,
"forecast with skill 0.8": tables[0.8],
"forecast with skill 0.5": tables[0.5],
"storage alone": storage_only,
}
)- The curves nest. Each forecast’s curve lies between “storage alone” and the perfect forecast, and the better the forecast, the closer to the perfect one. No curve crosses below the perfect one: a wrong forecast can at best be ignored, and the optimizer makes sure it is never worse than that.
- How a plan copes with a wrong forecast. Take the plans that hold the farm to 2 Mm³ a year. To reach that safety, the plan without a forecast rations 63 of the hundred winters; with a forecast of skill 0.5 it rations 64, with skill 0.8 55, with the perfect forecast 50. The weaker the forecast, the more winters the plan must hold back to be safe against the summers the forecast gets wrong. Those extra rationed winters are the price of the forecast’s errors, and the optimizer pays it by moving the trigger: 69 Mm³ of water in sight with skill 0.5 against 60 with the perfect forecast.
- The operator does not need to know the formula. It needs to know the skill, and to choose the plan for it. A plan tuned for a perfect forecast and fed a weak one would ration too rarely; that is the trap the next figure quantifies.
- Egri chiziqlar ichma-ich joylashadi. Har bir prognozning egri chizigʻi “faqat ombordagi suv” bilan mukammal prognoz orasida yotadi, va prognoz qancha yaxshi boʻlsa, mukammalga shuncha yaqin. Hech bir egri chiziq mukammalnikidan pastga kesib oʻtmaydi: notoʻgʻri prognozni eng yaxshi holda eʼtiborsiz qoldirish mumkin, optimallashtirgich esa undan yomon boʻlmasligini taʼminlaydi.
- Reja notoʻgʻri prognoz bilan qanday kurashadi. Xoʻjalikni yiliga 2 mln m³ da ushlab turadigan rejalarni oling. Shu xavfsizlikka erishish uchun prognozsiz reja yuz qishdan 63 tasini cheklaydi; sifati 0,5 prognoz bilan 64 tasini, 0,8 bilan 55 tasini, mukammal prognoz bilan 50 tasini. Prognoz qancha kuchsiz boʻlsa, reja prognoz xato qiladigan yozlardan xavfsiz boʻlish uchun shuncha koʻp qishda ushlab turishi kerak. Bu qoʻshimcha cheklangan qishlar — prognoz xatolarining narxi, optimallashtirgich uni chegarani surish bilan toʻlaydi: sifat 0,5 da koʻrinib turgan suv 69 mln m³, mukammal prognozda 60.
- Operator formulani bilishi shart emas. U sifatni bilishi va unga mos rejani tanlashi kerak. Mukammal prognozga moslangan, lekin kuchsiz prognoz berilgan reja juda kam cheklagan boʻlardi; keyingi rasm aynan shu tuzoqni oʻlchaydi.
- Кривые вложены друг в друга. Кривая каждого прогноза лежит между «только запасом» и идеальным прогнозом, и чем лучше прогноз, тем ближе к идеальному. Ни одна кривая не опускается ниже идеальной: ошибочный прогноз в лучшем случае можно проигнорировать, и оптимизатор следит, чтобы хуже этого не стало.
- Как план справляется с ошибочным прогнозом. Возьмём планы, удерживающие хозяйство на 2 млн м³ в год. Чтобы достичь этой безопасности, план без прогноза лимитирует 63 из ста зим; с прогнозом качества 0,5 — 64, с качеством 0,8 — 55, с идеальным прогнозом — 50. Чем слабее прогноз, тем больше зим план должен придерживать воду, чтобы застраховаться от тех лет, где прогноз ошибается. Эти лишние лимитированные зимы — цена ошибок прогноза, и оптимизатор платит её сдвигом порога: 69 млн м³ воды на горизонте при качестве 0,5 против 60 при идеальном прогнозе.
- Оператору не нужно знать формулу. Ему нужно знать качество и выбрать план под него. План, настроенный на идеальный прогноз и получающий слабый, лимитировал бы слишком редко; именно эту ловушку измеряет следующий рисунок.
What we find, on one chart. Three curves are hard to compare by eye, so we reduce each to one number. We fix the farm’s safety at a summer shortage of 2 Mm³ a year, the point we have used throughout, and ask: with this forecast, how much must the winter fields give up to keep the farm that safe? One point per skill, from “an average summer every year” to the perfect forecast, with the operator without a forecast as the line to beat.
Nima topdik, bitta diagrammada. Uchta egri chiziqni koʻz bilan solishtirish qiyin, shuning uchun har birini bitta raqamga keltiramiz. Xoʻjalik xavfsizligini yiliga 2 mln m³ yozgi taqchillikda — butun boʻlim davomida foydalangan nuqtamizda — qotiramiz va soʻraymiz: shu prognoz bilan xoʻjalikni shunchalik xavfsiz saqlash uchun qishki dalalar qanchadan voz kechishi kerak? Har bir sifat uchun bitta nuqta, “har yili oʻrtacha yoz”dan mukammal prognozgacha, prognozsiz operator esa — yengish kerak boʻlgan chiziq.
Что мы находим, на одном графике. Три кривые трудно сравнить на глаз, поэтому сводим каждую к одному числу. Фиксируем безопасность хозяйства на летнем дефиците 2 млн м³ в год — точке, которой мы пользовались всё время, — и спрашиваем: с этим прогнозом сколько должны отдать зимние поля, чтобы хозяйство оставалось настолько в безопасности? Одна точка на каждое качество, от «среднего лета каждый год» до идеального прогноза, а оператор без прогноза — линия, которую нужно превзойти.
Show the code
curve = forecast_value(tables, summer_cap=2.0)
_ = plot_forecast_value(curve, no_forecast=at_2, summer_cap=2.0)- A forecast with no skill is no forecast. At skill 0 the forecast says 40 Mm³ every year, and the rule is back to watching the storage: the curve starts on the orange line, within the grid’s steps (6.8 against 6.7 Mm³ a year).
- Value grows with skill, but not in proportion. A forecast of skill 0.5, halfway to perfect, delivers 17 % of what the perfect forecast delivers; one of skill 0.8 leaves the winter fields 5.8 Mm³ a year short, against 5.5 for the perfect forecast and 6.7 with none. Most of the value sits in the last stretch of skill. The curve wiggles because the forecast errors are one random draw of a hundred years; the trend is what counts.
- Where real forecasts sit. The statistical forecasts that Apel et al. (2018) built for rivers of Central Asia explain 60 to 80 % of the year-to-year variation of the April–September flow when issued in January, and 68 to 97 % when issued on 1 April, the date that matters most for the region’s water planning. In our terms that is a skill of roughly 0.8 to 0.95: real forecasts sit on the right-hand part of the curve, where the value is.
- The forecast’s worth depends on the decision, not only on the forecast. The same forecast was worth nothing for the summer decisions of this reservoir (we tried: the operator learns the summer by May, and water saved in one month is still there the next), and worth 1.1 Mm³ a year for the winter decision. A forecast is worth what the decision it informs can do with it, a point made for weather forecasts by Murphy (1993) and for reservoirs by Anghileri et al. (2016).
- What the operator does with a wrong forecast matters. Here the rule takes the forecast at its word, and the optimizer compensates by choosing the trigger. A cautious operator could count only part of the forecast; exercise 5 asks whether that helps.
- What a real forecast service adds. Not one number but a range: an ensemble of possible summers, or a best guess with an error band, and updates every month as the snow accumulates. The natural next step, not taken here, is to count on a cautious quantile of that range (“the summer we are 80 % sure of”) and let the optimizer choose how cautious; and to let the forecast improve through the winter, as exercise 6 begins to do.
- Sifati yoʻq prognoz — prognoz emas. Sifat 0 da prognoz har yili 40 mln m³ deydi, va qoida yana ombordagi suvga qarashga qaytadi: egri chiziq toʻr qadamlari doirasida toʻq sariq chiziqdan boshlanadi (yiliga 6.8 ga qarshi 6.7 mln m³).
- Qadr sifat bilan oʻsadi, lekin mutanosib emas. Sifati 0,5 boʻlgan, mukammalga yarim yoʻl bosgan prognoz mukammal prognoz beradiganning 17 % ini beradi; sifati 0,8 boʻlgani qishki dalalarni yiliga 5.8 mln m³ taqchil qoldiradi, mukammal prognozda 5.5, prognozsiz 6.7. Qadrning koʻp qismi sifatning oxirgi boʻlagida yotadi. Egri chiziq tebranadi, chunki prognoz xatolari — yuz yillik bitta tasodifiy tanlanma; muhimi — umumiy yoʻnalish.
- Haqiqiy prognozlar qayerda turadi. Apel va boshq. (2018) Markaziy Osiyo daryolari uchun qurgan statistik prognozlar aprel–sentyabr oqimining yildan yilga oʻzgarishining 60–80 % ini yanvarda berilganda, 68–97 % ini esa 1 aprelda — mintaqa suv rejalashtiruvi uchun eng muhim sanada — berilganda tushuntiradi. Bizning atamalarda bu taxminan 0,8 dan 0,95 gacha sifat: haqiqiy prognozlar egri chiziqning oʻng qismida, qadr yotgan joyda turadi.
- Prognozning qadri faqat prognozga emas, qarorga bogʻliq. Xuddi shu prognoz bu omborning yozgi qarorlari uchun hech narsaga arzimadi (biz sinab koʻrdik: operator yozni may oyigacha bilib oladi, bir oyda saqlangan suv esa keyingi oyda ham joyida turadi), qishki qaror uchun esa yiliga 1.1 mln m³ ga arzidi. Prognoz u xabardor qilgan qaror u bilan nima qila olsa, shunchaga arziydi — bu fikrni ob-havo prognozlari uchun Murphy (1993), suv omborlari uchun Anghileri va boshq. (2016) aytgan.
- Operator notoʻgʻri prognoz bilan nima qilishi muhim. Bu yerda qoida prognozga soʻzsiz ishonadi, optimallashtirgich esa chegarani tanlab buni qoplaydi. Ehtiyotkor operator prognozning faqat bir qismini hisobga olishi mumkin edi; 5-mashq bu yordam beradimi, deb soʻraydi.
- Haqiqiy prognoz xizmati nima qoʻshadi. Bitta raqam emas, oraliq: mumkin boʻlgan yozlar ansambli yoki xato oraligʻi bilan eng yaxshi taxmin, va qor toʻplangani sari har oy yangilanish. Bu yerda qilinmagan tabiiy keyingi qadam — shu oraliqning ehtiyotkor kvantiliga (“80 % ishonchimiz komil boʻlgan yoz”) tayanish va qanchalik ehtiyotkor boʻlishni optimallashtirgichga tanlatish; hamda prognozni qish davomida yaxshilanib borishiga yoʻl qoʻyish, 6-mashq buni boshlaydi.
- Прогноз без качества — не прогноз. При качестве 0 прогноз каждый год говорит 40 млн м³, и правило снова смотрит только на запас: кривая начинается на оранжевой линии, в пределах шага сетки (6.8 против 6.7 млн м³ в год).
- Ценность растёт с качеством, но не пропорционально. Прогноз качества 0,5, на полпути к идеальному, даёт 17 % того, что даёт идеальный; прогноз качества 0,8 оставляет зимним полям дефицит 5.8 млн м³ в год против 5.5 у идеального и 6.7 без прогноза. Большая часть ценности лежит на последнем отрезке качества. Кривая колеблется, потому что ошибки прогноза — одна случайная выборка из ста лет; важна тенденция.
- Где находятся настоящие прогнозы. Статистические прогнозы, которые Apel et al. (2018) построили для рек Центральной Азии, объясняют 60–80 % межгодовой изменчивости стока за апрель–сентябрь при выпуске в январе и 68–97 % при выпуске 1 апреля — в дату, которая важнее всего для водного планирования региона. В наших терминах это качество примерно от 0,8 до 0,95: настоящие прогнозы находятся на правой части кривой, там, где и лежит ценность.
- Ценность прогноза зависит от решения, а не только от прогноза. Тот же прогноз ничего не стоил для летних решений этого водохранилища (мы проверяли: оператор узнаёт лето к маю, а вода, сбережённая в одном месяце, никуда не девается к следующему) и стоил 1.1 млн м³ в год для зимнего решения. Прогноз стоит столько, сколько может сделать с ним решение, которое он обслуживает, — мысль, высказанная для прогнозов погоды Murphy (1993), а для водохранилищ — Anghileri et al. (2016).
- Важно, что оператор делает с ошибочным прогнозом. Здесь правило верит прогнозу на слово, а оптимизатор компенсирует это выбором порога. Осторожный оператор мог бы учитывать лишь часть прогноза; упражнение 5 спрашивает, помогает ли это.
- Что добавляет настоящая прогностическая служба. Не одно число, а диапазон: ансамбль возможных лет или лучшую оценку с полосой ошибки, и обновления каждый месяц по мере накопления снега. Естественный следующий шаг, здесь не сделанный, — опираться на осторожный квантиль этого диапазона («лето, в котором мы уверены на 80 %») и дать оптимизатору выбрать меру осторожности; и позволить прогнозу уточняться по ходу зимы, с чего начинает упражнение 6.
In one sentence. A forecast that is sometimes wrong is still worth acting on, but the operator must act on it with a plan chosen for its quality, and the better the forecast, the more it is worth, with most of the value in the last stretch between a good forecast and a perfect one: which is exactly where the real forecasts of the region’s hydrometeorological services sit, and why their quality matters for a reservoir like this one.
Bir jumlada. Baʼzan xato qiladigan prognozga tayanish baribir arziydi, lekin operator unga uning sifatiga mos tanlangan reja bilan tayanishi kerak; prognoz qancha yaxshi boʻlsa, shuncha koʻp arziydi, qadrning koʻp qismi esa yaxshi prognoz bilan mukammal prognoz orasidagi oxirgi boʻlakda yotadi — aynan mintaqa gidrometeorologiya xizmatlarining haqiqiy prognozlari turgan joyda, va shuning uchun ularning sifati bunday ombor uchun muhim.
Одной фразой. На прогноз, который иногда ошибается, всё же стоит опираться, но оператор должен делать это по плану, выбранному под его качество; чем лучше прогноз, тем больше он стоит, и большая часть ценности лежит на последнем отрезке между хорошим прогнозом и идеальным — ровно там, где находятся настоящие прогнозы гидрометеорологических служб региона, и потому их качество важно для такого водохранилища.
16 From this toy to a real reservoir
- Real rules have the same knobs, many times over. The release rule of the Kattakurgan reservoir in the Zarafshan model of this repository (
ZRBReleaseRule) has a storage level below which releases are cut (v1) and a cut factor (buffer_coef): our trigger and ration, with a separate value for each month. That is why the full model needs the optimizer. - What this toy leaves out. A forecast that is updated as the winter goes on, and the uncertainty band that comes with it; more users with different priorities; how crops actually respond to a shortage; evaporation that depends on the lake’s surface area; return flows.
- One river is one river. The numbers here belong to this one synthetic series of a hundred years. Other series give other numbers. The shape (a bent curve from “no rationing” to “cut deep”) was the same for the three other series we tested.
- Haqiqiy qoidalarda xuddi shu dastaklar, lekin koʻp marta. Ushbu ombordagi Zarafshon modelidagi Kattaqoʻrgʻon suv omborining chiqarish qoidasida (
ZRBReleaseRule) chiqarish kamaytiriladigan suv sathi (v1) va kamaytirish koeffitsiyenti (buffer_coef) bor: bizning chegara va ulush, har oy uchun alohida qiymat bilan. Shuning uchun toʻliq modelga optimallashtirgich kerak. - Bu oʻyinchoq model nimani hisobga olmaydi. Qish davomida yangilanib boradigan prognoz va u bilan keladigan noaniqlik oraligʻi; turli ustuvorlikdagi yana koʻproq isteʼmolchi; ekinlar taqchillikka aslida qanday javob berishi; koʻl yuzasiga bogʻliq bugʻlanish; qaytuvchi oqimlar.
- Bir daryo — bir daryo. Bu yerdagi raqamlar yuz yillik shu bitta sunʼiy qatorga tegishli. Boshqa qatorlar boshqa raqamlarni beradi. Shakl (“cheklash yoʻq”dan “keskin kesish”gacha bukilgan egri chiziq) biz sinagan boshqa uchta qator uchun ham bir xil boʻldi.
- У настоящих правил те же рычаги, только много раз. В правиле сброса Каттакурганского водохранилища в модели Зарафшана из этого репозитория (
ZRBReleaseRule) есть уровень наполнения, ниже которого сброс урезается (v1), и коэффициент урезания (buffer_coef): наши порог и доля, с отдельным значением для каждого месяца. Поэтому полной модели нужен оптимизатор. - Чего эта игрушечная модель не учитывает. Прогноз, который уточняется по ходу зимы, и полосу неопределённости, которая к нему прилагается; больше водопользователей с разными приоритетами; как на самом деле культуры реагируют на дефицит; испарение, зависящее от площади зеркала; возвратные воды.
- Одна река — это одна река. Числа здесь относятся к этому единственному синтетическому ряду в сто лет. Другие ряды дают другие числа. Форма (изогнутая кривая от «без лимитирования» до «сильного урезания») была той же для трёх других рядов, которые мы проверили.
17 Exercises
- A bigger reservoir. In the settings cell at the top, set
capacity=60.0and run the notebook again. Does rationing still pay? Where is the bend now? - A different second goal. Replace “worst month” with “years short” in
objectives()and run the optimizer again. Which plans are unbeaten now? Is “no rationing” still one of them? - A different river. In the settings cell, set
seed=1and run the notebook again; then compare withsynthetic_inflows(regime=PAPER_REGIME), the paper’s river drawn with the same seed. Which numbers change, and what stays the same? - A seasonal rule. Give the rule two rations, one for early and one for late summer. Does the optimizer find plans that beat the two-knob curve?
- A cautious operator. In
WinterRelease, count only part of the forecast:node.storage + 0.7 * forecast. Re-run the skill curve. Does caution help with a weak forecast (skill 0.5)? Does it hurt with a strong one? - A late forecast. Real forecasts arrive in late winter, when the snow has fallen. Let the rule use the forecast only in the last two winter months (
month >= 4) and the storage alone before. How much of the forecast’s value survives?
- Kattaroq ombor. Yuqoridagi sozlamalar katagida
capacity=60.0qilib qoʻying va daftarni qayta ishga tushiring. Cheklash hali ham oʻzini oqlaydimi? Bukilish endi qayerda? - Boshqa ikkinchi maqsad.
objectives()da “eng yomon oy”ni “taqchil yillar” bilan almashtiring va optimallashtirgichni qayta ishga tushiring. Endi qaysi rejalar ustun kelib boʻlmaydigan? “Cheklash yoʻq” hali ham ular orasidami? - Boshqa daryo. Sozlamalar katagida
seed=1qilib qoʻying va daftarni qayta ishga tushiring; soʻng oʻsha urugʻ bilan chizilgan maqoladagi daryo,synthetic_inflows(regime=PAPER_REGIME), bilan taqqoslang. Qaysi raqamlar oʻzgaradi, nima oʻzgarmaydi? - Mavsumiy qoida. Qoidaga ikkita ulush bering: biri yoz boshi, biri yoz oxiri uchun. Optimallashtirgich ikki dastakli egri chiziqdan ustun keladigan rejalarni topadimi?
- Ehtiyotkor operator.
WinterReleaseda prognozning faqat bir qismini hisobga oling:node.storage + 0.7 * forecast. Sifat egri chizigʻini qayta hisoblang. Ehtiyotkorlik kuchsiz prognozda (sifat 0,5) yordam beradimi? Kuchli prognozda zarar keltiradimi? - Kech kelgan prognoz. Haqiqiy prognozlar qish oxirida, qor yogʻib boʻlgach keladi. Qoida prognozdan faqat qishning oxirgi ikki oyida (
month >= 4) foydalansin, undan oldin esa faqat ombordagi suvga qarasin. Prognoz qadrining qancha qismi saqlanib qoladi?
- Водохранилище побольше. В ячейке настроек в начале тетради задайте
capacity=60.0и запустите тетрадь заново. Окупается ли лимитирование по-прежнему? Где теперь изгиб? - Другая вторая цель. Замените «худший месяц» на «дефицитные годы» в
objectives()и снова запустите оптимизатор. Какие планы теперь непревзойдённые? Остаётся ли среди них «без лимитирования»? - Другая река. В ячейке настроек задайте
seed=1и запустите тетрадь заново; затем сравните сsynthetic_inflows(regime=PAPER_REGIME), рекой из статьи с тем же зерном. Какие числа меняются, а что остаётся прежним? - Сезонное правило. Дайте правилу две доли: одну для начала лета, другую для его конца. Найдёт ли оптимизатор планы, превосходящие кривую с двумя рычагами?
- Осторожный оператор. В
WinterReleaseучитывайте только часть прогноза:node.storage + 0.7 * forecast. Пересчитайте кривую качества. Помогает ли осторожность при слабом прогнозе (качество 0,5)? Вредит ли при сильном? - Поздний прогноз. Настоящие прогнозы приходят в конце зимы, когда снег уже выпал. Пусть правило использует прогноз только в последние два зимних месяца (
month >= 4), а до того смотрит только на запас. Какая часть ценности прогноза сохраняется?
How to do an exercise in Colab: the code of every cell is folded; click “Show code” to see and change it, then run the cells below it again. For exercise 1, change the number in the settings cell at the top and run the notebook from there.
Colab da mashqni qanday bajarish: har bir katakning kodi yigʻilgan; “Show code” ni bosib uni koʻring va oʻzgartiring, soʻng undan pastdagi kataklarni qayta ishga tushiring. 1-mashq uchun yuqoridagi sozlamalar katagidagi raqamni oʻzgartiring va daftarni shu yerdan qayta ishga tushiring.
Как выполнить упражнение в Colab: код каждой ячейки свёрнут; нажмите «Show code», чтобы увидеть и изменить его, а затем снова запустите ячейки ниже. Для упражнения 1 измените число в ячейке настроек в начале тетради и запустите тетрадь заново оттуда.
Sources
- Hashimoto, T., Stedinger, J. R., Loucks, D. P. (1982). Reliability, resiliency, and vulnerability criteria for water resource system performance evaluation. Water Resources Research 18(1), 14–20. The reservoir, its numbers (p. 17, Table 1; seasons swapped here) and the trade-off between failing often and failing badly (Fig. 9).
- Draper, A. J., Lund, J. R. (2004). Optimal hedging and carryover storage value. Journal of Water Resources Planning and Management 130(1), 83–87. When rationing pays (p. 83) and why a rule with two parameters is often enough (p. 87).
- Apel, H., Abdykerimova, Z., Agalhanova, M., Baimaganbetov, A., Gavrilenko, N., Gerlitz, L., Kalashnikova, O., Unger-Shayesteh, K., Vorogushyn, S., Gafurov, A. (2018). Statistical forecast of seasonal discharge in Central Asia using observational records: development of a generic linear modelling tool for operational water resource management. Hydrology and Earth System Sciences 22, 2225–2254. How the region’s hydrometeorological services forecast the April–September flow, and from what.
- Anghileri, D., Voisin, N., Castelletti, A., Pianosi, F., Nijssen, B., Lettenmaier, D. P. (2016). Value of long-term streamflow forecasts to reservoir operations for water supply in snow-dominated river catchments. Water Resources Research 52, 4209–4225. Forecast value measured against the perfect forecast, for a reservoir.
- Murphy, A. H. (1993). What is a good forecast? An essay on the nature of goodness in weather forecasting. Weather and Forecasting 8, 281–293. The distinction between a forecast’s quality and its value to a decision.
- Deb, K., Pratap, A., Agarwal, S., Meyarivan, T. (2002). A fast and elitist multiobjective genetic algorithm: NSGA-II. IEEE Transactions on Evolutionary Computation 6(2), 182–197. The search method behind
taqsim.optimize.