Lotka-Volterra with Migration

In this section, we will once again revisit the Lotka-Volterra models to understand how we can build on the work we’ve already done of creating reusable component models. We will now take the next step and create reusable models of individual geographical regions (each with the usual population dynamics) and then connect those geographical regions together with migration models.

Two Species Region

The models in this section will all make use of the following model that represents a region consisting of two populations, one of rabbits and the other foxes, with the usual Lotka-Volterra population dynamics. The Modelica source code for the model is:

within ModelicaByExample.Subsystems.LotkaVolterra.Components;
model TwoSpecies "Lotka-Volterra two species configuration"
  // Import several component models from ModelicaByExample.Components.LotkaVolterra
  import ModelicaByExample.Components.LotkaVolterra.Components.RegionalPopulation;
  import ModelicaByExample.Components.LotkaVolterra.Components.Reproduction;
  import ModelicaByExample.Components.LotkaVolterra.Components.Starvation;
  import ModelicaByExample.Components.LotkaVolterra.Components.Predation;

  parameter Real alpha=0.1 "Birth rate";
  parameter Real gamma=0.4 "Starvation coefficient";
  parameter Real initial_rabbit_population=10 "Initial rabbit population";
  parameter Real initial_fox_population=10 "Initial fox population";
  parameter Real beta=0.02 "Prey (rabbits) consumed";
  parameter Real delta=0.02 "Predators (foxes) fed";

  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species rabbits
    "Population of rabbits in this region"
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species foxes
    "Population of foxes in this region"
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
protected
  RegionalPopulation rabbit_population(
      initial_population=initial_rabbit_population,
      init=RegionalPopulation.InitializationOptions.FixedPopulation) "Rabbit population"
    annotation (Placement(transformation(extent={{-50,-60},{-30,-40}})));
  Reproduction reproduction(alpha=alpha) "Reproduction of rabbits"
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={-80,-50})));
  RegionalPopulation fox_population(
      init=RegionalPopulation.InitializationOptions.FixedPopulation,
      initial_population=initial_fox_population)
    annotation (Placement(transformation(extent={{30,-60},{50,-40}})));
  Starvation fox_starvation(gamma=gamma) "Starvation of foxes"
    annotation (Placement(transformation(extent={{70,-60},{90,-40}})));
  Predation fox_predation(beta=beta, delta=delta)
    "Foxes eating rabbits"
    annotation (Placement(transformation(extent={{-10,-30},{10,-10}})));
