Skip to content

Scientific agent reference

Read only for a relevant numerical implementation or scientific question. Ordinary installation, result viewing and routine execution use the short agent guide. Code paths below are relative to the repository root; this is a topic index, not a mandatory checklist.

Progressive Reading

  • Mesh, domain, or region modeling: read the Mesh Region, Selector, and Region Set concepts in CONCEPTS.md. Prefer named regions and region sets over raw integer tags in user-facing workflows. Use low-level MeshTags helpers only when importing, validating, or teaching the tagging implementation.
  • Mesh or boundary tagging: read docs/module_map.md, then use mesh.summarize_mesh, mesh.require_cell_tags, and mesh.require_facet_tags. Reviewed array archives use mesh.read_array_mesh and an explicit ordering manifest; part exteriors use mesh.exterior_regions, not case-owned MeshTags construction. Preserve input-cell identity and independent coincident nodes. See docs/guide/array_mesh_inputs.md; a shared material interface is not an exterior surface and must not be silently turned into contact.
  • Abaqus migration: run agentfem inspect-abaqus source.inp --json before conversion and read docs/abaqus_migration.md. Preserve element formulation suffixes and report topology-only support; never substitute a native element formulation silently.
  • If migration.json reports an eligible narrow native route, use agentfem lower-abaqus PROJECT --reviewed-by NAME --unit-system SYSTEM to create case.native.py and lowering.json without activation. Read both, then use --activate --force only when accepted. Never invent units or delete a blocking finding. Multiple constant isotropic materials are lowered only through exact, non-overlapping ELSET/Section coverage. Dependent material tables, partial or ambiguous Section coverage, non-unit 2D thickness, and Step/BC inheritance require a dedicated lowering route rather than a first-row or final-value approximation.
  • Study setup: use studies.static_solid, studies.steady_heat_transfer, studies.transient_heat_transfer, or studies.dynamic_solid for common workflows. Use the general factories only when the common vocabulary does not fit. dynamic_solid(method=...) changes the procedure, not the physics.
  • Solution procedure: inspect procedures.py when Standard/Explicit or a particular time integrator must be selected. Do not encode the algorithm by inventing a new physical analysis type.
  • Model audit: use model.tree(), model.manifest(), model.summary(), and model.validate() when multiple fields, regions, loads, constraints, or steps must be inspected. Use model.check() to stop before execution on validation errors and model.write_ir(...) when a persistent scientific record is required.
  • Capability dispatch: inspect models.step_capability(model) or run model.check() before solve. It queries the same provider predicates as model.step(...), including target-field shape and the material protocol needed by the default lowering; never work around AFM-STUDY-002 by silently changing the Study.
  • Step failures: read error.details in the local execution record for step_options or step_capability failures. Use the recorded suggestions or rejection reasons to propose a targeted correction; syntax-only check does not validate these physical inputs. Do not automatically rerun or change physics.
  • API discovery: begin with agentfem.public_api("core") or the public_api.core section of agentfem capabilities workflow --json. Escalate to the advanced or expert layer only when the task requires it; the flat public_modules inventory remains a compatibility contract. These generated views share one product contract; never reconstruct a competing vocabulary from one example or an old compatibility method. Use a relevant topic/material name for focused discovery. Scoped views omit unrelated sections, not capability limitations or verification gaps; use the complete record only when needed. For an uncertain parameter, use agentfem capabilities --api fields.displacement --json (or models.Model.step) to read the installed signature. Do not repeat discovery for every already-known call; provider options still use the Step contract.
  • Model registration: use model.field(...), model.material(...), model.fix(...), model.traction(...), model.surface_force(...), model.distributing_coupling(...), and model.elastic_foundation(...) in application workflows when the assets should stay visible and auditable.
  • Application unknowns: use fields.py before dropping to spaces.py.
  • Sequential thermal--mechanical handoff: use the same runtime mesh and an explicit stage.predefine(T_solid, T, method="interpolate"). Never copy coefficient arrays merely because their lengths match. Protect the configured Step's construction and solve with stage.field_transaction(displacement=u); see docs/engineering_workflows.md. Physical times, when relevant, must be explicitly declared and equal. This does not imply cross-mesh transfer, unit conversion, live binding, or converged two-way coupling.
  • Function spaces: inspect spaces.py; only inspect kernel/dofs.py for implementation-level dof work.
  • Essential boundary conditions: read CONCEPTS.md, then use constraints/.
  • Natural loads: read CONCEPTS.md, then use loads.body_load, loads.neumann, or loads.boundary_load.
  • Constitutive laws: read docs/nonlinear_materials.md, query constitutive.capabilities(), then use constitutive/. Never turn a material-point law into a claimed FEM step without state/tangent/solver evidence. For a custom finite-strain history material, read docs/history_material_workflow.md and examples/history_material before creating a project Step provider. The bounded serial 3D/plane-strain route already owns quadrature, Newton, physical time, rollback/restart and unified multi-field output. Supply the local UserMaterial and its reference dP/dF, not a project Newton loop. Its exclusions are explicit; do not infer general plane stress, MPI, contact or arbitrary UMAT support. Prescribed uniform temperature is supplied by temperature= as positive kelvin or a scalar linear histories.temperature over physical time; the Step freezes this input and owns accepted TEMP output. Do not infer spatial thermal histories, heat conduction or two-way coupling from this option. Use material_points=(results.material_point("site", cell=..., point=...),) for selected raw histories through this same Step. site.E_GL is total Green–Lagrange strain; do not treat it as infinitesimal or mechanical strain for life assessment. No project sampling loop or XML edits are needed. The limited plane-stress option requires a 2D plane-stress Study and explicit constitutive.PlaneStressReduction(symmetry="reflection_symmetric"). The core solves thickness equilibrium, condenses the algorithmic tangent and preserves full 3D stress/history. Never truncate stiffness or infer a 3D material from a uniaxial fit. Forces are per unit reference thickness. For a reviewed strain-history VUMAT, read docs/strain_history_runtime.md. Use the declared runtime instead of writing project-specific ctypes/Fortran bridges. Never declare unused thermal, time, length or energy channels without source review, or infer state layouts and step limits from another case. constitutive.chaboche(...) is an experimental three-dimensional global combined-hardening route with quadrature rollback/restart and ALPHA output. Keep its external-definition evidence distinct from a calibrated structure-level stabilized-hysteresis validation. For composite solids and sections, read docs/composite_materials_and_forming.md. Keep a material's constants, assignment orientation, and laminate placement separate. A DecoupledFabricSurface has an experimental finite-kinematics in-plane membrane provider through studies.static_membrane() and model.step(...). That provider consumes yarn tension and trellising shear and rejects nonzero bending stiffness. Objective director-shell measures are local foundations; do not present them as a shell element, contact, or forming simulation until those providers and their benchmarks exist. constitutive.finite_strain_j2_logarithmic(...) has experimental public model.step(...) providers for ordinary strong boundaries/reference dead loads and for 3D affine-periodic cells with exactly one AbaqusPeriodicConstraint. Both share one provider-owned quadrature transaction and accepted-state portable checkpoint. The ordinary serial provider also accepts independently named rigid pairs using a differentiable SmoothRigidSurface (built-in plane/sphere/cylinder or a conforming external implementation), or a triangulated tool with interior or coplanar-seam projection. Each prescribed tool pose needs a normalized 0-to-1 schedule driven by the shared Step amplitude; per-pair accepted force--moment work and all poses must rollback together. Open triangle patches reject active free/noncoplanar edges. Reviewed closed-solid tools and Coulomb friction have separate, geometry-dependent admission limits; use the current matrix in docs/guide/model_setup.md, not a blanket friction prohibition or an assumption that all sharp features are supported. The separate affine-periodic provider accepts named multiple rigid pairs and tested locally planar Coulomb combinations, with total-residual MPC reduction and a nonsymmetric linear-solver policy for friction. Reject follower loads and unrelated weak boundary models; ordinary strong constraints and affine MPC cannot be mixed in one Step. The J2 law owns its analytic discrete tangent, but that alone does not validate a contact geometry, mixed displacement--pressure/contact combination, external structure response, general energy closure or distributed implicit route.
  • Named materials: use materials.define(...) when material identity, source, reuse, or separate mechanical/thermal roles matter. The Study selects the required physics role at model.material(...); it does not choose or rewrite the constitutive equation. Packaged load_definition(...) cards are reference-only unless their source and applicability establish otherwise.
  • Project materials: materials.load("materials/active.py") explicitly loads reviewed, trusted Python code and records its content hash. Do not use it to inspect downloaded code. TOML may select an asset but is not a constitutive language; installed or private collections belong in extension packages. For reviewed multi-source Fortran providers use constitutive.build_native_libraries rather than rewriting compiler/cache logic in a project. Keep source trust explicit. For supported serial transient delivery use project.execute_step around the ordinary Step, not a case-owned integration loop. See docs/project_execution.md. Project version constraints belong in project.requires_agentfem, not private-module probes or commit IDs.
  • Absorbing or Robin-like terms: use boundary_models/.
  • Assembly or lumped operators: inspect assembly.py.
  • Time stepping: inspect time/ and problems.py. For the serial response-Hex route use mass_scaling=FixedMassScaling(...) or InitialMassScaling(...) (reference estimate), or TargetIncrementMassScaling(...) instead of generating nodal arrays in a case. Target planning requires a reviewed bound provider and a mass budget. Never fill a missing contact/hourglass/damping contribution with zero merely to satisfy its coverage check. Never infer path stability from a single initial elastic tangent. An NPZ from another geometry is not a mass policy.
  • Time histories: define reusable assets in amplitudes.py, register important histories with model.amplitude("name", history), and reference the name from loads or prescribed data. Static single-solve, nonlinear normalized step time, and transient physical time have distinct documented coordinates. Do not substitute the half-cosine smooth_step for a quintic loading law: use quintic_step and pass its physical-time amplitude to the rigid-motion schedule. Changing that history changes checkpoint identity. Attach histories to model loads, prescribed values, or supported boundary models. Transient procedures update registered histories automatically; use callbacks only for genuinely application-specific state. Type a custom callback with time.input_update(...): RHS/output-only inputs may retain a prepared linear operator, while operator/state inputs require refresh. Linear heat and implicit dynamics enforce this distinction; nonlinear heat remains per-step assembled. For many related loading modes, use amplitudes.basis(...) and basis.combine(...); preserve coefficient order, derivatives, endpoint audit, and the amplitude fingerprint. Do not represent a frozen campaign waveform only as an anonymous callable.
  • Solves: inspect solvers.py.
  • Step incrementation: use steps.automatic(...) by default or steps.fixed(...) only when exact fixed subdivision is scientifically intended. max_increments limits accepted increments; Newton max_it limits iterations in one attempt.
  • Global implicit creep has an additional physical-time accuracy decision: The experimental constitutive.CreepPlasticityMaterial composition uses the ordinary creep_solid Study and model.step, not a project-level sequential material loop. It jointly commits creep/plastic histories; its maximum inelastic increment checks both CEEQ and PEEQ. Limit this composition to serial homogeneous 3D small strain; contact, MPI and temperature-dependent plastic properties are not admitted. See examples/thermal_cyclic_creep. creep_strain_error_tolerance bounds the endpoint creep-rate change times the attempted increment. Keep it distinct from Newton convergence and maximum_inelastic_increment; all three may independently trigger atomic rollback and cutback. Result time units come from the model UnitSystem and remain undeclared when no consistent-unit contract was supplied. For one-way thermal input, capture_history(...) records source Study, procedure, accepted-time role and content identity. Use assessments.sequential_energy_ledger(...) to keep heat and mechanical residuals separate. For engineering creep--fatigue from result histories, declare DwellInterval objects and pass a source-identified project rupture relation to creep_fatigue_from_result(...); do not embed normative curves. For tabulated rupture data in different units, explicitly call curve.in_units(stress_unit=..., time_unit=...); keep accepted histories unchanged. Conversion preserves provenance, not experimental applicability. For scalar strain-life instead, explicitly call assess_strain_history with a calibrated CoffinMansonBasquinCurve and signed total mechanical strain. Never substitute accumulated PEEQ or sparse visualization frames. For a reviewed strain-amplitude applicability interval, use the curve's from_strain_domain constructor; do not widen observed cycle extrema merely to make an endpoint fit evaluate. This declares a different input domain, not a newly validated material model. The result can be passed to assessments.creep_fatigue; it is not a stiffness-damage law or a certified service-life prediction. For experimental lives/run-outs, use assessments.LifeObservation and life_survival (see docs/life_observations.md). Keep distinct test conditions separate; survival confidence limits are not individual-life prediction bounds. For a predetermined sample size/horizon with known binary outcomes, use fixed_horizon_reliability, including zero failures. It rejects early censoring; do not delete such specimens or select the horizon after inspecting outcomes. Use creep_damage_from_result(..., quadrature_subdivisions=...) only for explicit quadrature refinement; source-gap checks still apply to recorded history, not interpolated points. Keep this separate from FE time refinement.
  • Multi-Step activation: use model.stage(...) and pass its result as configuration= to model.step(...); keep asset inheritance separate from incrementation and nonlinear solver controls.
  • Hybrid solids: a known C3D10H source mesh requires fields.displacement_pressure(...) and constitutive.mixed_neo_hookean(...); do not route it through a displacement-only provider.
  • Analysis steps: prefer model.step(...) when a model owns fields, materials, loads, and constraints. Pass a SolutionProcedure object when the numerical route must be explicit or must override the Study preference; capability inspection and lowering consume that same object. Treat named legacy Step factories as expert/compatibility entrypoints. Use problems.linear_static or problems.first_order_transient when a workflow intentionally starts from explicit K/C/F operators without model ownership.
  • Problem summaries: use problems.FEMProblem when a workflow needs a broader structured audit record.
  • Scientific formulas: when a formula arrives from JSON, a benchmark, experiment metadata, or agent-authored configuration, use expressions.expression(...), expressions.as_ufl(...), or expressions.interpolate(...). The language accepts coordinates, declared parameters, arithmetic, and reviewed mathematical functions; never replace it with eval, and do not silently reinterpret an invalid formula. Use as_ufl for symbolic weak-form physics and interpolate for known fields; the latter uses the same validated AST without a per-expression C++ JIT.
  • Results: read docs/results_and_campaigns.md; use solve_result() or solve_result(output="results.xdmf") for the standard static/transient solve/output/result lifecycle. Use results.SimulationResult, MPI-safe point/path probes, region integrals/averages, boundary resultants and field extrema in results, then attach additional XDMF/CSV artifacts from io. A model-owned case may instead declare a path in model.step(..., output="results.xdmf") and call solve_result() once; do not send the same output request through custom case loops. Model-generated static elasticity produces projected S/E/MISES automatically; SENER is opt-in through field_variables. J2 and creep expose the same completed-result writer through solve_result(output=...); raw quadrature fields remain result evidence and explicit *_CELL fields are written for visualization. Use results.small_strain_partition_fields for reviewed regional projections and results.reaction_resultant(..., on=..., component=...) only for named strong-constraint reactions. Inspect SimulationResult.metadata["constraint_balance_contract"] before claiming global equilibrium: MPC, weak, contact, projection, or multiplier channels remain unavailable until their active provider supplies actual dual forces. For imported physical surfaces use mesh.tagged_boundary_region(...) for both strong and weak conditions, call model.audit_boundaries(strict=True), and use results.region_measure(on=...) rather than application-owned UFL. Request field_extrema(..., location=True) when publishing a peak. Serial solve_result(output=...) writes one Uniform Grid with mixed point/cell attributes; low-level io.XDMFTimeSeries retains DOLFINx multi-Grid semantics. Under MPI, open the field_output metadata's recommended_visualization_artifact (normally PVD), not the scientific multi-Grid XDMF; that route needs no Extract Block step. Never write VTK/XDMF connectivity or reorder element nodes in an application case. Use solve_result(output=...), or io.ResultWriter for a custom loop. Use results.cell_field_from_values with explicit cell keys for external per-cell data. Equal array lengths do not establish matching field layouts. Keep reference geometry for Warp; do not apply Warp again to an already deformed presentation artifact. Never merge coincident physical nodes merely to remove duplicate visualization blocks: cracks/interfaces may require them. For field-learning data, create a fixed surrogates.ObservationGrid and call datasets.fem_observation_sample; preserve its units, layout, and mask. For a direct solver or external benchmark contract, use results.sample_rectilinear_grid(...); its physical-axis shape=(nx, ny, nz) is returned in array order (nz, ny, nx) and its inside mask must be retained for non-rectangular domains. For the pinned PDEAgent-Bench contract, use integrations.pdeagent_bench.solve_case(...) and read docs/pdeagent_bench.md; inspect SUPPORTED_FAMILIES rather than guessing capability from a case name. Do not read oracle-only mesh, FEM, solver, or manufactured-solution fields, dispatch on case IDs, or infer success from execution alone. Preserve the benchmark commit and failure taxonomy. For mixed flow, periodic MPC, or split fourth-order cases, report the resolved field pair, pressure reference, periodic method, and auxiliary boundary closure from solver_info rather than presenting them as implicit defaults. When observations and the FEM mesh use different axes or origins, pass an explicit surrogates.AffineCoordinateMap; for publication maps convert the result to datasets.RectilinearObservation before comparison. Never infer a transpose, unit conversion, or reference/current transformation from a figure. A fracture.DynamicFractureEvidenceBundle is the portable handoff for one fracture condition, but its provenance seal is not a validation claim. Distinguish execution status from result.trust_level, apply a named quality policy for routine checks, and attach explicit scientific claims before describing a result as verified.
  • Result provenance: register every numerical/report artifact before publishing, then use agentfem verify ... --json. Treat verified here as byte-integrity evidence only; scientific trust remains in SimulationResult.verification.
  • Runtime freeze: use provenance.freeze_runtime(...) before a frozen or blind campaign and provenance.require_runtime(...) before continuing it. A runtime match establishes execution compatibility, not scientific validity.
  • Scientific inputs: pass reusable mesh/source files as Path values and materials, loading bases, procedures, and observer plans through Campaign(scientific_inputs=...). Review fingerprint coverage; an opaque input is an explicit incomplete identity, not permission to ignore it.
  • For a single run, attach the same assets with result.add_scientific_inputs(...) before writing its manifest. Pass source files as Path, not filename strings, when byte identity matters.
  • Events: use events.first_passage(...) for threshold timing. Preserve its bracket, localization method, and censoring state; for discontinuous damage or contact, refine the solve rather than presenting linear interpolation as an exact event time.
  • For long transient jobs, pass one checkpointing.every(...) policy through model.step(checkpoint=...); do not implement cadence in a case loop. Set keep_last for bounded storage; do not delete checkpoint directories in application code.
  • For transient probes or scalar histories, pass reusable results.probe_history(...) / results.history(...) requests to model.step(history=...), run(history=...), or solve_result(history=...). Construction-time requests are consumed automatically by the common result lifecycle and retained with solver, output, progress, and checkpoint declarations in execution metadata. The callback receives the public Step and physical time. Keep the same named requests across restart; AgentFEM rejects a changed history schema instead of producing ragged data.
  • Restart: heat, Standard dynamics, and Explicit dynamics share run(until_step=...), save_checkpoint(...), load_checkpoint(...), and resumed solve_result(output=...). Treat output_scope="continuation_segment" as a partial field series even though the result retains full accepted-time and execution evidence. Do not change MPI size or mesh partition when loading the current partition-bound checkpoint.
  • Cohesive state portability: fixed-path cohesive transactions may instead use interfaces.save_portable_cohesive_state(...) and interfaces.load_portable_cohesive_state(...). Physical facet keys survive local order and MPI rank-count changes. create_dolfinx_split_mesh(..., comm=...) plus the unchanged fracture.mode_i_cohesive_force(...) factory provides the experimental sparse-payload MPI force path. When positive-side cell identities are known, prefer interfaces.split_conforming_cell_interface(...) to a hand-written facet list. Portable transient checkpoints also distinguish coincident independent nodes. Do not infer 3D imported surfaces or extreme-scale performance from this 2D owner-scheduled contract.
  • Cyclic cohesive equilibrium: use fracture.FiniteStrainCohesiveEquilibrium(...) when a UFL finite-strain bulk residual and paired-facet interfaces should enter one native Newton solve. Supply the physical scalar load through load_parameter or set_load; keep interface history commit/rollback under the owning Step. Strong-constraint reactions are supported through the residual reaction field, but do not reuse that definition for MPC, weak, contact or multiplier reactions.
  • Installed projects and external frontends: read docs/getting_started.md and docs/agent_gui_integration.md. Keep case.py as modeling truth, use project.current_run() for artifacts, publish a SimulationResult, and consume versioned JSON instead of scraping terminal prose.
  • Installed private extensions: run agentfem extensions --json before accepting a project's [extensions].required list. Treat every extension as trusted executable code; a missing package is not permission to install an unknown distribution. Keep private products in separate repositories and integrate them through the versioned extension API.
  • AF-IR and repair: inspect ir/ and validation.py. Treat AF-IR 0.1 as an experimental record, not as proof of backend-neutral executability.
  • API lifecycle: consult models.model_api_contract() or agentfem capabilities workflow --json when a method's status is uncertain. Run agentfem upgrade for version migration or reported migration findings. Compatibility findings that require semantic review are guidance, not permission for blind textual replacement.
  • Backend work: inspect backends/ and docs/air_architecture_roadmap.md. Preserve the full FEniCSx path and do not advertise a backend until its capabilities are independently tested.
  • Parameter campaigns and learned models: read docs/ai_native_learning.md, then inspect campaigns/, datasets/, and learning/. Build a fresh model per case; retain units, output shapes, case IDs, validation data, and applicability behavior. Do not present a training residual or successful prediction call as independent scientific validation. Use require_dataset(quality="engineering") for ordinary learning data and a release policy when every sample carries a scientific release contract. A user-owned neural-field model may enter through model.step(target=spec, executor=...) without an official adapter, but it must return SimulationResult; do not serialize the live callable into provenance or infer scientific validation from training loss.
  • Predefined fracture fields: construct reusable straight 2D cracks through fracture.segment(...) and fracture.crack_set(...). Preserve stable crack/tip identity, orientation, and fingerprints through every provider. Never infer fracture mode from an input label, average the two sides of a discontinuous field into one nodal value, or report one SIF without its local convention and ring-sensitivity evidence. The first geometry contract deliberately rejects intersecting, touching, and curved cracks. Use fracture.infinite_plate_stress_intensity(...) as an analytical oracle and fracture.interaction_integral_report(...) for common LEFM normalization; use fracture.dolfinx_interaction_integral_report(...) to lower solved DOLFINx stress and displacement-gradient fields, and keep its MPI reduction and multiple-radius evidence intact. The executable benchmarks.center_crack_mode_i_benchmark() is the first finite-element acceptance reference; its split-mesh generator is not a general mesher. do not apply the initial homogeneous straight-crack integral to curved, inhomogeneous, thermally strained, body-force, or traction-bearing crack-face cases without the required additional terms.
  • Neural energy integration: use learning.IntegrationPlan to distinguish the optimization rule from explicitly independent validation and refinement rules. Attach integration_consistency_check(...) evidence to the result. Loss reduction, held-out integration, refinement convergence, boundary checks, and physical balance are separate decisions.
  • Across-case execution: use campaigns.local_processes(workers=...) only for independent cases. It deliberately uses spawn. Do not nest it inside a within-case MPI communicator; use deterministic plan shards for separate MPI jobs or schedulers. Build/evaluate callables must be importable and serializable, and each case must construct fresh mutable solver state.
  • Convergence certificates: use convergence.audit(...) with one explicit refinement axis at a time, fixed values for every other varying parameter, and an observable-specific relative, absolute, or exact comparison. Missing, failed, ambiguous, or insufficient sequences remain inconclusive.
  • Response experiments: use responses.finite_difference(...) when baseline and perturbation cases should share Campaign identity, cache, failures, and evidence. Keep the parameter perturbation mode and output quantities explicit. A failed perturbation makes the response report incomplete.
  • External meshes: inventory with mesh.inspect_external_mesh(...) before choosing cell/facet types. Preserve conversion manifests and never describe mesh conversion as full Abaqus/ANSYS deck import.
  • Abaqus projects: run agentfem inspect-abaqus ... --json before agentfem migrate-abaqus .... The migration project must preserve the recursive source graph, Part/Instance scope, section/material assignments, source locations, and unresolved issue codes. A generated material candidate is reviewable data, not permission to execute it. Do not remove formulation suffixes, flatten same-named sets across scopes, or bypass the fail-closed guard before native operators and verification evidence are selected. Never invent a missing material property or silently omit a Step, load, boundary, interaction, or output listed under pending_assets.
  • Example workflows: inspect examples/ after reading WORKFLOW.md.

