一、经典 LV:单区域两物种
目标:在经典的掠食-猎物(Lotka–Volterra, LV)两物种模型上,加入区域间迁移 。
做法:把每个区域都当作一个“本地 LV 子系统”,然后用“扩散式”迁移把同类物种 在相邻区域之间耦合(从高密度流向低密度),并保证总量守恒 。
最小两区域(A、B)方程:
x ˙ A = α x A − β x A y A + D x ( x B − x A ) , y ˙ A = δ x A y A − γ y A + D y ( y B − y A ) , x ˙ B = α x B − β x B y B + D x ( x A − x B ) , y ˙ B = δ x B y B − γ y B + D y ( y A − y B ) . \begin{aligned}
\dot x_A &= \alpha x_A - \beta x_A y_A + D_x (x_B - x_A),\\
\dot y_A &= \delta x_A y_A - \gamma y_A + D_y (y_B - y_A),\\
\dot x_B &= \alpha x_B - \beta x_B y_B + D_x (x_A - x_B),\\
\dot y_B &= \delta x_B y_B - \gamma y_B + D_y (y_A - y_B).
\end{aligned}
x ˙ A y ˙ A x ˙ B y ˙ B = α x A − β x A y A + D x ( x B − x A ) , = δ x A y A − γ y A + D y ( y B − y A ) , = α x B − β x B y B + D x ( x A − x B ) , = δ x B y B − γ y B + D y ( y A − y B ) .
二、代码讲解
⚠️:下述代码仅代表部分功能实现的示例,并不是完整代码,完整代码后续会考虑上传至GitHub
1) 接口层:Interfaces.Species
作用:一根线 同时承载两个量:种群数量(population)和对它的“作用流”(rate,flow 变量)。
1 2 3 4 5 6 7 8 connector Species "物种连接器:传递种群数量与通量" Real population "种群数量"; flow Real rate "作用于种群的通量(正为流入,负为流出)"; annotation(Icon(graphics={ Rectangle(extent={{-80,60},{80,-60}}, lineColor={0,0,0}, fillColor={220,220,255}), Text(extent={{-80,65},{80,85}}, string="物种", fontSize=12)}), Documentation(info="<html><p>用于在组件之间传递“种群数量(population)”与其变化通量(rate)。</p></html>")); end Species;
2) 基础组件:繁殖/饥饿/捕食
Reproduction(繁殖) :对单一物种,增长与数量成正比
1 2 3 4 5 6 7 8 9 10 model Reproduction "繁殖:增长与种群数量成正比" extends ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.SinkOrSource; parameter Real alpha "出生率比例系数 α"; equation growth = alpha * species.population "增长 ~ α·种群"; annotation(Icon(graphics={ Ellipse(extent={{-60,40},{60,-40}}, fillColor={200,255,200}, lineColor={0,100,0}), Text(extent={{-60,45},{60,65}}, string="繁殖", fontSize=11)}), Documentation(info="<html><p>繁殖项:<code>growth = α·population</code>。</p></html>")); end Reproduction;
Starvation(饥饿/自然死亡) :对单一物种,衰退与数量成正比
1 2 3 4 5 6 7 8 9 10 model Starvation "饥饿:衰退与种群数量成正比" extends ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.SinkOrSource; parameter Real gamma "饥饿(死亡)系数 γ"; equation decline = gamma * species.population "衰退 ~ γ·种群"; annotation(Icon(graphics={ Ellipse(extent={{-60,40},{60,-40}}, fillColor={255,220,220}, lineColor={150,0,0}), Text(extent={{-60,45},{60,65}}, string="饥饿", fontSize=11)}), Documentation(info="<html><p>饥饿/自然死亡项:<code>decline = γ·population</code>。</p></html>")); end Starvation;
Predation(捕食) :两物种相互作用(A=猎物,B=掠食者)
1 2 3 4 5 6 7 8 9 10 11 12 model Predation "捕食:A为猎物,B为捕食者" extends ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Interaction; parameter Real beta "猎物被捕食系数 β"; parameter Real delta "捕食者摄食增殖系数 δ"; equation b_growth = delta * a.population * b.population "B增长 ~ δ·A·B"; a_decline = beta * a.population * b.population "A衰退 ~ β·A·B"; annotation(Icon(graphics={ Rectangle(extent={{-60,40},{60,-40}}, fillColor={255,240,200}, lineColor={120,60,0}), Text(extent={{-60,45},{60,65}}, string="捕食", fontSize=11)}), Documentation(info="<html><p>经典 Lotka–Volterra 捕食项。</p></html>")); end Predation;
3) 区域内状态持有者:RegionalPopulation
作用:把连接器里的 rate 积分成状态 population;这是唯一 持有微分方程的地方。
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 model RegionalPopulation "区域内的单物种种群(带初始化选项)" encapsulated type InitializationOptions = enumeration( Free "自由:不施加额外初值条件", FixedPopulation "固定初值:指定初始种群", SteadyState "稳态:初始导数为0"); parameter InitializationOptions init = InitializationOptions.Free annotation(Dialog(group="初始化")); parameter Real initial_population = 10 annotation(Dialog(group="初始化", enable=init==InitializationOptions.FixedPopulation)); ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species species annotation(Placement(transformation(extent={{90,-10},{110,10}}))); protected Real population(start=initial_population) "内部状态 = 外部连接器的 population"; initial equation if init == InitializationOptions.FixedPopulation then population = initial_population; elseif init == InitializationOptions.SteadyState then der(population) = 0; end if; equation der(population) = species.rate; species.population = population; assert(population >= 0, "种群数量必须非负"); annotation(Icon(graphics={ Circle(extent={{-40,40},{40,-40}}, fillColor={200,255,200}, lineColor={0,100,0}), Text(extent={{-50,45},{50,65}}, string="区域种群", fontSize=11)}), Documentation(info="<html><p>区域内的单物种状态持有者,<code>der(population) = rate</code>。</p></html>")); end RegionalPopulation;
4) 区域两物种子系统:TwoSpecies
作用:组装“兔-狐”这对物种在单一区域 内的 LV 过程。
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 model TwoSpecies "区域两物种(兔-狐)Lotka–Volterra 子系统" import RegionalPopulation = ModelicaByExample_CN.Components.LotkaVolterra.Components.RegionalPopulation; import Reproduction = ModelicaByExample_CN.Components.LotkaVolterra.Components.Reproduction; import Starvation = ModelicaByExample_CN.Components.LotkaVolterra.Components.Starvation; import Predation = ModelicaByExample_CN.Components.LotkaVolterra.Components.Predation; parameter Real alpha = 0.1 "兔出生率 α"; parameter Real gamma = 0.4 "狐饥饿系数 γ"; parameter Real initial_rabbit_population = 10 "兔初始数量"; parameter Real initial_fox_population = 10 "狐初始数量"; parameter Real beta = 0.02 "兔被捕食系数 β"; parameter Real delta = 0.02 "狐摄食增殖系数 δ"; ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species rabbits "本区域的兔" annotation(Placement(transformation(extent={{-110,-10},{-90,10}}))); ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species foxes "本区域的狐" annotation(Placement(transformation(extent={{90,-10},{110,10}}))); protected RegionalPopulation rabbit_population( initial_population=initial_rabbit_population, init = RegionalPopulation.InitializationOptions.FixedPopulation) annotation(Placement(transformation(extent={{-60,-10},{-40,10}}))); Reproduction reproduction(alpha=alpha) annotation(Placement(transformation(extent={{-80,30},{-60,50}}))); RegionalPopulation fox_population( initial_population=initial_fox_population, init = RegionalPopulation.InitializationOptions.FixedPopulation) annotation(Placement(transformation(extent={{40,-10},{60,10}}))); Starvation fox_starvation(gamma=gamma) annotation(Placement(transformation(extent={{60,30},{80,50}}))); Predation fox_predation(beta=beta, delta=delta) annotation(Placement(transformation(extent={{-10,-10},{10,10}}))); equation connect(reproduction.species, rabbit_population.species); connect(fox_predation.a, rabbit_population.species); connect(fox_starvation.species, fox_population.species); connect(fox_population.species, fox_predation.b); connect(rabbit_population.species, rabbits); connect(fox_population.species, foxes); annotation(Icon(graphics={ Rectangle(extent={{-60,60},{60,-60}}, fillColor={255,255,200}, lineColor={0,0,0}), Text(extent={{-60,65},{60,85}}, string="两物种区域", fontSize=12)}), Documentation(info="<html><p>该子系统组合了:兔(繁殖)+ 狐(饥饿)+ 捕食(A=兔,B=狐)。</p></html>")); end TwoSpecies;
5) 迁移组件:Migration
作用:把两个区域 的同类物种通过“差驱动扩散 + 守恒约束”耦合。
A端: rate A = k ( X A − X B ) , 守恒: rate A + rate B = 0. \text{A端:}\ \text{rate}_A = k\,(X_A - X_B),\qquad
\text{守恒:}\ \text{rate}_A + \text{rate}_B = 0.
A 端: rate A = k ( X A − X B ) , 守恒: rate A + rate B = 0.
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 model Migration "迁移(扩散)模型:A↔B 两区域" parameter Real rabbit_migration = 0.001 "兔迁移率"; parameter Real fox_migration = 0.005 "狐迁移率"; ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species rabbit_a "区域A的兔" annotation(Placement(transformation(extent={{-110,-40},{-90,-20}}))); ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species rabbit_b "区域B的兔" annotation(Placement(transformation(extent={{90,-40},{110,-20}}))); ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species fox_a "区域A的狐" annotation(Placement(transformation(extent={{-110,20},{-90,40}}))); ModelicaByExample_CN.Components.LotkaVolterra.Interfaces.Species fox_b "区域B的狐" annotation(Placement(transformation(extent={{90,20},{110,40}}))); equation // 兔扩散:按两区数量差迁移 + 总量守恒 rabbit_a.rate = (rabbit_a.population - rabbit_b.population) * rabbit_migration; rabbit_a.rate + rabbit_b.rate = 0 "兔总量守恒"; // 狐扩散:按两区数量差迁移 + 总量守恒 fox_a.rate = (fox_a.population - fox_b.population) * fox_migration; fox_a.rate + fox_b.rate = 0 "狐总量守恒"; annotation(Icon(graphics={ Line(points={{-50,0},{50,0}}, color={0,0,150}), Polygon(points={{45,5},{55,0},{45,-5}}, fillPattern=FillPattern.Solid), Text(extent={{-60,10},{60,30}}, string="迁移", fontSize=11)}), Documentation(info="<html><p>扩散式迁移:通量与两区域人口差成正比,并通过方程 <code>a.rate + b.rate = 0</code> 保证总量守恒。</p></html>")); end Migration;
6) 顶层系统:WithMigration
四个区域(A,B,C,D 都是 TwoSpecies),用三段 Migration 串联成 A↔B↔C↔D。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 model WithMigration "四区域:相邻迁移耦合(A↔B↔C↔D)" extends InitiallyDifferent; ModelicaByExample_CN.Subsystems.LotkaVolterra.Components.Migration migrate_AB "A↔B 迁移" annotation(Placement(transformation(extent={{-60,10},{-20,30}}))); ModelicaByExample_CN.Subsystems.LotkaVolterra.Components.Migration migrate_BC "B↔C 迁移" annotation(Placement(transformation(extent={{-20,10},{20,30}}))); ModelicaByExample_CN.Subsystems.LotkaVolterra.Components.Migration migrate_CD "C↔D 迁移" annotation(Placement(transformation(extent={{20,10},{60,30}}))); equation connect(migrate_AB.rabbit_a, A.rabbits); connect(migrate_AB.rabbit_b, B.rabbits); connect(migrate_AB.fox_a, A.foxes); connect(migrate_AB.fox_b, B.foxes); connect(migrate_BC.rabbit_a, B.rabbits); connect(migrate_BC.rabbit_b, C.rabbits); connect(migrate_BC.fox_a, B.foxes); connect(migrate_BC.fox_b, C.foxes); connect(migrate_CD.rabbit_a, C.rabbits); connect(migrate_CD.rabbit_b, D.rabbits); connect(migrate_CD.fox_a, C.foxes); connect(migrate_CD.fox_b, D.foxes); end WithMigration;
三、仿真运行
在 OpenModelica / MWorks 中使用
OpenModelica / OMEdit:
File → Open Model/Library… 选择 ModelicaByExample_CN/package.mo
打开:ModelicaByExample_CN.Subsystems.LotkaVolterra.Examples.WithMigration
StopTime = 200,点击 Simulate
Plot:A.rabbits.population、A.foxes.population、…、D.*
MWorks:
若对 MSL 版本敏感,把顶层 package.mo 中的
annotation(uses(Modelica(version="4.0.0")))
改成 MWorks 支持的版本(如 4.1.3),然后直接打开package.mo即可。
结果展示
各个不同的颜色分别代表了不同区域兔子和狐狸的数量