kryst 4.0.4

Krylov subspace and preconditioned iterative solvers for dense and sparse linear systems, with shared and distributed memory parallelism.
use crate::error::KError;
use crate::matrix::DistCsrOp;
use crate::matrix::op::LinOp;
use crate::parallel::Comm;
use crate::preconditioner::asm::{AsmBlockSolver, AsmInnerPc};

use super::{DistLocalApplyMode, DistPcBuilder, GlobalPcKind};

fn strict_mode_error(global_pc: GlobalPcKind, detail_key: &str, detail: &str) -> KError {
    KError::InvalidInput(format!(
        "err_key=pc_dist_strict_mode_rejected pc_dist_local_apply=strict pc_global={global_pc:?} detail_key={detail_key} detail={detail}"
    ))
}

fn asm_local_solver_supports_native(block_solver: AsmBlockSolver, inner_pc: AsmInnerPc) -> bool {
    match (block_solver, inner_pc) {
        (AsmBlockSolver::Csr, AsmInnerPc::Jacobi)
        | (AsmBlockSolver::LuDense, AsmInnerPc::Jacobi)
        | (AsmBlockSolver::Csr, AsmInnerPc::Ilu0)
        | (AsmBlockSolver::Csr, AsmInnerPc::Ilut { .. })
        | (AsmBlockSolver::LuDense, AsmInnerPc::Ilutp { .. }) => true,
        (AsmBlockSolver::LuDense, AsmInnerPc::Ilu0)
        | (AsmBlockSolver::LuDense, AsmInnerPc::Ilut { .. })
        | (AsmBlockSolver::Csr, AsmInnerPc::Ilutp { .. }) => false,
    }
}

fn validate_asm_like_strict_mode(
    dist_op: &DistCsrOp,
    global_pc: GlobalPcKind,
    overlap: usize,
    block_solver: AsmBlockSolver,
    inner_pc: AsmInnerPc,
    local_apply_mode: DistLocalApplyMode,
) -> Result<(), KError> {
    if !local_apply_mode.requires_native() {
        return Ok(());
    }
    if overlap == 0 {
        return Err(strict_mode_error(
            global_pc,
            "overlap_mode",
            "strict distributed local-apply requires overlap>0 for ASM/RAS native kernels",
        ));
    }
    if !asm_local_solver_supports_native(block_solver, inner_pc) {
        return Err(strict_mode_error(
            global_pc,
            "local_solver_support",
            "strict distributed local-apply requires a supported ASM/RAS block_solver + inner_pc pair",
        ));
    }
    if dist_op.comm().size() <= 1 {
        return Err(strict_mode_error(
            global_pc,
            "communication_plan_constraints",
            "strict distributed local-apply requires MPI communicator size>1 for ASM/RAS communication plans",
        ));
    }
    Ok(())
}

pub fn validate_dist_builder_strict_mode(
    dist_op: &DistCsrOp,
    builder: &DistPcBuilder,
) -> Result<(), KError> {
    match builder {
        DistPcBuilder::BlockJacobi { opts } => {
            if !opts.local_apply_mode.requires_native() {
                return Ok(());
            }
            if !opts.local_pc.build_capabilities().native_local_apply {
                Err(strict_mode_error(
                    GlobalPcKind::BlockJacobi,
                    "unsupported_local_pc",
                    "block-jacobi strict mode requires native local kernel; local pc supports wrapped_local only",
                ))
            } else {
                Ok(())
            }
        }
        DistPcBuilder::Asm {
            overlap,
            block_solver,
            inner_pc,
            local_apply_mode,
            ..
        } => validate_asm_like_strict_mode(
            dist_op,
            GlobalPcKind::Asm,
            *overlap,
            *block_solver,
            *inner_pc,
            *local_apply_mode,
        ),
        DistPcBuilder::Ras {
            overlap,
            block_solver,
            inner_pc,
            local_apply_mode,
            ..
        } => validate_asm_like_strict_mode(
            dist_op,
            GlobalPcKind::Ras,
            *overlap,
            *block_solver,
            *inner_pc,
            *local_apply_mode,
        ),
    }
}