equation
  connect(reproduction.species, rabbit_population.species)
    annotation (Line(
      points={{-80,-40},{-80,-20},{-40,-20},{-40,-40}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(fox_predation.a, rabbit_population.species)
    annotation (Line(
      points={{-10,-20},{-40,-20},{-40,-40}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(fox_starvation.species, fox_population.species)
    annotation (Line(
      points={{80,-40},{80,-20},{40,-20},{40,-40}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(fox_population.species, fox_predation.b)
    annotation (Line(
      points={{40,-40},{40,-20},{10,-20}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(rabbit_population.species, rabbits)
    annotation (Line(
      points={{-40,-40},{-40,0},{-100,0}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(fox_population.species, foxes)
    annotation (Line(
      points={{40,-40},{40,0},{100,0}},
      color={0,127,0},
      smooth=Smooth.None));
end TwoSpecies;

The diagram for this component is rendered as:

Region containing rabbits and foxes

This model will be used as the basis for the regional population dynamics in subsequent models presented in this section.

Unconnected Regions

We’ll start by building a model that consists of four unconnected regions. The Modelica source code for such a model is quite simple:

within ModelicaByExample.Subsystems.LotkaVolterra.Examples;
model UnconnectedPopulations "Several unconnected regional populations"
  Components.TwoSpecies A "Region A"
    annotation (Placement(transformation(extent={{-10,80},{10,100}})));
  Components.TwoSpecies B "Region B"
    annotation (Placement(transformation(extent={{-10,20},{10,40}})));
  Components.TwoSpecies C "Region C"
    annotation (Placement(transformation(extent={{-10,-40},{10,-20}})));
  Components.TwoSpecies D "Region D"
    annotation (Placement(transformation(extent={{-10,-100},{10,-80}})));
  annotation (experiment(StopTime=40, Intervals=0.008));
end UnconnectedPopulations;

The diagram for this model is equally simple:

Four unconnected regional populations

If we simulate this model, each population should follow the same trajectory since their initial conditions are identical. The following plot shows that this is, in fact, the case:

../../../_images/Uncon.png

In a moment, we’ll look at the effects of migration. But in order to fully appreciate the effect that migration has, we’ll need to introduce some differences in the evolution of the different regions. So let’s modify the initial conditions of the UnconnectedPopulations model to introduce some regional variation:

within ModelicaByExample.Subsystems.LotkaVolterra.Examples;
model InitiallyDifferent "Multiple regions with different initial populations"
  extends UnconnectedPopulations(
    B(initial_rabbit_population=5, initial_fox_population=12),
    C(initial_rabbit_population=12, initial_fox_population=5),
    D(initial_rabbit_population=7, initial_fox_population=7));
end InitiallyDifferent;

Simulating this model, we see that each region has a slightly different population dynamic:

../../../_images/ID.png

Migration

Now that we have simulated the population dynamics in four unconnected regions, it would be interesting to note the impact that migration might have on these dynamics.

Consider the following Modelica model for migration:

within ModelicaByExample.Subsystems.LotkaVolterra.Components;
model Migration "Simple 'diffusion' based model of migration"
  parameter Real rabbit_migration=0.001 "Rabbit migration rate";
  parameter Real fox_migration=0.005 "Fox migration rate";
  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species rabbit_a
    "Rabbit population in Region A"
    annotation (Placement(transformation(extent={{-70,90},{-50,110}})));
  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species rabbit_b
    "Rabbit population in Region B"
    annotation (Placement(transformation(extent={{-70,-110},{-50,-90}})));
  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species fox_a
    "Fox population in Region A"
    annotation (Placement(transformation(extent={{50,90},{70,110}})));
  ModelicaByExample.Components.LotkaVolterra.Interfaces.Species fox_b
    "Fox population in Region B"
    annotation (Placement(transformation(extent={{50,-110},{70,-90}})));
equation
  rabbit_a.rate = (rabbit_a.population-rabbit_b.population)*rabbit_migration;
  rabbit_a.rate + rabbit_b.rate = 0 "Conservation of rabbits";
  fox_a.rate = (fox_a.population-fox_b.population)*fox_migration;
  fox_a.rate + fox_b.rate = 0 "Conservation of foxes";
  annotation ( Icon(graphics={
        Rectangle(
          extent={{-100,100},{100,-100}},
          lineColor={0,127,0},
          fillColor={255,255,255},
          fillPattern=FillPattern.Solid),
        Text(
          extent={{-100,20},{100,-20}},
          lineColor={0,127,0},
          fillColor={255,255,255},
          fillPattern=FillPattern.Solid,
          textString="%name",
          origin={-120,0},
          rotation=90),
        Bitmap(extent={{-84,82},{-36,-82}}, fileName=
              "modelica://ModelicaByExample/Resources/Images/rabbit.png"),
        Bitmap(extent={{18,80},{94,-84}}, fileName=
              "modelica://ModelicaByExample/Resources/Images/fox.png")}));
end Migration;

This model looks at the population of both rabbits and foxes in the connected regions and specifies a rate of migration that is proportional to the difference in population between the regions. In other words, if there are more rabbits in one region than another, the rabbits will move from the more populated region to the less population region. This is effectively a “diffusion” model of migration and does not necessarily have a basis in ecology. It is introduced simply as an example of how we could add additional effects, on top of those implemented in each region, to change the population dynamics between regions.

If we connect our previously unconnected regions with migration paths, e.g.,

within ModelicaByExample.Subsystems.LotkaVolterra.Examples;
model WithMigration "Connect populations by migration"
  extends InitiallyDifferent;
  Components.Migration migrate_AB "Migration from region A to region B"
    annotation (Placement(transformation(extent={{-10,50},{10,70}})));
  Components.Migration migrate_BC "Migration from region B to region C"
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
  Components.Migration migrate_CD "Migration from region C to region D"
    annotation (Placement(transformation(extent={{-10,-70},{10,-50}})));
equation
  connect(migrate_CD.rabbit_b, D.rabbits) annotation (Line(
      points={{-6,-70},{-6,-76},{-20,-76},{-20,-90},{-10,-90}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_CD.rabbit_a, C.rabbits) annotation (Line(
      points={{-6,-50},{-6,-44},{-20,-44},{-20,-30},{-10,-30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_BC.rabbit_b, C.rabbits) annotation (Line(
      points={{-6,-10},{-6,-16},{-20,-16},{-20,-30},{-10,-30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_CD.fox_b, D.foxes) annotation (Line(
      points={{6,-70},{6,-76},{20,-76},{20,-90},{10,-90}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_BC.fox_b, C.foxes) annotation (Line(
      points={{6,-10},{6,-16},{20,-16},{20,-30},{10,-30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_CD.fox_a, C.foxes) annotation (Line(
      points={{6,-50},{6,-44},{20,-44},{20,-30},{10,-30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_BC.fox_a, B.foxes) annotation (Line(
      points={{6,10},{6,16},{20,16},{20,30},{10,30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_BC.rabbit_a, B.rabbits) annotation (Line(
      points={{-6,10},{-6,16},{-20,16},{-20,30},{-10,30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_AB.fox_b, B.foxes) annotation (Line(
      points={{6,50},{6,50},{6,44},{20,44},{20,30},{10,30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_AB.rabbit_b, B.rabbits) annotation (Line(
      points={{-6,50},{-6,44},{-20,44},{-20,30},{-10,30}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_AB.fox_a, A.foxes) annotation (Line(
      points={{6,70},{6,76},{20,76},{20,90},{10,90}},
      color={0,127,0},
      smooth=Smooth.None));
  connect(migrate_AB.rabbit_a, A.rabbits) annotation (Line(
      points={{-6,70},{-6,76},{-20,76},{-20,90},{-10,90}},
      color={0,127,0},
      smooth=Smooth.None));
end WithMigration;

the resulting system diagram becomes:

Four regional populations with migration paths

Simulating this system, we see that the population dynamics in the different regions start off out of sync, but eventually stabilize into repeating patterns:

../../../_images/WM.png

Conclusion

Earlier, we turned the Lotka-Volterra equations into components representing predation, starvation and reproduction. In this section, we were able to use those component models to build up subsystem models to represent the population dynamics in a particular region and then link those subsystems together into a hierarchical system model that also captured the effects of migration between these distinct regions.