Skip to content

Commit

Permalink
Merge branch 'main' into 55_on_the_fly
Browse files Browse the repository at this point in the history
  • Loading branch information
ddundo committed Sep 15, 2024
2 parents e652186 + 2088668 commit 81ce258
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 95 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/citation_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: 'Citation Check'

on:
# Run when a branch that modifies a .cff file is merged into main
push:
branches:
- main
paths:
- '**/*.cff'

# Run when commits are pushed to a branch or a PR that modifies a .cff file
pull_request:
paths:
- '**/*.cff'

# Allow manual triggerings
workflow_dispatch:

jobs:
citation_check:
name: 'Check Citation'
runs-on: ubuntu-latest

steps:
- name: 'Check out the repo'
uses: actions/checkout@v4

- name: 'Setup Python'
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: 'Check citation'
run: |
pip install cffconvert
make check_citation
30 changes: 20 additions & 10 deletions .github/workflows/test_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,22 @@ jobs:
name: 'Test suite'
runs-on: ubuntu-latest
container:
image: jwallwork/firedrake-parmmg:latest
image: ghcr.io/mesh-adaptation/firedrake-parmmg:latest
options: --user root
steps:
- uses: actions/checkout@v2
- name: 'Check out the repo'
id: checkout
uses: actions/checkout@v4

- name: 'Determine files differing from target branch'
id: changed-files
if: ${{ github.event_name != 'push' && github.ref != 'refs/heads/main' }}
uses: tj-actions/changed-files@v44
with:
files: |
.github/workflows/test_suite.yml
*.py
*.cxx
- name: 'Cleanup'
if: ${{ always() }}
Expand All @@ -37,33 +49,31 @@ jobs:
rm -rf build
- name: 'Setup Python'
if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || (steps.changed-files.outcome == 'success' && steps.changed-files.outputs.any_changed == 'true') || github.event_name == 'schedule' }}
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: 'Install Animate'
if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || (steps.changed-files.outcome == 'success' && steps.changed-files.outputs.any_changed == 'true') || github.event_name == 'schedule' }}
run: |
. /home/firedrake/firedrake/bin/activate
python -m pip uninstall -y animate
python -m pip uninstall -y goalie
python -m pip install -e .
- name: 'Check citation'
run: |
. /home/firedrake/firedrake/bin/activate
make check_citation
- name: 'Lint'
if: ${{ always() }}
if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || (steps.changed-files.outcome == 'success' && steps.changed-files.outputs.any_changed == 'true') || github.event_name == 'schedule' }}
run: |
. /home/firedrake/firedrake/bin/activate
make lint
- name: 'Test Animate'
if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || (steps.changed-files.outcome == 'success' && steps.changed-files.outputs.any_changed == 'true') || github.event_name == 'schedule' }}
run: |
. /home/firedrake/firedrake/bin/activate
export GITHUB_ACTIONS_TEST_RUN=1
python $(which firedrake-clean)
export ANIMATE_CHECKPOINT_DIR=$(pwd)/.checkpoints
python -m coverage erase
python -m coverage run --source=animate -m pytest -v --durations=20 test
python -m coverage report
python -m coverage report
6 changes: 5 additions & 1 deletion animate/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,12 @@ def set_parameters(self, metric_parameters=None):
self._metric_parameters.update(mp)
self._variable_parameters.update(vp)

mp = self._metric_parameters.copy()
if mp.get("dm_plex_metric_p") == np.inf:
mp["dm_plex_metric_p"] = 1.79769e308

