Skip to content

Parallel tests #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ on:

jobs:
test_suite:
uses: mesh-adaptation/docs/.github/workflows/reusable_test_suite.yml@main
uses: mesh-adaptation/docs/.github/workflows/reusable_test_suite.yml@parallel_dynamic_tests
31 changes: 26 additions & 5 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
"""
Global pytest configuration.

**Disclaimer: some functions copied from firedrake/src/tests/conftest.py
"""

import numpy as np
import pytest
from pyop2.mpi import COMM_WORLD

np.random.seed(0)


def pytest_configure(config):
"""
Register an additional marker.

**Disclaimer: copied from firedrake/src/tests/conftest.py
"""
config.addinivalue_line(
"markers",
"slow: mark test as slow to run",
"parallel_dynamic: mark test to run with nprocs equal to the current MPI size",
)


def pytest_collection_modifyitems(config, items):
"""
Replace ``parallel_dynamic`` markers with mpi-pytest's ``parallel(nprocs=N)``
marker, where N is the current MPI size determined from the ``mpiexec -n N`` command
line argument.
"""
rank_size = COMM_WORLD.Get_size()

for item in items:
# Check if a test is marked with the parallel_dynamic marker
markers = [
marker for marker in item.own_markers if marker.name == "parallel_dynamic"
]

if markers:
# Remove parallel_dynamic markers
for marker in markers:
item.own_markers.remove(marker)

# Add mpi-pytest's parallel marker with current process count
item.add_marker(pytest.mark.parallel(nprocs=rank_size))
23 changes: 23 additions & 0 deletions test/test_monge_ampere.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from unittest.mock import MagicMock

import numpy as np
import pytest
import ufl
from firedrake.assemble import assemble
from firedrake.bcs import DirichletBC, EquationBC
Expand Down Expand Up @@ -58,16 +59,19 @@ class TestExceptions(BaseClasses.TestMongeAmpere):
Unit tests for exceptions raised by Monge-Ampère movers.
"""

@pytest.mark.parallel_dynamic
def test_method_valueerror(self):
with self.assertRaises(ValueError) as cm:
MongeAmpereMover(self.dummy_mesh, self.dummy_monitor, method="method")
self.assertEqual(str(cm.exception), "Method 'method' not recognised.")

@pytest.mark.parallel_dynamic
def test_no_monitor_valueerror(self):
with self.assertRaises(ValueError) as cm:
MongeAmpereMover(self.dummy_mesh, None)
self.assertEqual(str(cm.exception), "Please supply a monitor function.")

@pytest.mark.parallel_dynamic
def test_1d_quasi_newton_valueerror(self):
mesh = self.mesh(dim=1)
with self.assertRaises(NotImplementedError) as cm:
Expand All @@ -88,6 +92,7 @@ def test_maxiter_convergenceerror(self, method):
self.assertEqual(str(cm.exception), "Solver failed to converge in 1 iteration.")

@parameterized.expand([(True,), (False,)])
@pytest.mark.parallel_dynamic
def test_divergence_convergenceerror(self, raise_errors):
"""
Test that divergence of the mesh mover raises a :class:`~.ConvergenceError` if
Expand All @@ -102,6 +107,7 @@ def test_divergence_convergenceerror(self, raise_errors):
self.assertEqual(str(cm.exception), "Solver diverged after 1 iteration.")

@parameterized.expand([("phi",), ("H",)])
@pytest.mark.parallel_dynamic
def test_initial_guess_valueerror(self, var):
mesh = self.mesh(n=2)
fs_constructor = {"phi": FunctionSpace, "H": TensorFunctionSpace}[var]
Expand All @@ -110,6 +116,7 @@ def test_initial_guess_valueerror(self, var):
MongeAmpereMover(mesh, ring_monitor, **kwargs)
self.assertEqual(str(cm.exception), "Need to initialise both phi *and* H.")

# @pytest.mark.parallel_dynamic
def test_non_straight_boundary_valueerror(self):
mesh = self.mesh(dim=2, n=3)
mesh.coordinates.dat.data_with_halos[1][0] -= 0.25
Expand All @@ -119,6 +126,7 @@ def test_non_straight_boundary_valueerror(self):
msg = "Boundary segment '1' is not linear."
self.assertEqual(str(cm.exception), msg)

# @pytest.mark.parallel_dynamic
def test_non_flat_plane_valueerror(self):
mesh = self.mesh(dim=3, n=3)
mesh.coordinates.dat.data_with_halos[1][0] -= 0.25
Expand All @@ -128,6 +136,7 @@ def test_non_flat_plane_valueerror(self):
msg = "Boundary segment '1' is not planar."
self.assertEqual(str(cm.exception), msg)

@pytest.mark.parallel_dynamic
def test_invalid_plane_valuerror(self):
mesh = self.mesh(dim=3, n=1)
mesh.coordinates.dat.data_with_halos[:][:] = 0.0
Expand All @@ -137,6 +146,7 @@ def test_invalid_plane_valuerror(self):
msg = "Could not determine a plane for the provided points."
self.assertEqual(str(cm.exception), msg)

