forked from 1b-agent/fieldcraft-upstream
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:
1 parent
9a8ae50bca
commit
1b57e2e672
20 files changed
+511
-27
No files matched your search
@@ -322,6 +322,7 @@ impl Model {
|
||||
Procedure::ExplicitDynamic(_) => "is dynamic",
|
||||
Procedure::HeatTransfer { transient: Some(_) } => "is transient heat transfer",
|
||||
Procedure::Static {}
|
||||
| Procedure::Buckle { .. }
|
||||
| Procedure::NonlinearStatic(_)
|
||||
| Procedure::Fluid { .. }
|
||||
| Procedure::HeatTransfer { transient: None }
|
||||
@@ -486,7 +487,7 @@ impl Model {
|
||||
}
|
||||
}
|
||||
match &s.procedure {
|
||||
Procedure::Static {} | Procedure::NonlinearStatic(_) => {
|
||||
Procedure::Static {} | Procedure::NonlinearStatic(_) | Procedure::Buckle { .. } => {
|
||||
if s.loads.is_empty() && !prescribes_motion {
|
||||
d.warning(&subject, "nothing loads the model in this step");
|
||||
}
|
||||
|
||||
@@ -55,6 +55,14 @@ pub enum Procedure {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
max_hz: Option<f64>,
|
||||
},
|
||||
/// Linear buckling: the load factors λ at which the step's loads (and prescribed motions),
|
||||
/// applied as a linear preload, make K + λ K_σ singular, and their mode shapes; the lowest
|
||||
/// `modes` positive ones, or those nearest above `shift` when one is given.
|
||||
Buckle {
|
||||
modes: u32,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
shift: Option<f64>,
|
||||
},
|
||||
/// Transient dynamics by explicit time integration up to `end_time`.
|
||||
ExplicitDynamic(ExplicitDynamic),
|
||||
/// Time-dependent incompressible flow in a fluid domain, from rest (or the domain's initial
|
||||
@@ -546,6 +554,7 @@ impl Procedure {
|
||||
match self {
|
||||
Procedure::Static {} => "static",
|
||||
Procedure::Frequency { .. } => "frequency",
|
||||
Procedure::Buckle { .. } => "buckle",
|
||||
Procedure::ExplicitDynamic(_) => "explicit_dynamic",
|
||||
Procedure::HeatTransfer { .. } => "heat_transfer",
|
||||
Procedure::CoupledTemperatureDisplacement {} => "coupled_temperature_displacement",
|
||||
@@ -618,6 +627,14 @@ impl Procedure {
|
||||
if let Procedure::HeatTransfer { transient: Some(t) } = self {
|
||||
return t.validate();
|
||||
}
|
||||
if let Procedure::Buckle { modes, shift } = *self {
|
||||
if !(1..=MAX_MODES).contains(&modes) {
|
||||
return Err(Error::Invalid(format!("a buckling step extracts 1 to {MAX_MODES} modes, got {modes}")));
|
||||
}
|
||||
if let Some(s) = shift {
|
||||
finite("buckling shift", s)?;
|
||||
}
|
||||
}
|
||||
if let Procedure::Frequency { modes, min_hz, max_hz } = *self {
|
||||
if !(1..=MAX_MODES).contains(&modes) {
|
||||
return Err(Error::Invalid(format!("a frequency step extracts 1 to {MAX_MODES} modes, got {modes}")));
|
||||
@@ -640,7 +657,7 @@ impl Procedure {
|
||||
pub fn default_output(&self) -> BTreeSet<OutputVariable> {
|
||||
match self {
|
||||
Procedure::Static {} | Procedure::NonlinearStatic(_) => OutputVariable::ALL.into_iter().collect(),
|
||||
Procedure::Frequency { .. } => [OutputVariable::Displacement].into_iter().collect(),
|
||||
Procedure::Frequency { .. } | Procedure::Buckle { .. } => [OutputVariable::Displacement].into_iter().collect(),
|
||||
Procedure::ExplicitDynamic(_) => [OutputVariable::Displacement, OutputVariable::Stress].into_iter().collect(),
|
||||
// Velocity, pressure and wall forces, always.
|
||||
Procedure::Fluid { .. } => BTreeSet::new(),
|
||||
|
||||
Reference in new issue
Block a user