一、经典 LV:单区域两物种

目标:在经典的掠食-猎物(Lotka–Volterra, LV)两物种模型上,加入区域间迁移
做法:把每个区域都当作一个“本地 LV 子系统”,然后用“扩散式”迁移把同类物种在相邻区域之间耦合(从高密度流向低密度),并保证总量守恒

最小两区域(A、B)方程:

x˙A=αxAβxAyA+Dx(xBxA),y˙A=δxAyAγyA+Dy(yByA),x˙B=αxBβxByB+Dx(xAxB),y˙B=δxByBγyB+Dy(yAyB).\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}


二、代码讲解

⚠️:下述代码仅代表部分功能实现的示例,并不是完整代码,完整代码后续会考虑上传至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端: rateA=k(XAXB),守恒: rateA+rateB=0.\text{A端:}\ \text{rate}_A = k\,(X_A - X_B),\qquad \text{守恒:}\ \text{rate}_A + \text{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:

  1. File → Open Model/Library… 选择 ModelicaByExample_CN/package.mo
  2. 打开:ModelicaByExample_CN.Subsystems.LotkaVolterra.Examples.WithMigration
  3. StopTime = 200,点击 Simulate
  4. Plot:A.rabbits.populationA.foxes.population、…、D.*

MWorks:
若对 MSL 版本敏感,把顶层 package.mo 中的
annotation(uses(Modelica(version="4.0.0")))
改成 MWorks 支持的版本(如 4.1.3),然后直接打开package.mo即可。


结果展示

各个不同的颜色分别代表了不同区域兔子和狐狸的数量

image-20251105214729685