# %matplotlib inline 是 Jupyter Notebook 中的一个命令,用于在 Notebook 中直接显示图形%matplotlib inline# 导入 sys 模块,用于访问系统相关的功能importsys# 导入 pathlib 模块中的 Path 类,用于处理文件路径frompathlibimportPath# 导入 datetime 模块中的 dt 别名,用于处理日期和时间importdatetimeasdt# 导入 yaml 模块,用于解析 YAML 格式的配置文件importyaml# 导入 pandas 模块,用于数据处理和分析importpandasaspd# 导入 matplotlib 模块,用于绘图importmatplotlib# 使用 ggplot 风格的绘图样式matplotlib.style.use("ggplot")# 导入 matplotlib.pyplot 模块,用于绘制图形importmatplotlib.pyplotasplt# 导入 pcse 模块,用于构建和运行作物模拟模型importpcse# 打印当前 Notebook 使用的 Python 和 PCSE 版本信息print("This notebook was built with:")print(f"python version: {sys.version}")print(f"PCSE version: {pcse.__version__}")
%matplotlib inline:
这是一个 Jupyter Notebook 中的魔术命令,用于在 Notebook 中直接显示图形,而不是在单独的窗口中显示。
导入模块:
sys
:用于访问系统相关的功能,如获取 Python 版本信息。
Path
:用于处理文件路径,方便文件操作。
datetime
:用于处理日期和时间,这里使用 dt
作为别名。
yaml
:用于解析 YAML 格式的配置文件,常用于存储配置信息。
pandas
:用于数据处理和分析,提供 DataFrame 和 Series 等数据结构。
matplotlib
:用于绘图,提供丰富的绘图功能。
matplotlib.pyplot
:用于绘制图形,提供简单的绘图接口。
pcse
:用于构建和运行作物模拟模型,提供作物生长模拟的环境和工具。
设置绘图样式:
matplotlib.style.use("ggplot")
:使用 ggplot 风格的绘图样式,使图形更具视觉吸引力。
打印版本信息:
print("This notebook was built with:")
:打印提示信息。
print(f"python version: {sys.version}")
:打印当前使用的 Python 版本。
print(f"PCSE version: {pcse.__version__}")
:打印当前使用的 PCSE 版本。
通过这些代码,你可以确保 Notebook 环境中已经正确导入了所需的模块,并且可以查看当前使用的 Python 和 PCSE 版本信息。这对于调试和版本管理非常重要。
说明:这段代码主要完成了几个关键的任务:
配置Jupyter Notebook的绘图环境:
%matplotlib inline
是一个Jupyter Notebook的魔法命令(magic command),用于确保所有的绘图都会直接在Notebook中显示,而不是在一个新窗口中打开。
导入必要的模块和库:
import sys
: 导入sys模块,提供了访问和使用Python解释器及操作系统相关功能的方法。
from pathlib import Path
: 导入Path类,简化了文件和目录路径的操作。
import datetime as dt
: 导入datetime模块,并使用dt作为别名,便于处理日期和时间。
import yaml
: 导入yaml模块,用于读取和写入YAML格式的文件,常用于配置文件。
import pandas as pd
: 导入pandas库,提供了高性能的数据结构和数据分析工具。
import matplotlib
: 导入matplotlib,一个广泛使用的绘图库。
matplotlib.style.use("ggplot")
: 设置绘图风格为ggplot,这种风格可以让图表看起来更加美观。
import matplotlib.pyplot as plt
: 导入pyplot子模块,提供了一组类似于Matlab接口的绘图函数。
import pcse
: 假设这是一个自定义或领域特定的库,用于构建和运行作物模拟模型。
打印Python和PCSE版本信息:
通过调用sys.version
和pcse.__version__
来获取并打印当前运行的Python和PCSE版本信息,这对于调试和追踪代码兼容性问题非常有用。
这段代码的执行流程非常清晰,通过引入一系列基础和专业库,设置了绘图风格,并打印出了环境的版本信息,为后续的代码编写和运行打下了良好的基础。这样的实践在数据分析和科学计算项目中非常常见,有助于确保环境一致性,减少潜在的问题。
这段代码使用 Jupyter Notebook 的魔法命令 %%writefile
将代码写入一个名为 gddmodel.py
的 Python 文件中。该文件定义了一个简单的模型,用于累积作物生长周期内的生长度日(Growing Degree Days, GDD)。以下是对代码的详细解释:
%%writefile gddmodel.py# 使用%%writefile命令将代码写入文件gddmodel.py# 这里我们导入PCSE中的一些组件,它们是构建任何模型的基本组成部分。from pcse.base import SimulationObject, StatesTemplate, RatesTemplate, ParamTemplatefrom pcse.traitlets import Float# 在PCSE中的模型总是继承自SimulationObjectclassGrowingDegreeDayModel(SimulationObject):"""一个简单的模型,用于在作物生长周期的开始和结束之间积累生长度日。"""# 这定义了模型参数。它继承自ParamTemplate,这确保参数接收其值,并在参数缺失时发出信号。classParameters(ParamTemplate):# 定义基温参数BaseTemperature = Float# 状态变量总是继承自StatesTemplate。StatesTemplate提供了某些行为,# 如状态变量必须被初始化。classStateVariables(StatesTemplate):# 定义生长度日状态变量GDD = Float# 速率变量类似于状态变量,但会自动初始化为零。classRateVariables(RatesTemplate):# 定义生长度日的变化率rGDD = Floatdefinitialize(self, day, kiosk, parameters):"""初始化仅在模型启动时运行一次。它始终有三个变量:- day: 模型开始的日期- kiosk: 变量Kiosk,这是一个在模型组件间共享的对象- parameters: 提供模型参数的对象"""# 下面的代码初始化参数、状态和速率变量。# 注意状态变量(这里:GDD)必须提供初始值。self.params =self.Parameters(parameters)self.states =self.StateVariables(kiosk, GDD=0.0)self.rates =self.RateVariables(kiosk)defcalc_rates(self, day, drv):"""计算在当前日期发生的改变率。它始终有两个变量:- 当前日期(一个日期)- 驱动变量`drv`,包含气象输入"""# 这里我们计算GDD的增加,并将其赋值给速率变量rGDDself.rates.rGDD =max(0.0, drv.TEMP -self.params.BaseTemperature)defintegrate(self, day, delt):"""这执行速率变化对状态的积分。它始终有两个变量:- 当前日期- 时间步长,固定为1.0天"""# 这里我们将rGDD乘以delt加到GDD上。# 与`delt`相乘主要是为了教育目的(因为delt=1.0),尽管这在未来可能会改变。self.states.GDD +=self.rates.rGDD * delt
SimulationObject
:所有 PCSE 模型的基本类。
StatesTemplate
:用于定义状态变量的模板。
RatesTemplate
:用于定义变化率变量的模板。
ParamTemplate
:用于定义参数的模板。
Float
:用于定义浮点数类型的参数。
# Models in PCSE always inherit from SimulationObjectclassGrowingDegreeDayModel(SimulationObject):"""A simple model to accumulate growing degree days between start and end of the crop cycle. """
GrowingDegreeDayModel
类继承自 SimulationObject
,这是所有 PCSE 模型的基本要求。
# This defines the model parameters. It inherits from ParamTemplate, this ensures that parameters# receive their value and it signals when parameters are missing.classParameters(ParamTemplate): BaseTemperature = Float
Parameters
类继承自 ParamTemplate
,用于定义模型的参数。
BaseTemperature
:定义了基温,这是一个浮点数类型的参数。
# State variables always inherit from StatesTemplate. StatesTemplate provides certain behaviour# such as that state variables must be initialized.classStateVariables(StatesTemplate): GDD = Float
StateVariables
类继承自 StatesTemplate
,用于定义模型的状态变量。
GDD
:累积的生长度日,这是一个浮点数类型的状态变量。
# Rate variables are somewhat similar to states but are initialized automatically to zero.classRateVariables(RatesTemplate): rGDD = Float
RateVariables
类继承自 RatesTemplate
,用于定义模型的变化率变量。
rGDD
:每日生长度日的变化率,这是一个浮点数类型的变化率变量。
definitialize(self, day, kiosk, parameters):"""Initialize runs only once, when the model is started. It always has three variables: - day: the day when the model starts - kiosk: the VariableKiosk which is an object which is shared between model components - parameters: an object providing the model parameters """# The code below initializes the parameters, state and rate variables.# Note that the initial value of state variable (here: GDD) must be provided. self.params = self.Parameters(parameters) self.states = self.StateVariables(kiosk, GDD=0.0) self.rates = self.RateVariables(kiosk)
initialize
方法在模型启动时仅运行一次。
day
:模型开始的日期。
kiosk
:变量共享对象,用于在模型组件之间共享变量。
parameters
:提供模型参数的对象。
初始化参数、状态变量和变化率变量。注意,状态变量 GDD
的初始值必须提供。
defcalc_rates(self, day, drv):"""calc_rates computes the rate of change that occur during the current day. It always has two variables: - the current day (a date) - the driving variables `drv` that contain the meteorological inputs """# Here we compute the increase in GDD and assign it to rate variable rGDD self.rates.rGDD =max(0.0, drv.TEMP - self.params.BaseTemperature)
calc_rates
方法计算当前天的变化率。
day
:当前日期。
drv
:包含气象输入的驱动变量。
计算每日生长度日的变化率 rGDD
,公式为 max(0.0, drv.TEMP - self.params.BaseTemperature)
,确保结果非负。
defintegrate(self, day, delt):"""This performs the integration of rates of change onto the states. It always has two variables: - the current day - the time step, which is fixed at 1.0 day """# Here we add rGDD multiplied by delt to GDD.# The multiplication with `delt` is mostly for educational purposes (since delt=1.0) although# this may change in the future. self.states.GDD += self.rates.rGDD * delt
integrate
方法将变化率积分到状态变量上。
day
:当前日期。
delt
:时间步长,固定为 1.0 天。
将变化率 rGDD
乘以时间步长 delt
并加到状态变量 GDD
上。这里的乘法主要是为了教育目的(因为 delt
固定为 1.0),但将来可能会有所变化。
这个 GrowingDegreeDayModel
类定义了一个简单的模型,用于累积作物生长周期内的生长度日(GDD)。模型通过继承 SimulationObject
类,定义了参数、状态变量和变化率变量,并实现了初始化、计算变化率和积分变化率的方法。通过这些方法,模型可以在每一天更新累积的生长度日。
# 导入PCSE库中用于构建任何模型的基本组件frompcse.baseimportSimulationObject, StatesTemplate, RatesTemplate, ParamTemplatefrompcse.traitletsimportFloat# 定义一个继承自SimulationObject的模型类,所有PCSE中的模型都需继承自SimulationObjectclassGrowingDegreeDayModel(SimulationObject):"""一个简单的模型,用于计算作物生长周期内从开始到结束的生长度日。"""# 定义模型参数,继承自ParamTemplate确保参数能够接收值,并在缺少参数时发出警告classParameters(ParamTemplate):BaseTemperature=Float# 定义状态变量,继承自StatesTemplate。StatesTemplate确保状态变量必须被初始化classStateVariables(StatesTemplate):GDD=Float # 这里GDD存储累积的生长度日# 定义速率变量,自动初始化为零classRateVariables(RatesTemplate):rGDD=Float # 这里rGDD表示当前天生长度日的增加量definitialize(self, day, kiosk, parameters):"""初始化方法,仅在模型启动时运行一次。参数:- day: 模型开始的日期- kiosk: VariableKiosk对象,模型组件间共享的数据容器- parameters: 提供模型参数的对象"""# 初始化参数、状态和速率变量。# 注意状态变量(如这里GDD)必须提供初始值self.params=self.Parameters(parameters)self.states=self.StateVariables(kiosk, GDD=0.0)self.rates=self.RateVariables(kiosk)defcalc_rates(self, day, drv):"""calc_rates计算当前天的变化率。参数:- day: 当前日期- drv: 驱动变量,包含气象输入数据"""# 计算GDD的增加量,并赋值给速率变量rGDD# 只有当当前温度减去基础温度大于等于0时,才增加GDDself.rates.rGDD=max(0.0, drv.TEMP -self.params.BaseTemperature)defintegrate(self, day, delt):"""根据变化率更新状态变量的值。参数:- day: 当前日期- delt: 时间步长,此处固定为1.0天"""# 将速率rGDD与时间步长delt相乘后累加到状态GDD上# 乘以delt主要是为了教育目的,尽管delt通常为1.0,但未来可能有所改变self.states.GDD+=self.rates.rGDD*delt
这段代码展示了如何使用PCSE库来构建一个作物生长度日(Growing Degree Day, GDD)模型。该模型用于计算作物在一个生长季节内累计的有效温度,即高于某个基准温度的日平均温度之和,这在农业气候研究中是常见的指标。
导入必要模块:
从pcse.base
导入SimulationObject
, StatesTemplate
, RatesTemplate
, ParamTemplate
类,以及从pcse.traitlets
导入Float
类型。这些类和类型用于构建模型的基本结构。
定义模型类GrowingDegreeDayModel
:
继承自SimulationObject
,这是PCSE中所有模型的基础类。
定义模型参数:
通过内部类Parameters
继承自ParamTemplate
,定义模型的参数BaseTemperature
为浮点数类型。参数负责存储模型所需的静态配置。
定义状态变量:
内部类StateVariables
继承自StatesTemplate
,定义状态变量GDD
,用于存储累积的生长度日,初始化为0。
定义速率变量:
内部类RateVariables
继承自RatesTemplate
,定义速率变量rGDD
,表示每天新增的生长度日,初始化为0。
初始化方法initialize
:
在模型初始化时调用,接收日期day
,VariableKiosk
对象kiosk
(用于共享数据),以及模型参数parameters
。
初始化参数、状态变量和速率变量,其中状态变量GDD
被设置为0.0。
计算速率方法calc_rates
:
计算每日的生长度日增加量rGDD
,基于当前温度与基准温度的差值,差值小于0则不计。
这个方法在每个时间步执行,计算出增加量。
积分方法integrate
:
更新状态变量GDD
,将速率rGDD
乘以时间步长delt
(通常为1.0天)后添加到累积值上。
这个步骤将速率转换为实际的状态变量变化,完成一个时间步的模拟。
请注意,上述模型的实现中并不包含一个能按模拟天数逐步推进的时间循环。你无需自行实现这个时间循环,因为PCSE引擎会负责实现它,并将其与农业管理方面的信息进行同步。
然而,PCSE引擎需要了解你的模型。此外,它还需要一些诸如在模拟过程中需要收集的变量等细节信息。为此,我们要创建一个引擎配置文件。这个配置文件是用纯Python编写的,尽管它通常带有“.conf”扩展名,以此表明它是一个配置文件,而不是一个模拟模型。
以下是上面所实现的积温模型的配置文件内容。我们将其写入到“gddmodel.conf”文件中。
为了实际运行该模型,我们还需要PCSE中的一些其他组件。首先,需要“引擎(Engine)”;其次,我们需要“参数提供者(ParameterProvider)”,它是封装模型参数的组件;最后,我们需要一个用于读取天气数据的组件:“Excel天气数据提供者(ExcelWeatherDataProvider)”。
# Engine 是 pcse 的核心类之一,它负责管理和运行作物模型。你可以使用 Engine 类实例化一个引擎,然后向其中添加作物、土壤、管理等多个模块,最后调用其方法来执行完整的作物生命周期模拟。frompcse.engineimportEngine# ParameterProvider 是一个抽象基类,用于提供模拟过程中所需的各类参数。在实际应用中,你需要创建这个类的子类来实现具体的参数读取逻辑。例如,参数可以从数据库、Excel 文件、CSV 文件或者硬编码的值中读取,具体取决于你的数据来源。frompcse.baseimportParameterProvider# ExcelWeatherDataProvider 是 ParameterProvider 的一个具体实现,用于从 Excel 文件中读取气象数据。这通常包括温度、降水、太阳辐射等对作物生长至关重要的环境条件。使用这个类,你可以在模拟过程中输入真实的气象数据,以获得更接近现实的作物生长预测。frompcse.inputimportExcelWeatherDataProvider
这段代码展示了如何使用Python进行作物模型参数配置、解析农业管理事件的YAML配置,并加载气象数据的过程。下面是详细的分步解读:
# 定义作物基础参数字典,这里以BaseTemperature为例,表示作物的基础温度cropd =dict(BaseTemperature=3.0)
这里定义了一个字典cropd
,存储作物的参数信息,例如BaseTemperature
,代表作物生长的基准温度。
# 实例化ParameterProvider类,传入作物参数(cropdata)、站点参数(sitedata)和土壤参数(soildata)params = ParameterProvider(cropdata=cropd, sitedata={}, soildata={})
创建了ParameterProvider
实例params
,它将用于提供作物、站点和土壤相关的参数。在这个例子中,只提供了作物参数cropd
,其它参数为空字典。
# 使用yaml.safe_load()解析农业管理事件的YAML格式字符串agro = yaml.safe_load("""- 2006-01-01: CropCalendar: crop_name: 'GDDCrop' variety_name: 'GDDVariety' crop_start_date: 2006-03-15 crop_start_type: emergence crop_end_date: 2006-09-25 crop_end_type: harvest max_duration: 300 TimedEvents: null StateEvents: null""")
使用yaml.safe_load()
函数解析一段YAML格式的字符串,内容包含了作物日历事件(CropCalendar
)、定时事件(TimedEvents
)和状态事件(StateEvents
)的配置。具体而言,CropCalendar
部分配置了作物名称、品种、开始日期、开始类型、结束日期、结束类型及最大持续时间。
# 获取当前工作目录下的'data'/'meteo'/'nl1.xlsx'路径weatherfile = Path.cwd()/"data"/"meteo"/"nl1.xlsx"# 实例化ExcelWeatherDataProvider,传入天气数据文件路径wdp = ExcelWeatherDataProvider(weatherfile)
获取当前工作目录下特定路径的Excel文件nl1.xlsx
,然后实例化ExcelWeatherDataProvider
类,传入上述文件路径。这一步骤用于读取并处理Excel文件中的气象数据。
下面的代码通过启动引擎并向其提供参数、天气数据、农业管理信息以及配置文件的路径来运行积温模型。
这段代码是关于构建和运行一个基于参数、天气数据、农业管理事件及配置文件的作物生长模型(以GDDModel为例)。下面是详细的步骤分解:
# 构建配置文件路径,指向当前工作目录下的"gddmodel.conf"配置文件gdd_config = Path.cwd()/"gddmodel.conf"
这里通过Path.cwd()
获取当前工作目录,并在其下构建gddmodel.conf
配置文件的完整路径。
# 实例化Engine类,传入参数提供者(params)、天气数据提供者(wdp)、农业管理事件(agro)和配置文件(config)gddmodel = Engine(params, wdp, agro, config=gdd_config)
创建Engine
对象gddmodel
,这是作物生长模型的核心引擎。在构造函数中,传入了参数提供者params
、天气数据提供者wdp
、农业管理事件agro
和配置文件路径gdd_config
。这样就构建了模型运行所需的所有外部条件。
# 调用run_till_terminate方法运行模型直到终止条件满足,即执行完整的作物生长周期模拟gddmodel.run_till_terminate()
调用Engine
对象的run_till_terminate
方法,来运行模型直到满足终止条件。这通常是指执行完整的作物生长周期模拟,从播种到收获或达到其他自然终止条件。
通过以上步骤,我们构建了模型核心组件并运行了整个作物生长周期的模拟。在这个过程中,模型会根据提供的参数、天气数据、农业管理事件和配置文件,来模拟作物的生长过程,直至完成一个生长周期。这为农业研究、决策支持等领域提供了重要的工具和数据支持。
我们现在可以获取模型的输出结果,将其转换为一个Pandas数据框,并展示其内容。如你所见,该数据框包含“GDD”(积温)和“rGDD”(积温日增长量)这两列,它们是配置文件中“OUTPUT”(输出)部分所指定的变量。
这段代码描述的是如何从作物生长模型(gddmodel
)中获取输出数据,将其转换为Pandas DataFrame格式,并展示数据集的最后几行以预览数据的结尾部分。下面是对这段代码的逐行解释:
# 从gddmodel对象获取输出结果output_data = gddmodel.get_output()
这里调用了gddmodel
对象的get_output()
方法来获取模型的输出数据。这通常包含作物生长过程中的各项指标数据,如温度、湿度、作物高度、生长阶段等。
# 使用pandas库将输出结果转换为DataFrame,并设置索引为"day"df1 = pd.DataFrame(output_data).set_index("day")
接下来,使用pd.DataFrame()
将output_data
转换为Pandas DataFrame。然后,通过.set_index("day")
方法,将DataFrame的索引设置为"day"
,这意味着DataFrame的每一行都将由日期表示,这样的设计方便后续按时间顺序对数据进行操作和分析。
# 显示DataFrame的最后几行,以预览数据的结尾部分df1.tail()
最后,使用df1.tail()
打印DataFrame的最后几行,以便快速预览数据集的尾巴,检查数据是否按照预期加载和排列。tail()
默认显示最后5行,但可以通过传递参数n
指定显示的行数,例如df1.tail(10)
将显示最后10行。
通过以上步骤,可以有效地将模型输出数据整理为便于分析和可视化的格式,进一步进行农业数据分析或报告制作。
如你所见,收获时的总积温天数为2321天。在最初的1.5个月里,由于气温较低,积温(GDD)值的累积相对缓慢,但随后在春末和夏季,累积速度加快,直至2006年9月25日达到收获日期。
更多内容敬请访问:irripro (IrriPro),https://github.com/irripro