Data Model
The first step in any empirical effort is to clean and organize the data. This is also true for computational experiments! GeneDrive.jl
uses structs to enforce consistency, define relationships, and dynamically assign methods to data.
Importantly, the struct approach enables modularity: users can construct experiments in a "building block" fashion by assembling information that has already been stored as proper GeneDrive.jl
inputs. The Features section outlines the environmental, biological, and genetic details that can be defined thanks to the data model.
Once the information for an experiment has been organized using the data model, we are ready to:
- Save or share our data in a structured and reproducible way.
- Call Ordinary Differential Equation (ODE) solution methods on our data.
- Call optimization solution methods on our data.
The code below shows how to construct an example study population using data that is included with the package.
using GeneDrive
# Select species type
species = AedesAegypti
# Define how genetic information is passed on
genetics = genetics_mendelian();
# Choose functional form of environmental response for species life stages
enviro_response = stages_rossi();
# Update population size as desired
update_population_size(enviro_response, 500);
# Assemble organism
organisms = make_organisms(species, genetics, enviro_response);
OrderedCollections.OrderedDict{DataType, Organism{AedesAegypti}} with 1 entry:
AedesAegypti => Organism{AedesAegypti}(Genetics{Mendelian}(Drive[Drive{AA}(AA…
To fully define an experiment, additional information is relevant: the spatial structure of the population, the ambient temperature of the habitat, and its geographic location should also be defined. The code below demonstrates how to do this; as above, it draws on pre-structured data from GeneDrive.jl
.
# Define temperature functional form and data
temperature = example_temperature_timeseries;
# Specify the geographic coordinates
coordinates = (16.1820, 145.7210);
# Define the spatial structure, name the location, and "populate" it
node1 = Node(:YorkeysKnob, organisms, temperature, coordinates);
Node(:YorkeysKnob, OrderedCollections.OrderedDict{Type{<:Species}, Organism}(AedesAegypti => Organism{AedesAegypti}(Genetics{Mendelian}(Drive[Drive{AA}(AA, [1.0 0.5 0.0; 0.5 0.25 0.0; 0.0 0.0 0.0], 1.0, [1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0], 0.5, 1.0, 1.0, 0.0, 63.0, 1.0, 0, 1), Drive{Aa}(Aa, [0.0 0.5 1.0; 0.5 0.5 0.5; 1.0 0.5 0.0], 1.0, [1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0], 0.5, 1.0, 1.0, 0.0, 63.0, 1.0, 0, 0), Drive{aa}(aa, [0.0 0.0 0.0; 0.0 0.25 0.5; 0.0 0.5 1.0], 1.0, [1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0], 0.5, 1.0, 1.0, 0.0, 63.0, 1.0, 1, 0)], [1.0 0.5 0.0; 0.5 0.25 0.0; 0.0 0.0 0.0;;; 0.0 0.5 1.0; 0.5 0.5 0.5; 1.0 0.5 0.0;;; 0.0 0.0 0.0; 0.0 0.25 0.5; 0.0 0.5 1.0], [1.0, 1.0, 1.0], [1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0;;; 1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0;;; 1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0], [0.5, 0.5, 0.5], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [0.0, 0.0, 0.0], [63.0, 63.0, 63.0], [1.0, 1.0, 1.0], [0, 0, 1], [1, 0, 0]), OrderedCollections.OrderedDict{Type{<:LifeStage}, Stage}(Egg => Stage{Egg}(EggMortalityRossi(0.0731, 0.0595), EggDurationRossi(0.00764, 273.0, 40.55, 13094.1, 92.501, 28169.2), 2, Density{NoDensity}(NoDensity, 1.0), nothing, 0), Larva => Stage{Larva}(LarvaMortalityRossi(0.0143, 0.00189), LarvaDurationRossi(0.00219, 273.0, 25.21, 7514.34), 3, Density{LogisticDensity}(LogisticDensity, 1.0), Egg, 0), Pupa => Stage{Pupa}(PupaMortalityRossi(0.0143, 0.00189), PupaDurationRossi(0.027, -1.7, 27.7), 2, Density{NoDensity}(NoDensity, 1.0), Larva, 0), Male => Stage{Male}(AdultMortalityRossi(0.053, 0.081, 23.0, 0.0006375000000000003), NoResponse(0.0), 1, Density{NoDensity}(NoDensity, 1.0), Pupa, 0), Female => Stage{Female}(AdultMortalityRossi(0.053, 0.081, 23.0, 0.0006375000000000003), NoResponse(0.0), 1, Density{NoDensity}(NoDensity, 1.0), Pupa, 500)))), TimeSeriesTemperature([27.0, 27.5, 28.05, 28.05, 28.3, 27.8, 27.9, 28.5, 27.75, 26.8 … 27.9, 26.0, 24.9, 26.6, 27.35, 28.0, 28.4, 28.5, 28.45, 28.8], 1.0, 1), (16.182, 145.721))
If the desired spatial structure is a network, we must also define migration rates for subsets of the population that move from node to node within that network. Migration is defined as a nested dictionary wherein the rate at which each genotype and lifestage moves between locations can be optionally specified. When migration rates are not defined for adjacent nodes or specific life stages (e.g., eggs) and genotypes, the default rate is set to zero.
# Define a second node
coordinates2 = (17.0966, 145.7747);
node2 = Node(:Gordonsvale, organisms, temperature, coordinates2);
# Create a network comprised of the two nodes
network = Network(:Queensland, node1, node2);
# Specify that adult males and females of all genotypes move
migration_data = Dict( # node1 <-> node2
("Male", "AA") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02),
("Male", "Aa") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02),
("Male", "aa") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02),
("Female", "AA") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02),
("Female", "Aa") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02),
("Female", "aa") => Dict((:YorkeysKnob, :Gordonsvale) => 0.02,
(:Gordonsvale, :YorkeysKnob) => 0.02)
);
# Add migration to the network object
assign_migration!(network, migration_data, species);