Recursion and Recursively Defined Geometry Objects
Methods can support recursion by having a function calling itself in a recursive loop. The following examples create recursive CAD geometry models of a Sierpinski carpet in 2D and a Menger sponge in 3D.
Sierpinski Carpet
The following method, create_carpet, initiates the recursion to a certain recursive level according to a user-defined Parameter mslevel, defined under Global Definitions in the Model Builder. It sets a limit at 5 levels in order to avoid creating an exceedingly large geometry. The method assumes that you have created a 2D Component and that you have declared an integer scalar variable counter.
int level = (int) model.param().evaluate("mslevel");
if (level < 1)
  error("Carpet level needs to be at least 1.");
if (level > 5)
  error("Carpet level needs to be at most 5.");
counter = 0;
model.component("comp1").geom("geom1").feature().clear();
model.component("comp1").geom("geom1").autoRebuild("off");
double cx0 = 0, cy0 = 0;
double si0 = 1;
carpet(level, cx0, cy0, si0);
model.component("comp1").geom("geom1").runPre("fin");
The method create_carpet in turn calls the main recursive function carpet, listed below, using four input arguments for the recursion level, the center x- and y-coordinates, and the current side length.
The input arguments are defined in the method’s Settings window, as shown below.
The code for the method carpet is shown below.
int l = level;
double posx, posy, si1;
String strix;
int l1;
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    posx = cx+i*si-si;
    posy = cy+j*si-si;
    strix = toString(i)+toString(j);
    if ((Math.abs((i-1))+Math.abs((j-1))) > 0) {
      if (l == 1) {
        counter = counter+1;
        model.component("comp1").geom("geom1").create("sq"+strix+"C"+          toString(counter), "Square");
        with(model.component("comp1").geom("geom1").feature("sq"+strix+"C"+          toString(counter)));
          set("base", "center");
          set("size", new double[]{si});
          set("pos", new double[]{posx, posy});
        endwith();
        model.component("comp1").geom("geom1").feature("sq"+strix+"C"+          toString(counter)).label("Square"+strix+"C"+toString(counter));
      } else {
        l1 = l-1;
        si1 = si/3;
        carpet(l1, posx, posy, si1);
      }
    }
  }
}
The figure below shows the resulting geometry for a level-3 carpet.
Note that the number of square geometry objects defined for the level 3 carpet is 512. The number of geometry objects for a level-N carpet is 8N. This means that for the maximum level 5 set by the method create_carpet, the resulting geometry has 32,768 geometry objects.
Menger Sponge
The methods for the Sierpinski carpet readily generalize to 3D. However, in 3D, you need to be careful not to generate more objects than your computer can handle. The method assumes that you have created a 3D Component and that you have declared an integer scalar variable counter.
The following method, create_sponge, initiates the recursion.
int level = (int) model.param().evaluate("mslevel");
if (level < 1)
  error("Sponge level needs to be at least 1.");
if (level > 3)
  error("Sponge level needs to be at most 3.");
counter = 0;
model.component("comp1").geom("geom1").feature().clear();
model.component("comp1").geom("geom1").autoRebuild("off");
double cx0 = 0, cy0 = 0, cz0 = 0;
double si0 = 1;
sponge(level, cx0, cy0, cz0, si0);
model.component("comp1").geom("geom1").runPre("fin");
The method sponge, shown below, is called by the above method and recursively creates a Menger sponge.
int l = level;
double posx, posy, posz, si1;
String strix;
int l1;
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    for (int k = 0; k < 3; k++) {
      posx = cx+i*si-si;
      posy = cy+j*si-si;
      posz = cz+k*si-si;
      strix = toString(i)+toString(j)+toString(k);
      if ((Math.abs((i-1))+Math.abs((j-1))+Math.abs((k-1))) > 1) {
        if (l == 1) {
          counter = counter+1;
          model.component("comp1").geom("geom1").create("blk"+strix+"C"+            toString(counter), "Block");
          with(model.component("comp1").geom("geom1").feature("blk"+strix+"C"+              toString(counter)));
            set("base", "center");
            set("size", new String[]{toString(si), toString(si),               toString(si)});
            set("pos", new double[]{posx, posy, posz});
          endwith();
          model.component("comp1").geom("geom1").feature("blk"+strix+"C"+              toString(counter)).label("Block"+strix+"C"+toString(counter));
        } else {
          l1 = l-1;
          si1 = si/3;
          sponge(l1, posx, posy, posz, si1);
        }
      }
    }
  }
}
The figure below shows the resulting geometry for a level-2 sponge.
In this case, the number of geometry objects grows with the level N as 20N and the level-2 sponge shown above has 400 block geometry objects.
Note that if you have any of the add-on products for additional CAD functionality, you can export these geometry objects on standard CAD formats. Without add-on products, you can export the meshed geometry to any of the supported mesh formats.
You can download the MPH files for these examples from:
www.comsol.com/model/recursion-and-recursively-defined-geometry-objects-75461