Source code for femorph_solver.estimators._problem

"""ProblemSpec — per-solve feature vector."""

from __future__ import annotations

from dataclasses import dataclass


[docs] @dataclass(frozen=True) class ProblemSpec: """Per-solve feature vector fed to the estimator. Attributes ---------- n_dof Number of free DOFs after BC reduction. The dominant feature — factor cost scales ~``n_dof^(4/3)`` on 3D meshes with nested dissection, solve cost ~``n_dof × log(n_dof)``. n_modes Modes requested. eigsh iteration count scales roughly linearly with this at fixed tolerance. nnz Non-zeros in the reduced K (upper triangle). Captures mesh connectivity density — a 3D plate has denser fill per DOF than a 1D beam. linear_solver Backend name. ``mkl_direct`` vs ``pardiso`` vs ``cholmod`` have measurably different factor + solve constants; we regress them separately rather than lumping. eigen_solver Eigen backend — ARPACK vs LOBPCG iterate differently. """ n_dof: int n_modes: int nnz: int = 0 linear_solver: str = "auto" eigen_solver: str = "arpack" #: Optional, set to True if the caller plans to use OOC. The #: estimator knows OOC is ~3.5× the in-core wall and ~0.78× the #: peak RSS (from TA-1 measurements) and applies that factor #: post-prediction. ooc: bool = False
[docs] @classmethod def from_model( cls, model, n_modes: int, *, linear_solver: str = "auto", eigen_solver: str = "arpack", ooc: bool = False, ) -> ProblemSpec: """Extract a ProblemSpec from a :class:`~femorph_solver.Model`. Uses the model's ``_dof_layout`` to count DOFs (cheap — doesn't factor K). ``nnz`` is not computed here (would require a full K assembly); the caller can supply it if they have a cached matrix. """ # _dof_layout returns (node_nums, layout_dict, N) where N # is the total DOF count before BC reduction. Actual free # DOF count depends on prescribed BCs — approximate as # ``N - len(prescribed)``. _, _, n_dof_full = model._dof_layout() try: prescribed = model._prescribed_dofs() n_dof_free = max(1, n_dof_full - len(prescribed)) except Exception: n_dof_free = n_dof_full return cls( n_dof=n_dof_free, n_modes=n_modes, linear_solver=linear_solver, eigen_solver=eigen_solver, ooc=ooc, )