/************************************************************
'ca1' model code repository
Written by Marianne Bezaire, marianne.bezaire@gmail.com, www.mariannebezaire.com
In the lab of Ivan Soltesz, www.ivansolteszlab.org
Published and latest versions of this code are available online at:
ModelDB: 
Open Source Brain: http://www.opensourcebrain.org/projects/nc_ca1

Main code file: ../main.hoc
This file: Last updated on April 9, 2015

This file defines a CellCategoryInfo class that is used to store
information about each cell type in the model (both realistic and
artificial cells). In the code, this class is used as an array, with
each object in the array corresponding to one cell type in the model.

The objects created from this class are used throughout the model
code. They include a list of references to every cell of each type, as
well as the starting and ending GIDs that define the range of GIDs
corresponding to each cell type, to make it easy to iterate through
each cell of a particular type.

If the number of cells of a type is updated (say because some cells
had to be killed off as part of an injury model), this class provides
a method for updating cell numbers and/or gid ranges of all affected
cell types.

This class also performs necessary calculations for positioning the cells
in 3D space, evenly distributed within layer prisms.
************************************************************/

begintemplate CellCategoryInfo			// Define a template that describes
										//  a class of celltype objects,
										//  where each celltype object
										//  holds type-specific parameters
										//  used in creating the network model
										
	public setCellTypeParams, setCellTypeDist, SynList, setSynList	// Define variables and funcs that are
	public cellType_string, technicalType, cellStartGid, cellEndGid	//  accessible from outside the class
	public updateGidRange, addPosVec, layerflag, LastHighIndex		//  object
	public dist, numCells, CellList, addCell, numCons, numConns, numSyns, wgtConns, numThisHost
	public setBins, dentateYBins, dentateXBins, dentateZBins
	public dentateYBinSize, dentateXBinSize, dentateZBinSize, is_art

	objref dist, cellpos, f1, CellList, numCons, numConns	// Define objects, including
	objref numSyns, wgtConns, ctype, SynList, this			//  'this', which is this
															//  instance of the class
																
	strdef cellType_string, technicalType, tempString, cmd, strtomax 	// Define strings

	proc setCellTypeParams(){			// Define a proc to set the parameters
										//  corresponding to the celltype this
										//  member of the class describes
										
										
									// Each cell type has two names, a 'friendly' name and
									//  a 'technical' name. The friendly name (ex: "pyramidalcell"
									//  or "pvbasketcell") is how the cell type is known within
									//  the model and in the results. The technical name is the
									//  actual name used in the model cell template. This distinction
									//  comes in handy for parameter space searches, where you may
									//  have two different cell definitions (with different morphology
									//  or different electrophysiology), and you want to keep all
									//  other model properties the same, but just compare the effect
									//  of altering a particular cell type within the model.
										
		cellType_string = $s1		// Friendly name of the cell type 
		
		technicalType = $s2			// Technical name of the cell type
		
		cellStartGid = $3 			// The smallest gid (unique ID number) associated with this cell
									//  type; the first number in gid range for this type (all cells
									//  of this type have consecutive numbers in the gid range).
									
		numCells = $4				// Number of cells of this type in the model (note that this may
									//  be changed depending on the Scale parameter of the model).
		
		cellEndGid =  $3 + $4 -1	// Last number in gid range for this cell type
		
		layerflag = $5				// Index of the layer in which the somata of this cell type
									//  are found (order of the layers is a matter
									//  of convention, documented by YOU)
									
		LastHighIndex = 0			// For all the random streams associated with all the cells
									//  of this cell type, the maximum highIndex value used by
									//  any of these streams. This number is eventually reported
									//  in the results for each simulation to aid the user in
									//  picking a new highIndex value (that is higher than any of
									//  the ones used previously) for the purpose of running
									//  a statistically independent version of the exact same model
									//  network. Running multiple, independent versions of a model
									//  is helpful for demonstrating robustness of model behavior
									//  to publication reviewers.
																
		is_art=$6					// A flag to tell if the cell type is artificial (1=artificial,
									//  0=realistic). An artificial cell is generally a point process
									//  just used for stimulating the cells of the network.
		
		objref CellList[numCells]	// Create a new CellList for this celltype, the length of which
									//  equals the number of cells of this type in the model
									
		numThisHost = 0				// Initialize a counter that keeps track of how many cells of
									//  this type are 'owned' by a particular processor in a parallel
									//  NEURON run. This number may or may not come in handy later, so
									//  we will keep the functionality for now.
		
		ctype = new StringFunctions()	// Create a new StringFunctions object
										//  for parsing the name of the celltype
	}
	
	proc setSynList(){			// From a file, read in the constants
		objref SynList[$1]
	}

	proc setCellTypeDist(){			// From a file, read in the constants for the Gaussian equation
									//  that describes the axonal distribution of this celltype

		f1 = new File()
		dist = new Vector(3)		// Define a new vector, dist, which holds the constants to be read
		
		sprint(tempString,"../cells/axondists/dist_%s.hoc",cellType_string)	//Specify the file to read
		
		f1.ropen(tempString)		// Open the file
		
		if (f1.isopen()) {			
			dist.scanf(f1)			// Read in the values from the file.
		} else {					// If the file is not open (probably because it does not exist),
			dist.x[0]=1				//  then set some placeholder distribution constants instead.
			dist.x[1]=0
			dist.x[2]=1000
			print "Note, there was no axonal distribution file for ", cellType_string
		}
		
		f1.close()					// Close the file
	}
	
	proc updateGidRange() {			// Define a proc that updates the range
									//  of gids used by this celltype. The range
									//  needs updating if this cell type was
									//  subject to cell loss (reducing the 
									//  number of cells) or if any celltype
									//  earlier in the gid sequence was subject
									//  to cell loss (causing the StartGid to
									//  be moved earlier)
									
		cellStartGid = $1			// Read in the start gid
		
		cellEndGid = $1 + numCells -1	// Solve for the end gid given the
										//  number of cells
		if (numCells>0) {
			objref CellList[numCells]	// Create a new CellList for this celltype
		}
	}
	
	proc setBins() {local runresult, toohigh, numtomin	// Defines the proc setBins,
														//  which takes the arguments
														//  for the length of the 
														//  brain region in X, Y, and Z,
														//  and determines how far apart
														//  the cells should be in each
														//  dimension to be evenly spaced.
														// This proc is used when you want
														//  the positions of the cells in the
														//  model to evenly fill out a prism
														//  of size X by Y by Z, where Z is
														//  given by which layer the celltype
														//  is in (specified by the layer index
														//  of the cell type in combination with
														//  the layer vector parameter).

		dentateZBins=int((numCells*($3)^2/($2*$1))^(1/3))	// Given the relative length of
															//  the Z dimension compared to
															//  the X and Y dimensions, solve
															//  for how many cells should be 
															//  spaced along the Z dimension.
															
		if (dentateZBins==0) {dentateZBins=1}	// Make sure the Z dimension is at least one
												//  cell wide.
		
		dentateYBins=int(($2/$3)*(numCells*($3)^2/($2*$1))^(1/3))	// Given the relative
																	//  length for Y compared
																	//  to the other dimensions,
																	//  solve for how many cells
																	//  should be spaced along
																	//  the Y dimension.
																	
		if (dentateYBins==0) {dentateYBins=1}	// Make sure the Y dimension is at least one
												//  cell wide.
												
		dentateXBins=int(($1/$3)*(numCells*($3)^2/($2*$1))^(1/3))	// Given the relative
																	//  length for X compared
																	//  to the other dimensions,
																	//  solve for how many cells
																	//  should be spaced along
																	//  the X dimension.
																	
		if (dentateXBins==0) {dentateXBins=1}	// Make sure the X dimension is at least one
												//  cell wide.
												
		// The above code may result in there being slightly too few or too many positions
		//  set aside for cells. To make the final spacing along each dimension most closely
		//  match the total number of cells of this type, we may either increase or decrease
		//  the number of cells assigned along each edge as follows
		
		// Find the largest dimension (which is the ideal dimension to change if the number
		//  of cell assignments is too large or too small) 
								
		if ($3 >= $2 && $3 >= $1) {
			strtomax="dentateZBins"
			numtomin=dentateXBins*dentateYBins
		} else {
			if ($2 >= $3 && $2 >= $1) {
				strtomax="dentateYBins"
				numtomin=dentateXBins*dentateZBins
			} else {
				strtomax="dentateXBins"
				numtomin=dentateYBins*dentateZBins
			}
		}

		while (dentateXBins*dentateYBins*dentateZBins < numCells){	// If not enough cell
																	//  positions are alloted,
			sprint(cmd, "%s=%s+1",strtomax,strtomax)				//  then another slot to
																	//  the largest dimension.
																	
			runresult=execute1(cmd, this) 	// This command was written as a string so
											//	the dimension to increase doesn't have to be hard coded
		}

		toohigh=dentateXBins*dentateYBins*dentateZBins-numtomin
		while (toohigh >= numCells){					// If too many cell positions were alloted,
			sprint(cmd, "%s=%s-1",strtomax,strtomax)	// 	remove one from the largest dimension.
			runresult=execute1(cmd, this) 				// This command was written as a string so
														//	the dimension to increase doesn't have
														//  to be hard coded
			toohigh=dentateXBins*dentateYBins*dentateZBins-numtomin
		}

		dentateZBinSize = int($3/dentateZBins)	// Length of each cell's 'personal space' (along Z dimension) in microns
		
		dentateYBinSize = int($2/dentateYBins)	// Length of each cell's 'personal space' (along Y dimension) in microns
		
		dentateXBinSize = int($1/dentateXBins)	// Length of each cell's 'personal space' (along X dimension) in microns
	}

endtemplate CellCategoryInfo