演示平台参数敏感性分析(蜘蛛图)

2025-12-25 18:57
47

演示平台参数敏感性分析(蜘蛛图)

下面给出“参数敏感性分析(蜘蛛图)”的完整教学模块,本科零门槛使用:

  • • 学生勾选 5 个作物参数 → 平台自动做局部分析(Local Sensitivity)

  • • 输出:蜘蛛图(Radar Chart) + 柱状图

  • • 算法:OAT(One-At-a-Time)±10% 扰动 → 计算标准化敏感度

  • • 全程 点击即运行,无需写代码


✅ 前端页面(templates/sensitivity.html)

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>参数敏感性分析</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    body { font-family: Arial; margin: 40px; background: #f9f9f9; }
    .container { max-width: 900px; margin: auto; background: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
    h1 { text-align: center; color: #2e7d32; }
    .chk-group { display: flex; flex-wrap: wrap; gap: 15px; margin: 20px 0; }
    .chk-group label { background: #e8f5e9; padding: 8px 12px; border-radius: 4px; cursor: pointer; }
    .chk-group input { margin-right: 6px; }
    button { padding: 10px 20px; background: #2e7d32; color: white; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { background: #1b5e20; }
    .chart-container { margin-top: 40px; display: flex; gap: 40px; flex-wrap: wrap; justify-content: center; }
    canvas { max-width: 400px; max-height: 400px; }
  </style>
</head>
<body>
  <div class="container">
    <h1>🕷️ 参数敏感性分析 - 蜘蛛图</h1>

    <div class="chk-group">
      <label><input type="checkbox" value="MAXLUE" checked> MAXLUE</label>
      <label><input type="checkbox" value="SPAN" checked> SPAN</label>
      <label><input type="checkbox" value="SLA" checked> SLA</label>
      <label><input type="checkbox" value="AMAXTB" > AMAXTB</label>
      <label><input type="checkbox" value="TMNFTB" > TMNFTB</label>
    </div>

    <button onclick="runSensitivity()">开始分析</button>

    <div class="chart-container">
      <canvas id="radar"></canvas>
      <canvas id="bar"></canvas>
    </div>
  </div>

  <script>
    const radarCtx = document.getElementById('radar').getContext('2d');
    const barCtx = document.getContext('2d', 'bar');

    let radarChart = new Chart(radarCtx, {
      type: 'radar',
      data: { labels: [], datasets: [] },
      options: { responsive: true, plugins: { title: { display: true, text: '参数敏感性(蜘蛛图)' } } }
    });

    let barChart = new Chart(barCtx, {
      type: 'bar',
      data: { labels: [], datasets: [] },
      options: { responsive: true, plugins: { title: { display: true, text: '敏感度柱状图' } } }
    });

    async function runSensitivity() {
      const checks = Array.from(document.querySelectorAll('input[type=checkbox]:checked')).map(cb => cb.value);
      if (checks.length === 0) { alert('至少选择一个参数'); return; }

      const response = await fetch('/sensitivity', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ params: checks })
      });

      const data = await response.json();

      // 蜘蛛图
      radarChart.data.labels = data.params;
      radarChart.data.datasets = [{
        label: '标准化敏感度',
        data: data.sensitivity,
        backgroundColor: 'rgba(46,125,50,0.2)',
        borderColor: 'rgba(46,125,50,1)',
        pointBackgroundColor: 'rgba(46,125,50,1)'
      }];
      radarChart.update();

      // 柱状图
      barChart.data.labels = data.params;
      barChart.data.datasets = [{
        label: '敏感度',
        data: data.sensitivity,
        backgroundColor: 'rgba(33,150,243,0.7)'
      }];
      barChart.update();
    }
  </script>
</body>
</html>

✅ 后端核心(app.py 新增)

import numpy as np
from flask import Blueprint, request, jsonify
from pcse.fileinput import YAMLCropDataProvider
from pcse.base import ParameterProvider
from pcse.db import NASAPowerWeatherDataProvider
from pcse.models import Wofost71_WLP_FD
from pcse.engine import Engine
import pandas as pd
import datetime
import copy

sen_bp = Blueprint('sensitivity', __name__, url_prefix='/')

@sen_bp.route('/sensitivity')
def sen_page():
    return render_template('sensitivity.html')

# ---------- 局部分析 ----------
@sen_bp.route('/sensitivity', methods=['POST'])
def sensitivity():
    selected = request.json['params']   # 列表
    base_crop = {
        'VARIETY_NAME': 'SenWheat',
        'CropType': 'wheat',
        'MAXLUE': 2.5,
        'SPAN': 30,
        'SLA': 20,
        'AMAXTB': [0, 1.0, 1.5, 1.0],
        'TMNFTB': [0, 0.0, 1, 0.0]
    }

    def run_one(crop_dict):
        cropd = YAMLCropDataProvider(crop_dict=crop_dict)
        weather = NASAPowerWeatherDataProvider(latitude=52.0, longitude=5.0)
        params = ParameterProvider(cropdata=cropd, soildata=None, sitedata=None)
        wofost = Wofost71_WLP_FD(params, weather, [])
        eng = Engine(wofost)
        eng.run_till_terminate()
        df = pd.DataFrame(eng.get_output())
        return df['LAI'].max()   # 敏感指标:最大 LAI

    base_lai = run_one(base_crop)
    sensitivity = []

    for p in selected:
        plus = copy.deepcopy(base_crop)
        minus = copy.deepcopy(base_crop)
        if isinstance(base_crop[p], list):
            # 对列表类参数整体浮动 10%
            plus[p] = [v * 1.1 if isinstance(v, float) else v for v in base_crop[p]]
            minus[p] = [v * 0.9 if isinstance(v, float) else v for v in base_crop[p]]
        else:
            plus[p] = base_crop[p] * 1.1
            minus[p] = base_crop[p] * 0.9

        plus_lai = run_one(plus)
        minus_lai = run_one(minus)
        s = abs((plus_lai - minus_lai) / (0.2 * base_lai))
        sensitivity.append(round(s, 3))

    return jsonify({"params": selected, "sensitivity": sensitivity})

✅ 使用流程(学生)

  1. 1. 打开:http://localhost:5000/sensitivity

  2. 2. 勾选想分析的参数(默认 3 个)

  3. 3. 点击“开始分析”

  4. 4. 等待 10–20 秒 → 看到:

    • • 蜘蛛图(雷达)

    • • 柱状图(敏感度数值)



✅ 教学提示(可打印)

图表如何理解
蜘蛛图离圆心越远 → 参数越敏感
柱状图数值越高 → 对输出影响越大

✅ 后续可拓展

  • • ➕ 支持多输出指标(LAI、产量、水分利用效率)

  • • ➕ 全局敏感性(Sobol,需更多算力)

  • • ➕ 下载敏感度报告(PDF)


✅ 下一步?

你想继续:

  • • ✅ 多指标蜘蛛图(LAI + 产量 + 水分)

  • • ✅ 下载 PDF 敏感性报告

  • • ✅ 教师后台(查看全班敏感度热力图)

我可以直接给你完整代码!