Multifield API
Weak-form DSL and multifield assembly interface.
Operator size and component ordering
This section summarizes the output size and component ordering of the basic differential operators used by the weak-form DSL.
All operator vectors are written in the order used internally by LowLevelFEM.
Scalar fields
Let
\[p = p(x,y,z)\]
be a scalar field.
| Dimension | Operator | Size | Component ordering |
|---|---|---|---|
| 1D | Grad(P) | 1 | [p,x] |
| 2D | Grad(P) | 2 | [p,x, p,y] |
| 3D | Grad(P) | 3 | [p,x, p,y, p,z] |
| 1D/2D/3D | Id(P) | 1 | [p] |
Div(P), Curl(P) and SymGrad(P) are not defined for scalar fields.
Vector fields
Let
\[u = \begin{bmatrix} u_x \\ u_y \\ u_z \end{bmatrix}\]
be a vector field. In 2D, only ux and uy are present.
Grad(Pu) for vector fields
Grad(Pu) returns the full displacement gradient in component-major ordering.
2D vector field
\[u = \begin{bmatrix} u_x \\ u_y \end{bmatrix}\]
| Operator | Size | Component ordering |
|---|---|---|
Grad(Pu) | 4 | [ux,x, ux,y, uy,x, uy,y] |
Equivalent matrix form:
\[\nabla u = \begin{bmatrix} u_{x,x} & u_{x,y} \\ u_{y,x} & u_{y,y} \end{bmatrix}\]
Flattened as:
\[[u_{x,x}, u_{x,y}, u_{y,x}, u_{y,y}]\]
3D vector field
| Operator | Size | Component ordering |
|---|---|---|
Grad(Pu) | 9 | [ux,x, ux,y, ux,z, uy,x, uy,y, uy,z, uz,x, uz,y, uz,z] |
Equivalent matrix form:
\[\nabla u = \begin{bmatrix} u_{x,x} & u_{x,y} & u_{x,z} \\ u_{y,x} & u_{y,y} & u_{y,z} \\ u_{z,x} & u_{z,y} & u_{z,z} \end{bmatrix}\]
Flattened as:
\[[u_{x,x}, u_{x,y}, u_{x,z}, u_{y,x}, u_{y,y}, u_{y,z}, u_{z,x}, u_{z,y}, u_{z,z}]\]
SymGrad(Pu) for vector fields
SymGrad(Pu) returns the engineering strain vector.
2D vector field
| Operator | Size | Component ordering |
|---|---|---|
SymGrad(Pu) | 3 | [εxx, εyy, γxy] |
Explicitly:
\[\operatorname{SymGrad}(u) = \begin{bmatrix} u_{x,x} \\ u_{y,y} \\ u_{x,y} + u_{y,x} \end{bmatrix}\]
3D vector field
| Operator | Size | Component ordering |
|---|---|---|
SymGrad(Pu) | 6 | [εxx, εyy, εzz, γxy, γyz, γzx] |
Explicitly:
\[\operatorname{SymGrad}(u) = \begin{bmatrix} u_{x,x} \\ u_{y,y} \\ u_{z,z} \\ u_{x,y} + u_{y,x} \\ u_{y,z} + u_{z,y} \\ u_{z,x} + u_{x,z} \end{bmatrix}\]
The shear components are engineering shear strains.
Div(Pu) for vector fields
| Dimension | Operator | Size | Component ordering |
|---|---|---|---|
| 2D | Div(Pu) | 1 | [ux,x + uy,y] |
| 3D | Div(Pu) | 1 | [ux,x + uy,y + uz,z] |
Curl(Pu) for vector fields
2D vector field
| Operator | Size | Component ordering |
|---|---|---|
Curl(Pu) | 1 | [uy,x - ux,y] |
3D vector field
| Operator | Size | Component ordering |
|---|---|---|
Curl(Pu) | 3 | [uz,y - uy,z, ux,z - uz,x, uy,x - ux,y] |
That is:
\[\nabla \times u = \begin{bmatrix} u_{z,y} - u_{y,z} \\ u_{x,z} - u_{z,x} \\ u_{y,x} - u_{x,y} \end{bmatrix}\]
Tensor fields
A second-order tensor field is stored as a full 3×3 tensor, even in many 2D workflows.
The internal tensor component ordering is column-major:
\[T = \begin{bmatrix} T_{11} & T_{12} & T_{13} \\ T_{21} & T_{22} & T_{23} \\ T_{31} & T_{32} & T_{33} \end{bmatrix}\]
stored as:
\[[T_{11}, T_{21}, T_{31}, T_{12}, T_{22}, T_{32}, T_{13}, T_{23}, T_{33}]\]
| Stored index | Tensor component |
|---|---|
1 | T11 |
2 | T21 |
3 | T31 |
4 | T12 |
5 | T22 |
6 | T32 |
7 | T13 |
8 | T23 |
9 | T33 |
TensorDiv(P) for tensor fields
Let T be a second-order tensor field.
| Dimension | Operator | Size | Component ordering |
|---|---|---|---|
| 2D | TensorDiv(P) | 2 | [T11,x + T12,y, T21,x + T22,y] |
| 3D | TensorDiv(P) | 3 | [T11,x + T12,y + T13,z, T21,x + T22,y + T23,z, T31,x + T32,y + T33,z] |
In index notation:
\[(\operatorname{div} T)_i = \frac{\partial T_{ij}}{\partial x_j}\]
Voigt convention
LowLevelFEM uses the following 3D Voigt ordering for symmetric second-order tensors:
\[[xx, yy, zz, xy, yz, zx]\]
That is:
| Voigt index | Component |
|---|---|
1 | xx |
2 | yy |
3 | zz |
4 | xy |
5 | yz |
6 | zx |
For stress-like tensors:
\[[S_{xx}, S_{yy}, S_{zz}, S_{xy}, S_{yz}, S_{zx}]\]
For engineering strain-like vectors:
\[[\varepsilon_{xx}, \varepsilon_{yy}, \varepsilon_{zz}, \gamma_{xy}, \gamma_{yz}, \gamma_{zx}]\]
where
\[\gamma_{xy} = 2\varepsilon_{xy}\]
and similarly for the other shear components.
Useful nonlinear 2D mapping
For large-displacement 2D formulations embedded in 3D tensor notation, Grad(Pu) has four components:
\[[u_{x,x}, u_{x,y}, u_{y,x}, u_{y,y}]\]
The variation of the Green-Lagrange strain can be written as
\[\delta E_\mathrm{voigt} = A(F) \, \nabla \delta u\]
with the 3D Voigt ordering
\[[xx, yy, zz, xy, yz, zx]\]
and
\[F = \begin{bmatrix} F_{11} & F_{12} & 0 \\ F_{21} & F_{22} & 0 \\ 0 & 0 & F_{33} \end{bmatrix}.\]
Then
\[A(F) = \begin{bmatrix} F_{11} & 0 & F_{21} & 0 \\ 0 & F_{12} & 0 & F_{22} \\ 0 & 0 & 0 & 0 \\ F_{12} & F_{11} & F_{22} & F_{21} \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix}.\]
Thus the material tangent contribution can be assembled as:
Kmat = ∫(Grad(Pu) ⋅ A' ⋅ D ⋅ A ⋅ Grad(Pu); Ω="body")with dimensions:
| Quantity | Size |
|---|---|
Grad(Pu) | 4 |
A | 6×4 |
D | 6×6 |
A' | 4×6 |
2D restriction matrix
For extracting the in-plane components [xx, yy, xy] from the 3D Voigt vector [xx, yy, zz, xy, yz, zx], use:
\[R = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 1 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix}.\]
Then:
\[D_{2D} = R^T D R\]
and
\[S_{2D} = R^T S_\mathrm{voigt}.\]
The corresponding 2D material tangent contribution is:
A2 = R' * A
D2 = R' * D * R
Kmat = ∫(Grad(Pu) ⋅ A2' ⋅ D2 ⋅ A2 ⋅ Grad(Pu); Ω="body")Embedded surface operators
SurfaceGrad(P) for scalar fields
Let
\[p = p(x,y,z)\]
be a scalar field defined on a surface embedded in 3D.
SurfaceGrad(P) returns the tangential surface gradient.
Embedded surface in 3D
| Operator | Size | Component ordering |
|---|---|---|
SurfaceGrad(P) | 2 | [p,1, p,2] |
where:
1and2denote the local tangent directions(t₁,t₂)evaluated at the Gauss point.
Mathematically:
\[\nabla_\Gamma p = \begin{bmatrix} \partial p/\partial s_1 \\ \partial p/\partial s_2 \end{bmatrix}\]
SurfaceGrad(Pu) for vector fields
For a 3D vector field defined on a surface:
| Operator | Size | Component ordering |
|---|---|---|
SurfaceGrad(Pu) | 6 | [ux,1, ux,2, uy,1, uy,2, uz,1, uz,2] |
Equivalent matrix form:
\[\nabla_\Gamma u = \begin{bmatrix} u_{x,1} & u_{x,2} \\ u_{y,1} & u_{y,2} \\ u_{z,1} & u_{z,2} \end{bmatrix}\]
SurfaceSymGrad(Pu) for vector fields
SurfaceSymGrad(Pu) returns the membrane strain vector in the local tangent coordinate system.
| Operator | Size | Component ordering |
|---|---|---|
SurfaceSymGrad(Pu) | 3 | [ε11, ε22, γ12] |
Explicitly:
\[\operatorname{SurfaceSymGrad}(u) = \begin{bmatrix} u_{1,1} \\ u_{2,2} \\ u_{1,2} + u_{2,1} \end{bmatrix}\]
where:
1and2are the local tangent directions.
The operator returns membrane strains only. No bending terms are included.
SurfaceDiv(Pu) for vector fields
| Operator | Size | Component ordering |
|---|---|---|
SurfaceDiv(Pu) | 1 | [ux,1 + uy,2] |
or equivalently:
\[\nabla_\Gamma \cdot u\]
Directional / axial operators
AxialGrad
Directional gradient operator along a prescribed axial direction.
This operator computes derivatives projected onto a specified axis.
Mathematically:
\[\nabla_a u = a \cdot \nabla u\]
where:
- (a) is the prescribed axial direction.
Unlike SurfaceGrad, the operator does not derive its directions from the local surface geometry.
Typical applications:
- beam/spar-like formulations,
- fiber-reinforced materials,
- directional constitutive laws,
- anisotropic transport,
- projected strain operators.
AxialGrad(P) for scalar fields
| Operator | Size | Component ordering |
|---|---|---|
AxialGrad(P) | 1 | [p,a] |
Equivalent form:
\[\frac{\partial p}{\partial a}\]
AxialGrad(Pu) for vector fields
| Operator | Size | Component ordering |
|---|---|---|
AxialGrad(Pu) | 3 | [ux,a, uy,a, uz,a] |
Equivalent form:
\[\frac{\partial u}{\partial a}\]
TangentialGrad(P) for scalar fields
Tangential derivative along a 1D embedded manifold.
| Operator | Size | Component ordering |
|---|---|---|
TangentialGrad(P) | 1 | [p,s] |
where:
sdenotes the local tangent direction.
Mathematically:
\[\nabla_t p = \frac{\partial p}{\partial s}\]
TangentialGrad(Pu) for vector fields
| Operator | Size | Component ordering |
|---|---|---|
TangentialGrad(Pu) | 3 | [ux,s, uy,s, uz,s] |
Equivalent form:
\[\frac{\partial u}{\partial s}\]
Matrix assembly
Bilinear forms use direct compressed sparse column (CSC) assembly by default:
K = ∫(SymGrad(Pu) ⋅ D ⋅ SymGrad(Pu); Ω="body")The structural sparsity pattern is built from the Gmsh element connectivity before numerical integration. Element matrices are then accumulated directly into the nzval arrays associated with that pattern. This avoids storing the full element-level I, J, and V triplet arrays and substantially reduces the memory required for large problems.
The legacy triplet-based assembly remains available explicitly:
K = ∫(SymGrad(Pu) ⋅ D ⋅ SymGrad(Pu);
Ω="body",
assembly=:ijv)The assembly keyword affects bilinear forms only. Linear forms use their own parallel vector-assembly path.
Parallel CSC assembly
By default, assembly uses all Julia threads available to the process:
K = ∫(Grad(P) ⋅ Grad(P); threads=:auto)The number of workers can also be selected explicitly:
K1 = ∫(Grad(P) ⋅ Grad(P); threads=1)
K4 = ∫(Grad(P) ⋅ Grad(P); threads=4)With one worker, element contributions are written directly to the final nzval array, and no reduction is required. With multiple workers, the first worker uses the final array and every additional worker uses a private value buffer with the same structural pattern. These buffers are reduced into the final array after assembly, avoiding data races.
element_chunk_size controls the scheduling block size for direct CSC assembly. The default :auto setting adapts the block size to the element and worker counts and uses at most 4096 elements per block:
K = ∫(Grad(P) ⋅ Grad(P); element_chunk_size=2048)Normally, the automatic setting should be preferred.
Reusing a CSC pattern
For repeated assembly on the same mesh and domain with compatible trial and test spaces, the structural pattern can be built once and reused:
Kpattern = build_csc_pattern(Pu, Pu; Ω="body")
fill!(Kpattern.nzval, 0.0)
K1 = ∫(SymGrad(Pu) ⋅ D1 ⋅ SymGrad(Pu);
Ω="body",
csc_matrix=Kpattern)
# Copy the numerical result if it must remain available after Kpattern is reused.
A1 = copy(K1.A)
fill!(Kpattern.nzval, 0.0)
K2 = ∫(SymGrad(Pu) ⋅ D2 ⋅ SymGrad(Pu);
Ω="body",
csc_matrix=Kpattern)Assembly adds contributions to the existing nzval entries. Therefore, fill!(Kpattern.nzval, 0.0) is required before every new independent assembly. Do not call dropzeros!(Kpattern): the explicitly stored zero entries belong to the reusable structural pattern and are required by direct CSC scatter.
The returned SystemMatrix wraps the supplied sparse matrix. Reusing and clearing that pattern therefore also changes earlier results that still refer to it; copy the assembled matrix first if both numerical results are needed.
A reusable pattern is valid only for the same mesh, selected physical domain, and structurally compatible trial/test spaces. Equal matrix dimensions alone do not guarantee compatibility.
When no pattern is supplied, it is created once within the integral, filled, and numerical zeros are removed from the returned matrix.
Compound operators and coefficient chains
For a compound bilinear form such as
B = A1 ⋅ Grad(Pu) + A2 ⋅ Pu
K = ∫(B' ⋅ D ⋅ B; Ω="body")the expression is expanded into ordinary bilinear terms. With direct CSC assembly, all expanded terms share:
- one structural pattern;
- the final
nzvalarray; - the worker-local value buffers;
- one dense nodal-coordinate array.
The worker-local buffers are cleared between expanded terms, while the final array is retained so that the terms accumulate into the same matrix. The pattern is therefore not rebuilt for every term.
Direct CSC assembly uses the same element kernel and Gauss-point coefficient evaluation as the triplet path. It supports scalar coefficients, ordinary matrices, matrix chains, and matrices whose entries are Number or ScalarField values, including mixed matrices containing both types:
K = ∫(Grad(Pu) ⋅ A ⋅ B ⋅ C ⋅ Grad(Pu); Ω="body")Only the global scatter operation differs between the CSC and IJV paths.
Element types and interpolation order
The CSC pattern builder does not assume a specific element shape or polynomial order. It uses the connectivity and number of element nodes returned by Gmsh, then expands every connected node pair to the components of the trial and test fields.
Consequently, direct CSC assembly supports every Gmsh Lagrange element type and interpolation order that is otherwise supported by the selected LowLevelFEM operator and element kernel. CSC assembly does not add support for an element or operator that is not implemented elsewhere in LowLevelFEM, but it does not introduce an additional element-type restriction.
Weak-Form DSL Operators
LowLevelFEM.Grad — FunctionGrad(P)Create a weak-form DSL gradient operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for∇.
Example
K = ∫(Grad(Pu) ⋅ Grad(Pu); Ω="solid")LowLevelFEM.Div — FunctionDiv(P)Create a weak-form DSL divergence operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for∇⋅.
Example
A = ∫(Div(Pu) ⋅ Div(Pu); Ω="domain")LowLevelFEM.Curl — FunctionCurl(P)Create a weak-form DSL curl operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for curl.
Example
A = ∫(Curl(Pu) ⋅ Curl(Pu); Ω="domain")LowLevelFEM.SymGrad — FunctionSymGrad(P)Create a weak-form DSL symmetric-gradient operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object forε(u).
Example
K = ∫(SymGrad(Pu) ⋅ C ⋅ SymGrad(Pu); Ω="solid")LowLevelFEM.ε — FunctionSymGrad(P)Create a weak-form DSL symmetric-gradient operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object forε(u).
Example
K = ∫(SymGrad(Pu) ⋅ C ⋅ SymGrad(Pu); Ω="solid")LowLevelFEM.Id — FunctionId(P)Create a weak-form DSL identity operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for identity mapping.
Example
M = ∫(Id(Pu) ⋅ Id(Pu); Ω="solid")LowLevelFEM.TensorDiv — FunctionTensorDiv(P)Create a weak-form DSL tensor-divergence operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for tensor divergence.
Example
A = ∫(TensorDiv(Pσ) ⋅ TensorDiv(Pσ); Ω="solid")LowLevelFEM.Adv — FunctionAdv(P)Create a weak-form DSL advection operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object for advection terms.
Example
A = ∫(Adv(Pu) ⋅ Id(Pu); Ω="domain")LowLevelFEM.AxialGrad — FunctionAxialGrad(P)Create a weak-form DSL axial gradient operator applied to P.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object representingt ⋅ ∇u.
Directional derivative operator along a prescribed axial direction.
Computes derivatives projected onto a predefined axis.
Unlike TangentialGrad, the axial direction is prescribed explicitly and does not depend on the local element geometry.
Typical applications include:
- anisotropic constitutive laws,
- fiber-reinforced materials,
- projected strain operators,
- directional transport problems.
See also: TangentialGrad
Example
```julia K = ∫(AxialGrad(Pu) ⋅ (E*A) ⋅ AxialGrad(Pu); Ω="truss")
LowLevelFEM.TangentialGrad — FunctionTangentialGrad(P)Create a weak-form DSL tangential gradient operator applied to P.
Description
The tangential gradient operator computes the projection of the gradient of a vector field onto the element axis:
TangentialGrad(u) = (∇u) ⋅ twhere t is the tangent vector of the element in the physical domain.
For vector fields u ∈ ℝᵈ, the result is a vector field of dimension d.
Arguments
P: Field descriptor (Problem) used in the weak form.
Returns
OpApplied: Operator application object representing(∇u) ⋅ t.
Directional derivative operator along the local element tangent direction.
Computes derivatives projected onto the local tangent vector of the element.
Typical applications include:
- beam and rod formulations,
- directional gradients,
- streamline-like operators,
- embedded directional mechanics.
See also: AxialGrad
Example
Kg = ∫(TangentialGrad(Pu) ⋅ N0 ⋅ TangentialGrad(Pu); Ω="truss")Notes
Unlike
AxialGrad, which produces a scalar strain measuret ⋅ ∇u ⋅ t,TangentialGradreturns a vector quantity.This operator is useful for constructing geometric stiffness matrices (initial stress effects) in truss and structural stability problems.
LowLevelFEM.SurfaceGrad — FunctionSurfaceGrad(P)Surface gradient operator for fields defined on embedded surfaces.
Computes tangential derivatives on a 2D manifold embedded in 3D using local tangent bases evaluated at Gauss points.
For scalar fields, returns the tangential surface gradient. For vector fields, returns the full tangential displacement gradient.
The operator is orientation independent and invariant with respect to the global coordinate system.
Typical applications include:
- membrane mechanics,
- surface PDEs,
- Laplace–Beltrami operators,
- surface diffusion.
See also: SurfaceDiv, SurfaceSymGrad
LowLevelFEM.SurfaceDiv — FunctionSurfaceDiv(P)Surface divergence operator.
Computes the tangential divergence of vector fields defined on embedded surfaces.
The operator uses local tangent bases evaluated at Gauss points.
See also: SurfaceGrad, SurfaceSymGrad
LowLevelFEM.SurfaceSymGrad — FunctionSurfaceSymGrad(P)Symmetric tangential gradient operator for membrane mechanics.
Computes the membrane strain vector on a surface embedded in 3D.
The operator evaluates strains in a local tangent coordinate system constructed at each Gauss point.
For vector fields, the operator returns:
[ε11, ε22, γ12]where 1 and 2 denote the local tangent directions.
Only in-surface membrane strains are included. Bending strains are not part of this operator.
Typical usage:
K = ∫(SurfaceSymGrad(Pu) ⋅ C ⋅ SurfaceSymGrad(Pu))See also: SurfaceGrad, SurfaceDiv
Weak-Form Integration
LowLevelFEM.∫ — Function∫(expr::WeakExpr; Ω=nothing, Γ=nothing, weight=nothing, gauss=:full,
threads=:auto, assembly=:csc)Assemble a finite element operator from a weak-form expression.
Examples
Diffusion
K = ∫( Grad(Pu) ⋅ Grad(Pu); Ω="domain")Elasticity
K = ∫( SymGrad(Pu) ⋅ C ⋅ SymGrad(Pu); Ω="solid")Tensor chain
K = ∫( Grad(Pu) ⋅ F' ⋅ S ⋅ F ⋅ Grad(Pu); Ω="solid")Elastic support
K = ∫( Id(Pu) ⋅ [kx 0; 0 ky] ⋅ Id(Pu); Γ="boundary")or
K = ∫( Pu ⋅ [kx 0; 0 ky] ⋅ Pu; Γ="boundary")Mixed formulation
A = ∫( Div(Pu) ⋅ Pp )
B = ∫( Pp ⋅ Div(Pu) )Linear form
f = ∫(Pu ⋅ g)With operator chain
f = ∫(Pu ⋅ A ⋅ g)With coefficient
f = ∫(PT ⋅ PT * h, Γ="right")The bilinear- and linear-form assembly uses all available Julia threads by default. Set threads to a positive integer to control the worker count.
Serial assembly can be selected for individual integrals with
threads=1For example
K = ∫(SymGrad(Pu) ⋅ C ⋅ SymGrad(Pu);
Ω="solid",
threads=1)Direct CSC matrix assembly is the default. The legacy IJV path remains available explicitly with assembly=:ijv.
Arguments
expr
Weak-form expression composed of operators and coefficients.
gauss
:full or :reduced integration, or Int that is a signed number: the increment of Gauss points relative to :full.
Keyword arguments
Ω
Volume physical group name.
Γ
Boundary physical group name.
Returns
SystemMatrix or ScalarField, VectorField, TensorField
∫(t::BilinearTerm; Ω=nothing, Γ=nothing, weight=nothing,
gauss=:full, threads=:auto, assembly=:csc,
csc_matrix=nothing, element_chunk_size=:auto, updateFrom=nothing)Assemble one bilinear term.
Direct CSC assembly is the default. If csc_matrix is omitted, the structural pattern is built once, filled, and finalized by removing numerical zeros. If a prebuilt SparseMatrixCSC{Float64,Int} is supplied, its complete pattern is preserved so that it can be reused. Assembly adds to its current nzval contents; reset them with fill!(csc_matrix.nzval, 0.0) before starting an independent assembly.
Use assembly=:ijv for the legacy triplet-based path. csc_matrix and element_chunk_size apply only to direct CSC assembly.
updateFrom is forwarded only when supplied. It is intended for specialized operators such as ContactGap, which can update their geometry from the given field immediately before assembly.
∫(a::OpApplied, b::OpApplied; kwargs...)Assemble the bilinear form defined by two applied operators with an identity coefficient. The CSC-related keywords and pattern-reuse rules are identical to those of ∫(::BilinearTerm).
∫(expr::CompoundBilinear; Ω=nothing, Γ=nothing, weight=nothing,
gauss=:auto, threads=:auto, assembly=:csc,
csc_matrix=nothing, element_chunk_size=:auto)Expand and assemble a compound bilinear operator expression.
Single-block compound forms
If all test-side terms belong to one Problem and all trial-side terms belong to one Problem, the original compound assembly path is used. For assembly=:csc, one structural pattern is built for the block and reused for all expanded left/right term pairs, together with the final nzval array, worker-local value buffers, and dense node-coordinate array.
A supplied csc_matrix is supported for this single-block case. Its complete structural pattern is preserved; reset csc_matrix.nzval before a new independent assembly, but not between internally expanded terms.
Multifield compound forms
If either side contains terms associated with multiple Problems, terms are first grouped by Problem in order of first occurrence. The test-side groups define block rows and the trial-side groups define block columns. Each block is then assembled independently from the Cartesian product of the terms belonging to that test/trial pair, and the completed blocks are combined with SystemMatrix(blocks).
For a square multifield system, test and trial fields must appear in the same order. The user is responsible for writing the compound expression with a consistent field order. For example,
B = Au ⋅ Grad(Pu) + Aφ ⋅ Grad(Pφ) + Gφ ⋅ Id(Pφ)
K = ∫(B' ⋅ D ⋅ B; Γ="beam")is grouped as [Pu, Pφ] on both sides and automatically produces the four blocks Kuu, Kuφ, Kφu, and Kφφ.
With direct CSC assembly, every multifield block builds its own structural pattern because blocks may have different dimensions. Compound terms inside a single block still reuse that block's pattern and worker buffers exactly as in the original homogeneous compound path. Consequently, csc_matrix is not accepted for a multifield compound form; external pattern reuse remains a single-block option.
Matrix chains and matrices containing Number and ScalarField entries use the same coefficient evaluation and element kernel as ordinary bilinear terms. Each compound term may select full(...) or reduced(...) quadrature; with gauss=:auto, mixed-rule products use full integration.
Use assembly=:ijv to select the legacy triplet path. Multifield IJV assembly is likewise performed independently block by block.
LowLevelFEM.∫Ω — Function∫Ω(name, expr)Convenience wrapper for volume integration on physical group name.
Arguments
name: Gmsh physical group name used as domainΩ.expr::WeakExpr: Weak-form expression to assemble.
Returns
SystemMatrix: Assembled matrix over the selected volume.
Example
K = ∫Ω("solid", Grad(Pu) ⋅ Grad(Pu))LowLevelFEM.∫Γ — Function∫Γ(name, expr)Convenience wrapper for boundary integration on physical group name.
Arguments
name: Gmsh physical group name used as boundaryΓ.expr::WeakExpr: Weak-form expression to assemble.
Returns
SystemMatrix: Assembled matrix over the selected boundary.
Example
KΓ = ∫Γ("loaded_boundary", Id(Pu) ⋅ Id(Pu))LowLevelFEM.build_csc_pattern — Functionbuild_csc_pattern(Pu::Problem, Ps::Problem; domain=nothing, Ω=nothing, Γ=nothing)Build the exact structural CSC pattern required by the selected mesh domain.
The pattern is constructed from node connectivity without allocating full degree-of-freedom triplet arrays. The returned matrix has zero-valued nzval entries and sorted row indices in every column.
The construction is independent of element shape and interpolation order: it uses the connectivity returned by Gmsh and expands every connected node pair to the test/trial field components. Consequently, it supports all Gmsh Lagrange element types that are otherwise supported by the selected LowLevelFEM operators and element kernel.
The returned pattern can be supplied as csc_matrix to a bilinear or compound integral. Serial assembly writes directly to its nzval array. Parallel assembly uses the same array as the first worker's accumulator and allocates one additional private nzval buffer per remaining worker, followed by an allocation-free reduction.
Before reusing the pattern for a new independent assembly, reset its numerical values with fill!(K.nzval, 0.0). Do not call dropzeros!(K), because that would remove structural entries required by subsequent direct CSC scatter.
The caller is responsible for reusing a pattern only with the same mesh, domain, and compatible test/trial spaces. Matrix dimensions alone do not prove structural compatibility.
Constitutive matrices
LowLevelFEM.ConstitutiveMatrix — FunctionConstitutiveMatrix(type::Symbol)
ConstitutiveMatrix(type::Symbol, mat::Material)
ConstitutiveMatrix(type::Symbol, E, ν)Return the isotropic elastic constitutive matrix for the selected model.
Supported models:
:PlaneStress:PlaneStrain:Axisymmetric:Solid
Engineering shear strain convention is used.
Alias
D is a shorthand alias for ConstitutiveMatrix and accepts the same arguments.
D(:Solid, mat)
D(:PlaneStress, E, ν)LowLevelFEM.D — FunctionD(args...)Shorthand alias for ConstitutiveMatrix.
Accepts the same arguments and returns the same constitutive matrix.
Examples
D(:Solid, mat)
D(:PlaneStress, 210e3, 0.3)Multifield Solver
LowLevelFEM.solveField — FunctionsolveField(K, rhs;
support=BoundaryCondition[],
mpc=MPC[],
coordSys=NodalCoordinateSystem[],
solver=:auto,
preconditioner=nothing,
reltol=sqrt(eps()),
maxiter=...)Solve a linear single-field finite-element system.
The solution pipeline is
prepare -> solve_linear_system -> reconstructand uses the transformation
\[u_\mathrm{global} = Q T u_\mathrm{reduced},\]
where Q is the nodal coordinate-basis transformation and T contains kinematic transformations such as reduced-order interpolation and MPCs.
Right-hand side
rhs may be a ScalarField, VectorField, TensorField, Global(...) wrapper, or a symbolic sum of local and global load terms. Multiple right-hand sides are supported columnwise.
With coordSys present, ordinary loads and prescribed component values are interpreted in the local solver basis. Global(...) marks right-hand-side data that are supplied in global Cartesian components.
Coordinate systems and MPCs
MPCs identify local solver components. Therefore master and slave nodes may deliberately use different local bases. In global coordinates the relation is
\[u_{g,s} = Q_s Q_m^T u_{g,m},\]
which can represent rotated periodic or sector-symmetry constraints.
CoordinateSystem + reducedOrder=true on the same field is not yet implemented.
Linear solvers
solver may be :auto, :backslash, :lu, :lu_no_ordering, :cholesky, :qr, :cg, or :gmres.
For an ordinary SystemMatrix, :auto uses Julia's backslash solver. For a SymmetricSystemMatrix, :auto first attempts Cholesky and falls back to LU when the matrix is symmetric but not positive definite.
The legacy keywords iterative and ordering are still accepted for backward compatibility. iterative=true selects conjugate gradient; ordering=false selects LU without column reordering. New code should prefer solver.
solveField(K, F::SystemVector; kwargs...)Solve a linear multifield finite-element system.
Full-order and reduced-order fields may be mixed in the same block system. Each field may have its own MPCs and local coordinate systems. A coordinate system may coexist with reducedOrder=true on another field, but CoordinateSystem + reducedOrder=true on the same field is not yet implemented.
For a multifield system,
\[x_\mathrm{global} = Q T x_\mathrm{reduced},\]
with block-diagonal field-wise coordinate transformation Q. MPC relations are imposed on local solver components, so deliberately different master and slave bases naturally produce rotated periodic relations.
F may be a SystemVector, Global(F), or a symbolic sum containing local and global SystemVector contributions. Multiple right-hand sides are supported.
The solver keywords and backward-compatibility behavior are the same as for the single-field method.
LowLevelFEM.solveEigenFields — FunctionsolveEigenFields(
K::SystemMatrix,
M::SystemMatrix;
n=6,
fmin=0.0,
support=Vector{BoundaryCondition}(),
mpc::Vector{MPC}=MPC[],
directSolver=false
)Compute natural frequencies and field-wise mode shapes of a multifield structural system.
The generalized eigenproblem is solved in the constrained and possibly reduced space, then the eigenvectors are reconstructed in the original full multifield DOF space.
Returns one Eigen object for each field.
LowLevelFEM.reductionMatrices — FunctionreductionMatrices(P::Problem) -> T, RConstruct sparse transformation matrices for reducing the polynomial order of a C0 Lagrange finite element field from order p to p - 1.
The returned matrices satisfy
u_full = T * u_reduced
u_reduced = R * u_full
K_reduced = T' * K * Tfor fields representable in the reduced space.
The transformation is constructed from the Gmsh Lagrange basis functions. All element types belonging to the problem must have the same polynomial order. An error is thrown for first-order meshes.
The matrices are expanded automatically according to P.pdim.
Example
T, R = reductionMatrices(problem)
Multi-point constraints
LowLevelFEM.MultiPointConstraint — TypeMultiPointConstraint(; master::String, slave::String,
field=nothing, problem=field, kwargs...)
MPC(; master::String, slave::String,
field=nothing, problem=field, kwargs...)Define a multi-point constraint between master and slave physical groups.
MPC is a shorthand alias for MultiPointConstraint.
The constraint identifies corresponding degrees of freedom on the master and slave physical groups. The constrained components can be selected individually using Boolean keyword arguments.
If no component keyword is specified, all components of the associated field are constrained.
Arguments
master::String: Name of the Gmsh physical group containing the master node(s).slave::String: Name of the Gmsh physical group containing the slave node(s).
Keyword Arguments
field: Field (Problem) to which the constraint belongs. This is required when MPCs are used in multifield systems.problem: Backward-compatible alias forfield.kwargs...: Boolean component selectors. Component names follow the field name and its components, for exampleux,uy,uz,φ,φx,φy,φz.A value of
trueconstrains the corresponding component, whilefalseleaves it unconstrained.
Behavior
Two types of coupling are supported.
Single-master coupling
If the master physical group contains a single node, all selected slave degrees of freedom are tied to that master node. This can be used for remote-point-type constraints.
Periodic coupling
If the master and slave physical groups contain multiple nodes, the correspondence is obtained from the periodic node mapping stored in the Gmsh model.
Periodic pairing must therefore be defined in Gmsh before constructing the constraint.
Examples
Tie all displacement components of a boundary to a remote point:
mpc = MPC(
master="remote",
slave="right",
field=U
)Constrain only selected components:
mpc = MPC(
master="remote",
slave="right",
field=U,
ux=true,
uy=true,
uz=false
)Periodic displacement coupling:
periodic = MPC(
master="right",
slave="left",
field=U
)
u = solveField(
K,
f;
support=[bc],
mpc=[periodic]
)Notes
MPC relations may be chained. During solution, chained constraints are resolved so that each constrained degree of freedom points directly to its final master degree of freedom.
LowLevelFEM.rigidRotationMap — FunctionrigidRotationMap(mpc_u::MPC, mpc_φ::MPC)Construct the rigid-body rotational kinematic map associated with a remote point constraint.
The function creates the displacement contribution generated by rotations about a single master node. The displacement and rotation MPCs must refer to the same master and slave physical groups.
The returned matrix represents the relation
\[u_{rot} = R \varphi,\]
where R is the rigid-rotation map.
The full displacement field of the slave region can therefore be written as
Arguments
mpc_u::MPC: MPC associated with the translational displacement field.mpc_φ::MPC: MPC associated with the rotational field.
Both MPCs must specify their fields and must use identical master and slave physical groups.
Returns
SystemMatrix: Kinematic transformation matrix mapping the rotation field to the displacement field.The trial field is the rotation field and the test field is the displacement field.
Supported Configurations
2D: displacement
VectorFieldwith two components and scalar rotation field.3D: displacement
VectorFieldwith three components and rotationVectorFieldwith three components.
The master physical group must contain exactly one node.
Example
mpc_u = MPC(master="remote", slave="right", field=U)
mpc_φ = MPC(master="remote", slave="right", field=Φ)
R = rigidRotationMap(mpc_u, mpc_φ)
Kuφ = Ku * R
Kφ = R' * Ku * R
K = SystemMatrix([
Ku Kuφ
Kuφ' Kφ
])
u, φ = solveField(K, F; support=[bc], mpc=[mpc_u, mpc_φ])
u_phys = u + R * φNotes
The component selections stored in the MPCs are respected when constructing the map.
LowLevelFEM.collapseMPC — FunctioncollapseMPC(field, mpc)Collapse generalized nodal quantities from MPC slave degrees of freedom onto their corresponding master degrees of freedom.
The operation applies the transpose of the MPC kinematic transformation to the supplied nodal field. It is primarily intended for generalized forces, reactions and moments associated with constrained degrees of freedom.
For an MPC transformation
\[q = T q_r,\]
the corresponding generalized force transformation is
collapseMPC performs this force-side reduction and returns the result in the original field representation, with the collapsed values assigned to the master degrees of freedom.
Arguments
field::Union{ScalarField,VectorField,TensorField}: Nodal field containing generalized forces or other quantities to be collapsed.mpc::MPC: Multi-point constraint defining the master-slave relation.
Returns
A field of the same type as field, containing the collapsed generalized quantities.
Example
For a rotational field in a coupled beam formulation:
mφ = Kuφ' * u + Kφ * φ
M = collapseMPC(mφ, mpc_φ)The resulting field M contains the resultant generalized moments transferred from the constrained slave degrees of freedom to their MPC master degrees of freedom.
Notes
This function is intended for nodal generalized quantities. It should not be used as an elementwise integration operation.
Periodic boundary conditions
Periodic constraints are represented by MPC objects. The corresponding master/slave node pairing must first be defined in the Gmsh model.
periodic = MPC(
master="right",
slave="left",
field=U
)
u = solveField(K, f; support=[bc], mpc=[periodic])