Thread control#

femorph-solver integrates with threadpoolctl to cap BLAS / OpenMP thread pools inside every solver. Without a cap, NumPy / SciPy inherit the BLAS backend’s default — often the host physical core count — which oversubscribes the CPU on shared runners and makes dense LAPACK paths 5-10x slower from scheduler contention.

Three levers, in resolution order:

  1. Per-call kwarg. Every solver accepts thread_limit: int | None = None:

    solve_modal(K, M, prescribed={}, n_modes=10, thread_limit=4)
    

    An explicit integer caps threads for that call only.

  2. Process-wide default. Set once and every subsequent solve picks it up:

    import femorph_solver
    femorph_solver.set_thread_limit(4)
    
  3. Environment variable. FEMORPH_SOLVER_NUM_THREADS=<int> seeds the process-wide default at import time. Convenient for CI / container entrypoints.

Passing thread_limit=None (the default) defers to the process-wide default; if that’s also unset, no cap is applied and the backend decides.

See femorph_solver.set_thread_limit() and femorph_solver.get_thread_limit() in the API reference.