Source code for struphy.initial.base
from abc import ABCMeta, abstractmethod
from typing import Callable
from struphy.io.options import LiteralOptions
from struphy.utils.utils import check_option
[docs]
class Perturbation(metaclass=ABCMeta):
"""Abstract base class for perturbation functions used as initial conditions in simulations.
This class provides the interface and common functionality for defining perturbation fields
in logical (eta) or physical coordinate spaces. Subclasses must implement the ``__call__``
method to define the perturbation as a callable function of spatial coordinates.
The class supports flexible representation bases (p-forms, vector fields, physical coordinates)
and allows specification of which component is perturbed for vector-valued quantities.
Attributes
----------
given_in_basis : str
Specifies the basis representation of the perturbation. Options:
- '0', '1', '2', '3' : Differential form basis (0-form=scalar, 1-form, etc.)
- 'v' : Vector field basis
- 'physical' : Physical (mapped) domain coordinates
- 'physical_at_eta' : Physical components evaluated in logical (eta) domain, u(F(eta))
- 'norm' : Normalized co-variant basis (:math:`delta_i / |delta_i|`)
comp : int
Component index for vector-valued perturbations (0-2 for vector components,
0 for scalar-valued functions). Default is 0.
Examples
--------
Subclasses should override ``__call__`` to implement specific perturbation fields:
>>> class CustomPerturbation(Perturbation):
... def __init__(self):
... self.given_in_basis = 'physical'
... def __call__(self, eta1, eta2, eta3, flat_eval=False):
... return eta1 * eta2 # Example perturbation field
"""
@abstractmethod
def __call__(self, eta1, eta2, eta3, flat_eval=False):
"""Evaluate the perturbation field at given coordinates.
Parameters
----------
eta1, eta2, eta3 : ndarray or float
Coordinate values in the eta (logical) space, or physical space depending on
the perturbation's basis representation.
flat_eval : bool, default=False
If True, treat inputs as flattened arrays and return flattened output.
If False, preserve the array shapes for meshgrid-like evaluation.
Returns
-------
ndarray or float
Perturbation field values at the given coordinates, with shape matching
the input coordinates (or flattened if flat_eval=True).
"""
pass
def prepare_eval_pts(self):
# TODO: we could prepare the arguments via a method in this base class (flat_eval, sparse meshgrid, etc.).
pass
def __repr__(self):
print(f" {self.__class__.__name__}:")
for k, v in self.__dict__.items():
print(f" {k}: {v}")
return ""
@property
def given_in_basis(self) -> str:
r"""In which basis the perturbation is represented, must be set in child class (use the setter below).
Either
* '0', '1', '2' or '3' for a p-form basis
* 'v' for a vector-field basis
* 'physical' when defined on the physical (mapped) domain
* 'physical_at_eta' when given the physical components evaluated on the logical domain, u(F(eta))
* 'norm' when given in the normalized co-variant basis (:math:`\delta_i / |\delta_i|`)
"""
return self._given_in_basis
@given_in_basis.setter
def given_in_basis(self, new: str):
check_option(new, LiteralOptions.GivenInBasis)
self._given_in_basis = new
@property
def comp(self) -> int:
"""Which component of vector is perturbed (=0 for scalar-valued functions).
Can be set in child class (use the setter below)."""
if not hasattr(self, "_comp"):
self._comp = 0
return self._comp
@comp.setter
def comp(self, new: int):
assert new in (0, 1, 2)
self._comp = new