The UFZ services GitLab and Mattermost will be unavailable on Monday, July 4 from 06:00 AM to 08:00 AM due to maintenance work.

Commit a49af6a9 authored by Adam Reichold's avatar Adam Reichold
Browse files

Trace contaminations of individual flowers to be able to analyse the temporal...

Trace contaminations of individual flowers to be able to analyse the temporal structure of the contact network.
parent 700ffa27
Pipeline #53364 passed with stage
in 10 minutes and 33 seconds
......@@ -6,6 +6,17 @@ version = 3
name = "access"
version = "0.1.0"
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "autocfg"
version = "1.0.1"
......@@ -18,9 +29,9 @@ version = "0.1.0"
[[package]]
name = "cc"
version = "1.0.71"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
......@@ -56,15 +67,24 @@ dependencies = [
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "kdtree"
version = "0.1.0"
[[package]]
name = "libc"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
name = "libm"
......@@ -97,6 +117,7 @@ version = "0.1.0"
dependencies = [
"access",
"bitmap",
"hashbrown",
"kdtree",
"process",
"queue",
......@@ -117,6 +138,12 @@ dependencies = [
"libm",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "pkg-config"
version = "0.3.22"
......@@ -197,6 +224,12 @@ dependencies = [
"memmap2",
]
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
......
......@@ -15,6 +15,7 @@ path = "src/lib.rs"
[dependencies]
access = { path = "libraries/access" }
bitmap = { path = "libraries/bitmap" }
hashbrown = "0.11"
kdtree = { path = "libraries/kdtree" }
process = { path = "libraries/process" }
queue = { path = "libraries/queue" }
......
......@@ -88,9 +88,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
name = "memoffset"
......
......@@ -36,7 +36,7 @@ use crate::{
pathogen::{colony_decontamination, colony_infection_contamination, Contaminated},
space::{reduce, shortest_path, Vector},
units::Tick,
Contacts, Rng, TaggedIndex, Vectors,
Contacts, FlowerContaminators, Rng, TaggedIndex, TemporalNetwork, Vectors,
};
\end{hiddencode}
......@@ -93,6 +93,7 @@ impl Bumblebee {
flower_patch_locations: &KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: &mut Vec<&'static FlowerPatchLocation>,
visited_flowers: &mut Vec<u16>,
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
tick: Tick,
flower_patches: &mut [FlowerPatch],
......@@ -102,6 +103,7 @@ impl Bumblebee {
next_id: &mut dyn FnMut() -> u32,
contacts: &mut Contacts,
vectors: &mut Vectors,
temporal_network: &mut TemporalNetwork,
) {
let bumblebee_params = match self.insect.species {
InsectSpecies::Bumblebee(species) => &params.bumblebees[species as usize],
......@@ -166,6 +168,7 @@ impl Bumblebee {
flower_patch_locations,
visible_flower_patch_locations,
visited_flowers,
flower_contaminators,
flower_patch_events,
tick,
flower_patches,
......@@ -174,6 +177,7 @@ impl Bumblebee {
next_id,
contacts,
vectors,
temporal_network,
);
}
}
......
......@@ -27,7 +27,7 @@ use crate::{
params::Params,
space::Vector,
units::{Area, Mass, Tick},
Rng, TaggedIndex,
FlowerContaminators, Rng, TaggedIndex,
};
\end{hiddencode}
......@@ -149,6 +149,7 @@ impl Event {
tick: Tick,
flower_patches: &mut [FlowerPatch],
flowers: &mut [u64],
flower_contaminators: &mut FlowerContaminators,
) {
loop {
match events.peek() {
......@@ -176,6 +177,8 @@ impl Event {
false,
);
flower_contaminators.remove(&(*flower_patch, *flower));
events.pop();
}
_ => break,
......
......@@ -45,7 +45,7 @@ use crate::{
reduce_lifetime,
space::{reduce, Vector, WithinDistanceOfLine},
units::Tick,
Contacts, Rng, TaggedIndex, Vectors,
Contacts, FlowerContaminators, Rng, TaggedIndex, TemporalNetwork, Vectors,
};
\end{hiddencode}
......@@ -107,6 +107,7 @@ The state update \marginpar{state update} directly manages the reproductive acti
flower_patch_locations: &KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: &mut Vec<&'static FlowerPatchLocation>,
visited_flowers: &mut Vec<u16>,
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
clutch_locations: &KDTree<ClutchLocation>,
visible_clutch_locations: &mut Vec<&'static ClutchLocation>,
......@@ -119,6 +120,7 @@ The state update \marginpar{state update} directly manages the reproductive acti
next_id: &mut dyn FnMut() -> u32,
contacts: &mut Contacts,
vectors: &mut Vectors,
temporal_network: &mut TemporalNetwork,
) {
let hoverfly_params = match self.insect.species {
InsectSpecies::Hoverfly(species) => &params.hoverflies[species as usize],
......@@ -227,6 +229,7 @@ The integration with the generic insect submodel is handled via the \inlinecode{
flower_patch_locations,
visible_flower_patch_locations,
visited_flowers,
flower_contaminators,
flower_patch_events,
tick,
flower_patches,
......@@ -235,6 +238,7 @@ The integration with the generic insect submodel is handled via the \inlinecode{
next_id,
contacts,
vectors,
temporal_network,
);
}
}
......
......@@ -413,6 +413,7 @@ Finally, an associative set of lists of messages used to relocate mobile entitie
flower_patch_locations: KDTree::new(flower_patch_locations.into()),
visible_flower_patch_locations: Default::default(),
visited_flowers,
flower_contaminators: Default::default(),
flower_patch_events,
clutch_locations: KDTree::new(clutch_locations.into()),
visible_clutch_locations: Default::default(),
......
......@@ -45,7 +45,7 @@ use crate::{
reduce_lifetime,
space::{reduce, Vector, WithinDistanceOfLine},
units::{Tick, Volume},
Contacts, Rng, TaggedIndex, Vectors,
Contacts, FlowerContaminators, Rng, TaggedIndex, TemporalNetwork, Vectors,
};
\end{hiddencode}
......@@ -149,6 +149,7 @@ The state update function \marginpar{state update} implements the state machine
flower_patch_locations: &KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: &mut Vec<&'static FlowerPatchLocation>,
visited_flowers: &mut Vec<u16>,
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
tick: Tick,
flower_patches: &mut [FlowerPatch],
......@@ -157,6 +158,7 @@ The state update function \marginpar{state update} implements the state machine
next_id: &mut dyn FnMut() -> u32,
contacts: &mut Contacts,
vectors: &mut Vectors,
temporal_network: &mut TemporalNetwork,
) {
match &mut self.activity {
\end{code}
......@@ -277,18 +279,21 @@ As the scarcity of nectar is primarily used as a driver for the exploration of t
}
infection_contamination(
self.id,
params,
insect_params,
self.species,
&mut self.pathogen_status,
*index,
flower,
flower_contaminators,
flower_patch_events,
tick,
flower_patch,
flowers,
rng,
vectors,
temporal_network,
);
}
......
......@@ -24,6 +24,7 @@ pub mod units;
use std::collections::BinaryHeap;
use std::mem::transmute;
use hashbrown::HashMap;
pub use rand_chacha::ChaCha8Rng as CryptoRng;
pub use rand_pcg::Pcg64 as Rng;
......@@ -113,7 +114,11 @@ The model is implemented as a distributed simulation using communicating process
Each process keeps track of the state variables of its subworld \inlinecode{world}, the free parameters of the various submodels \inlinecode{params} and a data structure describing the spatial subdivision of the simulated landscape \inlinecode{grid}. A process also manages the heaps \inlinecode{flower_patch_events} and \inlinecode{clutch_events} for the timestamped events driving the submodels which use a DES-based formalism. This data structure allows the fast retrieval of the event with the smallest timestamp without the overhead imposed by a fully sorted arrangement.
It also contains several auxiliary data structures which are used during the simulation but are not strictly speaking part of the state of the model, i.e. the \inlinecode{relocations} which contain mobile entities received from and sent to other processes and several K-D trees \inlinecode{flower\_patch\_locations}, \inlinecode{clutch\_locations} and \inlinecode{nest\_locations} used to speed up spatial queries for stationary entities within or near to this subworld. It also manages a temporary buffer for each K-D tree which is used to collect and shuffle query results to avoid artificial correlations due to the organisation of entity locations in the search trees.
It also contains several auxiliary data structures which are used during the simulation but are not strictly speaking part of the state of the model, i.e. the \inlinecode{relocations} which contain mobile entities received from and sent to other processes and several K-D trees \inlinecode{flower_patch_locations}, \inlinecode{clutch_locations} and \inlinecode{nest_locations} used to speed up spatial queries for stationary entities within or near to this subworld. It also manages a temporary buffer for each K-D tree which is used to collect and shuffle query results to avoid artificial correlations due to the organisation of entity locations in the search trees.
Furthermore, the \inlinecode{visited_flowers} buffers are used to store the trajectories each insect takes along the flowers within a patch which are stored independently of the state of the insects as they are never relocated to another subworld as a relocation implies not being within a flower patch.
Finally, the \inlinecode{flower_contaminators} hash table is used to determine which individual is responsible for contaminating a flower wihtin a patch and is used to trace the temporal structure of epidemiologically relevant contacts.
\begin{code}
pub struct Model {
......@@ -123,6 +128,7 @@ pub struct Model {
pub flower_patch_locations: KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: Vec<&'static FlowerPatchLocation>,
visited_flowers: [Vec<Vec<u16>>; 3],
flower_contaminators: FlowerContaminators,
flower_patch_events: BinaryHeap<FlowerPatchEvent>,
pub clutch_locations: KDTree<ClutchLocation>,
visible_clutch_locations: Vec<&'static ClutchLocation>,
......@@ -131,6 +137,8 @@ pub struct Model {
visible_nest_locations: Vec<&'static NestLocation>,
relocations: MailBoxes<Relocation>,
}
pub type FlowerContaminators = HashMap<(TaggedIndex, u16), u32>;
\end{code}
The subworld state itself consists of the current time step \inlinecode{tick}, the state associated with the various entities resp. their driving submodels, a PCG pseudo-random number generator (PRNG) \inlinecode{rng} \autocite{pcg_family_algorithms_random_number_generation} and the measurements of several micro-observables.
......@@ -207,6 +215,7 @@ impl Model {
&self.flower_patch_locations,
&mut self.visible_flower_patch_locations,
&mut self.visited_flowers,
&mut self.flower_contaminators,
&mut self.flower_patch_events,
&self.clutch_locations,
&mut self.visible_clutch_locations,
......@@ -309,6 +318,7 @@ impl World {
flower_patch_locations: &KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: &mut Vec<&'static FlowerPatchLocation>,
visited_flowers: &mut [Vec<Vec<u16>>; 3],
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
clutch_locations: &KDTree<ClutchLocation>,
visible_clutch_locations: &mut Vec<&'static ClutchLocation>,
......@@ -322,6 +332,7 @@ impl World {
self.tick,
&mut self.flower_patches,
&mut self.flowers,
flower_contaminators,
);
ClutchEvent::process(clutch_events, self.tick, &mut self.clutches);
......@@ -344,6 +355,7 @@ The invocation order between the three insect families is fixed, i.e. first the
flower_patch_locations,
visible_flower_patch_locations,
&mut visited_flowers[0][idx],
flower_contaminators,
flower_patch_events,
clutch_locations,
visible_clutch_locations,
......@@ -356,6 +368,7 @@ The invocation order between the three insect families is fixed, i.e. first the
&mut self.next_id,
&mut self.contacts,
&mut self.vectors,
&mut self.temporal_network,
);
\end{code}
......@@ -415,6 +428,7 @@ Note that the entries of the \inlinecode{visited\_flowers} arrays are never disc
flower_patch_locations,
visible_flower_patch_locations,
&mut visited_flowers[1][idx],
flower_contaminators,
flower_patch_events,
nest_locations,
visible_nest_locations,
......@@ -426,6 +440,7 @@ Note that the entries of the \inlinecode{visited\_flowers} arrays are never disc
&mut self.next_id,
&mut self.contacts,
&mut self.vectors,
&mut self.temporal_network,
);
if position != solitary_bee.insect.position {
......@@ -452,6 +467,7 @@ Note that the entries of the \inlinecode{visited\_flowers} arrays are never disc
flower_patch_locations,
visible_flower_patch_locations,
&mut visited_flowers[2][idx],
flower_contaminators,
flower_patch_events,
self.tick,
&mut self.flower_patches,
......@@ -461,6 +477,7 @@ Note that the entries of the \inlinecode{visited\_flowers} arrays are never disc
&mut self.next_id,
&mut self.contacts,
&mut self.vectors,
&mut self.temporal_network,
);
if position != bumblebee.insect.position {
......@@ -533,7 +550,7 @@ unsafe impl Message for Relocation {}
For a mobile entity to properly identify a stationary entity, e.g. for memorisation of depleted flower patches, both the process rank as well as the index of the entity need to be stored. To avoid the loss of memory density due to alignment and padding, these two values are packed into a single four byte integer using a single byte for the rank and three bytes for the index, i.e. at most $2^8 = 256$ processes handling fewer than $2^{24} = 16\,777\,216$ entities of a given type are assumed.
\begin{code}
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct TaggedIndex(u32);
......
......@@ -62,7 +62,7 @@ use crate::{
insect::Species as InsectSpecies,
params::{Bumblebee as BumblebeeParams, Insect as InsectParams, Params},
units::Tick,
Rng, TaggedIndex, Vectors,
FlowerContaminators, Rng, TaggedIndex, TemporalNetwork, Vectors,
};
\end{hiddencode}
......@@ -97,19 +97,31 @@ When an insect is infected, the micro-observable \inlinecode{vectors} is updated
\begin{code}
#[allow(clippy::too_many_arguments)]
pub fn infection_contamination(
id: u32,
params: &Params,
insect_params: &InsectParams,
species: InsectSpecies,
status: &mut Status,
index: TaggedIndex,
flower: u16,
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
tick: Tick,
flower_patch: &mut FlowerPatch,
flowers: &mut [u64],
rng: &mut Rng,
vectors: &mut Vectors,
temporal_network: &mut TemporalNetwork,
) {
\end{code}
The \inlinecode{flower_contaminators} hash table is used to track which individuals have contaminated a given flower within a patch. This is used to derive a temporal network for these epidemiologically relevant contacts by collecting the edges between contaminator and visitor. This tracking is tied to contamination events instead of infection events or just visits as the decontamination time provides the relevant time scale for the temporal network.
\begin{code}
if let Some(contaminator) = flower_contaminators.get(&(index, flower)) {
temporal_network.push((tick, *contaminator, id));
}
let is_susceptible = matches!(status, Status::Susceptible);
let is_infected = status.is_infected();
......@@ -135,8 +147,10 @@ pub fn infection_contamination(
if is_infected && !is_contaminated && insect_params.contamination.sample(rng) {
let flower_patch_params = &params.flower_patches[flower_patch.species.0 as usize];
let decontaminates_at = tick + flower_patch_params.decontamination(params, rng);
flower_patch_events.push(FlowerPatchEvent::Decontamination {
happens_at: tick + flower_patch_params.decontamination(params, rng),
happens_at: decontaminates_at,
flower_patch: index,
flower,
});
......@@ -146,6 +160,8 @@ pub fn infection_contamination(
.set(flowers, flower as usize, true);
flower_patch.contaminated_by_species = MaybeUninit::new(species);
flower_contaminators.insert((index, flower), id);
}
}
\end{code}
......
......@@ -44,7 +44,7 @@ use crate::{
reduce_lifetime,
space::{reduce, shortest_path, Vector, WithinDistanceOfLine},
units::Tick,
Contacts, Rng, TaggedIndex, Vectors,
Contacts, FlowerContaminators, Rng, TaggedIndex, TemporalNetwork, Vectors,
};
\end{hiddencode}
......@@ -101,6 +101,7 @@ impl SolitaryBee {
flower_patch_locations: &KDTree<FlowerPatchLocation>,
visible_flower_patch_locations: &mut Vec<&'static FlowerPatchLocation>,
visited_flowers: &mut Vec<u16>,
flower_contaminators: &mut FlowerContaminators,
flower_patch_events: &mut BinaryHeap<FlowerPatchEvent>,
nest_locations: &KDTree<NestLocation>,
visible_nest_locations: &mut Vec<&'static NestLocation>,
......@@ -112,6 +113,7 @@ impl SolitaryBee {
next_id: &mut dyn FnMut() -> u32,
contacts: &mut Contacts,
vectors: &mut Vectors,
temporal_network: &mut TemporalNetwork,
) {
let solitary_bee_params = match self.insect.species {
InsectSpecies::SolitaryBee(species) => &params.solitary_bees[species as usize],
......@@ -233,6 +235,7 @@ impl SolitaryBee {
flower_patch_locations,
visible_flower_patch_locations,
visited_flowers,
flower_contaminators,
flower_patch_events,
tick,
flower_patches,
......@@ -241,6 +244,7 @@ impl SolitaryBee {
next_id,
contacts,
vectors,
temporal_network,
);
}
}
......
......@@ -210,6 +210,9 @@ name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "heck"
......@@ -263,15 +266,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
name = "libloading"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
dependencies = [
"cfg-if",
"winapi",
......@@ -335,6 +338,7 @@ version = "0.1.0"
dependencies = [
"access",
"bitmap",
"hashbrown",
"kdtree",
"process",
"queue",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment