ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AquaCrop-OSPy模型输入设置全解析:从气象数据到作物参数

AquaCrop-OSPy模型输入设置全解析:从气象数据到作物参数 1. 模型整体输入框架1.1 AquaCrop-OSPy的输入文件体系先把话说在前面AquaCrop-OSPy这个库其实是用Python重新实现的一版AquaCrop模型。它保留了FAO原版AquaCrop的作物水分生产力模拟逻辑但把所有计算过程搬到了Python生态里好处是不用再依赖原版的Windows编译环境可以跟数据处理、参数优化、批量模拟这些流程无缝衔接。很多做气候变化影响评估、区域作物产量预测、灌溉策略优化的同行都是拿它当核心模拟引擎来用的。我写这篇笔记的契机是因为之前跑AquaCrop-OSPy时发现官方文档里对模型输入设置的描述非常零散例子代码也不够完整尤其是从旧版迁移到新版之后输入参数的结构变了不少一上来就报错的情况特别多。折腾了几轮之后我决定把自己踩过的坑和最终能跑通的配置方式整理出来这篇就是第二篇专门讲模型输入设置。先看整个输入体系。AquaCrop-OSPy的输入不是单一文件而是一组分散在几个文件夹里的文本文件。模型通过一个主配置文件一般叫SIMUAL或SIMUL开头把这些文件串起来。用官方老版本的结构举例一个典型的项目目录长这样project_folder/ ├── LIST ├── SIMUAL ├── CLIMATE │ ├── weather.CLI │ ├── CO2.C02 │ └── temperature.CAL ├── CROP │ └── tomato.CRO ├── CROPPING │ └── cropping.CRP ├── IRRIGATION │ ├── irrigation.IRR │ └── management.MAN ├── SOIL │ └── soil.SOI ├── INITIAL │ └── initial.INI └── OUTPUT └── (输出文件自动生成)这套结构是原版AquaCrop迁移过来的思路每个文件负责一类输入。新版AquaCrop-OSPy也就是v1.x以后已经逐步转向了Python字典、参数对象或者NetCDF输入的方式但很多老教程和现有项目仍然在用这套文件结构。我的建议是不要急着完全抛弃旧格式因为你做参数标定、跑批量模拟时旧格式反而更好追踪和修改。核心配置文件之间的引用关系是模型输入设置的底层骨架。1.2 输入设置前的基础准备在动手写任何文件之前有件事必须先做确认你安装的版本。AquaCrop-OSPy的接口变化很频繁我在GitHub上看到的信息2023年之前装的版本和2024年之后装的版本参数命名方式就有差异。推荐直接用下面的方式查看版本pip show aquacrop-ospy或者进入Python环境里看import aquacrop print(aquacrop.__version__)我实测碰到过一种情况同事安装的版本是1.0.0我的是2.0.0结果他写的输入字典在我这边直接报KeyError。原因是新版把Weather、Crop这些类合并到aquacrop.entities命名空间下了导入方式都不一样。所以下面这些内容我都以当前比较稳定的新版API为基准同时标注旧版兼容情况。另一个准备工作是搞清楚你的模拟目标。同一个模型你要做的是站点尺度单季模拟还是区域尺度多年连续模拟输入设置是完全不同的。站点尺度只需要一个气象站数据区域尺度则要考虑空间格点数据和NetCDF输入。尽量先明确目标再设置输入否则后面返工成本很高。2. 气象数据与气候变化场景设置2.1 气象数据格式与来源AquaCrop-OSPy最基础的气象输入是逐日气象数据至少需要以下变量最高气温°C最低气温°C降水量mm参考蒸散量ETomm/day大气CO2浓度ppm其中ETo不是直接观测值而是用FAO Penman-Monteith公式计算出来的。这一点太关键了我见过不止一个用户把实际蒸发量当成ETo输入导致模拟产量直接崩掉。AquaCrop-OSPy里ETo的单位是mm/day如果用的是小时尺度数据需要先做日尺度合成。气象数据放进去的常见做法有两种一是写在一个.CLI文件里二是直接用Python字典或Weather对象传参。我推荐第二种因为更方便做批量模拟和敏感性分析。看这个例子from aquacrop import Weather weather_data { date: 2000-01-01, tmin: 12.5, tmax: 26.3, precip: 0.0, eto: 3.8, co2: 369.0, } weather Weather(weather_data)这里有个细节date字段必须是字符串且格式为YYYY-MM-DD不能是时间戳对象。co2的默认值是350但如果你研究的是未来气候变化情景必须在输入的每个时段都带上对应的CO2浓度否则模型会把CO2水平固定成默认值温室效应导致的产量提升效应就模拟不出来。2.2 未来气候场景下的气象输入做气候变化影响评估的人通常需要把GCM全球气候模式的输出降尺度到站点生成未来的逐日气象序列。这时输入的就不是单个年份的数据而是一段连续多年序列。AquaCrop-OSPy支持以data字段输入多行数据weather_data [ {date: 2040-01-01, tmin: 14.0, tmax: 28.1, precip: 0.2, eto: 4.0, co2: 450.0}, {date: 2040-01-02, tmin: 14.2, tmax: 28.5, precip: 0.0, eto: 4.1, co2: 450.0}, # ...... 继续往下 ]但要注意AquaCrop-OSPy默认要求气象数据必须是完整连续的逐日序列中间不能有缺测日。如果GCM输出有闰年问题你需要先做插值或者补齐。我自己习惯用一个小的Python函数做天气数据质量检查import pandas as pd def check_weather(df): date_range pd.date_range(startdf[date].min(), enddf[date].max(), freqD) missing date_range.difference(pd.to_datetime(df[date])) if len(missing) 0: print(f缺失日期数: {len(missing)}) print(missing[:10]) return len(missing)运行这个函数你会发现很多气候模式数据在闰年2月29日处理上会出问题。AquaCrop-OSPy内部是按公历逐日推进的缺一天就会导致后续所有累计值错位这是我看过最常见的输入错误来源。另外提一句ETo的未来数据怎么获取一般有两种途径一是直接用GCM输出的温度、风速、辐射、湿度重算FAO Penman-Monteith二是用历史时期的ETo加上温度变化修正。我建议用第一种因为AquaCrop-OSPy对ETo的敏感性极高偷懒用delta方法很容易得到偏差很大的产量结果。如果你手里只有月平均ETo可以转换成日值但必须保持月总量一致。3. 作物参数与品种级别参数设置3.1 作物参数文件解析作物参数是AquaCrop-OSPy输入里最能体现模型灵魂的部分。AquaCrop继承了原版的理念把作物分成了“蒸腾驱动型”和“产量形成型”两部分来模拟涉及的关键参数可以归纳成几张表。先看一份典型的CROP文件结构这是一个番茄作物的示例片段Crop type : Tomato Calendar type : 1 Sowing date : 60 Harvest date : 210 Max root depth (m) : 1.0 Base temperature (degC) : 10.0 Cutoff temperature (degC) : 30.0 Kc_tr (transpiration coefficient) : 1.1实际在Python里这些参数大多被封装进Crop类中from aquacrop import Crop crop Crop( nameTomato, planting_date03-15, harvest_date10-15, max_root_depth1.0, base_temp10.0, cutoff_temp30.0, kc_tr1.1, )需要注意planting_date支持两种格式一种是03-15表示每年固定日期另一种是整型数字60表示从年初算起的第60天。这两种格式在不同版本里混用容易出错我的建议是统一用字符串日期格式这样代码可读性更好。作物的关键参数可以分为三组温度响应参数base_temp、cutoff_temp直接决定生育期长度和光合作用速率。根系生长参数max_root_depth、root_shape影响土壤水分吸收深度。产量形成参数harvest_index、reference_harvest_index、water_stress_coeff这些参数决定最终产量计算。如果是在做区域作物模拟作物参数还跟品种类型密切相关。比如玉米有不同的熟期类型base_temp和生育期长度差异很大设置错了会导致抽雄期和灌浆期错位模拟产量自然不可靠。3.2 品种参数的标定思路这里必须多说一句AquaCrop-OSPy自带的参数值来自FAO在多个站点标定的典型值代表“平均状况”不一定适合你所在区域的品种。在做模型输入设置时一定要花时间做品种参数标定。标定的核心参数是harvest_index和max_root_depth因为这两个对产量和水分胁迫响应影响最大。具体步骤一般是收集目标品种的多年田间观测数据包括出苗期、开花期、成熟期、最终产量。先固定气象和土壤输入只调作物参数。用模型自带默认值跑一次看模拟生育期长度和实测差多少天。如果偏长适当调高base_temp如果偏短调低base_temp。产量偏差大时先调harvest_index再调kc_tr。我这几年最深刻的感受是尽量不要同时调多个参数。一次只动一个跑到产量偏差不超过5%再换下一个。否则多个参数互相补偿你根本分不清是哪个参数救回来的拟合结果最后拿去预测别的年份时完全不可靠。4. 土壤、地下水位与管理措施输入4.1 土壤参数文件AquaCrop-OSPy里土壤参数主要是为了计算土壤水分平衡所以核心是各层的田间持水量FC、凋萎系数WP、饱和含水量SAT和饱和导水率Ks。这些参数通常来自土壤质地。很多人直接把查表值填进去我建议有条件的话至少用本地实测的容重和有机质含量修正一下水分特征曲线。Python端的土壤输入在旧版是写.SOI文件新版可以直接用Soil类from aquacrop import Soil soil Soil( layers[ {thickness: 0.3, fc: 0.25, wp: 0.11, sat: 0.45, ks: 100.0}, {thickness: 0.6, fc: 0.28, wp: 0.12, sat: 0.48, ks: 80.0}, {thickness: 0.5, fc: 0.30, wp: 0.13, sat: 0.50, ks: 60.0}, ] )注意thickness单位是米fc、wp、sat都是体积水分含量单位是cm³/cm³不是百分比。这一点特别容易踩坑我曾经收到一个用户数据他把fc20%直接填成了20.0模型跑出来根区含水量永远是过饱和状态产量直接为0。土壤分层不一定越多越好AquaCrop-OSPy对层数没有太严格的限制但每层厚度不要小于10cm否则数值迭代容易不稳定。一般农田模拟3到5层就够了。另外如果研究区域存在浅层地下水位可以用water_table参数开启地下水模块。开启后模型会计算毛管上升水对根区水分的补充。这个功能在干旱灌区非常有价值但注意地下水位深度必须是逐日序列不能只填一个常数否则模型默认地下水位恒定跟实际波动不符。4.2 灌溉与田间管理设置灌溉输入是管理措施中最需要细看的一项。AquaCrop-OSPy支持多种灌溉触发方式我用下来比较常用的有三种固定灌溉制度设定每次灌水量和灌溉间隔。根区耗水回补当根区土壤含水量低于某一阈值如田间持水量的60%时自动灌水。基于生长阶段限制在特定生长阶段禁止灌溉。从参数设置角度看灌溉文件的主要字段包括参数含义示例值irrigation_method灌溉方式0-雨养1-灌溉1wetting_fraction地面湿润比例0.7irrigation_efficiency灌溉水利用率0.85trigger_threshold触发灌水的土壤水量阈值0.5占田间持水量比例Python端设置可以参考from aquacrop import Irrigation irrigation Irrigation( method1, wetting_fraction0.7, efficiency0.85, trigger_threshold0.5 )我个人比较推荐trigger_threshold模式因为它更贴近实际生产中“看墒灌水”的做法。设置trigger_threshold0.5的意思是当根区有效含水量降低到田间持水量的一半时模型会自动补灌到田间持水量。这样做出来的灌溉用水曲线比固定间隔灌水更符合节水灌溉逻辑。田间管理还包括地表覆盖度mulches、杂草管理等参数。如果模拟的农田有地膜覆盖记得在cropping文件中开启mulch选项否则地表蒸发会被高估。地膜覆盖面积小但降温保墒效应显著敏感度测试里常常能差出30%的蒸发量。5. 模型运行配置与初始条件5.1 模拟运行配置model argumentsAquaCrop-OSPy把模拟运行相关的设置从输入文件里分了出来统一作为Model类的参数。这部分设置决定了模拟的起止时间、输出频率、是否启用CO2效应等。看一个最小示例from aquacrop import Model model Model( weatherweather, cropcrop, soilsoil, irrigationirrigation, start_date2040-01-01, end_date2040-12-31, )start_date和end_date要用字符串格式模型内部会解析成时间索引。如果只模拟一个生长季通常把开始日期设在播种前30天目的是让土壤水分在播种前有一个“预热”过程不至于从初始土壤含水量直接开始导致水分平衡突变。还有一个很重要的参数是output_interval默认是daily也就是每天输出一次模拟结果。如果你只是看最终产量改成monthly甚至seasonal会大幅减少输出文件体积和模拟耗时。我之前跑一个20年区域格点模拟用daily输出结果文件十几个G换成seasonal之后一下降到几百MB跑起来轻快得多。更关键的是新版模型有一个save_results选项可以控制是否把结果写成CSV或者NetCDF。如果不打算做后处理建议直接关闭只保留Python内存里的结果对象速度和内存占用都会友好很多。5.2 初始条件及其他输入初始条件主要包含两块初始土壤水分含量和初始作物覆盖度。initial.INI文件里一般这样写Initial soil water content method : 1 Initial soil water content (fraction of FC) : 0.8 Initial rooting depth (m) : 0.3在Python中可以通过InitialConditions类设置from aquacrop import InitialConditions init InitialConditions( init_soil_water_content0.8, # 占田间持水量的比例 init_rooting_depth0.3, )init_soil_water_content这个参数有两种理解方式一种是绝对土壤含水量值一种是占田间持水量的比例。我强烈建议用比例值因为这样更容易保持不同土壤类型之间的可比性。填0.8表示播种时土壤水分是田间持水量的80%比较符合北方旱地“墒情较好”的播种条件。如果填入的是绝对含水量0.3那不同土壤类型下对应的水分胁迫状态完全不同别人复现你的结果时会非常困惑。初始作物覆盖度一般不填默认是0因为播种时地表通常没有作物覆盖。但如果是再生稻或者多年生作物就需要填上初始值。这个参数对模型的早期水分平衡影响比较明显千万别漏。6. 实操演示从输入到一次完整模拟6.1 准备输入文件的实操步骤讲完理论接着就是动手环节。我这里用一个完整示例演示如何从零准备输入并跑通一次模拟。假设我要研究华北平原冬小麦的水分生产力品种参数使用模型自带默认值只改气象和灌溉设置看在不同灌溉阈值下产量的变化。第一步先安装和导入模块pip install aquacropimport aquacrop from aquacrop import Weather, Crop, Soil, Irrigation, InitialConditions, Model import pandas as pd第二步准备气象数据。我用一个模拟的简单数据框实际使用时替换成你的气象观测或降尺度数据import pandas as pd import numpy as np dates pd.date_range(2020-10-01, 2021-06-20, freqD) n len(dates) weather_df pd.DataFrame({ date: dates.strftime(%Y-%m-%d), tmin: np.random.normal(8, 3, n).round(1), tmax: np.random.normal(18, 5, n).round(1), precip: np.random.exponential(1.5, n).round(1), eto: np.random.normal(2.5, 0.8, n).round(2), co2: 410.0, })注意如果co2这一列不填模型会用默认值。建议明确填上方便追溯。第三步定义作物、土壤和灌溉对象crop Crop(nameWheat, planting_date10-15, harvest_date06-15) soil Soil( layers[ {thickness: 0.3, fc: 0.26, wp: 0.10, sat: 0.46, ks: 120}, {thickness: 0.5, fc: 0.28, wp: 0.11, sat: 0.48, ks: 100}, {thickness: 0.4, fc: 0.30, wp: 0.12, sat: 0.50, ks: 80}, ] ) irrigation Irrigation(method1, trigger_threshold0.6, wetting_fraction0.8)第四步初始化模型并运行model Model( weatherweather_df, cropcrop, soilsoil, irrigationirrigation, start_date2020-10-01, end_date2021-06-20, output_intervaldaily, ) model.run()运行完之后可以通过model.results查看输出results model.results yield_ton results[yield][-1] # 单位是t/ha print(f模拟产量: {yield_ton:.2f} t/ha)这里results是一个dict里面包含多个DataFrame键名在旧版和新版之间有差异。新版常见的是yield、biomass、water_fluxes等建议先print(results.keys())看一眼再取数。6.2 运行模拟并检查输出刚跑完模型别急着拿结果去画图。先检查几项关键输出判断这次模拟是否合理蒸散量总量冬小麦全生育期蒸发蒸腾量一般在350~500mm之间如果跑出来只有100mm或者超过1000mm大概率是气象输入或作物参数有问题。生物量和收获指数看模拟生物产量是否在合理范围。冬小麦生物量大约12~18 t/ha籽粒产量5~9 t/ha。土壤水分平衡模型输出里的water_fluxes表会给出径流、深层渗漏、蒸散各分量加总应该接近降水灌溉量。如果发现产量特别异常第一件事不是调参数而是去查气象文件里有没有极端值。我遇到过一次冬小麦产量模拟为0查了半天发现是气象数据里有一天最高气温写成了-99模型直接判定为极端低温把整个冠层杀死了。所以对输入数据的质量控制再怎么强调都不过分。7. 常见问题与排查技巧7.1 输入文件常见报错AquaCrop-OSPy的报错信息有时候不那么直观很多报错指向的其实是输入文件格式问题。我把自己遇到的典型报错整理成一个速查表报错信息可能原因解决办法KeyError: date气象DataFrame缺少date列或列名大小写不对检查列名必须为date且全部小写TypeError: argument of type int is not iterable作物参数或灌溉参数传了错误类型检查传入对象是否为字符串或列表method参数要传整数ValueError: Length of values does not match length of index某些输入序列长度不一致检查气象数据的co2列是否填了标量而非数组确保逐日序列长度一致AttributeError: NoneType object has no attribute params初始化模型时漏传了soil或crop对象检查是否所有必填对象都已传入RuntimeError: Soil water balance not balanced初始土壤含水量或灌溉设置不合理导致水分平衡不收敛降低init_soil_water_content到0.5附近或者检查灌溉阈值是否过低DataError: Date index not continuous气象数据存在缺失日期用check_weather函数检查补齐缺失日期上述报错前四种大多是低级格式错误后两种属于参数逻辑问题。我建议新手先跑一个官方自带的示例项目确保环境没问题再换成自己的数据。否则环境、数据、参数三处同时出问题排查起来非常痛苦。7.2 调参避坑经验最后分享几条这几年反复踩过的调参经验。第一别迷信默认参数。AquaCrop-OSPy的默认参数来自全球平均水平对特定区域和品种来说只能是起点。但模拟气候变化相对变化量比如未来产量下降20%时默认参数的影响会小一些因为模型偏差在基准期和未来期里大致抵消。如果是评估绝对产量必须本地化标定。第二灌溉阈值不要设得太高。我之前试过把trigger_threshold设为0.9意思是土壤含水量降到田间持水量的90%就灌水结果整个生长季几乎天天在灌水模拟产量看起来很高但灌溉水量完全不符合实际。生产上旱作物灌溉阈值一般设在0.4~0.6之间经济作物可以高一些但也不要超过0.7否则就是拿水资源换虚高产量。第三地下水位参数要跟气象序列同步。如果你研究区域有浅层地下水并且开了water_table功能那么地下水位序列必须和气象数据日期一一对应。我碰到过有人把地下水位数据按月输入模型解析时自动按日线性插值倒是能跑但插值后的数据在突变的月份会出现明显失真最好还是手动处理好逐日数据再输入。第四多做敏感性测试。调参之前可以用Python写个简单的循环把某一参数在±20%范围内变化看产量变化幅度。比如for kc in [0.9, 1.0, 1.1, 1.2]: crop_temp Crop(nameWheat, planting_date10-15, harvest_date06-15, kc_trkc) model_temp Model(weatherweather_df, cropcrop_temp, soilsoil, irrigationirrigation) model_temp.run() print(fkc_tr{kc}: yield{model_temp.results[yield][-1]:.2f} t/ha)这样跑一圈你就能直观看出哪个参数对结果影响最大后续重点优化它就可以了。第五设置随机种子。如果你在做参数优化或敏感性分析并且你的气象数据生成过程中涉及随机性一定要在代码开头加上np.random.seed()否则每次模拟的气象输入不同结果完全没有可比性。这一点看似不起眼却是很多人在复现实验中翻车的根源。我自己的习惯是每次改造输入文件之前先跑一次基线模拟记录下产量、蒸散量和土壤水分平衡三个关键输出。任何修改之后先跟基线对比再看绝对值是否合理。这样既能确认输入设置没有低级错误又能量化参数变化对结果的影响。这套工作流看起来笨但确实是我目前跑AquaCrop-OSPy项目最稳的方式写出来供你参考。
返回列表