Skip to main content

sim_lib_femm_function/
implementation.rs

1#![forbid(unsafe_code)]
2//! Quality evidence for FEMM function calls.
3
4use sim_kernel::{Cx, Symbol};
5use sim_lib_femm_core::FemmResult;
6use sim_lib_femm_post::{QuantitySpec, quantity};
7use sim_lib_femm_query::{ModelCallable, resolve_excitation};
8use sim_lib_femm_sensitiv::total_gradient;
9use sim_lib_femm_solve::{GradientTrust, SolveCertificate, SteadySolve};
10
11/// Quantity value, certificate, and optional total gradient for a completed solve.
12#[derive(Clone, Debug)]
13pub struct QualityAnswer {
14    /// Scalar value of the requested quantity.
15    pub value: f64,
16    /// Certificate describing residual, convergence, and gradient trust.
17    pub certificate: SolveCertificate,
18    /// Gradient values and trust tag when a parameter list is supplied.
19    pub gradient: Option<(Vec<f64>, GradientTrust)>,
20}
21
22/// Returns the requested quantity and the certificate for a completed solve.
23///
24/// Passing `Some(params)` for `wrt` also computes a trust-labelled total
25/// gradient and annotates the returned certificate with its trust level.
26/// Passing `None` skips gradient work.
27pub fn quality(
28    cx: &mut Cx,
29    solve: &SteadySolve,
30    quantity_spec: &QuantitySpec,
31    wrt: Option<&[Symbol]>,
32) -> FemmResult<QualityAnswer> {
33    let excitation = resolve_excitation(cx, &solve.model, &solve.solution.params, quantity_spec)?;
34    let value = quantity(&solve.solution, quantity_spec, &excitation)?;
35    let mut certificate = solve.certificate.clone();
36    let gradient = match wrt {
37        None => None,
38        Some(params) => {
39            let callable = ModelCallable {
40                model: solve.model.clone(),
41            };
42            let mut solve_for_gradient = SteadySolve {
43                model: solve.model.clone(),
44                solution: solve.solution.clone(),
45                factor: solve.factor.clone(),
46                certificate: solve.certificate.clone(),
47            };
48            let result = total_gradient(
49                cx,
50                &callable,
51                &mut solve_for_gradient,
52                std::slice::from_ref(quantity_spec),
53                params,
54            )?;
55            let values = result.gradient.into_iter().next().unwrap_or_default();
56            let trust = result
57                .trust
58                .into_iter()
59                .next()
60                .unwrap_or(GradientTrust::FiniteDifferenceOnly);
61            certificate.set_gradient_trust(trust.clone());
62            Some((values, trust))
63        }
64    };
65    Ok(QualityAnswer {
66        value,
67        certificate,
68        gradient,
69    })
70}