[docs]@config.nodeclassParallelArrayPlacement(NotParallel,PlacementStrategy):""" Implementation of the placement of cells in parallel arrays Cells are placed in rows on the plane defined by 2 selected axes. """spacing_x:float=config.attr(type=types.float(min=0),required=True)""" Space in between two cells along the main axis. """angle:float=config.attr(type=types.deg_to_radian(),required=True)""" Angle between the second axis and the axis of the rows of cells. """
[docs]defboot(self):ifself.angle%(np.pi)==np.pi/2:raiseConfigurationError("Parallel array angle should be not a multiple of pi/2 for "f"'{self.name}'. Provided angle: {self.angle}")
[docs]defplace(self,chunk,indicators):""" Cell placement: Create a lattice of parallel arrays/lines in the (x, y) surface. """forindicatorinindicators.values():cell_type=indicator.cell_typeradius=indicator.get_radius()forprtinself.partitions:width,depth,height=prt.data.mdc-prt.data.ldcldc=prt.data.ldc# Add a random shift to the starting points of the arrays for variation.x_shift=np.random.rand()*self.spacing_x# Place cells equally spaced over the entire length of the X axis kept# apart by the provided space.# They are placed in straight lines, tilted by a certain angle by adding# a shifting value.x_pos=np.arange(start=0.0,stop=width,step=self.spacing_x)+x_shiftifx_pos.shape[0]==0:# When the spacing_x of is larger than the simulation volume,# place a single row on a random position along the X axisx_pos=np.array([x_shift])# Amount of parallel arrays of cellsn_arrays=x_pos.shape[0]# Number of cellsn=np.sum(indicator.guess(prt.data))# Add extra cells to fill the lattice error volume which will be prunedn+=int((n_arrays*self.spacing_x%width)/width*n)# cells to distribute along the rowscells_per_row=round(n/n_arrays)# The rounded amount of cells that will be placedcells_placed=cells_per_row*n_arrays# Calculate the position of the cells along the z-axis.y_pos,y_axis_distance=np.linspace(start=0.0,stop=depth-radius,num=cells_per_row,retstep=True,endpoint=False,)# Center the cell soma center to the middle of the unit celly_pos+=radius+y_axis_distance/2# The length of the X axis rounded up to a multiple of the unit cell size.lattice_x=n_arrays*self.spacing_x# The length of the X axis where cells can be placed in.bounded_x=lattice_x-radius*2# Epsilon: open space in the unit cell along the Y axisepsilon=y_axis_distance/math.cos(self.angle)-radius*2ifepsilon<0:raisePackingError("Not enough space between cells placed on the same row "f"for '{self.name}'.")# Storage array for the cellscells=np.empty((cells_placed,3))foriinrange(y_pos.shape[0]):# Shift the arrays at an angleangleShift=y_pos[i]*math.tan(self.angle)# Apply shift and offsetx=x_pos+angleShift# Place the cells in a bounded lattice with a little modulus magicx=ldc[0]+x%bounded_x+radius# Place the cells in their y-position with jittery=(ldc[1]+y_pos[i]+epsilon*(np.random.rand(x.shape[0])-0.5)*math.cos(self.angle))# Place them at a uniformly random height throughout the partition.z=ldc[2]+np.random.uniform(radius,height-radius,x.shape[0])# Store this stack's cellscells[(i*len(x)):((i+1)*len(x)),0]=xcells[(i*len(x)):((i+1)*len(x)),1]=ycells[(i*len(x)):((i+1)*len(x)),2]=z# Place all the cells in 1 batch (more efficient)positions=cells[cells[:,0]<prt.data.ldc[0]+width-radius]# Determine in which chunks the cells must be placedcs=self.scaffold.configuration.network.chunk_sizechunks_list=np.array([chunk.data+np.floor_divide(p,cs[0])forpinpositions])unique_chunks_list=np.unique(chunks_list,axis=0)# For each chunk, place the cellsforcinunique_chunks_list:idx=np.where((chunks_list==c).all(axis=1))pos_current_chunk=positions[idx]self.place_cells(indicator,pos_current_chunk,chunk=c)report(f"Placed {len(positions)}{cell_type.name} in {prt.name}",level=3)