forked from 1b-agent/fieldcraft-upstream
Rename the project to FieldCraft
Crates fieldcraft-*, binaries fieldcraft and fieldcraft-cli, the .fieldcraft file (format tag fieldcraft/1, manifest fieldcraft.json), FIELDCRAFT_* environment variables, the MCP server name, egui widget ids (fc_*), the app icon document FieldCraft.icon and its generated family, docs and licences. No compatibility shim: no files of the earlier name exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
9bc4e1b182
commit
d5bd74570f
268 files changed
+1666
-1666
No files matched your search
@@ -28,7 +28,7 @@
|
||||
//!
|
||||
//! Node orders are corners first, then mid-side nodes in the model's edge order (the model is the
|
||||
//! one place they are written down), then face and body nodes, the Abaqus/VTK convention of
|
||||
//! [`modelcraft_model::ElementKind`]. The functions are the textbook ones (O. C. Zienkiewicz,
|
||||
//! [`fieldcraft_model::ElementKind`]. The functions are the textbook ones (O. C. Zienkiewicz,
|
||||
//! R. L. Taylor, J. Z. Zhu, *The Finite Element Method: Its Basis and Fundamentals*, 7th ed.,
|
||||
//! 2013, ch. 6; T. J. R. Hughes, *The Finite Element Method*, Dover 2000, ch. 3).
|
||||
//!
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
use crate::dense::positive_inverse;
|
||||
use crate::jet::Jet;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
//! webs or the two flanges of a box) (W. D. Pilkey, *Analysis and Design of Elastic Beams*,
|
||||
//! Wiley 2002, ch. 6).
|
||||
|
||||
use modelcraft_model::{BeamProfile, Elastic};
|
||||
use fieldcraft_model::{BeamProfile, Elastic};
|
||||
|
||||
use crate::dense::Matrix;
|
||||
use crate::dof::{DofSet, ElementDofs};
|
||||
@@ -211,7 +211,7 @@ fn line_axis(xyz: &[[f64; 3]]) -> Result<(Vec3, f64)> {
|
||||
let [a, b] = xyz else {
|
||||
return Err(Error::Invalid(format!("a two-node line element has 2 nodes, got {}", xyz.len())));
|
||||
};
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
let d = sub(*b, *a);
|
||||
let length = norm(d);
|
||||
let ex = unit(d).ok_or_else(|| Error::Degenerate("a line element has zero length".into()))?;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
//! discontinuous finite element functions using a least squares method*, IJNME 8(3):461–480,
|
||||
//! 1974); mid-side nodes take the mean of their edge's corners.
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
use crate::basis::Basis;
|
||||
use crate::dense::{Cholesky, Matrix, inverse3, positive_inverse};
|
||||
@@ -163,10 +163,10 @@ impl Formulation {
|
||||
}
|
||||
|
||||
/// The formulation a model section asks for, if this element type has it.
|
||||
pub fn from_section(kind: ElementKind, f: &modelcraft_model::Formulation) -> Result<Formulation> {
|
||||
pub fn from_section(kind: ElementKind, f: &fieldcraft_model::Formulation) -> Result<Formulation> {
|
||||
let ty = element_type(kind)?;
|
||||
if f.hybrid {
|
||||
if f.incompatible_modes || f.integration == modelcraft_model::Integration::Reduced {
|
||||
if f.incompatible_modes || f.integration == fieldcraft_model::Integration::Reduced {
|
||||
return Err(Error::NotSupported("hybrid elements with incompatible modes or reduced integration".into()));
|
||||
}
|
||||
return crate::hybrid::pressures(kind).map(|pressures| Formulation::Hybrid { pressures }).ok_or_else(|| {
|
||||
@@ -184,9 +184,9 @@ impl Formulation {
|
||||
};
|
||||
}
|
||||
match f.integration {
|
||||
modelcraft_model::Integration::Full => Ok(Formulation::Full),
|
||||
modelcraft_model::Integration::Reduced if ty.reduced.is_some() => Ok(Formulation::Reduced),
|
||||
modelcraft_model::Integration::Reduced => Err(Error::NotSupported(format!(
|
||||
fieldcraft_model::Integration::Full => Ok(Formulation::Full),
|
||||
fieldcraft_model::Integration::Reduced if ty.reduced.is_some() => Ok(Formulation::Reduced),
|
||||
fieldcraft_model::Integration::Reduced => Err(Error::NotSupported(format!(
|
||||
"reduced integration of {} elements (hex8 needs hourglass control; use full integration or incompatible modes)",
|
||||
kind.name()
|
||||
))),
|
||||
@@ -254,7 +254,7 @@ impl<'a> Solid<'a> {
|
||||
if xyz.len() != ty.nodes {
|
||||
return Err(Error::Invalid(format!("a {} element has {} nodes, got {}", kind.name(), ty.nodes, xyz.len())));
|
||||
}
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
let ok = match formulation {
|
||||
Formulation::Full => true,
|
||||
Formulation::Reduced => ty.reduced.is_some(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! A pressure p on a face gives node i the force fᵢ = −p ∫ Nᵢ n dA, where n dA =
|
||||
//! (∂x/∂ξ × ∂x/∂η) dξ dη on the face's own, possibly curved, geometry; faces are numbered so this
|
||||
//! normal points out of the element ([`modelcraft_model::ElementKind::faces`]), and a positive
|
||||
//! normal points out of the element ([`fieldcraft_model::ElementKind::faces`]), and a positive
|
||||
//! pressure pushes in. This is the consistent load vector of the virtual work of the surface load
|
||||
//! (Zienkiewicz, Taylor & Zhu 2013, §6.6), integrated with the model's face rules (exact for a
|
||||
//! uniform pressure on a flat face).
|
||||
@@ -10,10 +10,10 @@
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// ∫ Nᵢ n dA for every node of a face (node coordinates in the face's node order): the model's
|
||||
/// face integration ([`modelcraft_model::nodal::face_normal_weights`]), so pressures and the
|
||||
/// face integration ([`fieldcraft_model::nodal::face_normal_weights`]), so pressures and the
|
||||
/// tractions the model distributes use the same rule.
|
||||
pub fn normal_weights(xyz: &[[f64; 3]]) -> Result<Vec<[f64; 3]>> {
|
||||
modelcraft_model::nodal::face_normal_weights(xyz).map_err(|e| Error::Invalid(e.to_string()))
|
||||
fieldcraft_model::nodal::face_normal_weights(xyz).map_err(|e| Error::Invalid(e.to_string()))
|
||||
}
|
||||
|
||||
/// Consistent nodal forces of a uniform pressure (positive pushes into the element).
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//! Faces are integrated on their own (possibly curved) geometry with the model's face rule
|
||||
//! (the one pressures use), exact for N Nᵀ on flat faces.
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
use crate::basis::Basis;
|
||||
use crate::dense::Matrix;
|
||||
@@ -47,7 +47,7 @@ impl<'a> Conduction<'a> {
|
||||
if xyz.len() != ty.nodes {
|
||||
return Err(Error::Invalid(format!("a {} element has {} nodes, got {}", kind.name(), ty.nodes, xyz.len())));
|
||||
}
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
Ok(Conduction { ty, basis: Basis::of(kind), xyz })
|
||||
}
|
||||
|
||||
@@ -119,11 +119,11 @@ impl<'a> Conduction<'a> {
|
||||
}
|
||||
|
||||
/// Visit the points of a face (node coordinates in the face's node order): shape values and
|
||||
/// the area weight, with the model's face rule ([`modelcraft_model::nodal::integrate_face`], the
|
||||
/// the area weight, with the model's face rule ([`fieldcraft_model::nodal::integrate_face`], the
|
||||
/// one pressures and tractions use).
|
||||
fn each_face_point(xyz: &[[f64; 3]], mut visit: impl FnMut(&[f64], f64)) -> Result<()> {
|
||||
let mut collapsed = false;
|
||||
modelcraft_model::nodal::integrate_face(xyz, |n, nda| {
|
||||
fieldcraft_model::nodal::integrate_face(xyz, |n, nda| {
|
||||
let area = (nda[0] * nda[0] + nda[1] * nda[1] + nda[2] * nda[2]).sqrt();
|
||||
collapsed |= !(area > 0.0 && area.is_finite());
|
||||
visit(n, area);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
//! symmetric indefinite saddle point that the Cholesky factorization cannot solve; it waits for
|
||||
//! an indefinite (LDLᵀ) factorization.
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
use crate::dense::{Cholesky, Matrix};
|
||||
use crate::element::{Formulation, Solid, ThermalStrain, accumulate_symmetric, b_block};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Finite element kernels shared by ModelCraft's structural solvers.
|
||||
//! Finite element kernels shared by FieldCraft's structural solvers.
|
||||
//!
|
||||
//! Pure functions over element geometry and material constants, with no solver loop and no
|
||||
//! global state, so the implicit solver (`modelcraft-solver`) and the explicit solver that follows
|
||||
//! global state, so the implicit solver (`fieldcraft-solver`) and the explicit solver that follows
|
||||
//! it use the same element technology:
|
||||
//! - [`dof`]: degree-of-freedom kinds, what elements declare, and the global numbering
|
||||
//! ([`DofLayout`]) every solver assembles on;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! S₁₁ = 1/E₁, S₁₂ = −ν₁₂/E₁, S₁₃ = −ν₁₃/E₁, S₂₃ = −ν₂₃/E₂ (ν_ji/E_j = ν_ij/E_i), and
|
||||
//! 1/G on the shear diagonal.
|
||||
|
||||
use modelcraft_model::Elastic;
|
||||
use fieldcraft_model::Elastic;
|
||||
|
||||
use crate::dense::inverse3;
|
||||
use crate::{Error, Result};
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
//! inside the element (r > 0) for every element with some node off the axis; the solver holds
|
||||
//! the radial displacement of nodes on the axis at zero, as symmetry requires.
|
||||
|
||||
use modelcraft_model::{ElementKind, Extent, SectionKind};
|
||||
use fieldcraft_model::{ElementKind, Extent, SectionKind};
|
||||
|
||||
use crate::basis::Basis;
|
||||
use crate::dense::{Cholesky, Matrix};
|
||||
@@ -166,7 +166,7 @@ pub fn plane_type(kind: ElementKind) -> Result<&'static ElementType> {
|
||||
}
|
||||
|
||||
/// The formulation a 2D section asks for, if this element type has it.
|
||||
pub fn formulation(kind: ElementKind, f: &modelcraft_model::Formulation) -> Result<Formulation> {
|
||||
pub fn formulation(kind: ElementKind, f: &fieldcraft_model::Formulation) -> Result<Formulation> {
|
||||
let ty = plane_type(kind)?;
|
||||
if f.hybrid {
|
||||
return Err(Error::NotSupported("hybrid (mixed displacement–pressure) 2D elements".into()));
|
||||
@@ -179,9 +179,9 @@ pub fn formulation(kind: ElementKind, f: &modelcraft_model::Formulation) -> Resu
|
||||
};
|
||||
}
|
||||
match f.integration {
|
||||
modelcraft_model::Integration::Full => Ok(Formulation::Full),
|
||||
modelcraft_model::Integration::Reduced if ty.reduced.is_some() => Ok(Formulation::Reduced),
|
||||
modelcraft_model::Integration::Reduced => Err(Error::NotSupported(format!(
|
||||
fieldcraft_model::Integration::Full => Ok(Formulation::Full),
|
||||
fieldcraft_model::Integration::Reduced if ty.reduced.is_some() => Ok(Formulation::Reduced),
|
||||
fieldcraft_model::Integration::Reduced => Err(Error::NotSupported(format!(
|
||||
"reduced integration of 2D {} elements (quad4 needs hourglass control; use full integration or incompatible modes)",
|
||||
kind.name()
|
||||
))),
|
||||
@@ -254,7 +254,7 @@ impl<'a> Plane<'a> {
|
||||
if xyz.len() != ty.nodes {
|
||||
return Err(Error::Invalid(format!("a {} element has {} nodes, got {}", kind.name(), ty.nodes, xyz.len())));
|
||||
}
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
if xyz.iter().any(|p| p[2] != 0.0) {
|
||||
return Err(Error::Invalid(format!("a 2D {} element must lie in the XY plane (z = 0)", kind.name())));
|
||||
}
|
||||
@@ -760,7 +760,7 @@ mod tests {
|
||||
let f = e.body_force_field(|x| [x[0], 0.0, 0.0]).unwrap();
|
||||
assert!((f.iter().map(|v| v[0]).sum::<f64>() - 2.0 * pi * 7.0 / 3.0).abs() < 1e-12);
|
||||
// Pressure on the outer edge (x = 2) of the ring pushes inward with p · 2π · 2 · 1.
|
||||
let fp = modelcraft_model::nodal::edge_pressure(&[sq[1], sq[2]], Theory::Axisymmetric.extent(), 1.0).unwrap();
|
||||
let fp = fieldcraft_model::nodal::edge_pressure(&[sq[1], sq[2]], Theory::Axisymmetric.extent(), 1.0).unwrap();
|
||||
assert!((fp.iter().map(|v| v[0]).sum::<f64>() + 4.0 * pi).abs() < 1e-12, "{fp:?}");
|
||||
}
|
||||
|
||||
@@ -783,6 +783,6 @@ mod tests {
|
||||
let on_axis = sq.map(|p| [p[0] - 1.0, p[1], 0.0]);
|
||||
let e = Plane::new(ElementKind::Quad4, &on_axis, Formulation::IncompatibleModes, Theory::Axisymmetric).unwrap();
|
||||
assert!(e.stiffness(&d).is_ok());
|
||||
assert!(formulation(ElementKind::Quad4, &modelcraft_model::Formulation { hybrid: true, ..Default::default() }).is_err());
|
||||
assert!(formulation(ElementKind::Quad4, &fieldcraft_model::Formulation { hybrid: true, ..Default::default() }).is_err());
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
//! Stress 6-vectors hold tensor components (xx, yy, zz, xy, xz, yz), strain increments
|
||||
//! engineering shears.
|
||||
|
||||
use modelcraft_model::{Damage, Elastic, Hardening, Plastic};
|
||||
use fieldcraft_model::{Damage, Elastic, Hardening, Plastic};
|
||||
|
||||
use crate::material::von_mises;
|
||||
use crate::{Error, Result};
|
||||
@@ -328,7 +328,7 @@ impl J2 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use modelcraft_model::HardeningPoint;
|
||||
use fieldcraft_model::HardeningPoint;
|
||||
|
||||
const E: f64 = 200_000.0;
|
||||
const NU: f64 = 0.3;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Shape functions and their derivatives in natural coordinates.
|
||||
//!
|
||||
//! Node order is the model's (Abaqus/VTK, [`modelcraft_model::ElementKind`]):
|
||||
//! Node order is the model's (Abaqus/VTK, [`fieldcraft_model::ElementKind`]):
|
||||
//! - tetrahedra on the reference element with corners (0,0,0), (1,0,0), (0,1,0), (0,0,1) and the
|
||||
//! volume coordinates L₁ = 1 − ξ − η − ζ, L₂ = ξ, L₃ = η, L₄ = ζ; tet10 mid-side nodes on edges
|
||||
//! 1-2, 2-3, 3-1, 1-4, 2-4, 3-4 with N = 4 Lₐ L_b, corners N = L(2L − 1);
|
||||
@@ -9,7 +9,7 @@
|
||||
//! N = ⅛(1 + ξξᵢ)(1 + ηηᵢ)(1 + ζζᵢ)(ξξᵢ + ηηᵢ + ζζᵢ − 2), a mid-side node with ξᵢ = 0
|
||||
//! N = ¼(1 − ξ²)(1 + ηηᵢ)(1 + ζζᵢ), and likewise), mid-side nodes 9–12 on the bottom edges,
|
||||
//! 13–16 on the top edges, 17–20 on the vertical edges (mid-side positions come from
|
||||
//! [`modelcraft_model::ElementKind::edges`], the one place the node order is written down);
|
||||
//! [`fieldcraft_model::ElementKind::edges`], the one place the node order is written down);
|
||||
//! - the six-node wedge (pentahedron) as the product of the linear triangle {ξ, η ≥ 0,
|
||||
//! ξ + η ≤ 1} (area coordinates L₁ = 1 − ξ − η, L₂ = ξ, L₃ = η) and the linear interval
|
||||
//! ζ ∈ [−1, 1]: nodes 1–3 at ζ = −1 counter-clockwise seen from above, nodes 4–6 above them,
|
||||
@@ -19,7 +19,7 @@
|
||||
//! Element Method: Its Basis and Fundamentals*, 7th ed., Butterworth-Heinemann 2013, ch. 6;
|
||||
//! T. J. R. Hughes, *The Finite Element Method*, Dover 2000, ch. 3).
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
/// Most nodes an element family has (hex27, [`crate::basis::Basis::Hex27`]).
|
||||
pub const MAX_NODES: usize = 27;
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
//! the nodes (triangle, constant membrane and bending fields). Transverse shear stresses are
|
||||
//! reported at the mid-surface as the peak of the parabolic distribution, 3/2 Q/t.
|
||||
|
||||
use modelcraft_model::{Elastic, ElementKind};
|
||||
use fieldcraft_model::{Elastic, ElementKind};
|
||||
|
||||
use crate::dense::Matrix;
|
||||
use crate::dof::{DofSet, ElementDofs};
|
||||
@@ -213,7 +213,7 @@ impl Shell {
|
||||
if xyz.len() != nodes {
|
||||
return Err(Error::Invalid(format!("a {} element has {nodes} nodes, got {}", kind.name(), xyz.len())));
|
||||
}
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
let degenerate = || Error::Degenerate(format!("a {} shell element is degenerate (zero area, inverted or not convex)", kind.name()));
|
||||
let p = |i: usize| xyz.get(i).copied().unwrap_or_default();
|
||||
let (normal, along) = if nodes == 3 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Element-level checks: rigid-body modes, constant-strain reproduction (the single-element form
|
||||
//! of the patch test), mass and load totals, extrapolation.
|
||||
|
||||
use modelcraft_model::ElementKind;
|
||||
use fieldcraft_model::ElementKind;
|
||||
|
||||
use crate::dense::{Cholesky, Matrix};
|
||||
use crate::element::{Formulation, Solid};
|
||||
@@ -203,7 +203,7 @@ fn bad_input_is_an_error() {
|
||||
|
||||
#[test]
|
||||
fn section_formulations() {
|
||||
use modelcraft_model::{Formulation as F, Integration};
|
||||
use fieldcraft_model::{Formulation as F, Integration};
|
||||
let full = F::default();
|
||||
let reduced = F { integration: Integration::Reduced, ..F::default() };
|
||||
let im = F { incompatible_modes: true, ..F::default() };
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
//! stiffness and mass of a straight member, and the usual full integration of a curved one.
|
||||
//! Material: the Young's modulus of an isotropic material; the truss knows no other constant.
|
||||
|
||||
use modelcraft_model::{Elastic, ElementKind};
|
||||
use fieldcraft_model::{Elastic, ElementKind};
|
||||
|
||||
use crate::basis::Basis;
|
||||
use crate::dense::Matrix;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
//! element frequency satisfies ω² ≤ k λ_max(Σ b bᵀ) / (V min m_I). The bound is exact for a
|
||||
//! cube (its dilatation mode) and conservative for distorted elements.
|
||||
|
||||
use modelcraft_model::Elastic;
|
||||
use fieldcraft_model::Elastic;
|
||||
|
||||
use crate::dense::inverse3;
|
||||
use crate::material::Elasticity;
|
||||
@@ -58,7 +58,7 @@ fn degenerate(what: &str) -> Error {
|
||||
impl UniformStrain<8> {
|
||||
/// The uniform-strain hexahedron of corners `xyz` (the model's hex8 node order).
|
||||
pub fn hex8(xyz: &[[f64; 3]; 8]) -> Result<Hex8> {
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
let mut b = [[0.0; 3]; 8];
|
||||
let mut nodal_volume = [0.0; 8];
|
||||
let mut volume = 0.0;
|
||||
@@ -171,7 +171,7 @@ impl UniformStrain<8> {
|
||||
impl UniformStrain<4> {
|
||||
/// The linear tetrahedron of corners `xyz` (the model's tet4 node order).
|
||||
pub fn tet4(xyz: &[[f64; 3]; 4]) -> Result<Tet4> {
|
||||
modelcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
fieldcraft_model::check_coordinates(xyz.as_flattened()).map_err(|e| Error::Invalid(e.to_string()))?;
|
||||
let [x0, x1, x2, x3] = *xyz;
|
||||
let e = |x: [f64; 3]| [x[0] - x0[0], x[1] - x0[1], x[2] - x0[2]];
|
||||
// J[a][c] = ∂x_c/∂ξ_a: the edges from corner 1.
|
||||
@@ -330,7 +330,7 @@ mod tests {
|
||||
}
|
||||
assert!((h.nodal_volume.iter().sum::<f64>() - h.volume).abs() < 1e-14);
|
||||
// The volume of the distorted hexahedron agrees with the fem element's.
|
||||
let solid = crate::Solid::new(modelcraft_model::ElementKind::Hex8, &hex, crate::Formulation::Full).unwrap();
|
||||
let solid = crate::Solid::new(fieldcraft_model::ElementKind::Hex8, &hex, crate::Formulation::Full).unwrap();
|
||||
assert!((solid.volume().unwrap() - h.volume).abs() < 1e-14);
|
||||
let mut inverted = hex;
|
||||
inverted.swap(0, 1);
|
||||
|
||||
Reference in new issue
Block a user