Calculating distances in CC3D simulations.

This may seem like a trivial task. After all, Pitagorean theorem is one of the very first theorems that people learn in basic mathematics course. The purpose of this section is to present convenience functions which will make your code more readable. You can easily code such functions yourself but you probably will save some time if you use ready solutions. One of the complications in the CC3D is that sometimes you may run simulation using periodic boundary conditions. If that’s the case, imagine two cells close to the right hand side border of the lattice and moving to the right. When we have periodic boundary conditions along X axis one of such cells will cross lattice boundary and will appear on the left hand side of the lattice. What should be a distance between cells before and after once of them crosses lattice boundary? Clearly, if we use a naïve formula the distance between cells will be small when all cells are close to righ hand side border but if one of them crosses the border the distance calculated using the simple formula will jump dramatically. Intuitively we feel that this is incorrect. The way solve this problem is by shifting one cell to approximately center of the lattice and than applying the same shift to the other cell. If the other cell ends up outside of the lattice we add a vector whose components are equal to dimensions of the lattice but only along this axes along which we have periodic boundary conditions. The point here is to bring a cell which ends up outside the lattice to beinside using vectors with components equal to the lattice dimensions. The net result of these shifts is that we have two cells in the middle of the lattice and the distance between them is true distance regardless the type of boundary conditions we use. You should realize that when we talk about cell shifting we are talking only about calculations and not physical shifts that occur on the lattice.

Example CellDistance from CompuCellPythonTutorial directory demonstrates the use of the functions calculating distance between cells or between any 3D points:

class CellDistanceSteppable(SteppableBasePy):

    def __init__(self, frequency=1):
        SteppableBasePy.__init__(self, frequency)
        self.cellA = None
        self.cellB = None

    def start(self):
        self.cellA = self.potts.createCell()
        self.cellA.type = self.A
        self.cell_field[10:12, 10:12, 0] = self.cellA

        self.cellB = self.potts.createCell()
        self.cellB.type = self.B
        self.cell_field[92:94, 10:12, 0] = self.cellB

    def step(self, mcs):
        dist_vec = self.invariant_distance_vector_integer(p1=[10, 10, 0], p2=[92, 12, 0])

        print('dist_vec=', dist_vec, ' norm=', self.vector_norm(dist_vec))

        dist_vec = self.invariant_distance_vector(p1=[10, 10, 0], p2=[92.3, 12.1, 0])
        print('dist_vec=', dist_vec, ' norm=', self.vector_norm(dist_vec))

        print('distance invariant=', self.invariant_distance(p1=[10, 10, 0], p2=[92.3, 12.1, 0]))

        print('distance =', self.distance(p1=[10, 10, 0], p2=[92.3, 12.1, 0]))

        print('distance vector between cells =', self.distance_vector_between_cells(self.cellA, self.cellB))
        print('invariant distance vector between cells =',
              self.invariant_distance_vector_between_cells(self.cellA, self.cellB))
        print('distanceBetweenCells = ', self.distance_between_cells(self.cellA, self.cellB))
        print('invariantDistanceBetweenCells = ', self.invariant_distance_between_cells(self.cellA, self.cellB))

In the start function we create two cells – self.cellA and self.cellB. In the step function we calculate invariant distance vector between two points using self.invariant_distance_vector_integer function. Notice that the word Integer in the function name suggests that the result of this call will be a vector with integer components. Invariant distance vector is a vector that is obtained using our shifting operations described earlier.

The next function used inside step is self.vector_norm. It returns length of the vector. Notice that we specify vectors or 3D points in space using [] operator. For example to specify vector, or a point with coordinates x, y, z = (10, 12, -5) you use the following syntax:

[10, 12, -5]

If we want to calculate invariant vector but with components being floating point numbers we use self.invariant_distance_vector function. You may ask why not using floating point always? The reason is that sometimes CC3D expects vectors/points with integer coordinates to e.g. access specific lattice points. By using appropriate distance functions you may write cleaner code and avoid casting and rounding operators. However this is a matter of taste and if you prefer using floating point coordinates it is perfectly fine. Just be aware that when converting floating point coordinate to integer you need to use round and int functions.

Function self.distance calculates distance between two points in a naïve way. Sometimes this is all you need. Finally the set of last four calls self.distance_vector_between_cells, self.invariant_distance_vector_between_cells, self.distance_between_cells, self.invariant_distance_between_cells calculates distances and vectors between center of masses of cells. You could replace

self.invariant_distance_vector_between_cells(self.cellA,self.cellB)

with

self.invariant_distance_vector_between(
    p1=[ self.cellA.xCOM, self.cellA.yCOM, self.cellA.yCOM],
    p2=[ self.cellB.xCOM, self.cellB.yCOM, self.cellB.yCOM]
 )

but it is not hard to notice that the former is much easier to read.