Agent Rules

  • Do not mix constraints, loads, and boundary models.
  • For contact, keep surface geometry, rigid-body kinematics, and enforcement law separate. Report tool work only from accepted generalized force--translation/moment--rotation stations, never from penalty energy.
  • When a serial implicit rigid contact trace intersects strong prescribed displacement DOFs, lift the native contact tangent by the prescribed correction and keep constrained matrix rows/columns eliminated. The bounded finite-J2 source candidate exercises this on a symmetry-reduced indentation; it is not evidence for MPI implicit contact or non-smooth tool edges.
  • The finite-J2 rigid-contact result reports final unsmoothed contact-point diagnostics: active reference area, peak nominal penalty pressure and peak penetration per named pair. Their location is reference-boundary quadrature, not nodes; neither a peak alone nor a visually smoothed field proves contact accuracy. For spatial comparisons use the accepted trace's reference_positions and declared measure, not current-coordinate bins that move with deformation. export_accepted_contact_points preserves reference positions when all providers supply them and explicitly reports absence for older providers.
  • For a large serial nonlinear contact model, inspect accepted increment phase timings before tuning. Direct LU can dominate at finer resolution; an explicitly chosen nonsymmetric Krylov/preconditioner policy may help, but accept it only after converged linear reasons and force, work, contact-area, penetration and state comparisons on the same model. Do not make a machine- or case-specific policy the global default.
  • For explicit penalty contact, prefer the reviewed lumped-mass stability screen and preserve its contact-only scope. Add its spectral upper bound to the body/material spectral upper bound before converting the sum to a time increment; taking the minimum of separately derived time limits is not a valid composition. Apply any stricter caller ceiling afterward. Include both normal and tangential penalties plus Coulomb pressure coupling. Require a caller ceiling for curved-normal geometry until geometric stiffness has a reviewed bound; never infer stability from penetration or from a converged time history.
  • For every explicit Procedure, represent body/material, cohesive, contact, and future stiffness restrictions as named time.ExplicitStabilityContribution objects. Combine them once with time.combine_explicit_stability; never take the minimum of independently derived time limits when their stiffness operators act simultaneously, and never apply a safety factor separately to every contribution. The bounded finite Hex8 provider may consume a material-owned FirstPiolaTangentEnvelope instead of a caller frequency ceiling. It must cover all admissible material states/inputs in its stated stretch domain. Never invent an envelope from one initial tangent or equate dt="auto" with adaptive stepping. Native finite J2 still requires the caller ceiling. Its bounded finite-Hex contact candidate accepts one registered frictionless fixed/translating plane in serial or MPI with optional elastic bonding. Keep the reference-surface penalty meaning explicit; rotating/curved tools, multiple pairs and friction are not admitted. An equal-stiffness elastic reference bond can coexist on disjoint trace nodes; this is not debonding/contact switching. Use examples/finite_hex_moving_plane.py or examples/finite_hex_bond_contact.py.
  • Treat a surface projection as evidence, not merely coordinates. Preserve its surface identity, method, admissible-side unit normal, signed-gap convention, validity, discrete entity identity, geometry fingerprint and local coordinates. Never turn a failed or ambiguous closest-point query into an active contact silently, and never reuse local coordinates with another geometry fingerprint.
  • A TriangulatedRigidSurface can validate and project reviewed 3D triangle geometry, and imported triangle tools are executable in the bounded Explicit route through the reviewed serial or routed distributed BVH. This does not imply CAD healing, deformable-master contact, an implicit linearization, or free rigid-body dynamics.
  • triangle_surface_bvh(surface) accelerates a reviewed triangle surface while preserving its exact narrow phase and ambiguity semantics. Inspect its diagnostics when performance matters. For the MPI correctness reference, use partition_triangle_surface(surface, comm) followed by distributed_triangle_surface_bvh(partition, comm). That path owns only a facet shard per rank and reproduces the global projection contract, but uses all-gather communication; never describe it as scalable neighborhood search or executable general contact.
  • routed_distributed_triangle_surface_bvh(partition, comm) uses two-stage rank-AABB routing and the all-gather implementation as its correctness oracle. Inspect avoided-message and queried-rank evidence. Its sparse route uses packed numeric MPI_Alltoallv transport; do not describe that bounded route as a general neighborhood-collective or extreme-scale contact search.
  • RigidSphereSurface and RigidCylinderSurface provide exact analytical projection. The sphere also represents a 2D circle; the cylinder is 3D and infinite by default. Its optional axial_interval admits finite lateral interior only; rim/end queries remain unsupported. Keep their interior/exterior admissible side explicit, preserve singular_projection, and do not claim that projection-only geometry is an executable contact law.
  • Do not hide the finite-element workflow inside overly broad abstractions.
  • Do not make concrete geometry helpers, such as circle/disk/box predicates, the core modeling concept. Treat them as selectors used to build named regions. Regions are the stable interface for materials, loads, constraints, boundary models, output, and imported CAE mesh groups.
  • Keep application-specific geometry, source definitions, and parameter choices outside the core platform.
  • Validate imports, syntax, and a small runnable case after structural changes.
  • Report structured validation codes and paths when they are available; do not replace them with an unaddressed generic error.
  • When changing a public concept, update CONCEPTS.md, WORKFLOW.md, and the relevant skill references.
  • Keep solve increments and result frames distinct. Use output every="increment" or intervals=...; reserve “frame” for saved/read result states.
  • State whether a nonlinear capability is FEM-integrated, material-point verified, or a postprocessor. Use the benchmark registry as evidence.
  • Never call one converged mesh a convergence study. Use coarse-to-fine verification.ConvergenceStudy evidence and record a reference theory's applicability domain.
  • Do not silently extrapolate a learned model. Reject the request or invoke an explicit high-fidelity fallback and report which source produced the result.

