Skip to content

Commit a02c2fc

Browse files
authored
Implement interactive demo with wrapping noise habitat + normal dispersal + species visualisation (#22)
* Initial implementation of wrapping noise scenario * Implemented proof-of-concept demo notebook * Cleaned up wrapping noise scenario and added docs * Vendored https://github.com/Mapet13/opensimplex_noise_rust to defer to MathsCore::floor
1 parent 18e388b commit a02c2fc

File tree

38 files changed

+2338
-38
lines changed

38 files changed

+2338
-38
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ default-members = [
4444
"necsim/plugins/tskit",
4545
]
4646

47+
[profile.dev]
48+
debug = false
49+
4750
[profile.release]
4851
opt-level = 3
4952
lto = "fat"

docs/simulate.ron

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,49 @@
241241
migration: (0.0 < f64 <= 1.0),
242242
)
243243
/* (almost) infinite spatially-explicit scenario with Gaussian distributed dispersal
244-
* the entire infinite landscape is habitat but, without loss of generality, has deme 1 */
244+
* the entire infinite landscape is habitat but, without loss of generality, has deme 1
245+
* the landscape is on a (wrapping) torus with 0 <= x < 2^32 and 0 <= y < 2^32 */
245246
| AlmostInfinite(
246247
/* radius of a circle from which individuals are sampled */
247248
radius: (u16),
248249
/* sigma for the Gaussian dispersal kernel N(0, sigma^2) */
249250
sigma: (0.0 <= f64),
250251
)
252+
/* (almost) infinite spatially-explicit scenario with (approximate) Gaussian distributed dispersal
253+
each location (x, y) in the landscape has either habitat for exactly one individual,
254+
or is inhabitable, depending on a sample from an OpenSimplexNoise
255+
the landscape is on a (wrapping) torus with 0 <= x < 2^32 and 0 <= y < 2^32 */
256+
| WrappingNoise(
257+
/* random seed for the noise */
258+
seed: (i64),
259+
/* percentage of the habitat that will be habitable */
260+
coverage: (0.0 <= f64 <= 1.0),
261+
/* scale of the noise, is doubled on each octave
262+
* choose 0.025 for a reasonable default */
263+
scale: (0.0 < f64 <= 1.0),
264+
/* geometric persistence factor for noise amplitude across octaves
265+
* choose 0.5 for a reasonable default */
266+
persistence: (0.0 < f64 <= 1.0),
267+
/* number of noise octaves that are accumulated for each sample
268+
* a larger number of octaves provides finer-grained habitat boundaries
269+
but is less efficient to simulate
270+
* choose 1 for only one noise sample per location and maximum efficiency */
271+
octaves: (0 < usize),
272+
/* rectangular sample area, individuals living in here are simulated
273+
* the sample area can wrap around the torus */
274+
sample: Extent(
275+
/* lower-left origin x of the sample area */
276+
x: (u32),
277+
/* lower-left origin y of the sample area */
278+
y: (u32),
279+
/* width of the sample area */
280+
width: (1 <= u64 <= 2^32),
281+
/* height of the sample area */
282+
height: (1 <= u64 <= 2^32),
283+
),
284+
/* sigma for the Gaussian dispersal kernel N(0, sigma^2) */
285+
sigma: (0.0 <= f64),
286+
)
251287
),
252288

253289
/* selection of the coalescence algorithm which is used */

necsim/core/bond/src/closed_unit_f64.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ impl ClosedUnitF64 {
8888
Self(0.0_f64)
8989
}
9090

