// // DLASystem.cpp // #include "DLASystem.h" using std::cout; using std::endl; // this function gets called every step, // if there is an active particle then it gets moved, // if not then add a particle void DLASystem::update() { this->frame++; if (lastParticleIsActive == 1) { moveLastParticle(); } else if (this->particleList.size() < (size_t) endNum) { addParticleOnAddCircle(); setParticleActive(); } else { this->running = false; } } // set the value of a grid cell for a particular position // note the position has the initial particle at (0,0) // but this corresponds to the middle of the grid array ie grid[ halfGrid ][ halfGrid ] void DLASystem::setGrid(std::array pos, int val) { *index_grid(pos) = val; } // read the grid cell for a given position int DLASystem::readGrid(std::array pos) { return *index_grid(pos); } // add a particle to the system at a specific position void DLASystem::addParticle(std::array pos) { // create a new particle auto *p = new Particle(pos); // push_back means "add this to the end of the list" particleList.push_back(p); // pos coordinates should be -gridSize/2 < x < gridSize/2 setGrid(pos, 1); } // add a particle to the system at a random position on the addCircle // if we hit an occupied site then we do nothing except print a message // (this should never happen) void DLASystem::addParticleOnAddCircle() { double theta = rgen.random01() * 2 * M_PI; std::array pos{ceil(addCircle * cos(theta)), ceil(addCircle * sin(theta))}; if (readGrid(pos) == 0) { addParticle(pos); } else { cout << "FAIL " << pos[0] << " " << pos[1] << endl; } } // send back the position of a neighbour of a given grid cell // NOTE: there is no check that the neighbour is inside the grid, // this has to be done separately... void DLASystem::setPosNeighbour(std::array &setpos, const std::array pos, int val) { switch (val) { case 0: setpos[0] = pos[0] + 1.0; setpos[1] = pos[1]; break; case 1: setpos[0] = pos[0] - 1.0; setpos[1] = pos[1]; break; case 2: setpos[0] = pos[0]; setpos[1] = pos[1] + 1.0; break; case 3: setpos[0] = pos[0]; setpos[1] = pos[1] - 1.0; break; } } // when we add a particle to the cluster, we should update the cluster radius // and the sizes of the addCircle and the killCircle void DLASystem::updateClusterRadius(std::array pos) { double newRadius = distanceFromOrigin(pos); if (newRadius > clusterRadius) { clusterRadius = newRadius; // this is how big addCircle is supposed to be: // either 20% more than cluster radius, or at least 5 bigger. double check = clusterRadius * addRatio; if (check < clusterRadius + 5) { check = clusterRadius + 5; } // if it is smaller then update everything... if (addCircle < check) { addCircle = check; killCircle = killRatio * addCircle; } if (killCircle + 2 >= gridSize / 2) { std::cerr << "Early Exit" << endl; this->running = false; } } } // make a random move of the last particle in the particleList void DLASystem::moveLastParticle() { int direction = rgen.randomInt(4); // pick a random number in the range 0-3, which direction do we hop? std::array newpos{}; Particle *lastP = particleList[this->particleList.size() - 1]; setPosNeighbour(newpos, lastP->pos, direction); if (distanceFromOrigin(newpos) > killCircle) { setGrid(lastP->pos, 0); particleList.pop_back(); // remove particle from particleList setParticleInactive(); } else if (readGrid(newpos) == 0) { setGrid(lastP->pos, 0); // set the old grid site to empty // update the position particleList[this->particleList.size() - 1]->pos[0] = newpos[0]; particleList[this->particleList.size() - 1]->pos[1] = newpos[1]; setGrid(lastP->pos, 1); // set the new grid site to be occupied // check if we stick if (checkStick() && this->rgen.random01() <= this->stickProbability) { this->csv_out << frame << "," << lastP->pos[0] << "," << lastP->pos[1] << endl; setParticleInactive(); // make the particle inactive (stuck) updateClusterRadius(lastP->pos); // update the cluster radius, addCircle, etc. } } } // check if the last particle should stick (to a neighbour) int DLASystem::checkStick() { Particle *lastP = particleList[this->particleList.size() - 1]; int result = 0; // loop over neighbours for (int i = 0; i < 4; i++) { std::array checkpos{}; setPosNeighbour(checkpos, lastP->pos, i); // if the neighbour is occupied... if (readGrid(checkpos) == 1) result = 1; } return result; } // constructor DLASystem::DLASystem(Config config) : stickProbability(config.stickProbability), grid(gridSize * gridSize), csv_out(std::move(config.csv)), endNum(config.maxParticles), frame(0), running(false), lastParticleIsActive(false) { cout << "GridSize: " << gridSize << endl; rgen.setSeed(config.seed); /* * Handle grid data structure. * */ // Add particle to the centre of the grid to start std::array pos{0.0, 0.0}; addParticle(pos); // Make sure to include the central location in our csv. this->csv_out << 0 << "," << 0 << "," << 0 << std::endl; /* * System behaviour parameters * */ addRatio = 1.2; // how much bigger the addCircle should be, compared to cluster radius killRatio = 1.7; // how much bigger is the killCircle, compared to the addCircle addCircle = 10; killCircle = 2.0 * addCircle; clusterRadius = 0.0; } // destructor DLASystem::~DLASystem() { // delete the particles particleList.clear(); if (csv_out.is_open()) { csv_out.flush(); csv_out.close(); } }