Skip to main content

dexterior_core/
cochain.rs

1//! Cochains, i.e. values assigned to elements of a mesh.
2
3use nalgebra as na;
4
5use crate::{mesh::SubsetImpl, DualCellView, SimplexView};
6
7/// A vector of values corresponding to
8/// a set of `k`-dimensional cells on a mesh.
9///
10/// Cochains can be constructed using the following methods
11/// on [`SimplicialMesh`][crate::SimplicialMesh]:
12/// - [`new_zero_cochain`][crate::SimplicialMesh::new_zero_cochain]
13pub type Cochain<const DIM: usize, Primality> = CochainImpl<na::Const<DIM>, Primality>;
14
15/// The cochain type used internally by Dexterior.
16///
17/// This type cannot use const generics because they cannot currently
18/// do the compile-time generic arithmetic needed for operators.
19/// Thus, the more convenient alias [`Cochain`]
20/// is preferred for public APIs.
21#[derive(Clone)]
22pub struct CochainImpl<Dimension, Primality> {
23    /// The underlying vector of real values, exposed for convenience.
24    ///
25    /// Note that changing the dimension of this vector at runtime
26    /// will cause a dimension mismatch with operators,
27    /// leading to a panic when an operator is applied.
28    /// Use with caution.
29    ///
30    /// Values can also be accessed by indexing
31    /// with a corresponding [`SimplexView`] / [`DualCellView`]:
32    /// ```
33    /// # use dexterior_core::{mesh::tiny_mesh_3d, Cochain, Primal, Dual};
34    /// # let mesh_3d = tiny_mesh_3d();
35    /// let c: Cochain<1, Primal> = mesh_3d.new_zero_cochain();
36    /// let c_dual: Cochain<2, Dual> = mesh_3d.star() * &c;
37    ///
38    /// let boundary_edges = mesh_3d.boundary::<1>();
39    /// for edge in mesh_3d.simplices_in(&boundary_edges) {
40    ///     let edge_val = c[edge];
41    ///     let dual_val = c_dual[edge.dual()];
42    ///     // ...compared to direct access
43    ///     let edge_val = c.values[edge.index()];
44    ///     let dual_val = c_dual.values[edge.dual().index()];
45    /// }
46    /// ```
47    /// This way of access has the benefit of being typechecked at compile time,
48    /// preventing you from accidentally indexing the wrong type of cochain.
49    pub values: na::DVector<f64>,
50    _marker: std::marker::PhantomData<(Dimension, Primality)>,
51}
52
53impl<Dimension, Primality> CochainImpl<Dimension, Primality> {
54    // constructors only exposed to crate
55    // because cochains are always based on a mesh
56    // and it doesn't make sense for a user to create them directly;
57    // public constructors are methods on SimplicialMesh
58
59    #[inline]
60    pub(crate) fn from_values(values: na::DVector<f64>) -> Self {
61        Self {
62            values,
63            _marker: std::marker::PhantomData,
64        }
65    }
66
67    #[inline]
68    pub(crate) fn zeros(len: usize) -> Self {
69        Self::from_values(na::DVector::zeros(len))
70    }
71
72    /// Linearly interpolate along the line from `self` to `end`.
73    pub fn lerp(&self, end: &Self, t: f64) -> Self {
74        self + &(t * (end - self))
75    }
76
77    /// Replace a subset of this cochain's values with those of another cochain.
78    pub fn overwrite(&mut self, subset: &SubsetImpl<Dimension, Primality>, other: &Self) {
79        for i in subset.indices.ones() {
80            self.values[i] = other.values[i];
81        }
82    }
83}
84
85impl<Dimension, Primality> crate::operator::Operand for CochainImpl<Dimension, Primality> {
86    type Dimension = Dimension;
87    type Primality = Primality;
88
89    fn values(&self) -> &na::DVector<f64> {
90        &self.values
91    }
92
93    fn from_values(values: na::DVector<f64>) -> Self {
94        Self {
95            values,
96            _marker: std::marker::PhantomData,
97        }
98    }
99}
100
101// std trait impls for math ops and such
102// (several permutations needed to also work with references.
103// maybe this could be shortened with macros,
104// but I can't be bothered)
105
106impl<D, P> std::fmt::Debug for CochainImpl<D, P>
107where
108    D: na::DimName,
109{
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "{}-cochain, values {:?}", D::USIZE, self.values)
112    }
113}
114
115impl<D, P> PartialEq for CochainImpl<D, P> {
116    fn eq(&self, other: &Self) -> bool {
117        self.values == other.values
118    }
119}
120
121// Index with SimplexViews
122
123impl<'a, D, const MESH_DIM: usize> std::ops::Index<SimplexView<'a, D, MESH_DIM>>
124    for CochainImpl<D, crate::Primal>
125where
126    D: na::DimName,
127    na::Const<MESH_DIM>: na::DimNameSub<D>,
128{
129    type Output = f64;
130
131    fn index(&self, simplex: SimplexView<'a, D, MESH_DIM>) -> &Self::Output {
132        &self.values[simplex.index()]
133    }
134}
135
136impl<'a, D, const MESH_DIM: usize> std::ops::IndexMut<SimplexView<'a, D, MESH_DIM>>
137    for CochainImpl<D, crate::Primal>
138where
139    D: na::DimName,
140    na::Const<MESH_DIM>: na::DimNameSub<D>,
141{
142    fn index_mut(&mut self, simplex: SimplexView<'a, D, MESH_DIM>) -> &mut Self::Output {
143        &mut self.values[simplex.index()]
144    }
145}
146
147impl<'a, D, const MESH_DIM: usize> std::ops::Index<DualCellView<'a, D, MESH_DIM>>
148    for CochainImpl<D, crate::Dual>
149where
150    D: na::DimName,
151    na::Const<MESH_DIM>: na::DimNameSub<D>,
152{
153    type Output = f64;
154
155    fn index(&self, simplex: DualCellView<'a, D, MESH_DIM>) -> &Self::Output {
156        &self.values[simplex.index()]
157    }
158}
159
160impl<'a, D, const MESH_DIM: usize> std::ops::IndexMut<DualCellView<'a, D, MESH_DIM>>
161    for CochainImpl<D, crate::Dual>
162where
163    D: na::DimName,
164    na::Const<MESH_DIM>: na::DimNameSub<D>,
165{
166    fn index_mut(&mut self, simplex: DualCellView<'a, D, MESH_DIM>) -> &mut Self::Output {
167        &mut self.values[simplex.index()]
168    }
169}
170
171// Add
172
173impl<D, P> std::ops::Add for CochainImpl<D, P> {
174    type Output = Self;
175
176    fn add(self, rhs: Self) -> Self::Output {
177        CochainImpl::from_values(self.values + rhs.values)
178    }
179}
180
181impl<D, P> std::ops::Add<&CochainImpl<D, P>> for CochainImpl<D, P> {
182    type Output = Self;
183
184    fn add(self, rhs: &CochainImpl<D, P>) -> Self::Output {
185        CochainImpl::from_values(self.values + &rhs.values)
186    }
187}
188
189impl<D, P> std::ops::Add<CochainImpl<D, P>> for &CochainImpl<D, P> {
190    type Output = CochainImpl<D, P>;
191
192    fn add(self, rhs: CochainImpl<D, P>) -> Self::Output {
193        CochainImpl::from_values(&self.values + rhs.values)
194    }
195}
196
197impl<D, P> std::ops::Add for &CochainImpl<D, P> {
198    type Output = CochainImpl<D, P>;
199
200    fn add(self, rhs: Self) -> Self::Output {
201        CochainImpl::from_values(&self.values + &rhs.values)
202    }
203}
204
205// AddAssign
206
207impl<D, P> std::ops::AddAssign for CochainImpl<D, P> {
208    fn add_assign(&mut self, rhs: Self) {
209        self.values += rhs.values;
210    }
211}
212
213impl<D, P> std::ops::AddAssign<&CochainImpl<D, P>> for CochainImpl<D, P> {
214    fn add_assign(&mut self, rhs: &CochainImpl<D, P>) {
215        self.values += &rhs.values;
216    }
217}
218
219// Neg
220
221impl<D, P> std::ops::Neg for CochainImpl<D, P> {
222    type Output = Self;
223
224    fn neg(self) -> Self::Output {
225        Self::from_values(-self.values)
226    }
227}
228
229impl<D, P> std::ops::Neg for &CochainImpl<D, P> {
230    type Output = CochainImpl<D, P>;
231
232    fn neg(self) -> Self::Output {
233        CochainImpl::from_values(-&self.values)
234    }
235}
236
237// Sub
238
239impl<D, P> std::ops::Sub for CochainImpl<D, P> {
240    type Output = Self;
241
242    fn sub(self, rhs: Self) -> Self::Output {
243        Self::from_values(self.values - rhs.values)
244    }
245}
246
247impl<D, P> std::ops::Sub<&CochainImpl<D, P>> for CochainImpl<D, P> {
248    type Output = Self;
249
250    fn sub(self, rhs: &CochainImpl<D, P>) -> Self::Output {
251        CochainImpl::from_values(&self.values - &rhs.values)
252    }
253}
254
255impl<D, P> std::ops::Sub<CochainImpl<D, P>> for &CochainImpl<D, P> {
256    type Output = CochainImpl<D, P>;
257
258    fn sub(self, rhs: CochainImpl<D, P>) -> Self::Output {
259        CochainImpl::from_values(&self.values - &rhs.values)
260    }
261}
262
263impl<D, P> std::ops::Sub for &CochainImpl<D, P> {
264    type Output = CochainImpl<D, P>;
265
266    fn sub(self, rhs: Self) -> Self::Output {
267        CochainImpl::from_values(&self.values - &rhs.values)
268    }
269}
270
271// SubAssign
272
273impl<D, P> std::ops::SubAssign for CochainImpl<D, P> {
274    fn sub_assign(&mut self, rhs: Self) {
275        self.values -= rhs.values;
276    }
277}
278
279impl<D, P> std::ops::SubAssign<&CochainImpl<D, P>> for CochainImpl<D, P> {
280    fn sub_assign(&mut self, rhs: &CochainImpl<D, P>) {
281        self.values -= &rhs.values;
282    }
283}
284
285// Mul (scalar)
286
287impl<D, P> std::ops::Mul<CochainImpl<D, P>> for f64 {
288    type Output = CochainImpl<D, P>;
289
290    fn mul(self, rhs: CochainImpl<D, P>) -> Self::Output {
291        CochainImpl::from_values(self * rhs.values)
292    }
293}
294
295impl<D, P> std::ops::Mul<&CochainImpl<D, P>> for f64 {
296    type Output = CochainImpl<D, P>;
297
298    fn mul(self, rhs: &CochainImpl<D, P>) -> Self::Output {
299        CochainImpl::from_values(self * &rhs.values)
300    }
301}