# Pass parameters to PETSc
with OptionsManager(self._metric_parameters, "").inserted_options():
with OptionsManager(mp, "").inserted_options():
self._plex.metricSetFromOptions()
if self._plex.metricIsUniform():
raise NotImplementedError(
Expand Down
163 changes: 79 additions & 84 deletions test/test_adapt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Unit tests for invoking mesh adaptation tools Mmg2d, Mmg3d, and ParMmg.
"""

import os

import numpy as np
Expand All @@ -11,60 +15,57 @@ def load_mesh(fname):
"""
Load a mesh in gmsh format.
:param fname: file name, without the .msh extension
:arg fname: file name, without the .msh extension
:type fname: :class:`str`
:return: the mesh
:rtype: :class:`firedrake.mesh.MeshGeometry`
"""
venv = os.environ.get("VIRTUAL_ENV")
mesh_dir = os.path.join(venv, "src", "firedrake", "tests", "meshes")
return Mesh(os.path.join(mesh_dir, fname + ".msh"))


def try_adapt(mesh, metric, **kwargs):
def try_adapt(mesh, metric, serialise=None):
"""
Attempt to invoke PETSc's mesh adaptation functionality
and xfail if it is not installed.
Attempt to invoke PETSc's mesh adaptation functionality and xfail if it is not
installed.
:param mesh: the current mesh
:param metric: the :class:`RiemannianMetric` instance
:return: the adapted mesh w.r.t. the metric
:arg mesh: mesh to be adapted
:type mesh: :class:`firedrake.mesh.MeshGeometry`
:arg metric: Riemannian metric to adapt with respect to
:type metric: :class:`animate.metric.RiemannianMetric`
:return: mesh adapted according to the metric
:rtype: :class:`firedrake.mesh.MeshGeometry`
"""
try:
return adapt(mesh, metric, **kwargs)
return adapt(mesh, metric, serialise=serialise)
except PETSc.Error as exc:
if exc.ierr == 63:
pytest.xfail("No mesh adaptation tools are installed")
else:
raise Exception(f"PETSc error code {exc.ierr}") from exc


@pytest.mark.parallel(nprocs=2)
def test_adapt_2dparallel_error():
"""
Ensure adapting a 2D mesh in parallel raises a ``ValueError`` with the message
"Parallel adaptation is only supported in 3D."
"""
mesh = uniform_mesh(2)
mp = {
"dm_plex_metric": {
"no_insert": None,
"no_move": None,
"no_swap": None,
"no_surf": None,
}
}
metric = uniform_metric(mesh, metric_parameters=mp)
with pytest.raises(ValueError) as e_info:
try_adapt(mesh, metric, serialise=False)
try_adapt(mesh, uniform_metric(mesh), serialise=False)
assert str(e_info.value) == "Parallel adaptation is only supported in 3D."


@pytest.fixture(params=[2, 3])
def dim(request):
return request.param


@pytest.fixture(params=[True, False])
def serialise(request):
return request.param


def test_no_adapt(dim, **kwargs):
@pytest.mark.parametrize(
"dim,serialise",
[(2, True), (3, True), (3, False)],
ids=["mmg2d", "mmg3d", "ParMmg"],
)
def test_no_adapt(dim, serialise):
"""
Test that we can turn off mesh adaptation operations.
Ensure mesh adaptation operations can be turned off.
"""
mesh = uniform_mesh(dim)
dofs = mesh.coordinates.vector().gather().shape
Expand All @@ -77,40 +78,29 @@ def test_no_adapt(dim, **kwargs):
}
}
metric = uniform_metric(mesh, metric_parameters=mp)
newmesh = try_adapt(mesh, metric, **kwargs)
newmesh = try_adapt(mesh, metric, serialise=serialise)
assert newmesh.coordinates.vector().gather().shape == dofs


@pytest.mark.parallel(nprocs=2)
def test_no_adapt_2d_parallel():
"""
Test that we can turn off mesh adaptation operations in 2D.
"""
assert COMM_WORLD.size == 2
test_no_adapt(2, serialise=True)


@pytest.mark.parallel(nprocs=2)
def test_no_adapt_3d_parallel(serialise):
@pytest.mark.parametrize(
"dim,serialise", [(3, True), (3, False)], ids=["mmg3d", "ParMmg"]
)
def test_no_adapt_parallel(dim, serialise):
"""
Test that we can turn off mesh adaptation operations in 3D.
Ensure mesh adaptation operations can be turned off when running in parallel.
"""
assert COMM_WORLD.size == 2
test_no_adapt(3, serialise=serialise)
test_no_adapt(dim, serialise=serialise)


@pytest.mark.parametrize(
"meshname",
[
"annulus",
"cell-sets",
"square_with_embedded_line",
],
["annulus", "cell-sets", "square_with_embedded_line"],
)
def test_preserve_cell_tags_2d(meshname):
"""
Test that cell tags are preserved
after mesh adaptation.
Ensure cell tags are preserved after mesh adaptation.
"""
mesh = load_mesh(meshname)
metric = uniform_metric(mesh)
Expand All @@ -129,15 +119,11 @@ def test_preserve_cell_tags_2d(meshname):

@pytest.mark.parametrize(
"meshname",
[
"annulus",
"circle_in_square",
],
["annulus", "circle_in_square"],
)
def test_preserve_facet_tags_2d(meshname):
"""
Test that facet tags are preserved
after mesh adaptation.
Ensure facet tags are preserved after mesh adaptation.
"""
mesh = load_mesh(meshname)
metric = uniform_metric(mesh)
Expand All @@ -155,12 +141,16 @@ def test_preserve_facet_tags_2d(meshname):
assert np.isclose(bnd, newbnd), f"Length of arc {tag} not preserved"


def test_adapt_3d(**kwargs):
@pytest.mark.parametrize(
"dim,serialise",
[(2, True)], # [(2, True), (3, True), (3, False)], # FIXME: hang (#136)
ids=["mmg2d"], # ids=["mmg2d", "mmg3d, ParMmg"],
)
def test_adapt(dim, serialise):
"""
Test that we can successfully invoke
Mmg3d and that it changes the DoF count.
Test that we can successfully invoke Mmg and that it changes the DoF count.
"""
mesh = uniform_mesh(3)
mesh = uniform_mesh(dim)
dofs = mesh.coordinates.vector().gather().shape
mp = {
"dm_plex_metric": {
Expand All @@ -169,45 +159,46 @@ def test_adapt_3d(**kwargs):
}
}
metric = uniform_metric(mesh, metric_parameters=mp)
newmesh = try_adapt(mesh, metric, **kwargs)
newmesh = try_adapt(mesh, metric, serialise=serialise)
assert newmesh.coordinates.vector().gather().shape != dofs


@pytest.mark.parallel(nprocs=2)
def test_adapt_parallel_2d_np2(serialise):
"""
Test that we can successfully invoke Mmg for a 2D run with 2 MPI processes and that
it changes the DoF count.
"""
assert COMM_WORLD.size == 2
test_adapt_3d(serialise=True)


@pytest.mark.parallel(nprocs=2)
def test_adapt_parallel_3d_np2(serialise):
@pytest.mark.parametrize(
"dim,serialise",
[(2, True)], # [(2, True), (3, True), (3, False)], # FIXME: hang (#136)
ids=["mmg2d"], # ["mmg2d", "mmg3d, ParMmg"],
)
def test_adapt_parallel_np2(dim, serialise):
"""
Test that we can successfully invoke [Par]Mmg for a 3D run with 2 MPI processes and
that it changes the DoF count.
Test that we can successfully invoke [Par]Mmg with 2 MPI processes and that it
changes the DoF count.
"""
assert COMM_WORLD.size == 2
test_adapt_3d(serialise=serialise)
test_adapt(dim, serialise=serialise)


@pytest.mark.parallel(nprocs=3)
def test_adapt_parallel_3d_np3(serialise):
@pytest.mark.parametrize(
"dim,serialise",
[(2, True)], # [(2, True), (3, True), (3, False)], # FIXME: hang (#136)
ids=["mmg2d"], # ["mmg2d", "mmg3d, ParMmg"],
)
def test_adapt_parallel_np3(dim, serialise):
"""
Test that we can successfully invoke [Par]Mmg for a 3D run with 3 MPI processes and
that it changes the DoF count.
Test that we can successfully invoke [Par]Mmg with 3 MPI processes and that it
changes the DoF count.
"""
assert COMM_WORLD.size == 3
if not serialise:
pytest.skip("FIXME: test currently hangs") # FIXME: (#136)
test_adapt_3d(serialise=serialise)
test_adapt(dim, serialise=serialise)


# @pytest.mark.parametrize("dim", [2, 3], ids=["mmg2d", "mmg3d"]) # FIXME: hang (#136)
@pytest.mark.parametrize("dim", [2], ids=["mmg2d"])
def test_enforce_spd_h_min(dim):
"""
Tests that the :meth:`enforce_spd` method applies minimum magnitudes as expected.
Tests that :meth:`animate.metric.RiemannianMetric.enforce_spd` applies minimum
magnitudes as expected.
"""
mesh = uniform_mesh(dim)
h = 0.1
Expand All @@ -220,9 +211,12 @@ def test_enforce_spd_h_min(dim):
assert newmesh.coordinates.vector().gather().shape[0] < num_vertices


# @pytest.mark.parametrize("dim", [2, 3], ids=["mmg2d", "mmg3d"]) # FIXME: hang (#136)
@pytest.mark.parametrize("dim", [2], ids=["mmg2d"])
def test_enforce_spd_h_max(dim):
"""
Tests that the :meth:`enforce_spd` method applies maximum magnitudes as expected.
Tests that :meth:`animate.metric.RiemannianMetric.enforce_spd` applies maximum
magnitudes as expected.
"""
mesh = uniform_mesh(dim)
h = 0.1
Expand All @@ -235,5 +229,6 @@ def test_enforce_spd_h_max(dim):
assert newmesh.coordinates.vector().gather().shape[0] > num_vertices


# Debugging
if __name__ == "__main__":
test_no_adapt_2d_parallel()
test_adapt(3)

0 comments on commit 81ce258

Please sign in to comment.