Current Platform Focus

AgentFEM currently focuses on a dependable DOLFINx/PETSc path: linear elasticity, implicit and explicit linear structural dynamics, implicit heat transfer, sequential thermoelasticity, a bounded Neo-Hookean nonlinear solve, result/campaign/data flow, external mesh conversion, and deliberately staged path-dependent material tools. The first global J2 and Arrhenius power-law creep providers use regional quadrature materials. J2 MPI global equilibrium is public after partition-interface, cutback/rollback, cross-rank-count restart, and external thick-cylinder structural tests; distributed creep equilibrium retains its separate NAFEMS promotion gate. Their constitutive transactions and quadrature archives are MPI-safe and portable across rank counts. Product depth and external verification take priority over adding material names or backend abstractions.

For undamped linear structural modes, use studies.modal_solid(...) followed by model.step(target=u, modes=...). The SLEPc provider removes strongly constrained degrees of freedom and returns frequencies, eigenpair residuals, orthogonality evidence, and deterministically oriented singleton mode fields through SimulationResult. Repeated or numerically clustered modes must be compared through their recorded invariant subspace, not by matching individual vectors; reject a comparison when selected_clusters_complete is false. Treat a mass-normalized mode shape as a relative pattern, not as a dimensionless or physical displacement; use its recorded Mode shape warp field for visualization.

