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.
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 ModelicaByExample.Components.LotkaVolterra.Interfaces.Species foxes "Population of foxes in this region" annotation protected RegionalPopulation rabbit_population( initial_population=initial_rabbit_population, init=RegionalPopulation.InitializationOptions.FixedPopulation) "Rabbit population" annotation Reproduction reproduction(alpha=alpha) "Reproduction of rabbits" annotation RegionalPopulation fox_population( init=RegionalPopulation.InitializationOptions.FixedPopulation, initial_population=initial_fox_population) annotation Starvation fox_starvation(gamma=gamma) "Starvation of foxes" annotation Predation fox_predation(beta=beta, delta=delta) "Foxes eating rabbits" annotation equation connect(reproduction.species, rabbit_population.species) annotation connect(fox_predation.a, rabbit_population.species) annotation connect(fox_starvation.species, fox_population.species) annotation connect(fox_population.species, fox_predation.b) annotation connect(rabbit_population.species, rabbits) annotation connect(fox_population.species, foxes) annotation end TwoSpecies;
The diagram for this component is rendered as:
This model will be used as the basis for the regional population dynamics in subsequent models presented in this section.
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 Components.TwoSpecies B "Region B" annotation Components.TwoSpecies C "Region C" annotation Components.TwoSpecies D "Region D" annotation annotation end UnconnectedPopulations;
The diagram for this model is equally simple:
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:
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:
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 ModelicaByExample.Components.LotkaVolterra.Interfaces.Species rabbit_b "Rabbit population in Region B" annotation ModelicaByExample.Components.LotkaVolterra.Interfaces.Species fox_a "Fox population in Region A" annotation ModelicaByExample.Components.LotkaVolterra.Interfaces.Species fox_b "Fox population in Region B" annotation 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 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 Components.Migration migrate_BC "Migration from region B to region C" annotation Components.Migration migrate_CD "Migration from region C to region D" annotation equation connect(migrate_CD.rabbit_b, D.rabbits) annotation connect(migrate_CD.rabbit_a, C.rabbits) annotation connect(migrate_BC.rabbit_b, C.rabbits) annotation connect(migrate_CD.fox_b, D.foxes) annotation connect(migrate_BC.fox_b, C.foxes) annotation connect(migrate_CD.fox_a, C.foxes) annotation connect(migrate_BC.fox_a, B.foxes) annotation connect(migrate_BC.rabbit_a, B.rabbits) annotation connect(migrate_AB.fox_b, B.foxes) annotation connect(migrate_AB.rabbit_b, B.rabbits) annotation connect(migrate_AB.fox_a, A.foxes) annotation connect(migrate_AB.rabbit_a, A.rabbits) annotation end WithMigration;
the resulting system diagram becomes:
Simulating this system, we see that the population dynamics in the different regions start off out of sync, but eventually stabilize into repeating patterns:
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.