How Much Should a Nation Invest in Irrigation and Grain Reserves to Maximize Its Population in 30 Years?
Rainfall changes from year to year, and when a drought hits, agricultural output drops sharply. If we plan only around the average harvest, one bad stretch of weather can set a population back for years. In this post we build a small model that puts two kinds of preparation on the table at once:
- Irrigation investment, which permanently reduces how much a drought hurts the harvest.
- Grain reserves, which are consumed in lean years but lose some value to spoilage every year.
We then use Monte Carlo simulation to find the policy that maximizes the expected population after 30 years.
1. The Model
Rainfall. The rainfall index in year $t$ follows a Gamma distribution with mean 1 and coefficient of variation $\mathrm{CV}=0.45$:
$$
R_t \sim \mathrm{Gamma}\left(k,\ \tfrac{1}{k}\right),\qquad k=\frac{1}{\mathrm{CV}^2}
$$
Harvest. Let $I_t\in[0,1]$ be the irrigation coverage. Irrigation covers a fraction $\varepsilon$ of the rainfall shortfall, and keeping it running costs $u$ per unit of coverage:
$$
y_t = \min(1,R_t) + \bigl(1-\min(1,R_t)\bigr),\varepsilon, I_t,\qquad
F_t = F_{\max}, y_t - u, I_t
$$
Surplus and deficit. With population $P_t$ (food demand is one unit per person):
$$
\text{Surplus}_t=\max(F_t-P_t,,0),\qquad \text{Deficit}_t=\max(P_t-F_t,,0)
$$
Policy. Two parameters define the policy:
- $a\in[0,1]$ is the share of the surplus invested in irrigation.
- $s\ge 0$ is the target reserve, measured in years of consumption.
Irrigation dynamics. Here $C$ is the cost of covering the whole country:
$$
I_{t+1}=\min!\left(1,\ I_t+\frac{\mathrm{Inv}_t}{C}\right),\qquad \mathrm{Inv}_t=\min\bigl(a\cdot \text{Surplus}_t,\ (1-I_t),C\bigr)
$$
Reserve dynamics. In a deficit year the reserve is drawn down first. Surplus left after the irrigation investment is stored up to the target $sP_t$. A spoilage rate $\delta$ is applied each year:
$$
S_{t+1}=(1-\delta)\Bigl(S_t-\text{Draw}_t+\text{Store}_t\Bigr)
$$
Population growth. If a shortfall remains after drawing on the reserve, the population shrinks in proportion to the unmet demand. Otherwise it grows at up to $g$, depending on how much food is left over:
$$
P_{t+1}=P_t,(1+r_t),\qquad
r_t=
\begin{cases}
-m,\dfrac{\text{Shortage}_t}{P_t} & \text{if Shortage}_t>0\[3mm]
g\cdot\min!\left(1,\ \dfrac{\text{Leftover}_t}{0.1,P_t}\right) & \text{otherwise}
\end{cases}
$$
Objective. We search for the policy that maximizes the expected population at year 30:
$$
\max_{a,,s}\ \ \mathbb{E}\bigl[P_{30}\bigr]
$$
The trade-off is that surplus spent on irrigation or stored in the reserve is surplus that cannot go toward population growth today. The right balance between the two is what we want to find.
2. Full Source Code
The search uses 3,000 rainfall scenarios, and the chosen policy is then evaluated on 5,000 fresh scenarios it has never seen.
1 | import numpy as np |
3. Code Walkthrough
Section 1: Parameters
| Symbol | Variable | Value | Meaning |
|---|---|---|---|
| $T$ | T |
30 | planning horizon in years |
| $P_0$ | P0 |
100 | initial population (millions) |
| $F_{\max}$ | F_MAX |
130 | food output under perfect rainfall, which acts as a carrying capacity |
| $g$ | G |
0.025 | maximum annual growth rate |
| $m$ | M |
0.5 | how strongly shortages shrink the population |
| $\delta$ | SPOIL |
0.05 | annual spoilage of stored grain |
| $C$ | COST_FULL |
350 | cost of irrigating the entire country |
| $\varepsilon$ | EFF |
0.5 | share of the rainfall shortfall that full irrigation can offset |
| $u$ | UPKEEP |
2.5 | annual upkeep per unit of irrigation coverage |
| CV | CV |
0.45 | year-to-year variability of rainfall |
make_rain draws a matrix of shape (scenarios, years) from a Gamma distribution. Rainfall cannot be negative, and the Gamma distribution has a long tail on the wet side and a fat mass on the dry side. With the shape parameter $k=1/\mathrm{CV}^2$ and scale $1/k$, the mean is exactly 1.
Section 2: The Simulator
The key design choice is vectorization. P, I and S are arrays with one element per scenario, so a single pass over the 30 years advances thousands of scenarios at once through NumPy operations. A plain Python loop over scenarios would call the inner year-by-year logic millions of times across the policy grid. With vectorization, the whole grid search finishes in a couple of seconds, so no further speed-up is needed.
Inside the yearly loop, the order of events matters:
- Harvest.
rris the capped rainfall index $\min(1,R_t)$. The harvest isrr + (1 - rr) * EFF * I, which is exactly $y_t$ above, andprodsubtracts the irrigation upkeep. - Surplus or deficit. Exactly one of
surplusanddeficitis positive in any year. - Draw the reserve. In a deficit year,
drawmoves grain from the reserve to cover the gap. - Invest in irrigation. A share
aof the surplus goes toinv, capped so coverage never exceeds 100%. - Store grain.
roomis the gap between the target reserve $sP_t$ and the current reserve, andstorefills it from the remaining surplus. - Population update. Anything still unmet after the reserve is the
shortage, which drives a decline. Otherwise, growth is proportional to theleftoverfood, saturating at $g$ once the leftover reaches 10% of the population. - Spoilage. The reserve shrinks by the factor $1-\delta$.
When track=True, the function also records the full history of population, irrigation and reserves, which we need for the time-series plots.
Section 3: Grid Search
We evaluate every combination of 21 irrigation shares $a$ and 17 reserve targets $s$, which is 357 policies. Each is scored by the mean final population over the 3,000 training scenarios and stored in the matrix Z. np.argmax followed by np.unravel_index recovers the best pair of grid indices.
Section 4: Out-of-Sample Evaluation
Picking the winner on the same scenarios used to score it would flatter the policy. So the chosen policy is re-run on 5,000 new scenarios generated with a different seed. We compare four cases: no preparation, irrigation only, stockpile only, and the optimal combination. For each case we report the mean, the 5th percentile (a bad-luck measure), the median, and the share of scenarios in which the population falls below 50 million, which we call a collapse.
Section 5: Graphs
All six panels are drawn into a single figure through GridSpec, so one plt.show() produces everything.
4. Results
Console Output
=== Settings === Horizon: 30 years, scenarios (search/test): 3000/5000 Drought probability per year (rain < 0.7): 0.275 Grid search: 357 policies in 1.90 s === Optimal policy === Share of surplus invested in irrigation a* = 0.80 Target reserve s* = 0.250 years of consumption === Population after 30 years (millions, test scenarios) === Policy Mean 5th pct Median Collapse<50 No preparation (a=0, s=0) 72.5 50.7 72.0 4.4% Irrigation only 103.4 91.2 103.3 0.0% Stockpile only 86.4 63.2 86.3 0.3% Optimal combination 108.5 97.2 108.8 0.0% Gain of optimal policy over no preparation: +49.6% Mean irrigation coverage at year 30: 100.0% Mean reserve at year 30: 20.3 million food units
Graphs

