/** * \brief Examines all the agents within a voxel of the agent-grid, to determine if a swimming * agent is in contact with an agent in the biofilm * * <p>To determine if a swimming cell is near contact with the biofilm surface, the agent checks * the status of the agent grid voxel it resides within. If this is 1, this is noted as having * agents within it that are within the biofilm structure. The agent now examines how close it is * to each of these agents. If within a certain distance (radius + stickiness constant), then the * agent is deemed to have adhered to the structure. Any overlap will be addressed during shoving * analysis. If not, the agent will perform another move. * * @author Kieran Alden * @param gridIndex The index of the agent grid to be checked * @param distanceSeekingAgent The distance within which two cells are deemed to be in contact * @return Boolean noting whether the agent is in contact with an agent in the biofilm */ public Boolean isAgentInContactWithAgentInBiofilm(int gridIndex, double distanceSeekingAgent) { LocatedGroup agentsInGrid = currentSimulator.agentGrid.returnGroupInVoxel(gridIndex); Double dist = 0.0; // Now iterate through each one. If we're close enough, move done. // Shoving can then sort out distance between the two cells. for (LocatedAgent aLoc : agentsInGrid.group) dist = aLoc.getLocation().distance(swimmingAgentPosition); if (dist <= distanceSeekingAgent) return true; // If not, we'll do another move. return false; }
/** * \brief On agent division, divides the mass between the old and new agent, at a specified * fraction * * <p>On agent division, divides the mass between the old and new agent, at a specified fraction * * @param baby The new agent, which is inheriting mass * @param babyMassFrac The fraction of this agents mass that should be transferred to the new * agent */ public void divideCompounds(LocatedAgent baby, double babyMassFrac) { // Choose the division plan and apply position modifications for (int i = 0; i < particleMass.length; i++) { baby.particleMass[i] *= babyMassFrac; this.particleMass[i] *= 1 - babyMassFrac; } // Update radius, mass, volumes and growth rates updateSize(); baby.updateSize(); updateGrowthRates(); baby.updateGrowthRates(); }
/** * \brief Create a new agent in a specified position * * <p>Create a new agent in a specified position * * @param position Vector stating where this agent should be located */ public void createNewAgent(ContinuousVector position) { try { // Get a clone of the progenitor LocatedAgent baby = (LocatedAgent) sendNewAgent(); baby.giveName(); // randomize its mass baby.mutatePop(); baby.updateSize(); this._myDivRadius = getDivRadius(); baby._myDivRadius = getDivRadius(); baby._myDeathRadius = getDeathRadius(); // Just to avoid to be in the carrier position.x += this._totalRadius; baby.setLocation(position); baby.registerBirth(); } catch (CloneNotSupportedException e) { utils.LogFile.writeLog("Error met in LocAgent:createNewAgent()"); } }
/** * \brief Pulling : The movement of agents by a shrinking biofilm. Move calculated and added to * the agents movement vector. * * <p>The movement of agents by a shrinking biofilm. Move calculated and added to the agents * movement vector. * * @param aNeighbor Reference to the potentially shoving neighbour * @param isMutual Whether movement is shared between two agents or applied only to this one * @param gain Double noting change in position * @return Boolean stating whether pulling is detected (true) or not (false) */ public boolean addSpringMovement(LocatedAgent aNeighbor, boolean isMutual, double gain) { double d, distance, delta; if (aNeighbor == this) return false; // Build the escape vector and find the distance between you and your // neighbourhood d = computeDifferenceVector(_location, aNeighbor._location); _diff.normalizeVector(); distance = getShoveRadius() + aNeighbor.getShoveRadius(); distance += getSpeciesParam().shoveLimit; delta = d - distance; double lMax = _totalRadius; if (delta > 0) gain *= Math.exp(-delta * 5 / (lMax)); if (delta > lMax) gain = 0; /* Apply shoving _________________________________________________ */ if (isMutual) { _diff.times(-0.5 * delta * gain); this._movement.add(_diff); aNeighbor._movement.subtract(_diff); } else { _diff.times(-delta * gain); this._movement.add(_diff); } return (_movement.norm() > _radius * gain); }
/** * \brief Mutual shoving : The movement by shoving of an agent is calculated based on the cell * overlap and added to the agents movement vector. * * <p>Mutual shoving : The movement by shoving of an agent is calculated based on the cell overlap * and added to the agents movement vector. Both agents are moved of half the overlapping distance * in opposite directions. * * @param aNeighbour Reference to the potentially shoving neighbour * @param isMutual Whether movement is shared between two agents or applied only to this one * @param gain Double noting change in position * @return Boolean stating whether shoving is detected (true) or not (false) */ public boolean addPushMovement(LocatedAgent aNeighbour, boolean isMutual, double gain) { double d, distance; if (aNeighbour == this) return false; // Build the escape vector and find the distance between you and your // neighbourhood d = computeDifferenceVector(_location, aNeighbour._location); _diff.normalizeVector(); // Compute effective cell-cell distance distance = getShoveRadius() + aNeighbour.getShoveRadius(); distance += getSpeciesParam().shoveLimit; distance = d - distance; /* Apply shoving _________________________________________________ */ // Compute shoving distance for the current agent if (distance <= 0) { if (isMutual) { _diff.times(gain * 0.5 * Math.abs(distance)); this._movement.add(_diff); aNeighbour._movement.subtract(_diff); } else { _diff.times(Math.abs(gain * distance)); this._movement.add(_diff); } return true; } else { return false; } }
/** * \brief Used in 'one-time' attachment scenarios, where clones of the progenitor are created in * the birth area of the substratum. * * @param spRoot The XML mark-up group for a particular species being created. */ public void createPop(XMLParser spRoot) { int howMany = spRoot.getAttributeInt("number"); if (howMany <= 0) return; // Define the birth area - this is taken from the coordinates tags in the protocol file // (Nov13) OR if an initial area is not declared, this is the whole Y and Z of the domain with a // height of 1. ContinuousVector[] _initArea = defineSquareArea(spRoot); // Create all the required agents ContinuousVector cc = new ContinuousVector(); for (int i = 0; i < howMany; i++) if (_progenitor instanceof LocatedAgent) { // Set coordinates within the birth area - randomly if (!Simulator.isChemostat) shuffleCoordinates(cc, _initArea); // Create the agent at these coordinates ((LocatedAgent) _progenitor).createNewAgent(cc); } else _progenitor.createNewAgent(); LogFile.writeLog( howMany + " agents of species " + speciesName + " for one-time attachment successfully created"); }
/** * \brief Find a sibling of this agent * * <p>Find a sibling of this agent * * @param indexSpecies The index used to reference this species in the simulation dictionary */ public void findCloseSiblings(int indexSpecies) { int nNb; boolean test; double shoveDist; LocatedAgent aNb; getPotentialShovers(getInteractDistance()); nNb = _myNeighbors.size(); for (int iNb = 0; iNb < nNb; iNb++) { aNb = _myNeighbors.removeFirst(); // test EPS-species test = (indexSpecies == aNb.speciesIndex); // Test distance shoveDist = 2 * (getShoveRadius() + aNb.getShoveRadius()); test = test && computeDifferenceVector(_location, aNb.getLocation()) <= shoveDist; if (test & aNb != this) _myNeighbors.addLast(aNb); } }
/** * \brief With it determined that cell division will occur, create a new agent from the existing * one * * <p>With it determined that cell division will occur, create a new agent from the existing one * * @throws CloneNotSupportedException Thrown if the agent cannot be cloned */ public void makeKid() throws CloneNotSupportedException { // Create the new instance LocatedAgent baby = (LocatedAgent) sendNewAgent(); // Note that mutateAgent() does nothing yet baby.mutateAgent(); this._myDivRadius = getDivRadius(); baby._myDivRadius = getDivRadius(); baby._myDeathRadius = getDeathRadius(); // Update the lineage recordGenealogy(baby); // Share mass of all compounds between two daughter cells and compute // new size divideCompounds(baby, getBabyMassFrac()); // sonia:chemostat if (Simulator.isChemostat) { // upon division the daughter cells remain with the coordinates of their progenitor } else { // Compute movement to apply to both cells setDivisionDirection(getInteractDistance(baby) / 2); // move both daughter cells baby._movement.subtract(_divisionDirection); _movement.add(_divisionDirection); } // Now register the agent inside the guilds and the agent grid baby.registerBirth(); baby._netVolumeRate = 0; }
/** * \brief On agent division, transfers EPS between the old and new agent, at a specified ratio * * <p>On agent division, transfers EPS between the old and new agent, at a specified ratio * * @param baby The new agent, which is inheriting mass * @param splitRatio The ratio of the EPS that should be transferred to the new agent */ public void transferCompounds(LocatedAgent baby, double splitRatio) { // Choose the division plan and apply position modifications double m; for (int i = 0; i < particleMass.length; i++) { m = this.particleMass[i] * splitRatio; baby.particleMass[i] += m; this.particleMass[i] = this.particleMass[i] - m; } // Update radius, mass and volumes updateSize(); baby.updateSize(); }
@SuppressWarnings("unchecked") /** * \brief Creates a daughter Located Agent by cloning this agent and parameter objects * * <p>Creates a daughter Located Agent by cloning this agent and parameter objects * * @throws CloneNotSupportedException Thrown if the agent cannot be cloned */ public Object clone() throws CloneNotSupportedException { LocatedAgent o = (LocatedAgent) super.clone(); o._location = (ContinuousVector) this._location.clone(); o._movement = (ContinuousVector) this._movement.clone(); o._divisionDirection = (ContinuousVector) this._divisionDirection.clone(); o._myNeighbors = (LinkedList<LocatedAgent>) this._myNeighbors.clone(); o._agentGridIndex = this._agentGridIndex; return (Object) o; }
/** * \brief Return the shoving interaction distance to be used in shoving against a specified agent * * <p>Return the shoving interaction distance to be used in shoving against a specified agent * * @return Double specifying the shoving interaction distance that will be applied */ public double getInteractDistance(LocatedAgent baby) { return getShoveRadius() + baby.getShoveRadius() + ((LocatedParam) _speciesParam).shoveLimit; }
/** * \brief For self-attachment scenarios, initialises agents on the boundary layer rather than * substrarum, and models their swim to the surface or biofilm * * <p>For self-attachment scenarios, the agents are initialised at the top of the boundary layer * rather than on the substratum. These agents then perform a 'run and tumble' motion until they * either attach to the substratum or forming biofilm. This method captures this behaviour for * cells that are created for a time step. Once this swimming action has been performed, the agent * is created at its final position. Note that input of agents onto the boundary layer is decided * by a parameter set in the protocol file, cellAttachmentFrequency, measured in hours. The number * of cells is adjusted to suit the global time step that is being used. Also note that this * injection of cells can be for a set period (specified in the protocol file as parameter * cellInjectionPeriod), or can be stopped and started (modelling a 'settling' period) using * parameters cellInjectionOffPeriod and cellInjectionStopHour. This is explained in detail in the * tutorial for version 1.2 of iDynoMiCS. * * @param spRoot The Species markup from the protocol file for one particular species being * initialised * @param numberAttachedInjectedAgents The number of agents of this type that need to be created * in this global timestep */ public void createBoundaryLayerPop(XMLParser spRoot, int numberAttachedInjectedAgents) { LogFile.writeLog( "\t\tAttempting to create " + numberAttachedInjectedAgents + " agents of " + speciesName + " in the boundary layer"); // Create all the required agents // Note that this continues UNTIL THE DESIRED NUMBER OF CELLS HAVE ATTACHED SOMEWHERE // Just out of interest, I've decided to keep a count of how many cells are required for this to // happen int totalNumberOfInjectedAgents = 0; int agentsReturnedToBulk = 0; int requiredNumAttachedAgents = numberAttachedInjectedAgents; // Temporary DiscreteVector to make finding the boundary layer tidier. DiscreteVector dV = new DiscreteVector(); while (numberAttachedInjectedAgents > 0) { totalNumberOfInjectedAgents++; if (_progenitor instanceof LocatedAgent) { swimmingAgentPosition.reset(); // Now to choose coordinates for this particular agent while (true) { // This cell needs to take a random location in the Z and Y // directions. The X will come from the position of the // boundary layer on those axes. Generate these randomly. swimmingAgentPosition.y = ExtraMath.getUniRandDbl() * domain.length_Y; if (domain.is3D) { swimmingAgentPosition.z = ExtraMath.getUniRandDbl() * domain.length_Z; } // Now to work out the X coordinate. This is based on where // the top of the boundary layer is when this agent is // created. The top of the boundary layer is calculated in // Domain at each step. Now the resolution differs (this is // in nI x nJ x nK rather than microns - so this will need // to be converted accordingly. Method to calculate this: // - get the value from the top of the boundary layer // - reduce by 1 (such that the micron value will be the // top of the layer) // - multiply by resolution of this domain. dV.set(swimmingAgentPosition, domain._resolution); if (!domain.is3D) dV.k = 1; swimmingAgentPosition.x = domain._topOfBoundaryLayer[dV.j][dV.k] - 1.0; swimmingAgentPosition.x *= domain._resolution; // Check this is ok. // System.out.println("Trying starting position "+dV.toString()+" => // "+this.swimmingAgentPosition.toString()); if (domain.testCrossedBoundary(swimmingAgentPosition) == null) break; } // Now we can do the run and tumble motion of these cells int cellRunResult = performRunAndTumble(spRoot); // If increment the relevant counters, as these may be useful switch (cellRunResult) { case 1: // Successfully Attached numberAttachedInjectedAgents--; // Create the agent at these coordinates ((LocatedAgent) _progenitor).createNewAgent(this.swimmingAgentPosition); // System.out.println("Cell "+swimmingAgentPosition.toString()+" attached"); break; case 2: // System.out.println("Cell at "+swimmingAgentPosition.toString()+" returned to bulk"); agentsReturnedToBulk++; break; } } else { // If this isn't a located species, just create a new agent. _progenitor.createNewAgent(); } } // Write the stats to the log file incase of interest LogFile.writeLog( requiredNumAttachedAgents + " agents of species " + speciesName + " for self-attachment successfully created"); LogFile.writeLog( totalNumberOfInjectedAgents + " agents of species " + speciesName + " attempted to attach"); LogFile.writeLog( agentsReturnedToBulk + " agents of species " + speciesName + " returned to the bulk"); }