use crate::{
core::{
scheduler::Scheduler,
storage::{Allocator, Real, Scalar, StorageBackend},
},
geometry::grid::{BlockId, Grid},
};
use std::{marker::PhantomData, sync::Arc};
#[derive(Debug)]
pub struct FieldHandle<T: Scalar> {
index: usize,
_marker: PhantomData<fn() -> T>,
}
#[allow(clippy::expl_impl_clone_on_copy)]
impl<T: Scalar> Clone for FieldHandle<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Scalar> Copy for FieldHandle<T> {}
impl<T: Scalar> FieldHandle<T> {
#[inline(always)]
#[must_use]
pub const fn index(self) -> usize {
self.index
}
}
#[derive(Debug, Clone)]
pub struct FieldSpec {
pub name: &'static str,
pub ghost: u32,
pub static_field: bool,
}
#[derive(Debug)]
pub struct StateLayout {
specs: Vec<FieldSpec>,
}
impl StateLayout {
#[must_use]
pub fn specs(&self) -> &[FieldSpec] {
&self.specs
}
#[inline(always)]
#[must_use]
pub fn ghost(&self, index: usize) -> u32 {
self.specs[index].ghost
}
#[must_use]
pub const fn num_fields(&self) -> usize {
self.specs.len()
}
}
pub struct StateBuilder<T: Scalar> {
specs: Vec<FieldSpec>,
_marker: PhantomData<fn() -> T>,
}
impl<T: Scalar> Default for StateBuilder<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Scalar> StateBuilder<T> {
#[must_use]
pub fn new() -> Self {
Self {
specs: Vec::new(),
_marker: PhantomData,
}
}
pub fn register(&mut self, name: &'static str, ghost: u32) -> FieldHandle<T> {
let index = self.specs.len();
self.specs.push(FieldSpec {
name,
ghost,
static_field: false,
});
FieldHandle {
index,
_marker: PhantomData,
}
}
pub fn register_static(&mut self, name: &'static str, ghost: u32) -> FieldHandle<T> {
let index = self.specs.len();
self.specs.push(FieldSpec {
name,
ghost,
static_field: true,
});
FieldHandle {
index,
_marker: PhantomData,
}
}
pub fn build<G: Grid, A: Allocator<T>>(self, grid: &G, alloc: &A) -> State<T, A::Storage> {
let layout = Arc::new(StateLayout { specs: self.specs });
let blocks = (0..grid.num_blocks())
.map(|b| BlockStorage::allocate(&layout, grid, BlockId(b as u32), alloc, true))
.collect();
State { layout, blocks }
}
}
#[derive(Debug)]
pub struct BlockStorage<T: Scalar, S: StorageBackend<T>> {
fields: Vec<S>,
_marker: PhantomData<fn() -> T>,
}
impl<T: Scalar, S: StorageBackend<T>> BlockStorage<T, S> {
fn allocate<G: Grid, A: Allocator<T, Storage = S>>(
layout: &StateLayout,
grid: &G,
block: BlockId,
alloc: &A,
with_statics: bool,
) -> Self {
Self {
fields: layout
.specs
.iter()
.map(|spec| {
if spec.static_field && !with_statics {
alloc.allocate(0)
} else {
alloc.allocate(grid.block_len(block, spec.ghost))
}
})
.collect(),
_marker: PhantomData,
}
}
pub fn bind_mut<'a>(&'a mut self, layout: &'a StateLayout) -> BlockStateMut<'a, T, S> {
BlockStateMut {
layout,
fields: &mut self.fields,
_marker: PhantomData,
}
}
}
#[derive(Debug)]
pub struct State<T: Scalar, S: StorageBackend<T>> {
layout: Arc<StateLayout>,
blocks: Vec<BlockStorage<T, S>>,
}
impl<T: Scalar, S: StorageBackend<T>> State<T, S> {
#[must_use]
pub fn layout(&self) -> &StateLayout {
&self.layout
}
#[must_use]
pub const fn num_blocks(&self) -> usize {
self.blocks.len()
}
#[must_use]
pub fn like<G: Grid, A: Allocator<T, Storage = S>>(&self, grid: &G, alloc: &A) -> Self {
self.like_impl(grid, alloc, true)
}
#[must_use]
pub fn like_tendency<G: Grid, A: Allocator<T, Storage = S>>(
&self,
grid: &G,
alloc: &A,
) -> Self {
self.like_impl(grid, alloc, false)
}
fn like_impl<G: Grid, A: Allocator<T, Storage = S>>(
&self,
grid: &G,
alloc: &A,
with_statics: bool,
) -> Self {
let blocks = (0..self.blocks.len())
.map(|b| {
BlockStorage::allocate(&self.layout, grid, BlockId(b as u32), alloc, with_statics)
})
.collect();
Self {
layout: Arc::clone(&self.layout),
blocks,
}
}
#[inline(always)]
#[must_use]
pub fn slab(&self, block: BlockId, handle: FieldHandle<T>) -> &[T] {
self.blocks[block.index()].fields[handle.index()].as_slice()
}
#[inline(always)]
pub fn slab_mut(&mut self, block: BlockId, handle: FieldHandle<T>) -> &mut [T] {
self.blocks[block.index()].fields[handle.index()].as_mut_slice()
}
#[inline(always)]
pub fn view<'a, G: Grid>(
&'a self,
grid: &'a G,
block: BlockId,
handle: FieldHandle<T>,
) -> G::View<'a, T> {
let ghost = self.layout.ghost(handle.index());
grid.view(block, ghost, self.slab(block, handle))
}
#[inline(always)]
pub fn view_mut<'a, G: Grid>(
&'a mut self,
grid: &'a G,
block: BlockId,
handle: FieldHandle<T>,
) -> G::ViewMut<'a, T> {
let ghost = self.layout.ghost(handle.index());
let slab = self.blocks[block.index()].fields[handle.index()].as_mut_slice();
grid.view_mut(block, ghost, slab)
}
pub fn split_blocks_mut(&mut self) -> (&StateLayout, &mut [BlockStorage<T, S>]) {
(&self.layout, &mut self.blocks)
}
pub fn slab_pair_mut(
&mut self,
dst: BlockId,
src: BlockId,
handle: FieldHandle<T>,
) -> (&mut [T], &[T]) {
let [d, s] = self
.blocks
.get_disjoint_mut([dst.index(), src.index()])
.expect("slab_pair_mut requires two distinct, in-range blocks");
(
d.fields[handle.index()].as_mut_slice(),
s.fields[handle.index()].as_slice(),
)
}
}
fn block_axpy<T: Real, S: StorageBackend<T>>(
mine: &mut BlockStorage<T, S>,
theirs: &BlockStorage<T, S>,
alpha: T,
) {
for (a, b) in mine.fields.iter_mut().zip(&theirs.fields) {
if a.is_empty() || b.is_empty() {
continue; }
for (x, y) in a.as_mut_slice().iter_mut().zip(b.as_slice()) {
*x += alpha * *y;
}
}
}
fn block_copy<T: Real, S: StorageBackend<T>>(
mine: &mut BlockStorage<T, S>,
theirs: &BlockStorage<T, S>,
) {
for (a, b) in mine.fields.iter_mut().zip(&theirs.fields) {
if a.is_empty() || b.is_empty() {
continue; }
a.as_mut_slice().copy_from_slice(b.as_slice());
}
}
fn block_add_noise<T: Real, S: StorageBackend<T>>(
mine: &mut BlockStorage<T, S>,
amp: &BlockStorage<T, S>,
block: usize,
scale: T,
seed: u64,
salt: u64,
) {
for (f, (x, a)) in mine.fields.iter_mut().zip(&.fields).enumerate() {
if x.is_empty() || a.is_empty() {
continue; }
let block_field_key = crate::util::rng::mix_key(seed, &[salt, block as u64, f as u64]);
for (i, (xi, ai)) in x.as_mut_slice().iter_mut().zip(a.as_slice()).enumerate() {
if *ai == T::ZERO {
continue;
}
let key = crate::util::rng::splitmix64(block_field_key ^ i as u64);
let noise = T::from_f64(crate::util::rng::standard_normal(key));
*xi += scale * *ai * noise;
}
}
}
impl<T: Real, S: StorageBackend<T>> State<T, S> {
pub fn axpy(&mut self, alpha: T, other: &Self) {
debug_assert!(Arc::ptr_eq(&self.layout, &other.layout));
for (mine, theirs) in self.blocks.iter_mut().zip(&other.blocks) {
block_axpy(mine, theirs, alpha);
}
}
pub fn axpy_with<Sch: Scheduler>(&mut self, scheduler: &Sch, alpha: T, other: &Self) {
debug_assert!(Arc::ptr_eq(&self.layout, &other.layout));
scheduler.for_each_block_mut(
&mut self.blocks,
|| (),
|id, mine, ()| {
block_axpy(mine, &other.blocks[id.index()], alpha);
},
);
}
pub fn copy_from(&mut self, other: &Self) {
debug_assert!(Arc::ptr_eq(&self.layout, &other.layout));
for (mine, theirs) in self.blocks.iter_mut().zip(&other.blocks) {
block_copy(mine, theirs);
}
}
pub fn copy_from_with<Sch: Scheduler>(&mut self, scheduler: &Sch, other: &Self) {
debug_assert!(Arc::ptr_eq(&self.layout, &other.layout));
scheduler.for_each_block_mut(
&mut self.blocks,
|| (),
|id, mine, ()| {
block_copy(mine, &other.blocks[id.index()]);
},
);
}
pub fn fill_zero(&mut self) {
for block in &mut self.blocks {
for field in &mut block.fields {
field.as_mut_slice().fill(T::ZERO);
}
}
}
pub fn fill_zero_with<Sch: Scheduler>(&mut self, scheduler: &Sch) {
scheduler.for_each_block_mut(
&mut self.blocks,
|| (),
|_, block, ()| {
for field in &mut block.fields {
field.as_mut_slice().fill(T::ZERO);
}
},
);
}
pub fn add_noise(&mut self, amplitude: &Self, scale: T, seed: u64, salt: u64) {
debug_assert!(Arc::ptr_eq(&self.layout, &litude.layout));
for (b, (mine, amp)) in self.blocks.iter_mut().zip(&litude.blocks).enumerate() {
block_add_noise(mine, amp, b, scale, seed, salt);
}
}
pub fn add_noise_with<Sch: Scheduler>(
&mut self,
scheduler: &Sch,
amplitude: &Self,
scale: T,
seed: u64,
salt: u64,
) {
debug_assert!(Arc::ptr_eq(&self.layout, &litude.layout));
scheduler.for_each_block_mut(
&mut self.blocks,
|| (),
|id, mine, ()| {
block_add_noise(
mine,
&litude.blocks[id.index()],
id.index(),
scale,
seed,
salt,
);
},
);
}
}
pub struct BlockStateMut<'a, T: Scalar, S: StorageBackend<T>> {
layout: &'a StateLayout,
fields: &'a mut [S],
_marker: PhantomData<fn() -> T>,
}
impl<T: Scalar, S: StorageBackend<T>> BlockStateMut<'_, T, S> {
#[inline(always)]
pub fn slab_mut(&mut self, handle: FieldHandle<T>) -> &mut [T] {
self.fields[handle.index()].as_mut_slice()
}
#[inline(always)]
pub fn view_mut<'s, G: Grid>(
&'s mut self,
grid: &'s G,
block: BlockId,
handle: FieldHandle<T>,
) -> G::ViewMut<'s, T> {
let ghost = self.layout.ghost(handle.index());
grid.view_mut(block, ghost, self.fields[handle.index()].as_mut_slice())
}
pub fn view_split<'s, G: Grid>(
&'s mut self,
grid: &'s G,
block: BlockId,
write: FieldHandle<T>,
read: FieldHandle<T>,
) -> (G::ViewMut<'s, T>, G::View<'s, T>) {
assert_ne!(
write.index(),
read.index(),
"cannot split a field with itself"
);
let (lo, hi) = if write.index() < read.index() {
let (lo, hi) = self.fields.split_at_mut(read.index());
(&mut lo[write.index()], &hi[0])
} else {
let (lo, hi) = self.fields.split_at_mut(write.index());
(&mut hi[0], &lo[read.index()])
};
let wg = self.layout.ghost(write.index());
let rg = self.layout.ghost(read.index());
(
grid.view_mut(block, wg, lo.as_mut_slice()),
grid.view(block, rg, hi.as_slice()),
)
}
}