use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::io::ErrorKind;
use std::path::PathBuf;
use std::sync::{Arc, OnceLock, RwLock};
use std::time::Instant;

use chrono::Utc;
use runmat_analysis_core::{
    validate_model_against_geometry, AnalysisField, AnalysisInterfaceKind, AnalysisModel,
    AnalysisModelId, AnalysisStep, AnalysisStepKind, AnalysisValidationError, BoundaryCondition,
    BoundaryConditionKind, EvidenceConfidence, LoadCase, LoadKind, MaterialAssignment,
    MaterialMechanicalModel, MaterialModel, MaterialThermalModel, ReferenceFrame,
};
use runmat_analysis_fea::solve::backend::kind::LinearAlgebraBackendKind;
use runmat_analysis_fea::solve::preconditioner::SpdPreconditionerKind;
use runmat_analysis_fea::{
    fea_acoustic_frequency_response_field_id, fea_cht_energy_residual_field_id,
    fea_cht_fluid_temperature_field_id, fea_cht_interface_heat_flux_field_id,
    fea_cht_interface_temperature_jump_field_id, fea_cht_solid_temperature_field_id,
    fea_fsi_coupling_iteration_count_field_id, fea_fsi_fluid_pressure_field_id,
    fea_fsi_fluid_velocity_field_id, fea_fsi_interface_displacement_field_id,
    fea_fsi_interface_pressure_field_id, fea_fsi_interface_residual_field_id,
    fea_fsi_interface_traction_field_id, fea_fsi_structural_displacement_field_id,
    run_electromagnetic_with_options, run_linear_static_with_options, run_modal_with_options,
    run_nonlinear_with_options, run_thermal_with_options, run_transient_with_options,
    ComputeBackend, ElectromagneticSolveOptions, FeaProgressEvent, FeaProgressHandler,
    FeaProgressPhase, FeaProgressStatus, FeaRunError, FeaRunResult, LinearStaticSolveOptions,
    ModalSolveOptions, ThermalSolveOptions, FEA_FIELD_ACOUSTIC_PARTICLE_VELOCITY,
    FEA_FIELD_ACOUSTIC_PHASE, FEA_FIELD_ACOUSTIC_PRESSURE_IMAG,
    FEA_FIELD_ACOUSTIC_PRESSURE_MAGNITUDE, FEA_FIELD_ACOUSTIC_PRESSURE_REAL,
    FEA_FIELD_ACOUSTIC_SOUND_PRESSURE_LEVEL_DB, FEA_FIELD_CFD_PRESSURE,
    FEA_FIELD_CFD_RESIDUAL_CONTINUITY, FEA_FIELD_CFD_RESIDUAL_MOMENTUM,
    FEA_FIELD_CFD_REYNOLDS_NUMBER, FEA_FIELD_CFD_VELOCITY, FEA_FIELD_CFD_VORTICITY,
    FEA_FIELD_CFD_WALL_SHEAR_STRESS, FEA_FIELD_CHT_FLUID_PRESSURE, FEA_FIELD_CHT_FLUID_VELOCITY,
};
use runmat_geometry_core::{GeometryAsset, MaterialEvidenceConfidence, UnitSystem};
use runmat_meshing_core::{ElementFamilyHint, MeshConnectivityClass};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::operations::{
    operation_error, OperationContext, OperationEnvelope, OperationErrorEnvelope,
    OperationErrorSeverity, OperationErrorSpec, OperationErrorType,
};
use policy::{
    breach_rate_greater_than, breach_rate_less_than, electromagnetic_sweep_thresholds_for_policy,
    electromagnetic_thresholds_for_policy, thermo_field_quality_thresholds_for_policy,
    thermo_gradient_thresholds_for_policy, thermo_thresholds_for_policy,
    ElectromagneticQualityThresholds, EM_ASSIGNMENT_COVERAGE_MIN_BALANCED,
    EM_BOUNDARY_ANCHOR_MIN_BALANCED, EM_BOUNDARY_ENERGY_MIN_BALANCED,
    EM_BOUNDARY_LOCALIZATION_MIN_BALANCED, EM_BOUNDARY_PENALTY_CONTRIBUTION_MAX_BALANCED,
    EM_CONDITIONING_MAX_BALANCED, EM_CONDUCTIVITY_SPREAD_THRESHOLD_BALANCED,
    EM_ENERGY_IMBALANCE_MAX_BALANCED, EM_FLUX_DIVERGENCE_MAX_BALANCED,
    EM_GROUND_EFFECTIVENESS_MIN_BALANCED, EM_HETEROGENEITY_THRESHOLD_BALANCED,
    EM_IMAG_RESIDUAL_MAX_BALANCED, EM_INSULATION_LEAKAGE_MAX_BALANCED,
    EM_REAL_RESIDUAL_MAX_BALANCED, EM_REGION_CONTRAST_MAX_BALANCED, EM_RESONANCE_Q_MIN_BALANCED,
    EM_SOURCE_INTERFERENCE_MAX_BALANCED, EM_SOURCE_MATERIAL_ALIGNMENT_MIN_BALANCED,
    EM_SOURCE_OVERLAP_MAX_BALANCED, EM_SOURCE_REALIZATION_MIN_BALANCED,
    EM_SOURCE_REGION_COVERAGE_MIN_BALANCED, EM_SOURCE_REGION_ENERGY_CONSISTENCY_MIN_BALANCED,
    EM_SWEEP_COUNT_MIN_BALANCED, THERMO_HETEROGENEITY_THRESHOLD_BALANCED,
    THERMO_SPREAD_THRESHOLD_BALANCED,
};

mod contracts;
mod fea_document;
#[cfg(feature = "plot-core")]
mod figures;
mod policy;
mod promotion;
pub mod storage;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FeaRuntimeConfig {
    pub artifact_root: Option<PathBuf>,
    pub study_artifact_root: Option<PathBuf>,
    pub thermo_field_artifact_root: Option<PathBuf>,
}

fn fea_runtime_config() -> &'static RwLock<FeaRuntimeConfig> {
    static CONFIG: OnceLock<RwLock<FeaRuntimeConfig>> = OnceLock::new();
    CONFIG.get_or_init(|| RwLock::new(FeaRuntimeConfig::default()))
}

fn current_fea_runtime_config() -> FeaRuntimeConfig {
    fea_runtime_config()
        .read()
        .map(|guard| guard.clone())
        .unwrap_or_default()
}

pub fn default_fea_artifact_root() -> PathBuf {
    PathBuf::from("artifacts")
}

pub fn configure_fea_runtime(config: FeaRuntimeConfig) -> Result<(), String> {
    let mut guard = fea_runtime_config()
        .write()
        .map_err(|_| "FEA runtime config lock poisoned".to_string())?;
    *guard = config;
    Ok(())
}

thread_local! {
    static FEA_PROGRESS_HANDLER: RefCell<Option<FeaProgressHandler>> = const { RefCell::new(None) };
}

pub struct FeaProgressHandlerGuard {
    previous: Option<FeaProgressHandler>,
}

impl Drop for FeaProgressHandlerGuard {
    fn drop(&mut self) {
        FEA_PROGRESS_HANDLER.with(|slot| {
            slot.replace(self.previous.take());
        });
    }
}

pub fn replace_fea_progress_handler(
    handler: Option<FeaProgressHandler>,
) -> FeaProgressHandlerGuard {
    let previous = FEA_PROGRESS_HANDLER.with(|slot| slot.replace(handler));
    FeaProgressHandlerGuard { previous }
}

fn install_fea_solver_context() -> runmat_analysis_fea::FeaProgressContextGuard {
    let host_handler = FEA_PROGRESS_HANDLER.with(|slot| slot.borrow().clone());
    let handler = Some(Arc::new(move |event: FeaProgressEvent| {
        tracing::info!(
            target: "runmat_analysis",
            operation = %event.operation,
            phase = ?event.phase,
            status = ?event.status,
            current = event.current,
            total = event.total,
            fraction = event.fraction,
            "{}", event.message
        );
        if let Some(host_handler) = host_handler.as_ref() {
            host_handler(event);
        }
    }) as FeaProgressHandler);
    runmat_analysis_fea::replace_fea_progress_context(
        handler,
        Some(Arc::new(crate::interrupt::is_cancelled)),
    )
}

pub use contracts::{
    AnalysisAcousticRunOptions, AnalysisCfdRunOptions, AnalysisChtRunOptions,
    AnalysisCreateModelIntentSpec, AnalysisCreateModelPrepContext, AnalysisCreateModelProfile,
    AnalysisElectromagneticRunOptions, AnalysisFieldDescriptor, AnalysisFieldKind,
    AnalysisFieldLocation, AnalysisFieldStorage, AnalysisFsiRunOptions, AnalysisModalRunOptions,
    AnalysisNonlinearRunOptions, AnalysisRenderMesh, AnalysisRenderTopology,
    AnalysisRenderTopologySource, AnalysisResultsCompareData, AnalysisResultsCompareQuery,
    AnalysisResultsData, AnalysisResultsQuery, AnalysisResultsSummary, AnalysisRunKind,
    AnalysisRunOptions, AnalysisRunPrepContext, AnalysisRunResult, AnalysisStudyIssue,
    AnalysisStudyPlanData, AnalysisStudyRunData, AnalysisStudySpec, AnalysisStudySweepData,
    AnalysisStudySweepFailureEntry, AnalysisStudySweepPlanData, AnalysisStudySweepPlanEntry,
    AnalysisStudySweepRunEntry, AnalysisStudySweepSpec, AnalysisStudySweepValidateData,
    AnalysisStudySweepValidateEntry, AnalysisStudyValidateResult, AnalysisThermalRunOptions,
    AnalysisTransientRunOptions, AnalysisTrendKindSummary, AnalysisTrendsData, AnalysisTrendsQuery,
    AnalysisValidateResult, ContactInterfaceOptions, ElectroRegionConductivityScale,
    ElectroThermalCouplingOptions, ElectroTimeProfilePoint, ElectromagneticResultsData,
    ModalFrequencyBasis, ModalFrequencyUnits, ModalResultsData, NonlinearMethod,
    NonlinearResultsData, PlasticityConstitutiveOptions, PrecisionMode, PreconditionerMode,
    PrepCalibrationProfile, QualityGate, QualityPolicy, QualityReason, QualityReasonCode,
    RunProvenance, RunStatus, ThermalResultsData, ThermoFieldInterpolationMode, ThermoFieldSource,
    ThermoMechanicalCouplingOptions, ThermoRegionTemperatureDelta, ThermoTimeProfilePoint,
    TransientIntegrationMethod, TransientResultsData,
};
pub use fea_document::{
    is_fea_file_path, load_fea_document_from_path_async, parse_and_resolve_fea_document,
    FeaResolvedDocument,
};
#[cfg(feature = "plot-core")]
pub use figures::{
    analysis_generate_study_run_figures, AnalysisFigureGenerationOptions, AnalysisGeneratedFigure,
    AnalysisGeneratedFigureKind,
};

const ANALYSIS_CREATE_MODEL_OPERATION: &str = "fea.create_model";
const ANALYSIS_CREATE_MODEL_OP_VERSION: &str = "fea.create_model/v1";
const ANALYSIS_VALIDATE_STUDY_OPERATION: &str = "fea.validate_study";
const ANALYSIS_VALIDATE_STUDY_OP_VERSION: &str = "fea.validate_study/v1";
const ANALYSIS_PLAN_STUDY_OPERATION: &str = "fea.plan_study";
const ANALYSIS_PLAN_STUDY_OP_VERSION: &str = "fea.plan_study/v1";
const ANALYSIS_PLAN_STUDY_SWEEP_OPERATION: &str = "fea.plan_study_sweep";
const ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION: &str = "fea.plan_study_sweep/v1";
const ANALYSIS_RUN_STUDY_OPERATION: &str = "fea.run_study";
const ANALYSIS_RUN_STUDY_OP_VERSION: &str = "fea.run_study/v1";
const ANALYSIS_VALIDATE_STUDY_SWEEP_OPERATION: &str = "fea.validate_study_sweep";
const ANALYSIS_VALIDATE_STUDY_SWEEP_OP_VERSION: &str = "fea.validate_study_sweep/v1";
const ANALYSIS_RUN_STUDY_SWEEP_OPERATION: &str = "fea.run_study_sweep";
const ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION: &str = "fea.run_study_sweep/v1";
const ANALYSIS_VALIDATE_OPERATION: &str = "fea.validate";
const ANALYSIS_VALIDATE_OP_VERSION: &str = "fea.validate/v1";
const ANALYSIS_RUN_OPERATION: &str = "fea.run_linear_static";
const ANALYSIS_RUN_OP_VERSION: &str = "fea.run_linear_static/v1";
const ANALYSIS_RUN_MODAL_OPERATION: &str = "fea.run_modal";
const ANALYSIS_RUN_MODAL_OP_VERSION: &str = "fea.run_modal/v1";
const ANALYSIS_RUN_ACOUSTIC_OPERATION: &str = "fea.run_acoustic";
const ANALYSIS_RUN_ACOUSTIC_OP_VERSION: &str = "fea.run_acoustic/v1";
const ANALYSIS_RUN_TRANSIENT_OPERATION: &str = "fea.run_transient";
const ANALYSIS_RUN_TRANSIENT_OP_VERSION: &str = "fea.run_transient/v1";
const ANALYSIS_RUN_THERMAL_OPERATION: &str = "fea.run_thermal";
const ANALYSIS_RUN_THERMAL_OP_VERSION: &str = "fea.run_thermal/v1";
const ANALYSIS_RUN_NONLINEAR_OPERATION: &str = "fea.run_nonlinear";
const ANALYSIS_RUN_NONLINEAR_OP_VERSION: &str = "fea.run_nonlinear/v1";
const ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION: &str = "fea.run_electromagnetic";
const ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION: &str = "fea.run_electromagnetic/v1";
const ANALYSIS_RUN_CFD_OPERATION: &str = "fea.run_cfd";
const ANALYSIS_RUN_CFD_OP_VERSION: &str = "fea.run_cfd/v1";
const ANALYSIS_RUN_CHT_OPERATION: &str = "fea.run_cht";
const ANALYSIS_RUN_CHT_OP_VERSION: &str = "fea.run_cht/v1";
const ANALYSIS_RUN_FSI_OPERATION: &str = "fea.run_fsi";
const ANALYSIS_RUN_FSI_OP_VERSION: &str = "fea.run_fsi/v1";
const ANALYSIS_RESULTS_OPERATION: &str = "fea.results";
const ANALYSIS_RESULTS_OP_VERSION: &str = "fea.results/v1";
const ANALYSIS_RESULTS_COMPARE_OPERATION: &str = "fea.results_compare";
const ANALYSIS_RESULTS_COMPARE_OP_VERSION: &str = "fea.results_compare/v1";
const ANALYSIS_TRENDS_OPERATION: &str = "fea.trends";
const ANALYSIS_TRENDS_OP_VERSION: &str = "fea.trends/v1";
const TRANSIENT_RESIDUAL_WARN_THRESHOLD: f64 = 1.0e-4;

fn map_fea_run_error(
    operation: &str,
    op_version: &str,
    default_error_code: &'static str,
    cancel_error_code: &'static str,
    model: &AnalysisModel,
    context: &OperationContext,
    err: FeaRunError,
) -> OperationErrorEnvelope {
    match err {
        FeaRunError::Cancelled => operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: cancel_error_code,
                error_type: OperationErrorType::Cancelled,
                retryable: false,
                severity: OperationErrorSeverity::Warning,
            },
            "FEA run cancelled by user",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ),
        FeaRunError::InvalidModel(message) => operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: default_error_code,
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            message,
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ),
    }
}

fn reject_moment_loads_for_run_family(
    model: &AnalysisModel,
    operation: &'static str,
    op_version: &'static str,
    error_code: &'static str,
    family: &'static str,
    context: &OperationContext,
) -> Result<(), OperationErrorEnvelope> {
    if let Some(load) = model
        .loads
        .iter()
        .find(|load| matches!(load.kind, LoadKind::Moment { .. }))
    {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code,
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!("moment loads are structural loads and cannot be used as {family} loads"),
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("load_id".to_string(), load.load_id.clone()),
                ("region_id".to_string(), load.region_id.clone()),
            ]),
        ));
    }
    Ok(())
}

fn persist_fea_run_result_with_progress(
    operation: &str,
    op_version: &str,
    artifact_error_code: &'static str,
    context: &OperationContext,
    result: &AnalysisRunResult,
) -> Result<(), OperationErrorEnvelope> {
    runmat_analysis_fea::emit_fea_progress_phase(
        operation,
        FeaProgressPhase::ArtifactPersistence,
        FeaProgressStatus::Started,
        "persisting FEA run artifact",
        None,
        None,
    );
    match storage::persist_run_result(result) {
        Ok(_record) => {
            runmat_analysis_fea::emit_fea_progress_phase(
                operation,
                FeaProgressPhase::ArtifactPersistence,
                FeaProgressStatus::Completed,
                "FEA run artifact persisted",
                None,
                None,
            );
            Ok(())
        }
        Err(err) => {
            let message = format!("failed to persist FEA run artifact: {err}");
            runmat_analysis_fea::emit_fea_progress_phase(
                operation,
                FeaProgressPhase::ArtifactPersistence,
                FeaProgressStatus::Failed,
                &message,
                None,
                None,
            );
            Err(operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: artifact_error_code,
                    error_type: OperationErrorType::Internal,
                    retryable: true,
                    severity: OperationErrorSeverity::Error,
                },
                message,
                BTreeMap::from([("run_id".to_string(), result.run_id.clone())]),
            ))
        }
    }
}

pub fn analysis_create_model_op(
    geometry: &GeometryAsset,
    intent: AnalysisCreateModelIntentSpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisModel>, OperationErrorEnvelope> {
    if intent.model_id.trim().is_empty() {
        return Err(operation_error(
            ANALYSIS_CREATE_MODEL_OPERATION,
            ANALYSIS_CREATE_MODEL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.CREATE_MODEL.INVALID_INTENT",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model intent requires a non-empty model_id",
            BTreeMap::from([("geometry_id".to_string(), geometry.geometry_id.clone())]),
        ));
    }

    if geometry.meshes.is_empty() {
        return Err(operation_error(
            ANALYSIS_CREATE_MODEL_OPERATION,
            ANALYSIS_CREATE_MODEL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.CREATE_MODEL.GEOMETRY_EMPTY",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "geometry must contain at least one mesh to create an FEA model",
            BTreeMap::from([("geometry_id".to_string(), geometry.geometry_id.clone())]),
        ));
    }

    if geometry.units == UnitSystem::Unspecified {
        return Err(operation_error(
            ANALYSIS_CREATE_MODEL_OPERATION,
            ANALYSIS_CREATE_MODEL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.CREATE_MODEL.UNIT_UNSPECIFIED",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "geometry units must be specified before creating an FEA model",
            BTreeMap::from([("geometry_id".to_string(), geometry.geometry_id.clone())]),
        ));
    }

    let prep_mapped_region_ids = if let Some(prep) = intent.prep_context.as_ref() {
        if prep.source_geometry_id != geometry.geometry_id
            || prep.source_geometry_revision != geometry.revision
        {
            return Err(operation_error(
                ANALYSIS_CREATE_MODEL_OPERATION,
                ANALYSIS_CREATE_MODEL_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.CREATE_MODEL.PREP_MISMATCH",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                "FEA model prep context does not match geometry id/revision",
                BTreeMap::from([
                    ("geometry_id".to_string(), geometry.geometry_id.clone()),
                    (
                        "geometry_revision".to_string(),
                        geometry.revision.to_string(),
                    ),
                    (
                        "prep_geometry_id".to_string(),
                        prep.source_geometry_id.clone(),
                    ),
                    (
                        "prep_geometry_revision".to_string(),
                        prep.source_geometry_revision.to_string(),
                    ),
                ]),
            ));
        }

        let mesh_id_set = geometry
            .meshes
            .iter()
            .map(|mesh| mesh.mesh_id.as_str())
            .collect::<HashSet<_>>();
        let region_id_set = geometry
            .regions
            .iter()
            .map(|region| region.region_id.as_str())
            .collect::<HashSet<_>>();
        for mapping in &prep.region_mappings {
            if !region_id_set.is_empty() && !region_id_set.contains(mapping.region_id.as_str()) {
                return Err(operation_error(
                    ANALYSIS_CREATE_MODEL_OPERATION,
                    ANALYSIS_CREATE_MODEL_OP_VERSION,
                    &context,
                    OperationErrorSpec {
                        error_code: "RM.FEA.CREATE_MODEL.PREP_REGION_NOT_FOUND",
                        error_type: OperationErrorType::Validation,
                        retryable: false,
                        severity: OperationErrorSeverity::Error,
                    },
                    format!(
                        "prep context region '{}' is not present in geometry regions",
                        mapping.region_id
                    ),
                    BTreeMap::from([("region_id".to_string(), mapping.region_id.clone())]),
                ));
            }
            if mapping.source_mesh_ids.is_empty() || mapping.prepared_mesh_ids.is_empty() {
                return Err(operation_error(
                    ANALYSIS_CREATE_MODEL_OPERATION,
                    ANALYSIS_CREATE_MODEL_OP_VERSION,
                    &context,
                    OperationErrorSpec {
                        error_code: "RM.FEA.CREATE_MODEL.PREP_INVALID_MAPPING",
                        error_type: OperationErrorType::Input,
                        retryable: false,
                        severity: OperationErrorSeverity::Error,
                    },
                    "prep context mapping requires non-empty source/prepared mesh ids",
                    BTreeMap::from([("region_id".to_string(), mapping.region_id.clone())]),
                ));
            }
            for source_mesh_id in &mapping.source_mesh_ids {
                if !mesh_id_set.contains(source_mesh_id.as_str()) {
                    return Err(operation_error(
                        ANALYSIS_CREATE_MODEL_OPERATION,
                        ANALYSIS_CREATE_MODEL_OP_VERSION,
                        &context,
                        OperationErrorSpec {
                            error_code: "RM.FEA.CREATE_MODEL.PREP_MESH_NOT_FOUND",
                            error_type: OperationErrorType::Validation,
                            retryable: false,
                            severity: OperationErrorSeverity::Error,
                        },
                        format!(
                            "prep context source mesh '{}' is not present in geometry",
                            source_mesh_id
                        ),
                        BTreeMap::from([("source_mesh_id".to_string(), source_mesh_id.clone())]),
                    ));
                }
            }
        }

        Some(
            prep.region_mappings
                .iter()
                .map(|mapping| mapping.region_id.clone())
                .collect::<HashSet<_>>(),
        )
    } else {
        None
    };

    let fixed_region_id = select_fixed_region_id(geometry, prep_mapped_region_ids.as_ref())
        .or_else(|| {
            geometry
                .regions
                .first()
                .map(|region| region.region_id.clone())
        })
        .unwrap_or_else(|| "region_default".to_string());
    let load_region_id = select_load_region_id(geometry, prep_mapped_region_ids.as_ref())
        .or_else(|| {
            geometry
                .regions
                .last()
                .map(|region| region.region_id.clone())
        })
        .unwrap_or_else(|| fixed_region_id.clone());

    let mut inferred_materials = infer_material_models(geometry);
    if matches!(intent.profile, AnalysisCreateModelProfile::AcousticHarmonic) {
        for material in &mut inferred_materials {
            material.acoustic = Some(runmat_analysis_core::MaterialAcousticModel::default());
        }
    }
    if matches!(
        intent.profile,
        AnalysisCreateModelProfile::ElectromagneticStatic
    ) {
        for material in &mut inferred_materials {
            material.electrical = Some(runmat_analysis_core::MaterialElectricalModel::default());
        }
    }
    let inferred_assignments = infer_material_assignments(
        geometry,
        &inferred_materials,
        prep_mapped_region_ids.as_ref(),
    );

    let (default_bc, default_load, default_steps) = match intent.profile {
        AnalysisCreateModelProfile::LinearStaticStructural => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_force".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Force {
                    fx: 0.0,
                    fy: -1000.0,
                    fz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_static".to_string(),
                kind: AnalysisStepKind::Static,
            }],
        ),
        AnalysisCreateModelProfile::ThermoMechanicalCoupled => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_thermal_mech_force".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Force {
                    fx: 0.0,
                    fy: -650.0,
                    fz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_thermo_mech".to_string(),
                kind: AnalysisStepKind::Transient,
            }],
        ),
        AnalysisCreateModelProfile::ThermalStandalone => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_thermal_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::BodyForce {
                    gx: 0.0,
                    gy: 0.0,
                    gz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_thermal".to_string(),
                kind: AnalysisStepKind::Thermal,
            }],
        ),
        AnalysisCreateModelProfile::ModalStructural => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_modal_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::BodyForce {
                    gx: 0.0,
                    gy: 0.0,
                    gz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_modal".to_string(),
                kind: AnalysisStepKind::Modal,
            }],
        ),
        AnalysisCreateModelProfile::AcousticHarmonic => (
            BoundaryCondition {
                bc_id: "bc_default_acoustic_rigid_wall".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::AcousticRigidWall,
            },
            LoadCase {
                load_id: "load_default_acoustic_harmonic_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Pressure { magnitude_pa: 1.0 },
            },
            vec![AnalysisStep {
                step_id: "step_default_acoustic_harmonic".to_string(),
                kind: AnalysisStepKind::Modal,
            }],
        ),
        AnalysisCreateModelProfile::TransientStructural => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_transient_force".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Force {
                    fx: 0.0,
                    fy: -500.0,
                    fz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_transient".to_string(),
                kind: AnalysisStepKind::Transient,
            }],
        ),
        AnalysisCreateModelProfile::NonlinearStructural => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_nonlinear_force".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Force {
                    fx: 0.0,
                    fy: -750.0,
                    fz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_nonlinear".to_string(),
                kind: AnalysisStepKind::Nonlinear,
            }],
        ),
        AnalysisCreateModelProfile::ElectromagneticStatic => (
            BoundaryCondition {
                bc_id: "bc_default_em_ground".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::VectorPotentialGround,
            },
            LoadCase {
                load_id: "load_default_em_coil_current".to_string(),
                region_id: load_region_id,
                kind: LoadKind::CoilCurrent {
                    current_a: 100.0,
                    phase_rad: 0.0,
                    amplitude_scale: 1.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_electromagnetic".to_string(),
                kind: AnalysisStepKind::Electromagnetic,
            }],
        ),
        AnalysisCreateModelProfile::CfdSteadyState => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_cfd_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::BodyForce {
                    gx: 0.0,
                    gy: 0.0,
                    gz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_cfd".to_string(),
                kind: AnalysisStepKind::Cfd,
            }],
        ),
        AnalysisCreateModelProfile::CfdTransient => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_cfd_transient_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::BodyForce {
                    gx: 0.0,
                    gy: 0.0,
                    gz: 0.0,
                },
            },
            vec![AnalysisStep {
                step_id: "step_default_cfd_transient".to_string(),
                kind: AnalysisStepKind::Cfd,
            }],
        ),
        AnalysisCreateModelProfile::ChtCoupled => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_cht_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::BodyForce {
                    gx: 0.0,
                    gy: 0.0,
                    gz: 0.0,
                },
            },
            vec![
                AnalysisStep {
                    step_id: "step_default_cht_flow".to_string(),
                    kind: AnalysisStepKind::Cfd,
                },
                AnalysisStep {
                    step_id: "step_default_cht_thermal".to_string(),
                    kind: AnalysisStepKind::Thermal,
                },
            ],
        ),
        AnalysisCreateModelProfile::FsiCoupled => (
            BoundaryCondition {
                bc_id: "bc_default_fixed".to_string(),
                region_id: fixed_region_id,
                kind: BoundaryConditionKind::Fixed,
            },
            LoadCase {
                load_id: "load_default_fsi_seed".to_string(),
                region_id: load_region_id,
                kind: LoadKind::Force {
                    fx: 0.0,
                    fy: -450.0,
                    fz: 0.0,
                },
            },
            vec![
                AnalysisStep {
                    step_id: "step_default_fsi_structure".to_string(),
                    kind: AnalysisStepKind::Transient,
                },
                AnalysisStep {
                    step_id: "step_default_fsi_flow".to_string(),
                    kind: AnalysisStepKind::Cfd,
                },
            ],
        ),
    };

    let cfd = match intent.profile {
        AnalysisCreateModelProfile::CfdSteadyState => Some(runmat_analysis_core::CfdDomain {
            enabled: true,
            solve_family: runmat_analysis_core::CfdSolveFamily::SteadyState,
            reference_density_kg_per_m3: 1.225,
            dynamic_viscosity_pa_s: 1.81e-5,
            inlet_velocity_m_per_s: 5.0,
            turbulence_intensity: 0.05,
            time_profile: Vec::new(),
        }),
        AnalysisCreateModelProfile::CfdTransient => Some(runmat_analysis_core::CfdDomain {
            enabled: true,
            solve_family: runmat_analysis_core::CfdSolveFamily::Transient,
            reference_density_kg_per_m3: 1.225,
            dynamic_viscosity_pa_s: 1.81e-5,
            inlet_velocity_m_per_s: 5.0,
            turbulence_intensity: 0.08,
            time_profile: vec![
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 0.0,
                    inlet_scale: 0.5,
                },
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 1.0,
                    inlet_scale: 1.0,
                },
            ],
        }),
        AnalysisCreateModelProfile::ChtCoupled => Some(runmat_analysis_core::CfdDomain {
            enabled: true,
            solve_family: runmat_analysis_core::CfdSolveFamily::Transient,
            reference_density_kg_per_m3: 1.225,
            dynamic_viscosity_pa_s: 1.81e-5,
            inlet_velocity_m_per_s: 4.5,
            turbulence_intensity: 0.07,
            time_profile: vec![
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 0.0,
                    inlet_scale: 0.7,
                },
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 1.0,
                    inlet_scale: 1.0,
                },
            ],
        }),
        AnalysisCreateModelProfile::FsiCoupled => Some(runmat_analysis_core::CfdDomain {
            enabled: true,
            solve_family: runmat_analysis_core::CfdSolveFamily::Transient,
            reference_density_kg_per_m3: 1.225,
            dynamic_viscosity_pa_s: 1.81e-5,
            inlet_velocity_m_per_s: 4.0,
            turbulence_intensity: 0.06,
            time_profile: vec![
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 0.0,
                    inlet_scale: 0.6,
                },
                runmat_analysis_core::CfdTimeProfilePoint {
                    normalized_time: 1.0,
                    inlet_scale: 1.0,
                },
            ],
        }),
        _ => None,
    };
    let electromagnetic = match intent.profile {
        AnalysisCreateModelProfile::ElectromagneticStatic => {
            Some(runmat_analysis_core::ElectromagneticDomain {
                enabled: true,
                reference_frequency_hz: 60.0,
                applied_current_a: 100.0,
            })
        }
        _ => None,
    };
    let thermo_mechanical = match intent.profile {
        AnalysisCreateModelProfile::ChtCoupled => {
            Some(runmat_analysis_core::ThermoMechanicalDomain {
                enabled: true,
                reference_temperature_k: 293.15,
                applied_temperature_delta_k: 35.0,
                field_artifact_id: None,
                field_source: None,
                region_temperature_deltas: Vec::new(),
                time_profile: vec![
                    runmat_analysis_core::ThermoTimeProfilePoint {
                        normalized_time: 0.0,
                        scale: 0.6,
                    },
                    runmat_analysis_core::ThermoTimeProfilePoint {
                        normalized_time: 1.0,
                        scale: 1.0,
                    },
                ],
            })
        }
        _ => None,
    };

    let mut boundary_conditions = vec![default_bc];
    if matches!(
        intent.profile,
        AnalysisCreateModelProfile::CfdSteadyState
            | AnalysisCreateModelProfile::CfdTransient
            | AnalysisCreateModelProfile::ChtCoupled
            | AnalysisCreateModelProfile::FsiCoupled
    ) {
        let default_cfd_inlet_velocity = cfd
            .as_ref()
            .map(|domain| domain.inlet_velocity_m_per_s)
            .unwrap_or(0.0);
        boundary_conditions.extend([
            BoundaryCondition {
                bc_id: "bc_default_cfd_inlet".to_string(),
                region_id: "inlet".to_string(),
                kind: BoundaryConditionKind::CfdInletVelocity {
                    velocity_m_per_s: default_cfd_inlet_velocity,
                },
            },
            BoundaryCondition {
                bc_id: "bc_default_cfd_outlet".to_string(),
                region_id: "outlet".to_string(),
                kind: BoundaryConditionKind::CfdOutletPressure { pressure_pa: 0.0 },
            },
            BoundaryCondition {
                bc_id: "bc_default_cfd_wall_upper".to_string(),
                region_id: "wall_upper".to_string(),
                kind: BoundaryConditionKind::CfdNoSlipWall,
            },
            BoundaryCondition {
                bc_id: "bc_default_cfd_wall_lower".to_string(),
                region_id: "wall_lower".to_string(),
                kind: BoundaryConditionKind::CfdNoSlipWall,
            },
        ]);
    }

    let model = AnalysisModel {
        model_id: AnalysisModelId(intent.model_id),
        geometry_id: geometry.geometry_id.clone(),
        geometry_revision: geometry.revision,
        units: geometry.units,
        frame: ReferenceFrame::Global,
        materials: inferred_materials,
        material_assignments: inferred_assignments,
        structural: None,
        thermo_mechanical,
        electro_thermal: None,
        electromagnetic,
        cfd,
        interfaces: Vec::new(),
        boundary_conditions,
        loads: vec![default_load],
        steps: default_steps,
    };

    validate_model_against_geometry(&model, geometry.units, &ReferenceFrame::Global).map_err(
        |error| {
            operation_error(
                ANALYSIS_CREATE_MODEL_OPERATION,
                ANALYSIS_CREATE_MODEL_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.CREATE_MODEL.INVALID",
                    error_type: OperationErrorType::Validation,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                format!("created FEA model failed validation: {error:?}"),
                BTreeMap::from([
                    ("analysis_model_id".to_string(), model.model_id.0.clone()),
                    ("geometry_id".to_string(), geometry.geometry_id.clone()),
                ]),
            )
        },
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_CREATE_MODEL_OPERATION,
        ANALYSIS_CREATE_MODEL_OP_VERSION,
        &context,
        model,
    ))
}

pub fn analysis_validate_study_op(
    spec: &AnalysisStudySpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudyValidateResult>, OperationErrorEnvelope> {
    let issue_codes = validate_study_issue_codes(spec);
    let issues: Vec<AnalysisStudyIssue> = issue_codes
        .iter()
        .map(|code| AnalysisStudyIssue {
            code: code.clone(),
            message: study_issue_message(code).to_string(),
        })
        .collect();
    let study_fingerprint = study_fingerprint(spec);
    let evidence_artifact_path = persist_study_evidence(
        &study_fingerprint,
        "validate",
        serde_json::json!({
            "schema_version": "fea_study_validate_artifact/v1",
            "study_id": spec.study_id.clone(),
            "study_fingerprint": study_fingerprint.clone(),
            "valid": issue_codes.is_empty(),
            "issue_codes": issue_codes.clone(),
            "issues": issues.clone(),
            "electromagnetic_run_options": spec.electromagnetic_run_options.clone(),
        }),
    )
    .map_err(|err| {
        operation_error(
            ANALYSIS_VALIDATE_STUDY_OPERATION,
            ANALYSIS_VALIDATE_STUDY_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.VALIDATE_STUDY.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to persist study validation evidence artifact: {err}"),
            BTreeMap::from([("study_id".to_string(), spec.study_id.clone())]),
        )
    })?;
    Ok(OperationEnvelope::new(
        ANALYSIS_VALIDATE_STUDY_OPERATION,
        ANALYSIS_VALIDATE_STUDY_OP_VERSION,
        &context,
        AnalysisStudyValidateResult {
            valid: issue_codes.is_empty(),
            issue_codes,
            issues,
            evidence_artifact_path,
        },
    ))
}

pub fn analysis_plan_study_op(
    spec: &AnalysisStudySpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudyPlanData>, OperationErrorEnvelope> {
    let issue_codes = validate_study_issue_codes(spec);
    if !issue_codes.is_empty() {
        return Err(operation_error(
            ANALYSIS_PLAN_STUDY_OPERATION,
            ANALYSIS_PLAN_STUDY_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.PLAN_STUDY.INVALID_SPEC",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "study spec is invalid; run fea.validate for issue details",
            BTreeMap::from([("issue_codes".to_string(), issue_codes.join(","))]),
        ));
    }

    let study_fingerprint = study_fingerprint(spec);
    let run_operation = run_operation_for_kind(spec.run_kind).to_string();
    let run_op_version = run_operation_version_for_kind(spec.run_kind).to_string();
    let operation_sequence = study_operation_sequence(spec, &run_op_version);
    let evidence_artifact_path = persist_study_evidence(
        &study_fingerprint,
        "plan",
        serde_json::json!({
            "schema_version": "fea_study_plan_artifact/v1",
            "study_id": spec.study_id.clone(),
            "model_id": spec.create_model_intent.model_id.clone(),
            "run_kind": spec.run_kind,
            "backend": spec.backend,
            "run_options": study_run_options_json(spec),
            "study_fingerprint": study_fingerprint.clone(),
            "operation_sequence": operation_sequence.clone(),
            "run_operation": run_operation.clone(),
            "run_op_version": run_op_version.clone(),
        }),
    )
    .map_err(|err| {
        operation_error(
            ANALYSIS_PLAN_STUDY_OPERATION,
            ANALYSIS_PLAN_STUDY_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.PLAN_STUDY.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to persist study plan evidence artifact: {err}"),
            BTreeMap::from([("study_id".to_string(), spec.study_id.clone())]),
        )
    })?;
    Ok(OperationEnvelope::new(
        ANALYSIS_PLAN_STUDY_OPERATION,
        ANALYSIS_PLAN_STUDY_OP_VERSION,
        &context,
        AnalysisStudyPlanData {
            study_id: spec.study_id.clone(),
            model_id: spec.create_model_intent.model_id.clone(),
            run_kind: spec.run_kind,
            backend: spec.backend,
            electromagnetic_run_options: spec.electromagnetic_run_options.clone(),
            run_options: study_run_options_json(spec),
            operation_sequence,
            run_operation,
            run_op_version,
            study_fingerprint,
            evidence_artifact_path,
        },
    ))
}

pub fn analysis_run_study_op(
    spec: &AnalysisStudySpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudyRunData>, OperationErrorEnvelope> {
    let issue_codes = validate_study_issue_codes(spec);
    if !issue_codes.is_empty() {
        return Err(operation_error(
            ANALYSIS_RUN_STUDY_OPERATION,
            ANALYSIS_RUN_STUDY_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_STUDY.INVALID_SPEC",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "study spec is invalid; run fea.validate for issue details",
            BTreeMap::from([("issue_codes".to_string(), issue_codes.join(","))]),
        ));
    }

    let study_fingerprint = study_fingerprint(spec);
    let run_operation = run_operation_for_kind(spec.run_kind).to_string();
    let run_op_version = run_operation_version_for_kind(spec.run_kind).to_string();
    let operation_sequence = study_operation_sequence(spec, &run_op_version);

    let study_prep = crate::geometry::geometry_prep_for_analysis_op(
        &spec.geometry,
        crate::geometry::GeometryPrepForAnalysisSpec::default(),
        context.clone(),
    )?
    .data;
    let study_prep_artifact_id = study_prep.prep_artifact_id.clone();
    let mut create_model_intent = spec.create_model_intent.clone();
    create_model_intent.prep_context = Some(AnalysisCreateModelPrepContext {
        source_geometry_id: spec.geometry.geometry_id.clone(),
        source_geometry_revision: spec.geometry.revision,
        region_mappings: study_prep.prep.region_mappings.clone(),
    });

    let model = match &spec.model {
        Some(model) => model.clone(),
        None => {
            analysis_create_model_op(&spec.geometry, create_model_intent.clone(), context.clone())?
                .data
        }
    };
    analysis_validate(
        &model,
        spec.geometry.units,
        &ReferenceFrame::Global,
        context.clone(),
    )?;
    let (run_envelope, resolved_run_options, resolved_electromagnetic_run_options) = match spec
        .run_kind
    {
        AnalysisRunKind::LinearStatic => {
            let mut options = spec.linear_static_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_run_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_linear_static_with_options(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Modal => {
            let mut options = spec.modal_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_modal_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_modal_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Acoustic => {
            let mut options = spec.acoustic_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_acoustic_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_acoustic_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Thermal => {
            let mut options = spec.thermal_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_thermal_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_thermal_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Transient => {
            let mut options = spec.transient_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_transient_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_transient_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Cfd => {
            let mut options = spec.cfd_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_cfd_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_cfd_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Cht => {
            let mut options = spec.cht_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_cht_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_cht_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Fsi => {
            let mut options = spec.fsi_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_fsi_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_fsi_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Nonlinear => {
            let mut options = spec.nonlinear_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_nonlinear_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_nonlinear_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), None))
        }
        AnalysisRunKind::Electromagnetic => {
            let mut options = spec.electromagnetic_run_options.clone().unwrap_or_default();
            attach_prep_artifact_to_electromagnetic_options(&mut options, &study_prep_artifact_id);
            let run = analysis_run_electromagnetic_with_options_op(
                &model,
                spec.backend,
                options.clone(),
                context.clone(),
            )?;
            Ok((run, run_options_to_json(&options), Some(options)))
        }
    }?;

    let evidence_artifact_path = persist_study_evidence(
        &study_fingerprint,
        "run",
        serde_json::json!({
            "schema_version": "fea_study_run_artifact/v1",
            "study_id": spec.study_id.clone(),
            "model_id": model.model_id.0.clone(),
            "run_kind": spec.run_kind,
            "backend": spec.backend,
            "prep_artifact_id": study_prep_artifact_id.clone(),
            "run_options": resolved_run_options.clone(),
            "resolved_electromagnetic_run_options": resolved_electromagnetic_run_options.clone(),
            "study_fingerprint": study_fingerprint.clone(),
            "operation_sequence": operation_sequence.clone(),
            "run_operation": run_operation.clone(),
            "run_op_version": run_op_version.clone(),
            "run_id": run_envelope.data.run_id.clone(),
            "run_status": run_envelope.data.run_status,
            "publishable": run_envelope.data.publishable,
            "solver_convergence": run_envelope.data.solver_convergence,
            "result_quality": run_envelope.data.result_quality,
            "quality_reasons": run_envelope.data.quality_reasons.clone(),
            "provenance": run_envelope.data.provenance.clone(),
        }),
    )
    .map_err(|err| {
        operation_error(
            ANALYSIS_RUN_STUDY_OPERATION,
            ANALYSIS_RUN_STUDY_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_STUDY.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to persist study run evidence artifact: {err}"),
            BTreeMap::from([
                ("study_id".to_string(), spec.study_id.clone()),
                ("run_id".to_string(), run_envelope.data.run_id.clone()),
            ]),
        )
    })?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_STUDY_OPERATION,
        ANALYSIS_RUN_STUDY_OP_VERSION,
        &context,
        AnalysisStudyRunData {
            study_id: spec.study_id.clone(),
            model_id: model.model_id.0.clone(),
            run_kind: spec.run_kind,
            backend: spec.backend,
            electromagnetic_run_options: resolved_electromagnetic_run_options,
            prep_artifact_id: Some(study_prep_artifact_id),
            run_options: resolved_run_options,
            study_fingerprint,
            operation_sequence,
            run_operation,
            run_op_version,
            run_id: run_envelope.data.run_id,
            run_status: run_envelope.data.run_status,
            publishable: run_envelope.data.publishable,
            solver_convergence: run_envelope.data.solver_convergence,
            result_quality: run_envelope.data.result_quality,
            quality_reasons: run_envelope.data.quality_reasons,
            provenance: run_envelope.data.provenance,
            evidence_artifact_path,
        },
    ))
}

pub fn analysis_plan_study_sweep_op(
    spec: &AnalysisStudySweepSpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudySweepPlanData>, OperationErrorEnvelope> {
    let mut issue_codes = Vec::new();
    if spec.sweep_id.trim().is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.ID_EMPTY".to_string());
    }
    if spec.studies.is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.STUDIES_EMPTY".to_string());
    }
    if !issue_codes.is_empty() {
        return Err(operation_error(
            ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
            ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.PLAN_STUDY_SWEEP.INVALID_SPEC",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "study sweep spec is invalid",
            BTreeMap::from([("issue_codes".to_string(), issue_codes.join(","))]),
        ));
    }

    let mut plan_entries = Vec::with_capacity(spec.studies.len());
    let mut failure_entries = Vec::new();
    for (index, study) in spec.studies.iter().enumerate() {
        let planned = match analysis_plan_study_op(study, context.clone()) {
            Ok(plan) => plan,
            Err(err) => {
                if spec.fail_fast {
                    return Err(operation_error(
                        ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
                        ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
                        &context,
                        OperationErrorSpec {
                            error_code: "RM.FEA.PLAN_STUDY_SWEEP.STUDY_FAILED",
                            error_type: OperationErrorType::Validation,
                            retryable: false,
                            severity: OperationErrorSeverity::Error,
                        },
                        format!(
                            "study sweep planning failed at index {} for study_id {}: {}",
                            index, study.study_id, err.error_code
                        ),
                        BTreeMap::from([
                            ("sweep_id".to_string(), spec.sweep_id.clone()),
                            ("study_id".to_string(), study.study_id.clone()),
                            ("study_index".to_string(), index.to_string()),
                            ("cause_error_code".to_string(), err.error_code),
                        ]),
                    ));
                }
                failure_entries.push(AnalysisStudySweepFailureEntry {
                    study_id: study.study_id.clone(),
                    study_index: index,
                    error_code: err.error_code,
                    message: err.message,
                });
                continue;
            }
        };
        plan_entries.push(AnalysisStudySweepPlanEntry {
            study_id: planned.data.study_id,
            model_id: planned.data.model_id,
            run_kind: planned.data.run_kind,
            backend: planned.data.backend,
            electromagnetic_run_options: planned.data.electromagnetic_run_options,
            run_options: planned.data.run_options,
            operation_sequence: planned.data.operation_sequence,
            run_operation: planned.data.run_operation,
            run_op_version: planned.data.run_op_version,
            study_fingerprint: planned.data.study_fingerprint,
        });
    }

    let sanitized_sweep_id = sanitize_study_sweep_id(&spec.sweep_id);
    let evidence_path = study_evidence_root()
        .join("sweeps")
        .join(sanitized_sweep_id)
        .join("plan.json");
    if let Some(parent) = evidence_path.parent() {
        fs_create_dir_all(parent).map_err(|err| {
            operation_error(
                ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
                ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.PLAN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                    error_type: OperationErrorType::Internal,
                    retryable: true,
                    severity: OperationErrorSeverity::Error,
                },
                format!("failed to create study sweep planning evidence directory: {err}"),
                BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
            )
        })?;
    }
    let payload = serde_json::json!({
        "schema_version": "fea_study_sweep_plan_artifact/v1",
        "sweep_id": spec.sweep_id.clone(),
        "study_count": spec.studies.len(),
        "planned_count": plan_entries.len(),
        "failed_count": failure_entries.len(),
        "failure_entries": failure_entries.clone(),
        "plan_entries": plan_entries.clone(),
    });
    let payload_bytes = serde_json::to_vec_pretty(&payload).map_err(|err| {
        operation_error(
            ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
            ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.PLAN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to encode study sweep planning evidence payload: {err}"),
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;
    atomic_write_bytes(&evidence_path, &payload_bytes).map_err(|err| {
        operation_error(
            ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
            ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.PLAN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            err,
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;

    Ok(OperationEnvelope::new(
        ANALYSIS_PLAN_STUDY_SWEEP_OPERATION,
        ANALYSIS_PLAN_STUDY_SWEEP_OP_VERSION,
        &context,
        AnalysisStudySweepPlanData {
            sweep_id: spec.sweep_id.clone(),
            study_count: spec.studies.len(),
            planned_count: plan_entries.len(),
            failed_count: failure_entries.len(),
            failure_entries,
            plan_entries,
            evidence_artifact_path: evidence_path.display().to_string(),
        },
    ))
}

pub fn analysis_validate_study_sweep_op(
    spec: &AnalysisStudySweepSpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudySweepValidateData>, OperationErrorEnvelope> {
    let mut issue_codes = Vec::new();
    if spec.sweep_id.trim().is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.ID_EMPTY".to_string());
    }
    if spec.studies.is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.STUDIES_EMPTY".to_string());
    }

    let study_entries: Vec<AnalysisStudySweepValidateEntry> = spec
        .studies
        .iter()
        .map(|study| {
            let study_issue_codes = validate_study_issue_codes(study);
            let issues = study_issue_codes
                .iter()
                .map(|code| AnalysisStudyIssue {
                    code: code.clone(),
                    message: study_issue_message(code).to_string(),
                })
                .collect::<Vec<_>>();
            AnalysisStudySweepValidateEntry {
                study_id: study.study_id.clone(),
                valid: study_issue_codes.is_empty(),
                issue_codes: study_issue_codes,
                issues,
            }
        })
        .collect();

    let valid = issue_codes.is_empty() && study_entries.iter().all(|entry| entry.valid);
    let sanitized_sweep_id = sanitize_study_sweep_id(&spec.sweep_id);
    let evidence_path = study_evidence_root()
        .join("sweeps")
        .join(sanitized_sweep_id)
        .join("validate.json");
    if let Some(parent) = evidence_path.parent() {
        fs_create_dir_all(parent).map_err(|err| {
            operation_error(
                ANALYSIS_VALIDATE_STUDY_SWEEP_OPERATION,
                ANALYSIS_VALIDATE_STUDY_SWEEP_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.VALIDATE_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                    error_type: OperationErrorType::Internal,
                    retryable: true,
                    severity: OperationErrorSeverity::Error,
                },
                format!("failed to create study sweep validation evidence directory: {err}"),
                BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
            )
        })?;
    }
    let payload = serde_json::json!({
        "schema_version": "fea_study_sweep_validate_artifact/v1",
        "sweep_id": spec.sweep_id.clone(),
        "valid": valid,
        "issue_codes": issue_codes.clone(),
        "study_entries": study_entries,
    });
    let payload_bytes = serde_json::to_vec_pretty(&payload).map_err(|err| {
        operation_error(
            ANALYSIS_VALIDATE_STUDY_SWEEP_OPERATION,
            ANALYSIS_VALIDATE_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.VALIDATE_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to encode study sweep validation evidence payload: {err}"),
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;
    atomic_write_bytes(&evidence_path, &payload_bytes).map_err(|err| {
        operation_error(
            ANALYSIS_VALIDATE_STUDY_SWEEP_OPERATION,
            ANALYSIS_VALIDATE_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.VALIDATE_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            err,
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;

    Ok(OperationEnvelope::new(
        ANALYSIS_VALIDATE_STUDY_SWEEP_OPERATION,
        ANALYSIS_VALIDATE_STUDY_SWEEP_OP_VERSION,
        &context,
        AnalysisStudySweepValidateData {
            sweep_id: spec.sweep_id.clone(),
            valid,
            issue_codes,
            study_entries,
            evidence_artifact_path: evidence_path.display().to_string(),
        },
    ))
}

pub fn analysis_run_study_sweep_op(
    spec: &AnalysisStudySweepSpec,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisStudySweepData>, OperationErrorEnvelope> {
    let mut issue_codes = Vec::new();
    if spec.sweep_id.trim().is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.ID_EMPTY".to_string());
    }
    if spec.studies.is_empty() {
        issue_codes.push("RM.FEA.STUDY_SWEEP.STUDIES_EMPTY".to_string());
    }
    if !issue_codes.is_empty() {
        return Err(operation_error(
            ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
            ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_STUDY_SWEEP.INVALID_SPEC",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "study sweep spec is invalid",
            BTreeMap::from([("issue_codes".to_string(), issue_codes.join(","))]),
        ));
    }

    let mut run_entries = Vec::with_capacity(spec.studies.len());
    let mut failure_entries = Vec::new();
    for (index, study) in spec.studies.iter().enumerate() {
        let run = match analysis_run_study_op(study, context.clone()) {
            Ok(run) => run,
            Err(err) => {
                if spec.fail_fast {
                    return Err(operation_error(
                        ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
                        ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
                        &context,
                        OperationErrorSpec {
                            error_code: "RM.FEA.RUN_STUDY_SWEEP.STUDY_FAILED",
                            error_type: OperationErrorType::Validation,
                            retryable: false,
                            severity: OperationErrorSeverity::Error,
                        },
                        format!(
                            "study sweep failed at index {} for study_id {}: {}",
                            index, study.study_id, err.error_code
                        ),
                        BTreeMap::from([
                            ("sweep_id".to_string(), spec.sweep_id.clone()),
                            ("study_id".to_string(), study.study_id.clone()),
                            ("study_index".to_string(), index.to_string()),
                            ("cause_error_code".to_string(), err.error_code),
                        ]),
                    ));
                }
                failure_entries.push(AnalysisStudySweepFailureEntry {
                    study_id: study.study_id.clone(),
                    study_index: index,
                    error_code: err.error_code,
                    message: err.message,
                });
                continue;
            }
        };
        run_entries.push(AnalysisStudySweepRunEntry {
            study_id: run.data.study_id,
            run_kind: run.data.run_kind,
            run_id: run.data.run_id,
            run_status: run.data.run_status,
            publishable: run.data.publishable,
            run_operation: run.data.run_operation,
            run_op_version: run.data.run_op_version,
        });
    }

    let sanitized_sweep_id = sanitize_study_sweep_id(&spec.sweep_id);
    let evidence_root = study_evidence_root()
        .join("sweeps")
        .join(sanitized_sweep_id)
        .join("run.json");
    if let Some(parent) = evidence_root.parent() {
        fs_create_dir_all(parent).map_err(|err| {
            operation_error(
                ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
                ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                    error_type: OperationErrorType::Internal,
                    retryable: true,
                    severity: OperationErrorSeverity::Error,
                },
                format!("failed to create study sweep evidence directory: {err}"),
                BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
            )
        })?;
    }
    let payload = serde_json::json!({
        "schema_version": "fea_study_sweep_run_artifact/v1",
        "sweep_id": spec.sweep_id.clone(),
        "fail_fast": spec.fail_fast,
        "study_count": spec.studies.len(),
        "success_count": run_entries.len(),
        "failed_count": failure_entries.len(),
        "failure_entries": failure_entries.clone(),
        "run_entries": run_entries.clone(),
    });
    let payload_bytes = serde_json::to_vec_pretty(&payload).map_err(|err| {
        operation_error(
            ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
            ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to encode study sweep evidence payload: {err}"),
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;
    atomic_write_bytes(&evidence_root, &payload_bytes).map_err(|err| {
        operation_error(
            ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
            ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_STUDY_SWEEP.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            err,
            BTreeMap::from([("sweep_id".to_string(), spec.sweep_id.clone())]),
        )
    })?;

    let evidence_artifact_path = evidence_root.display().to_string();
    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_STUDY_SWEEP_OPERATION,
        ANALYSIS_RUN_STUDY_SWEEP_OP_VERSION,
        &context,
        AnalysisStudySweepData {
            sweep_id: spec.sweep_id.clone(),
            study_count: spec.studies.len(),
            success_count: run_entries.len(),
            failed_count: failure_entries.len(),
            failure_entries,
            run_entries,
            evidence_artifact_path,
        },
    ))
}

pub fn analysis_validate(
    model: &AnalysisModel,
    geometry_units: UnitSystem,
    geometry_frame: &ReferenceFrame,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisValidateResult>, OperationErrorEnvelope> {
    validate_model_against_geometry(model, geometry_units, geometry_frame)
        .map_err(|err| map_validate_error(err, model, &context))?;

    Ok(OperationEnvelope::new(
        ANALYSIS_VALIDATE_OPERATION,
        ANALYSIS_VALIDATE_OP_VERSION,
        &context,
        AnalysisValidateResult { valid: true },
    ))
}

pub fn analysis_run_linear_static_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_linear_static_with_options(model, backend, AnalysisRunOptions::default(), context)
}

pub fn analysis_run_modal_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_modal_with_options_op(model, backend, AnalysisModalRunOptions::default(), context)
}

pub fn analysis_run_acoustic_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_acoustic_with_options_op(
        model,
        backend,
        AnalysisAcousticRunOptions::default(),
        context,
    )
}

pub fn analysis_run_modal_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisModalRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_modal_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Modal);
    if !has_modal_step {
        return Err(operation_error(
            ANALYSIS_RUN_MODAL_OPERATION,
            ANALYSIS_RUN_MODAL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_MODAL.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one modal step for fea.run_modal",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }

    if options.mode_count == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_MODAL_OPERATION,
            ANALYSIS_RUN_MODAL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_MODAL.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_modal options require mode_count greater than zero",
            BTreeMap::from([("mode_count".to_string(), options.mode_count.to_string())]),
        ));
    }

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_MODAL_OPERATION,
        ANALYSIS_RUN_MODAL_OP_VERSION,
        &context,
    )?;
    if let Some(thermo_options) = thermo_options.as_ref() {
        if let Err((detail, metadata)) = validate_thermo_coupling_options(model, thermo_options) {
            return Err(operation_error(
                ANALYSIS_RUN_MODAL_OPERATION,
                ANALYSIS_RUN_MODAL_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_MODAL.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let electro_options = model_electro_coupling_options(model);
    if let Some(electro_options) = electro_options.as_ref() {
        if let Err((detail, metadata)) = validate_electro_coupling_options(model, electro_options) {
            return Err(operation_error(
                ANALYSIS_RUN_MODAL_OPERATION,
                ANALYSIS_RUN_MODAL_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: electro_thermal_invalid_options_error_code(
                        ANALYSIS_RUN_MODAL_OPERATION,
                    ),
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_MODAL_OPERATION,
        ANALYSIS_RUN_MODAL_OP_VERSION,
        &context,
    )?;

    let modal_run = run_modal_with_options(
        model,
        backend,
        ModalSolveOptions {
            mode_count: options.mode_count,
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(thermo_options),
            electro_thermal_context: to_fea_electro_thermal_context(electro_options),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_MODAL_OPERATION,
            ANALYSIS_RUN_MODAL_OP_VERSION,
            "RM.FEA.RUN_MODAL.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_MODAL.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let mut run = modal_run.run;
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }
    let solver_convergence = if run.diagnostics.iter().any(|item| {
        item.code == "FEA_MODAL_CONVERGENCE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    }) {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if modal_run.eigenvalues_hz.is_empty() || modal_run.mode_shapes.is_empty()
    {
        QualityGate::Fail
    } else if modal_run
        .residual_norms
        .iter()
        .copied()
        .fold(0.0_f64, f64::max)
        > options.residual_warn_threshold
    {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };
    let modal_orthogonality_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_MODAL_ORTHOGONALITY"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let modal_separation_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_MODAL_SEPARATION"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "modal solver convergence gate is warning".to_string(),
        });
    }
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ModalResidualExceeded,
            detail: format!(
                "modal residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    if modal_orthogonality_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ModalOrthogonalityExceeded,
            detail: "modal M-orthogonality off-diagonal threshold exceeded".to_string(),
        });
    }
    if modal_separation_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ModalSeparationLow,
            detail: "modal frequency separation threshold is low".to_string(),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let frequency_basis = ModalFrequencyBasis::NativeEigenSolve;

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && !quality_reasons.iter().any(|r| {
                    matches!(
                        r.code,
                        QualityReasonCode::ModalOrthogonalityExceeded
                            | QualityReasonCode::ModalSeparationLow
                    )
                })
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: Some(ModalResultsData {
            modal_payload_version: "modal_results/v1".to_string(),
            eigenvalues_hz: modal_run.eigenvalues_hz,
            mode_shapes: modal_run.mode_shapes,
            residual_norms: modal_run.residual_norms,
            mode_units: ModalFrequencyUnits::Hz,
            frequency_basis,
        }),
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    if let Some(nonlinear) = result.nonlinear_results.as_ref() {
        let event = format!(
            "fea.run_nonlinear outcome run_id={} model_id={} backend={:?} run_status={:?} publishable={} failed_increments={} max_iteration_count={} line_search_backtracks={} tangent_rebuild_count={} max_residual_norm={} max_increment_norm={} max_backtracks_per_increment={} quality_reason_count={}",
            result.run_id,
            model.model_id.0,
            backend,
            result.run_status,
            result.publishable,
            nonlinear.failed_increments,
            nonlinear.iteration_counts.iter().copied().max().unwrap_or(0),
            nonlinear.line_search_backtracks,
            nonlinear.tangent_rebuild_count,
            nonlinear
                .residual_norms
                .iter()
                .copied()
                .reduce(f64::max)
                .unwrap_or(0.0),
            nonlinear
                .increment_norms
                .iter()
                .copied()
                .reduce(f64::max)
                .unwrap_or(0.0),
            nonlinear.max_line_search_backtracks_per_increment,
            result.quality_reasons.len()
        );
        if matches!(result.run_status, RunStatus::Degraded | RunStatus::Rejected) {
            tracing::warn!(target: "runmat_analysis", "{event}");
        } else {
            tracing::info!(target: "runmat_analysis", "{event}");
        }
    }

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_MODAL_OPERATION,
        ANALYSIS_RUN_MODAL_OP_VERSION,
        "RM.FEA.RUN_MODAL.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_MODAL_OPERATION,
        ANALYSIS_RUN_MODAL_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_acoustic_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisAcousticRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_modal_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Modal);
    if !has_modal_step {
        return Err(operation_error(
            ANALYSIS_RUN_ACOUSTIC_OPERATION,
            ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ACOUSTIC.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include an acoustic harmonic step marker for fea.run_acoustic",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    if !model
        .materials
        .iter()
        .any(|material| material.acoustic.is_some())
    {
        return Err(operation_error(
            ANALYSIS_RUN_ACOUSTIC_OPERATION,
            ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ACOUSTIC.MISSING_ACOUSTIC_MATERIAL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_acoustic requires at least one acoustic material with density and sound-speed data",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("material_count".to_string(), model.materials.len().to_string()),
            ]),
        ));
    }
    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_ACOUSTIC_OPERATION,
        ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        "RM.FEA.RUN_ACOUSTIC.INVALID_ACOUSTIC_SOURCE",
        "acoustic",
        &context,
    )?;
    if !model.loads.iter().any(|load| match &load.kind {
        LoadKind::Pressure { magnitude_pa } => magnitude_pa.is_finite() && magnitude_pa.abs() > 0.0,
        _ => false,
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_ACOUSTIC_OPERATION,
            ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ACOUSTIC.MISSING_ACOUSTIC_SOURCE",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_acoustic requires a nonzero acoustic pressure source load",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("load_count".to_string(), model.loads.len().to_string()),
            ]),
        ));
    }
    if !model.boundary_conditions.iter().any(|bc| {
        matches!(
            &bc.kind,
            BoundaryConditionKind::AcousticRigidWall
                | BoundaryConditionKind::AcousticRadiation
                | BoundaryConditionKind::AcousticImpedance { .. }
        )
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_ACOUSTIC_OPERATION,
            ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ACOUSTIC.MISSING_ACOUSTIC_BOUNDARY",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_acoustic requires at least one acoustic boundary condition",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                (
                    "boundary_condition_count".to_string(),
                    model.boundary_conditions.len().to_string(),
                ),
            ]),
        ));
    }

    if options.mode_count == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_ACOUSTIC_OPERATION,
            ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ACOUSTIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_acoustic options require mode_count greater than zero",
            BTreeMap::from([("mode_count".to_string(), options.mode_count.to_string())]),
        ));
    }

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_ACOUSTIC_OPERATION,
        ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        &context,
    )?;
    if let Some(thermo_options) = thermo_options.as_ref() {
        if let Err((detail, metadata)) = validate_thermo_coupling_options(model, thermo_options) {
            return Err(operation_error(
                ANALYSIS_RUN_ACOUSTIC_OPERATION,
                ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_ACOUSTIC.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let electro_options = model_electro_coupling_options(model);
    if let Some(electro_options) = electro_options.as_ref() {
        if let Err((detail, metadata)) = validate_electro_coupling_options(model, electro_options) {
            return Err(operation_error(
                ANALYSIS_RUN_ACOUSTIC_OPERATION,
                ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: electro_thermal_invalid_options_error_code(
                        ANALYSIS_RUN_ACOUSTIC_OPERATION,
                    ),
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_ACOUSTIC_OPERATION,
        ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        &context,
    )?;
    let render_topology = render_topology_from_prep_context(prep_context.as_ref());

    let solve_start = Instant::now();
    let mut run = solve_acoustic_harmonic(
        model,
        backend,
        options.mode_count,
        prep_context,
        options.residual_warn_threshold,
    );
    let solve_ms = solve_start.elapsed().as_secs_f64() * 1000.0;
    run.diagnostics
        .push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
            code: "FEA_ACOUSTIC_COST".to_string(),
            severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
            message: format!(
                "solve_ms={} mode_count={} residual_warn_threshold={}",
                solve_ms, options.mode_count, options.residual_warn_threshold,
            ),
        });
    let acoustic_residual_norm = diagnostic_metric(
        &run.diagnostics,
        "FEA_ACOUSTIC_HELMHOLTZ_RESIDUAL",
        "normalized_residual_norm",
    )
    .unwrap_or(f64::INFINITY);
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }
    let solver_convergence = if acoustic_residual_norm <= options.residual_warn_threshold {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if run.fields_are_empty() {
        QualityGate::Fail
    } else if acoustic_residual_norm > options.residual_warn_threshold {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "acoustic solver convergence gate is warning".to_string(),
        });
    }
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ModalResidualExceeded,
            detail: format!(
                "acoustic residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology,
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_ACOUSTIC_OPERATION,
        ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        "RM.FEA.RUN_ACOUSTIC.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_ACOUSTIC_OPERATION,
        ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        &context,
        result,
    ))
}

fn solve_acoustic_harmonic(
    model: &AnalysisModel,
    backend: ComputeBackend,
    mode_count: usize,
    prep_context: Option<AnalysisRunPrepContext>,
    residual_warn_threshold: f64,
) -> FeaRunResult {
    let node_count = acoustic_node_count(model, prep_context.as_ref());
    let material_summary = acoustic_material_summary(model, mode_count);
    let boundary_summary = acoustic_boundary_summary(
        model,
        material_summary.characteristic_impedance_pa_s_per_m(),
    );
    let speed_of_sound_m_per_s = material_summary.speed_of_sound_m_per_s;
    let density_kg_per_m3 = material_summary.density_kg_per_m3;
    let drive_frequency_hz = acoustic_drive_frequency_hz(mode_count, node_count);
    let damping_ratio = material_summary.damping_ratio;
    let source_real = acoustic_source_vector(model, node_count);
    let source_imag = vec![0.0; node_count];
    let domain = acoustic_domain_topology(node_count, prep_context.as_ref());
    let system = acoustic_helmholtz_operator(
        domain,
        node_count,
        drive_frequency_hz,
        speed_of_sound_m_per_s,
        damping_ratio,
        &boundary_summary,
    );
    let (pressure_real, pressure_imag) =
        solve_complex_graph_operator(&system, &source_real, &source_imag);
    let normalized_residual_norm = acoustic_residual_norm(
        &system,
        &pressure_real,
        &pressure_imag,
        &source_real,
        &source_imag,
    );
    let pressure_magnitude = pressure_real
        .iter()
        .zip(pressure_imag.iter())
        .map(|(real, imag)| real.hypot(*imag))
        .collect::<Vec<_>>();
    let phase = pressure_real
        .iter()
        .zip(pressure_imag.iter())
        .map(|(real, imag)| imag.atan2(*real))
        .collect::<Vec<_>>();
    let sound_pressure_level_db = pressure_magnitude
        .iter()
        .map(|pressure| 20.0 * (pressure.max(2.0e-5) / 2.0e-5).log10())
        .collect::<Vec<_>>();
    let particle_velocity = recover_acoustic_particle_velocity(
        &pressure_real,
        domain,
        drive_frequency_hz,
        density_kg_per_m3,
    );
    let peak_pressure_pa = pressure_magnitude.iter().copied().fold(0.0_f64, f64::max);
    let sweep_frequencies_hz = acoustic_sweep_frequencies_hz(drive_frequency_hz);
    let mut frequency_response_fields = Vec::with_capacity(sweep_frequencies_hz.len());
    let mut sweep_peak_pressure_pa = 0.0_f64;
    let mut sweep_residual_norm = 0.0_f64;
    for frequency_hz in &sweep_frequencies_hz {
        let sweep_system = acoustic_helmholtz_operator(
            domain,
            node_count,
            *frequency_hz,
            speed_of_sound_m_per_s,
            damping_ratio,
            &boundary_summary,
        );
        let (sweep_real, sweep_imag) =
            solve_complex_graph_operator(&sweep_system, &source_real, &source_imag);
        let sweep_magnitude = sweep_real
            .iter()
            .zip(sweep_imag.iter())
            .map(|(real, imag)| real.hypot(*imag))
            .collect::<Vec<_>>();
        sweep_peak_pressure_pa =
            sweep_peak_pressure_pa.max(sweep_magnitude.iter().copied().fold(0.0_f64, f64::max));
        sweep_residual_norm = sweep_residual_norm.max(acoustic_residual_norm(
            &sweep_system,
            &sweep_real,
            &sweep_imag,
            &source_real,
            &source_imag,
        ));
        frequency_response_fields.push(AnalysisField::host_f64(
            fea_acoustic_frequency_response_field_id(*frequency_hz),
            vec![node_count],
            sweep_magnitude,
        ));
    }
    let sweep_frequency_min_hz = sweep_frequencies_hz
        .iter()
        .copied()
        .fold(f64::INFINITY, f64::min);
    let sweep_frequency_max_hz = sweep_frequencies_hz.iter().copied().fold(0.0_f64, f64::max);
    let sweep_bandwidth_hz = if sweep_frequency_min_hz.is_finite() {
        (sweep_frequency_max_hz - sweep_frequency_min_hz).max(0.0)
    } else {
        0.0
    };
    let known_answer = acoustic_known_answer_metrics(
        domain,
        &pressure_magnitude,
        drive_frequency_hz,
        speed_of_sound_m_per_s,
    );
    let mut fields = vec![
        AnalysisField::host_f64(
            FEA_FIELD_ACOUSTIC_PRESSURE_REAL,
            vec![node_count],
            pressure_real,
        ),
        AnalysisField::host_f64(
            FEA_FIELD_ACOUSTIC_PRESSURE_IMAG,
            vec![node_count],
            pressure_imag,
        ),
        AnalysisField::host_f64(
            FEA_FIELD_ACOUSTIC_PRESSURE_MAGNITUDE,
            vec![node_count],
            pressure_magnitude,
        ),
        AnalysisField::host_f64(FEA_FIELD_ACOUSTIC_PHASE, vec![node_count], phase),
        AnalysisField::host_f64(
            FEA_FIELD_ACOUSTIC_SOUND_PRESSURE_LEVEL_DB,
            vec![node_count],
            sound_pressure_level_db,
        ),
        AnalysisField::host_f64(
            FEA_FIELD_ACOUSTIC_PARTICLE_VELOCITY,
            vec![node_count, 3],
            particle_velocity,
        ),
    ];
    let acoustic_field_count = fields.len() + frequency_response_fields.len();
    fields.extend(frequency_response_fields);
    let severity = if normalized_residual_norm <= residual_warn_threshold {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };
    FeaRunResult {
        backend,
        solver_backend: "cpu_reference".to_string(),
        solver_device_apply_k_ratio: 0.0,
        solver_method: "acoustic_domain_graph_helmholtz_harmonic".to_string(),
        preconditioner: "none".to_string(),
        solver_host_sync_count: 0,
        diagnostics: vec![
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_HELMHOLTZ_RESIDUAL".to_string(),
                severity,
                message: format!(
                    "normalized_residual_norm={} equation_scale={} residual_warn_threshold={}",
                    normalized_residual_norm,
                    source_norm(&source_real, &source_imag).max(1.0),
                    residual_warn_threshold,
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_DOMAIN_ASSEMBLY".to_string(),
                severity: if domain.edge_count > 0 && domain.active_dimension_count >= 2 {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "domain_node_count={} domain_edge_count={} domain_active_dimension_count={} domain_dim_x={} domain_dim_y={} domain_dim_z={} domain_spacing_x_m={} domain_spacing_y_m={} domain_spacing_z_m={} boundary_node_count={} average_node_degree={} source_node_count={} domain_volume_m3={}",
                    domain.node_count,
                    domain.edge_count,
                    domain.active_dimension_count,
                    domain.dims[0],
                    domain.dims[1],
                    domain.dims[2],
                    domain.spacing[0],
                    domain.spacing[1],
                    domain.spacing[2],
                    domain.boundary_node_count,
                    domain.average_node_degree(),
                    source_real.iter().filter(|value| value.abs() > 1.0e-12).count(),
                    domain.volume_m3(),
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_HARMONIC_RESPONSE".to_string(),
                severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
                message: format!(
                    "drive_frequency_hz={} speed_of_sound_m_per_s={} density_kg_per_m3={} damping_ratio={} acoustic_node_count={} acoustic_field_count={} peak_pressure_pa={} acoustic_material_count={} acoustic_material_coverage_ratio={}",
                    drive_frequency_hz,
                    speed_of_sound_m_per_s,
                    density_kg_per_m3,
                    damping_ratio,
                    node_count,
                    acoustic_field_count,
                    peak_pressure_pa,
                    material_summary.explicit_material_count,
                    material_summary.coverage_ratio,
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_BOUNDARY_MODEL".to_string(),
                severity: if boundary_summary.has_acoustic_boundary_data() {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "acoustic_boundary_count={} rigid_wall_count={} radiation_boundary_count={} impedance_boundary_count={} acoustic_boundary_coverage_ratio={} mean_specific_impedance_pa_s_per_m={} radiation_loss_factor={} impedance_loss_factor={}",
                    boundary_summary.acoustic_boundary_count,
                    boundary_summary.rigid_wall_count,
                    boundary_summary.radiation_boundary_count,
                    boundary_summary.impedance_boundary_count,
                    boundary_summary.coverage_ratio,
                    boundary_summary.mean_specific_impedance_pa_s_per_m,
                    boundary_summary.radiation_loss_factor,
                    boundary_summary.impedance_loss_factor,
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_FREQUENCY_RESPONSE".to_string(),
                severity: if sweep_residual_norm <= residual_warn_threshold {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "sweep_count={} sweep_frequency_min_hz={} sweep_frequency_max_hz={} sweep_bandwidth_hz={} sweep_peak_pressure_pa={} sweep_max_residual_norm={} response_coverage_ratio={}",
                    sweep_frequencies_hz.len(),
                    sweep_frequency_min_hz,
                    sweep_frequency_max_hz,
                    sweep_bandwidth_hz,
                    sweep_peak_pressure_pa,
                    sweep_residual_norm,
                    if sweep_frequencies_hz.is_empty() { 0.0 } else { 1.0 }
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_ACOUSTIC_KNOWN_ANSWER".to_string(),
                severity: if known_answer.known_answer_coverage_ratio >= 1.0
                    && known_answer.tube_mode_alignment_error_ratio <= 0.5
                    && known_answer.tube_pressure_variation_ratio > 1.0e-12
                    && known_answer.cavity_mode_spacing_ratio.is_finite()
                    && known_answer.cavity_mode_spacing_ratio > 0.0
                {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "tube_mode_alignment_error_ratio={} tube_pressure_variation_ratio={} cavity_mode_spacing_ratio={} cavity_reference_mode_count={} known_answer_coverage_ratio={}",
                    known_answer.tube_mode_alignment_error_ratio,
                    known_answer.tube_pressure_variation_ratio,
                    known_answer.cavity_mode_spacing_ratio,
                    known_answer.cavity_reference_mode_count,
                    known_answer.known_answer_coverage_ratio,
                ),
            },
        ],
        fields,
    }
}

#[derive(Debug, Clone, Copy)]
struct AcousticKnownAnswerMetrics {
    tube_mode_alignment_error_ratio: f64,
    tube_pressure_variation_ratio: f64,
    cavity_mode_spacing_ratio: f64,
    cavity_reference_mode_count: usize,
    known_answer_coverage_ratio: f64,
}

fn acoustic_known_answer_metrics(
    topology: AcousticDomainTopology,
    pressure_magnitude: &[f64],
    drive_frequency_hz: f64,
    speed_of_sound_m_per_s: f64,
) -> AcousticKnownAnswerMetrics {
    let tube_length_m = topology.spacing[0] * topology.dims[0].saturating_sub(1).max(1) as f64;
    let fundamental_hz = speed_of_sound_m_per_s.max(1.0) / (2.0 * tube_length_m.max(1.0e-9));
    let nearest_mode = (drive_frequency_hz / fundamental_hz).round().max(1.0);
    let nearest_mode_frequency_hz = nearest_mode * fundamental_hz;
    let tube_mode_alignment_error_ratio = (drive_frequency_hz - nearest_mode_frequency_hz).abs()
        / drive_frequency_hz.abs().max(fundamental_hz);
    let (min_pressure, max_pressure) = pressure_magnitude
        .iter()
        .copied()
        .fold((f64::INFINITY, 0.0_f64), |(min_value, max_value), value| {
            (min_value.min(value), max_value.max(value))
        });
    let tube_pressure_variation_ratio = if min_pressure.is_finite() {
        (max_pressure - min_pressure).max(0.0) / max_pressure.max(1.0e-12)
    } else {
        0.0
    };

    let lengths = [
        topology.spacing[0] * topology.dims[0].saturating_sub(1).max(1) as f64,
        topology.spacing[1] * topology.dims[1].saturating_sub(1).max(1) as f64,
        topology.spacing[2] * topology.dims[2].saturating_sub(1).max(1) as f64,
    ];
    let mut cavity_reference_modes = Vec::new();
    for nx in 0..=1 {
        for ny in 0..=1 {
            for nz in 0..=1 {
                if nx == 0 && ny == 0 && nz == 0 {
                    continue;
                }
                let mode_sum = (nx as f64 / lengths[0].max(1.0e-9)).powi(2)
                    + (ny as f64 / lengths[1].max(1.0e-9)).powi(2)
                    + (nz as f64 / lengths[2].max(1.0e-9)).powi(2);
                cavity_reference_modes
                    .push(0.5 * speed_of_sound_m_per_s.max(1.0) * mode_sum.sqrt());
            }
        }
    }
    cavity_reference_modes.sort_by(|a, b| a.total_cmp(b));
    let cavity_reference_mode_count = cavity_reference_modes.len();
    let cavity_mode_spacing_ratio = cavity_reference_modes
        .windows(2)
        .map(|window| (window[1] - window[0]).abs())
        .filter(|spacing| *spacing > 1.0e-9)
        .fold(f64::INFINITY, f64::min)
        / fundamental_hz.max(1.0e-12);
    let cavity_mode_spacing_ratio = if cavity_mode_spacing_ratio.is_finite() {
        cavity_mode_spacing_ratio
    } else {
        0.0
    };

    AcousticKnownAnswerMetrics {
        tube_mode_alignment_error_ratio,
        tube_pressure_variation_ratio,
        cavity_mode_spacing_ratio,
        cavity_reference_mode_count,
        known_answer_coverage_ratio: if cavity_reference_mode_count > 0
            && !pressure_magnitude.is_empty()
            && drive_frequency_hz.is_finite()
        {
            1.0
        } else {
            0.0
        },
    }
}

fn acoustic_sweep_frequencies_hz(drive_frequency_hz: f64) -> Vec<f64> {
    let mut frequencies = Vec::new();
    for scale in [0.75, 1.0, 1.25] {
        let frequency = (drive_frequency_hz * scale).clamp(50.0, 20_000.0);
        if !frequencies
            .iter()
            .any(|existing| f64::abs(*existing - frequency) <= 1.0e-9)
        {
            frequencies.push(frequency);
        }
    }
    frequencies
}

fn acoustic_node_count(
    model: &AnalysisModel,
    prep_context: Option<&AnalysisRunPrepContext>,
) -> usize {
    prep_context
        .map(|prep| prep.prepared_node_count.max(3))
        .unwrap_or_else(|| model.loads.len().saturating_mul(3).max(3))
        .min(512)
}

#[derive(Debug, Clone, Copy)]
struct AcousticMaterialSummary {
    density_kg_per_m3: f64,
    speed_of_sound_m_per_s: f64,
    damping_ratio: f64,
    explicit_material_count: usize,
    coverage_ratio: f64,
}

impl AcousticMaterialSummary {
    fn characteristic_impedance_pa_s_per_m(self) -> f64 {
        (self.density_kg_per_m3 * self.speed_of_sound_m_per_s).max(1.0)
    }
}

fn acoustic_material_summary(model: &AnalysisModel, mode_count: usize) -> AcousticMaterialSummary {
    let mut density_sum = 0.0;
    let mut speed_sum = 0.0;
    let mut damping_sum = 0.0;
    let mut explicit_material_count = 0usize;
    for material in &model.materials {
        let Some(acoustic) = &material.acoustic else {
            continue;
        };
        density_sum += acoustic.density_kg_per_m3.max(1.0e-9);
        speed_sum += acoustic.speed_of_sound_m_per_s.max(1.0);
        damping_sum += acoustic.damping_ratio.max(0.0);
        explicit_material_count += 1;
    }
    if explicit_material_count == 0 {
        let reference_temperature_k = acoustic_reference_temperature_k(model);
        let speed_of_sound_m_per_s = 331.3 * (reference_temperature_k / 273.15).sqrt();
        let density_kg_per_m3 = 1.225 * (293.15 / reference_temperature_k.max(1.0));
        return AcousticMaterialSummary {
            density_kg_per_m3,
            speed_of_sound_m_per_s,
            damping_ratio: 0.02 + 0.002 * mode_count.saturating_sub(1).min(12) as f64,
            explicit_material_count,
            coverage_ratio: 0.0,
        };
    }
    let inv_count = 1.0 / explicit_material_count as f64;
    AcousticMaterialSummary {
        density_kg_per_m3: density_sum * inv_count,
        speed_of_sound_m_per_s: speed_sum * inv_count,
        damping_ratio: damping_sum * inv_count,
        explicit_material_count,
        coverage_ratio: explicit_material_count as f64 / model.materials.len().max(1) as f64,
    }
}

#[derive(Debug, Clone, Copy)]
struct AcousticBoundarySummary {
    acoustic_boundary_count: usize,
    rigid_wall_count: usize,
    radiation_boundary_count: usize,
    impedance_boundary_count: usize,
    coverage_ratio: f64,
    mean_specific_impedance_pa_s_per_m: f64,
    radiation_loss_factor: f64,
    impedance_loss_factor: f64,
}

impl AcousticBoundarySummary {
    fn has_acoustic_boundary_data(self) -> bool {
        self.acoustic_boundary_count > 0
    }
}

fn acoustic_boundary_summary(
    model: &AnalysisModel,
    characteristic_impedance_pa_s_per_m: f64,
) -> AcousticBoundarySummary {
    let mut rigid_wall_count = 0usize;
    let mut radiation_boundary_count = 0usize;
    let mut impedance_boundary_count = 0usize;
    let mut impedance_sum = 0.0;
    for bc in &model.boundary_conditions {
        match bc.kind {
            BoundaryConditionKind::AcousticRigidWall => rigid_wall_count += 1,
            BoundaryConditionKind::AcousticRadiation => radiation_boundary_count += 1,
            BoundaryConditionKind::AcousticImpedance {
                specific_impedance_pa_s_per_m,
            } => {
                impedance_boundary_count += 1;
                impedance_sum += specific_impedance_pa_s_per_m.max(1.0);
            }
            _ => {}
        }
    }
    let acoustic_boundary_count =
        rigid_wall_count + radiation_boundary_count + impedance_boundary_count;
    let mean_specific_impedance_pa_s_per_m = if impedance_boundary_count == 0 {
        characteristic_impedance_pa_s_per_m
    } else {
        impedance_sum / impedance_boundary_count as f64
    };
    let impedance_ratio =
        characteristic_impedance_pa_s_per_m / mean_specific_impedance_pa_s_per_m.max(1.0);
    AcousticBoundarySummary {
        acoustic_boundary_count,
        rigid_wall_count,
        radiation_boundary_count,
        impedance_boundary_count,
        coverage_ratio: acoustic_boundary_count as f64
            / model.boundary_conditions.len().max(1) as f64,
        mean_specific_impedance_pa_s_per_m,
        radiation_loss_factor: 0.05 * radiation_boundary_count as f64,
        impedance_loss_factor: 0.025
            * impedance_boundary_count as f64
            * impedance_ratio.clamp(0.1, 10.0),
    }
}

fn acoustic_reference_temperature_k(model: &AnalysisModel) -> f64 {
    if model.materials.is_empty() {
        293.15
    } else {
        model
            .materials
            .iter()
            .map(|material| material.thermal.reference_temperature_k.max(1.0))
            .sum::<f64>()
            / model.materials.len() as f64
    }
}

fn acoustic_drive_frequency_hz(mode_count: usize, node_count: usize) -> f64 {
    (125.0 * mode_count.max(1) as f64 * (node_count as f64).sqrt()).clamp(50.0, 20_000.0)
}

#[derive(Debug, Clone, Copy)]
struct AcousticDomainTopology {
    node_count: usize,
    dims: [usize; 3],
    spacing: [f64; 3],
    edge_count: usize,
    boundary_node_count: usize,
    active_dimension_count: usize,
}

impl AcousticDomainTopology {
    fn coords(self, index: usize) -> [usize; 3] {
        let x_dim = self.dims[0].max(1);
        let y_dim = self.dims[1].max(1);
        let plane = x_dim.saturating_mul(y_dim).max(1);
        let z = index / plane;
        let rem = index % plane;
        let y = rem / x_dim;
        let x = rem % x_dim;
        [x, y, z]
    }

    fn index(self, coords: [usize; 3]) -> Option<usize> {
        if coords
            .iter()
            .zip(self.dims.iter())
            .any(|(coord, dim)| *coord >= *dim)
        {
            return None;
        }
        let index = coords[0]
            + coords[1].saturating_mul(self.dims[0])
            + coords[2].saturating_mul(self.dims[0].saturating_mul(self.dims[1]));
        (index < self.node_count).then_some(index)
    }

    fn is_boundary_node(self, index: usize) -> bool {
        let coords = self.coords(index);
        coords
            .iter()
            .zip(self.dims.iter())
            .any(|(coord, dim)| *dim > 1 && (*coord == 0 || *coord + 1 == *dim))
    }

    fn average_node_degree(self) -> f64 {
        if self.node_count == 0 {
            0.0
        } else {
            2.0 * self.edge_count as f64 / self.node_count as f64
        }
    }

    fn volume_m3(self) -> f64 {
        self.spacing
            .iter()
            .zip(self.dims.iter())
            .map(|(spacing, dim)| spacing * dim.saturating_sub(1).max(1) as f64)
            .product::<f64>()
            .max(1.0e-12)
    }
}

#[derive(Debug, Clone, Copy)]
struct AcousticGraphEdge {
    left: usize,
    right: usize,
    stiffness: f64,
}

#[derive(Debug, Clone)]
struct AcousticDomainSystem {
    diag_real: Vec<f64>,
    diag_imag: Vec<f64>,
    edges: Vec<AcousticGraphEdge>,
}

fn acoustic_domain_topology(
    node_count: usize,
    prep_context: Option<&AnalysisRunPrepContext>,
) -> AcousticDomainTopology {
    let n = node_count.max(1);
    let volume_hint = prep_context
        .map(|prep| {
            prep.topology_volume_core_ratio
                + prep.topology_tet_family_ratio
                + prep.topology_hex_family_ratio
        })
        .unwrap_or(0.0);
    let z_dim = if n >= 8 && volume_hint > 0.05 {
        (n as f64).cbrt().round().max(2.0) as usize
    } else if n >= 24 {
        2
    } else {
        1
    };
    let y_dim = if n >= 3 {
        ((n as f64 / z_dim as f64).sqrt().ceil() as usize).max(2)
    } else {
        1
    };
    let x_dim = n.div_ceil(y_dim * z_dim).max(1);
    let dims = [x_dim, y_dim, z_dim];
    let spacing = dims.map(|dim| {
        if dim <= 1 {
            1.0
        } else {
            1.0 / dim.saturating_sub(1) as f64
        }
    });
    let mut edge_count = 0usize;
    let mut boundary_node_count = 0usize;
    let topology = AcousticDomainTopology {
        node_count: n,
        dims,
        spacing,
        edge_count: 0,
        boundary_node_count: 0,
        active_dimension_count: dims.iter().filter(|dim| **dim > 1).count(),
    };
    for node in 0..n {
        if topology.is_boundary_node(node) {
            boundary_node_count += 1;
        }
        let coords = topology.coords(node);
        for axis in 0..3 {
            if coords[axis] + 1 >= topology.dims[axis] {
                continue;
            }
            let mut next = coords;
            next[axis] += 1;
            if topology.index(next).is_some() {
                edge_count += 1;
            }
        }
    }
    AcousticDomainTopology {
        edge_count,
        boundary_node_count,
        ..topology
    }
}

fn acoustic_source_vector(model: &AnalysisModel, node_count: usize) -> Vec<f64> {
    let mut source = vec![0.0; node_count.max(1)];
    for (index, load) in model.loads.iter().enumerate() {
        let node = (index * 3 + load.region_id.len()) % source.len();
        let amplitude = match &load.kind {
            LoadKind::Pressure { magnitude_pa } => *magnitude_pa,
            _ => 0.0,
        };
        source[node] += amplitude;
    }
    source
}

fn acoustic_helmholtz_operator(
    topology: AcousticDomainTopology,
    node_count: usize,
    drive_frequency_hz: f64,
    speed_of_sound_m_per_s: f64,
    damping_ratio: f64,
    boundary_summary: &AcousticBoundarySummary,
) -> AcousticDomainSystem {
    let n = node_count.max(1);
    let omega = 2.0 * std::f64::consts::PI * drive_frequency_hz.max(1.0);
    let wave_number = omega / speed_of_sound_m_per_s.max(1.0);
    let mut diag_real = vec![0.0; n];
    let mut diag_imag = vec![0.0; n];
    let mut edges = Vec::with_capacity(topology.edge_count);
    for node in 0..n {
        let coords = topology.coords(node);
        for axis in 0..3 {
            if coords[axis] + 1 >= topology.dims[axis] {
                continue;
            }
            let mut next = coords;
            next[axis] += 1;
            let Some(next_index) = topology.index(next) else {
                continue;
            };
            let stiffness = 1.0 / topology.spacing[axis].max(1.0e-9).powi(2);
            diag_real[node] += stiffness;
            diag_real[next_index] += stiffness;
            edges.push(AcousticGraphEdge {
                left: node,
                right: next_index,
                stiffness,
            });
        }
    }
    let mass_term =
        (wave_number * topology.volume_m3().cbrt()).powi(2) / topology.node_count.max(1) as f64;
    let damping = (2.0 * damping_ratio.max(0.0) * mass_term.max(1.0e-9)).max(1.0e-9);
    let boundary_loss = (boundary_summary.radiation_loss_factor
        + boundary_summary.impedance_loss_factor)
        * wave_number.abs().max(1.0e-9)
        / topology.boundary_node_count.max(1) as f64;
    for node in 0..n {
        diag_real[node] -= mass_term;
        diag_imag[node] += damping;
        if topology.is_boundary_node(node) {
            diag_imag[node] += boundary_loss;
            diag_real[node] += 0.02 * boundary_summary.rigid_wall_count as f64;
        }
    }
    AcousticDomainSystem {
        diag_real,
        diag_imag,
        edges,
    }
}

fn solve_complex_graph_operator(
    system: &AcousticDomainSystem,
    source_real: &[f64],
    source_imag: &[f64],
) -> (Vec<f64>, Vec<f64>) {
    let n = system.diag_real.len().max(1);
    let mut matrix_real = vec![vec![0.0; n]; n];
    let mut matrix_imag = vec![vec![0.0; n]; n];
    for row in 0..n {
        matrix_real[row][row] = system.diag_real[row];
        matrix_imag[row][row] = system.diag_imag[row];
    }
    for edge in &system.edges {
        matrix_real[edge.left][edge.right] -= edge.stiffness;
        matrix_real[edge.right][edge.left] -= edge.stiffness;
    }
    let mut rhs_real = (0..n)
        .map(|index| source_real.get(index).copied().unwrap_or(0.0))
        .collect::<Vec<_>>();
    let mut rhs_imag = (0..n)
        .map(|index| source_imag.get(index).copied().unwrap_or(0.0))
        .collect::<Vec<_>>();
    solve_dense_complex_system(
        &mut matrix_real,
        &mut matrix_imag,
        &mut rhs_real,
        &mut rhs_imag,
    )
}

fn acoustic_residual_norm(
    system: &AcousticDomainSystem,
    pressure_real: &[f64],
    pressure_imag: &[f64],
    source_real: &[f64],
    source_imag: &[f64],
) -> f64 {
    let mut residual_sq = 0.0_f64;
    for i in 0..system.diag_real.len() {
        let mut applied_real =
            system.diag_real[i] * pressure_real[i] - system.diag_imag[i] * pressure_imag[i];
        let mut applied_imag =
            system.diag_real[i] * pressure_imag[i] + system.diag_imag[i] * pressure_real[i];
        for edge in system
            .edges
            .iter()
            .filter(|edge| edge.left == i || edge.right == i)
        {
            let neighbor = if edge.left == i {
                edge.right
            } else {
                edge.left
            };
            applied_real -= edge.stiffness * pressure_real[neighbor];
            applied_imag -= edge.stiffness * pressure_imag[neighbor];
        }
        let real = applied_real - source_real.get(i).copied().unwrap_or(0.0);
        let imag = applied_imag - source_imag.get(i).copied().unwrap_or(0.0);
        residual_sq += real * real + imag * imag;
    }
    residual_sq.sqrt() / source_norm(source_real, source_imag).max(1.0)
}

fn source_norm(source_real: &[f64], source_imag: &[f64]) -> f64 {
    source_real
        .iter()
        .zip(source_imag.iter().chain(std::iter::repeat(&0.0)))
        .map(|(real, imag)| real * real + imag * imag)
        .sum::<f64>()
        .sqrt()
}

fn solve_dense_complex_system(
    matrix_real: &mut [Vec<f64>],
    matrix_imag: &mut [Vec<f64>],
    rhs_real: &mut [f64],
    rhs_imag: &mut [f64],
) -> (Vec<f64>, Vec<f64>) {
    let n = rhs_real.len();
    for pivot in 0..n {
        let mut pivot_row = pivot;
        let mut pivot_norm = complex_abs_sq(matrix_real[pivot][pivot], matrix_imag[pivot][pivot]);
        for candidate in pivot + 1..n {
            let candidate_norm =
                complex_abs_sq(matrix_real[candidate][pivot], matrix_imag[candidate][pivot]);
            if candidate_norm > pivot_norm {
                pivot_row = candidate;
                pivot_norm = candidate_norm;
            }
        }
        if pivot_row != pivot {
            matrix_real.swap(pivot, pivot_row);
            matrix_imag.swap(pivot, pivot_row);
            rhs_real.swap(pivot, pivot_row);
            rhs_imag.swap(pivot, pivot_row);
        }
        if pivot_norm <= 1.0e-24 {
            matrix_real[pivot][pivot] += 1.0e-9;
        }
        let (pivot_inv_real, pivot_inv_imag) =
            complex_recip(matrix_real[pivot][pivot], matrix_imag[pivot][pivot]);
        for row in pivot + 1..n {
            let (factor_real, factor_imag) = complex_mul(
                matrix_real[row][pivot],
                matrix_imag[row][pivot],
                pivot_inv_real,
                pivot_inv_imag,
            );
            matrix_real[row][pivot] = 0.0;
            matrix_imag[row][pivot] = 0.0;
            for col in pivot + 1..n {
                let (update_real, update_imag) = complex_mul(
                    factor_real,
                    factor_imag,
                    matrix_real[pivot][col],
                    matrix_imag[pivot][col],
                );
                matrix_real[row][col] -= update_real;
                matrix_imag[row][col] -= update_imag;
            }
            let (rhs_update_real, rhs_update_imag) =
                complex_mul(factor_real, factor_imag, rhs_real[pivot], rhs_imag[pivot]);
            rhs_real[row] -= rhs_update_real;
            rhs_imag[row] -= rhs_update_imag;
        }
    }

    let mut solution_real = vec![0.0; n];
    let mut solution_imag = vec![0.0; n];
    for row in (0..n).rev() {
        let mut accum_real = rhs_real[row];
        let mut accum_imag = rhs_imag[row];
        for col in row + 1..n {
            let (update_real, update_imag) = complex_mul(
                matrix_real[row][col],
                matrix_imag[row][col],
                solution_real[col],
                solution_imag[col],
            );
            accum_real -= update_real;
            accum_imag -= update_imag;
        }
        let (inv_real, inv_imag) = complex_recip(matrix_real[row][row], matrix_imag[row][row]);
        (solution_real[row], solution_imag[row]) =
            complex_mul(accum_real, accum_imag, inv_real, inv_imag);
    }
    (solution_real, solution_imag)
}

fn complex_abs_sq(real: f64, imag: f64) -> f64 {
    real * real + imag * imag
}

fn complex_recip(real: f64, imag: f64) -> (f64, f64) {
    let denom = (real * real + imag * imag).max(1.0e-24);
    (real / denom, -imag / denom)
}

fn complex_mul(a_real: f64, a_imag: f64, b_real: f64, b_imag: f64) -> (f64, f64) {
    (
        a_real * b_real - a_imag * b_imag,
        a_real * b_imag + a_imag * b_real,
    )
}

fn recover_acoustic_particle_velocity(
    pressure_real: &[f64],
    topology: AcousticDomainTopology,
    drive_frequency_hz: f64,
    density_kg_per_m3: f64,
) -> Vec<f64> {
    let node_count = pressure_real.len().max(1);
    let omega = (2.0 * std::f64::consts::PI * drive_frequency_hz).max(1.0e-12);
    let impedance_scale = (density_kg_per_m3.max(1.0e-12) * omega).max(1.0e-12);
    let mut velocity = vec![0.0; node_count * 3];
    for node in 0..pressure_real.len() {
        for axis in 0..3 {
            velocity[node * 3 + axis] =
                -acoustic_axis_derivative(pressure_real, topology, node, axis) / impedance_scale;
        }
    }
    velocity
}

fn acoustic_axis_derivative(
    pressure: &[f64],
    topology: AcousticDomainTopology,
    index: usize,
    axis: usize,
) -> f64 {
    if topology.dims[axis] <= 1 {
        return 0.0;
    }
    let coords = topology.coords(index);
    let mut prev_coords = coords;
    let prev_index = if coords[axis] > 0 {
        prev_coords[axis] -= 1;
        topology.index(prev_coords)
    } else {
        None
    };
    let mut next_coords = coords;
    let next_index = if coords[axis] + 1 < topology.dims[axis] {
        next_coords[axis] += 1;
        topology.index(next_coords)
    } else {
        None
    };
    let spacing = topology.spacing[axis].max(1.0e-12);
    match (prev_index, next_index) {
        (Some(prev), Some(next)) => (pressure[next] - pressure[prev]) / (2.0 * spacing),
        (Some(prev), None) => (pressure[index] - pressure[prev]) / spacing,
        (None, Some(next)) => (pressure[next] - pressure[index]) / spacing,
        (None, None) => 0.0,
    }
}

fn cfd_reynolds_number(domain: &runmat_analysis_core::CfdDomain) -> f64 {
    cfd_reynolds_number_for_velocity(domain, domain.inlet_velocity_m_per_s)
}

fn cfd_reynolds_number_for_velocity(
    domain: &runmat_analysis_core::CfdDomain,
    inlet_velocity_m_per_s: f64,
) -> f64 {
    domain.reference_density_kg_per_m3 * inlet_velocity_m_per_s.abs()
        / domain.dynamic_viscosity_pa_s
}

fn cfd_profile_scale(domain: &runmat_analysis_core::CfdDomain, step_index: usize) -> f64 {
    domain
        .time_profile
        .get(step_index)
        .map(|point| point.inlet_scale)
        .filter(|scale| scale.is_finite() && *scale >= 0.0)
        .unwrap_or(1.0)
}

fn cfd_node_count_from_model(
    model: &AnalysisModel,
    prep_context: Option<&AnalysisRunPrepContext>,
) -> usize {
    prep_context
        .map(|prep| prep.prepared_node_count.max(3))
        .unwrap_or_else(|| model.loads.len().saturating_mul(3).max(3))
        .min(512)
}

#[derive(Clone, Debug)]
struct CfdDomainTopology {
    basis: CfdDomainTopologyBasis,
    geometry_source: CfdDomainGeometrySource,
    node_count: usize,
    control_volume_count: usize,
    control_volume_face_count: usize,
    control_volume_internal_face_count: usize,
    control_volume_boundary_face_count: usize,
    control_volume_connectivity_coverage_ratio: f64,
    domain_length_m: f64,
    hydraulic_diameter_m: f64,
    face_area_m2: f64,
    dx_m: f64,
    active_dimension_count: usize,
    element_geometry_node_count: usize,
    element_geometry_edge_count: usize,
    element_geometry_coverage_ratio: f64,
    element_topology_sample_element_count: usize,
    element_topology_sample_edge_count: usize,
    element_topology_sample_element_edges: [[u32; 3]; 4],
    element_topology_edge_nodes: Vec<[u32; 2]>,
    element_topology_element_edges: Vec<[u32; 3]>,
}

impl CfdDomainTopology {
    fn from_model(model: &AnalysisModel, prep_context: Option<&AnalysisRunPrepContext>) -> Self {
        let node_count = cfd_node_count_from_model(model, prep_context);
        match prep_context {
            Some(prep) => Self::from_prep(node_count, prep),
            None => Self::implicit_channel(node_count),
        }
    }

    fn implicit_channel(node_count: usize) -> Self {
        let node_count = node_count.max(2);
        let control_volume_count = node_count.saturating_sub(1).max(1);
        Self {
            basis: CfdDomainTopologyBasis::ImplicitChannel,
            geometry_source: CfdDomainGeometrySource::ImplicitChannel,
            node_count,
            control_volume_count,
            control_volume_face_count: control_volume_count.saturating_add(1),
            control_volume_internal_face_count: control_volume_count.saturating_sub(1),
            control_volume_boundary_face_count: 2,
            control_volume_connectivity_coverage_ratio: 0.0,
            domain_length_m: 1.0,
            hydraulic_diameter_m: 1.0,
            face_area_m2: 1.0,
            dx_m: 1.0 / control_volume_count as f64,
            active_dimension_count: 1,
            element_geometry_node_count: 0,
            element_geometry_edge_count: 0,
            element_geometry_coverage_ratio: 0.0,
            element_topology_sample_element_count: 0,
            element_topology_sample_edge_count: 0,
            element_topology_sample_element_edges: [[0; 3]; 4],
            element_topology_edge_nodes: Vec::new(),
            element_topology_element_edges: Vec::new(),
        }
    }

    fn from_prep(node_count: usize, prep: &AnalysisRunPrepContext) -> Self {
        let has_control_volume_connectivity = prep.control_volume_cell_count > 0
            && prep.control_volume_face_count > 0
            && prep.control_volume_connectivity_coverage_ratio > 0.0;
        let control_volume_count = if has_control_volume_connectivity {
            prep.control_volume_cell_count.max(1)
        } else {
            node_count.max(2).saturating_sub(1).max(1)
        };
        let node_count = if has_control_volume_connectivity {
            control_volume_count.saturating_add(1).max(2)
        } else {
            node_count.max(2)
        };
        let fallback_length = finite_positive_or(prep.coordinate_characteristic_length_m, 1.0)
            * control_volume_count as f64;
        let domain_length_m = finite_positive_or(prep.coordinate_span_x_m, fallback_length);
        let transverse_y = finite_positive_or(prep.coordinate_span_y_m, 0.0);
        let transverse_z = finite_positive_or(prep.coordinate_span_z_m, 0.0);
        let hydraulic_diameter_m = if transverse_y > 0.0 && transverse_z > 0.0 {
            (2.0 * transverse_y * transverse_z / (transverse_y + transverse_z)).max(1.0e-12)
        } else {
            finite_positive_or(prep.coordinate_characteristic_length_m, 1.0)
        };
        let coordinate_face_area_m2 = hydraulic_diameter_m.max(1.0e-12).powi(2);
        let has_element_geometry = prep.element_geometry_coverage_ratio > 0.0
            && prep.mean_element_area_m2.is_finite()
            && prep.mean_element_area_m2 > 0.0;
        let face_area_m2 = if has_element_geometry {
            prep.mean_element_area_m2
        } else {
            coordinate_face_area_m2
        };
        let hydraulic_diameter_m = if has_element_geometry {
            (4.0 * face_area_m2 / std::f64::consts::PI)
                .sqrt()
                .max(1.0e-12)
        } else {
            hydraulic_diameter_m
        };
        Self {
            basis: CfdDomainTopologyBasis::PrepControlVolumeConnectivity,
            geometry_source: if has_element_geometry {
                CfdDomainGeometrySource::PrepElementGeometry
            } else {
                CfdDomainGeometrySource::CoordinateSpan
            },
            node_count,
            control_volume_count,
            control_volume_face_count: if has_control_volume_connectivity {
                prep.control_volume_face_count
            } else {
                control_volume_count.saturating_add(1)
            },
            control_volume_internal_face_count: if has_control_volume_connectivity {
                prep.control_volume_internal_face_count
            } else {
                control_volume_count.saturating_sub(1)
            },
            control_volume_boundary_face_count: if has_control_volume_connectivity {
                prep.control_volume_boundary_face_count
            } else {
                2
            },
            control_volume_connectivity_coverage_ratio: prep
                .control_volume_connectivity_coverage_ratio
                .clamp(0.0, 1.0),
            domain_length_m,
            hydraulic_diameter_m,
            face_area_m2,
            dx_m: domain_length_m / control_volume_count as f64,
            active_dimension_count: prep.coordinate_active_dimension_count.max(1),
            element_geometry_node_count: prep.element_geometry_node_count,
            element_geometry_edge_count: prep.element_geometry_edge_count,
            element_geometry_coverage_ratio: prep.element_geometry_coverage_ratio.clamp(0.0, 1.0),
            element_topology_sample_element_count: prep.element_topology_sample_element_count,
            element_topology_sample_edge_count: prep.element_topology_sample_edge_count,
            element_topology_sample_element_edges: prep.element_topology_sample_element_edges,
            element_topology_edge_nodes: prep.element_topology_edge_nodes.clone(),
            element_topology_element_edges: prep.element_topology_element_edges.clone(),
        }
    }

    fn face_area_m2(&self) -> f64 {
        self.face_area_m2.max(1.0e-12)
    }

    fn control_volume_volume_m3(&self) -> f64 {
        self.face_area_m2() * self.dx_m.max(1.0e-12)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CfdDomainTopologyBasis {
    PrepControlVolumeConnectivity,
    ImplicitChannel,
}

impl CfdDomainTopologyBasis {
    fn as_str(self) -> &'static str {
        match self {
            Self::PrepControlVolumeConnectivity => "prep_control_volume_connectivity",
            Self::ImplicitChannel => "implicit_channel",
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CfdDomainGeometrySource {
    ImplicitChannel,
    CoordinateSpan,
    PrepElementGeometry,
}

impl CfdDomainGeometrySource {
    fn as_str(self) -> &'static str {
        match self {
            Self::ImplicitChannel => "implicit_channel",
            Self::CoordinateSpan => "coordinate_span",
            Self::PrepElementGeometry => "prep_element_geometry",
        }
    }
}

fn finite_positive_or(value: f64, fallback: f64) -> f64 {
    if value.is_finite() && value > 0.0 {
        value
    } else {
        fallback
    }
}

#[derive(Clone, Debug)]
struct CfdBoundarySummary {
    inlet_boundary_count: usize,
    outlet_boundary_count: usize,
    no_slip_wall_boundary_count: usize,
    slip_wall_boundary_count: usize,
    symmetry_boundary_count: usize,
    authored_boundary_count: usize,
    boundary_coverage_ratio: f64,
    wall_boundary_coverage_ratio: f64,
    nominal_inlet_velocity_m_per_s: f64,
    outlet_pressure_pa: f64,
}

impl CfdBoundarySummary {
    fn implicit_channel(domain: &runmat_analysis_core::CfdDomain, node_count: usize) -> Self {
        Self {
            inlet_boundary_count: 1,
            outlet_boundary_count: 1,
            no_slip_wall_boundary_count: node_count.saturating_sub(1).max(1),
            slip_wall_boundary_count: 0,
            symmetry_boundary_count: 0,
            authored_boundary_count: 0,
            boundary_coverage_ratio: 1.0,
            wall_boundary_coverage_ratio: 1.0,
            nominal_inlet_velocity_m_per_s: domain.inlet_velocity_m_per_s,
            outlet_pressure_pa: 0.0,
        }
    }

    fn from_model(
        model: &AnalysisModel,
        domain: &runmat_analysis_core::CfdDomain,
        node_count: usize,
    ) -> Self {
        let mut inlet_velocity_sum = 0.0_f64;
        let mut outlet_pressure_sum = 0.0_f64;
        let mut summary = Self {
            inlet_boundary_count: 0,
            outlet_boundary_count: 0,
            no_slip_wall_boundary_count: 0,
            slip_wall_boundary_count: 0,
            symmetry_boundary_count: 0,
            authored_boundary_count: 0,
            boundary_coverage_ratio: 0.0,
            wall_boundary_coverage_ratio: 0.0,
            nominal_inlet_velocity_m_per_s: domain.inlet_velocity_m_per_s,
            outlet_pressure_pa: 0.0,
        };

        for boundary in &model.boundary_conditions {
            match &boundary.kind {
                BoundaryConditionKind::CfdInletVelocity { velocity_m_per_s } => {
                    summary.inlet_boundary_count += 1;
                    summary.authored_boundary_count += 1;
                    inlet_velocity_sum += *velocity_m_per_s;
                }
                BoundaryConditionKind::CfdOutletPressure { pressure_pa } => {
                    summary.outlet_boundary_count += 1;
                    summary.authored_boundary_count += 1;
                    outlet_pressure_sum += *pressure_pa;
                }
                BoundaryConditionKind::CfdNoSlipWall => {
                    summary.no_slip_wall_boundary_count += 1;
                    summary.authored_boundary_count += 1;
                }
                BoundaryConditionKind::CfdSlipWall => {
                    summary.slip_wall_boundary_count += 1;
                    summary.authored_boundary_count += 1;
                }
                BoundaryConditionKind::CfdSymmetry => {
                    summary.symmetry_boundary_count += 1;
                    summary.authored_boundary_count += 1;
                }
                _ => {}
            }
        }

        if summary.authored_boundary_count == 0 {
            return Self::implicit_channel(domain, node_count);
        }

        if summary.inlet_boundary_count > 0 {
            summary.nominal_inlet_velocity_m_per_s =
                inlet_velocity_sum / summary.inlet_boundary_count as f64;
        }
        if summary.outlet_boundary_count > 0 {
            summary.outlet_pressure_pa = outlet_pressure_sum / summary.outlet_boundary_count as f64;
        }

        let wall_like_count = summary.no_slip_wall_boundary_count
            + summary.slip_wall_boundary_count
            + summary.symmetry_boundary_count;
        let required_groups_present = usize::from(summary.inlet_boundary_count > 0)
            + usize::from(summary.outlet_boundary_count > 0)
            + usize::from(wall_like_count > 0);
        summary.boundary_coverage_ratio = required_groups_present as f64 / 3.0;
        summary.wall_boundary_coverage_ratio = if wall_like_count > 0 { 1.0 } else { 0.0 };
        summary
    }

    fn wall_boundary_count(&self) -> usize {
        self.no_slip_wall_boundary_count + self.slip_wall_boundary_count
    }
}

fn validate_authored_cfd_boundary_conditions(model: &AnalysisModel) -> Result<(), String> {
    let mut inlet_boundary_count = 0usize;
    let mut outlet_boundary_count = 0usize;
    let mut wall_like_boundary_count = 0usize;
    let mut authored_boundary_count = 0usize;

    for boundary in &model.boundary_conditions {
        match &boundary.kind {
            BoundaryConditionKind::CfdInletVelocity { velocity_m_per_s } => {
                authored_boundary_count += 1;
                inlet_boundary_count += 1;
                if !velocity_m_per_s.is_finite() || *velocity_m_per_s < 0.0 {
                    return Err(format!(
                        "cfd inlet boundary {} requires finite non-negative velocity_m_per_s",
                        boundary.bc_id
                    ));
                }
            }
            BoundaryConditionKind::CfdOutletPressure { pressure_pa } => {
                authored_boundary_count += 1;
                outlet_boundary_count += 1;
                if !pressure_pa.is_finite() {
                    return Err(format!(
                        "cfd outlet boundary {} requires finite pressure_pa",
                        boundary.bc_id
                    ));
                }
            }
            BoundaryConditionKind::CfdNoSlipWall
            | BoundaryConditionKind::CfdSlipWall
            | BoundaryConditionKind::CfdSymmetry => {
                authored_boundary_count += 1;
                wall_like_boundary_count += 1;
            }
            _ => {}
        }
    }

    if authored_boundary_count == 0 {
        return Ok(());
    }
    if inlet_boundary_count == 0 || outlet_boundary_count == 0 || wall_like_boundary_count == 0 {
        return Err(format!(
            "authored cfd boundaries require at least one inlet, outlet, and wall/symmetry boundary; got inlet={} outlet={} wall_like={}",
            inlet_boundary_count, outlet_boundary_count, wall_like_boundary_count,
        ));
    }
    Ok(())
}

#[derive(Clone, Debug)]
struct CfdVelocityPressureSolution {
    topology: CfdDomainTopology,
    velocity: Vec<f64>,
    pressure: Vec<f64>,
    residual_momentum: Vec<f64>,
    residual_continuity: Vec<f64>,
    mass_balance_residual: f64,
    pressure_drop_pa: f64,
    control_volume_count: usize,
    inlet_boundary_count: usize,
    outlet_boundary_count: usize,
    wall_boundary_count: usize,
    no_slip_wall_boundary_count: usize,
    slip_wall_boundary_count: usize,
    symmetry_boundary_count: usize,
    authored_boundary_count: usize,
    boundary_coverage_ratio: f64,
    wall_boundary_coverage_ratio: f64,
    inlet_velocity_realization_ratio: f64,
    nominal_inlet_velocity_m_per_s: f64,
    outlet_pressure_pa: f64,
    pressure_correction_iteration_count: usize,
    pressure_correction_residual_ratio: f64,
    velocity_correction_residual_ratio: f64,
    transient_scale_min: f64,
    transient_scale_max: f64,
    transient_scale_variation: f64,
}

#[derive(Clone, Debug)]
struct CfdKnownAnswerMetrics {
    pressure_drop_balance_ratio: f64,
    mass_flux_uniformity_ratio: f64,
    pressure_monotonic_cell_fraction: f64,
    known_answer_coverage_ratio: f64,
}

fn recover_cfd_velocity_pressure(
    domain: &runmat_analysis_core::CfdDomain,
    topology: &CfdDomainTopology,
    step_index: usize,
) -> (Vec<f64>, Vec<f64>) {
    let boundary_summary = CfdBoundarySummary::implicit_channel(domain, topology.node_count);
    let solution = solve_cfd_velocity_pressure(
        domain,
        &boundary_summary,
        topology,
        step_index,
        1,
        32,
        1.0e-8,
    );
    (solution.velocity, solution.pressure)
}

fn solve_cfd_velocity_pressure(
    domain: &runmat_analysis_core::CfdDomain,
    boundary_summary: &CfdBoundarySummary,
    topology: &CfdDomainTopology,
    step_index: usize,
    step_count: usize,
    max_linear_iters: usize,
    tolerance: f64,
) -> CfdVelocityPressureSolution {
    let node_count = topology.node_count.max(2);
    let profile_scale = cfd_profile_scale(domain, step_index);
    let nominal_inlet_velocity = boundary_summary.nominal_inlet_velocity_m_per_s;
    let inlet_velocity = nominal_inlet_velocity * profile_scale;
    let reynolds = cfd_reynolds_number_for_velocity(domain, nominal_inlet_velocity).max(1.0);
    let hydraulic_diameter_m = topology.hydraulic_diameter_m;
    let friction_factor = if reynolds <= 2300.0 {
        64.0 / reynolds
    } else {
        0.3164 / reynolds.powf(0.25)
    };
    let friction_gradient_pa_per_m = 0.5
        * domain.reference_density_kg_per_m3
        * inlet_velocity
        * inlet_velocity.abs()
        * friction_factor
        / hydraulic_diameter_m;
    let pressure_drop_pa = (friction_gradient_pa_per_m * topology.domain_length_m).max(0.0);
    let denom = node_count.saturating_sub(1).max(1) as f64;
    let mut axial_velocity = vec![inlet_velocity; node_count];
    let mut pressure = (0..node_count)
        .map(|node| {
            let xi = node as f64 / denom;
            boundary_summary.outlet_pressure_pa + 0.5 * pressure_drop_pa * (1.0 - xi)
        })
        .collect::<Vec<_>>();
    let target_pressure = (0..node_count)
        .map(|node| {
            let xi = node as f64 / denom;
            boundary_summary.outlet_pressure_pa + pressure_drop_pa * (1.0 - xi)
        })
        .collect::<Vec<_>>();
    let correction_iters = max_linear_iters.max(1);
    let correction_tolerance = tolerance.max(1.0e-12);
    let mut pressure_correction_residual_ratio = f64::INFINITY;
    let mut velocity_correction_residual_ratio = f64::INFINITY;
    let mut pressure_correction_iteration_count = 0usize;
    for iteration in 0..correction_iters {
        let previous_pressure = pressure.clone();
        let previous_velocity = axial_velocity.clone();
        for node in 0..node_count {
            pressure[node] = 0.35 * pressure[node] + 0.65 * target_pressure[node];
        }
        for node in 0..node_count {
            if node == 0 {
                axial_velocity[node] = inlet_velocity;
                continue;
            }
            if node + 1 == node_count {
                axial_velocity[node] = axial_velocity[node.saturating_sub(1)];
                continue;
            }
            let gradient = (pressure[node + 1] - pressure[node - 1]) / (2.0 * topology.dx_m);
            let pressure_driven_speed = ((-2.0 * gradient * hydraulic_diameter_m)
                / (domain.reference_density_kg_per_m3.max(1.0e-12) * friction_factor.max(1.0e-12)))
            .max(0.0)
            .sqrt();
            axial_velocity[node] = 0.50 * axial_velocity[node] + 0.50 * pressure_driven_speed;
        }
        axial_velocity[node_count - 1] = axial_velocity[node_count - 2];

        let pressure_correction_norm = pressure
            .iter()
            .zip(previous_pressure.iter())
            .map(|(current, previous)| (current - previous) * (current - previous))
            .sum::<f64>()
            .sqrt();
        let pressure_scale = target_pressure
            .iter()
            .map(|value| value * value)
            .sum::<f64>()
            .sqrt()
            .max(1.0);
        pressure_correction_residual_ratio = pressure_correction_norm / pressure_scale;
        let velocity_correction_norm = axial_velocity
            .iter()
            .zip(previous_velocity.iter())
            .map(|(current, previous)| (current - previous) * (current - previous))
            .sum::<f64>()
            .sqrt();
        let velocity_scale = axial_velocity
            .iter()
            .map(|value| value * value)
            .sum::<f64>()
            .sqrt()
            .max(inlet_velocity.abs())
            .max(1.0e-12);
        velocity_correction_residual_ratio = velocity_correction_norm / velocity_scale;
        pressure_correction_iteration_count = iteration + 1;
        if pressure_correction_residual_ratio <= correction_tolerance
            && velocity_correction_residual_ratio <= correction_tolerance
        {
            break;
        }
    }
    pressure = target_pressure;

    let mut velocity = Vec::with_capacity(node_count * 3);
    for (node, axial) in axial_velocity.iter().copied().enumerate() {
        let xi = node as f64 / denom;
        let recirculation = (2.0 * std::f64::consts::PI * xi).sin()
            * axial
            * domain.turbulence_intensity.clamp(0.0, 1.0)
            * 0.02;
        velocity.extend_from_slice(&[axial, recirculation, 0.0]);
    }

    let (residual_momentum, residual_continuity) =
        cfd_residual_norms(&velocity, &pressure, domain, topology, step_count);
    let mass_balance_residual = residual_continuity.iter().copied().fold(0.0_f64, f64::max);
    let inlet_velocity_realization_ratio =
        inlet_velocity.abs() / nominal_inlet_velocity.abs().max(1.0e-12);
    let (transient_scale_min, transient_scale_max) = cfd_transient_scale_bounds(domain);

    CfdVelocityPressureSolution {
        topology: topology.clone(),
        velocity,
        pressure,
        residual_momentum,
        residual_continuity,
        mass_balance_residual,
        pressure_drop_pa,
        control_volume_count: topology.control_volume_count,
        inlet_boundary_count: boundary_summary.inlet_boundary_count,
        outlet_boundary_count: boundary_summary.outlet_boundary_count,
        wall_boundary_count: boundary_summary.wall_boundary_count(),
        no_slip_wall_boundary_count: boundary_summary.no_slip_wall_boundary_count,
        slip_wall_boundary_count: boundary_summary.slip_wall_boundary_count,
        symmetry_boundary_count: boundary_summary.symmetry_boundary_count,
        authored_boundary_count: boundary_summary.authored_boundary_count,
        boundary_coverage_ratio: boundary_summary.boundary_coverage_ratio,
        wall_boundary_coverage_ratio: boundary_summary.wall_boundary_coverage_ratio,
        inlet_velocity_realization_ratio,
        nominal_inlet_velocity_m_per_s: nominal_inlet_velocity,
        outlet_pressure_pa: boundary_summary.outlet_pressure_pa,
        pressure_correction_iteration_count,
        pressure_correction_residual_ratio,
        velocity_correction_residual_ratio,
        transient_scale_min,
        transient_scale_max,
        transient_scale_variation: (transient_scale_max - transient_scale_min).abs(),
    }
}

fn cfd_transient_scale_bounds(domain: &runmat_analysis_core::CfdDomain) -> (f64, f64) {
    if domain.time_profile.is_empty() {
        return (1.0, 1.0);
    }
    let (min, max) = domain
        .time_profile
        .iter()
        .filter_map(|point| point.inlet_scale.is_finite().then_some(point.inlet_scale))
        .fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), scale| {
            (min.min(scale), max.max(scale))
        });
    if min.is_finite() && max.is_finite() {
        (min, max)
    } else {
        (1.0, 1.0)
    }
}

fn cfd_known_answer_metrics(solution: &CfdVelocityPressureSolution) -> CfdKnownAnswerMetrics {
    let node_count = solution.pressure.len();
    let pressure_drop_observed = match (solution.pressure.first(), solution.pressure.last()) {
        (Some(first), Some(last)) => first - last,
        _ => 0.0,
    };
    let pressure_drop_balance_ratio = if solution.pressure_drop_pa.abs() > 1.0e-12 {
        pressure_drop_observed / solution.pressure_drop_pa
    } else if pressure_drop_observed.abs() <= 1.0e-12 {
        1.0
    } else {
        0.0
    };

    let axial_values = (0..node_count)
        .map(|node| solution.velocity.get(node * 3).copied().unwrap_or(0.0))
        .collect::<Vec<_>>();
    let mean_axial = if axial_values.is_empty() {
        0.0
    } else {
        axial_values.iter().sum::<f64>() / axial_values.len() as f64
    };
    let max_axial_deviation = axial_values
        .iter()
        .map(|value| (value - mean_axial).abs())
        .fold(0.0_f64, f64::max);
    let mass_flux_uniformity_ratio = max_axial_deviation / mean_axial.abs().max(1.0e-12);

    let pressure_edge_count = node_count.saturating_sub(1);
    let pressure_monotonic_cell_fraction = if pressure_edge_count == 0 {
        1.0
    } else {
        let tolerance = solution.pressure_drop_pa.abs().max(1.0) * 1.0e-12;
        let monotonic_edges = solution
            .pressure
            .windows(2)
            .filter(|pair| pair[0] + tolerance >= pair[1])
            .count();
        monotonic_edges as f64 / pressure_edge_count as f64
    };

    let known_answer_coverage_ratio = if node_count >= 2
        && solution.control_volume_count == node_count - 1
        && solution.velocity.len() == node_count * 3
        && solution.pressure_drop_pa.is_finite()
        && solution.pressure.iter().all(|value| value.is_finite())
        && solution.velocity.iter().all(|value| value.is_finite())
    {
        1.0
    } else {
        0.0
    };

    CfdKnownAnswerMetrics {
        pressure_drop_balance_ratio,
        mass_flux_uniformity_ratio,
        pressure_monotonic_cell_fraction,
        known_answer_coverage_ratio,
    }
}

fn cfd_known_answer_diagnostic(
    metrics: &CfdKnownAnswerMetrics,
    topology: &CfdDomainTopology,
) -> runmat_analysis_fea::diagnostics::FeaDiagnostic {
    let severity = if (metrics.pressure_drop_balance_ratio - 1.0).abs() <= 1.0e-10
        && metrics.mass_flux_uniformity_ratio <= 1.0e-10
        && metrics.pressure_monotonic_cell_fraction >= 1.0
        && metrics.known_answer_coverage_ratio >= 1.0
    {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };

    runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_KNOWN_ANSWER".to_string(),
        severity,
        message: format!(
            "basis=finite_volume_channel topology_basis={} pressure_drop_balance_ratio={} mass_flux_uniformity_ratio={} pressure_monotonic_cell_fraction={} known_answer_coverage_ratio={}",
            topology.basis.as_str(),
            metrics.pressure_drop_balance_ratio,
            metrics.mass_flux_uniformity_ratio,
            metrics.pressure_monotonic_cell_fraction,
            metrics.known_answer_coverage_ratio,
        ),
    }
}

fn recover_cfd_vorticity(velocity: &[f64], node_count: usize, dx_m: f64) -> Vec<f64> {
    let mut vorticity = vec![0.0; node_count * 3];
    if node_count < 2 {
        return vorticity;
    }
    let dx_m = dx_m.max(1.0e-12);

    for node in 0..node_count {
        let prev = node.saturating_sub(1);
        let next = (node + 1).min(node_count - 1);
        let prev_base = prev * 3;
        let next_base = next * 3;
        let dvx = velocity.get(next_base).copied().unwrap_or(0.0)
            - velocity.get(prev_base).copied().unwrap_or(0.0);
        let dvy = velocity.get(next_base + 1).copied().unwrap_or(0.0)
            - velocity.get(prev_base + 1).copied().unwrap_or(0.0);
        let base = node * 3;
        vorticity[base] = 0.0;
        vorticity[base + 1] = -dvx / (2.0 * dx_m);
        vorticity[base + 2] = dvy / (2.0 * dx_m);
    }

    vorticity
}

fn recover_cfd_wall_shear_stress(
    domain: &runmat_analysis_core::CfdDomain,
    velocity: &[f64],
    field_count: usize,
) -> Vec<f64> {
    let mut shear = vec![0.0; field_count * 3];
    let viscosity = domain.dynamic_viscosity_pa_s;
    for index in 0..field_count {
        let base = index * 3;
        shear[base] = viscosity * velocity.get(base).copied().unwrap_or(0.0);
        shear[base + 1] = viscosity * velocity.get(base + 1).copied().unwrap_or(0.0);
    }
    shear
}

fn cfd_residual_norms(
    velocity: &[f64],
    pressure: &[f64],
    domain: &runmat_analysis_core::CfdDomain,
    topology: &CfdDomainTopology,
    step_count: usize,
) -> (Vec<f64>, Vec<f64>) {
    let node_count = pressure.len().max(1);
    let reynolds = cfd_reynolds_number(domain).max(1.0);
    let hydraulic_diameter_m = topology.hydraulic_diameter_m.max(1.0e-12);
    let friction_factor = if reynolds <= 2300.0 {
        64.0 / reynolds
    } else {
        0.3164 / reynolds.powf(0.25)
    };
    let mean_axial_velocity = (0..node_count)
        .map(|node| velocity.get(node * 3).copied().unwrap_or(0.0))
        .sum::<f64>()
        / node_count as f64;
    let friction_gradient_pa_per_m = 0.5
        * domain.reference_density_kg_per_m3.max(1.0e-12)
        * mean_axial_velocity
        * mean_axial_velocity.abs()
        * friction_factor
        / hydraulic_diameter_m;
    let dx = topology.dx_m.max(1.0e-12);
    let mut momentum_base = 0.0_f64;
    let mut continuity_base = 0.0_f64;
    for node in 0..node_count {
        let prev = node.saturating_sub(1);
        let next = (node + 1).min(node_count - 1);
        let velocity_prev = velocity.get(prev * 3).copied().unwrap_or(0.0);
        let velocity_next = velocity.get(next * 3).copied().unwrap_or(0.0);
        let pressure_prev = pressure.get(prev).copied().unwrap_or(0.0);
        let pressure_next = pressure.get(next).copied().unwrap_or(0.0);
        let stencil_width = if prev == next {
            1.0
        } else {
            (next - prev) as f64 * dx
        };
        let divergence = (velocity_next - velocity_prev) / stencil_width.max(1.0e-12);
        let pressure_gradient = (pressure_next - pressure_prev) / stencil_width.max(1.0e-12);
        continuity_base += divergence.abs();
        momentum_base += (pressure_gradient + friction_gradient_pa_per_m).abs();
    }
    let velocity_scale = mean_axial_velocity
        .abs()
        .max(domain.inlet_velocity_m_per_s)
        .max(1.0e-9);
    let pressure_scale = pressure
        .iter()
        .copied()
        .map(f64::abs)
        .fold(0.0_f64, f64::max)
        .max(1.0);
    let continuity_base = (continuity_base / (node_count as f64 * velocity_scale)).clamp(0.0, 1.0);
    let momentum_base = (momentum_base / (node_count as f64 * pressure_scale)).clamp(0.0, 1.0);
    let residual_count = step_count.max(1);
    (
        vec![momentum_base; residual_count],
        vec![continuity_base; residual_count],
    )
}

fn build_cfd_run_fields(
    domain: &runmat_analysis_core::CfdDomain,
    solution: &CfdVelocityPressureSolution,
) -> Vec<AnalysisField> {
    let node_count = solution.pressure.len();
    let velocity = cell_centered_vector_from_nodal(&solution.velocity, node_count);
    let pressure = cell_centered_scalar_from_nodal(&solution.pressure);
    let cell_count = pressure.len().max(1);
    let vorticity = recover_cfd_vorticity(&velocity, cell_count, solution.topology.dx_m);
    let boundary_face_count = solution.wall_boundary_count.max(1);
    let wall_shear_stress = recover_cfd_wall_shear_stress(domain, &velocity, boundary_face_count);
    let residual_count = solution.residual_momentum.len();

    vec![
        AnalysisField::host_f64(FEA_FIELD_CFD_VELOCITY, vec![cell_count, 3], velocity),
        AnalysisField::host_f64(FEA_FIELD_CFD_PRESSURE, vec![cell_count], pressure),
        AnalysisField::host_f64(FEA_FIELD_CFD_VORTICITY, vec![cell_count, 3], vorticity),
        AnalysisField::host_f64(
            FEA_FIELD_CFD_WALL_SHEAR_STRESS,
            vec![boundary_face_count, 3],
            wall_shear_stress,
        ),
        AnalysisField::host_f64(
            FEA_FIELD_CFD_RESIDUAL_MOMENTUM,
            vec![residual_count],
            solution.residual_momentum.clone(),
        ),
        AnalysisField::host_f64(
            FEA_FIELD_CFD_RESIDUAL_CONTINUITY,
            vec![residual_count],
            solution.residual_continuity.clone(),
        ),
        AnalysisField::host_f64(
            FEA_FIELD_CFD_REYNOLDS_NUMBER,
            vec![1],
            vec![cfd_reynolds_number(domain)],
        ),
    ]
}

fn cfd_assembly_diagnostic(
    topology: &CfdDomainTopology,
    domain: &runmat_analysis_core::CfdDomain,
    time_step_s: f64,
    pressure_drop_pa: f64,
    mass_balance_residual: f64,
    residual_warn_threshold: f64,
) -> runmat_analysis_fea::diagnostics::FeaDiagnostic {
    let face_area_m2 = topology.face_area_m2();
    let control_volume_volume_m3 = topology.control_volume_volume_m3();
    let nominal_mass_flow_rate_kg_per_s =
        domain.reference_density_kg_per_m3 * domain.inlet_velocity_m_per_s * face_area_m2;
    let courant_number =
        domain.inlet_velocity_m_per_s.abs() * time_step_s.max(0.0) / topology.dx_m.max(1.0e-12);
    runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_ASSEMBLY".to_string(),
        severity: if mass_balance_residual <= residual_warn_threshold {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
        } else {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
        },
        message: format!(
            "basis=finite_volume_velocity_pressure topology_basis={} topology_geometry_source={} control_volume_count={} control_volume_face_count={} control_volume_internal_face_count={} control_volume_boundary_face_count={} control_volume_connectivity_coverage_ratio={} hydraulic_diameter_m={} domain_length_m={} dx_m={} face_area_m2={} control_volume_volume_m3={} nominal_mass_flow_rate_kg_per_s={} courant_number={} active_dimension_count={} element_geometry_node_count={} element_geometry_edge_count={} element_geometry_coverage_ratio={} element_topology_sample_element_count={} element_topology_sample_edge_count={} pressure_drop_pa={} mass_balance_residual={}",
            topology.basis.as_str(),
            topology.geometry_source.as_str(),
            topology.control_volume_count,
            topology.control_volume_face_count,
            topology.control_volume_internal_face_count,
            topology.control_volume_boundary_face_count,
            topology.control_volume_connectivity_coverage_ratio,
            topology.hydraulic_diameter_m,
            topology.domain_length_m,
            topology.dx_m,
            face_area_m2,
            control_volume_volume_m3,
            nominal_mass_flow_rate_kg_per_s,
            courant_number,
            topology.active_dimension_count,
            topology.element_geometry_node_count,
            topology.element_geometry_edge_count,
            topology.element_geometry_coverage_ratio,
            topology.element_topology_sample_element_count,
            topology.element_topology_sample_edge_count,
            pressure_drop_pa,
            mass_balance_residual,
        ),
    }
}

fn pressure_drop_from_nodal_pressure(pressure: &[f64]) -> f64 {
    match (pressure.first(), pressure.last()) {
        (Some(first), Some(last)) => first - last,
        _ => 0.0,
    }
}

fn cell_centered_vector_from_nodal(nodal: &[f64], node_count: usize) -> Vec<f64> {
    let cell_count = node_count.saturating_sub(1).max(1);
    let mut cell_values = Vec::with_capacity(cell_count * 3);
    for cell in 0..cell_count {
        let left = cell.min(node_count.saturating_sub(1));
        let right = (cell + 1).min(node_count.saturating_sub(1));
        for component in 0..3 {
            let left_value = nodal.get(left * 3 + component).copied().unwrap_or(0.0);
            let right_value = nodal
                .get(right * 3 + component)
                .copied()
                .unwrap_or(left_value);
            cell_values.push(0.5 * (left_value + right_value));
        }
    }
    cell_values
}

fn cell_centered_scalar_from_nodal(nodal: &[f64]) -> Vec<f64> {
    let node_count = nodal.len();
    let cell_count = node_count.saturating_sub(1).max(1);
    let mut cell_values = Vec::with_capacity(cell_count);
    for cell in 0..cell_count {
        let left = cell.min(node_count.saturating_sub(1));
        let right = (cell + 1).min(node_count.saturating_sub(1));
        let left_value = nodal.get(left).copied().unwrap_or(0.0);
        let right_value = nodal.get(right).copied().unwrap_or(left_value);
        cell_values.push(0.5 * (left_value + right_value));
    }
    cell_values
}

fn resample_scalar_profile(values: &[f64], target_count: usize) -> Vec<f64> {
    let target_count = target_count.max(1);
    if values.is_empty() {
        return vec![0.0; target_count];
    }
    if values.len() == target_count {
        return values.to_vec();
    }
    if target_count == 1 {
        return vec![values[0]];
    }
    let source_max = values.len().saturating_sub(1) as f64;
    let target_max = target_count.saturating_sub(1) as f64;
    (0..target_count)
        .map(|target_index| {
            let source_position = target_index as f64 * source_max / target_max.max(1.0);
            let left = source_position.floor() as usize;
            let right = source_position.ceil() as usize;
            if left == right {
                values.get(left).copied().unwrap_or(0.0)
            } else {
                let t = source_position - left as f64;
                let left_value = values.get(left).copied().unwrap_or(0.0);
                let right_value = values.get(right).copied().unwrap_or(left_value);
                left_value * (1.0 - t) + right_value * t
            }
        })
        .collect()
}

fn fluid_interface_face_count(topology: &CfdDomainTopology) -> usize {
    if topology.control_volume_connectivity_coverage_ratio > 0.0
        && topology.control_volume_boundary_face_count > 0
    {
        topology.control_volume_boundary_face_count
    } else {
        topology.control_volume_count
    }
    .max(1)
}

fn coupled_interface_graph_edge_target(
    topology: &CfdDomainTopology,
    interface_face_count: usize,
) -> usize {
    if interface_face_count < 2 {
        return 0;
    }
    let line_edge_count = interface_face_count - 1;
    let complete_graph_edge_count = interface_face_count * (interface_face_count - 1) / 2;
    if topology.control_volume_connectivity_coverage_ratio > 0.0 {
        topology
            .control_volume_internal_face_count
            .max(line_edge_count)
            .min(complete_graph_edge_count)
    } else {
        line_edge_count
    }
}

fn coupled_interface_graph_edges_for_topology(
    topology: &CfdDomainTopology,
    interface_face_count: usize,
) -> Vec<(usize, usize)> {
    use std::collections::BTreeSet;

    let target = coupled_interface_graph_edge_target(topology, interface_face_count);
    if target == 0 {
        return Vec::new();
    }

    let mut seen = BTreeSet::<(usize, usize)>::new();
    let mut edges = Vec::with_capacity(target);

    let full_edge_count = topology
        .element_topology_edge_nodes
        .len()
        .min(interface_face_count);
    if full_edge_count > 0 {
        for element_edges in &topology.element_topology_element_edges {
            let local_edges = element_edges
                .iter()
                .map(|edge| *edge as usize)
                .filter(|edge| *edge < full_edge_count)
                .collect::<Vec<_>>();
            if local_edges.len() < 2 {
                continue;
            }
            let pair_count = if local_edges.len() == 2 {
                1
            } else {
                local_edges.len()
            };
            for offset in 0..pair_count {
                let next = if offset + 1 < local_edges.len() {
                    offset + 1
                } else {
                    0
                };
                let left = local_edges[offset].min(local_edges[next]);
                let right = local_edges[offset].max(local_edges[next]);
                if left != right && seen.insert((left, right)) {
                    edges.push((left, right));
                    if edges.len() == target {
                        return edges;
                    }
                }
            }
        }
    }

    let sample_edge_count = topology
        .element_topology_sample_edge_count
        .min(interface_face_count);
    if edges.is_empty() {
        for element_edges in topology
            .element_topology_sample_element_edges
            .iter()
            .take(topology.element_topology_sample_element_count.min(4))
        {
            let local_edges = element_edges
                .iter()
                .map(|edge| *edge as usize)
                .filter(|edge| *edge < sample_edge_count)
                .collect::<Vec<_>>();
            if local_edges.len() < 2 {
                continue;
            }
            let pair_count = if local_edges.len() == 2 {
                1
            } else {
                local_edges.len()
            };
            for offset in 0..pair_count {
                let next = if offset + 1 < local_edges.len() {
                    offset + 1
                } else {
                    0
                };
                let left = local_edges[offset].min(local_edges[next]);
                let right = local_edges[offset].max(local_edges[next]);
                if left != right && seen.insert((left, right)) {
                    edges.push((left, right));
                    if edges.len() == target {
                        return edges;
                    }
                }
            }
        }
    }

    for (left, right) in coupled_interface_graph_edges(interface_face_count, target) {
        let edge = (left.min(right), left.max(right));
        if seen.insert(edge) {
            edges.push(edge);
            if edges.len() == target {
                break;
            }
        }
    }
    edges
}

fn coupled_interface_connectivity_coverage_ratio(
    topology: &CfdDomainTopology,
    interface_face_count: usize,
    edge_count: usize,
) -> f64 {
    let target = coupled_interface_graph_edge_target(topology, interface_face_count);
    if target == 0 {
        return 1.0;
    }
    (edge_count as f64 / target as f64).clamp(0.0, 1.0)
}

fn coupled_interface_mesh_backed_connectivity_ratio(
    topology: &CfdDomainTopology,
    edge_count: usize,
) -> f64 {
    if topology.basis == CfdDomainTopologyBasis::PrepControlVolumeConnectivity
        && topology.control_volume_connectivity_coverage_ratio > 0.0
        && edge_count > 0
    {
        1.0
    } else {
        0.0
    }
}

fn solve_cfd_finite_volume_run(
    model: &AnalysisModel,
    domain: &runmat_analysis_core::CfdDomain,
    backend: ComputeBackend,
    options: &AnalysisCfdRunOptions,
    prep_context: Option<&AnalysisRunPrepContext>,
) -> FeaRunResult {
    let topology = CfdDomainTopology::from_model(model, prep_context);
    let node_count = topology.node_count;
    let step_count = options.step_count.max(1);
    let field_step = match domain.solve_family {
        runmat_analysis_core::CfdSolveFamily::SteadyState => 0,
        runmat_analysis_core::CfdSolveFamily::Transient => step_count.saturating_sub(1),
    };
    let boundary_summary = CfdBoundarySummary::from_model(model, domain, node_count);
    let solution = solve_cfd_velocity_pressure(
        domain,
        &boundary_summary,
        &topology,
        field_step,
        step_count,
        options.max_linear_iters,
        options.tolerance,
    );
    let max_momentum_residual = solution
        .residual_momentum
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let max_continuity_residual = solution
        .residual_continuity
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let known_answer_metrics = cfd_known_answer_metrics(&solution);
    let fields = build_cfd_run_fields(domain, &solution);
    let residual_severity = if max_momentum_residual <= options.residual_warn_threshold
        && max_continuity_residual <= options.residual_warn_threshold
    {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };
    FeaRunResult {
        backend,
        solver_backend: "cpu_reference".to_string(),
        solver_device_apply_k_ratio: 0.0,
        solver_method: "cfd_velocity_pressure_finite_volume".to_string(),
        preconditioner: "finite_volume_pressure_balance".to_string(),
        solver_host_sync_count: 0,
        diagnostics: vec![
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_CFD_RESIDUAL".to_string(),
                severity: residual_severity,
                message: format!(
                    "max_momentum_residual={} max_continuity_residual={} residual_warn_threshold={} cfd_node_count={} cfd_step_count={}",
                    max_momentum_residual,
                    max_continuity_residual,
                    options.residual_warn_threshold,
                    node_count,
                    step_count,
                ),
            },
            cfd_assembly_diagnostic(
                &solution.topology,
                domain,
                options.time_step_s,
                solution.pressure_drop_pa,
                solution.mass_balance_residual,
                options.residual_warn_threshold,
            ),
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_CFD_BOUNDARY_CONDITIONS".to_string(),
                severity: if solution.boundary_coverage_ratio >= 1.0
                    && solution.wall_boundary_coverage_ratio >= 1.0
                    && solution.inlet_velocity_realization_ratio.is_finite()
                {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "boundary_source={} authored_boundary_count={} inlet_boundary_count={} outlet_boundary_count={} wall_boundary_count={} no_slip_wall_boundary_count={} slip_wall_boundary_count={} symmetry_boundary_count={} boundary_coverage_ratio={} wall_boundary_coverage_ratio={} nominal_inlet_velocity_m_per_s={} outlet_pressure_pa={} inlet_velocity_realization_ratio={}",
                    if solution.authored_boundary_count > 0 {
                        "authored"
                    } else {
                        "implicit_channel"
                    },
                    solution.authored_boundary_count,
                    solution.inlet_boundary_count,
                    solution.outlet_boundary_count,
                    solution.wall_boundary_count,
                    solution.no_slip_wall_boundary_count,
                    solution.slip_wall_boundary_count,
                    solution.symmetry_boundary_count,
                    solution.boundary_coverage_ratio,
                    solution.wall_boundary_coverage_ratio,
                    solution.nominal_inlet_velocity_m_per_s,
                    solution.outlet_pressure_pa,
                    solution.inlet_velocity_realization_ratio,
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_CFD_PRESSURE_CORRECTION".to_string(),
                severity: if solution.pressure_correction_residual_ratio <= options.tolerance
                    && solution.velocity_correction_residual_ratio <= options.tolerance
                {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
                } else {
                    runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                },
                message: format!(
                    "iteration_count={} max_linear_iters={} tolerance={} pressure_correction_residual_ratio={} velocity_correction_residual_ratio={}",
                    solution.pressure_correction_iteration_count,
                    options.max_linear_iters,
                    options.tolerance,
                    solution.pressure_correction_residual_ratio,
                    solution.velocity_correction_residual_ratio,
                ),
            },
            runmat_analysis_fea::diagnostics::FeaDiagnostic {
                code: "FEA_CFD_TRANSIENT_EVOLUTION".to_string(),
                severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
                message: format!(
                    "solve_family={} step_count={} time_step_s={} transient_profile_point_count={} transient_scale_min={} transient_scale_max={} transient_scale_variation={}",
                    match domain.solve_family {
                        runmat_analysis_core::CfdSolveFamily::SteadyState => "steady_state",
                        runmat_analysis_core::CfdSolveFamily::Transient => "transient",
                    },
                    step_count,
                    options.time_step_s,
                    domain.time_profile.len(),
                    solution.transient_scale_min,
                    solution.transient_scale_max,
                    solution.transient_scale_variation,
                ),
            },
            cfd_known_answer_diagnostic(&known_answer_metrics, &topology),
        ],
        fields,
    }
}

fn field_scalar_magnitudes(field: &AnalysisField, fallback_len: usize) -> Vec<f64> {
    let Some(values) = field.as_host_f64() else {
        return vec![0.0; fallback_len.max(1)];
    };
    match field.shape.as_slice() {
        [count, components] if *components > 1 => {
            let mut magnitudes = Vec::with_capacity(*count);
            for index in 0..*count {
                let start = index * *components;
                let magnitude = values
                    .get(start..start + *components)
                    .unwrap_or(&[])
                    .iter()
                    .map(|value| value * value)
                    .sum::<f64>()
                    .sqrt();
                magnitudes.push(magnitude);
            }
            magnitudes
        }
        _ => values.to_vec(),
    }
}

fn build_cht_run_fields(
    domain: &runmat_analysis_core::CfdDomain,
    topology: &CfdDomainTopology,
    thermal_run: &runmat_analysis_fea::FeaThermalRunResult,
    authored_interface_conductance_w_per_m2k: Option<f64>,
    max_linear_iters: usize,
    tolerance: f64,
) -> (Vec<AnalysisField>, ChtInterfaceClosure) {
    let node_count = topology.node_count;
    let (fluid_velocity, fluid_pressure) = recover_cfd_velocity_pressure(domain, topology, 0);
    let mean_axial_velocity = mean_cfd_axial_velocity(&fluid_velocity);
    let mut fields = vec![
        AnalysisField::host_f64(
            FEA_FIELD_CHT_FLUID_VELOCITY,
            vec![node_count, 3],
            fluid_velocity,
        ),
        AnalysisField::host_f64(
            FEA_FIELD_CHT_FLUID_PRESSURE,
            vec![node_count],
            fluid_pressure,
        ),
    ];
    let mut closure = ChtInterfaceClosure::default();

    for (step_index, temperature) in thermal_run.temperature_snapshots.iter().enumerate() {
        let fallback_len = temperature.element_count().max(1);
        let base_temperature = temperature
            .as_host_f64()
            .map(|values| values.to_vec())
            .unwrap_or_else(|| vec![thermal_run.reference_temperature_k; fallback_len]);
        let mut heat_flux = thermal_run
            .heat_flux_snapshots
            .get(step_index)
            .map(|field| field_scalar_magnitudes(field, fallback_len))
            .unwrap_or_else(|| vec![0.0; fallback_len]);
        if heat_flux.is_empty() {
            heat_flux.push(0.0);
        }
        let target_interface_count = fluid_interface_face_count(topology);
        if heat_flux.len() != target_interface_count {
            heat_flux = resample_scalar_profile(&heat_flux, target_interface_count);
        }
        let interface_count = heat_flux.len();
        let max_heat_flux = heat_flux
            .iter()
            .copied()
            .map(f64::abs)
            .fold(0.0_f64, f64::max);
        let target_jump_k = 0.01_f64;
        let interface_conductance_w_per_m2k = authored_interface_conductance_w_per_m2k
            .unwrap_or_else(|| (max_heat_flux / target_jump_k).max(25.0))
            .max(1.0e-12);
        let advection_shift_k = cht_advection_shift_k(
            domain,
            mean_axial_velocity,
            &base_temperature,
            thermal_run.reference_temperature_k,
        );

        let interface_solution = solve_cht_conjugate_interface(
            &base_temperature,
            &heat_flux,
            topology,
            interface_conductance_w_per_m2k,
            advection_shift_k,
            thermal_run.reference_temperature_k,
            max_linear_iters,
            tolerance,
        );
        let fluid_temperature = interface_solution.fluid_temperature;
        let solid_temperature = interface_solution.solid_temperature;
        let temperature_jump = interface_solution.temperature_jump;
        let coupled_heat_flux = interface_solution.coupled_heat_flux;
        fields.push(AnalysisField::host_f64(
            fea_cht_fluid_temperature_field_id(step_index),
            vec![base_temperature.len().max(1)],
            fluid_temperature,
        ));
        fields.push(AnalysisField::host_f64(
            fea_cht_solid_temperature_field_id(step_index),
            vec![base_temperature.len().max(1)],
            solid_temperature,
        ));

        closure.interface_face_count = closure.interface_face_count.max(interface_count);
        closure.max_temperature_jump_k = closure.max_temperature_jump_k.max(
            temperature_jump
                .iter()
                .copied()
                .map(f64::abs)
                .fold(0.0_f64, f64::max),
        );
        closure.max_advection_temperature_shift_k = closure
            .max_advection_temperature_shift_k
            .max(advection_shift_k.abs());
        closure.interface_conductance_w_per_m2k = closure
            .interface_conductance_w_per_m2k
            .max(interface_conductance_w_per_m2k);
        closure.max_flux_temperature_law_residual_ratio = closure
            .max_flux_temperature_law_residual_ratio
            .max(interface_solution.flux_temperature_law_residual_ratio);
        closure.max_heat_flux_realization_residual_ratio = closure
            .max_heat_flux_realization_residual_ratio
            .max(interface_solution.heat_flux_realization_residual_ratio);
        closure.max_coupled_interface_iteration_count = closure
            .max_coupled_interface_iteration_count
            .max(interface_solution.iteration_count);
        closure.max_coupled_interface_residual_ratio = closure
            .max_coupled_interface_residual_ratio
            .max(interface_solution.coupled_interface_residual_ratio);
        closure.thermal_network_edge_count = closure
            .thermal_network_edge_count
            .max(interface_solution.thermal_network_edge_count);
        closure.thermal_network_node_count = closure
            .thermal_network_node_count
            .max(interface_solution.thermal_network_node_count);
        closure.interface_connectivity_coverage_ratio = closure
            .interface_connectivity_coverage_ratio
            .max(interface_solution.interface_connectivity_coverage_ratio);
        closure.mesh_backed_interface_connectivity_ratio = closure
            .mesh_backed_interface_connectivity_ratio
            .max(interface_solution.mesh_backed_interface_connectivity_ratio);
        closure.full_topology_edge_count = closure
            .full_topology_edge_count
            .max(interface_solution.full_topology_edge_count);
        closure.full_topology_element_count = closure
            .full_topology_element_count
            .max(interface_solution.full_topology_element_count);
        closure.max_thermal_network_residual_ratio = closure
            .max_thermal_network_residual_ratio
            .max(interface_solution.thermal_network_residual_ratio);
        let fluid_heat = coupled_heat_flux.iter().sum::<f64>();
        let solid_heat = -fluid_heat;
        let heat_balance_ratio =
            (fluid_heat + solid_heat).abs() / (fluid_heat.abs() + solid_heat.abs() + 1.0e-12);
        closure.heat_flux_balance_ratio = closure.heat_flux_balance_ratio.max(heat_balance_ratio);
        let thermal_scale_k = base_temperature
            .iter()
            .map(|value| (value - thermal_run.reference_temperature_k).abs())
            .fold(0.0_f64, f64::max)
            .max(1.0);
        let normalized_temperature_jump =
            closure.max_temperature_jump_k / thermal_scale_k.max(1.0e-12);
        closure.max_thermal_transport_residual_ratio =
            closure.max_thermal_transport_residual_ratio.max(
                interface_solution
                    .flux_temperature_law_residual_ratio
                    .max(heat_balance_ratio)
                    .max(interface_solution.heat_flux_realization_residual_ratio)
                    .max(interface_solution.coupled_interface_residual_ratio)
                    .max(interface_solution.thermal_network_residual_ratio),
            );
        closure.interface_temperature_continuity_ratio = closure
            .interface_temperature_continuity_ratio
            .max((1.0 - normalized_temperature_jump).clamp(0.0, 1.0));
        let mean_heat_flux = if coupled_heat_flux.is_empty() {
            0.0
        } else {
            coupled_heat_flux.iter().sum::<f64>() / coupled_heat_flux.len() as f64
        };
        closure.mean_interface_heat_flux_w_per_m2 = closure
            .mean_interface_heat_flux_w_per_m2
            .max(mean_heat_flux.abs());
        fields.push(AnalysisField::host_f64(
            fea_cht_interface_heat_flux_field_id(step_index),
            vec![interface_count],
            coupled_heat_flux,
        ));

        fields.push(AnalysisField::host_f64(
            fea_cht_interface_temperature_jump_field_id(step_index),
            vec![interface_count],
            temperature_jump,
        ));
        let energy_residual = heat_balance_ratio
            .max(interface_solution.flux_temperature_law_residual_ratio)
            .max(interface_solution.heat_flux_realization_residual_ratio)
            .max(interface_solution.thermal_network_residual_ratio);
        closure.max_energy_residual = closure.max_energy_residual.max(energy_residual);
        fields.push(AnalysisField::host_f64(
            fea_cht_energy_residual_field_id(step_index),
            vec![1],
            vec![energy_residual],
        ));
    }

    (fields, closure)
}

#[derive(Debug, Clone)]
struct ChtConjugateInterfaceSolution {
    fluid_temperature: Vec<f64>,
    solid_temperature: Vec<f64>,
    temperature_jump: Vec<f64>,
    coupled_heat_flux: Vec<f64>,
    iteration_count: usize,
    coupled_interface_residual_ratio: f64,
    flux_temperature_law_residual_ratio: f64,
    heat_flux_realization_residual_ratio: f64,
    thermal_network_edge_count: usize,
    thermal_network_node_count: usize,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    full_topology_edge_count: usize,
    full_topology_element_count: usize,
    thermal_network_residual_ratio: f64,
}

fn solve_cht_conjugate_interface(
    base_temperature: &[f64],
    heat_flux: &[f64],
    topology: &CfdDomainTopology,
    interface_conductance_w_per_m2k: f64,
    advection_shift_k: f64,
    reference_temperature_k: f64,
    _max_linear_iters: usize,
    _tolerance: f64,
) -> ChtConjugateInterfaceSolution {
    let temperature_count = base_temperature.len().max(1);
    let interface_count = heat_flux.len().max(1);
    let interface_denom = interface_count.saturating_sub(1).max(1) as f64;
    let conductance = interface_conductance_w_per_m2k.max(1.0e-12);
    let axial_conductance = (0.05 * conductance).max(1.0e-12);
    let anchor_conductance = conductance;
    let base_interface_temperature = resample_scalar_profile(base_temperature, interface_count);
    let thermal_network_edges =
        coupled_interface_graph_edges_for_topology(topology, interface_count);
    let interface_connectivity_coverage_ratio = coupled_interface_connectivity_coverage_ratio(
        topology,
        interface_count,
        thermal_network_edges.len(),
    );
    let mesh_backed_interface_connectivity_ratio =
        coupled_interface_mesh_backed_connectivity_ratio(topology, thermal_network_edges.len());
    let mut operator = vec![vec![0.0; interface_count]; interface_count];
    for (row, diagonal) in operator.iter_mut().enumerate() {
        diagonal[row] = anchor_conductance;
    }
    for (left, right) in &thermal_network_edges {
        operator[*left][*left] += axial_conductance;
        operator[*right][*right] += axial_conductance;
        operator[*left][*right] -= axial_conductance;
        operator[*right][*left] -= axial_conductance;
    }
    let mut center_rhs = Vec::with_capacity(interface_count);

    for index in 0..interface_count {
        let base = base_interface_temperature
            .get(index)
            .copied()
            .unwrap_or(reference_temperature_k);
        let xi = index as f64 / interface_denom;
        let center_temperature = base + advection_shift_k * xi;
        center_rhs.push(anchor_conductance * center_temperature);
    }

    let mut matrix = operator.clone();
    let mut rhs = center_rhs.clone();
    let interface_center_temperature = solve_dense_real_system(&mut matrix, &mut rhs);
    let center_reaction = apply_dense_real_operator(&operator, &interface_center_temperature);
    let temperature_scale = interface_center_temperature
        .iter()
        .map(|value| (*value - reference_temperature_k).abs())
        .fold(0.0_f64, f64::max)
        .max(1.0);
    let thermal_network_residual_ratio = center_reaction
        .iter()
        .zip(center_rhs.iter())
        .map(|(reaction, rhs)| (reaction - rhs).abs())
        .fold(0.0_f64, f64::max)
        / (anchor_conductance * temperature_scale).max(1.0e-12);
    let mut interface_fluid_temperature = Vec::with_capacity(interface_count);
    let mut interface_solid_temperature = Vec::with_capacity(interface_count);
    for (center, flux) in interface_center_temperature
        .iter()
        .zip(heat_flux.iter().chain(std::iter::repeat(&0.0)))
    {
        let jump = *flux / conductance;
        interface_fluid_temperature.push(*center + 0.5 * jump);
        interface_solid_temperature.push(*center - 0.5 * jump);
    }
    let fluid_temperature =
        resample_scalar_profile(&interface_fluid_temperature, temperature_count);
    let solid_temperature =
        resample_scalar_profile(&interface_solid_temperature, temperature_count);
    let iteration_count = usize::from(temperature_count > 0);
    let coupled_interface_residual_ratio = thermal_network_residual_ratio;

    let mut temperature_jump = Vec::with_capacity(interface_count);
    let mut coupled_heat_flux = Vec::with_capacity(interface_count);
    let mut max_flux_temperature_law_residual = 0.0_f64;
    let mut max_heat_flux_realization_residual = 0.0_f64;
    let heat_flux_scale = heat_flux
        .iter()
        .copied()
        .map(f64::abs)
        .fold(0.0_f64, f64::max)
        .max(1.0e-12);
    for index in 0..interface_count {
        let jump = interface_fluid_temperature
            .get(index)
            .copied()
            .unwrap_or(reference_temperature_k)
            - interface_solid_temperature
                .get(index)
                .copied()
                .unwrap_or(reference_temperature_k);
        let coupled_flux = conductance * jump;
        let input_flux = heat_flux.get(index).copied().unwrap_or(0.0);
        max_flux_temperature_law_residual = max_flux_temperature_law_residual
            .max((conductance * jump - coupled_flux).abs() / heat_flux_scale);
        max_heat_flux_realization_residual = max_heat_flux_realization_residual
            .max((coupled_flux - input_flux).abs() / heat_flux_scale);
        temperature_jump.push(jump);
        coupled_heat_flux.push(coupled_flux);
    }

    ChtConjugateInterfaceSolution {
        fluid_temperature,
        solid_temperature,
        temperature_jump,
        coupled_heat_flux,
        iteration_count,
        coupled_interface_residual_ratio,
        flux_temperature_law_residual_ratio: max_flux_temperature_law_residual,
        heat_flux_realization_residual_ratio: max_heat_flux_realization_residual,
        thermal_network_edge_count: thermal_network_edges.len(),
        thermal_network_node_count: interface_count,
        interface_connectivity_coverage_ratio,
        mesh_backed_interface_connectivity_ratio,
        full_topology_edge_count: topology.element_topology_edge_nodes.len(),
        full_topology_element_count: topology.element_topology_element_edges.len(),
        thermal_network_residual_ratio,
    }
}

fn mean_cfd_axial_velocity(velocity: &[f64]) -> f64 {
    let node_count = velocity.len() / 3;
    if node_count == 0 {
        return 0.0;
    }
    (0..node_count)
        .map(|node| velocity.get(node * 3).copied().unwrap_or(0.0))
        .sum::<f64>()
        / node_count as f64
}

fn cht_advection_shift_k(
    domain: &runmat_analysis_core::CfdDomain,
    mean_axial_velocity_m_per_s: f64,
    temperature: &[f64],
    reference_temperature_k: f64,
) -> f64 {
    let thermal_span_k = temperature
        .iter()
        .map(|value| (value - reference_temperature_k).abs())
        .fold(0.0_f64, f64::max)
        .max(1.0);
    let reynolds_ratio = (cfd_reynolds_number_for_velocity(domain, mean_axial_velocity_m_per_s)
        / 1.0e5)
        .clamp(0.0, 5.0);
    thermal_span_k * reynolds_ratio * domain.turbulence_intensity.clamp(0.0, 1.0) * 2.0e-3
}

fn cht_interface_conductance_w_per_m2k(model: &AnalysisModel) -> Option<f64> {
    model
        .interfaces
        .iter()
        .find_map(|interface| match &interface.kind {
            AnalysisInterfaceKind::ConjugateHeatTransfer(interface)
                if interface.thermal_conductance_w_per_m2k.is_finite()
                    && interface.thermal_conductance_w_per_m2k > 0.0 =>
            {
                Some(interface.thermal_conductance_w_per_m2k)
            }
            AnalysisInterfaceKind::ConjugateHeatTransfer(_)
            | AnalysisInterfaceKind::FluidStructure(_)
            | AnalysisInterfaceKind::Contact(_) => None,
        })
}

#[derive(Debug, Clone, Copy, Default)]
struct ChtInterfaceClosure {
    interface_face_count: usize,
    max_temperature_jump_k: f64,
    max_energy_residual: f64,
    heat_flux_balance_ratio: f64,
    mean_interface_heat_flux_w_per_m2: f64,
    max_thermal_transport_residual_ratio: f64,
    interface_temperature_continuity_ratio: f64,
    max_advection_temperature_shift_k: f64,
    interface_conductance_w_per_m2k: f64,
    max_flux_temperature_law_residual_ratio: f64,
    max_heat_flux_realization_residual_ratio: f64,
    max_coupled_interface_iteration_count: usize,
    max_coupled_interface_residual_ratio: f64,
    thermal_network_edge_count: usize,
    thermal_network_node_count: usize,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    full_topology_edge_count: usize,
    full_topology_element_count: usize,
    max_thermal_network_residual_ratio: f64,
}

#[derive(Debug, Clone, Copy)]
struct ChtKnownAnswerMetrics {
    heated_channel_energy_residual_ratio: f64,
    conjugate_slab_flux_law_residual_ratio: f64,
    interface_temperature_continuity_ratio: f64,
    advection_shift_coverage_ratio: f64,
    coupled_interface_residual_ratio: f64,
    heat_flux_realization_residual_ratio: f64,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    thermal_network_residual_ratio: f64,
    known_answer_coverage_ratio: f64,
}

fn cht_known_answer_metrics(
    domain: &runmat_analysis_core::CfdDomain,
    closure: &ChtInterfaceClosure,
) -> ChtKnownAnswerMetrics {
    let reynolds = cfd_reynolds_number(domain);
    let advection_shift_coverage_ratio = if reynolds.is_finite()
        && reynolds > 0.0
        && closure.max_advection_temperature_shift_k.is_finite()
        && closure.max_advection_temperature_shift_k >= 0.0
    {
        1.0
    } else {
        0.0
    };
    let known_answer_coverage_ratio = if closure.interface_face_count > 0
        && closure.interface_conductance_w_per_m2k.is_finite()
        && closure.interface_conductance_w_per_m2k > 0.0
        && closure.max_energy_residual.is_finite()
        && closure.max_flux_temperature_law_residual_ratio.is_finite()
        && closure.max_heat_flux_realization_residual_ratio.is_finite()
        && closure.interface_temperature_continuity_ratio.is_finite()
        && closure.max_coupled_interface_residual_ratio.is_finite()
        && closure.thermal_network_node_count > 0
        && closure.interface_connectivity_coverage_ratio.is_finite()
        && closure.interface_connectivity_coverage_ratio >= 1.0
        && closure.mesh_backed_interface_connectivity_ratio.is_finite()
        && closure.max_thermal_network_residual_ratio.is_finite()
        && advection_shift_coverage_ratio >= 1.0
    {
        1.0
    } else {
        0.0
    };

    ChtKnownAnswerMetrics {
        heated_channel_energy_residual_ratio: closure.max_energy_residual,
        conjugate_slab_flux_law_residual_ratio: closure.max_flux_temperature_law_residual_ratio,
        interface_temperature_continuity_ratio: closure.interface_temperature_continuity_ratio,
        advection_shift_coverage_ratio,
        coupled_interface_residual_ratio: closure.max_coupled_interface_residual_ratio,
        heat_flux_realization_residual_ratio: closure.max_heat_flux_realization_residual_ratio,
        interface_connectivity_coverage_ratio: closure.interface_connectivity_coverage_ratio,
        mesh_backed_interface_connectivity_ratio: closure.mesh_backed_interface_connectivity_ratio,
        thermal_network_residual_ratio: closure.max_thermal_network_residual_ratio,
        known_answer_coverage_ratio,
    }
}

fn cht_known_answer_diagnostic(
    metrics: &ChtKnownAnswerMetrics,
    residual_threshold: f64,
) -> runmat_analysis_fea::diagnostics::FeaDiagnostic {
    let severity = if metrics.heated_channel_energy_residual_ratio <= residual_threshold
        && metrics.conjugate_slab_flux_law_residual_ratio <= residual_threshold
        && metrics.interface_temperature_continuity_ratio >= 0.999
        && metrics.advection_shift_coverage_ratio >= 1.0
        && metrics.coupled_interface_residual_ratio <= residual_threshold
        && metrics.heat_flux_realization_residual_ratio <= residual_threshold
        && metrics.interface_connectivity_coverage_ratio >= 1.0
        && metrics.thermal_network_residual_ratio <= residual_threshold
        && metrics.known_answer_coverage_ratio >= 1.0
    {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };

    runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CHT_KNOWN_ANSWER".to_string(),
        severity,
        message: format!(
            "basis=heated_channel_conjugate_slab heated_channel_energy_residual_ratio={} conjugate_slab_flux_law_residual_ratio={} interface_temperature_continuity_ratio={} advection_shift_coverage_ratio={} coupled_interface_residual_ratio={} heat_flux_realization_residual_ratio={} interface_connectivity_coverage_ratio={} mesh_backed_interface_connectivity_ratio={} thermal_network_residual_ratio={} known_answer_coverage_ratio={}",
            metrics.heated_channel_energy_residual_ratio,
            metrics.conjugate_slab_flux_law_residual_ratio,
            metrics.interface_temperature_continuity_ratio,
            metrics.advection_shift_coverage_ratio,
            metrics.coupled_interface_residual_ratio,
            metrics.heat_flux_realization_residual_ratio,
            metrics.interface_connectivity_coverage_ratio,
            metrics.mesh_backed_interface_connectivity_ratio,
            metrics.thermal_network_residual_ratio,
            metrics.known_answer_coverage_ratio,
        ),
    }
}

fn build_fsi_run_fields(
    domain: &runmat_analysis_core::CfdDomain,
    topology: &CfdDomainTopology,
    step_count: usize,
    structural_compliance_per_pa: f64,
    max_linear_iters: usize,
    tolerance: f64,
    residual_momentum: &[f64],
    residual_continuity: &[f64],
) -> (Vec<AnalysisField>, FsiInterfaceClosure) {
    let mut fields = Vec::new();
    let mut closure = FsiInterfaceClosure::default();
    let step_count = step_count.max(1);
    let node_count = topology.node_count;

    for step_index in 0..step_count {
        let (fluid_velocity, fluid_pressure) =
            recover_cfd_velocity_pressure(domain, topology, step_index);
        let interface_step = solve_fsi_partitioned_interface(
            &fluid_pressure,
            topology,
            structural_compliance_per_pa,
            max_linear_iters,
            tolerance,
        );
        let displacement = interface_step.structural_displacement;
        closure.interface_node_count = closure.interface_node_count.max(fluid_pressure.len());
        closure.interface_face_count = closure
            .interface_face_count
            .max(interface_step.interface_pressure.len());
        closure.max_coupling_iteration_count = closure
            .max_coupling_iteration_count
            .max(interface_step.iteration_count);
        closure.max_pressure_feedback_residual_ratio = closure
            .max_pressure_feedback_residual_ratio
            .max(interface_step.pressure_feedback_residual_ratio);
        closure.max_two_way_interface_residual_ratio = closure
            .max_two_way_interface_residual_ratio
            .max(interface_step.two_way_interface_residual_ratio);
        closure.max_structural_traction_update_residual_ratio = closure
            .max_structural_traction_update_residual_ratio
            .max(interface_step.structural_traction_update_residual_ratio);
        closure.max_pressure_displacement_law_residual_ratio = closure
            .max_pressure_displacement_law_residual_ratio
            .max(interface_step.pressure_displacement_law_residual_ratio);
        closure.max_structural_solve_residual_ratio = closure
            .max_structural_solve_residual_ratio
            .max(interface_step.structural_solve_residual_ratio);
        closure.max_interface_work_energy_residual_ratio = closure
            .max_interface_work_energy_residual_ratio
            .max(interface_step.interface_work_energy_residual_ratio);
        closure.max_interface_work_j_per_m2 = closure
            .max_interface_work_j_per_m2
            .max(interface_step.interface_work_j_per_m2.abs());
        closure.max_structural_strain_energy_j_per_m2 = closure
            .max_structural_strain_energy_j_per_m2
            .max(interface_step.structural_strain_energy_j_per_m2.abs());
        closure.structural_coupling_edge_count = closure
            .structural_coupling_edge_count
            .max(interface_step.structural_coupling_edge_count);
        closure.interface_connectivity_coverage_ratio = closure
            .interface_connectivity_coverage_ratio
            .max(interface_step.interface_connectivity_coverage_ratio);
        closure.mesh_backed_interface_connectivity_ratio = closure
            .mesh_backed_interface_connectivity_ratio
            .max(interface_step.mesh_backed_interface_connectivity_ratio);
        closure.full_topology_edge_count = closure
            .full_topology_edge_count
            .max(interface_step.full_topology_edge_count);
        closure.full_topology_element_count = closure
            .full_topology_element_count
            .max(interface_step.full_topology_element_count);
        closure.interface_stiffness_pa_per_m = closure
            .interface_stiffness_pa_per_m
            .max(interface_step.interface_stiffness_pa_per_m);
        let fluid_normal_force = interface_step.interface_pressure.iter().sum::<f64>();
        let structural_normal_force = -fluid_normal_force;
        let force_balance_ratio = (fluid_normal_force + structural_normal_force).abs()
            / (fluid_normal_force.abs() + structural_normal_force.abs() + 1.0e-12);
        closure.force_balance_ratio = closure.force_balance_ratio.max(force_balance_ratio);
        let mean_pressure = if interface_step.interface_pressure.is_empty() {
            0.0
        } else {
            interface_step.interface_pressure.iter().sum::<f64>()
                / interface_step.interface_pressure.len() as f64
        };
        closure.mean_interface_pressure_pa =
            closure.mean_interface_pressure_pa.max(mean_pressure.abs());
        closure.max_traction_magnitude_pa = closure.max_traction_magnitude_pa.max(
            interface_step
                .interface_pressure
                .iter()
                .map(|pressure| pressure.abs())
                .fold(0.0_f64, f64::max),
        );
        let interface_displacement = interface_step.interface_displacement.clone();
        let displacement_transfer_residual = displacement
            .iter()
            .zip(interface_displacement.iter())
            .map(|(structural, interface)| (structural - interface).abs())
            .fold(0.0_f64, f64::max);
        closure.max_displacement_transfer_residual_m = closure
            .max_displacement_transfer_residual_m
            .max(displacement_transfer_residual);
        closure.max_interface_displacement_m = closure.max_interface_displacement_m.max(
            interface_displacement
                .chunks_exact(3)
                .map(|components| {
                    (components[0] * components[0]
                        + components[1] * components[1]
                        + components[2] * components[2])
                        .sqrt()
                })
                .fold(0.0_f64, f64::max),
        );
        fields.push(AnalysisField::host_f64(
            fea_fsi_fluid_velocity_field_id(step_index),
            vec![node_count, 3],
            fluid_velocity,
        ));
        fields.push(AnalysisField::host_f64(
            fea_fsi_fluid_pressure_field_id(step_index),
            vec![node_count],
            fluid_pressure.clone(),
        ));

        fields.push(AnalysisField::host_f64(
            fea_fsi_structural_displacement_field_id(step_index),
            vec![node_count, 3],
            displacement.clone(),
        ));
        fields.push(AnalysisField::host_f64(
            fea_fsi_interface_displacement_field_id(step_index),
            vec![node_count, 3],
            interface_displacement,
        ));

        fields.push(AnalysisField::host_f64(
            fea_fsi_interface_pressure_field_id(step_index),
            vec![interface_step.interface_pressure.len().max(1)],
            interface_step.interface_pressure.clone(),
        ));
        let mut traction = Vec::with_capacity(interface_step.interface_pressure.len() * 3);
        for pressure in &interface_step.interface_pressure {
            traction.extend_from_slice(&[-*pressure, 0.0, 0.0]);
        }
        fields.push(AnalysisField::host_f64(
            fea_fsi_interface_traction_field_id(step_index),
            vec![interface_step.interface_pressure.len().max(1), 3],
            traction,
        ));

        let residual = residual_momentum
            .get(step_index)
            .copied()
            .unwrap_or(0.0)
            .max(residual_continuity.get(step_index).copied().unwrap_or(0.0))
            .max(interface_step.two_way_interface_residual_ratio);
        closure.max_interface_residual = closure.max_interface_residual.max(residual);
        fields.push(AnalysisField::host_f64(
            fea_fsi_interface_residual_field_id(step_index),
            vec![1],
            vec![residual],
        ));
        fields.push(AnalysisField::host_f64(
            fea_fsi_coupling_iteration_count_field_id(step_index),
            vec![1],
            vec![interface_step.iteration_count as f64],
        ));
    }

    (fields, closure)
}

#[derive(Debug, Clone)]
struct FsiPartitionedInterfaceStep {
    interface_pressure: Vec<f64>,
    structural_displacement: Vec<f64>,
    interface_displacement: Vec<f64>,
    iteration_count: usize,
    pressure_feedback_residual_ratio: f64,
    two_way_interface_residual_ratio: f64,
    structural_traction_update_residual_ratio: f64,
    pressure_displacement_law_residual_ratio: f64,
    structural_solve_residual_ratio: f64,
    interface_work_j_per_m2: f64,
    structural_strain_energy_j_per_m2: f64,
    interface_work_energy_residual_ratio: f64,
    structural_coupling_edge_count: usize,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    full_topology_edge_count: usize,
    full_topology_element_count: usize,
    interface_stiffness_pa_per_m: f64,
}

#[derive(Debug, Clone)]
struct FsiStructuralInterfaceResponse {
    displacement_x: Vec<f64>,
    reaction_pressure: Vec<f64>,
    residual_ratio: f64,
    coupling_edge_count: usize,
}

fn solve_fsi_partitioned_interface(
    fluid_pressure: &[f64],
    topology: &CfdDomainTopology,
    structural_compliance_per_pa: f64,
    max_linear_iters: usize,
    tolerance: f64,
) -> FsiPartitionedInterfaceStep {
    let max_linear_iters = max_linear_iters.max(1);
    let tolerance = tolerance.max(1.0e-12);
    let interface_face_count = fluid_interface_face_count(topology);
    let face_pressure = resample_scalar_profile(
        &cell_centered_scalar_from_nodal(fluid_pressure),
        interface_face_count,
    );
    let pressure_scale = face_pressure
        .iter()
        .map(|pressure| pressure.abs())
        .fold(0.0_f64, f64::max)
        .max(1.0);
    let compliance = structural_compliance_per_pa.max(1.0e-18);
    let pressure_relaxation = 0.65_f64;
    let displacement_relaxation = 0.65_f64;
    let traction_relaxation = 0.65_f64;
    let interface_stiffness_pa_per_m = 1.0 / compliance;
    let feedback_stiffness_pa_per_m = 0.25 * interface_stiffness_pa_per_m;
    let structural_coupling_edges =
        coupled_interface_graph_edges_for_topology(topology, interface_face_count);
    let interface_connectivity_coverage_ratio = coupled_interface_connectivity_coverage_ratio(
        topology,
        interface_face_count,
        structural_coupling_edges.len(),
    );
    let mesh_backed_interface_connectivity_ratio =
        coupled_interface_mesh_backed_connectivity_ratio(topology, structural_coupling_edges.len());
    let mut interface_pressure = vec![0.0; face_pressure.len()];
    let mut structural_traction = vec![0.0; face_pressure.len()];
    let mut structural_face_displacement = vec![0.0; face_pressure.len() * 3];
    let mut interface_face_displacement = vec![0.0; face_pressure.len() * 3];
    let mut iteration_count = 0_usize;
    let mut pressure_feedback_residual_ratio = if face_pressure.is_empty() { 0.0 } else { 1.0 };
    let mut displacement_transfer_residual_ratio = 0.0_f64;
    let mut structural_traction_update_residual_ratio =
        if face_pressure.is_empty() { 0.0 } else { 1.0 };
    let mut structural_solve_residual_ratio = if face_pressure.is_empty() { 0.0 } else { 1.0 };
    let mut structural_coupling_edge_count = 0_usize;

    for iteration in 1..=max_linear_iters {
        let target_pressure: Vec<f64> = face_pressure
            .iter()
            .enumerate()
            .map(|(face, pressure)| {
                let displacement = interface_face_displacement
                    .get(face * 3)
                    .copied()
                    .unwrap_or(0.0);
                *pressure - feedback_stiffness_pa_per_m * displacement
            })
            .collect();
        for (interface, target) in interface_pressure.iter_mut().zip(target_pressure.iter()) {
            *interface += pressure_relaxation * (*target - *interface);
        }
        let structural_response = solve_fsi_structural_interface_response(
            &interface_pressure,
            interface_stiffness_pa_per_m,
            &structural_coupling_edges,
        );
        structural_solve_residual_ratio = structural_response.residual_ratio;
        structural_coupling_edge_count =
            structural_coupling_edge_count.max(structural_response.coupling_edge_count);
        for (face, displacement_x) in structural_response.displacement_x.iter().enumerate() {
            structural_face_displacement[face * 3] = *displacement_x;
            structural_face_displacement[face * 3 + 1] = 0.0;
            structural_face_displacement[face * 3 + 2] = 0.0;
        }
        let target_structural_traction = structural_response.reaction_pressure;
        for (traction, target) in structural_traction
            .iter_mut()
            .zip(target_structural_traction.iter())
        {
            *traction += traction_relaxation * (*target - *traction);
        }
        for (interface, structural) in interface_face_displacement
            .iter_mut()
            .zip(structural_face_displacement.iter())
        {
            *interface += displacement_relaxation * (*structural - *interface);
        }
        let max_feedback_residual = target_pressure
            .iter()
            .zip(interface_pressure.iter())
            .map(|(target, interface)| (target - interface).abs())
            .fold(0.0_f64, f64::max);
        pressure_feedback_residual_ratio = max_feedback_residual / pressure_scale;
        let displacement_scale = structural_face_displacement
            .iter()
            .copied()
            .map(f64::abs)
            .fold(0.0_f64, f64::max)
            .max(1.0e-18);
        displacement_transfer_residual_ratio = structural_face_displacement
            .iter()
            .zip(interface_face_displacement.iter())
            .map(|(structural, interface)| (structural - interface).abs())
            .fold(0.0_f64, f64::max)
            / displacement_scale;
        structural_traction_update_residual_ratio = target_structural_traction
            .iter()
            .zip(structural_traction.iter())
            .map(|(target, traction)| (target - traction).abs())
            .fold(0.0_f64, f64::max)
            / pressure_scale;
        iteration_count = iteration;
        if pressure_feedback_residual_ratio <= tolerance
            && displacement_transfer_residual_ratio <= tolerance
            && structural_traction_update_residual_ratio <= tolerance
            && structural_solve_residual_ratio <= tolerance
        {
            break;
        }
    }
    let structural_response = solve_fsi_structural_interface_response(
        &interface_pressure,
        interface_stiffness_pa_per_m,
        &structural_coupling_edges,
    );
    structural_solve_residual_ratio = structural_solve_residual_ratio
        .max(structural_response.residual_ratio)
        .min(1.0);
    structural_coupling_edge_count =
        structural_coupling_edge_count.max(structural_response.coupling_edge_count);
    let pressure_displacement_law_residual_ratio = structural_response
        .reaction_pressure
        .iter()
        .zip(interface_pressure.iter())
        .map(|(reaction, pressure)| (reaction - pressure).abs() / pressure.abs().max(1.0))
        .fold(0.0_f64, f64::max);
    let interface_work_j_per_m2 = interface_pressure
        .iter()
        .zip(structural_response.displacement_x.iter())
        .map(|(pressure, displacement)| pressure * displacement)
        .sum::<f64>();
    let structural_strain_energy_j_per_m2 = 0.5
        * structural_response
            .reaction_pressure
            .iter()
            .zip(structural_response.displacement_x.iter())
            .map(|(reaction, displacement)| reaction * displacement)
            .sum::<f64>();
    let interface_work_energy_residual_ratio =
        (interface_work_j_per_m2 - 2.0 * structural_strain_energy_j_per_m2).abs()
            / (interface_work_j_per_m2.abs()
                + (2.0 * structural_strain_energy_j_per_m2).abs()
                + 1.0e-12);
    let interface_face_displacement_x = interface_face_displacement
        .chunks_exact(3)
        .map(|components| components[0])
        .collect::<Vec<_>>();
    let structural_displacement =
        vector_field_from_x_profile(&structural_response.displacement_x, fluid_pressure.len());
    let interface_displacement =
        vector_field_from_x_profile(&interface_face_displacement_x, fluid_pressure.len());

    FsiPartitionedInterfaceStep {
        interface_pressure,
        structural_displacement,
        interface_displacement,
        iteration_count,
        pressure_feedback_residual_ratio,
        two_way_interface_residual_ratio: pressure_feedback_residual_ratio
            .max(displacement_transfer_residual_ratio)
            .max(structural_traction_update_residual_ratio)
            .max(structural_solve_residual_ratio),
        structural_traction_update_residual_ratio,
        pressure_displacement_law_residual_ratio,
        structural_solve_residual_ratio,
        interface_work_j_per_m2,
        structural_strain_energy_j_per_m2,
        interface_work_energy_residual_ratio,
        structural_coupling_edge_count,
        interface_connectivity_coverage_ratio,
        mesh_backed_interface_connectivity_ratio,
        full_topology_edge_count: topology.element_topology_edge_nodes.len(),
        full_topology_element_count: topology.element_topology_element_edges.len(),
        interface_stiffness_pa_per_m,
    }
}

fn solve_fsi_structural_interface_response(
    pressure_load: &[f64],
    interface_stiffness_pa_per_m: f64,
    coupling_edges: &[(usize, usize)],
) -> FsiStructuralInterfaceResponse {
    let face_count = pressure_load.len();
    if face_count == 0 {
        return FsiStructuralInterfaceResponse {
            displacement_x: Vec::new(),
            reaction_pressure: Vec::new(),
            residual_ratio: 0.0,
            coupling_edge_count: 0,
        };
    }

    let diagonal_stiffness = interface_stiffness_pa_per_m.max(1.0e-9);
    let coupling_stiffness = 0.20 * diagonal_stiffness;
    let mut operator = vec![vec![0.0; face_count]; face_count];
    for (row, diagonal) in operator.iter_mut().enumerate() {
        diagonal[row] = diagonal_stiffness;
    }
    for (left, right) in coupling_edges {
        operator[*left][*left] += coupling_stiffness;
        operator[*right][*right] += coupling_stiffness;
        operator[*left][*right] -= coupling_stiffness;
        operator[*right][*left] -= coupling_stiffness;
    }

    let mut matrix = operator.clone();
    let mut rhs = pressure_load.to_vec();
    let displacement_x = solve_dense_real_system(&mut matrix, &mut rhs);
    let reaction_pressure = apply_dense_real_operator(&operator, &displacement_x);
    let pressure_scale = pressure_load
        .iter()
        .copied()
        .map(f64::abs)
        .fold(0.0_f64, f64::max)
        .max(1.0);
    let residual_ratio = reaction_pressure
        .iter()
        .zip(pressure_load.iter())
        .map(|(reaction, pressure)| (reaction - pressure).abs())
        .fold(0.0_f64, f64::max)
        / pressure_scale;

    FsiStructuralInterfaceResponse {
        displacement_x,
        reaction_pressure,
        residual_ratio,
        coupling_edge_count: coupling_edges.len(),
    }
}

fn coupled_interface_graph_edges(face_count: usize, edge_target: usize) -> Vec<(usize, usize)> {
    if face_count < 2 || edge_target == 0 {
        return Vec::new();
    }
    let complete_graph_edge_count = face_count * (face_count - 1) / 2;
    let edge_target = edge_target.min(complete_graph_edge_count);
    let mut edges = Vec::with_capacity(edge_target);
    for span in 1..face_count {
        for left in 0..face_count - span {
            edges.push((left, left + span));
            if edges.len() == edge_target {
                return edges;
            }
        }
    }
    edges
}

fn solve_dense_real_system(matrix: &mut [Vec<f64>], rhs: &mut [f64]) -> Vec<f64> {
    let n = rhs.len();
    for pivot in 0..n {
        let mut pivot_row = pivot;
        let mut pivot_abs = matrix[pivot][pivot].abs();
        for (candidate, row) in matrix.iter().enumerate().skip(pivot + 1) {
            let candidate_abs = row[pivot].abs();
            if candidate_abs > pivot_abs {
                pivot_row = candidate;
                pivot_abs = candidate_abs;
            }
        }
        if pivot_row != pivot {
            matrix.swap(pivot, pivot_row);
            rhs.swap(pivot, pivot_row);
        }
        if pivot_abs <= 1.0e-24 {
            matrix[pivot][pivot] += 1.0e-9;
        }
        let pivot_value = matrix[pivot][pivot];
        for row in pivot + 1..n {
            let factor = matrix[row][pivot] / pivot_value;
            matrix[row][pivot] = 0.0;
            for col in pivot + 1..n {
                matrix[row][col] -= factor * matrix[pivot][col];
            }
            rhs[row] -= factor * rhs[pivot];
        }
    }

    let mut solution = vec![0.0; n];
    for row in (0..n).rev() {
        let mut accum = rhs[row];
        for (col, value) in solution.iter().enumerate().skip(row + 1) {
            accum -= matrix[row][col] * value;
        }
        solution[row] = accum
            / matrix[row][row]
                .abs()
                .max(1.0e-18)
                .copysign(matrix[row][row]);
    }
    solution
}

fn apply_dense_real_operator(matrix: &[Vec<f64>], x: &[f64]) -> Vec<f64> {
    matrix
        .iter()
        .map(|row| {
            row.iter()
                .zip(x.iter())
                .map(|(coefficient, value)| coefficient * value)
                .sum::<f64>()
        })
        .collect()
}

fn vector_field_from_x_profile(x: &[f64], target_count: usize) -> Vec<f64> {
    let profile = resample_scalar_profile(x, target_count);
    let mut field = Vec::with_capacity(profile.len() * 3);
    for displacement_x in profile {
        field.extend_from_slice(&[displacement_x, 0.0, 0.0]);
    }
    field
}

#[derive(Debug, Clone, Copy, Default)]
struct FsiInterfaceClosure {
    interface_node_count: usize,
    interface_face_count: usize,
    max_interface_residual: f64,
    force_balance_ratio: f64,
    max_displacement_transfer_residual_m: f64,
    max_interface_displacement_m: f64,
    mean_interface_pressure_pa: f64,
    max_traction_magnitude_pa: f64,
    max_coupling_iteration_count: usize,
    max_pressure_feedback_residual_ratio: f64,
    max_two_way_interface_residual_ratio: f64,
    max_structural_traction_update_residual_ratio: f64,
    max_pressure_displacement_law_residual_ratio: f64,
    max_structural_solve_residual_ratio: f64,
    max_interface_work_j_per_m2: f64,
    max_structural_strain_energy_j_per_m2: f64,
    max_interface_work_energy_residual_ratio: f64,
    structural_coupling_edge_count: usize,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    full_topology_edge_count: usize,
    full_topology_element_count: usize,
    interface_stiffness_pa_per_m: f64,
}

#[derive(Debug, Clone, Copy)]
struct FsiKnownAnswerMetrics {
    pressure_loaded_wall_displacement_law_residual_ratio: f64,
    interface_traction_balance_residual_ratio: f64,
    interface_displacement_transfer_residual_m: f64,
    partitioned_pressure_feedback_residual_ratio: f64,
    two_way_interface_residual_ratio: f64,
    structural_traction_update_residual_ratio: f64,
    structural_solve_residual_ratio: f64,
    interface_work_energy_residual_ratio: f64,
    interface_connectivity_coverage_ratio: f64,
    mesh_backed_interface_connectivity_ratio: f64,
    known_answer_coverage_ratio: f64,
}

fn fsi_known_answer_metrics(closure: &FsiInterfaceClosure) -> FsiKnownAnswerMetrics {
    let known_answer_coverage_ratio = if closure.interface_node_count > 0
        && closure.interface_face_count > 0
        && closure.mean_interface_pressure_pa.is_finite()
        && closure.mean_interface_pressure_pa > 0.0
        && closure.max_traction_magnitude_pa.is_finite()
        && closure.max_traction_magnitude_pa > 0.0
        && closure.max_interface_displacement_m.is_finite()
        && closure.max_interface_displacement_m > 0.0
        && closure.interface_stiffness_pa_per_m.is_finite()
        && closure.interface_stiffness_pa_per_m > 0.0
        && closure
            .max_pressure_displacement_law_residual_ratio
            .is_finite()
        && closure.force_balance_ratio.is_finite()
        && closure.max_pressure_feedback_residual_ratio.is_finite()
        && closure.max_two_way_interface_residual_ratio.is_finite()
        && closure
            .max_structural_traction_update_residual_ratio
            .is_finite()
        && closure.max_structural_solve_residual_ratio.is_finite()
        && closure.max_interface_work_j_per_m2.is_finite()
        && closure.max_interface_work_j_per_m2 > 0.0
        && closure.max_structural_strain_energy_j_per_m2.is_finite()
        && closure.max_structural_strain_energy_j_per_m2 > 0.0
        && closure.max_interface_work_energy_residual_ratio.is_finite()
        && closure.structural_coupling_edge_count > 0
        && closure.interface_connectivity_coverage_ratio.is_finite()
        && closure.interface_connectivity_coverage_ratio >= 1.0
        && closure.mesh_backed_interface_connectivity_ratio.is_finite()
    {
        1.0
    } else {
        0.0
    };

    FsiKnownAnswerMetrics {
        pressure_loaded_wall_displacement_law_residual_ratio: closure
            .max_pressure_displacement_law_residual_ratio,
        interface_traction_balance_residual_ratio: closure.force_balance_ratio,
        interface_displacement_transfer_residual_m: closure.max_displacement_transfer_residual_m,
        partitioned_pressure_feedback_residual_ratio: closure.max_pressure_feedback_residual_ratio,
        two_way_interface_residual_ratio: closure.max_two_way_interface_residual_ratio,
        structural_traction_update_residual_ratio: closure
            .max_structural_traction_update_residual_ratio,
        structural_solve_residual_ratio: closure.max_structural_solve_residual_ratio,
        interface_work_energy_residual_ratio: closure.max_interface_work_energy_residual_ratio,
        interface_connectivity_coverage_ratio: closure.interface_connectivity_coverage_ratio,
        mesh_backed_interface_connectivity_ratio: closure.mesh_backed_interface_connectivity_ratio,
        known_answer_coverage_ratio,
    }
}

fn fsi_known_answer_diagnostic(
    metrics: &FsiKnownAnswerMetrics,
    tolerance: f64,
) -> runmat_analysis_fea::diagnostics::FeaDiagnostic {
    let severity = if metrics.pressure_loaded_wall_displacement_law_residual_ratio <= tolerance
        && metrics.interface_traction_balance_residual_ratio <= 1.0e-9
        && metrics.interface_displacement_transfer_residual_m <= 1.0e-12
        && metrics.partitioned_pressure_feedback_residual_ratio <= tolerance
        && metrics.two_way_interface_residual_ratio <= tolerance
        && metrics.structural_traction_update_residual_ratio <= tolerance
        && metrics.structural_solve_residual_ratio <= tolerance
        && metrics.interface_work_energy_residual_ratio <= tolerance
        && metrics.interface_connectivity_coverage_ratio >= 1.0
        && metrics.known_answer_coverage_ratio >= 1.0
    {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };

    runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_FSI_KNOWN_ANSWER".to_string(),
        severity,
        message: format!(
            "basis=pressure_loaded_wall_partitioned pressure_loaded_wall_displacement_law_residual_ratio={} interface_traction_balance_residual_ratio={} interface_displacement_transfer_residual_m={} partitioned_pressure_feedback_residual_ratio={} two_way_interface_residual_ratio={} structural_traction_update_residual_ratio={} structural_solve_residual_ratio={} interface_work_energy_residual_ratio={} interface_connectivity_coverage_ratio={} mesh_backed_interface_connectivity_ratio={} known_answer_coverage_ratio={}",
            metrics.pressure_loaded_wall_displacement_law_residual_ratio,
            metrics.interface_traction_balance_residual_ratio,
            metrics.interface_displacement_transfer_residual_m,
            metrics.partitioned_pressure_feedback_residual_ratio,
            metrics.two_way_interface_residual_ratio,
            metrics.structural_traction_update_residual_ratio,
            metrics.structural_solve_residual_ratio,
            metrics.interface_work_energy_residual_ratio,
            metrics.interface_connectivity_coverage_ratio,
            metrics.mesh_backed_interface_connectivity_ratio,
            metrics.known_answer_coverage_ratio,
        ),
    }
}

fn fsi_structural_compliance_per_pa(model: &AnalysisModel) -> f64 {
    if let Some(stiffness) = fsi_interface_normal_stiffness_pa_per_m(model) {
        return 1.0 / stiffness.max(1.0e-18);
    }

    let mean_modulus = model
        .materials
        .iter()
        .filter_map(|material| {
            let modulus = material.mechanical.youngs_modulus_pa;
            (modulus.is_finite() && modulus > 0.0).then_some(modulus)
        })
        .fold((0.0_f64, 0_usize), |(sum, count), modulus| {
            (sum + modulus, count + 1)
        });
    let youngs_modulus = if mean_modulus.1 > 0 {
        mean_modulus.0 / mean_modulus.1 as f64
    } else {
        200.0e9
    };
    1.0 / youngs_modulus.max(1.0e6)
}

fn fsi_interface_normal_stiffness_pa_per_m(model: &AnalysisModel) -> Option<f64> {
    model
        .interfaces
        .iter()
        .find_map(|interface| match &interface.kind {
            AnalysisInterfaceKind::FluidStructure(fluid_structure)
                if fluid_structure.normal_stiffness_pa_per_m.is_finite()
                    && fluid_structure.normal_stiffness_pa_per_m > 0.0 =>
            {
                Some(fluid_structure.normal_stiffness_pa_per_m)
            }
            AnalysisInterfaceKind::FluidStructure(_)
            | AnalysisInterfaceKind::ConjugateHeatTransfer(_)
            | AnalysisInterfaceKind::Contact(_) => None,
        })
}

pub fn analysis_run_transient_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_transient_with_options_op(
        model,
        backend,
        AnalysisTransientRunOptions::default(),
        context,
    )
}

pub fn analysis_run_cfd_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_cfd_with_options_op(model, backend, AnalysisCfdRunOptions::default(), context)
}

pub fn analysis_run_cfd_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisCfdRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_cfd_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Cfd);
    if !has_cfd_step {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one cfd step for fea.run_cfd",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }

    let Some(cfd_domain) = model.cfd.as_ref() else {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires model.cfd to be configured",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    };

    if !cfd_domain.enabled {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires cfd domain enabled=true",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    }
    if !cfd_domain.reference_density_kg_per_m3.is_finite()
        || cfd_domain.reference_density_kg_per_m3 <= 0.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires finite positive reference_density_kg_per_m3",
            BTreeMap::from([(
                "reference_density_kg_per_m3".to_string(),
                cfd_domain.reference_density_kg_per_m3.to_string(),
            )]),
        ));
    }
    if !cfd_domain.dynamic_viscosity_pa_s.is_finite() || cfd_domain.dynamic_viscosity_pa_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires finite positive dynamic_viscosity_pa_s",
            BTreeMap::from([(
                "dynamic_viscosity_pa_s".to_string(),
                cfd_domain.dynamic_viscosity_pa_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.inlet_velocity_m_per_s.is_finite() || cfd_domain.inlet_velocity_m_per_s < 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires finite non-negative inlet_velocity_m_per_s",
            BTreeMap::from([(
                "inlet_velocity_m_per_s".to_string(),
                cfd_domain.inlet_velocity_m_per_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.turbulence_intensity.is_finite()
        || cfd_domain.turbulence_intensity < 0.0
        || cfd_domain.turbulence_intensity > 1.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd requires turbulence_intensity in [0, 1]",
            BTreeMap::from([(
                "turbulence_intensity".to_string(),
                cfd_domain.turbulence_intensity.to_string(),
            )]),
        ));
    }
    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_CFD_OPERATION,
        ANALYSIS_RUN_CFD_OP_VERSION,
        "RM.FEA.RUN_CFD.INVALID_LOAD",
        "CFD",
        &context,
    )?;
    if let Err(detail) = validate_authored_cfd_boundary_conditions(model) {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_BOUNDARY_CONDITIONS",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            detail.clone(),
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("detail".to_string(), detail),
            ]),
        ));
    }

    if !options.time_step_s.is_finite() || options.time_step_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd options require finite positive time_step_s",
            BTreeMap::from([("time_step_s".to_string(), options.time_step_s.to_string())]),
        ));
    }
    if options.step_count == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd options require step_count greater than zero",
            BTreeMap::from([("step_count".to_string(), options.step_count.to_string())]),
        ));
    }
    if options.max_linear_iters == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd options require max_linear_iters greater than zero",
            BTreeMap::from([(
                "max_linear_iters".to_string(),
                options.max_linear_iters.to_string(),
            )]),
        ));
    }
    if !options.tolerance.is_finite() || options.tolerance <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd options require finite positive tolerance",
            BTreeMap::from([("tolerance".to_string(), options.tolerance.to_string())]),
        ));
    }
    if !options.residual_warn_threshold.is_finite() || options.residual_warn_threshold <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CFD_OPERATION,
            ANALYSIS_RUN_CFD_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CFD.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cfd options require finite positive residual_warn_threshold",
            BTreeMap::from([(
                "residual_warn_threshold".to_string(),
                options.residual_warn_threshold.to_string(),
            )]),
        ));
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_CFD_OPERATION,
        ANALYSIS_RUN_CFD_OP_VERSION,
        &context,
    )?;

    let solve_start = Instant::now();
    let mut run =
        solve_cfd_finite_volume_run(model, cfd_domain, backend, &options, prep_context.as_ref());
    let solve_ms = solve_start.elapsed().as_secs_f64() * 1000.0;
    run.diagnostics
        .push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
            code: "FEA_CFD_COST".to_string(),
            severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
            message: format!(
                "solve_ms={} step_count={} max_linear_iters={} tolerance={}",
                solve_ms, options.step_count, options.max_linear_iters, options.tolerance,
            ),
        });
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }

    let flow_topology = CfdDomainTopology::from_model(model, prep_context.as_ref());
    let flow_boundary_summary = CfdBoundarySummary::from_model(model, cfd_domain, 2);
    let flow_inlet_velocity = flow_boundary_summary.nominal_inlet_velocity_m_per_s;
    let reynolds_number = cfd_reynolds_number_for_velocity(cfd_domain, flow_inlet_velocity);
    let solve_family = match cfd_domain.solve_family {
        runmat_analysis_core::CfdSolveFamily::SteadyState => "steady_state",
        runmat_analysis_core::CfdSolveFamily::Transient => "transient",
    };
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_FLOW".to_string(),
        severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
        message: format!(
            "density={} viscosity={} inlet_velocity={} turbulence_intensity={} reynolds_number={} solve_family={} profile_point_count={} topology_basis={} control_volume_count={} control_volume_face_count={} control_volume_internal_face_count={} control_volume_boundary_face_count={} control_volume_connectivity_coverage_ratio={} domain_length_m={} hydraulic_diameter_m={}",
            cfd_domain.reference_density_kg_per_m3,
            cfd_domain.dynamic_viscosity_pa_s,
            flow_inlet_velocity,
            cfd_domain.turbulence_intensity,
            reynolds_number,
            solve_family,
            cfd_domain.time_profile.len(),
            flow_topology.basis.as_str(),
            flow_topology.control_volume_count,
            flow_topology.control_volume_face_count,
            flow_topology.control_volume_internal_face_count,
            flow_topology.control_volume_boundary_face_count,
            flow_topology.control_volume_connectivity_coverage_ratio,
            flow_topology.domain_length_m,
            flow_topology.hydraulic_diameter_m,
        ),
    });

    let max_momentum_residual = diagnostic_metric(
        &run.diagnostics,
        "FEA_CFD_RESIDUAL",
        "max_momentum_residual",
    )
    .unwrap_or(f64::INFINITY);
    let max_continuity_residual = diagnostic_metric(
        &run.diagnostics,
        "FEA_CFD_RESIDUAL",
        "max_continuity_residual",
    )
    .unwrap_or(f64::INFINITY);
    let solver_convergence = if max_momentum_residual <= options.residual_warn_threshold
        && max_continuity_residual <= options.residual_warn_threshold
    {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if run.fields_are_empty()
        || !max_momentum_residual.is_finite()
        || !max_continuity_residual.is_finite()
    {
        QualityGate::Fail
    } else if max_momentum_residual > options.residual_warn_threshold
        || max_continuity_residual > options.residual_warn_threshold
    {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "cfd solver convergence gate is warning".to_string(),
        });
    }
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::TransientResidualExceeded,
            detail: format!(
                "cfd residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass && result_quality == QualityGate::Pass
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_CFD_OPERATION,
        ANALYSIS_RUN_CFD_OP_VERSION,
        "RM.FEA.RUN_CFD.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_CFD_OPERATION,
        ANALYSIS_RUN_CFD_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_thermal_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_thermal_with_options_op(
        model,
        backend,
        AnalysisThermalRunOptions::default(),
        context,
    )
}

pub fn analysis_run_cht_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_cht_with_options_op(model, backend, AnalysisChtRunOptions::default(), context)
}

pub fn analysis_run_cht_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisChtRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_cfd_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Cfd);
    if !has_cfd_step {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one cfd step for fea.run_cht",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    let has_thermal_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Thermal);
    if !has_thermal_step {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one thermal step for fea.run_cht",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    let Some(cfd_domain) = model.cfd.as_ref() else {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires model.cfd to be configured",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    };
    if !cfd_domain.enabled {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires cfd domain enabled=true",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    }
    if !cfd_domain.reference_density_kg_per_m3.is_finite()
        || cfd_domain.reference_density_kg_per_m3 <= 0.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires finite positive reference_density_kg_per_m3",
            BTreeMap::from([(
                "reference_density_kg_per_m3".to_string(),
                cfd_domain.reference_density_kg_per_m3.to_string(),
            )]),
        ));
    }
    if !cfd_domain.dynamic_viscosity_pa_s.is_finite() || cfd_domain.dynamic_viscosity_pa_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires finite positive dynamic_viscosity_pa_s",
            BTreeMap::from([(
                "dynamic_viscosity_pa_s".to_string(),
                cfd_domain.dynamic_viscosity_pa_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.inlet_velocity_m_per_s.is_finite() || cfd_domain.inlet_velocity_m_per_s < 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires finite non-negative inlet_velocity_m_per_s",
            BTreeMap::from([(
                "inlet_velocity_m_per_s".to_string(),
                cfd_domain.inlet_velocity_m_per_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.turbulence_intensity.is_finite()
        || cfd_domain.turbulence_intensity < 0.0
        || cfd_domain.turbulence_intensity > 1.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires turbulence_intensity in [0, 1]",
            BTreeMap::from([(
                "turbulence_intensity".to_string(),
                cfd_domain.turbulence_intensity.to_string(),
            )]),
        ));
    }
    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_CHT_OPERATION,
        ANALYSIS_RUN_CHT_OP_VERSION,
        "RM.FEA.RUN_CHT.INVALID_LOAD",
        "CHT",
        &context,
    )?;
    if !options.time_step_s.is_finite() || options.time_step_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht options require finite positive time_step_s",
            BTreeMap::from([("time_step_s".to_string(), options.time_step_s.to_string())]),
        ));
    }
    if options.step_count == 0 || options.max_linear_iters == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht options require step_count/max_linear_iters greater than zero",
            BTreeMap::new(),
        ));
    }
    if !options.tolerance.is_finite() || options.tolerance <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht options require finite positive tolerance",
            BTreeMap::from([("tolerance".to_string(), options.tolerance.to_string())]),
        ));
    }
    if !options.residual_warn_threshold.is_finite() || options.residual_warn_threshold <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht options require finite positive residual_warn_threshold",
            BTreeMap::from([(
                "residual_warn_threshold".to_string(),
                options.residual_warn_threshold.to_string(),
            )]),
        ));
    }

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_CHT_OPERATION,
        ANALYSIS_RUN_CHT_OP_VERSION,
        &context,
    )?;
    let Some(thermo_options) = thermo_options else {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_cht requires model.thermo_mechanical to be configured",
            BTreeMap::new(),
        ));
    };
    if let Err((detail, metadata)) = validate_thermo_coupling_options(model, &thermo_options) {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            detail,
            metadata,
        ));
    }
    if let Err((detail, metadata)) = validate_coupled_flow_interfaces(model, "CHT") {
        return Err(operation_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_CHT.INVALID_INTERFACE_MAPPING",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            detail,
            metadata,
        ));
    }
    let applied_temperature_delta_k = thermo_options.applied_temperature_delta_k;

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_CHT_OPERATION,
        ANALYSIS_RUN_CHT_OP_VERSION,
        &context,
    )?;

    let solve_start = Instant::now();
    let thermal_run = run_thermal_with_options(
        model,
        backend,
        ThermalSolveOptions {
            step_count: options.step_count,
            time_step_s: options.time_step_s,
            residual_target: options.residual_warn_threshold,
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(Some(
                thermo_options.clone(),
            )),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_CHT_OPERATION,
            ANALYSIS_RUN_CHT_OP_VERSION,
            "RM.FEA.RUN_CHT.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_CHT.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let topology = CfdDomainTopology::from_model(model, prep_context.as_ref());
    let node_count = topology.node_count;
    let field_step = match cfd_domain.solve_family {
        runmat_analysis_core::CfdSolveFamily::SteadyState => 0,
        runmat_analysis_core::CfdSolveFamily::Transient => options.step_count.saturating_sub(1),
    };
    let (fluid_velocity, fluid_pressure) =
        recover_cfd_velocity_pressure(cfd_domain, &topology, field_step);
    let (cfd_residual_momentum, cfd_residual_continuity) = cfd_residual_norms(
        &fluid_velocity,
        &fluid_pressure,
        cfd_domain,
        &topology,
        options.step_count,
    );
    let max_cfd_momentum_residual = cfd_residual_momentum
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let max_cfd_continuity_residual = cfd_residual_continuity
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let (cht_fields, cht_interface_closure) = build_cht_run_fields(
        cfd_domain,
        &topology,
        &thermal_run,
        cht_interface_conductance_w_per_m2k(model),
        options.max_linear_iters,
        options.tolerance,
    );
    let mut run = thermal_run.run.clone();
    run.solver_method = "cht_conjugate_projection".to_string();
    run.preconditioner = "thermal_cfd_projection".to_string();
    run.fields.extend(cht_fields);
    let reynolds_number = cfd_reynolds_number(cfd_domain);
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_FLOW".to_string(),
        severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
        message: format!(
            "density={} viscosity={} inlet_velocity={} turbulence_intensity={} reynolds_number={} solve_family={} profile_point_count={} topology_basis={} control_volume_count={} control_volume_face_count={} control_volume_internal_face_count={} control_volume_boundary_face_count={} control_volume_connectivity_coverage_ratio={} domain_length_m={} hydraulic_diameter_m={}",
            cfd_domain.reference_density_kg_per_m3,
            cfd_domain.dynamic_viscosity_pa_s,
            cfd_domain.inlet_velocity_m_per_s,
            cfd_domain.turbulence_intensity,
            reynolds_number,
            match cfd_domain.solve_family {
                runmat_analysis_core::CfdSolveFamily::SteadyState => "steady_state",
                runmat_analysis_core::CfdSolveFamily::Transient => "transient",
            },
            cfd_domain.time_profile.len(),
            topology.basis.as_str(),
            topology.control_volume_count,
            topology.control_volume_face_count,
            topology.control_volume_internal_face_count,
            topology.control_volume_boundary_face_count,
            topology.control_volume_connectivity_coverage_ratio,
            topology.domain_length_m,
            topology.hydraulic_diameter_m,
        ),
    });
    let cfd_residual_severity = if max_cfd_momentum_residual <= options.residual_warn_threshold
        && max_cfd_continuity_residual <= options.residual_warn_threshold
    {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_RESIDUAL".to_string(),
        severity: cfd_residual_severity,
        message: format!(
            "max_momentum_residual={} max_continuity_residual={} residual_warn_threshold={} cfd_node_count={} cfd_step_count={}",
            max_cfd_momentum_residual,
            max_cfd_continuity_residual,
            options.residual_warn_threshold,
            node_count,
            options.step_count,
        ),
    });
    run.diagnostics.push(cfd_assembly_diagnostic(
        &topology,
        cfd_domain,
        options.time_step_s,
        pressure_drop_from_nodal_pressure(&fluid_pressure),
        max_cfd_continuity_residual,
        options.residual_warn_threshold,
    ));
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CHT_COUPLING".to_string(),
        severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
        message: format!(
            "reference_temperature_k={} applied_temperature_delta_k={} step_count={} time_step_s={} authored_interface_count={}",
            thermal_run.reference_temperature_k,
            applied_temperature_delta_k,
            options.step_count,
            options.time_step_s,
            model.interfaces.len(),
        ),
    });
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CHT_INTERFACE_CLOSURE".to_string(),
        severity: if cht_interface_closure.heat_flux_balance_ratio <= 1.0e-9
            && cht_interface_closure.max_energy_residual <= options.residual_warn_threshold
            && cht_interface_closure.max_temperature_jump_k <= 0.1
            && cht_interface_closure.max_thermal_transport_residual_ratio
                <= options.residual_warn_threshold
            && cht_interface_closure.max_flux_temperature_law_residual_ratio
                <= options.residual_warn_threshold
            && cht_interface_closure.max_heat_flux_realization_residual_ratio
                <= options.residual_warn_threshold
            && cht_interface_closure.max_coupled_interface_residual_ratio
                <= options.residual_warn_threshold
            && cht_interface_closure.interface_connectivity_coverage_ratio >= 1.0
            && cht_interface_closure.max_thermal_network_residual_ratio
                <= options.residual_warn_threshold
        {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
        } else {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
        },
        message: format!(
            "interface_face_count={} max_temperature_jump_k={} max_energy_residual={} heat_flux_balance_ratio={} mean_interface_heat_flux_w_per_m2={} thermal_transport_residual_ratio={} interface_temperature_continuity_ratio={} max_advection_temperature_shift_k={} interface_conductance_w_per_m2k={} flux_temperature_law_residual_ratio={} heat_flux_realization_residual_ratio={} coupled_interface_iteration_count={} coupled_interface_residual_ratio={} thermal_network_node_count={} thermal_network_edge_count={} interface_connectivity_coverage_ratio={} mesh_backed_interface_connectivity_ratio={} full_topology_edge_count={} full_topology_element_count={} thermal_network_residual_ratio={}",
            cht_interface_closure.interface_face_count,
            cht_interface_closure.max_temperature_jump_k,
            cht_interface_closure.max_energy_residual,
            cht_interface_closure.heat_flux_balance_ratio,
            cht_interface_closure.mean_interface_heat_flux_w_per_m2,
            cht_interface_closure.max_thermal_transport_residual_ratio,
            cht_interface_closure.interface_temperature_continuity_ratio,
            cht_interface_closure.max_advection_temperature_shift_k,
            cht_interface_closure.interface_conductance_w_per_m2k,
            cht_interface_closure.max_flux_temperature_law_residual_ratio,
            cht_interface_closure.max_heat_flux_realization_residual_ratio,
            cht_interface_closure.max_coupled_interface_iteration_count,
            cht_interface_closure.max_coupled_interface_residual_ratio,
            cht_interface_closure.thermal_network_node_count,
            cht_interface_closure.thermal_network_edge_count,
            cht_interface_closure.interface_connectivity_coverage_ratio,
            cht_interface_closure.mesh_backed_interface_connectivity_ratio,
            cht_interface_closure.full_topology_edge_count,
            cht_interface_closure.full_topology_element_count,
            cht_interface_closure.max_thermal_network_residual_ratio,
        ),
    });
    let cht_known_answer = cht_known_answer_metrics(cfd_domain, &cht_interface_closure);
    run.diagnostics.push(cht_known_answer_diagnostic(
        &cht_known_answer,
        options.residual_warn_threshold,
    ));
    let solve_ms = solve_start.elapsed().as_secs_f64() * 1000.0;
    run.diagnostics
        .push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
            code: "FEA_CHT_COST".to_string(),
            severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
            message: format!(
                "solve_ms={} step_count={} max_linear_iters={} tolerance={}",
                solve_ms, options.step_count, options.max_linear_iters, options.tolerance,
            ),
        });

    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }

    let max_cht_transport_residual = cht_interface_closure
        .max_thermal_transport_residual_ratio
        .max(
            cht_interface_closure
                .max_energy_residual
                .max(cht_interface_closure.heat_flux_balance_ratio),
        );
    let solver_convergence = if max_cfd_momentum_residual <= options.residual_warn_threshold
        && max_cfd_continuity_residual <= options.residual_warn_threshold
        && max_cht_transport_residual <= options.residual_warn_threshold
    {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if run.fields_are_empty()
        || thermal_run.temperature_snapshots.is_empty()
        || thermal_run.time_points_s.is_empty()
        || thermal_run.residual_norms.iter().any(|r| !r.is_finite())
        || !max_cfd_momentum_residual.is_finite()
        || !max_cfd_continuity_residual.is_finite()
        || !max_cht_transport_residual.is_finite()
    {
        QualityGate::Fail
    } else if max_cfd_momentum_residual > options.residual_warn_threshold
        || max_cfd_continuity_residual > options.residual_warn_threshold
        || max_cht_transport_residual > options.residual_warn_threshold
    {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "cht solver convergence gate is warning".to_string(),
        });
    }
    if max_cfd_momentum_residual > options.residual_warn_threshold
        || max_cfd_continuity_residual > options.residual_warn_threshold
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: format!(
                "cht cfd residual exceeds threshold {}",
                options.residual_warn_threshold,
            ),
        });
    }
    if max_cht_transport_residual > options.residual_warn_threshold {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: format!(
                "cht interface transport residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass && result_quality == QualityGate::Pass
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: Some(ThermalResultsData {
            thermal_payload_version: "thermal_results/v1".to_string(),
            time_points_s: thermal_run.time_points_s,
            temperature_snapshots: thermal_run.temperature_snapshots,
            temperature_gradient_snapshots: thermal_run.temperature_gradient_snapshots,
            heat_flux_snapshots: thermal_run.heat_flux_snapshots,
            heat_source_snapshots: thermal_run.heat_source_snapshots,
            boundary_heat_flux_snapshots: thermal_run.boundary_heat_flux_snapshots,
            residual_norms: thermal_run.residual_norms,
            reference_temperature_k: thermal_run.reference_temperature_k,
        }),
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_CHT_OPERATION,
        ANALYSIS_RUN_CHT_OP_VERSION,
        "RM.FEA.RUN_CHT.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_CHT_OPERATION,
        ANALYSIS_RUN_CHT_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_fsi_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_fsi_with_options_op(model, backend, AnalysisFsiRunOptions::default(), context)
}

pub fn analysis_run_fsi_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisFsiRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_cfd_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Cfd);
    if !has_cfd_step {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one cfd step for fea.run_fsi",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    let has_transient_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Transient);
    if !has_transient_step {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one transient step for fea.run_fsi",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    let Some(cfd_domain) = model.cfd.as_ref() else {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires model.cfd to be configured",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    };
    if !cfd_domain.enabled {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires cfd domain enabled=true",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    }
    if !cfd_domain.reference_density_kg_per_m3.is_finite()
        || cfd_domain.reference_density_kg_per_m3 <= 0.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires finite positive reference_density_kg_per_m3",
            BTreeMap::from([(
                "reference_density_kg_per_m3".to_string(),
                cfd_domain.reference_density_kg_per_m3.to_string(),
            )]),
        ));
    }
    if !cfd_domain.dynamic_viscosity_pa_s.is_finite() || cfd_domain.dynamic_viscosity_pa_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires finite positive dynamic_viscosity_pa_s",
            BTreeMap::from([(
                "dynamic_viscosity_pa_s".to_string(),
                cfd_domain.dynamic_viscosity_pa_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.inlet_velocity_m_per_s.is_finite() || cfd_domain.inlet_velocity_m_per_s < 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires finite non-negative inlet_velocity_m_per_s",
            BTreeMap::from([(
                "inlet_velocity_m_per_s".to_string(),
                cfd_domain.inlet_velocity_m_per_s.to_string(),
            )]),
        ));
    }
    if !cfd_domain.turbulence_intensity.is_finite()
        || cfd_domain.turbulence_intensity < 0.0
        || cfd_domain.turbulence_intensity > 1.0
    {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi requires turbulence_intensity in [0, 1]",
            BTreeMap::from([(
                "turbulence_intensity".to_string(),
                cfd_domain.turbulence_intensity.to_string(),
            )]),
        ));
    }
    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_FSI_OPERATION,
        ANALYSIS_RUN_FSI_OP_VERSION,
        "RM.FEA.RUN_FSI.INVALID_LOAD",
        "FSI",
        &context,
    )?;
    if !options.time_step_s.is_finite() || options.time_step_s <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi options require finite positive time_step_s",
            BTreeMap::from([("time_step_s".to_string(), options.time_step_s.to_string())]),
        ));
    }
    if options.step_count == 0 || options.max_linear_iters == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi options require step_count/max_linear_iters greater than zero",
            BTreeMap::new(),
        ));
    }
    if !options.tolerance.is_finite() || options.tolerance <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi options require finite positive tolerance",
            BTreeMap::from([("tolerance".to_string(), options.tolerance.to_string())]),
        ));
    }
    if !options.residual_warn_threshold.is_finite() || options.residual_warn_threshold <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_fsi options require finite positive residual_warn_threshold",
            BTreeMap::from([(
                "residual_warn_threshold".to_string(),
                options.residual_warn_threshold.to_string(),
            )]),
        ));
    }
    if let Err((detail, metadata)) = validate_coupled_flow_interfaces(model, "FSI") {
        return Err(operation_error(
            ANALYSIS_RUN_FSI_OPERATION,
            ANALYSIS_RUN_FSI_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_FSI.INVALID_INTERFACE_MAPPING",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            detail,
            metadata,
        ));
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_FSI_OPERATION,
        ANALYSIS_RUN_FSI_OP_VERSION,
        &context,
    )?;

    let solve_start = Instant::now();
    let topology = CfdDomainTopology::from_model(model, prep_context.as_ref());
    let node_count = topology.node_count;
    let field_step = match cfd_domain.solve_family {
        runmat_analysis_core::CfdSolveFamily::SteadyState => 0,
        runmat_analysis_core::CfdSolveFamily::Transient => options.step_count.saturating_sub(1),
    };
    let (fluid_velocity, fluid_pressure) =
        recover_cfd_velocity_pressure(cfd_domain, &topology, field_step);
    let (cfd_residual_momentum, cfd_residual_continuity) = cfd_residual_norms(
        &fluid_velocity,
        &fluid_pressure,
        cfd_domain,
        &topology,
        options.step_count,
    );
    let max_cfd_momentum_residual = cfd_residual_momentum
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let max_cfd_continuity_residual = cfd_residual_continuity
        .iter()
        .copied()
        .fold(0.0_f64, f64::max);
    let structural_compliance_per_pa = fsi_structural_compliance_per_pa(model);
    let (fsi_fields, fsi_interface_closure) = build_fsi_run_fields(
        cfd_domain,
        &topology,
        options.step_count,
        structural_compliance_per_pa,
        options.max_linear_iters,
        options.tolerance,
        &cfd_residual_momentum,
        &cfd_residual_continuity,
    );
    let max_interface_residual = fsi_interface_closure.max_interface_residual;
    let mut run = FeaRunResult {
        backend,
        solver_backend: "cpu_reference".to_string(),
        solver_device_apply_k_ratio: 0.0,
        solver_method: "fsi_partitioned_projection".to_string(),
        preconditioner: "interface_relaxation".to_string(),
        solver_host_sync_count: 0,
        diagnostics: Vec::new(),
        fields: fsi_fields,
    };
    let reynolds_number = cfd_reynolds_number(cfd_domain);
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_FLOW".to_string(),
        severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
        message: format!(
            "density={} viscosity={} inlet_velocity={} turbulence_intensity={} reynolds_number={} solve_family={} profile_point_count={} topology_basis={} control_volume_count={} control_volume_face_count={} control_volume_internal_face_count={} control_volume_boundary_face_count={} control_volume_connectivity_coverage_ratio={} domain_length_m={} hydraulic_diameter_m={}",
            cfd_domain.reference_density_kg_per_m3,
            cfd_domain.dynamic_viscosity_pa_s,
            cfd_domain.inlet_velocity_m_per_s,
            cfd_domain.turbulence_intensity,
            reynolds_number,
            match cfd_domain.solve_family {
                runmat_analysis_core::CfdSolveFamily::SteadyState => "steady_state",
                runmat_analysis_core::CfdSolveFamily::Transient => "transient",
            },
            cfd_domain.time_profile.len(),
            topology.basis.as_str(),
            topology.control_volume_count,
            topology.control_volume_face_count,
            topology.control_volume_internal_face_count,
            topology.control_volume_boundary_face_count,
            topology.control_volume_connectivity_coverage_ratio,
            topology.domain_length_m,
            topology.hydraulic_diameter_m,
        ),
    });
    let residual_severity = if max_interface_residual <= options.residual_warn_threshold {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_CFD_RESIDUAL".to_string(),
        severity: residual_severity,
        message: format!(
            "max_momentum_residual={} max_continuity_residual={} residual_warn_threshold={} cfd_node_count={} cfd_step_count={}",
            max_cfd_momentum_residual,
            max_cfd_continuity_residual,
            options.residual_warn_threshold,
            node_count,
            options.step_count,
        ),
    });
    run.diagnostics.push(cfd_assembly_diagnostic(
        &topology,
        cfd_domain,
        options.time_step_s,
        pressure_drop_from_nodal_pressure(&fluid_pressure),
        max_cfd_continuity_residual,
        options.residual_warn_threshold,
    ));
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_FSI_INTERFACE_RESIDUAL".to_string(),
        severity: residual_severity,
        message: format!(
            "max_interface_residual={} structural_compliance_per_pa={} residual_warn_threshold={} interface_node_count={} interface_face_count={}",
            max_interface_residual,
            structural_compliance_per_pa,
            options.residual_warn_threshold,
            fsi_interface_closure.interface_node_count,
            fsi_interface_closure.interface_face_count,
        ),
    });
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_FSI_INTERFACE_CLOSURE".to_string(),
        severity: if fsi_interface_closure.force_balance_ratio <= 1.0e-9
            && fsi_interface_closure.max_displacement_transfer_residual_m <= 1.0e-12
            && fsi_interface_closure.max_pressure_feedback_residual_ratio <= options.tolerance
            && fsi_interface_closure.max_two_way_interface_residual_ratio <= options.tolerance
            && fsi_interface_closure.max_structural_traction_update_residual_ratio
                <= options.tolerance
            && fsi_interface_closure.max_pressure_displacement_law_residual_ratio
                <= options.tolerance
            && fsi_interface_closure.max_structural_solve_residual_ratio <= options.tolerance
            && fsi_interface_closure.max_interface_work_energy_residual_ratio <= options.tolerance
            && fsi_interface_closure.structural_coupling_edge_count > 0
            && fsi_interface_closure.interface_connectivity_coverage_ratio >= 1.0
            && fsi_interface_closure.max_interface_residual <= options.residual_warn_threshold
        {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
        } else {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
        },
        message: format!(
            "interface_node_count={} interface_face_count={} max_interface_residual={} force_balance_ratio={} max_displacement_transfer_residual_m={} max_interface_displacement_m={} mean_interface_pressure_pa={} max_traction_magnitude_pa={} max_coupling_iteration_count={} pressure_feedback_residual_ratio={} two_way_interface_residual_ratio={} structural_traction_update_residual_ratio={} pressure_displacement_law_residual_ratio={} structural_solve_residual_ratio={} interface_work_j_per_m2={} structural_strain_energy_j_per_m2={} interface_work_energy_residual_ratio={} structural_coupling_edge_count={} interface_connectivity_coverage_ratio={} mesh_backed_interface_connectivity_ratio={} full_topology_edge_count={} full_topology_element_count={} interface_stiffness_pa_per_m={}",
            fsi_interface_closure.interface_node_count,
            fsi_interface_closure.interface_face_count,
            fsi_interface_closure.max_interface_residual,
            fsi_interface_closure.force_balance_ratio,
            fsi_interface_closure.max_displacement_transfer_residual_m,
            fsi_interface_closure.max_interface_displacement_m,
            fsi_interface_closure.mean_interface_pressure_pa,
            fsi_interface_closure.max_traction_magnitude_pa,
            fsi_interface_closure.max_coupling_iteration_count,
            fsi_interface_closure.max_pressure_feedback_residual_ratio,
            fsi_interface_closure.max_two_way_interface_residual_ratio,
            fsi_interface_closure.max_structural_traction_update_residual_ratio,
            fsi_interface_closure.max_pressure_displacement_law_residual_ratio,
            fsi_interface_closure.max_structural_solve_residual_ratio,
            fsi_interface_closure.max_interface_work_j_per_m2,
            fsi_interface_closure.max_structural_strain_energy_j_per_m2,
            fsi_interface_closure.max_interface_work_energy_residual_ratio,
            fsi_interface_closure.structural_coupling_edge_count,
            fsi_interface_closure.interface_connectivity_coverage_ratio,
            fsi_interface_closure.mesh_backed_interface_connectivity_ratio,
            fsi_interface_closure.full_topology_edge_count,
            fsi_interface_closure.full_topology_element_count,
            fsi_interface_closure.interface_stiffness_pa_per_m,
        ),
    });
    let fsi_known_answer = fsi_known_answer_metrics(&fsi_interface_closure);
    run.diagnostics.push(fsi_known_answer_diagnostic(
        &fsi_known_answer,
        options.tolerance,
    ));
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_FSI_COUPLING".to_string(),
        severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
        message: format!(
            "step_count={} time_step_s={} structural_step_count={} cfd_profile_point_count={} authored_interface_count={} interface_node_count={} interface_face_count={} max_linear_iters={} tolerance={}",
            options.step_count,
            options.time_step_s,
            model
                .steps
                .iter()
                .filter(|step| step.kind == AnalysisStepKind::Transient)
                .count(),
            cfd_domain.time_profile.len(),
            model.interfaces.len(),
            fsi_interface_closure.interface_node_count,
            fsi_interface_closure.interface_face_count,
            options.max_linear_iters,
            options.tolerance,
        ),
    });
    let solve_ms = solve_start.elapsed().as_secs_f64() * 1000.0;
    run.diagnostics
        .push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
            code: "FEA_FSI_COST".to_string(),
            severity: runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info,
            message: format!(
                "solve_ms={} step_count={} max_linear_iters={} tolerance={}",
                solve_ms, options.step_count, options.max_linear_iters, options.tolerance,
            ),
        });

    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }

    let solver_convergence = if max_interface_residual <= options.residual_warn_threshold {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if run.fields_are_empty()
        || !max_cfd_momentum_residual.is_finite()
        || !max_cfd_continuity_residual.is_finite()
        || !max_interface_residual.is_finite()
    {
        QualityGate::Fail
    } else if max_interface_residual > options.residual_warn_threshold {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "fsi solver convergence gate is warning".to_string(),
        });
    }
    if max_interface_residual > options.residual_warn_threshold {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: format!(
                "fsi interface residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass && result_quality == QualityGate::Pass
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_FSI_OPERATION,
        ANALYSIS_RUN_FSI_OP_VERSION,
        "RM.FEA.RUN_FSI.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_FSI_OPERATION,
        ANALYSIS_RUN_FSI_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_thermal_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisThermalRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_thermal_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Thermal);
    if !has_thermal_step {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one thermal step for fea.run_thermal",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }
    validate_thermal_run_model(model, &context)?;

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_THERMAL_OPERATION,
        ANALYSIS_RUN_THERMAL_OP_VERSION,
        &context,
    )?;
    let Some(thermo_options) = thermo_options else {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_thermal requires model.thermo_mechanical to be configured",
            BTreeMap::new(),
        ));
    };
    if let Err((detail, metadata)) = validate_thermo_coupling_options(model, &thermo_options) {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            detail,
            metadata,
        ));
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_THERMAL_OPERATION,
        ANALYSIS_RUN_THERMAL_OP_VERSION,
        &context,
    )?;

    let thermal_run = run_thermal_with_options(
        model,
        backend,
        ThermalSolveOptions {
            step_count: options.step_count,
            time_step_s: options.time_step_s,
            residual_target: options.residual_warn_threshold,
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(Some(thermo_options)),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            "RM.FEA.RUN_THERMAL.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_THERMAL.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let mut run = thermal_run.run;
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    let solver_convergence = if diagnostic_metric(
        &run.diagnostics,
        "FEA_THERMAL_STABILITY",
        "max_residual_norm",
    )
    .unwrap_or(0.0)
        <= options.residual_warn_threshold
    {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if thermal_run.temperature_snapshots.is_empty() {
        QualityGate::Fail
    } else {
        solver_convergence
    };
    let mut quality_reasons = Vec::new();
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermalResidualExceeded,
            detail: format!(
                "thermal residual exceeds threshold {}",
                options.residual_warn_threshold
            ),
        });
    }
    let thermal_conductivity_spread_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_THERMAL_CONSTITUTIVE",
        "conductivity_spread_ratio",
    );
    let thermal_heat_capacity_spread_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_THERMAL_CONSTITUTIVE",
        "heat_capacity_spread_ratio",
    );
    if thermal_conductivity_spread_ratio.unwrap_or(1.0) > 2.5
        || thermal_heat_capacity_spread_ratio.unwrap_or(1.0) > 2.5
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermalConstitutiveSpreadHigh,
            detail: format!(
                "thermal constitutive spread exceeds limit: conductivity_spread_ratio={} heat_capacity_spread_ratio={}",
                thermal_conductivity_spread_ratio.unwrap_or(1.0),
                thermal_heat_capacity_spread_ratio.unwrap_or(1.0)
            ),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            result_quality != QualityGate::Fail
                && !quality_reasons
                    .iter()
                    .any(|reason| reason.code == QualityReasonCode::ThermalConstitutiveSpreadHigh)
        }
        QualityPolicy::Exploratory => true,
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };

    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: Some(ThermalResultsData {
            thermal_payload_version: "thermal_results/v1".to_string(),
            time_points_s: thermal_run.time_points_s,
            temperature_snapshots: thermal_run.temperature_snapshots,
            temperature_gradient_snapshots: thermal_run.temperature_gradient_snapshots,
            heat_flux_snapshots: thermal_run.heat_flux_snapshots,
            heat_source_snapshots: thermal_run.heat_source_snapshots,
            boundary_heat_flux_snapshots: thermal_run.boundary_heat_flux_snapshots,
            residual_norms: thermal_run.residual_norms,
            reference_temperature_k: thermal_run.reference_temperature_k,
        }),
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_THERMAL_OPERATION,
        ANALYSIS_RUN_THERMAL_OP_VERSION,
        "RM.FEA.RUN_THERMAL.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_THERMAL_OPERATION,
        ANALYSIS_RUN_THERMAL_OP_VERSION,
        &context,
        result,
    ))
}

fn validate_thermal_run_model(
    model: &AnalysisModel,
    context: &OperationContext,
) -> Result<(), OperationErrorEnvelope> {
    if let Some(material) = model.materials.iter().find(|material| {
        !material.thermal.conductivity_w_per_mk.is_finite()
            || material.thermal.conductivity_w_per_mk <= 0.0
            || !material.thermal.specific_heat_j_per_kgk.is_finite()
            || material.thermal.specific_heat_j_per_kgk <= 0.0
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_THERMAL_MATERIAL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_thermal requires finite positive thermal conductivity and specific heat",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("material_id".to_string(), material.material_id.clone()),
                (
                    "conductivity_w_per_mk".to_string(),
                    material.thermal.conductivity_w_per_mk.to_string(),
                ),
                (
                    "specific_heat_j_per_kgk".to_string(),
                    material.thermal.specific_heat_j_per_kgk.to_string(),
                ),
            ]),
        ));
    }

    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_THERMAL_OPERATION,
        ANALYSIS_RUN_THERMAL_OP_VERSION,
        "RM.FEA.RUN_THERMAL.INVALID_THERMAL_SOURCE",
        "thermal",
        context,
    )?;

    if let Some(load) = model.loads.iter().find(|load| {
        matches!(
            &load.kind,
            LoadKind::HeatSource {
                volumetric_w_per_m3
            } if !volumetric_w_per_m3.is_finite()
        )
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_THERMAL_SOURCE",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_thermal requires finite thermal heat-source values",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("load_id".to_string(), load.load_id.clone()),
            ]),
        ));
    }

    if let Some(boundary) = model.boundary_conditions.iter().find(|bc| match &bc.kind {
        BoundaryConditionKind::ThermalPrescribedTemperature { temperature_k } => {
            !temperature_k.is_finite() || *temperature_k <= 0.0
        }
        BoundaryConditionKind::ThermalHeatFlux { heat_flux_w_per_m2 } => {
            !heat_flux_w_per_m2.is_finite()
        }
        BoundaryConditionKind::ThermalConvection {
            ambient_temperature_k,
            coefficient_w_per_m2k,
        } => {
            !ambient_temperature_k.is_finite()
                || *ambient_temperature_k <= 0.0
                || !coefficient_w_per_m2k.is_finite()
                || *coefficient_w_per_m2k < 0.0
        }
        _ => false,
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_THERMAL_OPERATION,
            ANALYSIS_RUN_THERMAL_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMAL.INVALID_THERMAL_BOUNDARY",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_thermal requires finite physically valid thermal boundary condition values",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("boundary_condition_id".to_string(), boundary.bc_id.clone()),
            ]),
        ));
    }

    Ok(())
}

pub fn analysis_run_transient_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisTransientRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_transient_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Transient);
    if !has_transient_step {
        return Err(operation_error(
            ANALYSIS_RUN_TRANSIENT_OPERATION,
            ANALYSIS_RUN_TRANSIENT_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_TRANSIENT.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one transient step for fea.run_transient",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_TRANSIENT_OPERATION,
        ANALYSIS_RUN_TRANSIENT_OP_VERSION,
        &context,
    )?;
    if let Some(thermo_options) = thermo_options.as_ref() {
        if let Err((detail, metadata)) = validate_thermo_coupling_options(model, thermo_options) {
            return Err(operation_error(
                ANALYSIS_RUN_TRANSIENT_OPERATION,
                ANALYSIS_RUN_TRANSIENT_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_TRANSIENT.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let electro_options = model_electro_coupling_options(model);
    if let Some(electro_options) = electro_options.as_ref() {
        if let Err((detail, metadata)) = validate_electro_coupling_options(model, electro_options) {
            return Err(operation_error(
                ANALYSIS_RUN_TRANSIENT_OPERATION,
                ANALYSIS_RUN_TRANSIENT_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: electro_thermal_invalid_options_error_code(
                        ANALYSIS_RUN_TRANSIENT_OPERATION,
                    ),
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_TRANSIENT_OPERATION,
        ANALYSIS_RUN_TRANSIENT_OP_VERSION,
        &context,
    )?;
    let transient_run = run_transient_with_options(
        model,
        backend,
        runmat_analysis_fea::solve::transient::TransientSolveOptions {
            time_step_s: options.time_step_s,
            min_time_step_s: options.min_time_step_s,
            max_time_step_s: options.max_time_step_s,
            step_count: options.step_count,
            max_linear_iters: options.max_linear_iters,
            tolerance: options.tolerance,
            residual_target: options.residual_target,
            adaptive_time_step: options.adaptive_time_step,
            max_step_retries: options.max_step_retries,
            adapt_min_scale: options.adapt_min_scale,
            adapt_max_scale: options.adapt_max_scale,
            adapt_growth_exponent: options.adapt_growth_exponent,
            adapt_retry_growth_cap: options.adapt_retry_growth_cap,
            adapt_nonconverged_shrink: options.adapt_nonconverged_shrink,
            dt_bucket_rel_tolerance: options.dt_bucket_rel_tolerance,
            progress_operation: ANALYSIS_RUN_TRANSIENT_OPERATION.to_string(),
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(thermo_options),
            electro_thermal_context: to_fea_electro_thermal_context(electro_options),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_TRANSIENT_OPERATION,
            ANALYSIS_RUN_TRANSIENT_OP_VERSION,
            "RM.FEA.RUN_TRANSIENT.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_TRANSIENT.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let mut run = transient_run.run;
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }
    let solver_convergence = if run.diagnostics.iter().any(|item| {
        item.code == "FEA_TRANSIENT_CONVERGENCE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    }) {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let result_quality = if transient_run.displacement_snapshots.is_empty()
        || transient_run.time_points_s.is_empty()
        || transient_run
            .residual_norms
            .iter()
            .any(|residual| !residual.is_finite())
    {
        QualityGate::Fail
    } else if transient_run
        .residual_norms
        .iter()
        .copied()
        .fold(0.0_f64, f64::max)
        > TRANSIENT_RESIDUAL_WARN_THRESHOLD
    {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };
    let transient_stability_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_TRANSIENT_STABILITY"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    }) || run.diagnostics.iter().any(|item| {
        item.code == "FEA_TRANSIENT_ENERGY"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let transient_step_failure_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_TRANSIENT_STEP_FAILURE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let thermo_transient_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_TM_TRANSIENT"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let electro_transient_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_ET_TRANSIENT"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let thermo_spatial_gradient_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "spatial_gradient_index",
    );
    let thermo_spatial_coverage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "spatial_coverage_ratio",
    );
    let thermo_temporal_variation =
        diagnostic_metric(&run.diagnostics, "FEA_TM_TRANSIENT", "temporal_variation");
    let thermo_field_extrapolation_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_TRANSIENT",
        "field_extrapolation_ratio",
    );
    let (thermo_gradient_spatial_threshold, thermo_gradient_temporal_threshold) =
        thermo_gradient_thresholds_for_policy(options.quality_policy);
    let thermo_gradient_instability = thermo_spatial_gradient_index
        .map(|value| value > thermo_gradient_spatial_threshold)
        .unwrap_or(false)
        || thermo_temporal_variation
            .map(|value| value > thermo_gradient_temporal_threshold)
            .unwrap_or(false);
    let thermo_spread_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "constitutive_material_spread_ratio",
    );
    let thermo_heterogeneity_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "assignment_heterogeneity_index",
    );
    let (thermo_spread_threshold, thermo_heterogeneity_threshold) =
        thermo_thresholds_for_policy(options.quality_policy);
    let (thermo_field_coverage_min, thermo_field_extrapolation_max) =
        thermo_field_quality_thresholds_for_policy(options.quality_policy);
    let thermo_spread_breach = thermo_spread_ratio
        .map(|value| value > thermo_spread_threshold)
        .unwrap_or(false);
    let thermo_heterogeneity_breach = thermo_heterogeneity_index
        .map(|value| value > thermo_heterogeneity_threshold)
        .unwrap_or(false);
    let thermo_field_coverage_breach = thermo_spatial_coverage_ratio
        .map(|value| value < thermo_field_coverage_min)
        .unwrap_or(false);
    let thermo_field_extrapolation_breach = thermo_field_extrapolation_ratio
        .map(|value| value > thermo_field_extrapolation_max)
        .unwrap_or(false);

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "transient solver convergence gate is warning".to_string(),
        });
    }
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::TransientResidualExceeded,
            detail: format!(
                "transient residual exceeds threshold {}",
                TRANSIENT_RESIDUAL_WARN_THRESHOLD
            ),
        });
    }
    if transient_stability_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::TransientStabilityExceeded,
            detail: "transient stability diagnostic exceeded threshold".to_string(),
        });
    }
    if transient_step_failure_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::TransientStepFailure,
            detail: "transient step retry budget was exhausted".to_string(),
        });
    }
    if thermo_transient_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalTransientStress,
            detail: "thermo-mechanical transient coupling severity exceeded balanced threshold"
                .to_string(),
        });
    }
    if electro_transient_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectroThermalTransientStress,
            detail: "electro-thermal transient coupling severity exceeded balanced threshold"
                .to_string(),
        });
    }
    if thermo_spread_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalConstitutiveSpreadHigh,
            detail: format!(
                "thermo constitutive material spread ratio {} exceeds threshold {}",
                thermo_spread_ratio.unwrap_or(0.0),
                thermo_spread_threshold
            ),
        });
    }
    if thermo_heterogeneity_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalAssignmentHeterogeneityHigh,
            detail: format!(
                "thermo assignment heterogeneity index {} exceeds threshold {}",
                thermo_heterogeneity_index.unwrap_or(0.0),
                thermo_heterogeneity_threshold
            ),
        });
    }
    if thermo_gradient_instability {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalGradientInstability,
            detail: format!(
                "thermo gradient instability spatial_gradient_index={} temporal_variation={} thresholds=({}, {})",
                thermo_spatial_gradient_index.unwrap_or(0.0),
                thermo_temporal_variation.unwrap_or(0.0),
                thermo_gradient_spatial_threshold,
                thermo_gradient_temporal_threshold,
            ),
        });
    }
    if thermo_field_coverage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalFieldCoverageLow,
            detail: format!(
                "thermo field spatial coverage ratio {} is below minimum {}",
                thermo_spatial_coverage_ratio.unwrap_or(0.0),
                thermo_field_coverage_min
            ),
        });
    }
    if thermo_field_extrapolation_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalFieldExtrapolationHigh,
            detail: format!(
                "thermo field extrapolation ratio {} exceeds maximum {}",
                thermo_field_extrapolation_ratio.unwrap_or(0.0),
                thermo_field_extrapolation_max
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }
    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && !quality_reasons.iter().any(|r| {
                    matches!(
                        r.code,
                        QualityReasonCode::TransientStabilityExceeded
                            | QualityReasonCode::TransientStepFailure
                            | QualityReasonCode::ThermoMechanicalTransientStress
                            | QualityReasonCode::ThermoMechanicalConstitutiveSpreadHigh
                            | QualityReasonCode::ThermoMechanicalAssignmentHeterogeneityHigh
                            | QualityReasonCode::ThermoMechanicalGradientInstability
                    )
                })
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };

    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: Some(TransientResultsData {
            transient_payload_version: "transient_results/v1".to_string(),
            time_points_s: transient_run.time_points_s,
            displacement_snapshots: transient_run.displacement_snapshots,
            rotation_snapshots: transient_run.rotation_snapshots,
            velocity_snapshots: transient_run.velocity_snapshots,
            angular_velocity_snapshots: transient_run.angular_velocity_snapshots,
            acceleration_snapshots: transient_run.acceleration_snapshots,
            angular_acceleration_snapshots: transient_run.angular_acceleration_snapshots,
            von_mises_snapshots: transient_run.von_mises_snapshots,
            kinetic_energy_snapshots: transient_run.kinetic_energy_snapshots,
            strain_energy_snapshots: transient_run.strain_energy_snapshots,
            residual_norm_snapshots: transient_run.residual_norm_snapshots,
            thermo_mechanical_temperature_snapshots: transient_run
                .thermo_mechanical_temperature_snapshots,
            thermo_mechanical_thermal_strain_snapshots: transient_run
                .thermo_mechanical_thermal_strain_snapshots,
            thermo_mechanical_thermal_stress_snapshots: transient_run
                .thermo_mechanical_thermal_stress_snapshots,
            thermo_mechanical_displacement_snapshots: transient_run
                .thermo_mechanical_displacement_snapshots,
            thermo_mechanical_von_mises_snapshots: transient_run
                .thermo_mechanical_von_mises_snapshots,
            thermo_mechanical_coupling_residual_snapshots: transient_run
                .thermo_mechanical_coupling_residual_snapshots,
            electro_thermal_temperature_snapshots: transient_run
                .electro_thermal_temperature_snapshots,
            electro_thermal_thermal_residual_snapshots: transient_run
                .electro_thermal_thermal_residual_snapshots,
            residual_norms: transient_run.residual_norms,
            integration_method: TransientIntegrationMethod::ImplicitEuler,
        }),
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_TRANSIENT_OPERATION,
        ANALYSIS_RUN_TRANSIENT_OP_VERSION,
        "RM.FEA.RUN_TRANSIENT.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_TRANSIENT_OPERATION,
        ANALYSIS_RUN_TRANSIENT_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_nonlinear_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_nonlinear_with_options_op(
        model,
        backend,
        AnalysisNonlinearRunOptions::default(),
        context,
    )
}

pub fn analysis_run_nonlinear_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisNonlinearRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_nonlinear_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Nonlinear);
    if !has_nonlinear_step {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one nonlinear step for fea.run_nonlinear",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("geometry_id".to_string(), model.geometry_id.clone()),
            ]),
        ));
    }

    if options.increment_count == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require increment_count greater than zero",
            BTreeMap::from([(
                "increment_count".to_string(),
                options.increment_count.to_string(),
            )]),
        ));
    }
    if options.max_newton_iters == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require max_newton_iters greater than zero",
            BTreeMap::from([(
                "max_newton_iters".to_string(),
                options.max_newton_iters.to_string(),
            )]),
        ));
    }
    if options.tolerance <= 0.0 || !options.tolerance.is_finite() {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require finite positive tolerance",
            BTreeMap::from([("tolerance".to_string(), options.tolerance.to_string())]),
        ));
    }
    if options.increment_norm_tolerance <= 0.0 || !options.increment_norm_tolerance.is_finite() {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require finite positive increment_norm_tolerance",
            BTreeMap::from([(
                "increment_norm_tolerance".to_string(),
                options.increment_norm_tolerance.to_string(),
            )]),
        ));
    }
    if options.residual_convergence_factor < 1.0 || !options.residual_convergence_factor.is_finite()
    {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require residual_convergence_factor >= 1.0",
            BTreeMap::from([(
                "residual_convergence_factor".to_string(),
                options.residual_convergence_factor.to_string(),
            )]),
        ));
    }
    if options.line_search_reduction <= 0.0
        || options.line_search_reduction >= 1.0
        || !options.line_search_reduction.is_finite()
    {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require line_search_reduction in (0, 1)",
            BTreeMap::from([(
                "line_search_reduction".to_string(),
                options.line_search_reduction.to_string(),
            )]),
        ));
    }
    if options.tangent_refresh_interval == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_nonlinear options require tangent_refresh_interval greater than zero",
            BTreeMap::from([(
                "tangent_refresh_interval".to_string(),
                options.tangent_refresh_interval.to_string(),
            )]),
        ));
    }

    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_NONLINEAR_OPERATION,
        ANALYSIS_RUN_NONLINEAR_OP_VERSION,
        &context,
    )?;
    if let Some(thermo_options) = thermo_options.as_ref() {
        if let Err((detail, metadata)) = validate_thermo_coupling_options(model, thermo_options) {
            return Err(operation_error(
                ANALYSIS_RUN_NONLINEAR_OPERATION,
                ANALYSIS_RUN_NONLINEAR_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let electro_options = model_electro_coupling_options(model);
    if let Some(electro_options) = electro_options.as_ref() {
        if let Err((detail, metadata)) = validate_electro_coupling_options(model, electro_options) {
            return Err(operation_error(
                ANALYSIS_RUN_NONLINEAR_OPERATION,
                ANALYSIS_RUN_NONLINEAR_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: electro_thermal_invalid_options_error_code(
                        ANALYSIS_RUN_NONLINEAR_OPERATION,
                    ),
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let plasticity_options = model_plasticity_constitutive_options(model);
    if let Some(plasticity_options) = plasticity_options.as_ref() {
        if let Err((detail, metadata)) =
            validate_plasticity_constitutive_options(plasticity_options)
        {
            return Err(operation_error(
                ANALYSIS_RUN_NONLINEAR_OPERATION,
                ANALYSIS_RUN_NONLINEAR_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let contact_options = model_contact_interface_options(model);
    if let Some(contact_options) = contact_options.as_ref() {
        if let Err((detail, metadata)) = validate_contact_interface_options(contact_options) {
            return Err(operation_error(
                ANALYSIS_RUN_NONLINEAR_OPERATION,
                ANALYSIS_RUN_NONLINEAR_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_NONLINEAR.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_NONLINEAR_OPERATION,
        ANALYSIS_RUN_NONLINEAR_OP_VERSION,
        &context,
    )?;
    let nonlinear_run = run_nonlinear_with_options(
        model,
        backend,
        runmat_analysis_fea::solve::nonlinear::NonlinearSolveOptions {
            increment_count: options.increment_count,
            max_newton_iters: options.max_newton_iters,
            tolerance: options.tolerance,
            residual_convergence_factor: options.residual_convergence_factor,
            increment_norm_tolerance: options.increment_norm_tolerance,
            line_search: options.line_search,
            max_line_search_backtracks: options.max_line_search_backtracks,
            line_search_reduction: options.line_search_reduction,
            tangent_refresh_interval: options.tangent_refresh_interval,
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(thermo_options),
            electro_thermal_context: to_fea_electro_thermal_context(electro_options),
            plasticity_context: to_fea_plasticity_constitutive_context(plasticity_options),
            contact_context: to_fea_contact_interface_context(contact_options),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_NONLINEAR_OPERATION,
            ANALYSIS_RUN_NONLINEAR_OP_VERSION,
            "RM.FEA.RUN_NONLINEAR.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_NONLINEAR.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let mut run = nonlinear_run.run;
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);
    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }

    let solver_convergence = if run.diagnostics.iter().any(|item| {
        item.code == "FEA_NONLINEAR_CONVERGENCE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    }) {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let max_nonlinear_residual = nonlinear_run
        .residual_norms
        .iter()
        .copied()
        .reduce(f64::max)
        .unwrap_or(0.0);
    let max_nonlinear_increment_norm = nonlinear_run
        .increment_norms
        .iter()
        .copied()
        .reduce(f64::max)
        .unwrap_or(0.0);
    let result_quality = if nonlinear_run.load_factors.is_empty()
        || nonlinear_run.displacement_snapshots.is_empty()
        || nonlinear_run.residual_norms.iter().any(|r| !r.is_finite())
        || nonlinear_run.increment_norms.iter().any(|v| !v.is_finite())
    {
        QualityGate::Fail
    } else if max_nonlinear_residual > options.tolerance * options.residual_convergence_factor * 2.0
        || max_nonlinear_increment_norm > options.increment_norm_tolerance * 4.0
    {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };
    let nonlinear_increment_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_NONLINEAR_CONVERGENCE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let max_nonlinear_iteration_count = nonlinear_run
        .iteration_counts
        .iter()
        .copied()
        .max()
        .unwrap_or(0);
    let iteration_cap_hits = nonlinear_run
        .iteration_counts
        .iter()
        .filter(|&&count| count >= options.max_newton_iters.max(1))
        .count();
    let strict_increment_failure = nonlinear_run.failed_increments > 0;
    let strict_iteration_cap_exhausted = iteration_cap_hits > 0;
    let thermo_nonlinear_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_TM_NONLINEAR"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let electro_nonlinear_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_ET_NONLINEAR"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let plastic_nonlinear_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_PLASTIC_NONLINEAR"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let contact_nonlinear_warn = run.diagnostics.iter().any(|item| {
        item.code == "FEA_CONTACT_NONLINEAR"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    });
    let thermo_spatial_gradient_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "spatial_gradient_index",
    );
    let thermo_spatial_coverage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "spatial_coverage_ratio",
    );
    let thermo_temporal_variation =
        diagnostic_metric(&run.diagnostics, "FEA_TM_NONLINEAR", "temporal_variation");
    let thermo_field_extrapolation_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_NONLINEAR",
        "field_extrapolation_ratio",
    );
    let (thermo_gradient_spatial_threshold, thermo_gradient_temporal_threshold) =
        thermo_gradient_thresholds_for_policy(options.quality_policy);
    let thermo_gradient_instability = thermo_spatial_gradient_index
        .map(|value| value > thermo_gradient_spatial_threshold)
        .unwrap_or(false)
        || thermo_temporal_variation
            .map(|value| value > thermo_gradient_temporal_threshold)
            .unwrap_or(false);
    let thermo_spread_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "constitutive_material_spread_ratio",
    );
    let thermo_heterogeneity_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_TM_COUPLING",
        "assignment_heterogeneity_index",
    );
    let (thermo_spread_threshold, thermo_heterogeneity_threshold) =
        thermo_thresholds_for_policy(options.quality_policy);
    let (thermo_field_coverage_min, thermo_field_extrapolation_max) =
        thermo_field_quality_thresholds_for_policy(options.quality_policy);
    let thermo_spread_breach = thermo_spread_ratio
        .map(|value| value > thermo_spread_threshold)
        .unwrap_or(false);
    let thermo_heterogeneity_breach = thermo_heterogeneity_index
        .map(|value| value > thermo_heterogeneity_threshold)
        .unwrap_or(false);
    let thermo_field_coverage_breach = thermo_spatial_coverage_ratio
        .map(|value| value < thermo_field_coverage_min)
        .unwrap_or(false);
    let thermo_field_extrapolation_breach = thermo_field_extrapolation_ratio
        .map(|value| value > thermo_field_extrapolation_max)
        .unwrap_or(false);

    let mut quality_reasons = Vec::new();
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "nonlinear solver convergence gate is warning".to_string(),
        });
    }
    if result_quality == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::NonlinearResidualExceeded,
            detail: format!(
                "nonlinear residual/increment norm exceeds thresholds residual={} increment_norm={}",
                options.tolerance * options.residual_convergence_factor * 2.0,
                options.increment_norm_tolerance * 4.0
            ),
        });
    }
    if nonlinear_increment_warn || strict_increment_failure || strict_iteration_cap_exhausted {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::NonlinearIncrementFailure,
            detail: format!(
                "nonlinear increment convergence warnings failed_increments={} iteration_cap_hits={} max_iteration_count={}",
                nonlinear_run.failed_increments,
                iteration_cap_hits,
                max_nonlinear_iteration_count
            ),
        });
    }
    if thermo_nonlinear_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalNonlinearStress,
            detail: "thermo-mechanical nonlinear coupling severity exceeded balanced threshold"
                .to_string(),
        });
    }
    if electro_nonlinear_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectroThermalNonlinearStress,
            detail: "electro-thermal nonlinear coupling severity exceeded balanced threshold"
                .to_string(),
        });
    }
    if plastic_nonlinear_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::PlasticityNonlinearStress,
            detail: "plasticity nonlinear severity exceeded balanced threshold".to_string(),
        });
    }
    if contact_nonlinear_warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ContactNonlinearStress,
            detail: "contact nonlinear severity exceeded balanced threshold".to_string(),
        });
    }
    if thermo_spread_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalConstitutiveSpreadHigh,
            detail: format!(
                "thermo constitutive material spread ratio {} exceeds threshold {}",
                thermo_spread_ratio.unwrap_or(0.0),
                thermo_spread_threshold
            ),
        });
    }
    if thermo_heterogeneity_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalAssignmentHeterogeneityHigh,
            detail: format!(
                "thermo assignment heterogeneity index {} exceeds threshold {}",
                thermo_heterogeneity_index.unwrap_or(0.0),
                thermo_heterogeneity_threshold
            ),
        });
    }
    if thermo_gradient_instability {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalGradientInstability,
            detail: format!(
                "thermo gradient instability spatial_gradient_index={} temporal_variation={} thresholds=({}, {})",
                thermo_spatial_gradient_index.unwrap_or(0.0),
                thermo_temporal_variation.unwrap_or(0.0),
                thermo_gradient_spatial_threshold,
                thermo_gradient_temporal_threshold,
            ),
        });
    }
    if thermo_field_coverage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalFieldCoverageLow,
            detail: format!(
                "thermo field spatial coverage ratio {} is below minimum {}",
                thermo_spatial_coverage_ratio.unwrap_or(0.0),
                thermo_field_coverage_min
            ),
        });
    }
    if thermo_field_extrapolation_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ThermoMechanicalFieldExtrapolationHigh,
            detail: format!(
                "thermo field extrapolation ratio {} exceeds maximum {}",
                thermo_field_extrapolation_ratio.unwrap_or(0.0),
                thermo_field_extrapolation_max
            ),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && !strict_increment_failure
                && !strict_iteration_cap_exhausted
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && !quality_reasons.iter().any(|r| {
                    matches!(
                        r.code,
                        QualityReasonCode::NonlinearResidualExceeded
                            | QualityReasonCode::NonlinearIncrementFailure
                            | QualityReasonCode::ThermoMechanicalNonlinearStress
                            | QualityReasonCode::PlasticityNonlinearStress
                            | QualityReasonCode::ContactNonlinearStress
                            | QualityReasonCode::ThermoMechanicalConstitutiveSpreadHigh
                            | QualityReasonCode::ThermoMechanicalAssignmentHeterogeneityHigh
                            | QualityReasonCode::ThermoMechanicalGradientInstability
                    )
                })
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };

    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let selected_preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: Some(NonlinearResultsData {
            nonlinear_payload_version: "nonlinear_results/v1".to_string(),
            load_factors: nonlinear_run.load_factors,
            displacement_snapshots: nonlinear_run.displacement_snapshots,
            rotation_snapshots: nonlinear_run.rotation_snapshots,
            von_mises_snapshots: nonlinear_run.von_mises_snapshots,
            plastic_strain_snapshots: nonlinear_run.plastic_strain_snapshots,
            equivalent_plastic_strain_snapshots: nonlinear_run.equivalent_plastic_strain_snapshots,
            contact_pressure_snapshots: nonlinear_run.contact_pressure_snapshots,
            contact_gap_snapshots: nonlinear_run.contact_gap_snapshots,
            load_factor_snapshots: nonlinear_run.load_factor_snapshots,
            residual_norm_snapshots: nonlinear_run.residual_norm_snapshots,
            thermo_mechanical_temperature_snapshots: nonlinear_run
                .thermo_mechanical_temperature_snapshots,
            thermo_mechanical_thermal_strain_snapshots: nonlinear_run
                .thermo_mechanical_thermal_strain_snapshots,
            thermo_mechanical_thermal_stress_snapshots: nonlinear_run
                .thermo_mechanical_thermal_stress_snapshots,
            thermo_mechanical_displacement_snapshots: nonlinear_run
                .thermo_mechanical_displacement_snapshots,
            thermo_mechanical_von_mises_snapshots: nonlinear_run
                .thermo_mechanical_von_mises_snapshots,
            thermo_mechanical_coupling_residual_snapshots: nonlinear_run
                .thermo_mechanical_coupling_residual_snapshots,
            electro_thermal_temperature_snapshots: nonlinear_run
                .electro_thermal_temperature_snapshots,
            electro_thermal_thermal_residual_snapshots: nonlinear_run
                .electro_thermal_thermal_residual_snapshots,
            residual_norms: nonlinear_run.residual_norms,
            increment_norms: nonlinear_run.increment_norms,
            iteration_counts: nonlinear_run.iteration_counts,
            failed_increments: nonlinear_run.failed_increments,
            line_search_backtracks: nonlinear_run.line_search_backtracks,
            max_line_search_backtracks_per_increment: nonlinear_run
                .max_line_search_backtracks_per_increment,
            tangent_rebuild_count: nonlinear_run.tangent_rebuild_count,
            iteration_spike_count: nonlinear_run.iteration_spike_count,
            convergence_stall_count: nonlinear_run.convergence_stall_count,
            backtrack_burst_count: nonlinear_run.backtrack_burst_count,
            method: NonlinearMethod::IncrementalNewtonRaphson,
        }),
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner: selected_preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_NONLINEAR_OPERATION,
        ANALYSIS_RUN_NONLINEAR_OP_VERSION,
        "RM.FEA.RUN_NONLINEAR.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_NONLINEAR_OPERATION,
        ANALYSIS_RUN_NONLINEAR_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_linear_static_with_options(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let thermo_options = resolve_thermo_coupling_options(
        model,
        model_thermo_coupling_options(model),
        ANALYSIS_RUN_OPERATION,
        ANALYSIS_RUN_OP_VERSION,
        &context,
    )?;
    if let Some(thermo_options) = thermo_options.as_ref() {
        if let Err((detail, metadata)) = validate_thermo_coupling_options(model, thermo_options) {
            return Err(operation_error(
                ANALYSIS_RUN_OPERATION,
                ANALYSIS_RUN_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_LINEAR_STATIC.INVALID_OPTIONS",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }
    let electro_options = model_electro_coupling_options(model);
    if let Some(electro_options) = electro_options.as_ref() {
        if let Err((detail, metadata)) = validate_electro_coupling_options(model, electro_options) {
            return Err(operation_error(
                ANALYSIS_RUN_OPERATION,
                ANALYSIS_RUN_OP_VERSION,
                &context,
                OperationErrorSpec {
                    error_code: electro_thermal_invalid_options_error_code(ANALYSIS_RUN_OPERATION),
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                detail,
                metadata,
            ));
        }
    }

    let requested_preconditioner = match options.preconditioner_mode {
        PreconditionerMode::Auto | PreconditionerMode::Jacobi => SpdPreconditionerKind::Jacobi,
        PreconditionerMode::Ilu => SpdPreconditionerKind::Ilu0,
        PreconditionerMode::Amg => SpdPreconditionerKind::Jacobi,
    };
    let runtime_tensor_available = runmat_accelerate_api::provider().is_some();
    let requested_solver_backend = match backend {
        ComputeBackend::Cpu => LinearAlgebraBackendKind::CpuReference,
        ComputeBackend::Gpu => {
            if runtime_tensor_available {
                LinearAlgebraBackendKind::RuntimeTensor
            } else {
                LinearAlgebraBackendKind::CpuReference
            }
        }
    };
    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_OPERATION,
        ANALYSIS_RUN_OP_VERSION,
        &context,
    )?;
    let run = run_linear_static_with_options(
        model,
        backend,
        LinearStaticSolveOptions {
            preconditioner_kind: requested_preconditioner,
            algebra_backend_kind: requested_solver_backend,
            prep_context: to_fea_prep_context(
                prep_context.as_ref(),
                options.prep_calibration_profile,
            ),
            thermo_mechanical_context: to_fea_thermo_mechanical_context(thermo_options),
            electro_thermal_context: to_fea_electro_thermal_context(electro_options),
        },
    )
    .map_err(|err| {
        map_fea_run_error(
            ANALYSIS_RUN_OPERATION,
            ANALYSIS_RUN_OP_VERSION,
            "RM.FEA.RUN_LINEAR_STATIC.SOLVER_MODEL_INVALID",
            "RM.FEA.RUN_LINEAR_STATIC.CANCELLED",
            model,
            &context,
            err,
        )
    })?;

    let mut run = run;
    let mut fallback_events = Vec::new();
    promotion::promote_run_fields_to_device_refs(&mut run, &mut fallback_events);

    match options.preconditioner_mode {
        PreconditionerMode::Auto | PreconditionerMode::Jacobi | PreconditionerMode::Ilu => {}
        PreconditionerMode::Amg => {
            fallback_events
                .push("SOLVER_PRECONDITIONER_FALLBACK:requested=amg:using=jacobi".to_string());
        }
    }

    if backend == ComputeBackend::Gpu && run.solver_backend != "runtime_tensor" {
        fallback_events.push(
            "SOLVER_BACKEND_FALLBACK:requested=runtime_tensor:using=cpu_reference".to_string(),
        );
    }

    let solver_convergence = if run.diagnostics.iter().any(|item| {
        item.code == "FEA_CONVERGENCE"
            && item.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    }) {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };

    let has_material_assignment_conflict = run.diagnostics.iter().any(|diag| {
        diag.code
            .starts_with("ANALYSIS_MATERIAL_ASSIGNMENT_CONFLICT_")
    });
    let result_quality = if run.fields_are_empty() {
        QualityGate::Fail
    } else if has_material_assignment_conflict {
        QualityGate::Warn
    } else {
        QualityGate::Pass
    };

    let mut quality_reasons = Vec::new();
    if has_material_assignment_conflict {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::MaterialAssignmentConflict,
            detail: "material assignment confidence conflict detected".to_string(),
        });
    }
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "solver convergence gate is warning".to_string(),
        });
    }
    if fallback_events
        .iter()
        .any(|event| event.starts_with("SOLVER_BACKEND_FALLBACK"))
    {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverBackendFallback,
            detail: "solver backend fell back from runtime_tensor to cpu_reference".to_string(),
        });
    }
    if fallback_events.iter().any(|event| {
        event.starts_with("BACKEND_NO_PROVIDER") || event.starts_with("BACKEND_UPLOAD_FAILED")
    }) {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::FieldPromotionFallback,
            detail: "field promotion fell back to host-backed values".to_string(),
        });
    }
    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass && result_quality == QualityGate::Pass
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: None,
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events,
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_OPERATION,
        ANALYSIS_RUN_OP_VERSION,
        "RM.FEA.RUN_LINEAR_STATIC.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_OPERATION,
        ANALYSIS_RUN_OP_VERSION,
        &context,
        result,
    ))
}

pub fn analysis_run_electromagnetic_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    analysis_run_electromagnetic_with_options_op(
        model,
        backend,
        AnalysisElectromagneticRunOptions::default(),
        context,
    )
}

pub fn analysis_run_electromagnetic_with_options_op(
    model: &AnalysisModel,
    backend: ComputeBackend,
    options: AnalysisElectromagneticRunOptions,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisRunResult>, OperationErrorEnvelope> {
    let _solver_context = install_fea_solver_context();
    let has_electromagnetic_step = model
        .steps
        .iter()
        .any(|step| step.kind == AnalysisStepKind::Electromagnetic);
    if !has_electromagnetic_step {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.REQUIRES_STEP",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "FEA model must include at least one electromagnetic step for fea.run_electromagnetic",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    }

    let Some(em_domain) = model.electromagnetic.as_ref() else {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_MODEL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires model.electromagnetic to be configured",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    };
    if !em_domain.enabled {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires electromagnetic domain enabled=true",
            BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
        ));
    }
    if !em_domain.reference_frequency_hz.is_finite() || em_domain.reference_frequency_hz <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires finite positive reference_frequency_hz",
            BTreeMap::from([(
                "reference_frequency_hz".to_string(),
                em_domain.reference_frequency_hz.to_string(),
            )]),
        ));
    }
    if !em_domain.applied_current_a.is_finite() || em_domain.applied_current_a <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires finite positive applied_current_a",
            BTreeMap::from([(
                "applied_current_a".to_string(),
                em_domain.applied_current_a.to_string(),
            )]),
        ));
    }
    if !options.residual_target.is_finite() || options.residual_target <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires residual_target to be finite and positive",
            BTreeMap::from([(
                "residual_target".to_string(),
                options.residual_target.to_string(),
            )]),
        ));
    }
    if !options.harmonic_tolerance.is_finite() || options.harmonic_tolerance <= 0.0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires harmonic_tolerance to be finite and positive",
            BTreeMap::from([(
                "harmonic_tolerance".to_string(),
                options.harmonic_tolerance.to_string(),
            )]),
        ));
    }
    if options.harmonic_max_iterations == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires harmonic_max_iterations greater than zero",
            BTreeMap::from([(
                "harmonic_max_iterations".to_string(),
                options.harmonic_max_iterations.to_string(),
            )]),
        ));
    }
    validate_electromagnetic_run_model(model, &context)?;

    let prep_context = resolve_run_prep_context(
        model,
        options.prep_artifact_id.as_deref(),
        options.prep_context.clone(),
        ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
        ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
        &context,
    )?;

    let sweep_frequency_hz = normalize_em_sweep_frequency_hz(
        em_domain.reference_frequency_hz,
        options.sweep_enabled,
        &options.sweep_frequency_hz,
    )
    .ok_or_else(|| {
        operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_OPTIONS",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic sweep_frequency_hz must contain finite positive values",
            BTreeMap::new(),
        )
    })?;
    let solve_options = ElectromagneticSolveOptions {
        prep_context: to_fea_prep_context(prep_context.as_ref(), options.prep_calibration_profile),
        residual_target: options.residual_target,
        harmonic_tolerance: options.harmonic_tolerance,
        harmonic_max_iterations: options.harmonic_max_iterations,
    };
    let mut sweep_runs = Vec::with_capacity(sweep_frequency_hz.len());
    let mut sweep_peak_flux_density = Vec::with_capacity(sweep_frequency_hz.len());
    let mut sweep_solve_quality = Vec::with_capacity(sweep_frequency_hz.len());
    for frequency_hz in &sweep_frequency_hz {
        let mut sweep_model = model.clone();
        if let Some(domain) = sweep_model.electromagnetic.as_mut() {
            domain.reference_frequency_hz = *frequency_hz;
        }
        let sweep_run =
            run_electromagnetic_with_options(&sweep_model, backend, solve_options.clone())
                .map_err(|err| {
                    map_fea_run_error(
                        ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
                        ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
                        "RM.FEA.RUN_ELECTROMAGNETIC.SOLVER_MODEL_INVALID",
                        "RM.FEA.RUN_ELECTROMAGNETIC.CANCELLED",
                        model,
                        &context,
                        err,
                    )
                })?;
        sweep_peak_flux_density.push(peak_abs_field_value(
            &sweep_run.magnetic_flux_density_magnitude_field,
        ));
        sweep_solve_quality.push(sweep_run.solve_quality);
        sweep_runs.push(sweep_run);
    }
    let sweep_metrics = summarize_em_sweep(&sweep_frequency_hz, &sweep_peak_flux_density);
    let primary_index =
        nearest_frequency_index(&sweep_frequency_hz, em_domain.reference_frequency_hz).unwrap_or(0);
    let em_run = sweep_runs[primary_index].clone();
    let mut run = em_run.run.clone();
    run.diagnostics.push(runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_EM_SWEEP".to_string(),
        severity: if sweep_metrics.sweep_count > 1 {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
        } else {
            runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
        },
        message: format!(
            "sweep_count={} resonance_peak_frequency_hz={} resonance_peak_flux_density={} resonance_bandwidth_hz={} resonance_quality_factor={} resonance_flux_gain={}",
            sweep_metrics.sweep_count,
            sweep_metrics.resonance_peak_frequency_hz.unwrap_or(0.0),
            sweep_metrics.resonance_peak_flux_density.unwrap_or(0.0),
            sweep_metrics.resonance_bandwidth_hz.unwrap_or(0.0),
            sweep_metrics.resonance_quality_factor.unwrap_or(0.0),
            sweep_metrics.resonance_flux_gain.unwrap_or(0.0),
        ),
    });
    if sweep_metrics.sweep_count > 1 {
        run.diagnostics.push(em_sweep_known_answer_diagnostic(
            em_domain.reference_frequency_hz,
            &sweep_frequency_hz,
            &sweep_metrics,
        ));
    }
    let solver_convergence = if run.diagnostics.iter().any(|diag| {
        diag.code == "FEA_EM_STATIC"
            && diag.severity == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    }) {
        QualityGate::Pass
    } else {
        QualityGate::Warn
    };
    let mut result_quality = if em_run.solve_quality >= 0.85 {
        QualityGate::Pass
    } else if em_run.solve_quality >= 0.6 {
        QualityGate::Warn
    } else {
        QualityGate::Fail
    };
    let mut quality_reasons = Vec::new();
    let em_conductivity_spread_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "conductivity_spread_ratio",
    );
    let em_assignment_heterogeneity_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "electromagnetic_material_heterogeneity_index",
    );
    let em_assignment_coverage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "assignment_coverage_ratio",
    );
    let em_assigned_coefficient_coverage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "assigned_coefficient_coverage_ratio",
    );
    let em_region_contrast_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "region_coefficient_contrast_index",
    );
    let em_condition_number_estimate = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_STATIC",
        "condition_number_estimate",
    );
    let em_source_realization_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_realization_ratio",
    );
    let em_source_region_coverage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_region_coverage_ratio",
    );
    let em_source_material_alignment_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_material_alignment_ratio",
    );
    let em_source_overlap_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_overlap_ratio",
    );
    let em_source_interference_index = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_interference_index",
    );
    let em_boundary_anchor_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_anchor_ratio",
    );
    let em_boundary_condition_localization_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_condition_localization_ratio",
    );
    let em_ground_anchor_effectiveness_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "ground_anchor_effectiveness_ratio",
    );
    let em_insulation_leakage_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "insulation_leakage_ratio",
    );
    let em_flux_divergence_ratio =
        diagnostic_metric(&run.diagnostics, "FEA_EM_STATIC", "flux_divergence_ratio");
    let em_energy_imbalance_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "energy_imbalance_ratio",
    );
    let em_boundary_energy_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_energy_ratio",
    );
    let em_boundary_penalty_conditioning_contribution = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_penalty_conditioning_contribution",
    );
    let em_source_region_energy_consistency_ratio = diagnostic_metric(
        &run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_region_energy_consistency_ratio",
    );
    let em_real_residual_norm =
        diagnostic_metric(&run.diagnostics, "FEA_EM_STATIC", "real_residual_norm");
    let em_imag_residual_norm =
        diagnostic_metric(&run.diagnostics, "FEA_EM_STATIC", "imag_residual_norm");
    let em_sweep_count = diagnostic_metric(&run.diagnostics, "FEA_EM_SWEEP", "sweep_count");
    let em_resonance_quality_factor =
        diagnostic_metric(&run.diagnostics, "FEA_EM_SWEEP", "resonance_quality_factor");
    let ElectromagneticQualityThresholds {
        em_spread_threshold,
        em_heterogeneity_threshold,
        em_coverage_min_threshold,
        em_contrast_max_threshold,
        em_conditioning_max_threshold,
        em_source_realization_min_threshold,
        em_source_region_coverage_min_threshold,
        em_source_material_alignment_min_threshold,
        em_source_overlap_max_threshold,
        em_source_interference_max_threshold,
        em_boundary_anchor_min_threshold,
        em_boundary_localization_min_threshold,
        em_ground_effectiveness_min_threshold,
        em_insulation_leakage_max_threshold,
        em_divergence_max_threshold,
        em_energy_imbalance_max_threshold,
        em_boundary_energy_min_threshold,
        em_boundary_penalty_contribution_max_threshold,
        em_source_region_energy_consistency_min_threshold,
        em_real_residual_max_threshold,
        em_imag_residual_max_threshold,
    } = electromagnetic_thresholds_for_policy(options.quality_policy);
    let (em_sweep_count_min_threshold, em_resonance_q_min_threshold) =
        electromagnetic_sweep_thresholds_for_policy(options.quality_policy);
    let em_spread_breach = em_conductivity_spread_ratio
        .map(|value| value > em_spread_threshold)
        .unwrap_or(false);
    let em_heterogeneity_breach = em_assignment_heterogeneity_index
        .map(|value| value > em_heterogeneity_threshold)
        .unwrap_or(false);
    let em_coverage_breach = em_assignment_coverage_ratio
        .map(|value| value < em_coverage_min_threshold)
        .unwrap_or(false);
    let em_assigned_coefficient_breach = em_assigned_coefficient_coverage_ratio
        .map(|value| value < em_coverage_min_threshold)
        .unwrap_or(false);
    let em_contrast_breach = em_region_contrast_index
        .map(|value| value > em_contrast_max_threshold)
        .unwrap_or(false);
    let em_conditioning_breach = em_condition_number_estimate
        .map(|value| value > em_conditioning_max_threshold)
        .unwrap_or(false);
    let em_source_realization_breach = em_source_realization_ratio
        .map(|value| value < em_source_realization_min_threshold)
        .unwrap_or(false);
    let em_source_region_coverage_breach = em_source_region_coverage_ratio
        .map(|value| value < em_source_region_coverage_min_threshold)
        .unwrap_or(false);
    let em_source_material_alignment_breach = em_source_material_alignment_ratio
        .map(|value| value < em_source_material_alignment_min_threshold)
        .unwrap_or(false);
    let em_source_overlap_breach = em_source_overlap_ratio
        .map(|value| value > em_source_overlap_max_threshold)
        .unwrap_or(false);
    let em_source_interference_breach = em_source_interference_index
        .map(|value| value > em_source_interference_max_threshold)
        .unwrap_or(false);
    let em_boundary_anchor_breach = em_boundary_anchor_ratio
        .map(|value| value < em_boundary_anchor_min_threshold)
        .unwrap_or(false);
    let em_boundary_localization_breach = em_boundary_condition_localization_ratio
        .map(|value| value < em_boundary_localization_min_threshold)
        .unwrap_or(false);
    let em_ground_effectiveness_breach = em_ground_anchor_effectiveness_ratio
        .map(|value| value < em_ground_effectiveness_min_threshold)
        .unwrap_or(false);
    let em_insulation_leakage_breach = em_insulation_leakage_ratio
        .map(|value| value > em_insulation_leakage_max_threshold)
        .unwrap_or(false);
    let em_divergence_breach = em_flux_divergence_ratio
        .map(|value| value > em_divergence_max_threshold)
        .unwrap_or(false);
    let em_energy_imbalance_breach = em_energy_imbalance_ratio
        .map(|value| value > em_energy_imbalance_max_threshold)
        .unwrap_or(false);
    let em_boundary_energy_breach = em_boundary_energy_ratio
        .map(|value| value < em_boundary_energy_min_threshold)
        .unwrap_or(false);
    let em_boundary_penalty_contribution_breach = em_boundary_penalty_conditioning_contribution
        .map(|value| value > em_boundary_penalty_contribution_max_threshold)
        .unwrap_or(false);
    let em_source_region_energy_consistency_breach = em_source_region_energy_consistency_ratio
        .map(|value| value < em_source_region_energy_consistency_min_threshold)
        .unwrap_or(false);
    let em_real_residual_breach = em_real_residual_norm
        .map(|value| value > em_real_residual_max_threshold)
        .unwrap_or(false);
    let em_imag_residual_breach = em_imag_residual_norm
        .map(|value| value > em_imag_residual_max_threshold)
        .unwrap_or(false);
    let sweep_governance_active = options.sweep_enabled || !options.sweep_frequency_hz.is_empty();
    let em_sweep_coverage_breach = sweep_governance_active
        && em_sweep_count
            .map(|value| value < em_sweep_count_min_threshold)
            .unwrap_or(false);
    let em_resonance_sharpness_breach = sweep_governance_active
        && em_resonance_quality_factor
            .map(|value| value < em_resonance_q_min_threshold)
            .unwrap_or(false);
    if (em_spread_breach
        || em_heterogeneity_breach
        || em_coverage_breach
        || em_assigned_coefficient_breach
        || em_contrast_breach
        || em_conditioning_breach
        || em_source_realization_breach
        || em_source_region_coverage_breach
        || em_source_material_alignment_breach
        || em_source_overlap_breach
        || em_source_interference_breach
        || em_boundary_anchor_breach
        || em_boundary_localization_breach
        || em_ground_effectiveness_breach
        || em_insulation_leakage_breach
        || em_divergence_breach
        || em_energy_imbalance_breach
        || em_boundary_energy_breach
        || em_boundary_penalty_contribution_breach
        || em_source_region_energy_consistency_breach
        || em_real_residual_breach
        || em_imag_residual_breach
        || em_sweep_coverage_breach
        || em_resonance_sharpness_breach)
        && result_quality == QualityGate::Pass
    {
        result_quality = QualityGate::Warn;
    }
    if solver_convergence == QualityGate::Warn {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::SolverNotConverged,
            detail: "electromagnetic solver convergence gate is warning".to_string(),
        });
    }
    if result_quality != QualityGate::Pass {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSolveQualityLow,
            detail: "electromagnetic static solve quality below production target".to_string(),
        });
    }
    if em_spread_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticConductivitySpreadHigh,
            detail: format!(
                "electromagnetic conductivity spread ratio {} exceeds threshold {}",
                em_conductivity_spread_ratio.unwrap_or(0.0),
                em_spread_threshold
            ),
        });
    }
    if em_heterogeneity_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticMaterialHeterogeneityHigh,
            detail: format!(
                "electromagnetic material heterogeneity index {} exceeds threshold {}",
                em_assignment_heterogeneity_index.unwrap_or(0.0),
                em_heterogeneity_threshold
            ),
        });
    }
    if em_coverage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticAssignmentCoverageLow,
            detail: format!(
                "electromagnetic assignment coverage ratio {} is below threshold {}",
                em_assignment_coverage_ratio.unwrap_or(0.0),
                em_coverage_min_threshold
            ),
        });
    }
    if em_assigned_coefficient_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticAssignmentCoverageLow,
            detail: format!(
                "electromagnetic assigned coefficient coverage ratio {} is below threshold {}",
                em_assigned_coefficient_coverage_ratio.unwrap_or(0.0),
                em_coverage_min_threshold
            ),
        });
    }
    if em_contrast_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticRegionContrastHigh,
            detail: format!(
                "electromagnetic region coefficient contrast index {} exceeds threshold {}",
                em_region_contrast_index.unwrap_or(0.0),
                em_contrast_max_threshold
            ),
        });
    }
    if em_conditioning_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticConditioningHigh,
            detail: format!(
                "electromagnetic condition-number estimate {} exceeds threshold {}",
                em_condition_number_estimate.unwrap_or(0.0),
                em_conditioning_max_threshold
            ),
        });
    }
    if em_source_realization_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceRealizationLow,
            detail: format!(
                "electromagnetic source realization ratio {} is below threshold {}",
                em_source_realization_ratio.unwrap_or(0.0),
                em_source_realization_min_threshold
            ),
        });
    }
    if em_source_region_coverage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceRegionCoverageLow,
            detail: format!(
                "electromagnetic source region coverage ratio {} is below threshold {}",
                em_source_region_coverage_ratio.unwrap_or(0.0),
                em_source_region_coverage_min_threshold
            ),
        });
    }
    if em_source_material_alignment_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceMaterialAlignmentLow,
            detail: format!(
                "electromagnetic source material alignment ratio {} is below threshold {}",
                em_source_material_alignment_ratio.unwrap_or(0.0),
                em_source_material_alignment_min_threshold
            ),
        });
    }
    if em_source_overlap_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceOverlapHigh,
            detail: format!(
                "electromagnetic source overlap ratio {} exceeds threshold {}",
                em_source_overlap_ratio.unwrap_or(0.0),
                em_source_overlap_max_threshold
            ),
        });
    }
    if em_source_interference_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceInterferenceHigh,
            detail: format!(
                "electromagnetic source interference index {} exceeds threshold {}",
                em_source_interference_index.unwrap_or(0.0),
                em_source_interference_max_threshold
            ),
        });
    }
    if em_boundary_anchor_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticBoundaryAnchoringLow,
            detail: format!(
                "electromagnetic boundary anchor ratio {} is below threshold {}",
                em_boundary_anchor_ratio.unwrap_or(0.0),
                em_boundary_anchor_min_threshold
            ),
        });
    }
    if em_boundary_localization_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticBoundaryLocalizationLow,
            detail: format!(
                "electromagnetic boundary condition localization ratio {} is below threshold {}",
                em_boundary_condition_localization_ratio.unwrap_or(0.0),
                em_boundary_localization_min_threshold
            ),
        });
    }
    if em_ground_effectiveness_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticGroundAnchorEffectivenessLow,
            detail: format!(
                "electromagnetic ground anchor effectiveness ratio {} is below threshold {}",
                em_ground_anchor_effectiveness_ratio.unwrap_or(0.0),
                em_ground_effectiveness_min_threshold
            ),
        });
    }
    if em_insulation_leakage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticInsulationLeakageHigh,
            detail: format!(
                "electromagnetic insulation leakage ratio {} exceeds threshold {}",
                em_insulation_leakage_ratio.unwrap_or(0.0),
                em_insulation_leakage_max_threshold
            ),
        });
    }
    if em_divergence_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticFluxDivergenceHigh,
            detail: format!(
                "electromagnetic flux divergence ratio {} exceeds threshold {}",
                em_flux_divergence_ratio.unwrap_or(0.0),
                em_divergence_max_threshold
            ),
        });
    }
    if em_energy_imbalance_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticEnergyImbalanceHigh,
            detail: format!(
                "electromagnetic energy imbalance ratio {} exceeds threshold {}",
                em_energy_imbalance_ratio.unwrap_or(0.0),
                em_energy_imbalance_max_threshold
            ),
        });
    }
    if em_boundary_energy_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticBoundaryEnergyLow,
            detail: format!(
                "electromagnetic boundary energy ratio {} is below threshold {}",
                em_boundary_energy_ratio.unwrap_or(0.0),
                em_boundary_energy_min_threshold
            ),
        });
    }
    if em_boundary_penalty_contribution_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticBoundaryPenaltyConditioningHigh,
            detail: format!(
                "electromagnetic boundary penalty conditioning contribution {} exceeds threshold {}",
                em_boundary_penalty_conditioning_contribution.unwrap_or(0.0),
                em_boundary_penalty_contribution_max_threshold
            ),
        });
    }
    if em_source_region_energy_consistency_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSourceRegionEnergyConsistencyLow,
            detail: format!(
                "electromagnetic source-region energy consistency ratio {} is below threshold {}",
                em_source_region_energy_consistency_ratio.unwrap_or(0.0),
                em_source_region_energy_consistency_min_threshold
            ),
        });
    }
    if em_real_residual_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticRealResidualHigh,
            detail: format!(
                "electromagnetic real residual norm {} exceeds threshold {}",
                em_real_residual_norm.unwrap_or(0.0),
                em_real_residual_max_threshold
            ),
        });
    }
    if em_imag_residual_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticImagResidualHigh,
            detail: format!(
                "electromagnetic imaginary residual norm {} exceeds threshold {}",
                em_imag_residual_norm.unwrap_or(0.0),
                em_imag_residual_max_threshold
            ),
        });
    }
    if em_sweep_coverage_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticSweepCoverageLow,
            detail: format!(
                "electromagnetic sweep count {} is below threshold {}",
                em_sweep_count.unwrap_or(0.0),
                em_sweep_count_min_threshold
            ),
        });
    }
    if em_resonance_sharpness_breach {
        quality_reasons.push(QualityReason {
            code: QualityReasonCode::ElectromagneticResonanceSharpnessLow,
            detail: format!(
                "electromagnetic resonance quality factor {} is below threshold {}",
                em_resonance_quality_factor.unwrap_or(0.0),
                em_resonance_q_min_threshold
            ),
        });
    }

    let publishable = match options.quality_policy {
        QualityPolicy::Strict => {
            solver_convergence == QualityGate::Pass
                && result_quality == QualityGate::Pass
                && quality_reasons.is_empty()
        }
        QualityPolicy::Balanced => {
            solver_convergence == QualityGate::Pass && result_quality == QualityGate::Pass
        }
        QualityPolicy::Exploratory => {
            solver_convergence != QualityGate::Fail && result_quality != QualityGate::Fail
        }
    };
    let run_status = if publishable {
        RunStatus::Publishable
    } else if result_quality == QualityGate::Fail {
        RunStatus::Rejected
    } else {
        RunStatus::Degraded
    };
    let solver_backend = run.solver_backend.clone();
    let solver_device_apply_k_ratio = run.solver_device_apply_k_ratio;
    let solver_host_sync_count = run.solver_host_sync_count;
    let solver_method = run.solver_method.clone();
    let preconditioner = run.preconditioner.clone();

    let result = AnalysisRunResult {
        run_id: storage::next_run_id(),
        run,
        render_topology: render_topology_from_prep_context(prep_context.as_ref()),
        modal_results: None,
        thermal_results: None,
        transient_results: None,
        nonlinear_results: None,
        electromagnetic_results: Some(ElectromagneticResultsData {
            electromagnetic_payload_version: "electromagnetic_results/v1".to_string(),
            reference_frequency_hz: em_run.reference_frequency_hz,
            applied_current_a: em_run.applied_current_a,
            vector_potential_real: em_run.vector_potential_real_field,
            vector_potential_imag: em_run.vector_potential_imag_field,
            magnetic_flux_density_real: em_run.magnetic_flux_density_real_field,
            magnetic_flux_density_imag: em_run.magnetic_flux_density_imag_field,
            magnetic_flux_density_magnitude: em_run.magnetic_flux_density_magnitude_field,
            magnetic_field_real: em_run.magnetic_field_real_field,
            magnetic_field_imag: em_run.magnetic_field_imag_field,
            current_density_real: em_run.current_density_real_field,
            current_density_imag: em_run.current_density_imag_field,
            electric_field_real: em_run.electric_field_real_field,
            electric_field_imag: em_run.electric_field_imag_field,
            power_loss_density: em_run.power_loss_density_field,
            energy_density: em_run.energy_density_field,
            residual_real: em_run.residual_real_field,
            residual_imag: em_run.residual_imag_field,
            electric_flux_density_real: em_run.electric_flux_density_real_field,
            electric_flux_density_imag: em_run.electric_flux_density_imag_field,
            poynting_vector_real: em_run.poynting_vector_real_field,
            poynting_vector_imag: em_run.poynting_vector_imag_field,
            sweep_frequency_hz,
            sweep_peak_flux_density,
            sweep_solve_quality,
            resonance_peak_frequency_hz: sweep_metrics.resonance_peak_frequency_hz,
            resonance_peak_flux_density: sweep_metrics.resonance_peak_flux_density,
            resonance_bandwidth_hz: sweep_metrics.resonance_bandwidth_hz,
            resonance_quality_factor: sweep_metrics.resonance_quality_factor,
            resonance_flux_gain: sweep_metrics.resonance_flux_gain,
        }),
        model_validity: QualityGate::Pass,
        solver_convergence,
        result_quality,
        run_status,
        publishable,
        quality_reasons,
        provenance: RunProvenance {
            backend,
            solver_backend,
            solver_device_apply_k_ratio,
            solver_host_sync_count,
            precision_mode: contracts::format_precision_mode(options.precision_mode),
            deterministic_mode: options.deterministic_mode,
            solver_method,
            preconditioner,
            quality_policy: contracts::format_quality_policy(options.quality_policy),
            fallback_events: Vec::new(),
        },
    };

    persist_fea_run_result_with_progress(
        ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
        ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
        "RM.FEA.RUN_ELECTROMAGNETIC.ARTIFACT_STORE_FAILED",
        &context,
        &result,
    )?;

    Ok(OperationEnvelope::new(
        ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
        ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
        &context,
        result,
    ))
}

fn validate_electromagnetic_run_model(
    model: &AnalysisModel,
    context: &OperationContext,
) -> Result<(), OperationErrorEnvelope> {
    if let Some(material) = model.materials.iter().find(|material| {
        material.electrical.as_ref().is_some_and(|electrical| {
            !electrical.conductivity_s_per_m.is_finite()
                || electrical.conductivity_s_per_m <= 0.0
                || !electrical.relative_permittivity.is_finite()
                || electrical.relative_permittivity <= 0.0
                || !electrical.relative_permeability.is_finite()
                || electrical.relative_permeability <= 0.0
                || electrical
                    .conductivity_frequency_response
                    .iter()
                    .any(|point| {
                        !point.frequency_hz.is_finite()
                            || point.frequency_hz <= 0.0
                            || !point.conductivity_scale.is_finite()
                            || point.conductivity_scale <= 0.0
                            || point
                                .dispersive_loss_scale
                                .is_some_and(|value| !value.is_finite() || value < 0.0)
                            || point
                                .relative_permittivity_scale
                                .is_some_and(|value| !value.is_finite() || value <= 0.0)
                            || point
                                .relative_permeability_scale
                                .is_some_and(|value| !value.is_finite() || value <= 0.0)
                    })
        })
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_ELECTROMAGNETIC_MATERIAL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires finite positive electrical material coefficients",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("material_id".to_string(), material.material_id.clone()),
            ]),
        ));
    }

    let electrical_material_count = model
        .materials
        .iter()
        .filter(|material| material.electrical.is_some())
        .count();
    if electrical_material_count == 0 {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.MISSING_ELECTROMAGNETIC_MATERIAL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires at least one electrical material",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                (
                    "material_count".to_string(),
                    model.materials.len().to_string(),
                ),
            ]),
        ));
    }
    let electrical_material_by_id = model
        .materials
        .iter()
        .filter(|material| material.electrical.is_some())
        .map(|material| material.material_id.as_str())
        .collect::<std::collections::BTreeSet<_>>();
    if let Some(assignment) = model.material_assignments.iter().find(|assignment| {
        !electrical_material_by_id.contains(assignment.assigned_material_id.as_str())
    }) {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.MISSING_ELECTROMAGNETIC_MATERIAL",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires every material assignment to reference an assigned electrical material",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("region_id".to_string(), assignment.region_id.clone()),
                (
                    "assigned_material_id".to_string(),
                    assignment.assigned_material_id.clone(),
                ),
            ]),
        ));
    }

    reject_moment_loads_for_run_family(
        model,
        ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
        ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
        "RM.FEA.RUN_ELECTROMAGNETIC.INVALID_ELECTROMAGNETIC_SOURCE",
        "electromagnetic",
        context,
    )?;

    let has_electromagnetic_source = model.loads.iter().any(|load| match &load.kind {
        LoadKind::CurrentDensity {
            jx,
            jy,
            jz,
            phase_rad,
            amplitude_scale,
        } => {
            jx.is_finite()
                && jy.is_finite()
                && jz.is_finite()
                && phase_rad.is_finite()
                && amplitude_scale.is_finite()
                && *amplitude_scale > 0.0
                && (jx.abs() + jy.abs() + jz.abs()) > 0.0
        }
        LoadKind::CoilCurrent {
            current_a,
            phase_rad,
            amplitude_scale,
        } => {
            current_a.is_finite()
                && current_a.abs() > 0.0
                && phase_rad.is_finite()
                && amplitude_scale.is_finite()
                && *amplitude_scale > 0.0
        }
        _ => false,
    });
    if !has_electromagnetic_source {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.MISSING_ELECTROMAGNETIC_SOURCE",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires a nonzero current-density or coil-current source",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                ("load_count".to_string(), model.loads.len().to_string()),
            ]),
        ));
    }

    let has_electromagnetic_boundary = model.boundary_conditions.iter().any(|bc| {
        matches!(
            &bc.kind,
            BoundaryConditionKind::MagneticInsulation
                | BoundaryConditionKind::VectorPotentialGround
        )
    });
    if !has_electromagnetic_boundary {
        return Err(operation_error(
            ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
            ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_ELECTROMAGNETIC.MISSING_ELECTROMAGNETIC_BOUNDARY",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "fea.run_electromagnetic requires magnetic insulation or vector-potential ground boundary data",
            BTreeMap::from([
                ("analysis_model_id".to_string(), model.model_id.0.clone()),
                (
                    "boundary_condition_count".to_string(),
                    model.boundary_conditions.len().to_string(),
                ),
            ]),
        ));
    }

    Ok(())
}

fn collect_analysis_result_fields(run_result: &AnalysisRunResult) -> Vec<AnalysisField> {
    let mut fields = Vec::new();
    let mut seen = HashSet::new();

    for field in &run_result.run.fields {
        push_analysis_result_field(&mut fields, &mut seen, field);
    }

    if let Some(modal) = run_result.modal_results.as_ref() {
        for field in &modal.mode_shapes {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
    }

    if let Some(thermal) = run_result.thermal_results.as_ref() {
        for field in &thermal.temperature_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &thermal.temperature_gradient_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &thermal.heat_flux_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &thermal.heat_source_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &thermal.boundary_heat_flux_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
    }

    if let Some(transient) = run_result.transient_results.as_ref() {
        for field in &transient.displacement_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.rotation_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.velocity_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.angular_velocity_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.acceleration_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.angular_acceleration_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.von_mises_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.kinetic_energy_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.strain_energy_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.residual_norm_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_temperature_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_thermal_strain_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_thermal_stress_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_displacement_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_von_mises_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.thermo_mechanical_coupling_residual_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.electro_thermal_temperature_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &transient.electro_thermal_thermal_residual_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
    }

    if let Some(nonlinear) = run_result.nonlinear_results.as_ref() {
        for field in &nonlinear.displacement_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.rotation_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.von_mises_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.plastic_strain_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.equivalent_plastic_strain_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.contact_pressure_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.contact_gap_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.load_factor_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.residual_norm_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_temperature_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_thermal_strain_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_thermal_stress_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_displacement_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_von_mises_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.thermo_mechanical_coupling_residual_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.electro_thermal_temperature_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
        for field in &nonlinear.electro_thermal_thermal_residual_snapshots {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
    }

    if let Some(electromagnetic) = run_result.electromagnetic_results.as_ref() {
        for field in [
            &electromagnetic.vector_potential_real,
            &electromagnetic.vector_potential_imag,
            &electromagnetic.magnetic_flux_density_real,
            &electromagnetic.magnetic_flux_density_imag,
            &electromagnetic.magnetic_flux_density_magnitude,
            &electromagnetic.magnetic_field_real,
            &electromagnetic.magnetic_field_imag,
            &electromagnetic.current_density_real,
            &electromagnetic.current_density_imag,
            &electromagnetic.electric_field_real,
            &electromagnetic.electric_field_imag,
            &electromagnetic.power_loss_density,
            &electromagnetic.energy_density,
            &electromagnetic.residual_real,
            &electromagnetic.residual_imag,
            &electromagnetic.electric_flux_density_real,
            &electromagnetic.electric_flux_density_imag,
            &electromagnetic.poynting_vector_real,
            &electromagnetic.poynting_vector_imag,
        ] {
            push_analysis_result_field(&mut fields, &mut seen, field);
        }
    }

    fields
}

fn push_analysis_result_field(
    fields: &mut Vec<AnalysisField>,
    seen: &mut HashSet<String>,
    field: &AnalysisField,
) {
    if !seen.insert(field.field_id.clone()) {
        return;
    }
    fields.push(field.clone());
}

fn filter_analysis_fields_by_indices(
    fields: &[AnalysisField],
    indices: &[usize],
) -> Vec<AnalysisField> {
    if fields.is_empty() {
        return Vec::new();
    }
    indices
        .iter()
        .filter_map(|index| fields.get(*index).cloned())
        .collect()
}

pub(crate) fn analysis_run_field_ids(run_result: &AnalysisRunResult) -> Vec<String> {
    collect_analysis_result_fields(run_result)
        .into_iter()
        .map(|field| field.field_id)
        .collect()
}

pub fn analysis_results_op(
    run_result: &AnalysisRunResult,
    query: AnalysisResultsQuery,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisResultsData>, OperationErrorEnvelope> {
    let mut collected_fields = collect_analysis_result_fields(run_result);

    if !query.include_fields.is_empty() {
        let mut filtered = Vec::new();
        for requested in &query.include_fields {
            let Some(field) = collected_fields
                .iter()
                .find(|field| &field.field_id == requested)
            else {
                return Err(operation_error(
                    ANALYSIS_RESULTS_OPERATION,
                    ANALYSIS_RESULTS_OP_VERSION,
                    &context,
                    OperationErrorSpec {
                        error_code: "RM.FEA.RESULTS.FIELD_NOT_FOUND",
                        error_type: OperationErrorType::Input,
                        retryable: false,
                        severity: OperationErrorSeverity::Error,
                    },
                    format!("requested FEA field '{requested}' was not produced by run"),
                    BTreeMap::from([
                        ("requested_field".to_string(), requested.clone()),
                        (
                            "available_fields".to_string(),
                            collected_fields
                                .iter()
                                .map(|field| field.field_id.clone())
                                .collect::<Vec<_>>()
                                .join(","),
                        ),
                    ]),
                ));
            };
            filtered.push(field.clone());
        }
        collected_fields = filtered;
    }
    let field_descriptors = collected_fields
        .iter()
        .map(AnalysisFieldDescriptor::from_field)
        .collect::<Vec<_>>();

    let (
        mode_count,
        available_mode_indices,
        min_frequency_hz,
        max_frequency_hz,
        max_modal_residual_norm,
        first_mode_converged,
    ) = if let Some(modal) = run_result.modal_results.as_ref() {
        let count = modal.eigenvalues_hz.len().min(modal.mode_shapes.len());
        let max_modal_residual_norm = modal.residual_norms.iter().copied().reduce(f64::max);
        let first_mode_converged = modal.residual_norms.first().copied().map(|v| v <= 1.0e-6);
        let (min_frequency_hz, max_frequency_hz) = if count == 0 {
            (None, None)
        } else {
            let mut min_value = f64::INFINITY;
            let mut max_value = f64::NEG_INFINITY;
            for value in modal.eigenvalues_hz.iter().copied().take(count) {
                min_value = min_value.min(value);
                max_value = max_value.max(value);
            }
            (Some(min_value), Some(max_value))
        };
        (
            count,
            (0..count).collect(),
            min_frequency_hz,
            max_frequency_hz,
            max_modal_residual_norm,
            first_mode_converged,
        )
    } else {
        (0, Vec::new(), None, None, None, None)
    };

    let (
        snapshot_count,
        time_start_s,
        time_end_s,
        max_transient_residual_norm,
        final_step_converged,
    ) = if let Some(transient) = run_result.transient_results.as_ref() {
        let count = transient
            .time_points_s
            .len()
            .min(transient.displacement_snapshots.len());
        let max_residual = transient.residual_norms.iter().copied().reduce(f64::max);
        let final_step_converged = max_residual.map(|value| value <= 1.0e-6);
        if count == 0 {
            (0, None, None, max_residual, final_step_converged)
        } else {
            (
                count,
                transient.time_points_s.first().copied(),
                transient.time_points_s.get(count - 1).copied(),
                max_residual,
                final_step_converged,
            )
        }
    } else if let Some(thermal) = run_result.thermal_results.as_ref() {
        let count = thermal
            .time_points_s
            .len()
            .min(thermal.temperature_snapshots.len());
        let max_residual = thermal.residual_norms.iter().copied().reduce(f64::max);
        let final_step_converged = max_residual.map(|value| value <= 1.0e-6);
        if count == 0 {
            (0, None, None, max_residual, final_step_converged)
        } else {
            (
                count,
                thermal.time_points_s.first().copied(),
                thermal.time_points_s.get(count - 1).copied(),
                max_residual,
                final_step_converged,
            )
        }
    } else {
        (0, None, None, None, None)
    };

    let (
        increment_count,
        failed_increment_count,
        max_nonlinear_residual_norm,
        max_nonlinear_increment_norm,
        max_nonlinear_iteration_count,
        final_increment_converged,
        nonlinear_line_search_backtracks,
        nonlinear_max_backtracks_per_increment,
        nonlinear_tangent_rebuild_count,
        nonlinear_iteration_spike_count,
        nonlinear_convergence_stall_count,
        nonlinear_backtrack_burst_count,
    ) = if let Some(nonlinear) = run_result.nonlinear_results.as_ref() {
        let count = nonlinear.load_factors.len();
        let max_residual = nonlinear.residual_norms.iter().copied().reduce(f64::max);
        let max_increment_norm = nonlinear.increment_norms.iter().copied().reduce(f64::max);
        let max_iteration_count = nonlinear.iteration_counts.iter().copied().max();
        let final_converged =
            max_residual.map(|value| value <= 1.0e-6 && nonlinear.failed_increments == 0);
        (
            count,
            Some(nonlinear.failed_increments),
            max_residual,
            max_increment_norm,
            max_iteration_count,
            final_converged,
            Some(nonlinear.line_search_backtracks),
            Some(nonlinear.max_line_search_backtracks_per_increment),
            Some(nonlinear.tangent_rebuild_count),
            Some(nonlinear.iteration_spike_count),
            Some(nonlinear.convergence_stall_count),
            Some(nonlinear.backtrack_burst_count),
        )
    } else {
        (
            0, None, None, None, None, None, None, None, None, None, None, None,
        )
    };

    let prep_calibration_profile = diagnostic_metric_string(
        &run_result.run.diagnostics,
        "FEA_PREP_CALIBRATION",
        "profile",
    );
    let prep_calibration_fingerprint = diagnostic_metric_u64(
        &run_result.run.diagnostics,
        "FEA_PREP_CALIBRATION",
        "calibration_fingerprint",
    );
    let prep_acceptance_score = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_PREP_ACCEPTANCE",
        "acceptance_score",
    );
    let prep_acceptance_passed = diagnostic_metric_bool(
        &run_result.run.diagnostics,
        "FEA_PREP_ACCEPTANCE",
        "accepted",
    );
    let prep_acceptance_fingerprint = diagnostic_metric_u64(
        &run_result.run.diagnostics,
        "FEA_PREP_ACCEPTANCE",
        "acceptance_fingerprint",
    );
    let thermo_coupling_enabled =
        diagnostic_metric_bool(&run_result.run.diagnostics, "FEA_TM_COUPLING", "enabled");
    let thermo_coupling_fingerprint = diagnostic_metric_u64(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "coupling_fingerprint",
    );
    let thermo_constitutive_temperature_factor = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "constitutive_temperature_factor",
    );
    let thermo_effective_modulus_scale = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "effective_modulus_scale",
    );
    let thermo_constitutive_material_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "constitutive_material_spread_ratio",
    );
    let thermo_assignment_heterogeneity_index = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "assignment_heterogeneity_index",
    );
    let thermo_region_delta_count = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "region_delta_count",
    );
    let thermo_spatial_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_COUPLING",
        "spatial_coverage_ratio",
    );
    let thermo_field_extrapolation_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_TRANSIENT",
        "field_extrapolation_ratio",
    )
    .or_else(|| {
        diagnostic_metric(
            &run_result.run.diagnostics,
            "FEA_TM_NONLINEAR",
            "field_extrapolation_ratio",
        )
    });
    let thermo_field_clamp_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_TRANSIENT",
        "field_clamp_ratio",
    )
    .or_else(|| {
        diagnostic_metric(
            &run_result.run.diagnostics,
            "FEA_TM_NONLINEAR",
            "field_clamp_ratio",
        )
    });
    let thermo_transient_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_TRANSIENT",
        "severity_peak",
    )
    .or_else(|| diagnostic_metric(&run_result.run.diagnostics, "FEA_TM_TRANSIENT", "severity"));
    let thermo_nonlinear_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_TM_NONLINEAR",
        "severity_peak",
    )
    .or_else(|| diagnostic_metric(&run_result.run.diagnostics, "FEA_TM_NONLINEAR", "severity"));
    let electro_thermal_coupling_enabled =
        diagnostic_metric_bool(&run_result.run.diagnostics, "FEA_ET_COUPLING", "enabled");
    let electro_thermal_coupling_fingerprint = diagnostic_metric_u64(
        &run_result.run.diagnostics,
        "FEA_ET_COUPLING",
        "coupling_fingerprint",
    );
    let electro_joule_heating_scale = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_COUPLING",
        "joule_heating_scale",
    );
    let electro_conductivity_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_COUPLING",
        "conductivity_spread_ratio",
    );
    let electro_transient_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_TRANSIENT",
        "severity_peak",
    )
    .or_else(|| diagnostic_metric(&run_result.run.diagnostics, "FEA_ET_TRANSIENT", "severity"));
    let electro_transient_time_scale_mean = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_TRANSIENT",
        "time_scale_mean",
    );
    let electro_nonlinear_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_NONLINEAR",
        "severity_peak",
    )
    .or_else(|| diagnostic_metric(&run_result.run.diagnostics, "FEA_ET_NONLINEAR", "severity"));
    let electro_nonlinear_time_scale_mean = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_ET_NONLINEAR",
        "time_scale_mean",
    );
    let plastic_nonlinear_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_PLASTIC_NONLINEAR",
        "severity_peak",
    )
    .or_else(|| {
        diagnostic_metric(
            &run_result.run.diagnostics,
            "FEA_PLASTIC_NONLINEAR",
            "severity",
        )
    });
    let plastic_nonlinear_severity_mean = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_PLASTIC_NONLINEAR",
        "severity_mean",
    );
    let plastic_load_realization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_PLASTIC_NONLINEAR",
        "load_realization_ratio",
    );
    let plastic_load_amplification_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_PLASTIC_NONLINEAR",
        "load_amplification_ratio",
    );
    let contact_nonlinear_severity = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_CONTACT_NONLINEAR",
        "severity_peak",
    )
    .or_else(|| {
        diagnostic_metric(
            &run_result.run.diagnostics,
            "FEA_CONTACT_NONLINEAR",
            "severity",
        )
    });
    let contact_nonlinear_severity_mean = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_CONTACT_NONLINEAR",
        "severity_mean",
    );
    let contact_load_realization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_CONTACT_NONLINEAR",
        "load_realization_ratio",
    );
    let contact_load_amplification_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_CONTACT_NONLINEAR",
        "load_amplification_ratio",
    );
    let thermal_max_residual_norm = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_STABILITY",
        "max_residual_norm",
    );
    let thermal_min_temperature_k = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_STABILITY",
        "min_temperature_k",
    );
    let thermal_max_temperature_k = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_STABILITY",
        "max_temperature_k",
    );
    let thermal_conductivity_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_CONSTITUTIVE",
        "conductivity_spread_ratio",
    );
    let thermal_heat_capacity_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_CONSTITUTIVE",
        "heat_capacity_spread_ratio",
    );
    let thermal_spatial_gradient_index = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_OUTCOME",
        "spatial_gradient_index",
    );
    let thermal_monotonic_response_fraction = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_OUTCOME",
        "monotonic_response_fraction",
    );
    let thermal_response_realization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_THERMAL_OUTCOME",
        "thermal_response_realization_ratio",
    );
    let electromagnetic_enabled =
        diagnostic_metric_bool(&run_result.run.diagnostics, "FEA_EM_STATIC", "enabled");
    let electromagnetic_formulation_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "formulation_coverage_ratio",
    );
    let electromagnetic_magnetostatic_curl_curl_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "magnetostatic_curl_curl_coverage_ratio",
    );
    let electromagnetic_magnetoquasistatic_eddy_current_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "magnetoquasistatic_eddy_current_coverage_ratio",
    );
    let electromagnetic_full_wave_displacement_current_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "full_wave_displacement_current_coverage_ratio",
    );
    let electromagnetic_displacement_to_conduction_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "displacement_to_conduction_ratio",
    );
    let electromagnetic_material_frequency_response_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_FORMULATION",
        "material_frequency_response_coverage_ratio",
    );
    let electromagnetic_reference_frequency_hz = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "reference_frequency_hz",
    );
    let electromagnetic_applied_current_a = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "applied_current_a",
    );
    let electromagnetic_solve_quality = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "solve_quality",
    );
    let electromagnetic_conductivity_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "conductivity_spread_ratio",
    );
    let electromagnetic_relative_permittivity_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "relative_permittivity_spread_ratio",
    );
    let electromagnetic_relative_permeability_spread_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "relative_permeability_spread_ratio",
    );
    let electromagnetic_material_heterogeneity_index = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "electromagnetic_material_heterogeneity_index",
    );
    let electromagnetic_assignment_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "assignment_coverage_ratio",
    );
    let electromagnetic_assigned_coefficient_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "assigned_coefficient_coverage_ratio",
    );
    let electromagnetic_region_coefficient_contrast_index = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "region_coefficient_contrast_index",
    );
    let electromagnetic_condition_number_estimate = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "condition_number_estimate",
    );
    let electromagnetic_source_realization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_realization_ratio",
    );
    let electromagnetic_source_region_coverage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_region_coverage_ratio",
    );
    let electromagnetic_source_material_alignment_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_material_alignment_ratio",
    );
    let electromagnetic_source_localization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_localization_ratio",
    );
    let electromagnetic_source_overlap_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_overlap_ratio",
    );
    let electromagnetic_source_interference_index = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_interference_index",
    );
    let electromagnetic_boundary_anchor_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_anchor_ratio",
    );
    let electromagnetic_boundary_condition_localization_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_condition_localization_ratio",
    );
    let electromagnetic_ground_anchor_effectiveness_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "ground_anchor_effectiveness_ratio",
    );
    let electromagnetic_insulation_leakage_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "insulation_leakage_ratio",
    );
    let electromagnetic_flux_divergence_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "flux_divergence_ratio",
    );
    let electromagnetic_energy_imbalance_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "energy_imbalance_ratio",
    );
    let electromagnetic_boundary_energy_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_energy_ratio",
    );
    let electromagnetic_boundary_penalty_conditioning_contribution = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "boundary_penalty_conditioning_contribution",
    );
    let electromagnetic_source_region_energy_consistency_ratio = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SOURCE_ENERGY",
        "source_region_energy_consistency_ratio",
    );
    let electromagnetic_real_residual_norm = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "real_residual_norm",
    );
    let electromagnetic_imag_residual_norm = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_STATIC",
        "imag_residual_norm",
    );
    let electromagnetic_sweep_count =
        diagnostic_metric(&run_result.run.diagnostics, "FEA_EM_SWEEP", "sweep_count");
    let electromagnetic_resonance_peak_frequency_hz = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SWEEP",
        "resonance_peak_frequency_hz",
    );
    let electromagnetic_resonance_peak_flux_density = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SWEEP",
        "resonance_peak_flux_density",
    );
    let electromagnetic_resonance_bandwidth_hz = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SWEEP",
        "resonance_bandwidth_hz",
    );
    let electromagnetic_resonance_quality_factor = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SWEEP",
        "resonance_quality_factor",
    );
    let electromagnetic_resonance_flux_gain = diagnostic_metric(
        &run_result.run.diagnostics,
        "FEA_EM_SWEEP",
        "resonance_flux_gain",
    );

    let summary = AnalysisResultsSummary {
        field_count: field_descriptors.len(),
        total_elements: field_descriptors
            .iter()
            .map(|field| field.element_count)
            .sum(),
        mode_count,
        available_mode_indices,
        min_frequency_hz,
        max_frequency_hz,
        max_modal_residual_norm,
        first_mode_converged,
        snapshot_count,
        time_start_s,
        time_end_s,
        max_transient_residual_norm,
        final_step_converged,
        increment_count,
        failed_increment_count,
        max_nonlinear_residual_norm,
        max_nonlinear_increment_norm,
        max_nonlinear_iteration_count,
        final_increment_converged,
        nonlinear_line_search_backtracks,
        nonlinear_max_backtracks_per_increment,
        nonlinear_tangent_rebuild_count,
        nonlinear_iteration_spike_count,
        nonlinear_convergence_stall_count,
        nonlinear_backtrack_burst_count,
        prep_calibration_profile,
        prep_calibration_fingerprint,
        prep_acceptance_score,
        prep_acceptance_passed,
        prep_acceptance_fingerprint,
        thermo_coupling_enabled,
        thermo_coupling_fingerprint,
        thermo_constitutive_temperature_factor,
        thermo_effective_modulus_scale,
        thermo_constitutive_material_spread_ratio,
        thermo_assignment_heterogeneity_index,
        thermo_region_delta_count,
        thermo_spatial_coverage_ratio,
        thermo_field_extrapolation_ratio,
        thermo_field_clamp_ratio,
        thermo_transient_severity,
        thermo_nonlinear_severity,
        electro_thermal_coupling_enabled,
        electro_thermal_coupling_fingerprint,
        electro_joule_heating_scale,
        electro_conductivity_spread_ratio,
        electro_transient_severity,
        electro_transient_time_scale_mean,
        electro_nonlinear_severity,
        electro_nonlinear_time_scale_mean,
        plastic_nonlinear_severity,
        plastic_nonlinear_severity_mean,
        plastic_load_realization_ratio,
        plastic_load_amplification_ratio,
        contact_nonlinear_severity,
        contact_nonlinear_severity_mean,
        contact_load_realization_ratio,
        contact_load_amplification_ratio,
        thermal_max_residual_norm,
        thermal_min_temperature_k,
        thermal_max_temperature_k,
        thermal_conductivity_spread_ratio,
        thermal_heat_capacity_spread_ratio,
        thermal_spatial_gradient_index,
        thermal_monotonic_response_fraction,
        thermal_response_realization_ratio,
        electromagnetic_enabled,
        electromagnetic_formulation_coverage_ratio,
        electromagnetic_magnetostatic_curl_curl_coverage_ratio,
        electromagnetic_magnetoquasistatic_eddy_current_coverage_ratio,
        electromagnetic_full_wave_displacement_current_coverage_ratio,
        electromagnetic_displacement_to_conduction_ratio,
        electromagnetic_material_frequency_response_coverage_ratio,
        electromagnetic_reference_frequency_hz,
        electromagnetic_applied_current_a,
        electromagnetic_solve_quality,
        electromagnetic_conductivity_spread_ratio,
        electromagnetic_relative_permittivity_spread_ratio,
        electromagnetic_relative_permeability_spread_ratio,
        electromagnetic_material_heterogeneity_index,
        electromagnetic_assignment_coverage_ratio,
        electromagnetic_assigned_coefficient_coverage_ratio,
        electromagnetic_region_coefficient_contrast_index,
        electromagnetic_condition_number_estimate,
        electromagnetic_source_realization_ratio,
        electromagnetic_source_region_coverage_ratio,
        electromagnetic_source_material_alignment_ratio,
        electromagnetic_source_localization_ratio,
        electromagnetic_source_overlap_ratio,
        electromagnetic_source_interference_index,
        electromagnetic_boundary_anchor_ratio,
        electromagnetic_boundary_condition_localization_ratio,
        electromagnetic_ground_anchor_effectiveness_ratio,
        electromagnetic_insulation_leakage_ratio,
        electromagnetic_flux_divergence_ratio,
        electromagnetic_energy_imbalance_ratio,
        electromagnetic_boundary_energy_ratio,
        electromagnetic_boundary_penalty_conditioning_contribution,
        electromagnetic_source_region_energy_consistency_ratio,
        electromagnetic_real_residual_norm,
        electromagnetic_imag_residual_norm,
        electromagnetic_sweep_count,
        electromagnetic_resonance_peak_frequency_hz,
        electromagnetic_resonance_peak_flux_density,
        electromagnetic_resonance_bandwidth_hz,
        electromagnetic_resonance_quality_factor,
        electromagnetic_resonance_flux_gain,
    };

    let modal_results = if query.include_modal_results && query.include_field_values {
        if let Some(modal) = run_result.modal_results.as_ref() {
            if query.mode_indices.is_empty() {
                Some(modal.clone())
            } else {
                let mut eigenvalues_hz = Vec::with_capacity(query.mode_indices.len());
                let mut mode_shapes = Vec::with_capacity(query.mode_indices.len());
                let mut residual_norms = Vec::with_capacity(query.mode_indices.len());
                for &index in &query.mode_indices {
                    let eigenvalue = modal.eigenvalues_hz.get(index).copied().ok_or_else(|| {
                        operation_error(
                            ANALYSIS_RESULTS_OPERATION,
                            ANALYSIS_RESULTS_OP_VERSION,
                            &context,
                            OperationErrorSpec {
                                error_code: "RM.FEA.RESULTS.MODE_NOT_FOUND",
                                error_type: OperationErrorType::Input,
                                retryable: false,
                                severity: OperationErrorSeverity::Error,
                            },
                            format!("requested modal mode index '{index}' was not produced by run"),
                            BTreeMap::from([
                                ("requested_mode_index".to_string(), index.to_string()),
                                (
                                    "available_mode_count".to_string(),
                                    modal.eigenvalues_hz.len().to_string(),
                                ),
                            ]),
                        )
                    })?;
                    let mode_shape = modal.mode_shapes.get(index).cloned().ok_or_else(|| {
                        operation_error(
                            ANALYSIS_RESULTS_OPERATION,
                            ANALYSIS_RESULTS_OP_VERSION,
                            &context,
                            OperationErrorSpec {
                                error_code: "RM.FEA.RESULTS.MODE_NOT_FOUND",
                                error_type: OperationErrorType::Input,
                                retryable: false,
                                severity: OperationErrorSeverity::Error,
                            },
                            format!(
                                "requested modal mode index '{index}' is missing mode shape data"
                            ),
                            BTreeMap::from([
                                ("requested_mode_index".to_string(), index.to_string()),
                                (
                                    "available_shape_count".to_string(),
                                    modal.mode_shapes.len().to_string(),
                                ),
                            ]),
                        )
                    })?;
                    let residual_norm =
                        modal.residual_norms.get(index).copied().ok_or_else(|| {
                            operation_error(
                                ANALYSIS_RESULTS_OPERATION,
                                ANALYSIS_RESULTS_OP_VERSION,
                                &context,
                                OperationErrorSpec {
                                    error_code: "RM.FEA.RESULTS.MODE_NOT_FOUND",
                                    error_type: OperationErrorType::Input,
                                    retryable: false,
                                    severity: OperationErrorSeverity::Error,
                                },
                                format!(
                                    "requested modal mode index '{index}' is missing residual data"
                                ),
                                BTreeMap::from([
                                    ("requested_mode_index".to_string(), index.to_string()),
                                    (
                                        "available_residual_count".to_string(),
                                        modal.residual_norms.len().to_string(),
                                    ),
                                ]),
                            )
                        })?;
                    eigenvalues_hz.push(eigenvalue);
                    mode_shapes.push(mode_shape);
                    residual_norms.push(residual_norm);
                }
                Some(ModalResultsData {
                    modal_payload_version: modal.modal_payload_version.clone(),
                    eigenvalues_hz,
                    mode_shapes,
                    residual_norms,
                    mode_units: modal.mode_units,
                    frequency_basis: modal.frequency_basis,
                })
            }
        } else {
            None
        }
    } else {
        None
    };

    let transient_results = if query.include_transient_results && query.include_field_values {
        if let Some(transient) = run_result.transient_results.as_ref() {
            if query.transient_snapshot_indices.is_empty() {
                Some(transient.clone())
            } else {
                let mut time_points_s = Vec::with_capacity(query.transient_snapshot_indices.len());
                let mut displacement_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let rotation_snapshots = filter_analysis_fields_by_indices(
                    &transient.rotation_snapshots,
                    &query.transient_snapshot_indices,
                );
                let mut velocity_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let angular_velocity_snapshots = filter_analysis_fields_by_indices(
                    &transient.angular_velocity_snapshots,
                    &query.transient_snapshot_indices,
                );
                let mut acceleration_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let angular_acceleration_snapshots = filter_analysis_fields_by_indices(
                    &transient.angular_acceleration_snapshots,
                    &query.transient_snapshot_indices,
                );
                let mut von_mises_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let mut kinetic_energy_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let mut strain_energy_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let mut residual_norm_snapshots =
                    Vec::with_capacity(query.transient_snapshot_indices.len());
                let mut residual_norms = Vec::with_capacity(query.transient_snapshot_indices.len());
                let thermo_mechanical_temperature_snapshots = filter_analysis_fields_by_indices(
                    &transient.thermo_mechanical_temperature_snapshots,
                    &query.transient_snapshot_indices,
                );
                let thermo_mechanical_thermal_strain_snapshots = filter_analysis_fields_by_indices(
                    &transient.thermo_mechanical_thermal_strain_snapshots,
                    &query.transient_snapshot_indices,
                );
                let thermo_mechanical_thermal_stress_snapshots = filter_analysis_fields_by_indices(
                    &transient.thermo_mechanical_thermal_stress_snapshots,
                    &query.transient_snapshot_indices,
                );
                let thermo_mechanical_displacement_snapshots = filter_analysis_fields_by_indices(
                    &transient.thermo_mechanical_displacement_snapshots,
                    &query.transient_snapshot_indices,
                );
                let thermo_mechanical_von_mises_snapshots = filter_analysis_fields_by_indices(
                    &transient.thermo_mechanical_von_mises_snapshots,
                    &query.transient_snapshot_indices,
                );
                let thermo_mechanical_coupling_residual_snapshots =
                    filter_analysis_fields_by_indices(
                        &transient.thermo_mechanical_coupling_residual_snapshots,
                        &query.transient_snapshot_indices,
                    );
                let electro_thermal_temperature_snapshots = filter_analysis_fields_by_indices(
                    &transient.electro_thermal_temperature_snapshots,
                    &query.transient_snapshot_indices,
                );
                let electro_thermal_thermal_residual_snapshots = filter_analysis_fields_by_indices(
                    &transient.electro_thermal_thermal_residual_snapshots,
                    &query.transient_snapshot_indices,
                );

                for &index in &query.transient_snapshot_indices {
                    let time_point = transient.time_points_s.get(index).copied().ok_or_else(|| {
                        operation_error(
                            ANALYSIS_RESULTS_OPERATION,
                            ANALYSIS_RESULTS_OP_VERSION,
                            &context,
                            OperationErrorSpec {
                                error_code: "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                error_type: OperationErrorType::Input,
                                retryable: false,
                                severity: OperationErrorSeverity::Error,
                            },
                            format!(
                                "requested transient snapshot index '{index}' was not produced by run"
                            ),
                            BTreeMap::from([
                                ("requested_snapshot_index".to_string(), index.to_string()),
                                (
                                    "available_snapshot_count".to_string(),
                                    transient.time_points_s.len().to_string(),
                                ),
                            ]),
                        )
                    })?;
                    let snapshot = transient
                        .displacement_snapshots
                        .get(index)
                        .cloned()
                        .ok_or_else(|| {
                            operation_error(
                                ANALYSIS_RESULTS_OPERATION,
                                ANALYSIS_RESULTS_OP_VERSION,
                                &context,
                                OperationErrorSpec {
                                    error_code: "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                    error_type: OperationErrorType::Input,
                                    retryable: false,
                                    severity: OperationErrorSeverity::Error,
                                },
                                format!(
                                    "requested transient snapshot index '{index}' is missing displacement data"
                                ),
                                BTreeMap::from([
                                    ("requested_snapshot_index".to_string(), index.to_string()),
                                    (
                                        "available_displacement_snapshot_count".to_string(),
                                        transient.displacement_snapshots.len().to_string(),
                                    ),
                                ]),
                            )
                        })?;
                    let velocity = transient.velocity_snapshots.get(index).cloned().ok_or_else(|| {
                        operation_error(
                            ANALYSIS_RESULTS_OPERATION,
                            ANALYSIS_RESULTS_OP_VERSION,
                            &context,
                            OperationErrorSpec {
                                error_code: "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                error_type: OperationErrorType::Input,
                                retryable: false,
                                severity: OperationErrorSeverity::Error,
                            },
                            format!(
                                "requested transient snapshot index '{index}' is missing velocity data"
                            ),
                            BTreeMap::from([
                                ("requested_snapshot_index".to_string(), index.to_string()),
                                (
                                    "available_velocity_snapshot_count".to_string(),
                                    transient.velocity_snapshots.len().to_string(),
                                ),
                            ]),
                        )
                    })?;
                    let acceleration =
                        transient
                            .acceleration_snapshots
                            .get(index)
                            .cloned()
                            .ok_or_else(|| {
                                operation_error(
                                    ANALYSIS_RESULTS_OPERATION,
                                    ANALYSIS_RESULTS_OP_VERSION,
                                    &context,
                                    OperationErrorSpec {
                                        error_code:
                                            "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                        error_type: OperationErrorType::Input,
                                        retryable: false,
                                        severity: OperationErrorSeverity::Error,
                                    },
                                    format!(
                                        "requested transient snapshot index '{index}' is missing acceleration data"
                                    ),
                                    BTreeMap::from([
                                        ("requested_snapshot_index".to_string(), index.to_string()),
                                        (
                                            "available_acceleration_snapshot_count".to_string(),
                                            transient.acceleration_snapshots.len().to_string(),
                                        ),
                                    ]),
                                )
                            })?;
                    let von_mises =
                        transient
                            .von_mises_snapshots
                            .get(index)
                            .cloned()
                            .ok_or_else(|| {
                                operation_error(
                                    ANALYSIS_RESULTS_OPERATION,
                                    ANALYSIS_RESULTS_OP_VERSION,
                                    &context,
                                    OperationErrorSpec {
                                        error_code:
                                            "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                        error_type: OperationErrorType::Input,
                                        retryable: false,
                                        severity: OperationErrorSeverity::Error,
                                    },
                                    format!(
                                        "requested transient snapshot index '{index}' is missing von Mises data"
                                    ),
                                    BTreeMap::from([
                                        ("requested_snapshot_index".to_string(), index.to_string()),
                                        (
                                            "available_von_mises_snapshot_count".to_string(),
                                            transient.von_mises_snapshots.len().to_string(),
                                        ),
                                    ]),
                                )
                            })?;
                    let kinetic_energy =
                        transient
                            .kinetic_energy_snapshots
                            .get(index)
                            .cloned()
                            .ok_or_else(|| {
                                operation_error(
                                    ANALYSIS_RESULTS_OPERATION,
                                    ANALYSIS_RESULTS_OP_VERSION,
                                    &context,
                                    OperationErrorSpec {
                                        error_code:
                                            "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                        error_type: OperationErrorType::Input,
                                        retryable: false,
                                        severity: OperationErrorSeverity::Error,
                                    },
                                    format!(
                                        "requested transient snapshot index '{index}' is missing kinetic energy data"
                                    ),
                                    BTreeMap::from([
                                        ("requested_snapshot_index".to_string(), index.to_string()),
                                        (
                                            "available_kinetic_energy_snapshot_count".to_string(),
                                            transient.kinetic_energy_snapshots.len().to_string(),
                                        ),
                                    ]),
                                )
                            })?;
                    let strain_energy =
                        transient
                            .strain_energy_snapshots
                            .get(index)
                            .cloned()
                            .ok_or_else(|| {
                                operation_error(
                                    ANALYSIS_RESULTS_OPERATION,
                                    ANALYSIS_RESULTS_OP_VERSION,
                                    &context,
                                    OperationErrorSpec {
                                        error_code:
                                            "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                        error_type: OperationErrorType::Input,
                                        retryable: false,
                                        severity: OperationErrorSeverity::Error,
                                    },
                                    format!(
                                        "requested transient snapshot index '{index}' is missing strain energy data"
                                    ),
                                    BTreeMap::from([
                                        ("requested_snapshot_index".to_string(), index.to_string()),
                                        (
                                            "available_strain_energy_snapshot_count".to_string(),
                                            transient.strain_energy_snapshots.len().to_string(),
                                        ),
                                    ]),
                                )
                            })?;
                    let residual_norm_snapshot = transient
                        .residual_norm_snapshots
                        .get(index)
                        .cloned()
                        .ok_or_else(|| {
                            operation_error(
                                ANALYSIS_RESULTS_OPERATION,
                                ANALYSIS_RESULTS_OP_VERSION,
                                &context,
                                OperationErrorSpec {
                                    error_code: "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                    error_type: OperationErrorType::Input,
                                    retryable: false,
                                    severity: OperationErrorSeverity::Error,
                                },
                                format!(
                                    "requested transient snapshot index '{index}' is missing residual field data"
                                ),
                                BTreeMap::from([
                                    ("requested_snapshot_index".to_string(), index.to_string()),
                                    (
                                        "available_residual_snapshot_count".to_string(),
                                        transient.residual_norm_snapshots.len().to_string(),
                                    ),
                                ]),
                            )
                        })?;

                    if index > 0 {
                        let residual = transient.residual_norms.get(index - 1).copied().ok_or_else(|| {
                            operation_error(
                                ANALYSIS_RESULTS_OPERATION,
                                ANALYSIS_RESULTS_OP_VERSION,
                                &context,
                                OperationErrorSpec {
                                    error_code: "RM.FEA.RESULTS.TRANSIENT_SNAPSHOT_NOT_FOUND",
                                    error_type: OperationErrorType::Input,
                                    retryable: false,
                                    severity: OperationErrorSeverity::Error,
                                },
                                format!(
                                    "requested transient snapshot index '{index}' is missing residual data"
                                ),
                                BTreeMap::from([
                                    ("requested_snapshot_index".to_string(), index.to_string()),
                                    (
                                        "available_residual_count".to_string(),
                                        transient.residual_norms.len().to_string(),
                                    ),
                                ]),
                            )
                        })?;
                        residual_norms.push(residual);
                    }

                    time_points_s.push(time_point);
                    displacement_snapshots.push(snapshot);
                    velocity_snapshots.push(velocity);
                    acceleration_snapshots.push(acceleration);
                    von_mises_snapshots.push(von_mises);
                    kinetic_energy_snapshots.push(kinetic_energy);
                    strain_energy_snapshots.push(strain_energy);
                    residual_norm_snapshots.push(residual_norm_snapshot);
                }

                Some(TransientResultsData {
                    transient_payload_version: transient.transient_payload_version.clone(),
                    time_points_s,
                    displacement_snapshots,
                    rotation_snapshots,
                    velocity_snapshots,
                    angular_velocity_snapshots,
                    acceleration_snapshots,
                    angular_acceleration_snapshots,
                    von_mises_snapshots,
                    kinetic_energy_snapshots,
                    strain_energy_snapshots,
                    residual_norm_snapshots,
                    thermo_mechanical_temperature_snapshots,
                    thermo_mechanical_thermal_strain_snapshots,
                    thermo_mechanical_thermal_stress_snapshots,
                    thermo_mechanical_displacement_snapshots,
                    thermo_mechanical_von_mises_snapshots,
                    thermo_mechanical_coupling_residual_snapshots,
                    electro_thermal_temperature_snapshots,
                    electro_thermal_thermal_residual_snapshots,
                    residual_norms,
                    integration_method: transient.integration_method,
                })
            }
        } else {
            None
        }
    } else {
        None
    };

    let thermal_results = if query.include_field_values {
        run_result.thermal_results.clone()
    } else {
        None
    };

    let nonlinear_results = if query.include_nonlinear_results && query.include_field_values {
        run_result.nonlinear_results.clone()
    } else {
        None
    };
    let electromagnetic_results =
        if query.include_electromagnetic_results && query.include_field_values {
            run_result.electromagnetic_results.clone()
        } else {
            None
        };
    let fields = if query.include_field_values {
        collected_fields
    } else {
        Vec::new()
    };

    let data = AnalysisResultsData {
        field_descriptors,
        fields,
        modal_results,
        thermal_results,
        transient_results,
        nonlinear_results,
        electromagnetic_results,
        diagnostics: if query.include_diagnostics {
            if query.diagnostic_codes.is_empty() {
                Some(run_result.run.diagnostics.clone())
            } else {
                Some(
                    run_result
                        .run
                        .diagnostics
                        .iter()
                        .filter(|diag| query.diagnostic_codes.iter().any(|code| code == &diag.code))
                        .cloned()
                        .collect(),
                )
            }
        } else {
            None
        },
        run_status: run_result.run_status,
        publishable: run_result.publishable,
        quality_reasons: run_result.quality_reasons.clone(),
        provenance: run_result.provenance.clone(),
        summary,
    };

    Ok(OperationEnvelope::new(
        ANALYSIS_RESULTS_OPERATION,
        ANALYSIS_RESULTS_OP_VERSION,
        &context,
        data,
    ))
}

pub fn analysis_results_by_run_id_op(
    run_id: &str,
    query: AnalysisResultsQuery,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisResultsData>, OperationErrorEnvelope> {
    let run_result = storage::load_run_result(run_id).map_err(|err| {
        operation_error(
            ANALYSIS_RESULTS_OPERATION,
            ANALYSIS_RESULTS_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to load FEA run artifact: {err}"),
            BTreeMap::from([("run_id".to_string(), run_id.to_string())]),
        )
    })?;

    let Some(run_result) = run_result else {
        return Err(operation_error(
            ANALYSIS_RESULTS_OPERATION,
            ANALYSIS_RESULTS_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS.RUN_NOT_FOUND",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!("FEA run_id '{run_id}' was not found"),
            BTreeMap::from([("run_id".to_string(), run_id.to_string())]),
        ));
    };

    analysis_results_op(&run_result, query, context)
}

pub fn analysis_results_compare_op(
    query: AnalysisResultsCompareQuery,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisResultsCompareData>, OperationErrorEnvelope> {
    let baseline = storage::load_run_result(&query.baseline_run_id).map_err(|err| {
        operation_error(
            ANALYSIS_RESULTS_COMPARE_OPERATION,
            ANALYSIS_RESULTS_COMPARE_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS_COMPARE.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to load baseline FEA run artifact: {err}"),
            BTreeMap::from([("run_id".to_string(), query.baseline_run_id.clone())]),
        )
    })?;
    let Some(baseline) = baseline else {
        return Err(operation_error(
            ANALYSIS_RESULTS_COMPARE_OPERATION,
            ANALYSIS_RESULTS_COMPARE_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS_COMPARE.RUN_NOT_FOUND",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!(
                "FEA baseline run_id '{}' was not found",
                query.baseline_run_id
            ),
            BTreeMap::from([("run_id".to_string(), query.baseline_run_id.clone())]),
        ));
    };

    let candidate = storage::load_run_result(&query.candidate_run_id).map_err(|err| {
        operation_error(
            ANALYSIS_RESULTS_COMPARE_OPERATION,
            ANALYSIS_RESULTS_COMPARE_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS_COMPARE.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to load candidate FEA run artifact: {err}"),
            BTreeMap::from([("run_id".to_string(), query.candidate_run_id.clone())]),
        )
    })?;
    let Some(candidate) = candidate else {
        return Err(operation_error(
            ANALYSIS_RESULTS_COMPARE_OPERATION,
            ANALYSIS_RESULTS_COMPARE_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.RESULTS_COMPARE.RUN_NOT_FOUND",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!(
                "FEA candidate run_id '{}' was not found",
                query.candidate_run_id
            ),
            BTreeMap::from([("run_id".to_string(), query.candidate_run_id.clone())]),
        ));
    };

    let baseline_solve_ms = run_solve_ms(&baseline);
    let candidate_solve_ms = run_solve_ms(&candidate);
    let failed_increment_delta = match (
        baseline.nonlinear_results.as_ref(),
        candidate.nonlinear_results.as_ref(),
    ) {
        (Some(a), Some(b)) => Some(b.failed_increments as i64 - a.failed_increments as i64),
        _ => None,
    };
    let max_iteration_delta = match (
        baseline.nonlinear_results.as_ref(),
        candidate.nonlinear_results.as_ref(),
    ) {
        (Some(a), Some(b)) => Some(
            b.iteration_counts.iter().copied().max().unwrap_or(0) as i64
                - a.iteration_counts.iter().copied().max().unwrap_or(0) as i64,
        ),
        _ => None,
    };
    let nonlinear_spike_count_delta = match (
        baseline.nonlinear_results.as_ref(),
        candidate.nonlinear_results.as_ref(),
    ) {
        (Some(a), Some(b)) => Some(b.iteration_spike_count as i64 - a.iteration_spike_count as i64),
        _ => None,
    };
    let nonlinear_stall_count_delta = match (
        baseline.nonlinear_results.as_ref(),
        candidate.nonlinear_results.as_ref(),
    ) {
        (Some(a), Some(b)) => {
            Some(b.convergence_stall_count as i64 - a.convergence_stall_count as i64)
        }
        _ => None,
    };

    let data = AnalysisResultsCompareData {
        baseline_run_id: baseline.run_id,
        candidate_run_id: candidate.run_id,
        publishable_changed: baseline.publishable != candidate.publishable,
        run_status_changed: baseline.run_status != candidate.run_status,
        quality_reason_count_delta: candidate.quality_reasons.len() as i64
            - baseline.quality_reasons.len() as i64,
        failed_increment_delta,
        max_iteration_delta,
        nonlinear_spike_count_delta,
        nonlinear_stall_count_delta,
        solve_ms_delta: match (baseline_solve_ms, candidate_solve_ms) {
            (Some(a), Some(b)) => Some(b - a),
            _ => None,
        },
    };

    Ok(OperationEnvelope::new(
        ANALYSIS_RESULTS_COMPARE_OPERATION,
        ANALYSIS_RESULTS_COMPARE_OP_VERSION,
        &context,
        data,
    ))
}

pub fn analysis_trends_op(
    query: AnalysisTrendsQuery,
    context: OperationContext,
) -> Result<OperationEnvelope<AnalysisTrendsData>, OperationErrorEnvelope> {
    let runs = storage::list_run_results().map_err(|err| {
        operation_error(
            ANALYSIS_TRENDS_OPERATION,
            ANALYSIS_TRENDS_OP_VERSION,
            &context,
            OperationErrorSpec {
                error_code: "RM.FEA.TRENDS.ARTIFACT_STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to list FEA run artifacts: {err}"),
            BTreeMap::new(),
        )
    })?;

    let mut grouped: HashMap<AnalysisRunKind, Vec<AnalysisRunResult>> = HashMap::new();
    for run in runs {
        grouped.entry(run_kind(&run)).or_default().push(run);
    }

    let window = query.window_size.max(1);
    let mut summaries = Vec::new();
    for kind in [
        AnalysisRunKind::LinearStatic,
        AnalysisRunKind::Modal,
        AnalysisRunKind::Acoustic,
        AnalysisRunKind::Thermal,
        AnalysisRunKind::Transient,
        AnalysisRunKind::Cfd,
        AnalysisRunKind::Cht,
        AnalysisRunKind::Fsi,
        AnalysisRunKind::Nonlinear,
        AnalysisRunKind::Electromagnetic,
    ] {
        let Some(mut entries) = grouped.remove(&kind) else {
            continue;
        };
        entries.sort_by(|a, b| b.run_id.cmp(&a.run_id));
        if entries.len() > window {
            entries.truncate(window);
        }
        let sample_count = entries.len();
        if sample_count == 0 {
            continue;
        }

        let mut solve_samples = entries
            .iter()
            .filter_map(run_solve_ms)
            .filter(|value| value.is_finite())
            .collect::<Vec<_>>();
        solve_samples.sort_by(|a, b| a.total_cmp(b));
        let median_solve_ms = percentile(&solve_samples, 0.5);
        let p95_solve_ms = percentile(&solve_samples, 0.95);
        let publishable_rate =
            entries.iter().filter(|run| run.publishable).count() as f64 / sample_count as f64;

        let failed_increment_rate = if kind == AnalysisRunKind::Nonlinear {
            let failed = entries
                .iter()
                .filter_map(|run| run.nonlinear_results.as_ref())
                .filter(|nonlinear| nonlinear.failed_increments > 0)
                .count();
            Some(failed as f64 / sample_count as f64)
        } else {
            None
        };
        let mean_spike_count = if kind == AnalysisRunKind::Nonlinear {
            let values = entries
                .iter()
                .filter_map(|run| run.nonlinear_results.as_ref())
                .map(|nonlinear| nonlinear.iteration_spike_count as f64)
                .collect::<Vec<_>>();
            Some(mean(&values))
        } else {
            None
        };
        let mean_stall_count = if kind == AnalysisRunKind::Nonlinear {
            let values = entries
                .iter()
                .filter_map(|run| run.nonlinear_results.as_ref())
                .map(|nonlinear| nonlinear.convergence_stall_count as f64)
                .collect::<Vec<_>>();
            Some(mean(&values))
        } else {
            None
        };
        let prep_acceptance_rate = {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric_bool(&run.run.diagnostics, "FEA_PREP_ACCEPTANCE", "accepted")
                })
                .collect::<Vec<_>>();
            if values.is_empty() {
                None
            } else {
                Some(values.iter().filter(|value| **value).count() as f64 / values.len() as f64)
            }
        };
        let prep_calibration_fast_rate = calibration_profile_rate(&entries, "fast");
        let prep_calibration_balanced_rate = calibration_profile_rate(&entries, "balanced");
        let prep_calibration_conservative_rate = calibration_profile_rate(&entries, "conservative");
        let thermo_coupling_enabled_rate = {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric_bool(&run.run.diagnostics, "FEA_TM_COUPLING", "enabled")
                })
                .collect::<Vec<_>>();
            if values.is_empty() {
                None
            } else {
                Some(values.iter().filter(|value| **value).count() as f64 / values.len() as f64)
            }
        };
        let thermo_transient_warn_rate = if kind == AnalysisRunKind::Transient {
            diagnostic_warning_rate(&entries, "FEA_TM_TRANSIENT")
        } else {
            None
        };
        let thermo_nonlinear_warn_rate = if kind == AnalysisRunKind::Nonlinear {
            diagnostic_warning_rate(&entries, "FEA_TM_NONLINEAR")
        } else {
            None
        };
        let thermo_spread_breach_rate = {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_TM_COUPLING",
                        "constitutive_material_spread_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, THERMO_SPREAD_THRESHOLD_BALANCED)
        };
        let thermo_heterogeneity_breach_rate = {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_TM_COUPLING",
                        "assignment_heterogeneity_index",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, THERMO_HETEROGENEITY_THRESHOLD_BALANCED)
        };
        let electro_thermal_coupling_enabled_rate = {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric_bool(&run.run.diagnostics, "FEA_ET_COUPLING", "enabled")
                })
                .collect::<Vec<_>>();
            if values.is_empty() {
                None
            } else {
                Some(values.iter().filter(|value| **value).count() as f64 / values.len() as f64)
            }
        };
        let electro_transient_warn_rate = if kind == AnalysisRunKind::Transient {
            diagnostic_warning_rate(&entries, "FEA_ET_TRANSIENT")
        } else {
            None
        };
        let electro_nonlinear_warn_rate = if kind == AnalysisRunKind::Nonlinear {
            diagnostic_warning_rate(&entries, "FEA_ET_NONLINEAR")
        } else {
            None
        };
        let plastic_nonlinear_warn_rate = if kind == AnalysisRunKind::Nonlinear {
            diagnostic_warning_rate(&entries, "FEA_PLASTIC_NONLINEAR")
        } else {
            None
        };
        let contact_nonlinear_warn_rate = if kind == AnalysisRunKind::Nonlinear {
            diagnostic_warning_rate(&entries, "FEA_CONTACT_NONLINEAR")
        } else {
            None
        };
        let thermal_stability_warn_rate = if kind == AnalysisRunKind::Thermal {
            diagnostic_warning_rate(&entries, "FEA_THERMAL_STABILITY")
        } else {
            None
        };
        let thermal_constitutive_warn_rate = if kind == AnalysisRunKind::Thermal {
            diagnostic_warning_rate(&entries, "FEA_THERMAL_CONSTITUTIVE")
        } else {
            None
        };
        let thermal_spread_breach_rate = if kind == AnalysisRunKind::Thermal {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_THERMAL_CONSTITUTIVE",
                        "conductivity_spread_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, 2.5)
        } else {
            None
        };
        let electromagnetic_solve_warn_rate = if kind == AnalysisRunKind::Electromagnetic {
            Some(diagnostic_warning_rate(&entries, "FEA_EM_STATIC").unwrap_or(0.0))
        } else {
            None
        };
        let electromagnetic_spread_breach_rate = if kind == AnalysisRunKind::Electromagnetic {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "conductivity_spread_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_CONDUCTIVITY_SPREAD_THRESHOLD_BALANCED)
        } else {
            None
        };
        let electromagnetic_heterogeneity_breach_rate = if kind == AnalysisRunKind::Electromagnetic
        {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "electromagnetic_material_heterogeneity_index",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_HETEROGENEITY_THRESHOLD_BALANCED)
        } else {
            None
        };
        let electromagnetic_coverage_breach_rate = if kind == AnalysisRunKind::Electromagnetic {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "assignment_coverage_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_less_than(&values, EM_ASSIGNMENT_COVERAGE_MIN_BALANCED)
        } else {
            None
        };
        let electromagnetic_contrast_breach_rate = if kind == AnalysisRunKind::Electromagnetic {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "region_coefficient_contrast_index",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_REGION_CONTRAST_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_conditioning_breach_rate = if kind == AnalysisRunKind::Electromagnetic {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "condition_number_estimate",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_CONDITIONING_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_source_realization_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "source_realization_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_SOURCE_REALIZATION_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_source_region_coverage_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "source_region_coverage_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_SOURCE_REGION_COVERAGE_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_source_material_alignment_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "source_material_alignment_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_SOURCE_MATERIAL_ALIGNMENT_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_source_overlap_breach_rate = if kind == AnalysisRunKind::Electromagnetic
        {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_SOURCE_ENERGY",
                        "source_overlap_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_SOURCE_OVERLAP_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_source_interference_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "source_interference_index",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_greater_than(&values, EM_SOURCE_INTERFERENCE_MAX_BALANCED)
            } else {
                None
            };
        let electromagnetic_boundary_anchor_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "boundary_anchor_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_BOUNDARY_ANCHOR_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_boundary_localization_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "boundary_condition_localization_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_BOUNDARY_LOCALIZATION_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_ground_effectiveness_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "ground_anchor_effectiveness_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_GROUND_EFFECTIVENESS_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_insulation_leakage_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "insulation_leakage_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_greater_than(&values, EM_INSULATION_LEAKAGE_MAX_BALANCED)
            } else {
                None
            };
        let electromagnetic_divergence_breach_rate = if kind == AnalysisRunKind::Electromagnetic {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(
                        &run.run.diagnostics,
                        "FEA_EM_STATIC",
                        "flux_divergence_ratio",
                    )
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_FLUX_DIVERGENCE_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_energy_imbalance_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "energy_imbalance_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_greater_than(&values, EM_ENERGY_IMBALANCE_MAX_BALANCED)
            } else {
                None
            };
        let electromagnetic_boundary_energy_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "boundary_energy_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_BOUNDARY_ENERGY_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_boundary_penalty_contribution_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "boundary_penalty_conditioning_contribution",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_greater_than(&values, EM_BOUNDARY_PENALTY_CONTRIBUTION_MAX_BALANCED)
            } else {
                None
            };
        let electromagnetic_source_region_energy_consistency_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SOURCE_ENERGY",
                            "source_region_energy_consistency_ratio",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_SOURCE_REGION_ENERGY_CONSISTENCY_MIN_BALANCED)
            } else {
                None
            };
        let electromagnetic_real_residual_breach_rate = if kind == AnalysisRunKind::Electromagnetic
        {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(&run.run.diagnostics, "FEA_EM_STATIC", "real_residual_norm")
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_REAL_RESIDUAL_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_imag_residual_breach_rate = if kind == AnalysisRunKind::Electromagnetic
        {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(&run.run.diagnostics, "FEA_EM_STATIC", "imag_residual_norm")
                })
                .collect::<Vec<_>>();
            breach_rate_greater_than(&values, EM_IMAG_RESIDUAL_MAX_BALANCED)
        } else {
            None
        };
        let electromagnetic_sweep_coverage_breach_rate = if kind == AnalysisRunKind::Electromagnetic
        {
            let values = entries
                .iter()
                .filter_map(|run| {
                    diagnostic_metric(&run.run.diagnostics, "FEA_EM_SWEEP", "sweep_count")
                })
                .collect::<Vec<_>>();
            breach_rate_less_than(&values, EM_SWEEP_COUNT_MIN_BALANCED)
        } else {
            None
        };
        let electromagnetic_resonance_sharpness_breach_rate =
            if kind == AnalysisRunKind::Electromagnetic {
                let values = entries
                    .iter()
                    .filter_map(|run| {
                        diagnostic_metric(
                            &run.run.diagnostics,
                            "FEA_EM_SWEEP",
                            "resonance_quality_factor",
                        )
                    })
                    .collect::<Vec<_>>();
                breach_rate_less_than(&values, EM_RESONANCE_Q_MIN_BALANCED)
            } else {
                None
            };

        summaries.push(AnalysisTrendKindSummary {
            run_kind: kind,
            sample_count,
            median_solve_ms,
            p95_solve_ms,
            publishable_rate,
            failed_increment_rate,
            mean_spike_count,
            mean_stall_count,
            prep_acceptance_rate,
            prep_calibration_fast_rate,
            prep_calibration_balanced_rate,
            prep_calibration_conservative_rate,
            thermo_coupling_enabled_rate,
            thermo_transient_warn_rate,
            thermo_nonlinear_warn_rate,
            thermo_spread_breach_rate,
            thermo_heterogeneity_breach_rate,
            electro_thermal_coupling_enabled_rate,
            electro_transient_warn_rate,
            electro_nonlinear_warn_rate,
            plastic_nonlinear_warn_rate,
            contact_nonlinear_warn_rate,
            thermal_stability_warn_rate,
            thermal_constitutive_warn_rate,
            thermal_spread_breach_rate,
            electromagnetic_solve_warn_rate,
            electromagnetic_spread_breach_rate,
            electromagnetic_heterogeneity_breach_rate,
            electromagnetic_coverage_breach_rate,
            electromagnetic_contrast_breach_rate,
            electromagnetic_conditioning_breach_rate,
            electromagnetic_source_realization_breach_rate,
            electromagnetic_source_region_coverage_breach_rate,
            electromagnetic_source_material_alignment_breach_rate,
            electromagnetic_source_overlap_breach_rate,
            electromagnetic_source_interference_breach_rate,
            electromagnetic_boundary_anchor_breach_rate,
            electromagnetic_boundary_localization_breach_rate,
            electromagnetic_ground_effectiveness_breach_rate,
            electromagnetic_insulation_leakage_breach_rate,
            electromagnetic_divergence_breach_rate,
            electromagnetic_energy_imbalance_breach_rate,
            electromagnetic_boundary_energy_breach_rate,
            electromagnetic_boundary_penalty_contribution_breach_rate,
            electromagnetic_source_region_energy_consistency_breach_rate,
            electromagnetic_real_residual_breach_rate,
            electromagnetic_imag_residual_breach_rate,
            electromagnetic_sweep_coverage_breach_rate,
            electromagnetic_resonance_sharpness_breach_rate,
        });
    }

    Ok(OperationEnvelope::new(
        ANALYSIS_TRENDS_OPERATION,
        ANALYSIS_TRENDS_OP_VERSION,
        &context,
        AnalysisTrendsData {
            window_size: window,
            summaries,
        },
    ))
}

fn run_kind(run: &AnalysisRunResult) -> AnalysisRunKind {
    if run
        .run
        .diagnostics
        .iter()
        .any(|diag| diag.code == "FEA_ACOUSTIC_HARMONIC_RESPONSE")
    {
        AnalysisRunKind::Acoustic
    } else if run.electromagnetic_results.is_some()
        || run
            .run
            .diagnostics
            .iter()
            .any(|diag| diag.code == "FEA_EM_STATIC")
    {
        AnalysisRunKind::Electromagnetic
    } else if run
        .run
        .diagnostics
        .iter()
        .any(|diag| diag.code == "FEA_CHT_COUPLING")
    {
        AnalysisRunKind::Cht
    } else if run
        .run
        .diagnostics
        .iter()
        .any(|diag| diag.code == "FEA_FSI_COUPLING")
    {
        AnalysisRunKind::Fsi
    } else if run
        .run
        .diagnostics
        .iter()
        .any(|diag| diag.code == "FEA_CFD_FLOW")
    {
        AnalysisRunKind::Cfd
    } else if run.nonlinear_results.is_some() {
        AnalysisRunKind::Nonlinear
    } else if run.thermal_results.is_some() {
        AnalysisRunKind::Thermal
    } else if run.transient_results.is_some() {
        AnalysisRunKind::Transient
    } else if run.modal_results.is_some() {
        AnalysisRunKind::Modal
    } else {
        AnalysisRunKind::LinearStatic
    }
}

fn run_operation_version_for_kind(kind: AnalysisRunKind) -> &'static str {
    match kind {
        AnalysisRunKind::LinearStatic => ANALYSIS_RUN_OP_VERSION,
        AnalysisRunKind::Modal => ANALYSIS_RUN_MODAL_OP_VERSION,
        AnalysisRunKind::Acoustic => ANALYSIS_RUN_ACOUSTIC_OP_VERSION,
        AnalysisRunKind::Thermal => ANALYSIS_RUN_THERMAL_OP_VERSION,
        AnalysisRunKind::Transient => ANALYSIS_RUN_TRANSIENT_OP_VERSION,
        AnalysisRunKind::Cfd => ANALYSIS_RUN_CFD_OP_VERSION,
        AnalysisRunKind::Cht => ANALYSIS_RUN_CHT_OP_VERSION,
        AnalysisRunKind::Fsi => ANALYSIS_RUN_FSI_OP_VERSION,
        AnalysisRunKind::Nonlinear => ANALYSIS_RUN_NONLINEAR_OP_VERSION,
        AnalysisRunKind::Electromagnetic => ANALYSIS_RUN_ELECTROMAGNETIC_OP_VERSION,
    }
}

fn run_operation_for_kind(kind: AnalysisRunKind) -> &'static str {
    match kind {
        AnalysisRunKind::LinearStatic => ANALYSIS_RUN_OPERATION,
        AnalysisRunKind::Modal => ANALYSIS_RUN_MODAL_OPERATION,
        AnalysisRunKind::Acoustic => ANALYSIS_RUN_ACOUSTIC_OPERATION,
        AnalysisRunKind::Thermal => ANALYSIS_RUN_THERMAL_OPERATION,
        AnalysisRunKind::Transient => ANALYSIS_RUN_TRANSIENT_OPERATION,
        AnalysisRunKind::Cfd => ANALYSIS_RUN_CFD_OPERATION,
        AnalysisRunKind::Cht => ANALYSIS_RUN_CHT_OPERATION,
        AnalysisRunKind::Fsi => ANALYSIS_RUN_FSI_OPERATION,
        AnalysisRunKind::Nonlinear => ANALYSIS_RUN_NONLINEAR_OPERATION,
        AnalysisRunKind::Electromagnetic => ANALYSIS_RUN_ELECTROMAGNETIC_OPERATION,
    }
}

fn sanitize_study_sweep_id(sweep_id: &str) -> String {
    sweep_id
        .chars()
        .map(|ch| {
            if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' {
                ch
            } else {
                '_'
            }
        })
        .collect()
}

fn validate_study_issue_codes(spec: &AnalysisStudySpec) -> Vec<String> {
    let mut issue_codes = Vec::new();

    if spec.study_id.trim().is_empty() {
        issue_codes.push("RM.FEA.STUDY.ID_EMPTY".to_string());
    }
    if spec.create_model_intent.model_id.trim().is_empty() {
        issue_codes.push("RM.FEA.STUDY.MODEL_ID_EMPTY".to_string());
    }
    if spec.geometry.meshes.is_empty() {
        issue_codes.push("RM.FEA.STUDY.GEOMETRY_MESHES_EMPTY".to_string());
    }
    if spec.geometry.units == UnitSystem::Unspecified {
        issue_codes.push("RM.FEA.STUDY.GEOMETRY_UNITS_UNSPECIFIED".to_string());
    }
    if !profile_supports_run_kind(spec.create_model_intent.profile, spec.run_kind) {
        issue_codes.push("RM.FEA.STUDY.RUN_KIND_PROFILE_MISMATCH".to_string());
    }
    if let Some(model) = &spec.model {
        if model.geometry_id != spec.geometry.geometry_id
            || model.geometry_revision != spec.geometry.revision
        {
            issue_codes.push("RM.FEA.STUDY.MODEL_GEOMETRY_MISMATCH".to_string());
        }
        if validate_model_against_geometry(model, spec.geometry.units, &ReferenceFrame::Global)
            .is_err()
        {
            issue_codes.push("RM.FEA.STUDY.MODEL_INVALID".to_string());
        }
    }
    if spec.electromagnetic_run_options.is_some()
        && spec.run_kind != AnalysisRunKind::Electromagnetic
    {
        issue_codes.push("RM.FEA.STUDY.RUN_OPTIONS_KIND_MISMATCH".to_string());
    }
    if spec.linear_static_run_options.is_some() && spec.run_kind != AnalysisRunKind::LinearStatic
        || spec.modal_run_options.is_some() && spec.run_kind != AnalysisRunKind::Modal
        || spec.acoustic_run_options.is_some() && spec.run_kind != AnalysisRunKind::Acoustic
        || spec.thermal_run_options.is_some() && spec.run_kind != AnalysisRunKind::Thermal
        || spec.transient_run_options.is_some() && spec.run_kind != AnalysisRunKind::Transient
        || spec.cfd_run_options.is_some() && spec.run_kind != AnalysisRunKind::Cfd
        || spec.cht_run_options.is_some() && spec.run_kind != AnalysisRunKind::Cht
        || spec.fsi_run_options.is_some() && spec.run_kind != AnalysisRunKind::Fsi
        || spec.nonlinear_run_options.is_some() && spec.run_kind != AnalysisRunKind::Nonlinear
    {
        issue_codes.push("RM.FEA.STUDY.RUN_OPTIONS_KIND_MISMATCH".to_string());
    }
    if spec.run_kind == AnalysisRunKind::Electromagnetic {
        if let Some(options) = spec.electromagnetic_run_options.as_ref() {
            if !options.residual_target.is_finite() || options.residual_target <= 0.0 {
                issue_codes
                    .push("RM.FEA.STUDY.ELECTROMAGNETIC_RESIDUAL_TARGET_INVALID".to_string());
            }
            if !options.harmonic_tolerance.is_finite() || options.harmonic_tolerance <= 0.0 {
                issue_codes
                    .push("RM.FEA.STUDY.ELECTROMAGNETIC_HARMONIC_TOLERANCE_INVALID".to_string());
            }
            if options.harmonic_max_iterations == 0 {
                issue_codes.push(
                    "RM.FEA.STUDY.ELECTROMAGNETIC_HARMONIC_MAX_ITERATIONS_INVALID".to_string(),
                );
            }
            if options.sweep_enabled
                && !options
                    .sweep_frequency_hz
                    .iter()
                    .all(|frequency_hz| frequency_hz.is_finite() && *frequency_hz > 0.0)
            {
                issue_codes
                    .push("RM.FEA.STUDY.ELECTROMAGNETIC_SWEEP_FREQUENCY_INVALID".to_string());
            }
        }
    }

    issue_codes
}

fn study_issue_message(code: &str) -> &'static str {
    match code {
        "RM.FEA.STUDY.ID_EMPTY" => "study_id must be non-empty",
        "RM.FEA.STUDY.MODEL_ID_EMPTY" => "create_model_intent.model_id must be non-empty",
        "RM.FEA.STUDY.GEOMETRY_MESHES_EMPTY" => "geometry must contain at least one mesh",
        "RM.FEA.STUDY.GEOMETRY_UNITS_UNSPECIFIED" => {
            "geometry.units must be specified (not unspecified)"
        }
        "RM.FEA.STUDY.RUN_KIND_PROFILE_MISMATCH" => {
            "model.profile selects the solver; run kind must match the selected profile when supplied"
        }
        "RM.FEA.STUDY.MODEL_GEOMETRY_MISMATCH" => {
            "resolved model geometry id or revision does not match the study geometry"
        }
        "RM.FEA.STUDY.MODEL_INVALID" => "resolved model failed FEA validation",
        "RM.FEA.STUDY.RUN_OPTIONS_KIND_MISMATCH" => {
            "run options are only valid for the solver selected by model.profile"
        }
        "RM.FEA.STUDY.ELECTROMAGNETIC_RESIDUAL_TARGET_INVALID" => {
            "electromagnetic_run_options.residual_target must be finite and positive"
        }
        "RM.FEA.STUDY.ELECTROMAGNETIC_HARMONIC_TOLERANCE_INVALID" => {
            "electromagnetic_run_options.harmonic_tolerance must be finite and positive"
        }
        "RM.FEA.STUDY.ELECTROMAGNETIC_HARMONIC_MAX_ITERATIONS_INVALID" => {
            "electromagnetic_run_options.harmonic_max_iterations must be greater than zero"
        }
        "RM.FEA.STUDY.ELECTROMAGNETIC_SWEEP_FREQUENCY_INVALID" => {
            "electromagnetic_run_options.sweep_frequency_hz must contain finite positive values when sweep_enabled is true"
        }
        _ => "unrecognized study validation issue",
    }
}

fn profile_supports_run_kind(
    profile: AnalysisCreateModelProfile,
    run_kind: AnalysisRunKind,
) -> bool {
    profile.derived_run_kind() == run_kind
}

fn study_fingerprint(spec: &AnalysisStudySpec) -> String {
    let payload = serde_json::to_vec(spec).unwrap_or_else(|_| format!("{spec:?}").into_bytes());
    let mut hasher = Sha256::new();
    hasher.update(payload);
    format!("sha256:{:x}", hasher.finalize())
}

fn study_operation_sequence(spec: &AnalysisStudySpec, run_op_version: &str) -> Vec<String> {
    let mut operation_sequence = Vec::with_capacity(3);
    if spec.model.is_none() {
        operation_sequence.push(ANALYSIS_CREATE_MODEL_OP_VERSION.to_string());
    }
    operation_sequence.push(ANALYSIS_VALIDATE_OP_VERSION.to_string());
    operation_sequence.push(run_op_version.to_string());
    operation_sequence
}

fn study_run_options_json(spec: &AnalysisStudySpec) -> serde_json::Value {
    match spec.run_kind {
        AnalysisRunKind::LinearStatic => serde_json::to_value(&spec.linear_static_run_options),
        AnalysisRunKind::Modal => serde_json::to_value(&spec.modal_run_options),
        AnalysisRunKind::Acoustic => serde_json::to_value(&spec.acoustic_run_options),
        AnalysisRunKind::Thermal => serde_json::to_value(&spec.thermal_run_options),
        AnalysisRunKind::Transient => serde_json::to_value(&spec.transient_run_options),
        AnalysisRunKind::Cfd => serde_json::to_value(&spec.cfd_run_options),
        AnalysisRunKind::Cht => serde_json::to_value(&spec.cht_run_options),
        AnalysisRunKind::Fsi => serde_json::to_value(&spec.fsi_run_options),
        AnalysisRunKind::Nonlinear => serde_json::to_value(&spec.nonlinear_run_options),
        AnalysisRunKind::Electromagnetic => serde_json::to_value(&spec.electromagnetic_run_options),
    }
    .unwrap_or(serde_json::Value::Null)
}

fn run_options_to_json<T: Serialize>(options: &T) -> serde_json::Value {
    serde_json::to_value(options).unwrap_or(serde_json::Value::Null)
}

fn attach_prep_artifact_to_run_options(options: &mut AnalysisRunOptions, prep_artifact_id: &str) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_modal_options(
    options: &mut AnalysisModalRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_acoustic_options(
    options: &mut AnalysisAcousticRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_thermal_options(
    options: &mut AnalysisThermalRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_transient_options(
    options: &mut AnalysisTransientRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_cfd_options(
    options: &mut AnalysisCfdRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_cht_options(
    options: &mut AnalysisChtRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_fsi_options(
    options: &mut AnalysisFsiRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_nonlinear_options(
    options: &mut AnalysisNonlinearRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn attach_prep_artifact_to_electromagnetic_options(
    options: &mut AnalysisElectromagneticRunOptions,
    prep_artifact_id: &str,
) {
    if options.prep_artifact_id.is_none() {
        options.prep_artifact_id = Some(prep_artifact_id.to_string());
    }
}

fn study_evidence_root() -> PathBuf {
    let config = current_fea_runtime_config();
    config
        .study_artifact_root
        .or_else(|| {
            std::env::var("RUNMAT_FEA_STUDY_ARTIFACT_ROOT")
                .or_else(|_| std::env::var("RUNMAT_ANALYSIS_STUDY_ARTIFACT_ROOT"))
                .ok()
                .map(PathBuf::from)
        })
        .unwrap_or_else(|| {
            config
                .artifact_root
                .unwrap_or_else(default_fea_artifact_root)
                .join("studies")
        })
}

fn thermo_field_artifact_root() -> PathBuf {
    let config = current_fea_runtime_config();
    config
        .thermo_field_artifact_root
        .or_else(|| {
            std::env::var("RUNMAT_THERMO_FIELD_ARTIFACT_ROOT")
                .ok()
                .map(PathBuf::from)
        })
        .unwrap_or_else(|| {
            config
                .artifact_root
                .unwrap_or_else(default_fea_artifact_root)
                .join("thermo-fields")
        })
}

fn persist_study_evidence(
    study_fingerprint: &str,
    stage: &str,
    payload: serde_json::Value,
) -> Result<String, String> {
    let study_key = study_fingerprint.replace(':', "_");
    let root = study_evidence_root().join(study_key);
    fs_create_dir_all(&root)
        .map_err(|err| format!("failed to create study evidence directory: {err}"))?;
    let path = root.join(format!("{stage}.json"));
    let bytes = serde_json::to_vec_pretty(&payload)
        .map_err(|err| format!("failed to encode study evidence payload: {err}"))?;
    atomic_write_bytes(&path, &bytes)?;
    Ok(path.display().to_string())
}

fn atomic_write_bytes(path: &PathBuf, bytes: &[u8]) -> Result<(), String> {
    let tmp = path.with_extension(format!(
        "tmp-{}-{}",
        std::process::id(),
        Utc::now().timestamp_nanos_opt().unwrap_or_default()
    ));
    fs_write(&tmp, bytes)
        .map_err(|err| format!("failed to write temporary study evidence file: {err}"))?;
    fs_rename(&tmp, path).map_err(|err| {
        let _ = fs_remove_file(&tmp);
        format!("failed to atomically persist study evidence file: {err}")
    })
}

fn fs_create_dir_all(path: impl Into<PathBuf>) -> std::io::Result<()> {
    runmat_filesystem::create_dir_all(path.into())
}

fn fs_write(path: impl Into<PathBuf>, bytes: &[u8]) -> std::io::Result<()> {
    runmat_filesystem::write(path.into(), bytes)
}

fn fs_rename(from: impl Into<PathBuf>, to: impl Into<PathBuf>) -> std::io::Result<()> {
    runmat_filesystem::rename(from.into(), to.into())
}

fn fs_remove_file(path: impl Into<PathBuf>) -> std::io::Result<()> {
    match runmat_filesystem::remove_file(path.into()) {
        Ok(()) => Ok(()),
        Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
        Err(err) => Err(err),
    }
}

fn fs_exists(path: impl Into<PathBuf>) -> std::io::Result<bool> {
    match runmat_filesystem::metadata(path.into()) {
        Ok(_) => Ok(true),
        Err(err) if err.kind() == ErrorKind::NotFound => Ok(false),
        Err(err) => Err(err),
    }
}

fn fs_read_to_string(path: impl Into<PathBuf>) -> std::io::Result<String> {
    runmat_filesystem::read_to_string(path.into())
}

fn to_fea_prep_context(
    context: Option<&AnalysisRunPrepContext>,
    calibration_profile: Option<PrepCalibrationProfile>,
) -> Option<runmat_analysis_fea::FeaPrepContext> {
    context.map(|prep| runmat_analysis_fea::FeaPrepContext {
        prepared_mesh_count: prep.prepared_mesh_count,
        prepared_node_count: prep.prepared_node_count,
        prepared_element_count: prep.prepared_element_count,
        mapped_region_count: prep.mapped_region_count,
        min_scaled_jacobian: prep.min_scaled_jacobian,
        mean_aspect_ratio: prep.mean_aspect_ratio,
        inverted_element_count: prep.inverted_element_count,
        mapped_load_count: prep.mapped_load_count,
        mapped_bc_count: prep.mapped_bc_count,
        layout_seed: prep.layout_seed,
        topology_dof_multiplier: prep.topology_dof_multiplier,
        topology_bandwidth_estimate: prep.topology_bandwidth_estimate,
        mapped_region_participation_ratio: prep.mapped_region_participation_ratio,
        topology_surface_patch_ratio: prep.topology_surface_patch_ratio,
        topology_volume_core_ratio: prep.topology_volume_core_ratio,
        topology_mixed_family_ratio: prep.topology_mixed_family_ratio,
        topology_region_span_mean: prep.topology_region_span_mean,
        topology_region_block_count: prep.topology_region_block_count,
        topology_region_mesh_mean: prep.topology_region_mesh_mean,
        topology_region_mesh_variance: prep.topology_region_mesh_variance,
        topology_triangle_family_ratio: prep.topology_triangle_family_ratio,
        topology_quad_family_ratio: prep.topology_quad_family_ratio,
        topology_tet_family_ratio: prep.topology_tet_family_ratio,
        topology_hex_family_ratio: prep.topology_hex_family_ratio,
        coordinate_span_x_m: prep.coordinate_span_x_m,
        coordinate_span_y_m: prep.coordinate_span_y_m,
        coordinate_span_z_m: prep.coordinate_span_z_m,
        coordinate_active_dimension_count: prep.coordinate_active_dimension_count,
        coordinate_characteristic_length_m: prep.coordinate_characteristic_length_m,
        element_geometry_node_count: prep.element_geometry_node_count,
        element_geometry_edge_count: prep.element_geometry_edge_count,
        mean_element_edge_length_m: prep.mean_element_edge_length_m,
        mean_element_area_m2: prep.mean_element_area_m2,
        element_geometry_coverage_ratio: prep.element_geometry_coverage_ratio,
        reference_element_coordinates_m: prep.reference_element_coordinates_m,
        reference_element_area_m2: prep.reference_element_area_m2,
        element_topology_sample_element_count: prep.element_topology_sample_element_count,
        element_topology_sample_edge_count: prep.element_topology_sample_edge_count,
        element_topology_sample_edge_nodes: prep.element_topology_sample_edge_nodes,
        element_topology_sample_node_coordinates_m: prep.element_topology_sample_node_coordinates_m,
        element_topology_sample_element_edges: prep.element_topology_sample_element_edges,
        element_topology_sample_element_orientations: prep
            .element_topology_sample_element_orientations,
        element_topology_sample_element_areas_m2: prep.element_topology_sample_element_areas_m2,
        element_topology_node_coordinates_m: prep.element_topology_node_coordinates_m.clone(),
        element_topology_edge_nodes: prep.element_topology_edge_nodes.clone(),
        element_topology_element_edges: prep.element_topology_element_edges.clone(),
        element_topology_element_orientations: prep.element_topology_element_orientations.clone(),
        element_topology_element_areas_m2: prep.element_topology_element_areas_m2.clone(),
        calibration_profile_override: calibration_profile.and_then(map_calibration_profile),
    })
}

fn render_topology_from_prep_context(
    context: Option<&AnalysisRunPrepContext>,
) -> Option<AnalysisRenderTopology> {
    let prep = context?;
    if prep.element_topology_node_coordinates_m.is_empty()
        || prep.element_topology_edge_nodes.is_empty()
        || prep.element_topology_element_edges.is_empty()
    {
        return None;
    }

    let triangles = prep
        .element_topology_element_edges
        .iter()
        .filter_map(|element_edges| triangle_from_element_edges(element_edges, prep))
        .collect::<Vec<_>>();
    if triangles.is_empty() {
        return None;
    }

    Some(AnalysisRenderTopology {
        schema_version: "analysis_render_topology/v1".to_string(),
        source: AnalysisRenderTopologySource::SolverPrep,
        meshes: vec![AnalysisRenderMesh {
            mesh_id: "solver_surface".to_string(),
            vertices: prep.element_topology_node_coordinates_m.clone(),
            triangles,
        }],
    })
}

fn triangle_from_element_edges(
    element_edges: &[u32; 3],
    prep: &AnalysisRunPrepContext,
) -> Option<[u32; 3]> {
    let mut nodes = Vec::<u32>::with_capacity(3);
    for edge_index in element_edges {
        let edge = prep.element_topology_edge_nodes.get(*edge_index as usize)?;
        for node in edge {
            if !nodes.contains(node) {
                nodes.push(*node);
            }
        }
    }
    if nodes.len() == 3 {
        Some([nodes[0], nodes[1], nodes[2]])
    } else {
        None
    }
}

fn map_calibration_profile(
    profile: PrepCalibrationProfile,
) -> Option<runmat_analysis_fea::FeaPrepCalibrationProfile> {
    match profile {
        PrepCalibrationProfile::Auto => None,
        PrepCalibrationProfile::Fast => Some(runmat_analysis_fea::FeaPrepCalibrationProfile::Fast),
        PrepCalibrationProfile::Balanced => {
            Some(runmat_analysis_fea::FeaPrepCalibrationProfile::Balanced)
        }
        PrepCalibrationProfile::Conservative => {
            Some(runmat_analysis_fea::FeaPrepCalibrationProfile::Conservative)
        }
    }
}

fn model_thermo_coupling_options(model: &AnalysisModel) -> Option<ThermoMechanicalCouplingOptions> {
    let domain = model.thermo_mechanical.as_ref()?;
    let expansion = if model.materials.is_empty() {
        1.2e-5
    } else {
        model
            .materials
            .iter()
            .map(|material| material.thermal.expansion_coefficient_per_k.max(0.0))
            .sum::<f64>()
            / model.materials.len() as f64
    };

    Some(ThermoMechanicalCouplingOptions {
        enabled: domain.enabled,
        reference_temperature_k: domain.reference_temperature_k,
        applied_temperature_delta_k: domain.applied_temperature_delta_k,
        thermal_expansion_coefficient: expansion,
        field_artifact_id: domain.field_artifact_id.clone(),
        field_source: domain
            .field_source
            .as_ref()
            .map(|source| ThermoFieldSource {
                source_id: source.source_id.clone(),
                revision: source.revision,
                interpolation_mode: source.interpolation_mode.map(|mode| match mode {
                    runmat_analysis_core::ThermoFieldInterpolationMode::Linear => {
                        ThermoFieldInterpolationMode::Linear
                    }
                    runmat_analysis_core::ThermoFieldInterpolationMode::Step => {
                        ThermoFieldInterpolationMode::Step
                    }
                }),
                expected_region_ids: source.expected_region_ids.clone(),
            }),
        region_temperature_deltas: domain
            .region_temperature_deltas
            .iter()
            .map(|delta| ThermoRegionTemperatureDelta {
                region_id: delta.region_id.clone(),
                temperature_delta_k: delta.temperature_delta_k,
            })
            .collect(),
        time_profile: domain
            .time_profile
            .iter()
            .map(|point| ThermoTimeProfilePoint {
                normalized_time: point.normalized_time,
                scale: point.scale,
            })
            .collect(),
    })
}

fn model_electro_coupling_options(model: &AnalysisModel) -> Option<ElectroThermalCouplingOptions> {
    let domain = model.electro_thermal.as_ref()?;
    let electrical_materials: Vec<_> = model
        .materials
        .iter()
        .filter_map(|material| material.electrical.as_ref())
        .collect();
    let base_conductivity = if electrical_materials.is_empty() {
        1.0
    } else {
        electrical_materials
            .iter()
            .map(|e| e.conductivity_s_per_m.max(1.0e-12))
            .sum::<f64>()
            / electrical_materials.len() as f64
    };
    let resistive_coeff = if electrical_materials.is_empty() {
        0.0
    } else {
        electrical_materials
            .iter()
            .map(|e| e.resistive_heating_coefficient.max(0.0))
            .sum::<f64>()
            / electrical_materials.len() as f64
    };

    Some(ElectroThermalCouplingOptions {
        enabled: domain.enabled,
        reference_temperature_k: domain.reference_temperature_k,
        applied_voltage_v: domain.applied_voltage_v,
        base_electrical_conductivity_s_per_m: base_conductivity,
        resistive_heating_coefficient: resistive_coeff,
        region_conductivity_scales: domain
            .region_conductivity_scales
            .iter()
            .map(|scale| ElectroRegionConductivityScale {
                region_id: scale.region_id.clone(),
                conductivity_scale: scale.conductivity_scale,
            })
            .collect(),
        time_profile: domain
            .time_profile
            .iter()
            .map(|point| ElectroTimeProfilePoint {
                normalized_time: point.normalized_time,
                current_scale: point.current_scale,
            })
            .collect(),
    })
}

fn model_plasticity_constitutive_options(
    model: &AnalysisModel,
) -> Option<PlasticityConstitutiveOptions> {
    let plastic = model
        .materials
        .iter()
        .find_map(|material| material.plastic.as_ref())?;
    Some(PlasticityConstitutiveOptions {
        enabled: true,
        yield_strain: plastic.yield_strain,
        hardening_modulus_ratio: plastic.hardening_modulus_ratio,
        saturation_exponent: plastic.saturation_exponent,
    })
}

fn model_contact_interface_options(model: &AnalysisModel) -> Option<ContactInterfaceOptions> {
    model
        .interfaces
        .iter()
        .filter_map(|interface| match &interface.kind {
            AnalysisInterfaceKind::Contact(contact) => Some(ContactInterfaceOptions {
                enabled: true,
                penalty_stiffness_scale: contact.penalty_stiffness_scale,
                max_penetration_ratio: contact.max_penetration_ratio,
                friction_coefficient: contact.friction_coefficient,
            }),
            AnalysisInterfaceKind::FluidStructure(_)
            | AnalysisInterfaceKind::ConjugateHeatTransfer(_) => None,
        })
        .next()
}

fn to_fea_thermo_mechanical_context(
    options: Option<ThermoMechanicalCouplingOptions>,
) -> Option<runmat_analysis_fea::FeaThermoMechanicalContext> {
    options.map(|tm| runmat_analysis_fea::FeaThermoMechanicalContext {
        enabled: tm.enabled,
        reference_temperature_k: tm.reference_temperature_k,
        applied_temperature_delta_k: tm.applied_temperature_delta_k,
        thermal_expansion_coefficient: tm.thermal_expansion_coefficient,
        field_source: tm
            .field_source
            .map(|source| runmat_analysis_fea::FeaThermoFieldSource {
                source_id: source.source_id,
                revision: source.revision,
                interpolation_mode: source.interpolation_mode.map(|mode| match mode {
                    contracts::ThermoFieldInterpolationMode::Linear => {
                        runmat_analysis_fea::FeaThermoFieldInterpolationMode::Linear
                    }
                    contracts::ThermoFieldInterpolationMode::Step => {
                        runmat_analysis_fea::FeaThermoFieldInterpolationMode::Step
                    }
                }),
                expected_region_ids: source.expected_region_ids,
            }),
        region_temperature_deltas: tm
            .region_temperature_deltas
            .into_iter()
            .map(
                |ThermoRegionTemperatureDelta {
                     region_id,
                     temperature_delta_k,
                 }| runmat_analysis_fea::FeaThermoRegionTemperatureDelta {
                    region_id,
                    temperature_delta_k,
                },
            )
            .collect(),
        time_profile: tm
            .time_profile
            .into_iter()
            .map(
                |ThermoTimeProfilePoint {
                     normalized_time,
                     scale,
                 }| runmat_analysis_fea::FeaThermoTimeProfilePoint {
                    normalized_time,
                    scale,
                },
            )
            .collect(),
    })
}

fn to_fea_electro_thermal_context(
    options: Option<ElectroThermalCouplingOptions>,
) -> Option<runmat_analysis_fea::FeaElectroThermalContext> {
    options.map(|et| runmat_analysis_fea::FeaElectroThermalContext {
        enabled: et.enabled,
        reference_temperature_k: et.reference_temperature_k,
        applied_voltage_v: et.applied_voltage_v,
        base_electrical_conductivity_s_per_m: et.base_electrical_conductivity_s_per_m,
        resistive_heating_coefficient: et.resistive_heating_coefficient,
        region_conductivity_scales: et
            .region_conductivity_scales
            .into_iter()
            .map(
                |ElectroRegionConductivityScale {
                     region_id,
                     conductivity_scale,
                 }| runmat_analysis_fea::FeaElectroRegionConductivityScale {
                    region_id,
                    conductivity_scale,
                },
            )
            .collect(),
        time_profile: et
            .time_profile
            .into_iter()
            .map(
                |ElectroTimeProfilePoint {
                     normalized_time,
                     current_scale,
                 }| runmat_analysis_fea::FeaElectroTimeProfilePoint {
                    normalized_time,
                    current_scale,
                },
            )
            .collect(),
    })
}

fn to_fea_plasticity_constitutive_context(
    options: Option<PlasticityConstitutiveOptions>,
) -> Option<runmat_analysis_fea::FeaPlasticityConstitutiveContext> {
    options.map(
        |plasticity| runmat_analysis_fea::FeaPlasticityConstitutiveContext {
            enabled: plasticity.enabled,
            yield_strain: plasticity.yield_strain,
            hardening_modulus_ratio: plasticity.hardening_modulus_ratio,
            saturation_exponent: plasticity.saturation_exponent,
        },
    )
}

fn to_fea_contact_interface_context(
    options: Option<ContactInterfaceOptions>,
) -> Option<runmat_analysis_fea::FeaContactInterfaceContext> {
    options.map(|contact| runmat_analysis_fea::FeaContactInterfaceContext {
        enabled: contact.enabled,
        penalty_stiffness_scale: contact.penalty_stiffness_scale,
        max_penetration_ratio: contact.max_penetration_ratio,
        friction_coefficient: contact.friction_coefficient,
    })
}

fn electro_thermal_invalid_options_error_code(operation: &str) -> &'static str {
    match operation {
        ANALYSIS_RUN_MODAL_OPERATION => "RM.FEA.RUN_MODAL.INVALID_ELECTRO_THERMAL_OPTIONS",
        ANALYSIS_RUN_ACOUSTIC_OPERATION => "RM.FEA.RUN_ACOUSTIC.INVALID_ELECTRO_THERMAL_OPTIONS",
        ANALYSIS_RUN_TRANSIENT_OPERATION => "RM.FEA.RUN_TRANSIENT.INVALID_ELECTRO_THERMAL_OPTIONS",
        ANALYSIS_RUN_NONLINEAR_OPERATION => "RM.FEA.RUN_NONLINEAR.INVALID_ELECTRO_THERMAL_OPTIONS",
        ANALYSIS_RUN_OPERATION => "RM.FEA.RUN_LINEAR_STATIC.INVALID_ELECTRO_THERMAL_OPTIONS",
        _ => "RM.FEA.RUN.INVALID_ELECTRO_THERMAL_OPTIONS",
    }
}

fn validate_thermo_coupling_options(
    model: &AnalysisModel,
    options: &ThermoMechanicalCouplingOptions,
) -> Result<(), (String, BTreeMap<String, String>)> {
    if !options.enabled {
        return Ok(());
    }
    if !options.reference_temperature_k.is_finite() || options.reference_temperature_k <= 0.0 {
        return Err((
            "thermo coupling requires finite positive reference_temperature_k".to_string(),
            BTreeMap::from([(
                "reference_temperature_k".to_string(),
                options.reference_temperature_k.to_string(),
            )]),
        ));
    }
    if !options.applied_temperature_delta_k.is_finite() {
        return Err((
            "thermo coupling requires finite applied_temperature_delta_k".to_string(),
            BTreeMap::from([(
                "applied_temperature_delta_k".to_string(),
                options.applied_temperature_delta_k.to_string(),
            )]),
        ));
    }
    if !options.thermal_expansion_coefficient.is_finite()
        || options.thermal_expansion_coefficient < 0.0
    {
        return Err((
            "thermo coupling requires finite non-negative thermal_expansion_coefficient"
                .to_string(),
            BTreeMap::from([(
                "thermal_expansion_coefficient".to_string(),
                options.thermal_expansion_coefficient.to_string(),
            )]),
        ));
    }

    let mut last_t = -1.0_f64;
    for (idx, point) in options.time_profile.iter().enumerate() {
        if !point.normalized_time.is_finite()
            || point.normalized_time < 0.0
            || point.normalized_time > 1.0
        {
            return Err((
                "thermo time_profile normalized_time must be finite and within [0, 1]".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    (
                        "normalized_time".to_string(),
                        point.normalized_time.to_string(),
                    ),
                ]),
            ));
        }
        if !point.scale.is_finite() {
            return Err((
                "thermo time_profile scale must be finite".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    ("scale".to_string(), point.scale.to_string()),
                ]),
            ));
        }
        if point.normalized_time + 1.0e-12 < last_t {
            return Err((
                "thermo time_profile normalized_time must be monotonic non-decreasing".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    (
                        "normalized_time".to_string(),
                        point.normalized_time.to_string(),
                    ),
                ]),
            ));
        }
        last_t = point.normalized_time;
    }

    let model_region_ids = model
        .material_assignments
        .iter()
        .map(|assignment| assignment.region_id.as_str())
        .collect::<HashSet<_>>();

    for delta in &options.region_temperature_deltas {
        if !delta.temperature_delta_k.is_finite() {
            return Err((
                "thermo region_temperature_deltas must use finite temperature_delta_k".to_string(),
                BTreeMap::from([
                    ("region_id".to_string(), delta.region_id.clone()),
                    (
                        "temperature_delta_k".to_string(),
                        delta.temperature_delta_k.to_string(),
                    ),
                ]),
            ));
        }
    }

    if let Some(source) = options.field_source.as_ref() {
        if source.source_id.trim().is_empty() {
            return Err((
                "thermo field_source requires a non-empty source_id".to_string(),
                BTreeMap::new(),
            ));
        }
        for expected_region in &source.expected_region_ids {
            if !model_region_ids.contains(expected_region.as_str()) {
                return Err((
                    "thermo field_source expected_region_ids must exist in model material assignments"
                        .to_string(),
                    BTreeMap::from([("region_id".to_string(), expected_region.clone())]),
                ));
            }
        }
    }

    Ok(())
}

fn validate_electro_coupling_options(
    model: &AnalysisModel,
    options: &ElectroThermalCouplingOptions,
) -> Result<(), (String, BTreeMap<String, String>)> {
    if !options.enabled {
        return Ok(());
    }
    if !options.reference_temperature_k.is_finite() || options.reference_temperature_k <= 0.0 {
        return Err((
            "electro-thermal coupling requires finite positive reference_temperature_k".to_string(),
            BTreeMap::from([(
                "reference_temperature_k".to_string(),
                options.reference_temperature_k.to_string(),
            )]),
        ));
    }
    if !options.applied_voltage_v.is_finite() {
        return Err((
            "electro-thermal coupling requires finite applied_voltage_v".to_string(),
            BTreeMap::from([(
                "applied_voltage_v".to_string(),
                options.applied_voltage_v.to_string(),
            )]),
        ));
    }
    if !options.base_electrical_conductivity_s_per_m.is_finite()
        || options.base_electrical_conductivity_s_per_m <= 0.0
    {
        return Err((
            "electro-thermal coupling requires finite positive base_electrical_conductivity_s_per_m"
                .to_string(),
            BTreeMap::from([(
                "base_electrical_conductivity_s_per_m".to_string(),
                options.base_electrical_conductivity_s_per_m.to_string(),
            )]),
        ));
    }
    if !options.resistive_heating_coefficient.is_finite()
        || options.resistive_heating_coefficient < 0.0
    {
        return Err((
            "electro-thermal coupling requires finite non-negative resistive_heating_coefficient"
                .to_string(),
            BTreeMap::from([(
                "resistive_heating_coefficient".to_string(),
                options.resistive_heating_coefficient.to_string(),
            )]),
        ));
    }
    let mut last_t = -1.0_f64;
    for (idx, point) in options.time_profile.iter().enumerate() {
        if !point.normalized_time.is_finite()
            || point.normalized_time < 0.0
            || point.normalized_time > 1.0
        {
            return Err((
                "electro time_profile normalized_time must be finite and within [0, 1]".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    (
                        "normalized_time".to_string(),
                        point.normalized_time.to_string(),
                    ),
                ]),
            ));
        }
        if !point.current_scale.is_finite() {
            return Err((
                "electro time_profile current_scale must be finite".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    ("current_scale".to_string(), point.current_scale.to_string()),
                ]),
            ));
        }
        if point.normalized_time + 1.0e-12 < last_t {
            return Err((
                "electro time_profile normalized_time must be monotonic non-decreasing".to_string(),
                BTreeMap::from([
                    ("time_profile_index".to_string(), idx.to_string()),
                    (
                        "normalized_time".to_string(),
                        point.normalized_time.to_string(),
                    ),
                ]),
            ));
        }
        last_t = point.normalized_time;
    }
    let model_region_ids = model
        .material_assignments
        .iter()
        .map(|assignment| assignment.region_id.as_str())
        .collect::<HashSet<_>>();
    for scale in &options.region_conductivity_scales {
        if !scale.conductivity_scale.is_finite() || scale.conductivity_scale <= 0.0 {
            return Err((
                "electro region_conductivity_scales must use finite positive conductivity_scale"
                    .to_string(),
                BTreeMap::from([
                    ("region_id".to_string(), scale.region_id.clone()),
                    (
                        "conductivity_scale".to_string(),
                        scale.conductivity_scale.to_string(),
                    ),
                ]),
            ));
        }
        if !model_region_ids.is_empty() && !model_region_ids.contains(scale.region_id.as_str()) {
            return Err((
                "electro region_conductivity_scales region_id must exist in model material assignments"
                    .to_string(),
                BTreeMap::from([("region_id".to_string(), scale.region_id.clone())]),
            ));
        }
    }
    Ok(())
}

fn validate_plasticity_constitutive_options(
    options: &PlasticityConstitutiveOptions,
) -> Result<(), (String, BTreeMap<String, String>)> {
    if !options.enabled {
        return Ok(());
    }
    if !options.yield_strain.is_finite() || options.yield_strain <= 0.0 {
        return Err((
            "plasticity constitutive model requires finite positive yield_strain".to_string(),
            BTreeMap::from([("yield_strain".to_string(), options.yield_strain.to_string())]),
        ));
    }
    if !options.hardening_modulus_ratio.is_finite() || options.hardening_modulus_ratio < 0.0 {
        return Err((
            "plasticity constitutive model requires finite non-negative hardening_modulus_ratio"
                .to_string(),
            BTreeMap::from([(
                "hardening_modulus_ratio".to_string(),
                options.hardening_modulus_ratio.to_string(),
            )]),
        ));
    }
    if !options.saturation_exponent.is_finite() || options.saturation_exponent < 0.0 {
        return Err((
            "plasticity constitutive model requires finite non-negative saturation_exponent"
                .to_string(),
            BTreeMap::from([(
                "saturation_exponent".to_string(),
                options.saturation_exponent.to_string(),
            )]),
        ));
    }
    Ok(())
}

fn validate_contact_interface_options(
    options: &ContactInterfaceOptions,
) -> Result<(), (String, BTreeMap<String, String>)> {
    if !options.enabled {
        return Ok(());
    }
    if !options.penalty_stiffness_scale.is_finite() || options.penalty_stiffness_scale <= 0.0 {
        return Err((
            "contact interface model requires finite positive penalty_stiffness_scale".to_string(),
            BTreeMap::from([(
                "penalty_stiffness_scale".to_string(),
                options.penalty_stiffness_scale.to_string(),
            )]),
        ));
    }
    if !options.max_penetration_ratio.is_finite() || options.max_penetration_ratio < 0.0 {
        return Err((
            "contact interface model requires finite non-negative max_penetration_ratio"
                .to_string(),
            BTreeMap::from([(
                "max_penetration_ratio".to_string(),
                options.max_penetration_ratio.to_string(),
            )]),
        ));
    }
    if !options.friction_coefficient.is_finite() || options.friction_coefficient < 0.0 {
        return Err((
            "contact interface model requires finite non-negative friction_coefficient".to_string(),
            BTreeMap::from([(
                "friction_coefficient".to_string(),
                options.friction_coefficient.to_string(),
            )]),
        ));
    }
    Ok(())
}

fn validate_coupled_flow_interfaces(
    model: &AnalysisModel,
    family: &str,
) -> Result<(), (String, BTreeMap<String, String>)> {
    for interface in &model.interfaces {
        match &interface.kind {
            AnalysisInterfaceKind::Contact(_) => {
                return Err((
                    format!(
                        "{family} coupling does not accept structural contact interfaces as fluid/thermal interface mappings"
                    ),
                    BTreeMap::from([
                        ("interface_id".to_string(), interface.interface_id.clone()),
                        (
                            "primary_region_id".to_string(),
                            interface.primary_region_id.clone(),
                        ),
                        (
                            "secondary_region_id".to_string(),
                            interface.secondary_region_id.clone(),
                        ),
                        ("interface_kind".to_string(), "contact".to_string()),
                    ]),
                ));
            }
            AnalysisInterfaceKind::FluidStructure(fluid_structure) => {
                if family != "FSI" {
                    return Err((
                        format!(
                            "{family} coupling does not accept fluid-structure interfaces as fluid/thermal interface mappings"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "primary_region_id".to_string(),
                                interface.primary_region_id.clone(),
                            ),
                            (
                                "secondary_region_id".to_string(),
                                interface.secondary_region_id.clone(),
                            ),
                            ("interface_kind".to_string(), "fluid_structure".to_string()),
                        ]),
                    ));
                }
                if !fluid_structure.normal_stiffness_pa_per_m.is_finite()
                    || fluid_structure.normal_stiffness_pa_per_m <= 0.0
                {
                    return Err((
                        format!(
                            "{family} fluid-structure interface requires finite positive normal_stiffness_pa_per_m"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "normal_stiffness_pa_per_m".to_string(),
                                fluid_structure.normal_stiffness_pa_per_m.to_string(),
                            ),
                        ]),
                    ));
                }
                if !fluid_structure.damping_ratio.is_finite() || fluid_structure.damping_ratio < 0.0
                {
                    return Err((
                        format!(
                            "{family} fluid-structure interface requires finite non-negative damping_ratio"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "damping_ratio".to_string(),
                                fluid_structure.damping_ratio.to_string(),
                            ),
                        ]),
                    ));
                }
                if !fluid_structure.relaxation_factor.is_finite()
                    || fluid_structure.relaxation_factor <= 0.0
                    || fluid_structure.relaxation_factor > 1.0
                {
                    return Err((
                        format!(
                            "{family} fluid-structure interface requires relaxation_factor in (0, 1]"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "relaxation_factor".to_string(),
                                fluid_structure.relaxation_factor.to_string(),
                            ),
                        ]),
                    ));
                }
            }
            AnalysisInterfaceKind::ConjugateHeatTransfer(cht_interface) => {
                if family != "CHT" {
                    return Err((
                        format!(
                            "{family} coupling does not accept conjugate heat-transfer interfaces as fluid/structure interface mappings"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "primary_region_id".to_string(),
                                interface.primary_region_id.clone(),
                            ),
                            (
                                "secondary_region_id".to_string(),
                                interface.secondary_region_id.clone(),
                            ),
                            (
                                "interface_kind".to_string(),
                                "conjugate_heat_transfer".to_string(),
                            ),
                        ]),
                    ));
                }
                if !cht_interface.thermal_conductance_w_per_m2k.is_finite()
                    || cht_interface.thermal_conductance_w_per_m2k <= 0.0
                {
                    return Err((
                        format!(
                            "{family} conjugate heat-transfer interface requires finite positive thermal_conductance_w_per_m2k"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "thermal_conductance_w_per_m2k".to_string(),
                                cht_interface.thermal_conductance_w_per_m2k.to_string(),
                            ),
                        ]),
                    ));
                }
                if !cht_interface.contact_resistance_m2k_per_w.is_finite()
                    || cht_interface.contact_resistance_m2k_per_w < 0.0
                {
                    return Err((
                        format!(
                            "{family} conjugate heat-transfer interface requires finite non-negative contact_resistance_m2k_per_w"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "contact_resistance_m2k_per_w".to_string(),
                                cht_interface.contact_resistance_m2k_per_w.to_string(),
                            ),
                        ]),
                    ));
                }
                if !cht_interface.relaxation_factor.is_finite()
                    || cht_interface.relaxation_factor <= 0.0
                    || cht_interface.relaxation_factor > 1.0
                {
                    return Err((
                        format!(
                            "{family} conjugate heat-transfer interface requires relaxation_factor in (0, 1]"
                        ),
                        BTreeMap::from([
                            ("interface_id".to_string(), interface.interface_id.clone()),
                            (
                                "relaxation_factor".to_string(),
                                cht_interface.relaxation_factor.to_string(),
                            ),
                        ]),
                    ));
                }
            }
        }
    }
    Ok(())
}

#[derive(Debug, Clone, Deserialize)]
struct ThermoFieldArtifact {
    schema_version: String,
    source_geometry_id: String,
    source_geometry_revision: u32,
    #[serde(default)]
    artifact_status: Option<String>,
    #[serde(default)]
    approved_by: Option<String>,
    #[serde(default)]
    payload_hash: Option<String>,
    #[serde(default)]
    signature: Option<String>,
    #[serde(default)]
    field_source: Option<ThermoFieldSource>,
    #[serde(default)]
    region_temperature_deltas: Vec<ThermoRegionTemperatureDelta>,
    #[serde(default)]
    time_profile: Vec<ThermoTimeProfilePoint>,
}

fn thermo_field_payload_hash(artifact: &ThermoFieldArtifact) -> String {
    let source = artifact.field_source.as_ref();
    let source_id = source.map(|s| s.source_id.as_str()).unwrap_or("");
    let source_revision = source.map(|s| s.revision).unwrap_or(0);
    let interpolation = source
        .and_then(|s| s.interpolation_mode)
        .map(|mode| match mode {
            ThermoFieldInterpolationMode::Linear => "linear",
            ThermoFieldInterpolationMode::Step => "step",
        })
        .unwrap_or("");
    let expected_regions = source
        .map(|s| s.expected_region_ids.join(","))
        .unwrap_or_default();
    let region_terms = artifact
        .region_temperature_deltas
        .iter()
        .map(|delta| {
            format!(
                "{}:{:016x}",
                delta.region_id,
                delta.temperature_delta_k.to_bits()
            )
        })
        .collect::<Vec<_>>()
        .join(",");
    let time_terms = artifact
        .time_profile
        .iter()
        .map(|point| {
            format!(
                "{:016x}:{:016x}",
                point.normalized_time.to_bits(),
                point.scale.to_bits()
            )
        })
        .collect::<Vec<_>>()
        .join(",");
    let canonical = format!(
        "{}|{}|{}|{}|{}|{}|{}|{}|{}",
        artifact.schema_version,
        artifact.source_geometry_id,
        artifact.source_geometry_revision,
        source_id,
        source_revision,
        interpolation,
        expected_regions,
        region_terms,
        time_terms
    );
    let mut hasher = Sha256::new();
    hasher.update(canonical.as_bytes());
    format!("sha256:{:x}", hasher.finalize())
}

fn thermo_field_signature(payload_hash: &str, approved_by: &str, signing_key: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(format!("{payload_hash}:{approved_by}:{signing_key}").as_bytes());
    format!("sigv1:sha256:{:x}", hasher.finalize())
}

fn resolve_thermo_coupling_options(
    model: &AnalysisModel,
    options: Option<ThermoMechanicalCouplingOptions>,
    operation: &'static str,
    op_version: &'static str,
    context: &OperationContext,
) -> Result<Option<ThermoMechanicalCouplingOptions>, OperationErrorEnvelope> {
    let Some(mut options) = options else {
        return Ok(None);
    };
    let Some(field_artifact_id) = options.field_artifact_id.as_deref() else {
        return Ok(Some(options));
    };

    let root = thermo_field_artifact_root();
    let path = root.join(format!("{field_artifact_id}.json"));
    if !fs_exists(&path).map_err(|err| {
        operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to inspect thermo field artifact: {err}"),
            BTreeMap::from([
                (
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                ),
                (
                    "thermo_field_artifact_path".to_string(),
                    path.display().to_string(),
                ),
            ]),
        )
    })? {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.NOT_FOUND",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!(
                "thermo field artifact '{}' was not found",
                field_artifact_id
            ),
            BTreeMap::from([
                (
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                ),
                (
                    "thermo_field_artifact_path".to_string(),
                    path.display().to_string(),
                ),
            ]),
        ));
    }

    let raw = fs_read_to_string(&path).map_err(|err| {
        operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to read thermo field artifact: {err}"),
            BTreeMap::from([(
                "thermo_field_artifact_path".to_string(),
                path.display().to_string(),
            )]),
        )
    })?;
    let artifact: ThermoFieldArtifact = serde_json::from_str(&raw).map_err(|err| {
        operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.INVALID",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!("invalid thermo field artifact payload: {err}"),
            BTreeMap::from([(
                "thermo_field_artifact_path".to_string(),
                path.display().to_string(),
            )]),
        )
    })?;

    if artifact.schema_version != "fea_thermo_field_artifact/v1"
        && artifact.schema_version != "analysis_thermo_field_artifact/v1"
    {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.SCHEMA_UNSUPPORTED",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!(
                "thermo field artifact schema '{}' is not supported",
                artifact.schema_version
            ),
            BTreeMap::from([
                (
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                ),
                (
                    "thermo_field_artifact_schema".to_string(),
                    artifact.schema_version.clone(),
                ),
            ]),
        ));
    }

    if artifact.source_geometry_id != model.geometry_id
        || artifact.source_geometry_revision != model.geometry_revision
    {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.MISMATCH",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "thermo field artifact geometry lineage does not match FEA model",
            BTreeMap::from([
                (
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                ),
                ("model_geometry_id".to_string(), model.geometry_id.clone()),
                (
                    "model_geometry_revision".to_string(),
                    model.geometry_revision.to_string(),
                ),
                (
                    "artifact_geometry_id".to_string(),
                    artifact.source_geometry_id.clone(),
                ),
                (
                    "artifact_geometry_revision".to_string(),
                    artifact.source_geometry_revision.to_string(),
                ),
            ]),
        ));
    }

    let expected_hash = thermo_field_payload_hash(&artifact);
    if artifact.payload_hash.as_deref() != Some(expected_hash.as_str()) {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_THERMO_FIELD.DIGEST_MISMATCH",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "thermo field artifact payload hash does not match payload contents",
            BTreeMap::from([
                (
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                ),
                ("expected_payload_hash".to_string(), expected_hash),
                (
                    "artifact_payload_hash".to_string(),
                    artifact.payload_hash.clone().unwrap_or_default(),
                ),
            ]),
        ));
    }

    if matches!(artifact.artifact_status.as_deref(), Some("approved")) {
        let Some(approved_by) = artifact.approved_by.as_deref() else {
            return Err(operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_THERMO_FIELD.APPROVER_MISSING",
                    error_type: OperationErrorType::Validation,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                "approved thermo field artifact is missing approved_by",
                BTreeMap::from([(
                    "thermo_field_artifact_id".to_string(),
                    field_artifact_id.to_string(),
                )]),
            ));
        };

        let allowed = std::env::var("RUNMAT_THERMO_FIELD_ALLOWED_APPROVERS")
            .ok()
            .map(|value| {
                value
                    .split(',')
                    .map(|entry| entry.trim().to_string())
                    .filter(|entry| !entry.is_empty())
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default();
        if !allowed.is_empty() && !allowed.iter().any(|entry| entry == approved_by) {
            return Err(operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_THERMO_FIELD.APPROVER_UNAUTHORIZED",
                    error_type: OperationErrorType::Validation,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                "thermo field artifact approver is not authorized",
                BTreeMap::from([
                    (
                        "thermo_field_artifact_id".to_string(),
                        field_artifact_id.to_string(),
                    ),
                    ("approved_by".to_string(), approved_by.to_string()),
                ]),
            ));
        }

        let signing_key = std::env::var("RUNMAT_THERMO_FIELD_SIGNING_KEY")
            .unwrap_or_else(|_| "runmat-dev-thermo-signing-key".to_string());
        let expected_signature = thermo_field_signature(&expected_hash, approved_by, &signing_key);
        if artifact.signature.as_deref() != Some(expected_signature.as_str()) {
            return Err(operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_THERMO_FIELD.SIGNATURE_INVALID",
                    error_type: OperationErrorType::Validation,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                "thermo field artifact signature validation failed",
                BTreeMap::from([
                    (
                        "thermo_field_artifact_id".to_string(),
                        field_artifact_id.to_string(),
                    ),
                    ("expected_signature".to_string(), expected_signature),
                    (
                        "artifact_signature".to_string(),
                        artifact.signature.clone().unwrap_or_default(),
                    ),
                ]),
            ));
        }
    }

    options.field_source = artifact.field_source;
    options.region_temperature_deltas = artifact.region_temperature_deltas;
    options.time_profile = artifact.time_profile;

    Ok(Some(options))
}

fn resolve_run_prep_context(
    model: &AnalysisModel,
    prep_artifact_id: Option<&str>,
    legacy_prep_context: Option<AnalysisRunPrepContext>,
    operation: &'static str,
    op_version: &'static str,
    context: &OperationContext,
) -> Result<Option<AnalysisRunPrepContext>, OperationErrorEnvelope> {
    if prep_artifact_id.is_none() {
        if legacy_prep_context.is_some() {
            return Err(operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_PREP.UNTRUSTED_CONTEXT",
                    error_type: OperationErrorType::Input,
                    retryable: false,
                    severity: OperationErrorSeverity::Error,
                },
                "FEA run prep_context must be referenced by prep_artifact_id",
                BTreeMap::from([("analysis_model_id".to_string(), model.model_id.0.clone())]),
            ));
        }
        return Ok(None);
    }

    let prep_artifact_id = prep_artifact_id.expect("checked is_some");
    let Some(artifact) = crate::geometry::load_prep_artifact(prep_artifact_id).map_err(|err| {
        operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_PREP.STORE_FAILED",
                error_type: OperationErrorType::Internal,
                retryable: true,
                severity: OperationErrorSeverity::Error,
            },
            format!("failed to load prep artifact: {err}"),
            BTreeMap::from([("prep_artifact_id".to_string(), prep_artifact_id.to_string())]),
        )
    })?
    else {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_PREP.NOT_FOUND",
                error_type: OperationErrorType::Input,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!("prep artifact '{}' was not found", prep_artifact_id),
            BTreeMap::from([("prep_artifact_id".to_string(), prep_artifact_id.to_string())]),
        ));
    };

    if artifact.schema_version != "geometry_prep_artifact/v1" {
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_PREP.SCHEMA_UNSUPPORTED",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            format!(
                "prep artifact schema '{}' is not supported",
                artifact.schema_version
            ),
            BTreeMap::from([("prep_artifact_id".to_string(), prep_artifact_id.to_string())]),
        ));
    }

    if artifact.source_geometry_id != model.geometry_id
        || artifact.source_geometry_revision != model.geometry_revision
    {
        crate::geometry::record_prep_mismatch_reject();
        return Err(operation_error(
            operation,
            op_version,
            context,
            OperationErrorSpec {
                error_code: "RM.FEA.RUN_PREP.MISMATCH",
                error_type: OperationErrorType::Validation,
                retryable: false,
                severity: OperationErrorSeverity::Error,
            },
            "prep artifact geometry lineage does not match FEA model",
            BTreeMap::from([
                ("prep_artifact_id".to_string(), prep_artifact_id.to_string()),
                ("model_geometry_id".to_string(), model.geometry_id.clone()),
                (
                    "model_geometry_revision".to_string(),
                    model.geometry_revision.to_string(),
                ),
                (
                    "prep_geometry_id".to_string(),
                    artifact.source_geometry_id.clone(),
                ),
                (
                    "prep_geometry_revision".to_string(),
                    artifact.source_geometry_revision.to_string(),
                ),
            ]),
        ));
    }

    if crate::geometry::require_latest_prep_revision() {
        if let Some(latest_revision) = crate::geometry::latest_prep_revision_for_geometry(
            &model.geometry_id,
        )
        .map_err(|err| {
            operation_error(
                operation,
                op_version,
                context,
                OperationErrorSpec {
                    error_code: "RM.FEA.RUN_PREP.STORE_FAILED",
                    error_type: OperationErrorType::Internal,
                    retryable: true,
                    severity: OperationErrorSeverity::Error,
                },
                format!("failed to evaluate prep artifact freshness: {err}"),
                BTreeMap::from([("prep_artifact_id".to_string(), prep_artifact_id.to_string())]),
            )
        })? {
            if artifact.source_geometry_revision < latest_revision {
                crate::geometry::record_prep_stale_reject();
                return Err(operation_error(
                    operation,
                    op_version,
                    context,
                    OperationErrorSpec {
                        error_code: "RM.FEA.RUN_PREP.STALE",
                        error_type: OperationErrorType::Validation,
                        retryable: false,
                        severity: OperationErrorSeverity::Error,
                    },
                    "prep artifact is stale; a newer geometry revision prep artifact exists",
                    BTreeMap::from([
                        ("prep_artifact_id".to_string(), prep_artifact_id.to_string()),
                        (
                            "prep_geometry_revision".to_string(),
                            artifact.source_geometry_revision.to_string(),
                        ),
                        (
                            "latest_geometry_revision".to_string(),
                            latest_revision.to_string(),
                        ),
                    ]),
                ));
            }
        }
    }

    let prepared_mesh_count = artifact.prep.prepared_meshes.len();
    let prepared_node_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.node_count as usize)
        .sum::<usize>();
    let prepared_element_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.element_count as usize)
        .sum::<usize>();
    let mesh_count = prepared_mesh_count.max(1) as f64;
    let topology_surface_patch_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.connectivity_class == MeshConnectivityClass::SurfacePatch)
        .count() as f64
        / mesh_count;
    let topology_volume_core_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.connectivity_class == MeshConnectivityClass::VolumeCore)
        .count() as f64
        / mesh_count;
    let topology_mixed_family_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.element_family_hint == ElementFamilyHint::Mixed)
        .count() as f64
        / mesh_count;
    let topology_triangle_family_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.element_family_hint == ElementFamilyHint::Triangle)
        .count() as f64
        / mesh_count;
    let topology_quad_family_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.element_family_hint == ElementFamilyHint::Quad)
        .count() as f64
        / mesh_count;
    let topology_tet_family_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.element_family_hint == ElementFamilyHint::Tet)
        .count() as f64
        / mesh_count;
    let topology_hex_family_ratio = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter(|mesh| mesh.element_family_hint == ElementFamilyHint::Hex)
        .count() as f64
        / mesh_count;
    let topology_region_span_mean = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.region_span_hint as f64)
        .sum::<f64>()
        / mesh_count;
    let region_block_count = artifact.prep.region_mappings.len().max(1);
    let region_mesh_counts = artifact
        .prep
        .region_mappings
        .iter()
        .map(|mapping| mapping.prepared_mesh_ids.len().max(1) as f64)
        .collect::<Vec<_>>();
    let topology_region_mesh_mean = if region_mesh_counts.is_empty() {
        1.0
    } else {
        region_mesh_counts.iter().sum::<f64>() / region_mesh_counts.len() as f64
    };
    let topology_region_mesh_variance = if region_mesh_counts.len() <= 1 {
        0.0
    } else {
        region_mesh_counts
            .iter()
            .map(|count| {
                let delta = *count - topology_region_mesh_mean;
                delta * delta
            })
            .sum::<f64>()
            / region_mesh_counts.len() as f64
    };
    let topology_dof_multiplier = if model.loads.is_empty() {
        1.0
    } else {
        ((prepared_node_count as f64 / (model.loads.len() as f64 * 3.0)).clamp(1.0, 4.0) * 0.35
            + 1.0)
            .min(4.0)
    };
    let topology_bandwidth_estimate = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.region_span_hint)
        .sum::<u32>()
        .clamp(1, 128);
    let mapped_region_participation_ratio = if artifact.prep.region_mappings.is_empty() {
        0.0
    } else {
        (artifact
            .prep
            .region_mappings
            .iter()
            .filter(|mapping| {
                model
                    .loads
                    .iter()
                    .any(|load| load.region_id == mapping.region_id)
                    || model
                        .boundary_conditions
                        .iter()
                        .any(|bc| bc.region_id == mapping.region_id)
            })
            .count() as f64
            / artifact.prep.region_mappings.len() as f64)
            .clamp(0.0, 1.0)
    };
    let coordinate_span_x_m = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.coordinate_span_m[0])
        .fold(0.0_f64, f64::max)
        .max(1.0e-12);
    let coordinate_span_y_m = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.coordinate_span_m[1])
        .fold(0.0_f64, f64::max);
    let coordinate_span_z_m = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.coordinate_span_m[2])
        .fold(0.0_f64, f64::max);
    let coordinate_active_dimension_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.coordinate_active_dimension_count as usize)
        .max()
        .unwrap_or(1)
        .max(1);
    let (coordinate_length_sum, coordinate_length_weight) = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter_map(|mesh| {
            let length = mesh.coordinate_characteristic_length_m;
            (length.is_finite() && length > 0.0)
                .then_some((length, mesh.element_count.max(1) as f64))
        })
        .fold((0.0_f64, 0.0_f64), |(sum, weight_sum), (length, weight)| {
            (sum + length * weight, weight_sum + weight)
        });
    let coordinate_characteristic_length_m = if coordinate_length_weight > 0.0 {
        coordinate_length_sum / coordinate_length_weight
    } else {
        1.0
    };
    let element_geometry_node_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.element_geometry_node_count as usize)
        .sum::<usize>();
    let element_geometry_edge_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.element_geometry_edge_count as usize)
        .sum::<usize>();
    let (edge_length_sum, edge_length_weight) = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter_map(|mesh| {
            let length = mesh.mean_element_edge_length_m;
            (length.is_finite() && length > 0.0)
                .then_some((length, mesh.element_count.max(1) as f64))
        })
        .fold((0.0_f64, 0.0_f64), |(sum, weight_sum), (length, weight)| {
            (sum + length * weight, weight_sum + weight)
        });
    let mean_element_edge_length_m = if edge_length_weight > 0.0 {
        edge_length_sum / edge_length_weight
    } else {
        0.0
    };
    let (area_sum, area_weight) = artifact
        .prep
        .prepared_meshes
        .iter()
        .filter_map(|mesh| {
            let area = mesh.mean_element_area_m2;
            (area.is_finite() && area > 0.0).then_some((area, mesh.element_count.max(1) as f64))
        })
        .fold((0.0_f64, 0.0_f64), |(sum, weight_sum), (area, weight)| {
            (sum + area * weight, weight_sum + weight)
        });
    let mean_element_area_m2 = if area_weight > 0.0 {
        area_sum / area_weight
    } else {
        0.0
    };
    let (coverage_sum, coverage_weight) = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| {
            (
                mesh.element_geometry_coverage_ratio.clamp(0.0, 1.0),
                mesh.element_count.max(1) as f64,
            )
        })
        .fold(
            (0.0_f64, 0.0_f64),
            |(sum, weight_sum), (coverage, weight)| (sum + coverage * weight, weight_sum + weight),
        );
    let element_geometry_coverage_ratio = if coverage_weight > 0.0 {
        coverage_sum / coverage_weight
    } else {
        0.0
    };
    let (reference_element_coordinates_m, reference_element_area_m2) = artifact
        .prep
        .prepared_meshes
        .iter()
        .find_map(|mesh| {
            let area = mesh.reference_element_area_m2;
            (area.is_finite() && area > 0.0).then_some((mesh.reference_element_coordinates_m, area))
        })
        .unwrap_or(([[0.0; 3]; 3], 0.0));
    let (
        element_topology_sample_element_count,
        element_topology_sample_edge_count,
        element_topology_sample_edge_nodes,
        element_topology_sample_node_coordinates_m,
        element_topology_sample_element_edges,
        element_topology_sample_element_orientations,
        element_topology_sample_element_areas_m2,
    ) = artifact
        .prep
        .prepared_meshes
        .iter()
        .find_map(|mesh| {
            (mesh.element_topology_sample_element_count > 0
                && mesh.element_topology_sample_edge_count > 0)
                .then_some((
                    mesh.element_topology_sample_element_count as usize,
                    mesh.element_topology_sample_edge_count as usize,
                    mesh.element_topology_sample_edge_nodes,
                    mesh.element_topology_sample_node_coordinates_m,
                    mesh.element_topology_sample_element_edges,
                    mesh.element_topology_sample_element_orientations,
                    mesh.element_topology_sample_element_areas_m2,
                ))
        })
        .unwrap_or((
            0,
            0,
            [[0; 2]; 8],
            [[0.0; 3]; 8],
            [[0; 3]; 4],
            [[0; 3]; 4],
            [0.0; 4],
        ));
    let mut element_topology_node_coordinates_m = Vec::<[f64; 3]>::new();
    let mut element_topology_edge_nodes = Vec::<[u32; 2]>::new();
    let mut element_topology_element_edges = Vec::<[u32; 3]>::new();
    let mut element_topology_element_orientations = Vec::<[i8; 3]>::new();
    let mut element_topology_element_areas_m2 = Vec::<f64>::new();
    let mut node_offset = 0_u32;
    let mut edge_offset = 0_u32;
    for mesh in &artifact.prep.prepared_meshes {
        let node_count = mesh.element_topology_node_coordinates_m.len();
        element_topology_node_coordinates_m
            .extend(mesh.element_topology_node_coordinates_m.iter().copied());
        for edge in &mesh.element_topology_edge_nodes {
            if let (Some(left), Some(right)) = (
                edge[0].checked_add(node_offset),
                edge[1].checked_add(node_offset),
            ) {
                element_topology_edge_nodes.push([left, right]);
            }
        }
        for element_edges in &mesh.element_topology_element_edges {
            if let (Some(a), Some(b), Some(c)) = (
                element_edges[0].checked_add(edge_offset),
                element_edges[1].checked_add(edge_offset),
                element_edges[2].checked_add(edge_offset),
            ) {
                element_topology_element_edges.push([a, b, c]);
            }
        }
        element_topology_element_orientations
            .extend(mesh.element_topology_element_orientations.iter().copied());
        element_topology_element_areas_m2
            .extend(mesh.element_topology_element_areas_m2.iter().copied());
        node_offset = node_offset.saturating_add(node_count as u32);
        edge_offset = edge_offset.saturating_add(mesh.element_topology_edge_nodes.len() as u32);
    }
    let control_volume_cell_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.control_volume_cell_count as usize)
        .sum::<usize>();
    let control_volume_face_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.control_volume_face_count as usize)
        .sum::<usize>();
    let control_volume_internal_face_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.control_volume_internal_face_count as usize)
        .sum::<usize>();
    let control_volume_boundary_face_count = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| mesh.control_volume_boundary_face_count as usize)
        .sum::<usize>();
    let (control_volume_coverage_sum, control_volume_coverage_weight) = artifact
        .prep
        .prepared_meshes
        .iter()
        .map(|mesh| {
            (
                mesh.control_volume_connectivity_coverage_ratio
                    .clamp(0.0, 1.0),
                mesh.element_count.max(1) as f64,
            )
        })
        .fold(
            (0.0_f64, 0.0_f64),
            |(sum, weight_sum), (coverage, weight)| (sum + coverage * weight, weight_sum + weight),
        );
    let control_volume_connectivity_coverage_ratio = if control_volume_coverage_weight > 0.0 {
        control_volume_coverage_sum / control_volume_coverage_weight
    } else {
        0.0
    };

    Ok(Some(AnalysisRunPrepContext {
        prepared_mesh_count,
        prepared_node_count,
        prepared_element_count,
        mapped_region_count: artifact.prep.region_mappings.len(),
        min_scaled_jacobian: artifact.prep.quality.min_scaled_jacobian,
        mean_aspect_ratio: artifact.prep.quality.mean_aspect_ratio,
        inverted_element_count: artifact.prep.quality.inverted_element_count as usize,
        mapped_load_count: model
            .loads
            .iter()
            .filter(|load| {
                artifact
                    .prep
                    .region_mappings
                    .iter()
                    .any(|mapping| mapping.region_id == load.region_id)
            })
            .count(),
        mapped_bc_count: model
            .boundary_conditions
            .iter()
            .filter(|bc| {
                artifact
                    .prep
                    .region_mappings
                    .iter()
                    .any(|mapping| mapping.region_id == bc.region_id)
            })
            .count(),
        layout_seed: {
            let mut seed = 1469598103934665603_u64;
            for mapping in &artifact.prep.region_mappings {
                for byte in mapping.region_id.as_bytes() {
                    seed ^= *byte as u64;
                    seed = seed.wrapping_mul(1099511628211_u64);
                }
            }
            seed
        },
        topology_dof_multiplier,
        topology_bandwidth_estimate,
        mapped_region_participation_ratio,
        topology_surface_patch_ratio,
        topology_volume_core_ratio,
        topology_mixed_family_ratio,
        topology_region_span_mean,
        topology_region_block_count: region_block_count,
        topology_region_mesh_mean,
        topology_region_mesh_variance,
        topology_triangle_family_ratio,
        topology_quad_family_ratio,
        topology_tet_family_ratio,
        topology_hex_family_ratio,
        coordinate_span_x_m,
        coordinate_span_y_m,
        coordinate_span_z_m,
        coordinate_active_dimension_count,
        coordinate_characteristic_length_m,
        element_geometry_node_count,
        element_geometry_edge_count,
        mean_element_edge_length_m,
        mean_element_area_m2,
        element_geometry_coverage_ratio,
        reference_element_coordinates_m,
        reference_element_area_m2,
        control_volume_cell_count,
        control_volume_face_count,
        control_volume_internal_face_count,
        control_volume_boundary_face_count,
        control_volume_connectivity_coverage_ratio,
        element_topology_sample_element_count,
        element_topology_sample_edge_count,
        element_topology_sample_edge_nodes,
        element_topology_sample_node_coordinates_m,
        element_topology_sample_element_edges,
        element_topology_sample_element_orientations,
        element_topology_sample_element_areas_m2,
        element_topology_node_coordinates_m,
        element_topology_edge_nodes,
        element_topology_element_edges,
        element_topology_element_orientations,
        element_topology_element_areas_m2,
    }))
}

fn run_solve_ms(run: &AnalysisRunResult) -> Option<f64> {
    for code in [
        "FEA_NONLINEAR_COST",
        "FEA_TRANSIENT_COST",
        "FEA_MODAL_COST",
        "FEA_ACOUSTIC_COST",
        "FEA_CFD_COST",
        "FEA_CHT_COST",
        "FEA_FSI_COST",
    ] {
        if let Some(value) = diagnostic_metric(&run.run.diagnostics, code, "solve_ms") {
            return Some(value);
        }
    }
    None
}

fn diagnostic_metric(
    diagnostics: &[runmat_analysis_fea::diagnostics::FeaDiagnostic],
    code: &str,
    key: &str,
) -> Option<f64> {
    diagnostics
        .iter()
        .find(|diag| diag.code == code)
        .and_then(|diag| {
            diag.message
                .split_whitespace()
                .find_map(|token| token.strip_prefix(&format!("{key}=")))
        })
        .and_then(|value| value.parse::<f64>().ok())
}

fn diagnostic_metric_u64(
    diagnostics: &[runmat_analysis_fea::diagnostics::FeaDiagnostic],
    code: &str,
    key: &str,
) -> Option<u64> {
    diagnostics
        .iter()
        .find(|diag| diag.code == code)
        .and_then(|diag| {
            diag.message
                .split_whitespace()
                .find_map(|token| token.strip_prefix(&format!("{key}=")))
        })
        .and_then(|value| value.parse::<u64>().ok())
}

fn diagnostic_metric_bool(
    diagnostics: &[runmat_analysis_fea::diagnostics::FeaDiagnostic],
    code: &str,
    key: &str,
) -> Option<bool> {
    diagnostics
        .iter()
        .find(|diag| diag.code == code)
        .and_then(|diag| {
            diag.message
                .split_whitespace()
                .find_map(|token| token.strip_prefix(&format!("{key}=")))
        })
        .and_then(|value| value.parse::<bool>().ok())
}

fn diagnostic_metric_string(
    diagnostics: &[runmat_analysis_fea::diagnostics::FeaDiagnostic],
    code: &str,
    key: &str,
) -> Option<String> {
    diagnostics
        .iter()
        .find(|diag| diag.code == code)
        .and_then(|diag| {
            diag.message
                .split_whitespace()
                .find_map(|token| token.strip_prefix(&format!("{key}=")))
        })
        .map(|value| value.to_string())
}

fn percentile(sorted_samples: &[f64], ratio: f64) -> Option<f64> {
    if sorted_samples.is_empty() {
        return None;
    }
    let index = ((sorted_samples.len() - 1) as f64 * ratio.clamp(0.0, 1.0)).round() as usize;
    sorted_samples.get(index).copied()
}

fn mean(values: &[f64]) -> f64 {
    if values.is_empty() {
        0.0
    } else {
        values.iter().sum::<f64>() / values.len() as f64
    }
}

fn calibration_profile_rate(entries: &[AnalysisRunResult], profile: &str) -> Option<f64> {
    let values = entries
        .iter()
        .filter_map(|run| {
            diagnostic_metric_string(&run.run.diagnostics, "FEA_PREP_CALIBRATION", "profile")
        })
        .collect::<Vec<_>>();
    if values.is_empty() {
        return None;
    }
    Some(
        values
            .iter()
            .filter(|value| value.as_str() == profile)
            .count() as f64
            / values.len() as f64,
    )
}

fn diagnostic_warning_rate(entries: &[AnalysisRunResult], code: &str) -> Option<f64> {
    let values = entries
        .iter()
        .filter_map(|run| {
            run.run
                .diagnostics
                .iter()
                .find(|diag| diag.code == code)
                .map(|diag| {
                    diag.severity
                        == runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
                })
        })
        .collect::<Vec<_>>();
    if values.is_empty() {
        None
    } else {
        Some(values.iter().filter(|value| **value).count() as f64 / values.len() as f64)
    }
}

fn infer_material_models(geometry: &GeometryAsset) -> Vec<MaterialModel> {
    let mut materials = Vec::new();
    for evidence in &geometry.source_geometry.material_evidence {
        let value = evidence.value.to_ascii_lowercase();
        let (material_id, name, youngs_modulus_pa, poisson_ratio) = if value.contains("aluminum") {
            ("mat_aluminum", "Aluminum", 69e9, 0.33)
        } else if value.contains("steel") {
            ("mat_steel", "Steel", 200e9, 0.30)
        } else if value.contains("polymer") || value.contains("plastic") {
            ("mat_polymer", "Polymer", 3.2e9, 0.37)
        } else {
            ("mat_inferred", "Inferred Material", 100e9, 0.32)
        };

        if materials
            .iter()
            .any(|m: &MaterialModel| m.material_id == material_id)
        {
            continue;
        }
        materials.push(MaterialModel {
            material_id: material_id.to_string(),
            name: name.to_string(),
            mechanical: MaterialMechanicalModel {
                youngs_modulus_pa,
                poisson_ratio,
                density_kg_per_m3: 7850.0,
            },
            thermal: MaterialThermalModel {
                reference_temperature_k: 293.15,
                modulus_temp_coeff_per_k: -2.5e-4,
                ..MaterialThermalModel::default()
            },
            acoustic: None,
            electrical: None,
            plastic: None,
        });
    }

    if materials.is_empty() {
        materials.push(MaterialModel {
            material_id: "mat_default_steel".to_string(),
            name: "Steel (Default)".to_string(),
            mechanical: MaterialMechanicalModel {
                youngs_modulus_pa: 200e9,
                poisson_ratio: 0.3,
                density_kg_per_m3: 7850.0,
            },
            thermal: MaterialThermalModel {
                reference_temperature_k: 293.15,
                modulus_temp_coeff_per_k: -2.5e-4,
                ..MaterialThermalModel::default()
            },
            acoustic: None,
            electrical: None,
            plastic: None,
        });
    }

    materials
}

fn select_fixed_region_id(
    geometry: &GeometryAsset,
    prep_regions: Option<&HashSet<String>>,
) -> Option<String> {
    geometry
        .regions
        .iter()
        .filter(|region| {
            prep_regions
                .map(|mapped| mapped.contains(&region.region_id))
                .unwrap_or(true)
        })
        .find(|region| {
            let key = format!(
                "{} {}",
                region.name.to_ascii_lowercase(),
                region
                    .tag
                    .as_deref()
                    .unwrap_or_default()
                    .to_ascii_lowercase()
            );
            key.contains("root")
                || key.contains("base")
                || key.contains("fixed")
                || key.contains("mount")
        })
        .map(|region| region.region_id.clone())
}

fn select_load_region_id(
    geometry: &GeometryAsset,
    prep_regions: Option<&HashSet<String>>,
) -> Option<String> {
    geometry
        .regions
        .iter()
        .filter(|region| {
            prep_regions
                .map(|mapped| mapped.contains(&region.region_id))
                .unwrap_or(true)
        })
        .find(|region| {
            let key = format!(
                "{} {}",
                region.name.to_ascii_lowercase(),
                region
                    .tag
                    .as_deref()
                    .unwrap_or_default()
                    .to_ascii_lowercase()
            );
            key.contains("tip")
                || key.contains("load")
                || key.contains("force")
                || key.contains("free")
        })
        .map(|region| region.region_id.clone())
}

#[derive(Debug, Clone, Default)]
struct EmSweepSummary {
    sweep_count: usize,
    resonance_peak_frequency_hz: Option<f64>,
    resonance_peak_flux_density: Option<f64>,
    resonance_bandwidth_hz: Option<f64>,
    resonance_quality_factor: Option<f64>,
    resonance_flux_gain: Option<f64>,
}

fn normalize_em_sweep_frequency_hz(
    reference_frequency_hz: f64,
    sweep_enabled: bool,
    requested: &[f64],
) -> Option<Vec<f64>> {
    let mut values = if sweep_enabled {
        requested.to_vec()
    } else {
        Vec::new()
    };
    if values.is_empty() {
        values.push(reference_frequency_hz);
    }
    if !values
        .iter()
        .all(|frequency_hz| frequency_hz.is_finite() && *frequency_hz > 0.0)
    {
        return None;
    }
    values.sort_by(|a, b| a.total_cmp(b));
    values.dedup_by(|a, b| (*a - *b).abs() <= 1.0e-9);
    Some(values)
}

fn nearest_frequency_index(frequencies_hz: &[f64], target_hz: f64) -> Option<usize> {
    frequencies_hz
        .iter()
        .enumerate()
        .min_by(|(_, a), (_, b)| (*a - target_hz).abs().total_cmp(&(*b - target_hz).abs()))
        .map(|(index, _)| index)
}

fn peak_abs_field_value(field: &runmat_analysis_core::AnalysisField) -> f64 {
    field
        .as_host_f64()
        .map(|values| values.iter().copied().map(f64::abs).fold(0.0_f64, f64::max))
        .unwrap_or(0.0)
}

fn summarize_em_sweep(frequencies_hz: &[f64], peak_flux_density: &[f64]) -> EmSweepSummary {
    if frequencies_hz.is_empty() || frequencies_hz.len() != peak_flux_density.len() {
        return EmSweepSummary::default();
    }
    let sweep_count = frequencies_hz.len();
    let (peak_index, peak_flux_density_value) = peak_flux_density
        .iter()
        .copied()
        .enumerate()
        .max_by(|(_, a), (_, b)| a.total_cmp(b))
        .unwrap_or((0, 0.0));
    let peak_frequency_hz = frequencies_hz[peak_index];
    let min_flux_density_value = peak_flux_density
        .iter()
        .copied()
        .fold(f64::INFINITY, f64::min);
    let resonance_flux_gain =
        (peak_flux_density_value / min_flux_density_value.max(1.0e-12)).max(1.0);

    let half_power = peak_flux_density_value * std::f64::consts::FRAC_1_SQRT_2;
    let mut left = peak_index;
    while left > 0 && peak_flux_density[left - 1] >= half_power {
        left -= 1;
    }
    let mut right = peak_index;
    while right + 1 < peak_flux_density.len() && peak_flux_density[right + 1] >= half_power {
        right += 1;
    }
    let resonance_bandwidth_hz = if right > left {
        Some((frequencies_hz[right] - frequencies_hz[left]).max(0.0))
    } else {
        None
    };
    let resonance_quality_factor = resonance_bandwidth_hz
        .filter(|bandwidth| *bandwidth > 0.0)
        .map(|bandwidth| (peak_frequency_hz / bandwidth).max(0.0));

    EmSweepSummary {
        sweep_count,
        resonance_peak_frequency_hz: Some(peak_frequency_hz),
        resonance_peak_flux_density: Some(peak_flux_density_value),
        resonance_bandwidth_hz,
        resonance_quality_factor,
        resonance_flux_gain: Some(resonance_flux_gain),
    }
}

fn em_sweep_known_answer_diagnostic(
    reference_frequency_hz: f64,
    frequencies_hz: &[f64],
    metrics: &EmSweepSummary,
) -> runmat_analysis_fea::diagnostics::FeaDiagnostic {
    let min_frequency_hz = frequencies_hz.iter().copied().fold(f64::INFINITY, f64::min);
    let max_frequency_hz = frequencies_hz.iter().copied().fold(0.0_f64, f64::max);
    let reference_scale = reference_frequency_hz.abs().max(1.0e-9);
    let reference_frequency_in_sweep_ratio = if frequencies_hz.iter().any(|frequency_hz| {
        (*frequency_hz - reference_frequency_hz).abs() <= reference_scale * 1.0e-9
    }) {
        1.0
    } else {
        0.0
    };
    let reference_frequency_bracket_ratio = if min_frequency_hz <= reference_frequency_hz
        && reference_frequency_hz <= max_frequency_hz
    {
        1.0
    } else {
        0.0
    };
    let normalized_peak_frequency_error_ratio = metrics
        .resonance_peak_frequency_hz
        .map(|peak_frequency_hz| {
            ((peak_frequency_hz - reference_frequency_hz).abs() / reference_scale).clamp(0.0, 1.0)
        })
        .unwrap_or(1.0);
    let resonance_flux_gain = metrics.resonance_flux_gain.unwrap_or(0.0);
    let resonance_quality_factor = metrics.resonance_quality_factor.unwrap_or(0.0);
    let sweep_known_answer_coverage_ratio = if metrics.sweep_count >= 3
        && reference_frequency_in_sweep_ratio >= 1.0
        && reference_frequency_bracket_ratio >= 1.0
        && normalized_peak_frequency_error_ratio <= 0.25
        && resonance_flux_gain >= 1.0
        && resonance_quality_factor >= 1.5
    {
        1.0
    } else {
        0.0
    };
    let severity = if sweep_known_answer_coverage_ratio >= 1.0 {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Info
    } else {
        runmat_analysis_fea::diagnostics::FeaDiagnosticSeverity::Warning
    };

    runmat_analysis_fea::diagnostics::FeaDiagnostic {
        code: "FEA_EM_SWEEP_KNOWN_ANSWER".to_string(),
        severity,
        message: format!(
            "basis=bracketed_reference_frequency sweep_count={} reference_frequency_hz={} sweep_frequency_min_hz={} sweep_frequency_max_hz={} reference_frequency_in_sweep_ratio={} reference_frequency_bracket_ratio={} normalized_peak_frequency_error_ratio={} resonance_flux_gain={} resonance_quality_factor={} sweep_known_answer_coverage_ratio={}",
            metrics.sweep_count,
            reference_frequency_hz,
            min_frequency_hz,
            max_frequency_hz,
            reference_frequency_in_sweep_ratio,
            reference_frequency_bracket_ratio,
            normalized_peak_frequency_error_ratio,
            resonance_flux_gain,
            resonance_quality_factor,
            sweep_known_answer_coverage_ratio,
        ),
    }
}

fn infer_material_assignments(
    geometry: &GeometryAsset,
    materials: &[MaterialModel],
    prep_regions: Option<&HashSet<String>>,
) -> Vec<MaterialAssignment> {
    let default_material = materials
        .first()
        .map(|m| m.material_id.clone())
        .unwrap_or_else(|| "mat_default_steel".to_string());
    let mut assignments = Vec::new();

    for region in &geometry.regions {
        let key = format!(
            "{} {}",
            region.name.to_ascii_lowercase(),
            region
                .tag
                .as_deref()
                .unwrap_or_default()
                .to_ascii_lowercase()
        );
        let assigned_material = if key.contains("aluminum") {
            materials
                .iter()
                .find(|m| m.material_id.contains("aluminum"))
                .map(|m| m.material_id.clone())
                .unwrap_or_else(|| default_material.clone())
        } else if key.contains("steel") {
            materials
                .iter()
                .find(|m| m.material_id.contains("steel"))
                .map(|m| m.material_id.clone())
                .unwrap_or_else(|| default_material.clone())
        } else if key.contains("polymer") || key.contains("plastic") {
            materials
                .iter()
                .find(|m| m.material_id.contains("polymer"))
                .map(|m| m.material_id.clone())
                .unwrap_or_else(|| default_material.clone())
        } else {
            default_material.clone()
        };

        let evidence_confidence = if geometry
            .source_geometry
            .material_evidence
            .iter()
            .any(|e| e.confidence == MaterialEvidenceConfidence::High)
        {
            EvidenceConfidence::Verified
        } else if geometry
            .source_geometry
            .material_evidence
            .iter()
            .any(|e| e.confidence == MaterialEvidenceConfidence::Medium)
        {
            EvidenceConfidence::Probable
        } else {
            EvidenceConfidence::Inferred
        };
        let confidence = if prep_regions
            .map(|mapped| mapped.contains(&region.region_id))
            .unwrap_or(false)
        {
            EvidenceConfidence::Verified
        } else {
            evidence_confidence
        };

        assignments.push(MaterialAssignment {
            region_id: region.region_id.clone(),
            expected_material_id: assigned_material.clone(),
            assigned_material_id: assigned_material,
            confidence,
        });
    }

    assignments
}

fn map_validate_error(
    error: AnalysisValidationError,
    model: &AnalysisModel,
    context: &OperationContext,
) -> OperationErrorEnvelope {
    let (error_code, message, mut error_context) = match error {
        AnalysisValidationError::MissingMaterials => (
            "RM.FEA.VALIDATE.MISSING_MATERIALS",
            "FEA model must include at least one material".to_string(),
            BTreeMap::new(),
        ),
        AnalysisValidationError::MissingBoundaryConditions => (
            "RM.FEA.VALIDATE.MISSING_BCS",
            "FEA model must include at least one boundary condition".to_string(),
            BTreeMap::new(),
        ),
        AnalysisValidationError::MissingLoads => (
            "RM.FEA.VALIDATE.MISSING_LOADS",
            "FEA model must include at least one load".to_string(),
            BTreeMap::new(),
        ),
        AnalysisValidationError::InvalidMomentVector { load_id } => (
            "RM.FEA.VALIDATE.INVALID_MOMENT",
            format!("moment load {load_id} must have finite components"),
            BTreeMap::from([("load_id".to_string(), load_id)]),
        ),
        AnalysisValidationError::ZeroMomentVector { load_id } => (
            "RM.FEA.VALIDATE.ZERO_MOMENT",
            format!("moment load {load_id} must have nonzero magnitude"),
            BTreeMap::from([("load_id".to_string(), load_id)]),
        ),
        AnalysisValidationError::UnitMismatch { model, geometry } => (
            "RM.FEA.VALIDATE.UNIT_MISMATCH",
            format!("model units {model:?} do not match geometry units {geometry:?}"),
            BTreeMap::from([
                ("model_units".to_string(), format!("{model:?}")),
                ("geometry_units".to_string(), format!("{geometry:?}")),
            ]),
        ),
        AnalysisValidationError::FrameMismatch { model, geometry } => (
            "RM.FEA.VALIDATE.FRAME_MISMATCH",
            format!("model frame {model:?} does not match geometry frame {geometry:?}"),
            BTreeMap::from([
                ("model_frame".to_string(), format!("{model:?}")),
                ("geometry_frame".to_string(), format!("{geometry:?}")),
            ]),
        ),
    };

    error_context.insert("analysis_model_id".to_string(), model.model_id.0.clone());
    error_context.insert("geometry_id".to_string(), model.geometry_id.clone());

    operation_error(
        ANALYSIS_VALIDATE_OPERATION,
        ANALYSIS_VALIDATE_OP_VERSION,
        context,
        OperationErrorSpec {
            error_code,
            error_type: OperationErrorType::Validation,
            retryable: false,
            severity: OperationErrorSeverity::Error,
        },
        message,
        error_context,
    )
}

#[cfg(test)]
mod tests;
