trellis_runner/
result.rs

1//! This module defines the default output type for a trellis calculation, in addition to the error
2//! wrapper.
3
4use crate::{State, UserState};
5use num_traits::float::FloatCore;
6use std::fmt;
7
8/// Type alias for the result of running a calculation
9pub type CalculationResult<O, S, E> = Result<Output<O, S>, TrellisError<O, E>>;
10
11/// The output of a calculation
12///
13/// The calculation output is user defined in the finalise step of the [`Calculation`] trait, but
14/// this is presented as a good verbose option in situations where the caller wants granular
15/// information about the calculation and its progress. It returns the entire original problem,
16/// solver and state object.
17pub struct Output<R, S>
18where
19    S: UserState,
20{
21    /// The original calculation carried out by `trellis`
22    pub result: R,
23    /// Solver state after the last iterationn
24    pub state: State<S>,
25}
26
27impl<R, S> std::fmt::Display for Output<R, S>
28where
29    R: fmt::Display,
30    S: UserState,
31    <S as UserState>::Float: FloatCore,
32{
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        if let Some(cause) = self.state.termination_cause() {
35            use crate::Cause::*;
36            match cause {
37                Converged => {
38                    writeln!(
39                        f,
40                        "Solver converged after {} iterations",
41                        self.state.current_iteration()
42                    )?;
43                    if let Some(duration) = self.state.duration() {
44                        writeln!(f, "Duration {:?}", duration)?;
45                    }
46                    writeln!(f, "{}", self.result)?;
47                }
48                ControlC => {
49                    writeln!(
50                        f,
51                        "Solver converged by ctrl-c intervention after {} iterations",
52                        self.state.current_iteration()
53                    )?;
54                }
55                Parent => {
56                    writeln!(
57                        f,
58                        "Solver converged by parent intervention after {} iterations",
59                        self.state.current_iteration()
60                    )?;
61                }
62                ExceededMaxIterations => {
63                    writeln!(
64                        f,
65                        "Solver exceeded maximum iterations ({})",
66                        self.state.current_iteration()
67                    )?;
68                }
69            }
70        } else {
71            writeln!(f, "Solver is still in progress.")?;
72        }
73        Ok(())
74    }
75}
76
77impl<R, S> Output<R, S>
78where
79    S: UserState,
80{
81    pub(crate) fn new(result: R, state: State<S>) -> Self {
82        Self { result, state }
83    }
84}
85
86#[derive(thiserror::Error, Debug)]
87/// An error wrapper for trellis calculations
88///
89/// The error wraps the underlying error type [`ErrorCause`], which contains information about the
90/// reason the calculation failed. In addition it can optionally return the an output from the
91/// calculation. This is useful in situations where a failure occured due to running out of
92/// iterations, or termination from the parent thread, but the state of the calculation at that
93/// point may still contain meaningful information. Maybe the calculation ran out of iterations
94/// because it was unable to reach the required tolerance, but is still at convergence?
95pub struct TrellisError<O, E> {
96    #[source]
97    /// The underlying error cause.
98    pub cause: ErrorCause<E>,
99    /// An optional result which can be extracted by the caller
100    pub result: Option<O>,
101}
102
103impl<O, E> From<E> for TrellisError<O, E> {
104    fn from(cause: E) -> Self {
105        Self {
106            cause: ErrorCause::User(cause),
107            result: None,
108        }
109    }
110}
111
112impl<O, E: ::std::fmt::Debug> ::std::fmt::Display for TrellisError<O, E> {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        writeln!(f, "Trellis error: {:?}", self.cause)
115    }
116}
117
118#[derive(thiserror::Error, Debug)]
119pub enum ErrorCause<E> {
120    #[error("error in user defined calculation: {0}")]
121    User(#[from] E),
122    #[error("exceeded maximum number of iterations")]
123    MaxIterExceeded,
124    #[error("calculation cancelled due to ctrl-c")]
125    ControlC,
126    #[error("calculation cancelled due to cancelled token")]
127    CancellationToken,
128}