/** * \brief For self-attachment scenarios, calculates the new position of the agent based on XY and * XZ angles and a distance to move * * <p>For self-attachment scenarios, calculates the new position of the agent based on XY and XZ * angles and a distance to move. No return value as the global swimmingAgentPosition is altered. * The angles XY and XZ are also global parameters as these can also be altered by other methods * in the swimming agent position checks * * @param distanceAgentMoves Distance that the agent is to move */ public void calculateNewAgentPosition(Double distanceAgentMoves) { // Now calculate where this angle would place the agent. swimMovement.set( Math.cos(angleOfMovingAgentXY) * Math.cos(angleOfMovingAgentXZ), Math.sin(angleOfMovingAgentXY), Math.sin(angleOfMovingAgentXZ)); swimMovement.times(distanceAgentMoves); swimmingAgentPosition.add(swimMovement); }
/** * \brief Defines a region of the computation domain where a new species may be created, using * restrictions in the protocol file * * <p>Defines a region of the computation domain where a new species may be created, using * restrictions in the protocol file. These restrictions for a particular species are specified in * 'coordinates' tags. There should be two such tags where this is used - one to start the * restriction and one to end it. Example of use: \<coordinates x="0" y="0" z="0"/\> \<coordinates * x="1" y="264" z="0"/\>. This method will read these in and create an array that represents this * area * * @param spRoot The information within the 'initArea' tags of the protocol file * @return A continuous vector representing the area of the domain specified in these tags */ public ContinuousVector[] defineSquareArea(XMLParser spRoot) { List<XMLParser> area = spRoot.getChildrenParsers("coordinates"); ContinuousVector[] initArea = new ContinuousVector[2]; initArea[0] = new ContinuousVector(); initArea[1] = new ContinuousVector(); // KA NOV 13 - CHANGED THIS, AS WE'RE GOING TO LET THE USER NOT DECLARE AN INITIAL AREA IF THEY // WANT THE CELLS SPREAD ACROSS // THE WHOLE DOMAIN. THUS THIS NEEDS CHECKING AND FIXING if (area.size() > 0) { // First Coordinate Tag ContinuousVector cc1 = new ContinuousVector(area.get(0)); // Second Coordinate Tag ContinuousVector cc2 = new ContinuousVector(area.get(1)); // Set each point initArea[0].x = Math.min(cc1.x, cc2.x); initArea[1].x = Math.max(cc1.x, cc2.x); initArea[0].y = Math.min(cc1.y, cc2.y); initArea[1].y = Math.max(cc1.y, cc2.y); // In the case of 2D simulation, the agent's z-coordinate is 0. if (domain.is3D) { initArea[0].z = Math.min(cc1.z, cc2.z); initArea[1].z = Math.max(cc1.z, cc2.z); } else { initArea[0].z = initArea[1].z = 0.0; } } else { // NO INITIAL AREA HAS BEEN DECLARED, USE THE WHOLE SUBSTRATUM. NOTE THAT THE X (HEIGHT) // COORDINATE IS SET TO 1 SO THE // CELLS ARE PLACED NEAR THE SUBSTRATUM // Set each point initArea[0].x = 0.0; initArea[0].y = 0.0; initArea[0].z = 0.0; initArea[1].x = 1.0; initArea[1].y = domain.length_Y; initArea[1].z = domain.length_Z; } return initArea; }
/** * \brief For self-attachment scenarios, performs the run and tumble motion of a swimming cell, * and the required position checks and corrections * * <p>For self-attachment scenarios, the agents enter the system on top of the boundary layer and * perform a run and tumble motion, swimming through the biofilm. This method captures that * motion. A random angle is chosen and the cell moves at a certain distance, set by cell tumble * speed in the protocol file. The agent may move back into the bulk, where we assume it will go * back into the mix and thus we take no further action with it. The aim is for that cell to swim * around until it either hits the substratum surface or biofilm surface, where it will attach. If * the agent does not attach in this distance, a new angle is chosen (a 'tumble') and this is * repeated. This method takes care of all checks to the coordinates (such as a crossed boundary * and hitting the surface/biofilm). An integer value is returned that states the fate of this * move - the cell has either not attached anywhere (0), and thus needs to tumble, the cell has * attached (1), or the cell has returned to the bulk (2). * * @author Kieran Alden * @param spRoot The Species markup from the protocol file for one particular species being * initialised * @return Integer value noting the fate of this move (0 no attachment, 1 attachment, 2 returned * to bulk) */ public int performRunAndTumble(XMLParser spRoot) { // To clarify how Jan has designed this to work: // The moving cell has a speed, measured in microns a second, and this is specified in the // protocol file. // You would thus think that the cell would move at a speed that this related to this and the // global timestep. // However, it has been decided that this is not to be the case, and the cell will run and // tumble continuously this timestep // until it has attached to a surface or to the biofilm. // Thus this speed is only used to calculate how far the cell will move before tumbling, and the // speed at which the cell moves // has no relation to the global timestep (and thus no relation to real time) // Now the distance the cell will move in a particular direction is calculated from the tumble // interval (specified in the protocol file) // and the cell run speed (also in the protocol file). The cell will move away at a random angle // for that distance, and will then // tumble (changing the angle). This continues until this cell has either returned into the bulk // or has attached to the surface // or biofilm // Eventually we will look to change this tumble interval, as this can be used to alter cell // movement when under chemotaxis Double distanceEachTumble = getLocatedParam().cellRunSpeed * getLocatedParam().tumbleInterval; Double distanceToMoveThisTumble = 0.0; Double distanceAgentMoves = 0.0; Double distanceSeekingAgent = getLocatedParam().getStickinessRadius(); // We will use a integer flag system to determine what has happened to the cell after a run // 0 - no attachment, must do another run // 1 - cell has attached and thus movement is over // 2 - cell has returned into the bulk (and thus is discarded) int cellRunResult = 0; while (true) // for ( int i = 0; i < 1000000; i++ ) { /* * Now work out the new position of the cell based on the distance * covered this tumble. Firstly calculate that direction - generate * a random angle between 0 and 360. */ setAgentAngleOfMovement(); /* * Now we're going to break the move down into chunks such that * collision or move over the boundary can be detected. The size of * the chunk is going to be the resolution of the agent grid. */ distanceToMoveThisTumble = distanceEachTumble; while (distanceToMoveThisTumble > 0.0) { /* * If the cell has less distance to move remaining that the * resolution, should only move that much. */ distanceAgentMoves = Math.min(distanceToMoveThisTumble, distanceSeekingAgent); calculateNewAgentPosition(distanceAgentMoves); /* * Now need to check for collision on any point of that path. */ cellRunResult = checkAgentMove(distanceAgentMoves); // Add the distance moved in this mini move to the tally. distanceToMoveThisTumble -= distanceAgentMoves; // If the cell has finished moving, we can return the result. if (cellRunResult != 0) return cellRunResult; } } // return cellRunResult; }