91+
#[must_use]
92+
pub const fn half() -> Self {
93+
Self(0.5_f64)
94+
}
95+
9196
#[must_use]
9297
pub const fn one() -> Self {
9398
Self(1.0_f64)

necsim/core/bond/src/off_by_one_u64.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use core::{
77

88
use serde::{Deserialize, Deserializer, Serialize};
99

10-
use crate::OffByOneU32;
10+
use crate::{ClosedUnitF64, OffByOneU32};
1111

1212
#[derive(Debug)]
1313
#[allow(clippy::module_name_repetitions)]
@@ -150,3 +150,14 @@ impl Mul for OffByOneU64 {
150150
Self((self.0 + 1) * (other.0 + 1) - 1)
151151
}
152152
}
153+
154+
impl Mul<ClosedUnitF64> for OffByOneU64 {
155+
type Output = Self;
156+
157+
fn mul(self, other: ClosedUnitF64) -> Self::Output {
158+
#[allow(clippy::cast_possible_truncation)]
159+
#[allow(clippy::cast_sign_loss)]
160+
#[allow(clippy::cast_precision_loss)]
161+
Self(((((self.get() as f64) * other.get()) as u128) - 1) as u64)
162+
}
163+
}

necsim/core/src/cogs/habitat.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub trait Habitat<M: MathsCore>: crate::cogs::Backup + core::fmt::Debug + Sized
1515
where
1616
Self: 'a;
1717

18+
#[must_use]
19+
fn is_finite(&self) -> bool;
20+
1821
#[must_use]
1922
fn get_extent(&self) -> &LandscapeExtent;
2023

@@ -24,7 +27,7 @@ pub trait Habitat<M: MathsCore>: crate::cogs::Backup + core::fmt::Debug + Sized
2427
}
2528

2629
#[must_use]
27-
#[debug_ensures(ret.get() == {
30+
#[debug_ensures(!self.is_finite() || ret.get() == {
2831
self.iter_habitable_locations()
2932
.map(|location| u128::from(self.get_habitat_at_location(&location)))
3033
.sum()

necsim/core/src/landscape/extent.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use necsim_core_bond::OffByOneU32;
22

33
use super::Location;
44

5-
#[allow(clippy::module_name_repetitions)]
6-
#[derive(PartialEq, Eq, Clone, Debug, TypeLayout)]
5+
#[allow(clippy::module_name_repetitions, clippy::unsafe_derive_deserialize)]
6+
#[derive(PartialEq, Eq, Clone, Debug, serde::Deserialize, serde::Serialize, TypeLayout)]
7+
#[serde(rename = "Extent")]
8+
#[serde(deny_unknown_fields)]
79
#[repr(C)]
810
pub struct LandscapeExtent {
911
x: u32,

necsim/core/src/reporter/filter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl<R: Reporter, KeepSpeciation: Boolean, KeepDispersal: Boolean, KeepProgress:
2626
for FilteredReporter<R, KeepSpeciation, KeepDispersal, KeepProgress>
2727
{
2828
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
29-
fmt.debug_struct("FilteredReporter")
29+
fmt.debug_struct(stringify!(FilteredReporter))
3030
.field("reporter", &self.reporter)
3131
.finish()
3232
}

necsim/impls/cuda/src/event_buffer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<ReportSpeciation: Boolean, ReportDispersal: Boolean> fmt::Debug
4848
for EventBuffer<ReportSpeciation, ReportDispersal>
4949
{
5050
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
51-
fmt.debug_struct("EventBuffer")
51+
fmt.debug_struct(stringify!(EventBuffer))
5252
.field("max_events", &self.max_events)
5353
.field("event_counter", &self.event_counter)
5454
.finish()

necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl<M: MathsCore, H: Habitat<M>, G: RngCore<M>> core::fmt::Debug
116116
for InMemoryPackedAliasDispersalSampler<M, H, G>
117117
{
118118
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
119-
f.debug_struct("InMemoryPackedAliasDispersalSampler")
119+
f.debug_struct(stringify!(InMemoryPackedAliasDispersalSampler))
120120
.field("alias_dispersal_ranges", &self.alias_dispersal_ranges)
121121
.field(
122122
"alias_dispersal_buffer",

necsim/impls/no-std/src/cogs/dispersal_sampler/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ pub mod in_memory;
33
pub mod non_spatial;
44
pub mod spatially_implicit;
55
pub mod trespassing;
6+
pub mod wrapping_noise;

0 commit comments

Comments
 (0)