Loading of periodic meshes generated by gmsh#4935
Loading of periodic meshes generated by gmsh#4935miguelcoolchips wants to merge 13 commits intofiredrakeproject:mainfrom
Conversation
PETSc's DMLocalizeCoordinates only creates cell-local (DG) coordinates
for cells that straddle the periodic boundary, but Firedrake's
reordered_coords expects every cell to have an entry. Add
_fully_localize_coordinates() which expands the partial cell coordinate
vector to cover all cells, filling non-periodic cells from the CG
vertex data via vecGetClosure.
The function is called from make_mesh_from_mesh_topology before the
existing DG coordinate path runs, so periodic Gmsh meshes now work
transparently with Mesh("file.msh") -- no special parameters needed.
Closes firedrakeproject#1246
Closes firedrakeproject#1852
Made-with: Cursor
Every solve test now asserts that the solution matches across the periodic boundaries using PointEvaluator probe pairs, rather than only checking that the solution is nonzero. Made-with: Cursor
If we are solving Poisson, it'd be preferable to remove PointEvaluator from these tests and use errornorm with a manufactured (periodic and polynomial) solution |
Replace PointEvaluator-based periodicity checks with manufactured solution approach using errornorm, following test_periodic_2d.py. - x-periodic cases (2D, 3D): polynomial manufactured solutions (quadratic in y/z, constant in x) with tight tolerances - doubly-periodic case: trigonometric manufactured solution with refined mesh (h=0.05) for adequate resolution per wavelength - All tests solve Helmholtz (-div(grad(u)) + u = f) to avoid null space issues Made-with: Cursor
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
tests/periodic/test_periodic.py
Outdated
| x and y. No boundary conditions needed. | ||
|
|
||
| Uses a wider tolerance than the polynomial tests because a | ||
| non-trivial doubly-periodic solution must be trigonometric, |
There was a problem hiding this comment.
The function at the boundary is just zero, and for the other polynomial solutions the boundary data is still just constant. If you use cosine instead of sine you could get non-constant boundary conditions.
I would suggest something like
Function(V).interpolate(cos(pi*x)*cos(pi*y))
There was a problem hiding this comment.
Good point. I changed it to cosine.
firedrake/mesh.py
Outdated
| PETSc's ``DMLocalizeCoordinates`` only creates cell-local (DG) | ||
| coordinates for cells that straddle the periodic boundary. The |
There was a problem hiding this comment.
This isn't quite true. If 'sparse localize' is false then the whole coordinate field is localised. I think DMLocalizeCoordinates should be able to do the right thing for us.
There was a problem hiding this comment.
Is it then just a matter of exposing DMSetSparseLocalize with petsc4py and setting sparseLocalize = False? Or maybe just set the option dm_sparse_localize 0?
I also realized that this implementation iterates over the elements. Definitely not the most efficient.
There was a problem hiding this comment.
@connorjward are we happy with the current implementation minus the fact that could be done in cython to avoid iterating over cells in python? I can do that next
There was a problem hiding this comment.
I've asked Matt for advice. Doing it in Cython might be good but honestly I have found that the difference is performance isn't that great.
| else: | ||
| needs_expansion = True | ||
|
|
||
| if not needs_expansion or dofs_per_cell is None: |
There was a problem hiding this comment.
I don't really understand this bit. What case is this capturing?
| For file-based periodic meshes (e.g. Gmsh) the localization | ||
| happens inside ``setFromOptions`` with ``sparseLocalize = True`` |
There was a problem hiding this comment.
This is wrong. setFromOptions is not called in that routine: https://petsc.org/release/src/dm/impls/plex/plexgmsh.c.html#DMPlexCreateGmshFromFile
| Remove this function once petsc4py exposes | ||
| ``DMSetSparseLocalize``; at that point we can set | ||
| ``sparseLocalize = False`` before ``setFromOptions`` and let | ||
| PETSc handle full localization natively. |
There was a problem hiding this comment.
I have asked Matt for input on this. Adding bindings to petsc4py is easy so I could just do that once we know the right approach.
| def test_periodic_2d_coordinates(periodic_2d_mesh): | ||
| """Mesh uses a DG coordinate element after loading.""" | ||
| elem = periodic_2d_mesh.ufl_coordinate_element() | ||
| assert "DG" in str(elem) |
There was a problem hiding this comment.
| assert "DG" in str(elem) | |
| assert elem.family() == "Discontinuous Galerkin" |
| f_coeff = (2 * pi / Lx) ** 2 + (2 * pi / Ly) ** 2 + 1.0 | ||
| f = Function(V).assign(f_coeff * u_exact) |
There was a problem hiding this comment.
| f_coeff = (2 * pi / Lx) ** 2 + (2 * pi / Ly) ** 2 + 1.0 | |
| f = Function(V).assign(f_coeff * u_exact) |
| u = TrialFunction(V) | ||
| v = TestFunction(V) | ||
| a = (inner(grad(u), grad(v)) + inner(u, v)) * dx | ||
| L = inner(f, v) * dx |
There was a problem hiding this comment.
| L = inner(f, v) * dx | |
| L = a(v, u_exact) |
| L = inner(f, v) * dx | ||
|
|
||
| uh = Function(V) | ||
| solve(a == L, uh, solver_parameters={"ksp_type": "cg"}) |
There was a problem hiding this comment.
| solve(a == L, uh, solver_parameters={"ksp_type": "cg"}) | |
| solve(a == L, uh) |
|
|
||
| l2err = sqrt(assemble(inner(uh - u_exact, uh - u_exact) * dx)) | ||
| l2norm = sqrt(assemble(inner(u_exact, u_exact) * dx)) | ||
| assert l2err / l2norm < 0.15 |
There was a problem hiding this comment.
This should give zero if u_exact is a Function in V. Additionally, it is more convenient to use errornorm and norm in the lines above
Summary
This PR adds support for loading periodic meshes created by Gmsh (
.mshfiles with$Periodicsections) into Firedrake. This addresses #1852 and #1246.Note
This PR was generated by AI. The tests seem to be working, but frankly I am not sure if the implementation is correct. Any feedback is welcome. I will do my best to address the comments.
Problem
When PETSc loads a Gmsh file with periodic boundaries via
DMPlexCreateFromFile, it callsDMLocalizeCoordinateswhich creates cell-local (DG) coordinates only for cells that straddle the periodic boundary. However, Firedrake'sreordered_coordsfunction expects every cell to have an entry in the cell coordinate vector when DG coordinates are present, causing aValueErrorreshape failure.Solution
A new function
_fully_localize_coordinates(dm)is added tofiredrake/mesh.pyand called inmake_mesh_from_mesh_topologybefore the coordinate element type is determined. It expands PETSc's partially localized coordinates to cover all cells by filling in missing entries from CG vertex coordinates viavecGetClosure.The function includes early-return guards so it is a no-op for:
getCoordinatesLocalized()returnsFalse)PeriodicRectangleMesh)Tests
Three Gmsh geometry files and corresponding pre-generated
.mshfiles (MSH 2 format) are included as test fixtures:p2d.geo/p2d.msh— 2D rectangle, periodic in xp2d_xy.geo/p2d_xy.msh— 2D rectangle, periodic in x and yp3d.geo/p3d.msh— 3D box, periodic in xEach test solves a Poisson problem and verifies solution periodicity using
PointEvaluatorat paired probe points across the periodic boundaries. All tests run in both serial and parallel (2 MPI ranks).Test plan
test_periodic_2d_coordinates— DG coordinate element is usedtest_periodic_2d_x_solve/_parallel— Poisson on x-periodic 2D meshtest_periodic_2d_xy_solve/_parallel— Poisson on doubly-periodic 2D meshtest_periodic_3d_solve/_parallel— Poisson on x-periodic 3D mesh