runmat-analysis-fea 0.5.6

Finite element assembly/solve/post scaffolding for RunMat
Documentation
use crate::{
    assembly::AssemblySummary,
    operator::{apply_k, OperatorSystem},
    solve::{
        preconditioner::SpdPreconditionerKind,
        runtime_tensor_solver::{
            prepare_runtime_tensor_linear_system, solve_prepared_linear_system_runtime_tensor,
            RuntimeTensorPreparedLinearSystem,
        },
    },
};

use super::math::dot;

pub(super) struct LinearSolveAttempt {
    pub(super) vector: Vec<f64>,
    pub(super) runtime_tensor: Option<crate::solve::linear::LinearSolveResult>,
}

pub(super) struct CgSolveOptions<'a> {
    pub(super) max_iters: usize,
    pub(super) tol: f64,
    pub(super) use_runtime_tensor: bool,
    pub(super) prepared_runtime_system: Option<&'a RuntimeTensorPreparedLinearSystem>,
    pub(super) initial_guess: Option<&'a [f64]>,
}

pub(super) fn solve_k_system_cg(
    summary: &AssemblySummary,
    system: &OperatorSystem,
    rhs: &[f64],
    options: CgSolveOptions<'_>,
) -> LinearSolveAttempt {
    if options.use_runtime_tensor {
        if let Some(prepared) = options.prepared_runtime_system {
            if let Some(result) = solve_prepared_linear_system_runtime_tensor(
                summary,
                prepared,
                rhs,
                SpdPreconditionerKind::Jacobi,
                None,
            ) {
                return LinearSolveAttempt {
                    vector: result.solution.clone(),
                    runtime_tensor: Some(result),
                };
            }
        } else if let Some(fallback_prepared) = prepare_runtime_tensor_linear_system(summary) {
            if let Some(result) = solve_prepared_linear_system_runtime_tensor(
                summary,
                &fallback_prepared,
                rhs,
                SpdPreconditionerKind::Jacobi,
                None,
            ) {
                return LinearSolveAttempt {
                    vector: result.solution.clone(),
                    runtime_tensor: Some(result),
                };
            }
        }
    }

    let mut x = match options.initial_guess {
        Some(values) if values.len() == rhs.len() => values.to_vec(),
        _ => vec![0.0; rhs.len()],
    };
    let mut r = {
        let kx = apply_k(system, &x);
        rhs.iter()
            .zip(kx.iter())
            .map(|(rhs_i, kx_i)| rhs_i - kx_i)
            .collect::<Vec<f64>>()
    };
    let mut p = r.clone();
    let mut rr_old = dot(&r, &r);
    if rr_old.sqrt() <= options.tol {
        return LinearSolveAttempt {
            vector: x,
            runtime_tensor: None,
        };
    }

    for _ in 0..options.max_iters {
        let ap = apply_k(system, &p);
        let denom = dot(&p, &ap).abs().max(1.0e-12);
        let alpha = rr_old / denom;
        for i in 0..x.len() {
            x[i] += alpha * p[i];
            r[i] -= alpha * ap[i];
        }
        let rr_new = dot(&r, &r);
        if rr_new.sqrt() <= options.tol {
            break;
        }
        let beta = rr_new / rr_old.max(1.0e-12);
        for i in 0..p.len() {
            p[i] = r[i] + beta * p[i];
        }
        rr_old = rr_new;
    }

    LinearSolveAttempt {
        vector: x,
        runtime_tensor: None,
    }
}