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
@@ -1,15 +1,15 @@
|
||||
//! Linear static and frequency analysis of a model's step, and their results.
|
||||
|
||||
use modelcraft_fem::ElementType;
|
||||
use modelcraft_fem::beam::BeamForces;
|
||||
use modelcraft_fem::element::{Extrapolation, extrapolation};
|
||||
use modelcraft_fem::quadrature::Rule;
|
||||
use modelcraft_fem::shell::ShellStress;
|
||||
use modelcraft_fem::{DofKind, DofLayout, PointStress, von_mises};
|
||||
use modelcraft_model::{ElementKind, Model, Procedure, Resolved, Step};
|
||||
use fieldcraft_fem::ElementType;
|
||||
use fieldcraft_fem::beam::BeamForces;
|
||||
use fieldcraft_fem::element::{Extrapolation, extrapolation};
|
||||
use fieldcraft_fem::quadrature::Rule;
|
||||
use fieldcraft_fem::shell::ShellStress;
|
||||
use fieldcraft_fem::{DofKind, DofLayout, PointStress, von_mises};
|
||||
use fieldcraft_model::{ElementKind, Model, Procedure, Resolved, Step};
|
||||
use serde::Serialize;
|
||||
|
||||
use modelcraft_parallel as parallel;
|
||||
use fieldcraft_parallel as parallel;
|
||||
|
||||
use crate::cholesky::{Factor, FactorError, UpperColumns};
|
||||
use crate::constraints::{Budget, Reduction, coupling_rows};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
//! zero-stiffness direction); the factorization stops and names the column: the first such column
|
||||
//! in the postorder, whichever thread meets it.
|
||||
|
||||
use modelcraft_parallel as parallel;
|
||||
use fieldcraft_parallel as parallel;
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use modelcraft_fem::dense::inverse3;
|
||||
use modelcraft_fem::{DofKind, DofSet};
|
||||
use modelcraft_model::{CouplingKind, Model, Resolved};
|
||||
use fieldcraft_fem::dense::inverse3;
|
||||
use fieldcraft_fem::{DofKind, DofSet};
|
||||
use fieldcraft_model::{CouplingKind, Model, Resolved};
|
||||
|
||||
use crate::sparse::Csr;
|
||||
use crate::system::Mesh;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Point masses, rotary inertia, springs and dashpots in the implicit system.
|
||||
//!
|
||||
//! The model resolves each definition to its nodes ([`modelcraft_model::Resolved::masses`] and
|
||||
//! The model resolves each definition to its nodes ([`fieldcraft_model::Resolved::masses`] and
|
||||
//! `springs`); here each copy is turned into global degrees of freedom and its matrix:
|
||||
//!
|
||||
//! - a point mass adds m_x, m_y, m_z on the node's translations and its inertia tensor J (3 × 3,
|
||||
@@ -12,11 +12,11 @@
|
||||
//! - a dashpot has the same form on velocities; static and frequency steps have no velocities, so
|
||||
//! they ignore dashpots with a warning (damping enters dynamic steps).
|
||||
//!
|
||||
//! What each copy needs at its nodes ([`needs`]) goes into the mesh's [`modelcraft_fem::DofLayout`]
|
||||
//! What each copy needs at its nodes ([`needs`]) goes into the mesh's [`fieldcraft_fem::DofLayout`]
|
||||
//! like an element's declared kinds; [`dof_of`] then finds its unknowns there.
|
||||
|
||||
use modelcraft_fem::{DofKind, DofSet};
|
||||
use modelcraft_model::{Resolved, ResolvedSpring};
|
||||
use fieldcraft_fem::{DofKind, DofSet};
|
||||
use fieldcraft_model::{Resolved, ResolvedSpring};
|
||||
|
||||
use crate::sparse::{Csr, add_entry};
|
||||
use crate::system::Mesh;
|
||||
@@ -103,7 +103,7 @@ impl Discrete {
|
||||
.filter(|d| m.mass[*d] != 0.0 && DofKind::translation(*d).is_some_and(|k| planar.contains(k)))
|
||||
.map(|d| dof_of(mesh, m.node, d).map(|dof| (dof, m.mass[d])).ok_or_else(|| missing("point mass", &m.name, m.node, d)))
|
||||
.collect::<Result<_>>()?;
|
||||
let inertia = modelcraft_model::discrete::inertia_matrix(m.inertia);
|
||||
let inertia = fieldcraft_model::discrete::inertia_matrix(m.inertia);
|
||||
let rotations = if m.inertia.iter().any(|c| *c != 0.0) {
|
||||
(3..6).map(|d| dof_of(mesh, m.node, d).ok_or_else(|| missing("point mass", &m.name, m.node, d))).collect::<Result<_>>()?
|
||||
} else {
|
||||
|
||||
@@ -286,7 +286,7 @@ pub fn lanczos(op: &impl Operator, shift: f64, wanted: Wanted, block: usize, max
|
||||
// W = Op Q_j.
|
||||
let mut w: Vec<Vec<f64>> = Vec::with_capacity(b);
|
||||
// The block's solves are independent: one task each.
|
||||
for y in modelcraft_parallel::map(mcur.len(), |i| {
|
||||
for y in fieldcraft_parallel::map(mcur.len(), |i| {
|
||||
let mut y = mcur.get(i).cloned().unwrap_or_default();
|
||||
op.solve(&mut y).map(|()| y)
|
||||
}) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Heat transfer: steady and transient conduction with prescribed temperatures, surface fluxes,
|
||||
//! convection and internal heat sources.
|
||||
//!
|
||||
//! Every node of the solid elements carries one temperature ([`modelcraft_fem::heat::HEAT_DOFS`]).
|
||||
//! Every node of the solid elements carries one temperature ([`fieldcraft_fem::heat::HEAT_DOFS`]).
|
||||
//! The conductivity K (with the convection films) and capacity C are assembled on the node
|
||||
//! graph in element label order ([`modelcraft_fem::heat`]); prescribed temperatures are
|
||||
//! graph in element label order ([`fieldcraft_fem::heat`]); prescribed temperatures are
|
||||
//! eliminated, the free ones numbered by nested dissection and solved by sparse Cholesky.
|
||||
//!
|
||||
//! - **Steady**: K T = f.
|
||||
@@ -16,10 +16,10 @@
|
||||
//! The heat flux −k∇T is reported at the integration points and averaged at the nodes; the heat
|
||||
//! flow at prescribed temperatures is the reaction K T − f (C Ṫ included in transient steps).
|
||||
|
||||
use modelcraft_fem::element::{Extrapolation, extrapolation};
|
||||
use modelcraft_fem::heat::{Conduction, PointFlux, face_film, face_weights};
|
||||
use modelcraft_fem::{DofLayout, DofSet};
|
||||
use modelcraft_model::{ElementKind, Load, Model, Procedure, Resolved, SectionKind, Step, TEMPERATURE_DOF};
|
||||
use fieldcraft_fem::element::{Extrapolation, extrapolation};
|
||||
use fieldcraft_fem::heat::{Conduction, PointFlux, face_film, face_weights};
|
||||
use fieldcraft_fem::{DofLayout, DofSet};
|
||||
use fieldcraft_model::{ElementKind, Load, Model, Procedure, Resolved, SectionKind, Step, TEMPERATURE_DOF};
|
||||
|
||||
use crate::cholesky::{Factor, FactorError, UpperColumns};
|
||||
use crate::ordering::nested_dissection;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! ModelCraft's implicit solver: linear static and frequency analysis of a model's steps.
|
||||
//! FieldCraft's implicit solver: linear static and frequency analysis of a model's steps.
|
||||
//!
|
||||
//! The pipeline, for one step of a [`modelcraft_model::Model`]:
|
||||
//! The pipeline, for one step of a [`fieldcraft_model::Model`]:
|
||||
//! 1. **System** ([`system`]): the model is resolved; every section's elements become solid
|
||||
//! elements with their material's elasticity and the section's formulation
|
||||
//! ([`modelcraft_fem`] kernels); the step's boundary conditions give the prescribed degrees of
|
||||
//! ([`fieldcraft_fem`] kernels); the step's boundary conditions give the prescribed degrees of
|
||||
//! freedom, its loads the load vector (forces and tractions as the model distributes them,
|
||||
//! consistent pressure loads, gravity and body forces).
|
||||
//! 2. **Assembly** ([`sparse`]) on the node graph, elements in label order.
|
||||
@@ -16,7 +16,7 @@
|
||||
//! kind of factorization.
|
||||
//!
|
||||
//! Element matrices and stresses, the factorization's subtrees and dense tiles, the Lanczos
|
||||
//! block solves and matrix products run on threads ([`modelcraft_parallel`]), but every number
|
||||
//! block solves and matrix products run on threads ([`fieldcraft_parallel`]), but every number
|
||||
//! is computed by one task in an order fixed by the model alone and sums are taken in a fixed
|
||||
//! order, so the same model gives the same numbers bit for bit on any number of threads and on
|
||||
//! every platform; sizes and iterations are capped by [`Limits`].
|
||||
@@ -44,14 +44,14 @@ pub use heat::{HeatResult, solve_heat};
|
||||
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Model(#[from] modelcraft_model::Error),
|
||||
Model(#[from] fieldcraft_model::Error),
|
||||
#[error("invalid input: {0}")]
|
||||
Invalid(String),
|
||||
#[error("not supported yet: {0}")]
|
||||
NotSupported(String),
|
||||
/// An element kernel failed (inverted geometry, a formulation it lacks, …).
|
||||
#[error("element {label}: {source}")]
|
||||
Element { label: u32, source: modelcraft_fem::Error },
|
||||
Element { label: u32, source: fieldcraft_fem::Error },
|
||||
/// The stiffness (or shifted matrix) is singular.
|
||||
#[error("{0}")]
|
||||
Singular(String),
|
||||
@@ -68,7 +68,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Error {
|
||||
/// A kernel error, naming the element.
|
||||
pub fn element(label: u32, source: modelcraft_fem::Error) -> Error {
|
||||
pub fn element(label: u32, source: fieldcraft_fem::Error) -> Error {
|
||||
Error::Element { label, source }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! label, then the element's own node order), so assembled values are the same bit for bit on
|
||||
//! every run and platform.
|
||||
|
||||
use modelcraft_fem::{DofLayout, DofSet};
|
||||
use fieldcraft_fem::{DofLayout, DofSet};
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
@@ -34,7 +34,7 @@ impl Csr {
|
||||
/// Rows are computed in parallel blocks; each row's sum runs in column order.
|
||||
pub fn mul(&self, x: &[f64], y: &mut [f64]) {
|
||||
let n = self.n.min(y.len());
|
||||
modelcraft_parallel::chunks_mut(y.get_mut(..n).unwrap_or_default(), ROWS, |b, block| {
|
||||
fieldcraft_parallel::chunks_mut(y.get_mut(..n).unwrap_or_default(), ROWS, |b, block| {
|
||||
for (i, yr) in block.iter_mut().enumerate() {
|
||||
let (cols, vals) = self.row(b * ROWS + i);
|
||||
*yr = cols.iter().zip(vals).map(|(c, v)| v * x.get(*c).copied().unwrap_or(0.0)).sum();
|
||||
@@ -130,7 +130,7 @@ impl NodeGraph {
|
||||
}
|
||||
}
|
||||
|
||||
/// Most kinds a node carries ([`modelcraft_fem::DofKind::ALL`]).
|
||||
/// Most kinds a node carries ([`fieldcraft_fem::DofKind::ALL`]).
|
||||
const MAX_KINDS: usize = 8;
|
||||
|
||||
/// Where node b's block lies in node a's rows of a matrix built by [`NodeGraph::zero_matrix`],
|
||||
|
||||
+25
-25
@@ -7,17 +7,17 @@
|
||||
//! translations for solids, Ux and Uy for 2D elements), nodes by label, then six per coupled reference point (points by
|
||||
//! name): translations along X, Y, Z and rotations about them.
|
||||
|
||||
use modelcraft_fem::beam::{Beam, BeamSection};
|
||||
use modelcraft_fem::dense::Matrix;
|
||||
use modelcraft_fem::element::{Formulation, Solid, ThermalStrain};
|
||||
use modelcraft_fem::plane::{Plane, Theory};
|
||||
use modelcraft_fem::shell::{Shell, ShellSection};
|
||||
use modelcraft_fem::truss::{Truss, young_modulus};
|
||||
use modelcraft_fem::{DofKind, DofLayout, DofSet, Elasticity, ElementDofs, PointStress, elasticity, face, plane};
|
||||
use modelcraft_model::nodal::edge_pressure;
|
||||
use fieldcraft_fem::beam::{Beam, BeamSection};
|
||||
use fieldcraft_fem::dense::Matrix;
|
||||
use fieldcraft_fem::element::{Formulation, Solid, ThermalStrain};
|
||||
use fieldcraft_fem::plane::{Plane, Theory};
|
||||
use fieldcraft_fem::shell::{Shell, ShellSection};
|
||||
use fieldcraft_fem::truss::{Truss, young_modulus};
|
||||
use fieldcraft_fem::{DofKind, DofLayout, DofSet, Elasticity, ElementDofs, PointStress, elasticity, face, plane};
|
||||
use fieldcraft_model::nodal::edge_pressure;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use modelcraft_model::{Elastic, ElementKind, Integration, Load, Model, Resolved, SectionKind, Step, Target, centrifugal_acceleration};
|
||||
use fieldcraft_model::{Elastic, ElementKind, Integration, Load, Model, Resolved, SectionKind, Step, Target, centrifugal_acceleration};
|
||||
|
||||
use crate::analysis::MassKind;
|
||||
use crate::{Error, Result};
|
||||
@@ -56,21 +56,21 @@ impl Kernel<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stiffness(&self, d: &Elasticity) -> modelcraft_fem::Result<modelcraft_fem::dense::Matrix> {
|
||||
pub fn stiffness(&self, d: &Elasticity) -> fieldcraft_fem::Result<fieldcraft_fem::dense::Matrix> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.stiffness(d),
|
||||
Kernel::Plane(p) => p.stiffness(d),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mass(&self, density: f64) -> modelcraft_fem::Result<modelcraft_fem::dense::Matrix> {
|
||||
pub fn mass(&self, density: f64) -> fieldcraft_fem::Result<fieldcraft_fem::dense::Matrix> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.mass(density),
|
||||
Kernel::Plane(p) => p.mass(density),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lumped_mass(&self, density: f64) -> modelcraft_fem::Result<Vec<f64>> {
|
||||
pub fn lumped_mass(&self, density: f64) -> fieldcraft_fem::Result<Vec<f64>> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.lumped_mass(density),
|
||||
Kernel::Plane(p) => p.lumped_mass(density),
|
||||
@@ -78,7 +78,7 @@ impl Kernel<'_> {
|
||||
}
|
||||
|
||||
/// Consistent nodal forces of a uniform force per volume, as 3-vectors (Z zero for 2D).
|
||||
pub fn body_force(&self, b: [f64; 3]) -> modelcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
pub fn body_force(&self, b: [f64; 3]) -> fieldcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.body_force(b),
|
||||
Kernel::Plane(p) => Ok(p.body_force_field(|_| b)?.into_iter().map(|v| [v[0], v[1], 0.0]).collect()),
|
||||
@@ -86,7 +86,7 @@ impl Kernel<'_> {
|
||||
}
|
||||
|
||||
/// Consistent nodal forces of a force per volume b(x), as 3-vectors (Z zero for 2D).
|
||||
pub fn body_force_field(&self, b: impl Fn([f64; 3]) -> [f64; 3]) -> modelcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
pub fn body_force_field(&self, b: impl Fn([f64; 3]) -> [f64; 3]) -> fieldcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.body_force_field(b),
|
||||
Kernel::Plane(p) => Ok(p.body_force_field(b)?.into_iter().map(|v| [v[0], v[1], 0.0]).collect()),
|
||||
@@ -95,7 +95,7 @@ impl Kernel<'_> {
|
||||
|
||||
/// Integration-point stresses for nodal displacements given as 3-vectors, with a thermal
|
||||
/// strain when given.
|
||||
pub fn stresses_with(&self, d: &Elasticity, u: &[[f64; 3]], thermal: Option<&ThermalStrain>) -> modelcraft_fem::Result<Vec<PointStress>> {
|
||||
pub fn stresses_with(&self, d: &Elasticity, u: &[[f64; 3]], thermal: Option<&ThermalStrain>) -> fieldcraft_fem::Result<Vec<PointStress>> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.stresses_with(d, u, thermal),
|
||||
Kernel::Plane(p) => p.stresses_with(d, &u.iter().map(|v| [v[0], v[1]]).collect::<Vec<_>>(), thermal),
|
||||
@@ -103,21 +103,21 @@ impl Kernel<'_> {
|
||||
}
|
||||
|
||||
/// The equivalent nodal forces of a thermal strain, as 3-vectors (Z zero for 2D).
|
||||
pub fn thermal_load(&self, d: &Elasticity, thermal: &ThermalStrain) -> modelcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
pub fn thermal_load(&self, d: &Elasticity, thermal: &ThermalStrain) -> fieldcraft_fem::Result<Vec<[f64; 3]>> {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.thermal_load(d, thermal),
|
||||
Kernel::Plane(p) => Ok(p.thermal_load(d, thermal)?.into_iter().map(|v| [v[0], v[1], 0.0]).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rule(&self) -> modelcraft_fem::quadrature::Rule {
|
||||
pub fn rule(&self) -> fieldcraft_fem::quadrature::Rule {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.rule(),
|
||||
Kernel::Plane(p) => p.rule(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> &'static modelcraft_fem::ElementType {
|
||||
pub fn ty(&self) -> &'static fieldcraft_fem::ElementType {
|
||||
match self {
|
||||
Kernel::Solid(s) => s.ty,
|
||||
Kernel::Plane(p) => p.ty,
|
||||
@@ -217,7 +217,7 @@ impl Structural {
|
||||
}
|
||||
|
||||
impl StructuralKernel {
|
||||
pub fn stiffness(&self) -> modelcraft_fem::Result<Matrix> {
|
||||
pub fn stiffness(&self) -> fieldcraft_fem::Result<Matrix> {
|
||||
match self {
|
||||
StructuralKernel::Shell(s) => s.stiffness(),
|
||||
StructuralKernel::Beam(b) => Ok(b.stiffness()),
|
||||
@@ -225,7 +225,7 @@ impl StructuralKernel {
|
||||
}
|
||||
|
||||
/// The consistent or lumped mass over every element degree of freedom.
|
||||
pub fn mass(&self, density: f64, kind: MassKind) -> modelcraft_fem::Result<Matrix> {
|
||||
pub fn mass(&self, density: f64, kind: MassKind) -> fieldcraft_fem::Result<Matrix> {
|
||||
Ok(match (self, kind) {
|
||||
(StructuralKernel::Shell(s), MassKind::Consistent) => s.mass(density)?,
|
||||
(StructuralKernel::Shell(s), MassKind::Lumped) => {
|
||||
@@ -242,7 +242,7 @@ impl StructuralKernel {
|
||||
}
|
||||
|
||||
/// Consistent nodal forces and moments of a uniform force per volume, six per node.
|
||||
pub fn body_force(&self, b: [f64; 3]) -> modelcraft_fem::Result<Vec<f64>> {
|
||||
pub fn body_force(&self, b: [f64; 3]) -> fieldcraft_fem::Result<Vec<f64>> {
|
||||
match self {
|
||||
StructuralKernel::Shell(s) => s.body_force(b),
|
||||
StructuralKernel::Beam(beam) => Ok(beam.body_force(b)),
|
||||
@@ -405,8 +405,8 @@ impl Mesh {
|
||||
)));
|
||||
}
|
||||
let set = resolved.sets.get(§ion.set).ok_or_else(|| Error::Invalid(format!("set `{}` is not resolved", section.set)))?;
|
||||
let bad = |e: modelcraft_fem::Error| match e {
|
||||
modelcraft_fem::Error::NotSupported(m) => Error::NotSupported(format!("section `{name}`: {m}")),
|
||||
let bad = |e: fieldcraft_fem::Error| match e {
|
||||
fieldcraft_fem::Error::NotSupported(m) => Error::NotSupported(format!("section `{name}`: {m}")),
|
||||
e => Error::Invalid(format!("section `{name}`, material `{}`: {e}", section.material)),
|
||||
};
|
||||
let shared = match §ion.kind {
|
||||
@@ -483,7 +483,7 @@ impl Mesh {
|
||||
layout.element(&e.2, e.7);
|
||||
}
|
||||
for e in &trusses {
|
||||
layout.element(&e.2, modelcraft_fem::ElementDofs::new(translations));
|
||||
layout.element(&e.2, fieldcraft_fem::ElementDofs::new(translations));
|
||||
}
|
||||
for e in &structural {
|
||||
layout.element(&e.2, ElementDofs::new(Structural::KINDS));
|
||||
@@ -705,7 +705,7 @@ pub fn loads(model: &Model, resolved: &Resolved, mesh: &Mesh, step: &Step, activ
|
||||
let xyz: Vec<[f64; 3]> = idx.iter().filter_map(|i| mesh.xyz.get(*i).copied()).collect();
|
||||
let theory = e.theory.ok_or_else(|| Error::Internal("2D element without a theory".into()))?;
|
||||
let forces = edge_pressure(&xyz, theory.extent(), *magnitude)
|
||||
.map_err(|err| Error::element(g.element, modelcraft_fem::Error::Invalid(err.to_string())))?;
|
||||
.map_err(|err| Error::element(g.element, fieldcraft_fem::Error::Invalid(err.to_string())))?;
|
||||
for (i, v) in idx.iter().zip(forces) {
|
||||
add(&mut f, &translations(mesh, *i)?, v);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user