In the Part 2, we discussed the various cases we have to consider to generate periodically distributed ellipsoids inside an axis-aligned bounding box.
A typical starting point for our algorithm can be seen in Figure 1. Our task is to generate periodic particles as discussed in Part 2.
In this article, we will discuss a few more details and a possible way of implementing the algorithm in C++.
Shrinking the bounding box
If we start with the initial bounding box we may run into situations where the faces of the bounding box touch or intersect only a few ellipsoids. That can lead to a sparse distribution of ellipsoids in the extended bounding box. We can see that in Figure 2 where the expanded bounding box in blue appears to have a lot of empty space. Excessive empty space implies that periodic boundary conditions on the boundary particles will not lead to the desired homogeneous deformation of the exterior of the RVE without a lot of extra checks on the locations of particles.
One solution to this problem is to move shrink the initial bounding box by a small amount so that each of the three faces () intersects a larger number of particles. There is a possibility that some of the original boundary particles will be missed in this procedure. But keep in mind that all we are interested in is periodicity, and a few missed small particles should not affect the particle distribution in the RVE significantly given that we are already changing the distribution by adding periodic particles. Of course, we do have to check that the particle size and mass distribution remains within reasonable bounds after the particle generation procedure.
Figure 3 shows the distribution of the periodic particles after the initial bounding box has been shrunk by a small factor. There are still gaps, but fewer particles will need special treatment at the boundaries with the new distribution.
You can try to convince yourself that the new particle distribution is actually periodic by looking at the image in Figure 4 (orthographic projection). However, a computational check of overlaps is essential if the margin parameter (discussed in Part 2) is smaller than 2.
We will assume that we have arrays of particle positions, radii, and axis orientations and also a bounding box. We will also assume that we have a marginFactor to determine the locations where the periodic particles will be placed, and a faceShiftFactor that will determine how much the initial bounding box will have to be shrunk.
Max-min particle radii
The first step in the particle generation process is the determination of the minimum and maximum particle radii. The maximum radius will determine the minimum size of the margin while the minimum radius will determine how much bounding box shrinkage is acceptable. We will use the binary search based std::minmax_element for this purpose because of its complexity.
Setting up the shrunk bounding box
We use the face shift factor and the minimum particle radius to compute the shrunk domain and get a vector of vertices of the domain.
Setting up the three faces for intersections
Next we set up a set of face indices (see Part 2 for a discussion of the orientations of faces) and create three Face objects for convenience.
Check intersections and add periodic particles
We just loop through the particles and check intersections with the three faces. Of course, new particle will need to be assigned IDs. We use the size of the list of initial particles to determine the starting particle ID for the periodic particles. A safer alternative is to find the maximum particle ID in the existing set and then increment that value as new particles are added.
Treatment for special cases
For special cases where more than one periodic particle has to be inserted for a given particle we use the addExtraTranslations function. Note the special treatment of shared vertices and shared edges.
Recall that when we do the ellipsoid-face intersections (see Part 1), we return a pair containing a bool indicating whether there is an intersection and also containing a pair that identifies whether the intersection is at a vertex or an edge along with the vertex or edge index..
If the intersection is at a vertex, we check whether the vertex has index 0 (i.e., it is shared by three faces). In that case we create three particles, two parallel to the coordinate axes and the third along a diagonal.
If the vertex has index 1 or 3, it is shared by two faces and a copy along a diagonal is necessary.
If the intersection is at an edge, we find an in-plane and an out-of-plane shift.
At the end of the process, we will have a few duplicate particles which will have to be cleaned up. We store a set of tags in the seen vector and then remove the elements that satisfy the seen condition.
The approach discussed here is remarkably fast for RVEs containing a reasonably large number of particles.
If you have questions/comments/corrections, please contact banerjee at parresianz dot com dot zen (without the dot zen).