In the agent based modeling we are typically more interested in the rules governing the cell fate rather than the basic setting of the lattice (if we don’t look at the off lattice model). However, the particular setting of the computational domain can have an effect on the model dynamics. In 2D we typically consider the following lattices and neighborhoods:

with the rectangular grid with Moore neighborhood being probably most frequently utilized. With von Neumann neighborhood we have the fewest number of neighbors and cells are spatially saturated earlier. The problem with Moore neighborhood is that the distance to all of the sites is not the same. Hexagonal grid has good properties (same distance, 6 neighbors), but is less frequently utilized, because implementation is more involved. In todays post I will show that essentially there is no difference in implementation between all of those lattices.

We will use the codes for basic ABM model posted before as a template (MATLAB version here and C++ version here). In both cases we considered rectangular lattice with Moore neighborhood. If we set migration probability to zero, set large value of proliferation capacity and set the spontaneous death rate to zero, i.e. we simulate essentially only division events, we will see in visualizations of simulated tumors what kind of neighborhood we assumed:

Let us see how the visualization looks like for different types of neighborhoods/lattices.

In both implementations we had a separate array defining the neighborhood of the cell. Thus, in order to modify the code to von Neumann neighborhood we need to change only two consecutive lines in the MATLAB code in which we define the neighborhood and the permutations table:

aux = int32([-N -1 1 N])'; %indices to heighborhood Pms = perms(uint8(1:4))'; %permutations

In C++ implementation we need to modify the definition of the neighborhood:

static const int indcNeigh[] = {-N, -1, 1, N};//neighborhood

and the *for* loop in the *returnEmptyPlace* function:

for(int j=0;j<4;j++) {//searching through neighborhood if (!lattice[indx+indcNeigh[j]]) { neigh[nF] = indx+indcNeigh[j]; nF++; } }

As it could be expected, switching to von Neumann neighborhood made the simulated tumor diamond shaped.

Let us now consider hexagonal lattice. The very first observation that we need to make is that hexagonal lattice is essentially an rectangular lattice with shifted odd (or even) rows and two definitions of neighborhood (one for cells in odd rows, second for cells in even rows), see picture below

Thus, in the modified implementation we again only need to change the definition of the neighborhood and add the condition for choosing the proper one. In MATLAB it can be achieved by introducing two definitions of neighborhoods

auxR = int32([-N -1 1 N-1 N N+1])'; %indices to heighborhood auxL = int32([-N -1 1 -N-1 N -N+1])'; %indices to heighborhood Pms = perms(uint8(1:6))'; %permutations

and modify the lines in the main loop in which we create the neighborhood for all viable cells (create variable *S*)

odd = mod(cells,2) ~= 0; %selecting cells in the odd rows S = zeros(6,length(cells)); if any(odd) %creating neighborhood for the cells in the odd rows SR = bsxfun(@plus,cells(odd),auxR(Pms(:,randi(nP,1,sum(odd))))); S(:,odd) = SR; end even = ~odd; if any(even) %creating neighborhood for the cells in the even rows SL = bsxfun(@plus,cells(even),auxL(Pms(:,randi(nP,1,sum(even))))); S(:,even) = SL; end

In C++ the changes are even easier. We again introduce two neighborhoods:

static const int indcNeighR[] = {-N,-1,1,N-1,N,N+1};//neighborhood static const int indcNeighL[] = {-N,-1,1,-N-1,N,-N+1};//neighborhood

and modify the *returnEmptyPlace* function

for(int j=0;j<6;j++) {//searching through neighborhood if (indx % 2) {//if odd row if (!lattice[indx+indcNeighR[j]]) { neigh[nF] = indx+indcNeighR[j]; nF++; } } else {//if even row if (!lattice[indx+indcNeighL[j]]) { neigh[nF] = indx+indcNeighL[j]; nF++; } } }

We might also want to change the code for visualization. In the previous settings it was enough to represent each cell as a pixel in the image. In case of hexagonal lattice in order to be correct we need to somehow shift the pixels in odd (or even rows). The easiest way to achieve that is to represent each cell as 4 pixels and adjust position according to the row number:

The modified visualization code in MATLAB is the following

function visualizeHex( N, cells, cellsIsStem, cellsPmax, pmax ) %select cells in the odd rows odd = mod(cells,2) ~= 0; M = ones(2*N,2*N,3); %matrix for image, we expand it %disperse cell by one spot i = mod(double(cells)-1,N)+1; %row, REMEMBER THAT CELLS ARE UINTs! j = ceil(double(cells)/N); %column cells = (i*2-1)+(2*j-1)*2*N; %add cells to the top right in odd rows cells = [cells reshape(bsxfun(@plus,cells(odd),[-1; 2*N-1; 2*N]),1,[])]; cellsIsStem = [cellsIsStem reshape(repmat(cellsIsStem(odd),3,1),1,[])]; cellsPmax = [cellsPmax reshape(repmat(cellsPmax(odd),3,1),1,[])]; %add cells to top left in even rows even = [~odd false(1,3*sum(odd))]; cells = [cells reshape(bsxfun(@plus,cells(even),[-1; -2*N-1; -2*N]),1,[])]; cellsIsStem = [cellsIsStem reshape(repmat(cellsIsStem(even),3,1),1,[])]; cellsPmax = [cellsPmax reshape(repmat(cellsPmax(even),3,1),1,[])]; %plotting color = hot(3*pmax); M(cells(~cellsIsStem)) = color(cellsPmax(~cellsIsStem)+1,1); M(cells(~cellsIsStem)+4*N*N) = color(cellsPmax(~cellsIsStem)+1,2); M(cells(~cellsIsStem)+8*N*N) = color(cellsPmax(~cellsIsStem)+1,3); CSCs = cells(cellsIsStem); M(CSCs) = color(2*pmax,1); M(CSCs+4*N*N) = color(2*pmax,2); M(CSCs+8*N*N) = color(2*pmax,3); figure(1) clf imshow(M); end

Finally, simulating the tumor with exactly the same parameters settings on the hexagonal lattice results in way more circular tumor.