5. Reading the Results
The comparison table
In a run of this model, the drought probability per year (rainfall index below 0.7) came out at roughly 27%, which is more than one year in four. Under those conditions the picture was clear:
- No preparation left the population at about 72 million on average, down from the initial 100 million, and roughly 4% of scenarios collapsed below 50 million.
- Stockpile only raised the average to about 86 million. Reserves smooth out single bad years but cannot fix a structural problem: when droughts come back to back, the reserve runs dry and spoilage eats into it the whole time.
- Irrigation only did better, at about 103 million, because it permanently raises the harvest in every dry year rather than only compensating after the fact.
- The optimal combination reached about 108 million on average, roughly a 50% improvement over doing nothing, and it also had the best 5th percentile. Preparation does not just raise the average, it lifts the floor.
The optimum found by the search was a high irrigation share (about 80% of surplus) together with a modest reserve of a quarter of a year of consumption. Irrigation does the heavy lifting, and a small reserve covers the gaps while irrigation is still being built up and the shocks that irrigation cannot absorb.
Graph (1): Rainfall distribution
The histogram shows the right-skewed rainfall index with the red drought threshold at 0.7. The large mass to the left of the line is the reason this problem is hard: bad years are common, not rare events.
Graph (2): 3D surface of expected population
The horizontal axes are the reserve target $s$ and the irrigation share $a$, and the vertical axis is the expected population at year 30. Moving along the $a$ axis produces a steep rise, which shows how valuable irrigation is. Moving along the $s$ axis gives a much gentler hill that falls off at large $s$, because holding too much grain costs spoilage and growth. The red point marks the optimum.
Graph (3): Heat map
The same surface seen from above. The bright region is a broad plateau along high irrigation shares with small reserves. The plateau is wide, which means the policy is forgiving: you do not need to hit the exact optimum to capture most of the benefit.
Graph (4): Population paths
The median and the 5th-to-95th percentile band for both policies. Without preparation, the band widens over time and its lower edge drops, showing how one unlucky run of droughts can drive a population down. Under the optimal policy the whole band sits higher and is narrower, so the outcome is both better and more predictable.
Graph (5): Distribution of final population
The gray histogram (no preparation) is wide and has a long left tail, which is the collapse risk. The red histogram (optimal policy) is shifted right and concentrated. Shrinking that left tail is the real prize of drought preparation.
Graph (6): Irrigation coverage and reserve over time
Irrigation coverage climbs steadily and approaches 100% as surplus is converted into infrastructure, while the average reserve rises early and then levels off at its target. This confirms the strategy: build irrigation first, and keep a modest buffer for the shocks that remain.
6. Summary
- Planning for the average year is not enough when rainfall varies strongly, because the population is hurt more by bad stretches than it gains from good ones.
- Irrigation investment changes the structure of the system, while grain reserves only buffer it. In this example irrigation was the more valuable tool.
- The best policy combined both, and its advantage showed up most strongly in the worst-case outcomes.
- Vectorizing the simulation over scenarios makes it practical to search hundreds of policies in seconds, and testing the winner on fresh scenarios keeps the comparison honest.
Natural extensions include making the irrigation share depend on the current reserve, adding a cost for the reserve itself, or replacing the grid search with a dynamic-programming or reinforcement-learning approach to find a policy that reacts to the state of the system each year.









