lattice_qcd_rs/integrator/
symplectic_euler.rs

1//! Basic symplectic Euler integrator.
2//!
3//! For an example see the module level documentation [`super`].
4
5use std::vec::Vec;
6
7use na::SVector;
8#[cfg(feature = "serde-serialize")]
9use serde::{Deserialize, Serialize};
10
11use super::{
12    super::{
13        field::{EField, LinkMatrix, Su3Adjoint},
14        lattice::LatticeCyclic,
15        simulation::{
16            LatticeState, LatticeStateWithEField, LatticeStateWithEFieldNew, SimulationStateLeap,
17            SimulationStateSynchronous,
18        },
19        thread::{run_pool_parallel_vec, ThreadError},
20        CMatrix3, Real,
21    },
22    integrate_efield, integrate_link, SymplecticIntegrator,
23};
24
25/// Error for [`SymplecticEuler`].
26#[derive(Debug, Clone, Eq, PartialEq, Hash)]
27#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
28pub enum SymplecticEulerError<Error> {
29    /// multithreading error, see [`ThreadError`].
30    ThreadingError(ThreadError),
31    /// Other Error cause in non threaded section
32    StateInitializationError(Error),
33}
34
35impl<Error> From<ThreadError> for SymplecticEulerError<Error> {
36    fn from(err: ThreadError) -> Self {
37        Self::ThreadingError(err)
38    }
39}
40
41impl<Error: core::fmt::Display> core::fmt::Display for SymplecticEulerError<Error> {
42    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43        match self {
44            Self::ThreadingError(error) => write!(f, "thread error: {}", error),
45            Self::StateInitializationError(error) => write!(f, "initialization error: {}", error),
46        }
47    }
48}
49
50impl<Error: std::error::Error + 'static> std::error::Error for SymplecticEulerError<Error> {
51    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52        match self {
53            Self::ThreadingError(error) => Some(error),
54            Self::StateInitializationError(error) => Some(error),
55        }
56    }
57}
58
59/// Basic symplectic Euler integrator
60///
61/// slightly slower than [`super::SymplecticEulerRayon`] (for appropriate choice of `number_of_thread`)
62/// but use less memory
63///
64/// For an example see the module level documentation [`super`].
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
66#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
67pub struct SymplecticEuler {
68    number_of_thread: usize,
69}
70
71impl SymplecticEuler {
72    getter_copy!(
73        /// getter on the number of thread the integrator use.
74        pub const,
75        number_of_thread,
76        usize
77    );
78
79    /// Create a integrator using a set number of threads
80    pub const fn new(number_of_thread: usize) -> Self {
81        Self { number_of_thread }
82    }
83
84    fn link_matrix_integrate<State, const D: usize>(
85        self,
86        link_matrix: &LinkMatrix,
87        e_field: &EField<D>,
88        lattice: &LatticeCyclic<D>,
89        delta_t: Real,
90    ) -> Result<Vec<CMatrix3>, ThreadError>
91    where
92        State: LatticeStateWithEField<D>,
93    {
94        run_pool_parallel_vec(
95            lattice.get_links(),
96            &(link_matrix, e_field, lattice),
97            &|link, (link_matrix, e_field, lattice)| {
98                integrate_link::<State, D>(link, link_matrix, e_field, lattice, delta_t)
99            },
100            self.number_of_thread,
101            lattice.number_of_canonical_links_space(),
102            lattice,
103            &CMatrix3::zeros(),
104        )
105        .map_err(|err| err.into())
106    }
107
108    fn e_field_integrate<State, const D: usize>(
109        self,
110        link_matrix: &LinkMatrix,
111        e_field: &EField<D>,
112        lattice: &LatticeCyclic<D>,
113        delta_t: Real,
114    ) -> Result<Vec<SVector<Su3Adjoint, D>>, ThreadError>
115    where
116        State: LatticeStateWithEField<D>,
117    {
118        run_pool_parallel_vec(
119            lattice.get_points(),
120            &(link_matrix, e_field, lattice),
121            &|point, (link_matrix, e_field, lattice)| {
122                integrate_efield::<State, D>(point, link_matrix, e_field, lattice, delta_t)
123            },
124            self.number_of_thread,
125            lattice.number_of_points(),
126            lattice,
127            &SVector::<_, D>::from_element(Su3Adjoint::default()),
128        )
129        .map_err(|err| err.into())
130    }
131}
132
133impl Default for SymplecticEuler {
134    /// Default value using the number of threads rayon would use,
135    /// see [`rayon::current_num_threads()`].
136    fn default() -> Self {
137        Self::new(rayon::current_num_threads().min(1))
138    }
139}
140
141impl std::fmt::Display for SymplecticEuler {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(
144            f,
145            "Euler integrator with {} thread",
146            self.number_of_thread()
147        )
148    }
149}
150
151impl<State, const D: usize> SymplecticIntegrator<State, SimulationStateLeap<State, D>, D>
152    for SymplecticEuler
153where
154    State: SimulationStateSynchronous<D> + LatticeStateWithEField<D> + LatticeStateWithEFieldNew<D>,
155{
156    type Error = SymplecticEulerError<State::Error>;
157
158    fn integrate_sync_sync(&self, l: &State, delta_t: Real) -> Result<State, Self::Error> {
159        let link_matrix = self.link_matrix_integrate::<State, D>(
160            l.link_matrix(),
161            l.e_field(),
162            l.lattice(),
163            delta_t,
164        )?;
165        let e_field =
166            self.e_field_integrate::<State, D>(l.link_matrix(), l.e_field(), l.lattice(), delta_t)?;
167
168        State::new(
169            l.lattice().clone(),
170            l.beta(),
171            EField::new(e_field),
172            LinkMatrix::new(link_matrix),
173            l.t() + 1,
174        )
175        .map_err(SymplecticEulerError::StateInitializationError)
176    }
177
178    fn integrate_leap_leap(
179        &self,
180        l: &SimulationStateLeap<State, D>,
181        delta_t: Real,
182    ) -> Result<SimulationStateLeap<State, D>, Self::Error> {
183        let link_matrix = LinkMatrix::new(self.link_matrix_integrate::<State, D>(
184            l.link_matrix(),
185            l.e_field(),
186            l.lattice(),
187            delta_t,
188        )?);
189        let e_field = EField::new(self.e_field_integrate::<State, D>(
190            &link_matrix,
191            l.e_field(),
192            l.lattice(),
193            delta_t,
194        )?);
195        SimulationStateLeap::<State, D>::new(
196            l.lattice().clone(),
197            l.beta(),
198            e_field,
199            link_matrix,
200            l.t() + 1,
201        )
202        .map_err(SymplecticEulerError::StateInitializationError)
203    }
204
205    fn integrate_sync_leap(
206        &self,
207        l: &State,
208        delta_t: Real,
209    ) -> Result<SimulationStateLeap<State, D>, Self::Error> {
210        let e_field = self.e_field_integrate::<State, D>(
211            l.link_matrix(),
212            l.e_field(),
213            l.lattice(),
214            delta_t / 2_f64,
215        )?;
216        // we do not advance the step counter
217        SimulationStateLeap::<State, D>::new(
218            l.lattice().clone(),
219            l.beta(),
220            EField::new(e_field),
221            l.link_matrix().clone(),
222            l.t(),
223        )
224        .map_err(SymplecticEulerError::StateInitializationError)
225    }
226
227    fn integrate_leap_sync(
228        &self,
229        l: &SimulationStateLeap<State, D>,
230        delta_t: Real,
231    ) -> Result<State, Self::Error> {
232        let link_matrix = LinkMatrix::new(self.link_matrix_integrate::<State, D>(
233            l.link_matrix(),
234            l.e_field(),
235            l.lattice(),
236            delta_t,
237        )?);
238        // we advance the counter by one
239        let e_field = EField::new(self.e_field_integrate::<State, D>(
240            &link_matrix,
241            l.e_field(),
242            l.lattice(),
243            delta_t / 2_f64,
244        )?);
245        State::new(
246            l.lattice().clone(),
247            l.beta(),
248            e_field,
249            link_matrix,
250            l.t() + 1,
251        )
252        .map_err(SymplecticEulerError::StateInitializationError)
253    }
254
255    fn integrate_symplectic(&self, l: &State, delta_t: Real) -> Result<State, Self::Error> {
256        // override for optimization.
257        // This remove a clone operation.
258
259        let e_field_half = EField::new(self.e_field_integrate::<State, D>(
260            l.link_matrix(),
261            l.e_field(),
262            l.lattice(),
263            delta_t / 2_f64,
264        )?);
265        let link_matrix = LinkMatrix::new(self.link_matrix_integrate::<State, D>(
266            l.link_matrix(),
267            &e_field_half,
268            l.lattice(),
269            delta_t,
270        )?);
271        let e_field = EField::new(self.e_field_integrate::<State, D>(
272            &link_matrix,
273            &e_field_half,
274            l.lattice(),
275            delta_t / 2_f64,
276        )?);
277
278        State::new(
279            l.lattice().clone(),
280            l.beta(),
281            e_field,
282            link_matrix,
283            l.t() + 1,
284        )
285        .map_err(SymplecticEulerError::StateInitializationError)
286    }
287}