From a7d6254cb1dadb546ac945d5114dbbbac30688d1 Mon Sep 17 00:00:00 2001 From: daubners Date: Mon, 27 Apr 2026 10:46:22 +0100 Subject: [PATCH] generalize CH from periodic --- docs/notebooks/01-using-solvers.ipynb | 10 +++++----- docs/notebooks/04-parameter-estimation.ipynb | 6 +++--- evoxels/boundary_conditions.py | 6 +++--- evoxels/precompiled_solvers/cahn_hilliard.py | 4 ++-- evoxels/problem_definition.py | 3 ++- tests/test_inversion.py | 6 +++--- tests/test_rhs.py | 4 ++-- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/notebooks/01-using-solvers.ipynb b/docs/notebooks/01-using-solvers.ipynb index 0507899..5c7147d 100644 --- a/docs/notebooks/01-using-solvers.ipynb +++ b/docs/notebooks/01-using-solvers.ipynb @@ -128,7 +128,7 @@ "source": [ "## Assembling custom solvers\n", "\n", - "We can unwrap what is actually happening under the hood a bit more by manually assembling this solver based on the predefined problem definition, timestepper and solver framework. The `TimeDependentSolver` is a pre-defined class which initialises a scalar field and solves a time-dependent PDE (as opposed to a steady-state problem). This is done by combining a backend (`torch/jax`) with a timestepper class (e.g. `PseudoSpectralIMEX`) and a problem class which defines the numerical discretisation of the right-hand side of a PDE (e.g. `PeriodicCahnHilliard`)." + "We can unwrap what is actually happening under the hood a bit more by manually assembling this solver based on the predefined problem definition, timestepper and solver framework. The `TimeDependentSolver` is a pre-defined class which initialises a scalar field and solves a time-dependent PDE (as opposed to a steady-state problem). This is done by combining a backend (`torch/jax`) with a timestepper class (e.g. `PseudoSpectralIMEX`) and a problem class which defines the numerical discretisation of the right-hand side of a PDE (e.g. `CahnHilliard`)." ] }, { @@ -148,7 +148,7 @@ } ], "source": [ - "from evoxels.problem_definition import PeriodicCahnHilliard\n", + "from evoxels.problem_definition import CahnHilliard\n", "from evoxels.solvers import TimeDependentSolver\n", "from evoxels.timesteppers import PseudoSpectralIMEX\n", "\n", @@ -160,7 +160,7 @@ " vf, # VoxelFields object\n", " 'c2', # Name of initial field\n", " 'torch', # Backend\n", - " problem_cls = PeriodicCahnHilliard, # Problem definition\n", + " problem_cls = CahnHilliard, # Problem definition\n", " timestepper_cls = PseudoSpectralIMEX, # Timestepping scheme\n", " device='cuda',\n", " )\n", @@ -244,7 +244,7 @@ "\n", "solver = TimeDependentSolver(\n", " vf, 'c3', 'torch', device='cuda',\n", - " problem_cls = PeriodicCahnHilliard,\n", + " problem_cls = CahnHilliard,\n", " timestepper_cls = PseudoSpectralIMEX\n", " )\n", "\n", @@ -306,7 +306,7 @@ "\n", "solver = TimeDependentSolver(\n", " vf, 'c4', 'torch', device='cuda',\n", - " problem_cls = PeriodicCahnHilliard,\n", + " problem_cls = CahnHilliard,\n", " timestepper_cls = ForwardEuler\n", " )\n", "\n", diff --git a/docs/notebooks/04-parameter-estimation.ipynb b/docs/notebooks/04-parameter-estimation.ipynb index e0528d7..4a45c5d 100644 --- a/docs/notebooks/04-parameter-estimation.ipynb +++ b/docs/notebooks/04-parameter-estimation.ipynb @@ -62,7 +62,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We create an `InversionModel` for the ``PeriodicCahnHilliard`` problem which sets up the voxel grid and the parameters of the PDE." + "We create an `InversionModel` for the ``CahnHilliard`` problem which sets up the voxel grid and the parameters of the PDE." ] }, { @@ -71,10 +71,10 @@ "metadata": {}, "outputs": [], "source": [ - "from evoxels.problem_definition import PeriodicCahnHilliard\n", + "from evoxels.problem_definition import CahnHilliard\n", "fixed_problem_kwargs={\"mu_hom\": None}\n", "pos_params = [\"D\", \"eps\"]\n", - "model = evo.InversionModel(vf, PeriodicCahnHilliard, pos_params, fixed_problem_kwargs)" + "model = evo.InversionModel(vf, CahnHilliard, pos_params, fixed_problem_kwargs)" ] }, { diff --git a/evoxels/boundary_conditions.py b/evoxels/boundary_conditions.py index 12aa901..ed4bf8a 100644 --- a/evoxels/boundary_conditions.py +++ b/evoxels/boundary_conditions.py @@ -46,11 +46,11 @@ def pad_fft_periodic(self, field): def pad_fft_dirichlet_periodic(self, field): """Pad with inverse of flipped field in x direction.""" - return self.vg.concatenate((field, -self.vg.lib.flip(field, [0])), 1) + return self.vg.concatenate((field, -self.vg.lib.flip(field, [1])), 1) def pad_fft_zero_flux_periodic(self, field): """Pad with flipped field in x direction.""" - return self.vg.concatenate((field, self.vg.lib.flip(field, [0])), 1) + return self.vg.concatenate((field, self.vg.lib.flip(field, [1])), 1) def trim_boundary_nodes(self, field): return field @@ -114,7 +114,7 @@ def pad_fft_periodic(self, field): def pad_fft_dirichlet_periodic(self, field): """Pad with inverse of flipped field in x direction.""" bc = self.vg.lib.zeros_like(field[:,0:1]) - return self.vg.concatenate((field, bc, -self.vg.lib.flip(field, [0]), bc), 1) + return self.vg.concatenate((field, bc, -self.vg.lib.flip(field, [1]), bc), 1) def pad_fft_zero_flux_periodic(self, field): """Pad with flipped field in x direction.""" diff --git a/evoxels/precompiled_solvers/cahn_hilliard.py b/evoxels/precompiled_solvers/cahn_hilliard.py index 2f60baf..e36f7e1 100644 --- a/evoxels/precompiled_solvers/cahn_hilliard.py +++ b/evoxels/precompiled_solvers/cahn_hilliard.py @@ -1,4 +1,4 @@ -from ..problem_definition import PeriodicCahnHilliard +from ..problem_definition import CahnHilliard from ..solvers import TimeDependentSolver from ..timesteppers import PseudoSpectralIMEX from typing import Callable @@ -26,7 +26,7 @@ def run_cahn_hilliard_solver( voxelfields, fieldnames, backend, - problem_cls = PeriodicCahnHilliard, + problem_cls = CahnHilliard, timestepper_cls = PseudoSpectralIMEX, device=device, ) diff --git a/evoxels/problem_definition.py b/evoxels/problem_definition.py index cd7a393..373adb3 100644 --- a/evoxels/problem_definition.py +++ b/evoxels/problem_definition.py @@ -281,12 +281,13 @@ def rhs(self, t, u): @dataclass -class PeriodicCahnHilliard(SemiLinearODE): +class CahnHilliard(SemiLinearODE): vg: VoxelGrid eps: float = 3.0 D: float = 1.0 mu_hom: Callable | None = None A: float = 0.25 + bc: tuple = ('periodic', 'periodic', 'periodic') _fourier_symbol: Any = field(init=False, repr=False) def __post_init__(self): diff --git a/tests/test_inversion.py b/tests/test_inversion.py index 83b15f7..a31e520 100644 --- a/tests/test_inversion.py +++ b/tests/test_inversion.py @@ -5,13 +5,13 @@ import pytest import evoxels as evo from evoxels.inversion import InversionModel -from evoxels.problem_definition import PeriodicCahnHilliard +from evoxels.problem_definition import CahnHilliard diffrax_available = importlib.util.find_spec("diffrax") is not None def test_train_validates_sequence_length(): vf = evo.VoxelFields((4, 4, 4)) - model = InversionModel(vf, PeriodicCahnHilliard) + model = InversionModel(vf, CahnHilliard) data = { "ts": np.array([0.0, 1.0, 2.0]), "ys": np.zeros((3, 4, 4, 4), dtype=np.float32), @@ -27,7 +27,7 @@ def test_inversion_forward_solve_constant_solution(): vf = evo.VoxelFields((4, 4, 4)) vf.add_field('c', np.full((4, 4, 4), 0.5, dtype=np.float32)) - model = InversionModel(vf, PeriodicCahnHilliard, {'eps': 3.0}) + model = InversionModel(vf, CahnHilliard, {'eps': 3.0}) saveat = dfx.SaveAt(ts=jnp.array([0.0, 0.1, 0.2], dtype=jnp.float32)) sol = model.forward_solve({'D': 1.0}, 'c', saveat, dt0=0.1, verbose=False) assert sol.shape == (3, 4, 4, 4) diff --git a/tests/test_rhs.py b/tests/test_rhs.py index 683a95c..c31effe 100644 --- a/tests/test_rhs.py +++ b/tests/test_rhs.py @@ -2,7 +2,7 @@ import sympy as sp import sympy.vector as spv -from evoxels.problem_definition import PeriodicCahnHilliard, \ +from evoxels.problem_definition import CahnHilliard, \ TwoPhaseAllenCahn, CoupledReactionDiffusion, ReactionDiffusionSBM, \ MultiPhaseAllenCahn from evoxels.utils import rhs_convergence_test @@ -12,7 +12,7 @@ def test_Cahn_Hilliard_rhs(): _ ,_ , slope, order = rhs_convergence_test( - ODE_class = PeriodicCahnHilliard, + ODE_class = CahnHilliard, problem_kwargs = {'eps': 3.0, 'D': 1.0, 'A': 0.25}, test_function = test_fun_ch, convention = 'cell_center',