Source code for struphy.models.poisson

import copy
import logging

from feectools.ddm.mpi import mpi as MPI

from struphy.io.options import BaseUnits, LiteralOptions
from struphy.models.base import StruphyModel
from struphy.models.species import (
    FieldSpecies,
)
from struphy.models.variables import FEECVariable
from struphy.propagators.base import Propagator
from struphy.propagators.implicit_diffusion import ImplicitDiffusion
from struphy.propagators.poisson_solve import PoissonSolve
from struphy.propagators.time_dependent_source import TimeDependentSource

logger = logging.getLogger("struphy")
rank = MPI.COMM_WORLD.Get_rank()


[docs] class Poisson(StruphyModel): """Weak discretization of Poisson's equation with a diffusion matrix, stabilization and an optional time-dependent right-hand side. Parameters ---------- base_units: BaseUnits Base units for normalization (default: BaseUnits()) with_t_dep_source: bool Whether the right-hand side source term is time-dependent (default: False) """ @classmethod def model_type(cls) -> LiteralOptions.ModelTypes: return "Fluid" ## species class EMFields(FieldSpecies): def __init__(self): self.phi = FEECVariable(space="H1") self.source = FEECVariable(space="H1") self.init_variables() ## propagators class Propagators: def __init__(self, rho: FEECVariable = None, with_t_dep_source=False): if with_t_dep_source: self.source = TimeDependentSource() self.poisson = PoissonSolve(rho=rho) ## abstract methods def __init__(self, base_units: BaseUnits = BaseUnits(), with_t_dep_source=False): self.with_t_dep_source = with_t_dep_source # 0. store input parameters self.params = copy.deepcopy(locals()) # 1. instantiate all species self.em_fields = self.EMFields() # 2. derive units (must be done after instantiating species to access charge and mass numbers) self.setup_equation_params(base_units=base_units) # 3. instantiate all propagators self.propagators = self.Propagators(rho=self.em_fields.source, with_t_dep_source=with_t_dep_source) # 4. assign variables to propagators if with_t_dep_source: self.propagators.source.variables.source = self.em_fields.source self.propagators.poisson.variables.phi = self.em_fields.phi # 5. define scalars to be tracked during simulation @property def bulk_species(self): return None @property def velocity_scale(self): return None def allocate_helpers(self): """Solve initial Poisson equation. :meta private: """ # # use setter to assign source # self.propagators.poisson.rho = Propagator.mass_ops.M0.dot(self.em_fields.source.spline.vector) # Solve with dt=1. and compute electric field logger.info("\nSolving initial Poisson problem...") self.propagators.poisson(1.0) logger.info("... Done.") # default parameters def generate_default_parameter_file(self, path=None, prompt=True): params_path = super().generate_default_parameter_file(path=path, prompt=prompt) new_file = [] with open(params_path, "r") as f: for line in f: new_file += [line] with open(params_path, "w") as f: for line in new_file: f.write(line)
[docs] @classmethod def doc_pde(cls): r"""**PDEs solved by model:** Find :math:`\phi \in H^1` such that .. math:: -\nabla \cdot D_0(\mathbf{x}) \nabla \phi + n_0(\mathbf{x}) \phi = \rho(t, \mathbf{x}) where :math:`n_0, \rho(t) : \Omega \to \mathbb{R}` are real-valued functions, :math:`\rho(t)` is parametrized by time :math:`t`, and :math:`D_0 : \Omega \to \mathbb{R}^{3 \times 3}` is a positive matrix. Boundary terms from integration by parts are assumed to vanish. """
[docs] @classmethod def doc_normalization(cls): r"""The coefficient scaling is .. math:: \hat D = \hat n / \hat x^2,\qquad \hat\rho = \hat n. No dedicated velocity normalization is used."""
[docs] @classmethod def doc_scalar_quantities(cls): r"""**The following scalars are tracked during simulation:** - No default scalar diagnostics are defined by this model."""
[docs] @classmethod def doc_discretization(cls): """Time integration is performed by the following propagators (in sequence): 1. :class:`~struphy.propagators.time_dependent_source.TimeDependentSource` (if :attr:`with_t_dep_source` is True) 2. :class:`~struphy.propagators.poisson_solve.PoissonSolve` """ doc = rf"""**1. TimeDependentSource:** {TimeDependentSource.__doc__} **2. PoissonFieldSolve:** {PoissonSolve.__doc__} """ return doc
[docs] @classmethod def doc_long_description(cls): r"""Poisson is the standalone elliptic field-solve model used for weak diffusion/Poisson problems. It is also the building block reused by other models for initial electrostatic solves."""
[docs] @classmethod def doc_examples(cls): r"""Create and initialize a Poisson model: .. code-block:: python from struphy.models import Poisson model = Poisson() model.em_fields.phi model.em_fields.source """
[docs] @classmethod def doc_use_cases(cls): r"""This model is appropriate for: - elliptic benchmark problems - electrostatic field solves with prescribed source terms - testing weak Poisson discretizations and boundary handling"""
[docs] @classmethod def doc_cannot_be_used_for(cls): r"""This model is not suitable for: - hyperbolic time-dependent wave propagation - self-consistent kinetic plasma evolution on its own - magnetic-field dynamics or full Maxwell coupling"""