# @pytest.mark.parallel_dynamic
def test_curved_notimplementederror(self):
coords = Function(VectorFunctionSpace(UnitTriangleMesh(), "CG", 2))
coords.interpolate(coords.function_space().mesh().coordinates)
Expand All @@ -145,20 +155,23 @@ def test_curved_notimplementederror(self):
msg = "MongeAmpereMover_Relaxation not implemented on curved meshes."
self.assertEqual(str(cm.exception), msg)

# @pytest.mark.parallel_dynamic
def test_tangling_valueerror(self):
mover = MongeAmpereMover(self.mesh(2, n=3), ring_monitor)
mover.xi.dat.data[3] += 0.2
with self.assertRaises(ValueError) as cm:
mover.move()
self.assertEqual(str(cm.exception), "Mesh has 1 tangled element.")

@pytest.mark.parallel_dynamic
def test_periodic_plex_valueerror(self):
mover = MongeAmpereMover(self.mesh(1, n=3, periodic=True), const_monitor)
with self.assertRaises(ValueError) as cm:
mover._update_plex_coordinates()
msg = "Cannot update DMPlex coordinates for periodic meshes."
self.assertEqual(str(cm.exception), msg)

@pytest.mark.parallel_dynamic
def test_fix_invalid_segment_valueerror(self):
with self.assertRaises(ValueError) as cm:
MongeAmpereMover(self.mesh(1), const_monitor, fixed_boundary_segments=[-1])
Expand All @@ -180,6 +193,7 @@ class TestMonitor(BaseClasses.TestMongeAmpere):
(3, "quasi_newton"),
]
)
@pytest.mark.parallel_dynamic
def test_uniform_monitor(self, dim, method):
"""
Test that the mesh mover converges in one iteration for a constant monitor
Expand All @@ -204,6 +218,7 @@ def test_uniform_monitor(self, dim, method):
@parameterized.expand(
[(2, "relaxation"), (2, "quasi_newton"), (3, "relaxation"), (3, "quasi_newton")]
)
@pytest.mark.parallel_dynamic
def test_continue(self, dim, method):
"""
Test that providing a good initial guess benefits the solver.
Expand All @@ -230,6 +245,7 @@ def test_continue(self, dim, method):
@parameterized.expand(
[(2, "relaxation"), (2, "quasi_newton"), (3, "relaxation"), (3, "quasi_newton")]
)
# @pytest.mark.parallel_dynamic
def test_change_monitor(self, dim, method):
"""
Test that the mover can handle changes to the monitor function, such as would
Expand Down Expand Up @@ -259,6 +275,7 @@ class TestBCs(BaseClasses.TestMongeAmpere):
Unit tests for boundary conditions of Monge-Ampère movers.
"""

@pytest.mark.parallel_dynamic
def _test_boundary_preservation(self, mesh, method, fixed_boundaries):
bnd = assemble(Constant(1.0) * ufl.ds(domain=mesh))
coord_space = mesh.coordinates.function_space()
Expand Down Expand Up @@ -293,6 +310,7 @@ def _test_boundary_preservation(self, mesh, method, fixed_boundaries):
(3, "quasi_newton"),
]
)
@pytest.mark.parallel_dynamic
def test_periodic(self, dim, method):
"""
Test that periodic unit domains are not given boundary conditions by the
Expand All @@ -310,6 +328,7 @@ def test_periodic(self, dim, method):
# Check that the variational problem does not have boundary conditions
self.assertTrue(len(mover._l2_projector._problem.bcs) == 0)

@pytest.mark.parallel_dynamic
def test_initial_guess_valueerror(self):
mesh = self.mesh(2, n=2)
phi_init = Function(FunctionSpace(mesh, "CG", 1))
Expand All @@ -336,6 +355,7 @@ def test_initial_guess_valueerror(self):
(3, "quasi_newton", []),
]
)
# @pytest.mark.parallel_dynamic
def test_boundary_preservation_axis_aligned(self, dim, method, fixed_boundaries):
"""
Test that boundaries of unit domains are preserved by the Monge-Ampère movers.
Expand Down Expand Up @@ -370,6 +390,7 @@ def test_boundary_preservation_axis_aligned(self, dim, method, fixed_boundaries)
(3, "quasi_newton", []),
]
)
# @pytest.mark.parallel_dynamic
def test_boundary_preservation_non_axis_aligned(
self, dim, method, fixed_boundaries
):
Expand Down Expand Up @@ -451,6 +472,7 @@ class TestMisc(BaseClasses.TestMongeAmpere):
(3, "quasi_newton"),
]
)
@pytest.mark.parallel_dynamic
def test_continue(self, dim, method):
"""
Test that providing a good initial guess benefits the solver.
Expand Down Expand Up @@ -485,6 +507,7 @@ def test_continue(self, dim, method):
(3, "quasi_newton"),
]
)
@pytest.mark.parallel_dynamic
def test_coordinate_update(self, dim, method):
mesh = self.mesh(dim=dim, n=2)
xyz = list(ufl.SpatialCoordinate(mesh))
Expand Down
Loading