feax.assembler
Assembler functions that work with Problem and InternalVars.
This module provides the main assembler API for finite element analysis with separated internal variables. It handles the assembly of residual vectors and Jacobian matrices for both volume and surface integrals, supporting various physics kernels (Laplace, mass, surface, and universal).
Operator Objects
class Operator()
Element-level operator for quadrature-point computations.
Provides methods for interpolating solutions, computing gradients, interpolating internal variables, and integrating over quadrature points. Used internally by kernel functions to eliminate code duplication.
Parameters
- problem (Problem): The finite element problem.
- fe_index (int): Index of the finite element variable (default 0).
eval
def eval(cell_sol: np.ndarray, shape_vals: np.ndarray = None) -> np.ndarray
Interpolate nodal solution to quadrature points.
Parameters
- cell_sol (np.ndarray): Nodal solution values, shape (num_nodes, vec).
- shape_vals (np.ndarray, optional): Shape function values. Uses volume shape functions if None.
Returns
np.ndarray Solution at quadrature points, shape (num_quads, vec).
grad
def grad(cell_sol: np.ndarray, cell_shape_grads: np.ndarray) -> np.ndarray
Compute solution gradient at quadrature points.
Parameters
- cell_sol (np.ndarray): Nodal solution values, shape (num_nodes, vec).
- cell_shape_grads (np.ndarray): Shape function gradients, shape (num_quads, num_nodes, dim).
Returns
np.ndarray Gradient at quadrature points, shape (num_quads, vec, dim).
hess
def hess(cell_sol: np.ndarray, cell_shape_hessians: np.ndarray) -> np.ndarray
Compute solution Hessian (second spatial derivatives) at quadrature points.
Parameters
- cell_sol (np.ndarray): Nodal solution values, shape (num_nodes, vec).
- cell_shape_hessians (np.ndarray): Shape function second derivatives in physical coordinates, shape (num_quads, num_nodes, dim, dim).
Returns
np.ndarray Hessian at quadrature points, shape (num_quads, vec, dim, dim). H[q, i, K, L] = sum_a u[a, i] * d²h_a/(dX_K dX_L) at quad point q.
interpolate_var
def interpolate_var(var: np.ndarray) -> np.ndarray
Interpolate a single internal variable to quadrature points.
Handles node-based (shape function interpolation), cell-based (broadcast), and quad-based (pass-through) variables.
Parameters
- var (np.ndarray): Internal variable. Shape determines interpolation strategy: - scalar: broadcast to all quad points - (num_nodes,): node-based, interpolate via shape functions - (1,) or cell-based: broadcast to all quad points - (num_quads,): quad-based, pass through
Returns
np.ndarray Values at quadrature points, shape (num_quads,).
interpolate_vars
def interpolate_vars(
cell_internal_vars: Tuple[np.ndarray, ...]) -> List[np.ndarray]
Interpolate all internal variables to quadrature points.
Parameters
- cell_internal_vars (tuple of np.ndarray): Internal variables for a single element.
Returns
list of np.ndarray Interpolated values at quadrature points.
integrate_grad
def integrate_grad(quad_values: np.ndarray,
cell_v_grads_JxW: np.ndarray) -> np.ndarray
Integrate tensor quad-point values against test function gradients.
Computes: sum_q sigma(q) : grad_v(q) * JxW(q)
Parameters
- quad_values (np.ndarray): Physics values at quad points, shape (num_quads, vec, dim).
- cell_v_grads_JxW (np.ndarray): Pre-multiplied test function gradients × JxW, shape (num_quads, num_nodes, vec, dim).
Returns
np.ndarray Element contribution, shape (num_nodes, vec).
integrate_val
def integrate_val(quad_values: np.ndarray,
cell_JxW: np.ndarray,
shape_vals: np.ndarray = None) -> np.ndarray
Integrate scalar/vector quad-point values with shape functions.
Computes: sum_q f(q) * N(q) * JxW(q)
Parameters
- quad_values (np.ndarray): Physics values at quad points, shape (num_quads, vec).
- cell_JxW (np.ndarray): Jacobian determinant × quadrature weights, shape (num_quads,).
- shape_vals (np.ndarray, optional): Shape function values. Uses volume shape functions if None.
Returns
np.ndarray Element contribution, shape (num_nodes, vec).
gather_internal_vars
@staticmethod
def gather_internal_vars(
problem: 'Problem', internal_vars: Tuple[np.ndarray,
...]) -> List[np.ndarray]
Gather global internal variables to per-cell format.
Transforms node-based variables from global (num_nodes,) arrays to per-cell (num_cells, num_nodes_per_elem) arrays using element connectivity. Cell-based and quad-based variables are passed through.
Parameters
- problem (Problem): The finite element problem with connectivity information.
- internal_vars (tuple of np.ndarray): Global internal variables.
Returns
list of np.ndarray Per-cell internal variables ready for vmapped kernels.
interpolate_to_quad_points
def interpolate_to_quad_points(var: np.ndarray, shape_vals: np.ndarray,
num_cells: int, num_quads: int) -> np.ndarray
Interpolate node-based or cell-based values to quadrature points.
.. deprecated::
Use :meth:Operator.interpolate_var instead.
This function handles three cases:
- Node-based: shape (num_nodes,) -> interpolate using shape functions
- Cell-based: shape (num_cells,) -> broadcast to all quad points in cell
- Quad-based: shape (num_cells, num_quads) -> pass through (legacy)
Parameters
- var (np.ndarray): Variable to interpolate. Can be: - (num_nodes,) for node-based - (num_cells,) for cell-based - (num_cells, num_quads) for quad-based (legacy)
- shape_vals (np.ndarray): Shape function values at quadrature points, shape (num_quads, num_nodes)
- num_cells (int): Number of cells/elements
- num_quads (int): Number of quadrature points per cell
Returns
np.ndarray Values at quadrature points, shape (num_quads,)
get_laplace_kernel
def get_laplace_kernel(problem: 'Problem', tensor_map: Callable) -> Callable
Create Laplace kernel function for gradient-based physics.
The Laplace kernel handles gradient-based terms in the weak form, such as those arising in elasticity, heat conduction, and diffusion problems. It implements the integral term: ∫ σ(∇u) : ∇v dΩ where σ is the stress/flux tensor computed from the gradient.
Parameters
- problem (Problem): The finite element problem containing mesh and element information.
- tensor_map (Callable): Function that maps gradient tensor to stress/flux tensor. Signature: (u_grad: ndarray, *internal_vars) -> ndarray where u_grad has shape (vec, dim) and returns (vec, dim).
Returns
Callable Laplace kernel function that computes the contribution to the weak form from gradient-based physics.
get_mass_kernel
def get_mass_kernel(problem: 'Problem', mass_map: Callable) -> Callable
Create mass kernel function for non-gradient terms.
The mass kernel handles terms without derivatives in the weak form, such as mass matrices, reaction terms, or body forces. It implements the integral term: ∫ m(u, x) · v dΩ where m is a mass-like term.
Parameters
- problem (Problem): The finite element problem containing mesh and element information.
- mass_map (Callable): Function that computes the mass term. Signature: (u: ndarray, x: ndarray, *internal_vars) -> ndarray where u has shape (vec,), x has shape (dim,), and returns (vec,).
Returns
Callable Mass kernel function that computes the contribution to the weak form from non-gradient physics.
get_surface_kernel
def get_surface_kernel(problem: 'Problem', surface_map: Callable) -> Callable
Create surface kernel function for boundary integrals.
The surface kernel handles boundary integrals in the weak form, such as surface tractions, pressures, or fluxes. It implements the integral term: ∫ t(u, x) · v dΓ where t is the surface load/flux.
Parameters
- problem (Problem): The finite element problem containing mesh and element information.
- surface_map (Callable): Function that computes the surface traction/flux. Signature: (u: ndarray, x: ndarray, *internal_vars) -> ndarray where u has shape (vec,), x has shape (dim,), and returns (vec,).
Returns
Callable Surface kernel function that computes the contribution to the weak form from boundary loads/fluxes.
create_volume_kernel
def create_volume_kernel(problem: 'Problem') -> Callable
Create unified volume kernel combining all volume physics.
This function creates a kernel that combines contributions from all volume integral terms: Laplace (gradient-based), mass (non-gradient), universal (custom), and weak form kernels. The resulting kernel is used for both residual and Jacobian assembly.
Parameters
- problem (Problem): The finite element problem that may define get_tensor_map(), get_mass_map(), get_weak_form(), and/or get_universal_kernel() methods.
Returns
Callable Combined volume kernel function that sums contributions from all applicable physics kernels.
Notes
The kernel checks for the existence of each physics method in the problem and only includes contributions from those that are defined. This allows for flexible problem definitions with any combination of physics terms.
For multi-variable problems, the priority order is:
get_universal_kernel()— full low-level controlget_weak_form()— high-level quad-point physics definition
create_surface_kernel
def create_surface_kernel(problem: 'Problem', surface_index: int) -> Callable
Create unified surface kernel for a specific boundary.
This function creates a kernel that combines contributions from standard surface maps and universal surface kernels for a specific boundary surface identified by surface_index.
Parameters
- problem (Problem): The finite element problem that may define get_surface_maps() and/or get_universal_kernels_surface() methods.
- surface_index (int): Index identifying which boundary surface this kernel is for. Corresponds to the index in problem.location_fns.
Returns
Callable Combined surface kernel function for the specified boundary.
Notes
Multiple boundaries can have different physics. The surface_index parameter selects which surface map and universal kernel to use.
For multi-variable problems, only universal_kernels_surface should be used, as get_surface_maps() only supports single-variable problems.
split_and_compute_cell
def split_and_compute_cell(
problem: 'Problem', cells_sol_flat: np.ndarray, jac_flag: bool,
internal_vars_volume: Tuple[np.ndarray, ...]) -> Any
Compute volume integrals for residual or Jacobian assembly.
This function evaluates volume integrals over all elements, optionally computing the Jacobian via forward-mode automatic differentiation. It uses batching to manage memory for large meshes.
Parameters
- problem (Problem): The finite element problem containing mesh and quadrature data.
- cells_sol_flat (np.ndarray): Flattened solution values at element nodes. Shape: (num_cells, num_nodes * vec).
- jac_flag (bool): If True, compute both values and Jacobian. If False, compute only values.
- internal_vars_volume (tuple of np.ndarray): Material properties at quadrature points for each variable. Each array has shape (num_cells, num_quads).
Returns
np.ndarray or tuple of np.ndarray If jac_flag is False: weak form values with shape (num_cells, num_dofs). If jac_flag is True: tuple of (values, jacobian) where jacobian has shape (num_cells, num_dofs, num_dofs).
Notes
The function splits computation into batches (default 20) to avoid memory issues with large meshes. This is particularly important for 3D problems.
compute_face
def compute_face(problem: 'Problem', cells_sol_flat: np.ndarray,
jac_flag: bool,
internal_vars_surfaces: List[Tuple[np.ndarray, ...]]) -> Any
Compute surface integrals for residual or Jacobian assembly.
This function evaluates surface integrals over all boundary faces, optionally computing the Jacobian via forward-mode automatic differentiation.
Parameters
- problem (Problem): The finite element problem containing boundary information.
- cells_sol_flat (np.ndarray): Flattened solution values at element nodes. Shape: (num_cells, num_nodes * vec).
- jac_flag (bool): If True, compute both values and Jacobian. If False, compute only values.
- internal_vars_surfaces (list of tuple of np.ndarray): Surface variables for each boundary. Each entry corresponds to one boundary surface and contains arrays with shape (num_surface_faces, num_face_quads).
Returns
list of np.ndarray or list of tuple If jac_flag is False: list of weak form values for each boundary. If jac_flag is True: list of (values, jacobian) tuples for each boundary.
Notes
Each boundary surface can have different loading conditions or physics, handled through separate surface kernels and internal variables.
compute_residual_vars_helper
def compute_residual_vars_helper(
problem: 'Problem', weak_form_flat: np.ndarray,
weak_form_face_flat: List[np.ndarray]) -> List[np.ndarray]
Assemble residual from element and face contributions.
This helper function assembles the global residual vector by accumulating contributions from volume and surface integrals at the appropriate nodes.
Parameters
- problem (Problem): The finite element problem containing connectivity information.
- weak_form_flat (np.ndarray): Flattened weak form values from volume integrals. Shape: (num_cells, num_dofs_per_cell).
- weak_form_face_flat (list of np.ndarray): Weak form values from surface integrals for each boundary. Each array has shape (num_boundary_faces, num_dofs_per_face).
Returns
list of np.ndarray Global residual for each solution variable. Each array has shape (num_total_nodes, vec).
Notes
Uses JAX's at[].add() for scatter-add operations to accumulate contributions from multiple elements sharing the same nodes.
get_jacobian_info
def get_jacobian_info(problem: 'Problem', sol_list: List[np.ndarray],
internal_vars: InternalVars) -> dict
Get Jacobian matrix information without full matrix construction.
This function provides safe access to Jacobian statistics that works correctly with JIT-compiled solvers and cuDSS backend. Unlike the internal _get_J function, this does not cause GPU memory conflicts.
Parameters
- problem (Problem): The finite element problem definition.
- sol_list (list of np.ndarray): Solution arrays for each variable.
- internal_vars (InternalVars): Internal variables container.
Returns
dict Dictionary containing:
- 'nnz': Number of non-zero entries (int)
- 'shape': Matrix shape (tuple)
- 'matrix_view': Matrix storage format (MatrixView enum)
Examples
>>> info = get_jacobian_info(problem, sol_list, internal_vars)
>>> print(f"Jacobian NNZ: {`info['nnz']:,`}")
>>> print(f"Matrix view: {`info['matrix_view'].name`}")
Notes
This function is safe to call from user code and does not interfere with JIT-compiled solvers using cuDSS backend.
get_res
def get_res(problem: 'Problem', sol_list: List[np.ndarray],
internal_vars: InternalVars) -> List[np.ndarray]
Compute residual vector with separated internal variables.
Assembles the global residual vector by evaluating the weak form at the current solution state. Includes contributions from both volume and surface integrals.
Parameters
- problem (Problem): The finite element problem containing mesh and physics definitions.
- sol_list (list of np.ndarray): Solution arrays for each variable. Each array has shape (num_total_nodes, vec).
- internal_vars (InternalVars): Container with material properties and loading parameters.
Returns
list of np.ndarray Residual arrays for each solution variable. Each array has shape (num_total_nodes, vec).
Examples
>>> residual = get_res(problem, [solution], internal_vars)
>>> res_norm = np.linalg.norm(jax.flatten_util.ravel_pytree(residual)[0])
>>> print(f"Residual norm: {`res_norm`}")
Notes
The residual represents the imbalance in the weak form equations. For converged solutions, the residual should be near zero.
create_J_bc_function
def create_J_bc_function(
problem: 'Problem',
bc: 'DirichletBC',
symmetric: bool = True
) -> Callable[[np.ndarray, InternalVars], sparse.BCOO]
Create Jacobian function with Dirichlet BC applied.
Returns a function that computes the Jacobian matrix with Dirichlet boundary conditions enforced. The BC application modifies the matrix to enforce constraints.
Parameters
- problem (Problem): The finite element problem definition.
- bc (DirichletBC): Dirichlet boundary condition specifications.
- symmetric (bool, default True): If True, use symmetric elimination (zero BC rows and columns). If False, use non-symmetric elimination (zero BC rows only, keep columns). Non-symmetric elimination preserves the K_10 coupling, which is needed for the incremental Newton approach without pre-applied BCs.
Returns
Callable Function with signature (sol_flat, internal_vars) -> sparse.BCOO that returns the BC-modified Jacobian matrix.
Notes
The returned function is suitable for use in Newton solvers and can be differentiated for sensitivity analysis.
create_res_bc_function
def create_res_bc_function(
problem: 'Problem',
bc: 'DirichletBC') -> Callable[[np.ndarray, InternalVars], np.ndarray]
Create residual function with Dirichlet BC applied.
Returns a function that computes the residual vector with Dirichlet boundary conditions enforced. The BC application zeros out residuals at constrained DOFs.
Parameters
- problem (Problem): The finite element problem definition.
- bc (DirichletBC): Dirichlet boundary condition specifications.
Returns
Callable Function with signature (sol_flat, internal_vars) -> np.ndarray that returns the BC-modified residual vector.
Notes
The returned function is used in Newton solvers to find solutions that satisfy both the weak form equations and boundary conditions.
create_J_bc_parametric
def create_J_bc_parametric(problem: 'Problem',
symmetric: bool = True) -> Callable
Create a Jacobian function that takes bc as an explicit argument.
Unlike :func:create_J_bc_function which captures bc in a closure,
this version accepts bc as a third argument so that the function
traces through the BC pytree under jax.vmap.
Parameters
- problem (Problem): The finite element problem definition.
- symmetric (bool, default True): If True, use symmetric elimination.
Returns
Callable
Function with signature (sol_flat, internal_vars, bc) -> sparse.BCOO.
create_res_bc_parametric
def create_res_bc_parametric(problem: 'Problem') -> Callable
Create a residual function that takes bc as an explicit argument.
Unlike :func:create_res_bc_function which captures bc in a closure,
this version accepts bc as a third argument. This enables a single
JIT-compiled function to be reused across time steps where only the
prescribed BC values change (same DOF locations, different values).
Parameters
- problem (Problem): The finite element problem definition.
Returns
Callable
Function with signature (sol_flat, internal_vars, bc) -> np.ndarray.