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:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-13 23:51:40 -04:00
1 parent 9bc4e1b182
commit d5bd74570f
268 files changed
+1666 -1666

No files matched your search

+2 -2
View File
@@ -17,11 +17,11 @@
//! Both are modelled on one symmetric part (a quarter of the roof, an eighth of the cylinder)
//! with quad4 (MITC4) shells on a structured mesh whose quadrilaterals are exactly flat.
use modelcraft_model::{
use fieldcraft_model::{
BoundaryCondition, Distribution, Elastic, ElementKind, Formulation, Load, Material, Model, Procedure, Section, SectionKind, Set, Step, Symmetry,
Target,
};
use modelcraft_solver::{Limits, solve_static};
use fieldcraft_solver::{Limits, solve_static};
/// A cylindrical shell patch: x from 0 to `length`, angle φ from 0 (top, z = R) to `arc`,
/// y = R sin φ, z = R cos φ; n × n quad4 cells. Returns the model and the node at (x, φ).
+1 -1
View File
@@ -5,7 +5,7 @@
use std::collections::BTreeMap;
use modelcraft_model::{
use fieldcraft_model::{
BoundaryCondition, Coupling, CouplingKind, Dofs, Elastic, ElementFace, ElementKind, Formulation, Integration, Load, Material, Model, Procedure,
ReferencePoint, Section, SectionKind, Set, Step, Symmetry, Target,
};
+4 -4
View File
@@ -6,8 +6,8 @@ mod common;
use std::f64::consts::PI;
use common::*;
use modelcraft_model::{BoundaryCondition, CouplingKind, Distribution, ElementKind, Load, Procedure, Symmetry, Target};
use modelcraft_solver::{FrequencyOptions, Limits, StaticResult, solve_frequency, solve_static};
use fieldcraft_model::{BoundaryCondition, CouplingKind, Distribution, ElementKind, Load, Procedure, Symmetry, Target};
use fieldcraft_solver::{FrequencyOptions, Limits, StaticResult, solve_frequency, solve_static};
const ALL: [u8; 6] = [1, 2, 3, 4, 5, 6];
@@ -221,7 +221,7 @@ fn tube(nr: usize, nt: usize, nz: usize, ri: f64, ro: f64, length: f64) -> Grid
.filter(|(_, p)| near(p[1], 1.0))
.map(|(l, p)| (*l, *g.param.iter().find(|(_, o)| near(o[1], 0.0) && near(o[0], p[0]) && near(o[2], p[2])).unwrap().0))
.collect();
let elements: Vec<(u32, modelcraft_model::Element)> = g.model.elements().iter().map(|(l, e)| (*l, e.clone())).collect();
let elements: Vec<(u32, fieldcraft_model::Element)> = g.model.elements().iter().map(|(l, e)| (*l, e.clone())).collect();
for (label, e) in elements {
let nodes: Vec<u32> = e.nodes.iter().map(|n| seam.iter().find(|(a, _)| a == n).map_or(*n, |(_, b)| *b)).collect();
g.model.set_element(label, e.kind, nodes).unwrap();
@@ -308,7 +308,7 @@ fn coupling_errors_name_the_problem() {
g.load("m", moment(rp("Tip"), [1.0, 0.0, 0.0]));
assert!(err(&g).contains("rotation about X is not coupled"), "{}", err(&g));
let loose = |g: &mut Grid| g.model.add_reference_point("Loose", modelcraft_model::ReferencePoint { xyz: [3.0, 0.0, 0.0], label: 0 }).unwrap();
let loose = |g: &mut Grid| g.model.add_reference_point("Loose", fieldcraft_model::ReferencePoint { xyz: [3.0, 0.0, 0.0], label: 0 }).unwrap();
// A force on a reference point nothing couples.
let mut g = base();
+2 -2
View File
@@ -6,8 +6,8 @@
mod common;
use common::*;
use modelcraft_model::*;
use modelcraft_solver::{Limits, solve_static};
use fieldcraft_model::*;
use fieldcraft_solver::{Limits, solve_static};
const NU: f64 = 0.4999;
+4 -4
View File
@@ -8,15 +8,15 @@
//! bars, P / (1 + 2 cos³θ) in the vertical one.
//! - rod3: constant-stress patch with off-centre middle nodes; a bar hanging under its own
//! weight (quadratic displacement, exact at the nodes); curved members are checked in
//! `modelcraft_fem::truss` (a ring's radial expansion gives the hoop strain δ/R exactly).
//! `fieldcraft_fem::truss` (a ring's radial expansion gives the hoop strain δ/R exactly).
//! - Spring–mass ω = √(k/m), springs along three skew directions, the two-mass chain, a bar with
//! a tip mass ω = √(EA/(LM)), the torsional pendulum ω = √(k_t/J).
use std::f64::consts::PI;
use modelcraft_fem::truss::axial;
use modelcraft_model::*;
use modelcraft_solver::{FrequencyOptions, FrequencyResult, Limits, MassKind, StaticResult, solve_frequency, solve_static};
use fieldcraft_fem::truss::axial;
use fieldcraft_model::*;
use fieldcraft_solver::{FrequencyOptions, FrequencyResult, Limits, MassKind, StaticResult, solve_frequency, solve_static};
const ZERO_Z: [Option<f64>; 6] = [None, None, Some(0.0), None, None, None];
+8 -8
View File
@@ -6,11 +6,11 @@
use std::collections::BTreeMap;
use std::f64::consts::PI;
use modelcraft_model::{
use fieldcraft_model::{
BoundaryCondition, Elastic, ElementEdge, ElementKind, Formulation, Integration, Load, Material, Model, Procedure, Section, SectionKind, Set,
Step, Symmetry, Target,
};
use modelcraft_solver::{FrequencyOptions, Limits, StaticResult, solve_frequency, solve_static};
use fieldcraft_solver::{FrequencyOptions, Limits, StaticResult, solve_frequency, solve_static};
/// A structured 2D mesh of `n` cells on [0, 1]², mapped by `map`; nodes on the doubled lattice so
/// quadratic elements take their mid-side nodes from the map.
@@ -540,15 +540,15 @@ fn two_dimensional_results_are_deterministic_and_errors_name_the_problem() {
assert_eq!(g.solve(), g.solve());
// A coupling is not supported in 2D yet.
let mut g = make();
g.model.add_reference_point("RP", modelcraft_model::ReferencePoint { xyz: [0.0, 2.0, 0.0], label: 0 }).unwrap();
g.model.add_reference_point("RP", fieldcraft_model::ReferencePoint { xyz: [0.0, 2.0, 0.0], label: 0 }).unwrap();
g.model
.add_coupling(
"c",
modelcraft_model::Coupling {
fieldcraft_model::Coupling {
reference_point: "RP".into(),
set: "bottom".into(),
kind: modelcraft_model::CouplingKind::Kinematic,
dofs: modelcraft_model::Dofs::all(),
kind: fieldcraft_model::CouplingKind::Kinematic,
dofs: fieldcraft_model::Dofs::all(),
},
)
.unwrap();
@@ -571,8 +571,8 @@ fn two_dimensional_results_are_deterministic_and_errors_name_the_problem() {
/// Give the material an expansion coefficient and heat every node by `dt` above the reference.
fn heat(g: &mut Grid, alpha: f64, dt: f64) {
let mut m = g.model.material("Mat").unwrap().clone();
m.thermal = Some(modelcraft_model::Thermal {
expansion: Some(modelcraft_model::Directional::Isotropic(alpha)),
m.thermal = Some(fieldcraft_model::Thermal {
expansion: Some(fieldcraft_model::Directional::Isotropic(alpha)),
reference_temperature: 20.0,
..Default::default()
});
+6 -6
View File
@@ -9,11 +9,11 @@
use std::collections::BTreeMap;
use modelcraft_model::{
use fieldcraft_model::{
BeamProfile, BoundaryCondition, Distribution, Elastic, ElementFace, ElementKind, Formulation, Load, Material, Model, Procedure, Section,
SectionKind, Set, Step, Target,
};
use modelcraft_solver::{FrequencyOptions, Limits, MassKind, StaticResult, solve_frequency, solve_static};
use fieldcraft_solver::{FrequencyOptions, Limits, MassKind, StaticResult, solve_frequency, solve_static};
/// A model under construction: nodes and elements numbered from 1, one material, one step.
struct Build {
@@ -343,7 +343,7 @@ fn cantilever_beam_deflection_is_exact() {
// A stocky I beam, so shear deformation matters: δ = P L³/(3 E I) + P L/(G A_s) in each plane.
let profile = BeamProfile::I { height: 200.0, width: 100.0, web: 6.0, flange: 10.0 };
let length: f64 = 1000.0;
let p = modelcraft_fem::beam::properties(&profile, 0.3);
let p = fieldcraft_fem::beam::properties(&profile, 0.3);
let (e, g) = (210e3, 210e3 / 2.6);
for n in [1, 4] {
for (along_y, name) in [(true, "weak"), (false, "strong")] {
@@ -377,7 +377,7 @@ fn cantilever_beam_frequencies() {
// Euler–Bernoulli ω_k = (β_k L)² √(E I / (ρ A L⁴)).
let profile = BeamProfile::Pipe { radius: 10.0, thickness: 1.0 };
let length: f64 = 10000.0;
let p = modelcraft_fem::beam::properties(&profile, 0.3);
let p = fieldcraft_fem::beam::properties(&profile, 0.3);
let base = (210e3 * p.iy / (7.85e-9 * p.area * length.powi(4))).sqrt() / (2.0 * std::f64::consts::PI);
let beta = [1.875_104_068_711_961, 4.694_091_132_974_175, 7.854_757_438_237_613];
for mass in [MassKind::Consistent, MassKind::Lumped] {
@@ -443,7 +443,7 @@ fn portal_frame_matches_the_slope_deflection_solution() {
b.force("push", vec![n as u32 + 1], [load, 0.0, 0.0]);
let r = b.solve();
let g = e / 2.6;
let (pc, pb) = (modelcraft_fem::beam::properties(&column, 0.3), modelcraft_fem::beam::properties(&girder, 0.3));
let (pc, pb) = (fieldcraft_fem::beam::properties(&column, 0.3), fieldcraft_fem::beam::properties(&girder, 0.3));
let (phi_c, phi_b) = (12.0 * e * pc.iy / (g * pc.shear_z * h * h), 12.0 * e * pb.iy / (g * pb.shear_z * l * l));
let (a, bb) = (e * pc.iy / (h * (1.0 + phi_c)), e * pb.iy / (l * (1.0 + phi_b)));
let c = 6.0 * a / (h * (a * (4.0 + phi_c) + 6.0 * bb));
@@ -499,7 +499,7 @@ fn rods_continue_a_solid_column() {
}
let rods: Vec<_> = r.elements.iter().filter(|e| e.kind == ElementKind::Rod2).collect();
assert_eq!(rods.len(), 4);
assert!(rods.iter().flat_map(|e| &e.nodal).all(|s| rel(modelcraft_fem::von_mises(s), 4.0) < 1e-12), "{rods:?}");
assert!(rods.iter().flat_map(|e| &e.nodal).all(|s| rel(fieldcraft_fem::von_mises(s), 4.0) < 1e-12), "{rods:?}");
// Solid nodes have no rotations.
assert!(r.rotation.iter().all(|x| *x == [0.0; 3]));
}
+4 -4
View File
@@ -7,8 +7,8 @@
mod common;
use common::*;
use modelcraft_model::*;
use modelcraft_solver::{Limits, solve_heat, solve_static};
use fieldcraft_model::*;
use fieldcraft_solver::{Limits, solve_heat, solve_static};
const K: f64 = 45.0;
@@ -26,7 +26,7 @@ fn hold(g: &mut Grid, name: &str, value: f64, f: impl Fn([f64; 3]) -> bool) {
g.bc(name, BoundaryCondition::Temperature { target: Target::Set(name.into()), value });
}
fn temperature_at(r: &modelcraft_solver::HeatResult, label: u32) -> f64 {
fn temperature_at(r: &fieldcraft_solver::HeatResult, label: u32) -> f64 {
r.temperature()[r.nodes.binary_search(&label).unwrap()]
}
@@ -384,7 +384,7 @@ fn heat_steps_refuse_what_they_cannot_solve() {
/// One rod3 or two rod2 along X over [0, `len`], heated by `dt`, fixed at x = 0, the other
/// nodes held in Y and Z, and the end held along X too when `held`.
fn heated_rod(kind: ElementKind, len: f64, alpha: [f64; 3], dt: f64, held: bool) -> modelcraft_solver::StaticResult {
fn heated_rod(kind: ElementKind, len: f64, alpha: [f64; 3], dt: f64, held: bool) -> fieldcraft_solver::StaticResult {
let mut m = Model::default();
for (label, x) in [(1, 0.0), (2, len), (3, 0.5 * len)] {
m.set_node(label, [x, 0.0, 0.0]).unwrap();
+5 -5
View File
@@ -6,8 +6,8 @@ mod common;
use std::f64::consts::PI;
use common::*;
use modelcraft_model::{BoundaryCondition, ElementKind, Load, Procedure, Symmetry, Target};
use modelcraft_solver::{FrequencyOptions, Limits, MassKind, StaticResult, solve_frequency, solve_static};
use fieldcraft_model::{BoundaryCondition, ElementKind, Load, Procedure, Symmetry, Target};
use fieldcraft_solver::{FrequencyOptions, Limits, MassKind, StaticResult, solve_frequency, solve_static};
fn static_solve(g: &Grid) -> StaticResult {
solve_static(&g.model, Some("Step"), &Limits::default()).unwrap()
@@ -50,7 +50,7 @@ fn patch_test_every_element() {
let x = g.model.node(label).unwrap().xyz;
let u = field(x);
let set = format!("b{i}");
g.model.add_set(&set, modelcraft_model::Set { nodes: vec![label], ..Default::default() }).unwrap();
g.model.add_set(&set, fieldcraft_model::Set { nodes: vec![label], ..Default::default() }).unwrap();
g.bc(
&set,
BoundaryCondition::Displacement { target: Target::Set(set.clone()), values: [Some(u[0]), Some(u[1]), Some(u[2]), None, None, None] },
@@ -132,7 +132,7 @@ fn gravity_and_body_force_totals() {
/// Cantilever L × b × h, clamped at x = 0, a shear traction on the free end. Timoshenko
/// deflection δ = P L³ / (3 E I) + P L / (κ G A), κ = 5/6, with ν = 0 so the clamp adds no
/// lateral restraint.
fn cantilever_tip(kind: ElementKind, formulation: modelcraft_model::Formulation, n: [usize; 3]) -> f64 {
fn cantilever_tip(kind: ElementKind, formulation: fieldcraft_model::Formulation, n: [usize; 3]) -> f64 {
let (l, b, h, young, p) = (10.0, 1.0, 1.0, 1000.0, 1.0);
let mut g = grid(n, kind, |q| [l * q[0], b * q[1], h * q[2]]);
g.define(iso(young, 0.0), None, formulation, Procedure::Static {});
@@ -377,7 +377,7 @@ fn plastic_materials_solve_elastically_with_a_warning() {
g.define(iso(1000.0, 0.3), None, full(), Procedure::Static {});
if plastic {
let mut m = g.model.material("Mat").unwrap().clone();
m.plastic = Some(modelcraft_model::Plastic::Bilinear { yield_stress: 1.0, hardening_modulus: 0.0, hardening: Default::default() });
m.plastic = Some(fieldcraft_model::Plastic::Bilinear { yield_stress: 1.0, hardening_modulus: 0.0, hardening: Default::default() });
g.model.replace_material("Mat", m).unwrap();
}
g.fixed("root", |q| near(q[0], 0.0));