Solver#
Solvers#
- class fluxfem.solver.LinearSolver(method: str = 'spsolve', tol: float = 1e-08, maxiter: int = 200)#
Lightweight wrapper for solving linear systems with optional Dirichlet BCs.
Supports dense arrays or FluxSparseMatrix and can either condense or enforce Dirichlet conditions before solving with the chosen backend.
- class fluxfem.solver.NonlinearSolver(space: FESpaceClosure, res_form: Any, params: Any, *, tol: float = 1e-08, maxiter: int = 20, linear_method: str = 'spsolve', line_search: bool = False, max_ls: int = 10, ls_c: float = 0.0001, linear_tol: float | None = None, dirichlet: DirichletBC | tuple[ndarray, ndarray] | None = None, external_vector: ndarray | None = None, linear_maxiter: int | None = None, jacobian_pattern: Any | None = None)#
Backward-compatible Newton-based nonlinear solver.
This is a thin wrapper around
newton_solvekept for legacy code paths. PreferNonlinearAnalysis+NewtonSolveRunnerfor new workflows.
- class fluxfem.solver.LinearAnalysis(space: Any, bilinear_form: Callable[[jax.tree_util.register_pytree_node_class, Any], jax.numpy.ndarray], params: Any, base_rhs_vector: ndarray, dirichlet: tuple[ndarray, ndarray] | None = None, pattern: Any | None = None, dtype: Any = jax.numpy.float64)#
Bundle linear problem data for a single solve or a load-scaled sequence.
The matrix is assembled once from
bilinear_form; the RHS is scaled by the load factor.
- class fluxfem.solver.NonlinearAnalysis(space: Any, residual_form: Callable[[jax.tree_util.register_pytree_node_class, jax.numpy.ndarray, Any], jax.numpy.ndarray], params: Any, base_external_vector: ndarray | None = None, dirichlet: tuple[ndarray, ndarray] | None = None, extra_terms: list[Callable[[ndarray], tuple[ndarray, ndarray] | tuple[ndarray, ndarray, dict[str, Any]] | None]] | None = None, assembly_policy: Any | None = None, jacobian_pattern: Any | None = None, dtype: Any = jax.numpy.float64)#
Bundle problem data needed for a Newton solve with load scaling.
- Variables:
space (Any) – FE space containing topology/dofs.
residual_form (Any) – Internal residual form (e.g., neo_hookean_residual_form).
params (Any) – Parameters forwarded to the residual form.
base_external_vector (Any | None) – Unscaled external load vector (scaled by load factor in external_for_load).
dirichlet (tuple | None) – (dofs, values) for Dirichlet boundary conditions.
extra_terms (list[callable] | None) – Optional extra term assemblers returning (K, f[, metrics]).
assembly_policy (Any | None) – Optional volume assembly execution policy forwarded to residual/Jacobian assembly.
jacobian_pattern (Any | None) – Optional sparsity pattern to reuse between load steps.
dtype (Any) – dtype for the solution vector (defaults to float64).
- class fluxfem.solver.LinearSolveRunner(analysis: LinearAnalysis, config: LinearSolveConfig)#
Solve linear systems for one or more load factors using a unified interface.
- class fluxfem.solver.NewtonSolveRunner(analysis: NonlinearAnalysis, config: NewtonLoopConfig)#
Run one or more Newton solves across load factors.
This orchestrates load stepping, assembles external load per step, and returns the full (Dirichlet-expanded) solution and per-step history.
- class fluxfem.solver.LinearSolveConfig(method: str = 'spsolve', tol: float = 1e-08, maxiter: int | None = None, preconditioner: Any | None = None, ksp_type: str | None = None, pc_type: str | None = None, ksp_rtol: float | None = None, ksp_atol: float | None = None, ksp_max_it: int | None = None, petsc_ksp_norm_type: str | None = None, petsc_ksp_monitor_true_residual: bool = False, petsc_ksp_converged_reason: bool = False, petsc_ksp_monitor_short: bool = False, petsc_shell_pmat: bool = False, petsc_shell_pmat_mode: str = 'full', petsc_shell_pmat_rebuild_iters: int | None = None, petsc_shell_fallback: bool = False, petsc_shell_fallback_ksp_types: tuple[str, ...] = ('bcgs', 'gmres'), petsc_shell_fallback_rebuild_pmat: bool = True)#
Control parameters for the linear solve with optional load scaling.
- class fluxfem.solver.NewtonLoopConfig(tol: float = 1e-08, atol: float = 0.0, maxiter: int = 20, line_search: bool = False, max_ls: int = 10, ls_c: float = 0.0001, linear_solver: str = 'spsolve', linear_maxiter: int | None = None, linear_tol: float | None = None, linear_preconditioner: Any | None = None, petsc_ksp_type: str = 'gmres', petsc_pc_type: str = 'none', petsc_rtol: float | None = None, petsc_atol: float | None = None, petsc_max_it: int | None = None, petsc_options: dict[str, Any] | None = None, petsc_use_pmat: bool = False, matfree_mode: str = 'linearize', load_sequence: Sequence[float] | None = None, n_steps: int = 1)#
Control parameters for the Newton loop with load stepping.
Coupled Systems#
- class fluxfem.solver.CoupledSystem(K_u: jax.tree_util.register_pytree_node_class, F_u: jax.numpy.ndarray)#
JAX-native coupled system with sparse-first assembly.
- class fluxfem.solver.CoupledSystemBuilder(system: CoupledSystem)#
Minimal JAX-native builder for structural blocks and remote springs.
- class fluxfem.solver.DirichletSpec(field: 'str', value: 'float | Sequence[float] | np.ndarray | jnp.ndarray | None' = None, nodes: 'int | Sequence[int] | np.ndarray | None' = None, components: 'int | Sequence[int] | np.ndarray | None' = None, local_dofs: 'int | Sequence[int] | np.ndarray | None' = None)#
- class fluxfem.solver.ConstraintSpec(kind: 'str', master: 'str', slave: 'str', C: 'np.ndarray | jnp.ndarray | None' = None, rho: 'float' = 0.0, F_contact: 'np.ndarray | jnp.ndarray | None' = None, contact_obj: 'Any | None' = None, family: 'str | None' = None, enforcement: 'str | None' = None, law: 'str | None' = None, formulation: 'str | None' = None, value_dim: 'int | None' = None, weak_form: 'Any | None' = None, state: 'Any | None' = None, params: 'Any | None' = None, residual: 'Any | None' = None, scale: 'float' = 1.0, residual_sign: 'float' = -1.0, normal_source: 'str' = 'master', sparse: 'bool | None' = None, batch_jac: 'bool | None' = None, ref_point: 'np.ndarray | jnp.ndarray | None' = None, slave_coords: 'np.ndarray | jnp.ndarray | None' = None, weights: 'np.ndarray | jnp.ndarray | None' = None, normalize_weights: 'bool' = True, embedding: 'Any | None' = None)#
Sparse#
- class fluxfem.solver.SparsityPattern(rows: jnp.ndarray, cols: jnp.ndarray, n_dofs: int, idx: jnp.ndarray | None = None, diag_idx: jnp.ndarray | None = None, perm: jnp.ndarray | None = None, indptr: jnp.ndarray | None = None, indices: jnp.ndarray | None = None)#
Jacobian sparsity pattern (rows/cols) that is independent of the solution.
- class fluxfem.solver.FluxSparseMatrix(rows_or_pattern: SparsityPattern | ArrayLike, cols: ArrayLike | None = None, data: ArrayLike | None = None, n_dofs: int | None = None, meta: dict | None = None)#
Sparse matrix wrapper (COO) with a fixed pattern and mutable values. - pattern stores rows/cols/n_dofs (optionally idx for dense scatter) - data stores the numeric values for the current nonlinear iterate
- class fluxfem.solver.FluxSparseOperator(rows: ndarray, cols: ndarray, data: ndarray, shape: tuple[int, int], meta: dict | None = None)
Sparse operator wrapper (COO) for rectangular or square operators.
This is intentionally narrower than FluxSparseMatrix: - stores raw COO rows/cols/data - tracks a general
shape=(n_rows, n_cols)- supports dense conversion and forward/adjoint matvecIt is intended as the first rectangular sparse abstraction for Petrov-Galerkin-style bilinear assembly without destabilizing existing square-matrix solver paths.
Dirichlet#
- fluxfem.solver.enforce_dirichlet_dense(K, F, dofs, vals)#
Apply Dirichlet conditions directly to stiffness/load (dense).
- fluxfem.solver.enforce_dirichlet_sparse(A: jax.tree_util.register_pytree_node_class, F, dofs, vals)#
Apply Dirichlet conditions to FluxSparseMatrix + load (CSR).
- fluxfem.solver.free_dofs(n_dofs: int, dir_dofs) ndarray#
Return free DOF indices given total DOFs and Dirichlet DOFs.
- fluxfem.solver.condense_dirichlet_fluxsparse(A: jax.tree_util.register_pytree_node_class, F, dofs, vals)#
Condense Dirichlet DOFs for a FluxSparseMatrix. Returns: (K_ff, F_free, free_dofs, dir_dofs, dir_vals)
- fluxfem.solver.condense_dirichlet_dense(K, F, dofs, vals)#
Eliminate Dirichlet dofs for dense/CSR matrices and return condensed system. Returns: (K_cc, F_c, free_dofs, dir_dofs, dir_vals)
- fluxfem.solver.expand_dirichlet_solution(u_free, free_dofs, dir_dofs, dir_vals, n_total)#
Expand condensed solution back to full vector.
Iterative#
- fluxfem.solver.cg_solve(A: Any, b: jnp.ndarray, *, x0: jnp.ndarray | None = None, tol: float = 1e-08, maxiter: int | None = None, preconditioner: object | None = None) tuple[jnp.ndarray, CGInfo]#
Conjugate gradient (Ax=b) in JAX. Supports single RHS (n,) or multiple RHS (n, n_rhs).
- fluxfem.solver.cg_solve_jax(A: Any, b: jnp.ndarray, *, x0: jnp.ndarray | None = None, tol: float = 1e-08, maxiter: int | None = None, preconditioner: object | None = None) tuple[jnp.ndarray, CGInfo]#
Conjugate gradient via jax.scipy.sparse.linalg.cg. Supports single RHS (n,) or multiple RHS (n, n_rhs).
Block matrices#
- fluxfem.solver.block_diag(*, order: Sequence[str | int] | None = None, **blocks: Any) dict[str | int, Any]#
Build a dict of diagonal blocks with an optional explicit field order.
- fluxfem.solver.make_block_matrix(*, diag: Mapping[str | int, Any] | Sequence[Any], rel: Mapping[tuple[str | int, str | int], Any] | None = None, add_contiguous: Any | None = None, sizes: Mapping[str | int, int] | None = None, symmetric: bool = False, transpose_rule: str = 'T') FluxBlockMatrix#
Build a lazy FluxBlockMatrix from diagonal blocks, optional relations, and a full matrix.
- class fluxfem.solver.FluxBlockMatrix(blocks: dict[str | int, dict[str | int, Any]], *, sizes: Mapping[str | int, int], order: Sequence[str | int] | None = None, symmetric: bool = False, transpose_rule: str = 'T')#
Lazy block-matrix container that assembles on demand.
Nonlinear#
- fluxfem.solver.newton_solve(space, res_form: ResidualForm[Any], u0: ArrayLike, params: Any, *, tol: float = 1e-08, atol: float = 0.0, maxiter: int = 20, linear_solver: str = 'spsolve', linear_maxiter: int | None = None, linear_tol: float | None = None, linear_preconditioner: object | None = None, petsc_ksp_type: str = 'gmres', petsc_pc_type: str = 'none', petsc_rtol: float | None = None, petsc_atol: float | None = None, petsc_max_it: int | None = None, petsc_options: dict[str, Any] | None = None, petsc_use_pmat: bool = False, matfree_mode: str = 'linearize', matfree_cache: dict[str, Any] | None = None, dirichlet: tuple[np.ndarray, np.ndarray] | None = None, callback: Callable[[np.ndarray, SolverResult], Any] | None = None, line_search: bool = False, max_ls: int = 10, ls_c: float = 0.0001, external_vector: np.ndarray | None = None, jacobian_pattern: SparsityPattern | None = None, extra_terms: list[ExtraTerm] | None = None, assembly_policy: Any | None = None) tuple[np.ndarray, SolverResult]#
Gridap-style Newton–Raphson solver on free DOFs only.
Unknown vector = free DOFs (Dirichlet eliminated).
Residual/Jacobian are assembled on full DOFs; we slice to free DOFs.
Convergence: ||R_free||_inf < max(atol, tol * ||R_free0||_inf).
external_vector: optional global RHS (internal - external).
CG path accepts an operator with matvec that acts on free DOFs via a wrapper.
cg_matfree uses JVP/linearize to form a matrix-free matvec (no global Jacobian).
linear_preconditioner: forwarded to cg_solve/cg_solve_jax or PETSc shell (None | “jacobi” | “block_jacobi” | “diag0” | callable).
petsc_* options configure the inner PETSc shell solve when linear_solver=”petsc_shell”.
matfree_cache: optional dict for reusing matrix-free preconditioners across calls.
linear_tol: CG tolerance (defaults to 0.1 * tol if not provided).
jacobian_pattern: optional SparsityPattern to reuse sparsity across load steps.
extra_terms: optional list of callbacks returning (K, f[, metrics]) for extra terms.
- fluxfem.solver.solve_nonlinear(space, residual_form: Callable[[jax.tree_util.register_pytree_node_class, jax.numpy.ndarray, Any], jax.numpy.ndarray], params: Any, *, dirichlet: tuple[ndarray, ndarray] | None = None, base_external_vector: ndarray | None = None, extra_terms: list[Callable[[ndarray], tuple[ndarray, ndarray] | tuple[ndarray, ndarray, dict[str, Any]] | None]] | None = None, assembly_policy: Any | None = None, dtype=jax.numpy.float64, maxiter: int = 20, tol: float = 1e-08, atol: float = 1e-10, linear_solver: str = 'spsolve', linear_maxiter: int | None = None, linear_tol: float | None = None, linear_preconditioner=None, petsc_ksp_type: str = 'gmres', petsc_pc_type: str = 'none', petsc_rtol: float | None = None, petsc_atol: float | None = None, petsc_max_it: int | None = None, petsc_options: dict[str, Any] | None = None, petsc_use_pmat: bool = False, matfree_mode: str = 'linearize', line_search: bool = False, max_ls: int = 10, ls_c: float = 0.0001, n_steps: int = 1, jacobian_pattern=None, u0: ndarray | None = None) tuple[ndarray, list[LoadStepResult]]#
Convenience wrapper: build NonlinearAnalysis and run NewtonSolveRunner.