pub struct Simulation {
pub model: Model,
pub lattice: Lattice,
pub volume: Vec<u32>,
pub surface: Vec<i64>,
pub cell_type: Vec<u8>,
pub moments: Vec<Moments>,
pub activity: Activity,
pub fields: Fields,
pub exchange: Vec<Exchange>,
pub mcs: u64,
pub accepted: u64,
pub attempted: u64,
/* private fields */
}Expand description
A running simulation: the lattice, one volume and one type per cell, and the random stream.
Fields§
§model: ModelThe model this run realises.
lattice: LatticeThe site labels.
volume: Vec<u32>Volume per label. Index 0 is the medium, counted like any other so the total over the vector is the lattice, and never constrained.
surface: Vec<i64>Surface per label, counted as bonds from a cell’s sites to sites of any other label, over the model’s own neighbour order.
cell_type: Vec<u8>Type per label.
moments: Vec<Moments>Second moments per label, for the length constraint.
activity: ActivityHow recently each site was taken, for the types that keep a memory.
fields: FieldsDiffusing species on the same lattice.
exchange: Vec<Exchange>Secretion and uptake per cell type.
mcs: u64Monte Carlo steps completed.
accepted: u64Copy attempts accepted since the run started.
attempted: u64Copy attempts made since the run started.
Implementations§
Source§impl Simulation
impl Simulation
Sourcepub fn tiled(model: Model, side: usize) -> Result<Simulation, String>
pub fn tiled(model: Model, side: usize) -> Result<Simulation, String>
Start a run from a tiled lattice of square cells, all of type 1.
§Errors
Whatever Model::validate reports.
Examples found in repository?
27fn main() {
28 let mut args = std::env::args().skip(1);
29 let steps: u64 = args.next().and_then(|s| s.parse().ok()).unwrap_or(100);
30 // Sizes follow the step count, so a sweep widens without a rebuild.
31 let sizes: Vec<usize> = {
32 let stated: Vec<usize> = args.filter_map(|s| s.parse().ok()).collect();
33 if stated.is_empty() {
34 vec![128, 256, 512, 1024]
35 } else {
36 stated
37 }
38 };
39
40 println!(
41 "{:>6} {:>7} {:>12} {:>12} {:>8}",
42 "size", "cells", "cpu (s)", "gpu (s)", "speedup"
43 );
44 for size in sizes {
45 let mut cpu = Simulation::tiled(model(size), 8).unwrap();
46 let cells = cpu.n_cells();
47
48 let t = Instant::now();
49 for _ in 0..steps {
50 cpu.step();
51 }
52 let cpu_s = t.elapsed().as_secs_f64();
53
54 #[cfg(feature = "cuda")]
55 let gpu_s = {
56 let seeded = Simulation::tiled(model(size), 8).unwrap();
57 match glazier::cuda::GpuSimulation::from_cpu(&seeded) {
58 Ok(mut g) => {
59 // One step first, so the compile and the upload stay out of
60 // the measurement.
61 g.step(1).unwrap();
62 let t = Instant::now();
63 g.step(steps).unwrap();
64 t.elapsed().as_secs_f64()
65 }
66 Err(e) => {
67 eprintln!("no device: {e}");
68 f64::NAN
69 }
70 }
71 };
72 #[cfg(not(feature = "cuda"))]
73 let gpu_s = f64::NAN;
74
75 println!(
76 "{size:>6} {cells:>7} {cpu_s:>12.3} {gpu_s:>12.3} {:>8.1}",
77 cpu_s / gpu_s
78 );
79 }
80 println!("\n{steps} Monte Carlo steps, one attempt per site per step.");
81}Sourcepub fn tiled_grid(
model: Model,
side: usize,
nx: usize,
ny: usize,
nz: usize,
) -> Result<Simulation, String>
pub fn tiled_grid( model: Model, side: usize, nx: usize, ny: usize, nz: usize, ) -> Result<Simulation, String>
Start a run from nx by ny square cells, leaving the rest medium.
§Errors
Whatever Model::validate reports.
Sourcepub fn set_cell_types(&mut self, types: &[u8])
pub fn set_cell_types(&mut self, types: &[u8])
Set the type of each cell, in label order, from a list one per cell.
Sourcepub fn n_cells(&self) -> usize
pub fn n_cells(&self) -> usize
Number of cells, excluding the medium.
Examples found in repository?
27fn main() {
28 let mut args = std::env::args().skip(1);
29 let steps: u64 = args.next().and_then(|s| s.parse().ok()).unwrap_or(100);
30 // Sizes follow the step count, so a sweep widens without a rebuild.
31 let sizes: Vec<usize> = {
32 let stated: Vec<usize> = args.filter_map(|s| s.parse().ok()).collect();
33 if stated.is_empty() {
34 vec![128, 256, 512, 1024]
35 } else {
36 stated
37 }
38 };
39
40 println!(
41 "{:>6} {:>7} {:>12} {:>12} {:>8}",
42 "size", "cells", "cpu (s)", "gpu (s)", "speedup"
43 );
44 for size in sizes {
45 let mut cpu = Simulation::tiled(model(size), 8).unwrap();
46 let cells = cpu.n_cells();
47
48 let t = Instant::now();
49 for _ in 0..steps {
50 cpu.step();
51 }
52 let cpu_s = t.elapsed().as_secs_f64();
53
54 #[cfg(feature = "cuda")]
55 let gpu_s = {
56 let seeded = Simulation::tiled(model(size), 8).unwrap();
57 match glazier::cuda::GpuSimulation::from_cpu(&seeded) {
58 Ok(mut g) => {
59 // One step first, so the compile and the upload stay out of
60 // the measurement.
61 g.step(1).unwrap();
62 let t = Instant::now();
63 g.step(steps).unwrap();
64 t.elapsed().as_secs_f64()
65 }
66 Err(e) => {
67 eprintln!("no device: {e}");
68 f64::NAN
69 }
70 }
71 };
72 #[cfg(not(feature = "cuda"))]
73 let gpu_s = f64::NAN;
74
75 println!(
76 "{size:>6} {cells:>7} {cpu_s:>12.3} {gpu_s:>12.3} {:>8.1}",
77 cpu_s / gpu_s
78 );
79 }
80 println!("\n{steps} Monte Carlo steps, one attempt per site per step.");
81}Sourcepub fn delta_energy(&self, target: usize, new: u32) -> f64
pub fn delta_energy(&self, target: usize, new: u32) -> f64
Energy change if site target took the label new.
The contact term counts only unlike-label bonds, which is the
(1 - delta) factor of the Potts Hamiltonian. The volume term is the
change in lambda (V - V_target)^2 for the two cells involved.
Sourcepub fn move_work(&self, new: u32, old: u32, target: usize, source: usize) -> f64
pub fn move_work(&self, new: u32, old: u32, target: usize, source: usize) -> f64
Work the cell gaining the site does against its own memory and its drift.
Both read the two sites a copy runs between, so both are properties of
the move rather than of the configuration and neither appears in
Simulation::energy.
Sourcepub fn chemotaxis_work(&self, new: u32, target: usize, source: usize) -> f64
pub fn chemotaxis_work(&self, new: u32, target: usize, source: usize) -> f64
Work done against a chemical gradient by the cell gaining the site.
This is a property of the move rather than of the configuration: it
reads the field at the two sites the copy runs between, so it has no
counterpart in Simulation::energy and cannot be checked by
recomputing a total. A positive sensitivity makes a move up the
gradient cheaper, which is what makes a cell climb it.
Sourcepub fn attempt(&mut self) -> bool
pub fn attempt(&mut self) -> bool
One copy attempt: a random target site takes the label of a random neighbour, accepted by the Metropolis rule.
A copy that would empty a cell is refused, so a cell never vanishes and the label set is fixed for the run.
Sourcepub fn step(&mut self)
pub fn step(&mut self)
One Monte Carlo step: as many copy attempts as there are sites, then the fields diffuse, decay and exchange with the cells that own the sites they sit on.
Examples found in repository?
27fn main() {
28 let mut args = std::env::args().skip(1);
29 let steps: u64 = args.next().and_then(|s| s.parse().ok()).unwrap_or(100);
30 // Sizes follow the step count, so a sweep widens without a rebuild.
31 let sizes: Vec<usize> = {
32 let stated: Vec<usize> = args.filter_map(|s| s.parse().ok()).collect();
33 if stated.is_empty() {
34 vec![128, 256, 512, 1024]
35 } else {
36 stated
37 }
38 };
39
40 println!(
41 "{:>6} {:>7} {:>12} {:>12} {:>8}",
42 "size", "cells", "cpu (s)", "gpu (s)", "speedup"
43 );
44 for size in sizes {
45 let mut cpu = Simulation::tiled(model(size), 8).unwrap();
46 let cells = cpu.n_cells();
47
48 let t = Instant::now();
49 for _ in 0..steps {
50 cpu.step();
51 }
52 let cpu_s = t.elapsed().as_secs_f64();
53
54 #[cfg(feature = "cuda")]
55 let gpu_s = {
56 let seeded = Simulation::tiled(model(size), 8).unwrap();
57 match glazier::cuda::GpuSimulation::from_cpu(&seeded) {
58 Ok(mut g) => {
59 // One step first, so the compile and the upload stay out of
60 // the measurement.
61 g.step(1).unwrap();
62 let t = Instant::now();
63 g.step(steps).unwrap();
64 t.elapsed().as_secs_f64()
65 }
66 Err(e) => {
67 eprintln!("no device: {e}");
68 f64::NAN
69 }
70 }
71 };
72 #[cfg(not(feature = "cuda"))]
73 let gpu_s = f64::NAN;
74
75 println!(
76 "{size:>6} {cells:>7} {cpu_s:>12.3} {gpu_s:>12.3} {:>8.1}",
77 cpu_s / gpu_s
78 );
79 }
80 println!("\n{steps} Monte Carlo steps, one attempt per site per step.");
81}Sourcepub fn energy(&self) -> f64
pub fn energy(&self) -> f64
Total energy of the current configuration.
Each unlike-label bond is counted once, so this is comparable between neighbour orders and is what the incremental deltas must reproduce.
Sourcepub fn recounted_moments(&self) -> Vec<Moments>
pub fn recounted_moments(&self) -> Vec<Moments>
Recompute the moments from the lattice, for checking the bookkeeping.
Sourcepub fn recounted_surfaces(&self) -> Vec<i64>
pub fn recounted_surfaces(&self) -> Vec<i64>
Recount the surfaces from the lattice, for checking the bookkeeping.
Sourcepub fn recounted_volumes(&self) -> Vec<u32>
pub fn recounted_volumes(&self) -> Vec<u32>
Recount the volumes from the lattice, for checking the bookkeeping.
Source§impl Simulation
impl Simulation
Sourcepub fn divide_and_die(&mut self) -> (usize, usize)
pub fn divide_and_die(&mut self) -> (usize, usize)
Cells that have reached their division volume split, and cells of a type with a death rate die.
A dividing cell is cut by the line through its centroid perpendicular to its major axis, so the halves are the compact ones. A dying cell’s sites become medium. Both change the boundary of every neighbour, so the surfaces are recounted whenever either happens rather than patched.
Sourcepub fn live_cells(&self) -> usize
pub fn live_cells(&self) -> usize
Labels with sites on the lattice.
Trait Implementations§
Source§impl Clone for Simulation
impl Clone for Simulation
Source§fn clone(&self) -> Simulation
fn clone(&self) -> Simulation
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more