solver, model, engine: linear buckling steps

Procedure::Buckle {modes, shift}: the step's loads as a linear preload, K_σ from its stresses,
and (K + λ K_σ) φ = 0 by block Lanczos in the K inner product: K⁻¹(−K_σ) on Cholesky without a
shift, the buckling transformation (K + σ K_σ)⁻¹ K on LDLᵀ with one, whose inertia counts the
load factors below σ (Grimes–Lewis–Simon 1994). Lanczos gains an operand and an eigenvalue map
(defaults keep frequency steps bit for bit) and reports converged negative eigenvalues. Results
are frequency results with a `buckling` part; modes display with their load factor.
solve.buckle (in the Static drop-down); Abaqus *BUCKLE, Nastran SOL 105 and LS-DYNA refuse it by
name. Euler columns of hex20 within 0.18 % of Engesser's load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 13:30:59 -04:00
1 parent 9a8ae50bca
commit 1b57e2e672
20 files changed
+511 -27

No files matched your search

+2 -2
View File
@@ -470,8 +470,8 @@ pub static CATALOGUE: &[Capability] = &[
"Static",
Solve,
"Implicit",
"Linear static (or coupled temperature–displacement) solve of a step with FieldCraft's own solver; nonlinear static and heat transfer in the drop-down",
&["solve.nonlinear", "solve.heat"],
"Linear static (or coupled temperature–displacement) solve of a step with FieldCraft's own solver; nonlinear static, buckling and heat transfer in the drop-down",
&["solve.nonlinear", "solve.buckle", "solve.heat"],
),
ready("solve.frequency", "Frequency", Solve, "Implicit", "Natural frequencies and mode shapes of a frequency step"),
ready(
+1
View File
@@ -109,6 +109,7 @@ pub static ALL: &[CommandSpec] = &[
solve::STATIC,
solve::NONLINEAR,
solve::FREQUENCY,
solve::BUCKLE,
solve::HEAT,
solve::EXPLICIT,
solve::HISTORY,
+29 -2
View File
@@ -7,8 +7,8 @@ use fieldcraft_mesh::surface::{BoundarySurface, SurfaceOptions};
use fieldcraft_model::FluidDomain;
use fieldcraft_parallel as parallel;
use fieldcraft_solver::{
FrequencyOptions, Limits, Location, MassKind, StaticResult, StepResult as ImplicitResult, solve_frequency, solve_heat, solve_nonlinear,
solve_static,
FrequencyOptions, Limits, Location, MassKind, StaticResult, StepResult as ImplicitResult, solve_buckle, solve_frequency, solve_heat,
solve_nonlinear, solve_static,
};
use serde_json::{Value, json};
@@ -60,6 +60,14 @@ pub const FREQUENCY: CommandSpec = CommandSpec {
run: run_frequency,
};
pub const BUCKLE: CommandSpec = CommandSpec {
id: "solve.buckle",
label: "Solve Buckling Step",
params: r#"{"step": "Buckle", "max_dofs": 6000000, "max_factor_entries": 400000000, "max_iterations": 3000, "threads": 8} — linear buckling of a buckling step (the only one when `step` is left out): the step's loads as a linear preload, then the lowest load factors λ with (K + λ K_σ) φ = 0 and their mode shapes (largest component 1), or those above the step's shift (whose LDLᵀ factor counts the load factors below it); negative load factors found are reported; the result stays in the session and its modes display like frequency modes; `threads` as for solve.static"#,
kind: CommandKind::Model,
run: run_buckle,
};
pub const EXPLICIT: CommandSpec = CommandSpec {
id: "solve.explicit",
label: "Solve Explicit Dynamic Step",
@@ -159,6 +167,7 @@ fn run_all(s: &mut Session, _: &Value) -> Result<Value> {
Procedure::Static {} | Procedure::CoupledTemperatureDisplacement {} => run_static,
Procedure::HeatTransfer { .. } => run_heat,
Procedure::Frequency { .. } => run_frequency,
Procedure::Buckle { .. } => run_buckle,
Procedure::ExplicitDynamic(_) => run_explicit,
Procedure::Fluid { .. } => run_fluid,
Procedure::NonlinearStatic(_) => run_nonlinear,
@@ -292,6 +301,24 @@ fn run_frequency(s: &mut Session, p: &Value) -> Result<Value> {
Ok(summary)
}
fn run_buckle(s: &mut Session, p: &Value) -> Result<Value> {
let limits = limits(p)?;
let step = opt::<String>(p, "step")?;
let model = &s.model;
let r = parallel::with_threads(threads(s, p)?, || solve_buckle(model, step.as_deref(), &limits))?;
let summary = json!({
"step": r.step,
"dofs": r.stats.dofs,
"equations": r.stats.equations,
"lanczos_vectors": r.stats.lanczos_vectors,
"modes": r.modes.iter().enumerate().map(|(i, m)| json!({"mode": i + 1, "load_factor": m.eigenvalue})).collect::<Vec<_>>(),
"buckling": r.buckling,
"warnings": r.warnings,
});
s.store_result(StepResult::Implicit(ImplicitResult::Frequency(r)));
Ok(summary)
}
fn run_explicit(s: &mut Session, p: &Value) -> Result<Value> {
use fieldcraft_explicit::Limits as ExplicitLimits;
let limits = ExplicitLimits {