For small-strain linear steady-state vibration, use studies.harmonic_solid(...) and give model.step(...) explicit K, F, and optional M, C, and K_loss operators. The lowering preserves them in an inspectable operators.direct_harmonic_system(...). This provider-neutral procedure is at engineering maturity for supported strong constraints, one frequency or a bounded canonical sweep, and one real spatial load with a common phase. Supply exactly one of frequency in Hz or angular_frequency in rad/s. Results use the public exp(+i*omega*t) convention and expose U_REAL, U_IMAG, U_AMPLITUDE, U_PHASE, residual, cycle-energy, and load evidence. Sweep checkpoints are portable scalar evidence ledgers, not full-field recovery. Complex modes, prestressed small-on-large response, nonlinear harmonic balance, arbitrary constraint families, arbitrary complex spatial loads, and frequency-dependent operator families are not currently supported.

Use dynamics.spectrum(...), frequency_response(...), and damping_from_free_decay(...) on arrays or scalar result histories. constitutive.GeneralizedMaxwell remains the scalar material-point and spectral asset. For global solid equilibrium, register constitutive.IsotropicGeneralizedMaxwell, select studies.viscoelastic_solid(dimension=3), and call model.step(target=u, duration=..., steps=..., amplitude=...). Use time_points=(0.0, ..., duration) instead of steps when known loading events or separated relaxation scales require a nonuniform grid. For an automatically resolved path, pass incrementation=steps.automatic(...) and optionally time_error_tolerance=...; do not combine these controls. The error-controlled route accepts the two-half-step endpoint, rolls back all nodal and quadrature state after a rejected attempt, and records accepted/rejected increments. The Step owns committed quadrature history, exact branch updates, standard viscoelastic fields, energy evidence, and the declared portable serial/MPI restart route. The generalized-Maxwell material-specific harmonic provider is separate and remains experimental; do not infer its maturity from the engineering generic harmonic procedure or the elastic NAFEMS Test 5H comparison. Neither route implies finite strain or physical aging.

