File size: 9,430 Bytes
8f8a793 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | <!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>模块1 · Forcing 驱动数据</title>
<link rel="stylesheet" href="style.css"/>
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
<script src="https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js"></script>
<script src="data.js"></script>
</head>
<body>
<div class="wrap">
<h1>模块 1 · Forcing(驱动数据)</h1>
<p class="sub">forcing = 逐时间步喂给模型的气象输入(降水/PET/温度)。模型再好,输入不对必错(garbage in, garbage out)。</p>
<h2>真实数据:Hesperange 三要素</h2>
<div class="card">
<p class="sub">拖动下面窗口滑块看不同时段。注意 PET 的季节循环、流量冬春涨夏季退。</p>
<div class="row">
<label>起始天 <span id="d0v">0</span></label><input id="d0" type="range" min="0" max="1125" step="1" value="0"/>
<label>结束天 <span id="d1v">1126</span></label><input id="d1" type="range" min="1" max="1126" step="1" value="1126"/>
</div>
<div id="forc" class="plot" style="height:400px"></div>
</div>
<h2>方法菜单:空间聚合(点雨量站 → 流域面平均)— 可切换 & 可改代码</h2>
<div class="card">
<table>
<tr><th>版本</th><th>思想</th><th>谁用</th><th>可微性</th></tr>
<tr><td>arithmetic mean</td><td>算术平均</td><td>简单集总</td><td>线性→可微</td></tr>
<tr><td><b>Thiessen (1911)</b></td><td>面积加权(最近邻多边形)</td><td><b>HBV 等经典集总</b></td><td>线性→可微</td></tr>
<tr><td>IDW</td><td>反距离加权 1/d^p</td><td>插值/半分布预处理</td><td>线性→可微</td></tr>
<tr><td>gridded 网格</td><td>雷达MRMS/卫星GPM 逐格、不面平均</td><td><b>CREST/EF5</b> <span class="crest-tag">CREST</span></td><td>模块8(分布式)</td></tr>
</table>
<p class="status">注意:CREST 的选择(gridded)属于分布式阶段,本页先对比三种"面平均"法。Hesperange 数据已是面平均,所以用<b>合成 3 站</b>(对真实序列加偏差+噪声)演示——合成部分已在代码里标明。</p>
<div id="tabs"></div>
<textarea id="code" class="code" spellcheck="false"></textarea>
<div class="row" style="margin-top:10px">
<label>IDW 幂次 p = <span id="pv">2.0</span></label>
<input id="pw" type="range" min="0.5" max="4.0" step="0.1" value="2.0"/>
<label>站1权重因子(Thiessen面积) <span id="a1v">120</span> km²</label>
<input id="a1" type="range" min="20" max="220" step="5" value="120"/>
<button id="run">运行 Run</button>
<button id="reset" class="ghost">重置代码</button>
<span id="status" class="status"></span>
</div>
<div id="plot" class="plot" style="height:330px;margin-top:10px"></div>
<div id="readout" class="readout"></div>
<div id="err"></div>
</div>
<h2>术语 Glossary</h2>
<div class="card">
<details class="gl" open><summary>流域面平均降水 · Catchment-average precipitation</summary><div class="body">
<div class="en">Aggregating point gauges into one basin-representative series — the standard input of lumped models.</div>
<div class="zh">把点雨量站汇成一个代表全流域的降水序列,集总模型的标准输入。</div></div></details>
<details class="gl"><summary>Thiessen 多边形 · Thiessen polygon (1911)</summary><div class="body">
<div class="en">Weight each gauge by the area for which it is the nearest gauge (its polygon).</div>
<div class="zh">按"每站最近邻控制的面积"加权求面平均;经典、简单、常用于业务集总模型。</div></div></details>
<details class="gl"><summary>IDW · Inverse-distance weighting</summary><div class="body">
<div class="en">Weights ∝ 1/distance^p to a target point (e.g. centroid); p controls how fast influence decays.</div>
<div class="zh">权重 ∝ 1/距离^p;p 越大近站影响越强。上面的滑块就是 p。</div></div></details>
<details class="gl"><summary>网格降水 · Gridded precipitation (CREST 的选择)</summary><div class="body">
<div class="en">Per-cell precipitation fields from radar (MRMS) or satellite (TRMM/GPM); CREST/EF5 ingests these directly per grid cell — no averaging.</div>
<div class="zh">雷达/卫星反演的逐网格降水场;CREST/EF5 逐格直接吃、不做面平均(见模块8 分布式)。</div></div></details>
<details class="gl"><summary>率定/验证划分 · Calibration / Validation split & 暖机 Warm-up</summary><div class="body">
<div class="en">Tune on one period, test on an independent one; discard an initial warm-up while states converge.</div>
<div class="zh">一段调参、另一段独立检验防过拟合;开头暖机期让状态收敛、评估时丢弃。本课划分:113/676/338 天。</div></div></details>
</div>
<div class="next"><a href="m00_overview.html">← 0 水量平衡</a><a href="m02_pet.html">下一模块:2 PET →</a></div>
<div class="foot">data: TU Delft CIE4431 Hesperange · anchor: CREST/EF5</div>
</div>
<script src="shared.js"></script>
<script>
renderNav("m01_forcing.html");
const DAYS=[...Array(PRECIP.length).keys()];
/* forcing viewer */
function drawForcing(){
const a=parseInt(document.getElementById("d0").value), b=Math.max(a+1,parseInt(document.getElementById("d1").value));
const xs=DAYS.slice(a,b);
Plotly.react("forc",[
{x:xs,y:PRECIP.slice(a,b),type:"bar",name:"precip",marker:{color:"#2563eb"},yaxis:"y"},
{x:xs,y:PET.slice(a,b),type:"scatter",mode:"lines",name:"PET",line:{color:"#ea580c",width:1},yaxis:"y2"},
{x:xs,y:QOBS.slice(a,b),type:"scatter",mode:"lines",name:"Q_obs",line:{color:"#16a34a",width:1.2},yaxis:"y3"},
],{margin:{t:8,r:10,b:38,l:55},showlegend:false,
xaxis:{anchor:"y3",title:"day"},
yaxis:{domain:[0.7,1],title:"P",autorange:"reversed"},
yaxis2:{domain:[0.36,0.66],title:"PET"},
yaxis3:{domain:[0,0.32],title:"Q_obs"}},{responsive:true,displayModeBar:false});
}
bindSlider("d0","d0v",v=>v.toFixed(0),drawForcing);
bindSlider("d1","d1v",v=>v.toFixed(0),drawForcing);
drawForcing();
/* aggregation versions: each tab loads an editable python snippet */
const SNIPPETS={
mean:`# V1 arithmetic mean -- USED_BY: simple lumped setups
def aggregate(gauges, areas, dists, p):
"""gauges: (T,3) synthetic stations. Return catchment-average (T,)."""
return np.nanmean(gauges, axis=1)`,
thiessen:`# V2 Thiessen (1911) area-weighted -- USED_BY: HBV-era lumped (classic choice)
def aggregate(gauges, areas, dists, p):
w = areas / areas.sum() # polygon-area weights
return np.nansum(gauges * w[None, :], axis=1)`,
idw:`# V3 inverse-distance weighting -- USED_BY: interpolation / semi-distributed pre-processing
def aggregate(gauges, areas, dists, p):
w = 1.0 / dists**p # closer gauge -> larger weight
w = w / w.sum()
return np.nansum(gauges * w[None, :], axis=1)`};
const codeEl=document.getElementById("code");
codeEl.value=SNIPPETS.thiessen; // start on the classic
let runFn=null;
const getTab=makeTabs("tabs",[
{key:"mean",label:"V1 mean"},
{key:"thiessen",label:"V2 Thiessen (HBV)"},
{key:"idw",label:"V3 IDW"},
],k=>{codeEl.value=SNIPPETS[k]; if(runFn)runFn();});
bindSlider("pw","pv",v=>v.toFixed(1),()=>runFn&&runFn());
bindSlider("a1","a1v",v=>v.toFixed(0),()=>runFn&&runFn());
runFn=makeRunner({
codeEl, runBtn:document.getElementById("run"), resetBtn:document.getElementById("reset"),
statusEl:document.getElementById("status"), errEl:document.getElementById("err"),
params:()=>({p:parseFloat(document.getElementById("pw").value),
a1:parseFloat(document.getElementById("a1").value)}),
buildScript:(code,prm)=>`
import numpy as np
P = np.array(${JSON.stringify(PRECIP)})
rng = np.random.default_rng(0)
# SYNTHETIC 3 gauges: bias factors x noise around the true catchment-average
factors = np.array([1.15, 0.9, 0.95])
gauges = np.clip(P[:,None]*factors[None,:]*(1+0.1*rng.standard_normal((len(P),3))), 0, None)
areas = np.array([${prm.a1}, 90.0, 82.0]) # Thiessen polygon areas (km2) - slider controls station 1
dists = np.array([3.0, 5.0, 8.0]) # distance to centroid (km)
${code}
avg = np.asarray(aggregate(gauges, areas, dists, ${prm.p}), float)
yrs = len(P)/365.25
__out = {"avg": avg.tolist(), "annual": float(avg.sum()/yrs), "true_annual": float(P.sum()/yrs)}
`,
onResult:(r,prm,ms)=>{
const a=150,b=260, xs=DAYS.slice(a,b);
linePlot("plot",[
{x:xs,y:r.avg.slice(a,b),type:"scatter",mode:"lines",name:"aggregated",line:{color:"#2563eb",width:1.4}},
{x:xs,y:PRECIP.slice(a,b),type:"scatter",mode:"lines",name:"true catchment-avg",line:{color:"#0f172a",width:1.2,dash:"dash"}},
],"precip (mm/d)","day (zoom 150-260)");
document.getElementById("readout").innerHTML=
`年降水=<b>${r.annual.toFixed(0)} mm/yr</b>(真值 ${r.true_annual.toFixed(0)}) | `+
`偏差 <b>${(100*(r.annual-r.true_annual)/r.true_annual).toFixed(1)}%</b> | 运行 <b>${ms.toFixed(0)} ms</b><br>`+
`试试:切换版本看年降水如何变化;拖站1面积/IDW幂次;或直接改代码里的权重公式。`;
}
});
</script>
</body>
</html>
|