Source code for Zmag_bins.Zmag_dynamic_stereo_solvers
"""Factory and thin wrappers for stereographic **dynamic** solvers.
* :class:`StereoDemagSolver` — :class:`~Zmag_bins.Zmag_dynamic_stereo_demag.Z_sim_dynamic_stereo_demag`
* :class:`StereoNoDemagSolver` — :class:`~Zmag_bins.Zmag_dynamic_stereo_nodemag.Z_sim_dynamic_stereo_nodemag`
:func:`create_stereo_solver` is the recommended entry point. Wrappers forward attribute
access to :attr:`StereoSolverBase.impl`."""
from __future__ import annotations
from abc import ABC, abstractmethod
from copy import deepcopy
from typing import Any
from Zmag_bins.Zmag_dynamic_stereo_demag import Z_sim_dynamic_stereo_demag
from Zmag_bins.Zmag_dynamic_stereo_nodemag import Z_sim_dynamic_stereo_nodemag
__all__ = [
"StereoSolverBase",
"StereoDemagSolver",
"StereoNoDemagSolver",
"create_stereo_solver",
"Z_sim_dynamic_stereo_demag",
"Z_sim_dynamic_stereo_nodemag",
]
[docs]
class StereoSolverBase(ABC):
"""
Abstract facade over dynamic stereographic solver implementations.
Deep-copies ``args_init_sim`` and forwards attributes to :attr:`impl`.
"""
[docs]
def __init__(self, args_init_sim: dict[str, Any]):
"""
Deep-copy initialization dict and construct the concrete solver.
Parameters
----------
args_init_sim : dict
Simulation setup dictionary for the underlying class.
"""
self._args_init_sim = deepcopy(args_init_sim)
self._solver = self._build_solver(self._args_init_sim)
@abstractmethod
def _build_solver(self, args_init_sim: dict[str, Any]):
"""Instantiate the model-specific solver implementation."""
raise NotImplementedError
@property
def impl(self):
"""Underlying ``Z_sim_dynamic_stereo_*`` instance."""
return self._solver
def __getattr__(self, name: str):
"""Delegate attribute access to :attr:`impl`."""
return getattr(self._solver, name)
[docs]
class StereoDemagSolver(StereoSolverBase):
"""
Wrapper for :class:`~Zmag_bins.Zmag_dynamic_stereo_demag.Z_sim_dynamic_stereo_demag`.
Requires ``demag`` in ``energy_terms``.
"""
def _build_solver(self, args_init_sim: dict[str, Any]):
terms = list(args_init_sim.get("energy_terms", []))
if "demag" not in terms:
raise ValueError("StereoDemagSolver requires 'demag' in energy_terms.")
return Z_sim_dynamic_stereo_demag(args_init_sim)
[docs]
class StereoNoDemagSolver(StereoSolverBase):
"""
Wrapper for :class:`~Zmag_bins.Zmag_dynamic_stereo_nodemag.Z_sim_dynamic_stereo_nodemag`.
Strips ``demag`` from ``energy_terms`` if present.
"""
def _build_solver(self, args_init_sim: dict[str, Any]):
return Z_sim_dynamic_stereo_nodemag(args_init_sim)
[docs]
def create_stereo_solver(args_init_sim: dict[str, Any], model: str = "demag") -> StereoSolverBase:
"""
Build a stereographic dynamic solver wrapper.
Parameters
----------
args_init_sim : dict
Simulation setup dictionary.
model : str
``"demag"`` or ``"nodemag"``.
Returns
-------
StereoSolverBase
Delegating wrapper around the concrete solver.
Raises
------
ValueError
If ``model`` is unrecognized.
"""
mode = model.strip().lower()
if mode == "demag":
return StereoDemagSolver(args_init_sim)
if mode == "nodemag":
return StereoNoDemagSolver(args_init_sim)
raise ValueError(f"Unknown model='{model}'. Use 'demag' or 'nodemag'.")