Learn from existing FEniCSx cases

Preserve the PDE, discretization, boundaries and solver when migrating working code. Use operators.from_ufl to adopt integrated weak forms, then substitute named operators when their semantics match. Do not invent a new wrapper for every case or force unsupported physics into the closest named operator. Use (M / dt).expression or (1.0 / dt) * M.expression; UFL Forms and UFL integrands have different arithmetic. A history/load form has one argument, not a matrix role; use from_ufl(..., role="vector") when needed. For a nonlinear residual use role="residual", followed by operators.linearize. Use mesh.from_arrays(cells=..., coordinates=..., coordinate_element=..., comm=...) instead of guessing positional DOLFINx mesh arguments.

For constant, coordinate-dependent or explicitly updated time-dependent fields, use expressions.interpolate(target, source, parameters=...); a bare constant need not be wrapped in a DOLFINx Expression. Vector/tensor inputs accept lists or NumPy arrays matching the target value shape. Interpolation is an assignment, not a persistent time binding. See docs/guide/field_expression_inputs.md.

For high-order mixed-field boundary data, use spaces.independent_subspace(W, i) and paired parent/independent dof location. Transfer output by interpolation; never copy coefficient arrays between different numberings. See docs/guide/high_order_mixed_fields.md.

For serial degree-one imported nodes, spaces.locate_input_nodes uses mesh input identity and backend entity layouts; it is not a coordinate or source-label lookup. Use interfaces.reference_trace_from_boundary(..., geometry="bilinear") for explicitly supported warped Q1 boundary traces. Do not hand-write face connectivity or collapse coincident nodes at a bonded interface.

For the bounded response-Hex route, use accepted_material_fields() for named state arrays and accepted_contact_evidence(name=...) for accepted pair evidence. Keep source-law SDV names in the provider, not the core. Observe only accepted stations; custom history callbacks are pure observations, never material updates. Their absence must not change the mechanical solution. Mass-preparation diagnostics describe supplied reference bounds, not a whole-path stability proof. Keep worker and reporting budgets separate from scientific controls.