Deepcopy of meshes

Hi there,

Is there any way to make a deepcopy of a mesh? I naively tried

from dolfinx.generation import UnitSquareMesh
from dolfinx.cpp.mesh import CellType
import copy

comm = MPI.COMM_WORLD

no_elem = 32
mesh_II = UnitSquareMesh(comm, no_elem, no_elem, CellType.triangle)  
mesh_I = copy.deepcopy(mesh_II)

which results in

cannot pickle 'dolfinx.cpp.mesh.Mesh' object

Best,
Mario

Consider the following:

from dolfinx.mesh import create_unit_square, CellType, Mesh
from mpi4py import MPI
import numpy as np
comm = MPI.COMM_WORLD

no_elem = 5
mesh_II = create_unit_square(comm, no_elem, no_elem, CellType.triangle)

mesh_I = Mesh(mesh_II.comm, mesh_II.topology, mesh_II.geometry, mesh_II.ufl_domain())
mesh_I.geometry.x[:, 0] += 1
geom_II = mesh_II.geometry.x.copy()
geom_II[:, 0] += 1
assert(np.allclose(geom_II, mesh_I.geometry.x))
print(np.isclose(mesh_II.geometry.x, mesh_I.geometry.x))
print(mesh_II.geometry.x, mesh_I.geometry.x)

Is there a specific reason you would like to deepcopy your mesh?

1 Like
cannot import name 'create_unit_square' from 'dolfinx.mesh'

Which version of dolfinx do you use? I’m using 0.3.0 from the ubuntu 20.04 repository (the same happens within a docker container using 0.3.0).

By the way, the deepcopy of the mesh seems promising for problems where I take results of a solution on static mesh_I as input for mesh_II (different physics/equation), which is slightly deformed. For a parallel run, the sizes of the (sub-)vectors are generally not the same if I define the same mesh twice.

Im using the main branch of dolfinx.
It would be a bit more tricky to do this with the 0.3.0 version.

Hmm, the same happens using 0.3.1.dev0 (docker pull dolfinx/dolfinx:latest). What means ‘main branch’? Is there a docker container available?

Could you print the output of

import dolfinx.common
print(dolfinx.common.git_commit_hash)

Im using 9e2e2662b238d87be1378d812f29ec6e16b7410c

for 0.3.1.dev0 (docker pull dolfinx/dolfinx:latest) the output is:

8380c060f82b33f511404bc43524856edffac834

Sorry for the confusion. I didn’t update the container. Now the output is

36f8a4f4ba59ce682698e6e158a0c7abaff6fded

But back to the main topic. Your example works well, but because of some API changes, I would like to do this under 0.3.0. You mean, it’s a bit tricky. Can you please give a hint?

Thanks for your help @dokken

Probably it’s more convenient to wait for dolfinx 0.4.0 and consider all other API changes consecutively.

You basically need to call:

  1. https://github.com/FEniCS/dolfinx/blob/v0.3.0/python/dolfinx/wrappers/mesh.cpp#L280-L283
  2. Attach ufl_data: https://github.com/FEniCS/dolfinx/blob/v0.3.0/python/dolfinx/mesh.py#L126-L128

Does this still work for v0.10.0? Below, I’m seeing that mesh_II.geometry.x is modified when mesh_I.geometry.x is changed

from dolfinx.mesh import create_unit_square, CellType, Mesh
from mpi4py import MPI
import numpy as np
comm = MPI.COMM_WORLD

import dolfinx.common
print(dolfinx.common.git_commit_hash)

no_elem = 5
mesh_II = create_unit_square(comm, no_elem, no_elem, CellType.triangle)

mesh_I = Mesh(mesh_II, mesh_II.ufl_domain())
print(mesh_II.geometry.x[-1, 0])
mesh_I.geometry.x[:, 0] += 1
print(mesh_I.geometry.x[-1, 0])
print(mesh_II.geometry.x[-1, 0])
geom_II = mesh_II.geometry.x.copy()
geom_II[:, 0] += 1

assert(np.allclose(geom_II, mesh_I.geometry.x))
print(np.isclose(mesh_II.geometry.x, mesh_I.geometry.x))
print(mesh_II.geometry.x, mesh_I.geometry.x)

The output I get is

601b058a51f6a8f3cea11811048283f00855e18d
0.0
1.0
1.0
Traceback (most recent call last):
  File "/workspace-gen2/tests/test_translation_unknown.py", line 139, in <module>
    copy_mesh()
  File "/workspace-gen2/tests/test_translation_unknown.py", line 131, in copy_mesh
    assert(np.allclose(geom_II, mesh_I.geometry.x))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

Looks like this issue in v0.10.0 is resolved using mesh.create_mesh:

    from dolfinx.mesh import create_unit_square, CellType, create_mesh
    from mpi4py import MPI
    import numpy as np
    comm = MPI.COMM_WORLD

    import dolfinx.common
    print(dolfinx.common.git_commit_hash)

    no_elem = 5
    mesh_II = create_unit_square(comm, no_elem, no_elem, CellType.triangle)

    mesh_I = create_mesh(comm, mesh_II.geometry.dofmap, mesh_II.ufl_domain(), mesh_II.geometry.x)
    mesh_I.geometry.x[:, 0] += 1
    geom_II = mesh_II.geometry.x.copy()
    geom_II[:, 0] += 1

    assert(np.allclose(geom_II, mesh_I.geometry.x))
    print(np.isclose(mesh_II.geometry.x, mesh_I.geometry.x))
    print(mesh_II.geometry.x, mesh_I.geometry.x)

This is how one should do this for v0.10.0:

from dolfinx.mesh import create_unit_square, CellType, Mesh
from mpi4py import MPI
import numpy as np
import ufl
comm = MPI.COMM_WORLD

import dolfinx.common
print(dolfinx.common.git_commit_hash)

no_elem = 5
mesh_II = create_unit_square(comm, no_elem, no_elem, CellType.triangle)

# Extract correct CPP mesh constructor
if mesh_II.geometry.x.dtype == np.float32:
    _cpp_mesh = dolfinx.cpp.mesh.Mesh_float32
elif  mesh_II.geometry.x.dtype == np.float64:
    _cpp_mesh = dolfinx.cpp.mesh.Mesh_float64
# Initialize C++ mesh
cpp_mesh = _cpp_mesh(mesh_II.comm, mesh_II.topology._cpp_object, mesh_II.geometry._cpp_object)
# Initialize Python wrapper with new symbolic numbering in the `ufl.Mesh`
mesh_I = Mesh(cpp_mesh, ufl.Mesh(mesh_II.ufl_domain().ufl_coordinate_element()))

print(mesh_II.geometry.x[-1, 0])
mesh_I.geometry.x[:, 0] += 1
print(mesh_I.geometry.x[-1, 0])
print(mesh_II.geometry.x[-1, 0])
geom_II = mesh_II.geometry.x.copy()
geom_II[:, 0] += 1

assert(np.allclose(geom_II, mesh_I.geometry.x))
print(np.isclose(mesh_II.geometry.x, mesh_I.geometry.x))
print(mesh_II.geometry.x, mesh_I.geometry.x)

I’ve made a convenience function: Add copy mesh function by jorgensd · Pull Request #188 · scientificcomputing/scifem · GitHub

Note that this is strictly not a deepcopy, as the topology is shared between the two meshes.
I don’t see this as problematic, as it just reduces the overhead of recomputing connectivity and entity permutations

1 Like