How to create union of domains and define functions on subdomains?

I’d like to solve an PDE in a square with a source term g defined piecewisely. The function g(x,y) has one value in an union of 3 disks inside the square and and another value outside the union. I can find the all the intersection points of the circles and define multiple borders, but it will be quite awkward to do so. Besides, I am going to solve the problem with many such problems with 3 disks of random centers and radii. So the number of disjoint boundaries may vary from case to case. I have the following questions.

  1. Can we define union of subdomains in FreeFem? If not, I guess I would use external programs such as gmsh to create the mesh, but then I am not sure if it’s easy to identify the internal boundaries since the number of disjoint boundaries vary from one problem to another.

  2. Can we define a function that’s only supported in a subdomain? For the piecewise function g, we can define it on the whole domain using logical operators or if-else statements. But it would nicer if we can define separate functions on subdomains.

  1. You can use the + operator with meshes, read the documentation.
  2. You can define multiple fespace on different meshes.

I went over the Mesh generation part of the doc. Yes we can add meshes. I tried the following code.

  border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
  border b(t=0, 2*pi){x=0.5+cos(t); y=sin(t); label=2;}
  mesh Th1 = buildmesh(a(50));
  mesh Th2 = buildmesh(b(50));
  mesh Th = Th1+Th2;
  plot(Th);

Then the mesh looks like the following

The meshes overlap! Maybe I missed some other parts of the doc?

If there are only two disks, I can easily define the whole boundary explicitly and then mesh it. However, the situation becomes very complicated if there 3 disks, which may or may not overlap.

Ah, if the meshes overlap in a non-confirming way, then unless you can define the border explicitly, you cannot use buildmesh and the operator +.

1 Like

Thanks! It helps a lot! I guess I will resolve to gmsh then.

I managed to generate the requried mesh using gmsh as in the following code:

SetFactory("OpenCASCADE");
Rectangle (1) = {-3, -3, 0, 6, 6};
DBC() = CombinedBoundary{Surface{1};}; // Dirichlet BC
Physical Curve(1) = DBC(); // whole boundary, can use index to access different parts
Disk (2) = {0, 0, 0, 0.5};
Disk (3) = {0.8, 0, 0, 0.4};
Disk (4) = {0.3, 0.6, 0, 0.3};
v() = BooleanUnion{ Surface{2}; Delete; }{ Surface{3, 4}; Delete; }; // combine all together with conformal boundaries
w() = BooleanFragments{Surface{1}; Delete; }{Surface{v()}; Delete; };
Physical Surface(1) = w(); // whole domain, can use index to access different parts

Mesh.Algorithm = 6;
Mesh.CharacteristicLengthMin = 0.1;
Mesh.CharacteristicLengthMax = 0.1;

It has only one physical boundary, which is the boundary of the square. Then I follow this example and wrote the following code (loading .mesh or .msh file using gmshload failed, so I followed your suggestion to use DM):

load "gmsh"
load "PETSc-complex"
macro dimension()2// EOM
include "macro_ddm.idp"

// import mesh from gmsh
DM dm("3disks.msh");
if(!mpirank)
    mesh Th = dm;
broadcast(processor(0), Th);
plot(Th);

Mat<complex> A;
func Pk = P2;
createMat(Th, A, Pk);

// define the bilinear form
real k = 2.0;
macro grad(u)[dx(u), dy(u)]// EOM
varf vPb(u, v) = int2d(Th)(- (grad(u)' * grad(v)) + k^2 * u * v) + on(1, u=0);
fespace Vh(Th, Pk);
A = vPb(Vh, Vh, tgv=-1);

// define the linear form
int nRhs = 1;
complex[int, int] rhs(Vh.ndof, nRhs), sol(Vh.ndof, nRhs);
for(int i = 0; i < nRhs; ++i) {
    func g = (x^2+y^2<0.5^2) | ((x-0.8)^2+y^2<0.4^2) | ((x-0.3)^2+(y-0.6)^2<0.3^2); // will use multiple rhs later
    varf vRhs(u, v) = int2d(Th)(g * v) + on(1, u=0);
    rhs(:, i) = vRhs(0, Vh, tgv=-1);
}

// solve the system
complex[int, int] B(A.n, rhs.m), X(A.n, rhs.m);
ChangeNumbering(A, rhs, B);
set(A, sparams = "-ksp_type preonly -pc_type lu");
KSPSolve(A, B, X);
ChangeNumbering(A, sol, X, inverse = true, exchange = true);
for(int i = 0; i < rhs.m; ++i) {
    Vh u;
    u[] = sol(:, i).re;
    plotD(Th, u, cmm="u_"+i);
}

The code runs well if i use -np 1. However, when it goes to multiple processes, the the plot only show parts of the solution. So I am afraid I can’t not output the global solution.

It looks like a mixing of some of the decomposed domains.

If I use the the built-in mesh such as

border a(t=0, 2*pi) { x=cos(t); y=sin(t); label=1; }
mesh Th;
if(!mpirank)
    Th = buildmesh(a(300));
broadcast(processor(0), Th);

Then it’s ok to use multiple processes. Is there something we should take care for imported mesh when using PETSc?

Problem solved if I use the msh2 format and use gmshload:

gmsh 3disks.geo -2 -o 3disks.msh -format msh2

But the .mesh format always fail to be imported:

'/Applications/FreeFem++.app/Contents/ff-4.11/ff-petsc/r/bin/mpiexec' -np 8 /Applications/FreeFem++.app/Contents/ff-4.11/bin/FreeFem++-mpi -nw 'randomSource.edp' -ne -wg
-- FreeFem++ v4.11 (Lun 11 avr 2022 22:25:22 CEST - git v4.11)
   file : randomSource.edp
 Load: lg_fem lg_mesh lg_mesh3 eigenvalue parallelempi 
 load: init metis (v  5 )
 sizestack + 1024 =10352  ( 9328 )

mshptg bug in number of points 0 > 0 == max nb points 
 Sorry Error build delaunay triangle   error = 1
  current line = 10 mpirank 0 / 8
Exec error : Error mshptg8_
   -- number :1
Exec error : Error mshptg8_
   -- number :1
 err code 8 ,  mpirank 0

In the YouTube video I remembered you suggest use the .mesh rather than the .msh format.

This is wrong:

DM dm("3disks.msh");
if(!mpirank)
    mesh Th = dm;
broadcast(processor(0), Th);
plot(Th);

You need to do:

mesh Th;
if(!mpirank) {
    DM dm("3disks.msh", communicator = mpiCommSelf);
    Th = dm;
}
broadcast(processor(0), Th);
plot(Th);
1 Like

This works! Thank you for your prompt reply!

I get a warning:

Warning: -- Your set of boundary condition is incompatible with the mesh label.

However, from my gmsh file I can see there is only one boundary which has label 1. I used your code below and doubled checked the label is 1:

int[int] lab = labels(Th);
varf onG(u, v) = on(lab, u = label);
fespace Vh(Th, P1);
Vh u;
u[] = onG(0, Vh, tgv = -1);
plot(u, wait=1);

This warning appears when you have floating subdomains, i.e., subdomains that do not touch the boundary. It’s benign.

1 Like