class Simulator(): """Runs the brownian-motion simulation. :author: Peter Sander :author: ZHENG Yannan """ def __init__(self, root: object, size=50, num_sapiens=50, num_infected=20): """Create a simulation with the given field size. :root: tkinter.Tk graphics object """ self.size = size self._sapiens = [] # all sapiens in the simulation self._field = Field(size) Stats.step = 0 self._view = SimulatorView(root, size) #self._colours = ('red', 'green', 'blue', 'yellow', 'magenta', 'cyan') self.colours = { State.SUSCEPTIBLE: 'slate blue', State.INFECTED: 'red', State.RECOVERED: 'spring green', State.DEAD: 'black' } Stats.I = num_infected Stats.S = num_sapiens - num_infected Stats.I0 = Stats.I self.reset(num_sapiens) def runLongSimulation(self) -> None: """Run the simulation from its current state for a reasonably long period, e.g. 500 steps. """ self.simulate(500) def simulate(self, numSteps, delay=1) -> None: """Run the simulation from its current state for the given number of steps. :delay: Time (in secs) between each iteration. """ Stats.step = 1 while Stats.step <= numSteps: if Stats.isViable() == True: self.simulateOneStep() Stats.I0 = Stats.I time.sleep(delay) def simulateOneStep(self) -> None: """Run the simulation from its current state for a single step. """ Stats.step += 1 # all _sapiens in motion for Sapiens in self._sapiens: if Stats.step == Sapiens.r_or_d: Sapiens.if_I_die() if Sapiens.colour != 'black': Sapiens.move() self._view.showStatus(Stats.step, self._sapiens) def reset(self, num_sapiens): """Reset the simulation to a starting location. """ Stats.step = 0 self._sapiens = [] self.populate(num_sapiens) self._view.showStatus(Stats.step, self._sapiens) def populate(self, num_sapiens=50): """Populates the _field with randomly-locationed _sapiens. """ self._field.clear() for p in range(Stats.S): location = Location( max=self.size) # generate 0 <= random Location < size velocity = Velocity() #color = self._colours[random.randint(0, self._colours.__len__() - 1)] color = self.colours[State.SUSCEPTIBLE] Sapien_new = Sapiens(location, velocity, color, self._field) self._sapiens.append(Sapien_new) # generate random -1 <= random Velocity < 1 # store sapiens with location and velocity for p in range(Stats.I): location = Location(max=self.size) velocity = Velocity() color = self.colours[State.INFECTED] Sapien_new = Sapiens(location, velocity, color, self._field) Sapien_new.r_or_d = Virus.RECOVERY_TIME self._sapiens.append(Sapien_new)
class Simulator(): """Runs the brownian-motion simulation. :author: Peter Sander """ def __init__(self, root: object, size=50): """Create a simulation with the given field size. :root: tkinter.Tk graphics object """ self.size = size self._sapiens = [] # all sapiens in the simulation self._field = Field(size) self.step = 0 self._view = SimulatorView(root, size) self._colours = { State.SUSCEPTIBLE: 'slate blue', State.INFECTED: 'red', State.RECOVERED: 'spring green', State.DEAD: 'black' } self._stats = Stats() self.reset() def runLongSimulation(self) -> None: """Run the simulation from its current state for a reasonably long period, e.g. 500 steps. """ self.simulate(500, 50) def simulate(self, numSapiens=50, delay=1.0) -> None: """Run the simulation from its current state for the given number of steps. :delay: Time (in secs) between each iteration. """ self.step = 0 self.populate(numSapiens) while self._stats.isViable(self._sapiens): self.simulateOneStep() print("R: " + str(self._stats.calculateR(self._sapiens))) # self.step += 1 time.sleep(delay) def simulateOneStep(self) -> None: """Run the simulation from its current state for a single step. """ collisions = Collisions() self.step += 1 # all _sapiens in motion for i in range(len(self._sapiens) - 1): for j in range(i + 1, len(self._sapiens)): if self._sapiens[i].location.row == self._sapiens[j].location.row \ and self._sapiens[i].location.col == self._sapiens[j].location.col: collisions.collisions(self._sapiens[i], self._sapiens[j]) for sapien in self._sapiens: sapien.setColour(self._colours[sapien.state]) sapien.move() sapien.setColour(self._colours[sapien.state]) self._view.showStatus(self.step, self._sapiens, self._stats.state(self._sapiens)) def reset(self): """Reset the simulation to a starting location. """ self.step = 0 self._sapiens = [] self.populate(0) self._view.showStatus(self.step, self._sapiens, self._stats.state(self._sapiens)) def populate(self, numSapiens=50): """Populates the _field with randomly-locationed _sapiens. """ self._field.clear() for s in range(numSapiens): location = Location( None, None, 0, self.size) # generate 0 <= random location < size velocity = Velocity(randrange(-1, 1), randrange( -1, 1)) # generate random -1 <= random velocity < 1 colour = self._colours[State.SUSCEPTIBLE] state = State.SUSCEPTIBLE self._sapiens.append( Sapiens(location, velocity, colour, self._field, state, 0, 0)) # append particle with location and velocity if len(self._sapiens) > 0: shuffle(self._sapiens) self._sapiens[0].state = State.INFECTED
class Simulator(): """Runs the brownian-motion simulation. :author: Peter Sander :author: ZHENG Yannan """ def __init__(self, root: object, size=50, num_particle=50): """Create a simulation with the given field size. :root: tkinter.Tk graphics object """ self.size = size self._particles = [] # all particles in the simulation self._field = Field(size) self.step = 0 self._view = SimulatorView(root, size) self._colours = { State.SUSCEPTIBLE: 'slate blue', State.INFECTED: 'red', State.RECOVERED: 'spring green', State.DEAD: 'black' } self.reset(num_particle) def Collision(self, num_particle): for p in range(num_particle - 1): for q in range(p, num_particle): if abs(self._particles[p].position.row - self._particles[q].position.row) + abs( self._particles[p].position.col - self._particles[q].position.col) == 2: if self._particles[p].colour == 'red' and self._particles[ q].colour == 'slate blue': self._particles[q].colour = 'red' if self._particles[q].colour == 'red' and self._particles[ p].colour == 'slate blue': self._particles[p].colour = 'red' def runLongSimulation(self) -> None: """Run the simulation from its current state for a reasonably long period, e.g. 500 steps. """ self.simulate(500) def simulate(self, numSteps, delay=1) -> None: """Run the simulation from its current state for the given number of steps. :delay: Time (in secs) between each iteration. """ self.step = 1 while self.step <= numSteps: self.simulateOneStep() # self.step += 1 time.sleep(delay) def simulateOneStep(self) -> None: """Run the simulation from its current state for a single step. """ self.step += 1 # all _particles in motion for particle in self._particles: if particle.colour != 'black': if particle.colour == 'red' or 'slate blue': particle.move() particle.cure() if particle.colour == 'spring green': particle.move() self._view.showStatus(self.step, self._particles) self.Collision(50) def reset(self, num_particle): """Reset the simulation to a starting position. """ self.step = 0 self._particles = [] self.populate(num_particle) self._view.showStatus(self.step, self._particles) def populate(self, num_particle=50): """Populates the _field with randomly-positioned _particles. """ self._field.clear() particle_new = Particle(Position(max=self.size), Direction(), self._colours.get(State.INFECTED), self._field) self._particles.append(particle_new) for p in range(num_particle - 1): position = Position( max=self.size) # generate 0 <= random Position < size direction = Direction() color = self._colours.get(State.SUSCEPTIBLE) particle_new = Particle(position, direction, color, self._field) self._particles.append(particle_new)
class Species(object): def __init__(self, name, mass, charge, world): self.name = name self.mass = mass self.charge = charge self.world = world self.den = Field(world.ni, world.nj, world.nj, 1) # returns the number of simulation particles def getNp(self): return particles.size() # returns the number of real particles def getRealCount(self): return 1.0 # returns the species momentum def getMomentum(self): return 1.0 # returns the species kinetic energy def getKE(self): return 1.0 # moves all particles using electric field ef[] def advance(self): #get the time step dt = self.world.getDt() #save mesh bounds x0 = self.world.getX0() xm = self.world.getXm() #continue while particles remain for part in self.particles: #get logical coordinate of particle's position lc = self.world.XtoL(part.pos) #electric field at particle position ef_part = self.world.ef.gather(lc) #update velocity from F=qE part.vel += ef_part * (dt * self.charge / self.mass) #update position from v=dx/dt part.pos += part.vel * dt #did self particle leave the domain? reflect back for i in range(0, 3): if part.pos[i] < x0[i]: part.pos[i] = 2 * x0[i] - part.pos[i] part.vel[i] *= -1.0 elif part.pos[i] >= xm[i]: part.pos[i] = 2 * xm[i] - part.pos[i] part.vel[i] *= -1.0 if self.charge > 0: name = 'ions' else: name = 'electrons' writeParticles(self.particles, name, self.world.ts) return # # compute number density # def computeNumberDensity(self): # den.clear() # for part in self.particles: # lc = world.XtoL(part.pos) # den.scatter(lc, part.mpw) # # # divide by node volume # self.den.data = np.divide(self.den.data,world.node_vol) # adds a new particle def addParticle(self, pos, vel, mpw): # don't do anything (return) if pos outside domain bounds [x0,xd) if (not self.world.inBounds(pos)): return # get particle logical coordinate lc = self.world.XtoL(pos) # evaluate electric field at particle position ef_part = self.world.ef.gather(lc) # rewind velocity back by 0.5*dt*ef vel -= self.charge / self.mass * ef_part * (0.5 * self.world.getDt()) return Particle(pos, vel, mpw) # add to list # random load of particles in a x1-x2 box representing num_den number density def loadParticlesBox(self, x1, x2, num_den, num_mp): return # quiet start load of particles in a x1-x2 box representing num_den number density def loadParticlesBoxQS(self, x1, x2, num_den, num_mp): box_vol = np.prod(np.subtract(x2, x1)) # box num_mp_tot = np.prod(np.subtract( num_mp, np.ones(3))) # total number of simulation particles num_real = num_den * box_vol # number of real particles double mpw = num_real / num_mp_tot # macroparticle weight # compute particle grid spacing d = np.divide(np.subtract(x2, x1), np.subtract(num_mp, np.ones(3))) l = [] # load particles on a equally spaced grid for i in range(0, num_mp[0]): for j in range(0, num_mp[1]): for k in range(0, num_mp[2]): pos = np.add(x1, np.multiply(np.array([i, j, k]), d)) # shift particles on max faces back to the domain if abs(pos[0] - x2[0]) < 1e-15: pos[0] -= 1e-4 * d[0] if abs(pos[1] - x2[1]) < 1e-15: pos[1] -= 1e-4 * d[1] if abs(pos[2] - x2[2]) < 1e-15: pos[2] -= 1e-4 * d[2] w = 1 # relative weight if i == 0 or i == (num_mp[0] - 1): w *= 0.5 if j == 0 or j == (num_mp[1] - 1): w *= 0.5 if k == 0 or k == (num_mp[2] - 1): w *= 0.5 # add rewind vel = np.zeros(3) # particle is stationary p = self.addParticle(pos, vel, mpw * w) # add a new particle to the array l.append(p) self.particles = l #TODO: check density evaluation at ts = 1 # particles OK def computeNumberDensity(self): self.den.clear() if self.charge > 0: spname = 'ion' else: spname = 'el' name = spname + 'NumDens' volname = spname + 'VolDens' denname = spname + '_den' for part in self.particles: lc = self.world.XtoL(part.pos) self.den.scatter(lc, part.mpw) write_3D(self.world, self.den.data, denname, self.world.getTs(), 0) write_3D(self.world, self.world.node_vol, volname, self.world.getTs(), 0) # divide by node volume self.den.data = np.divide(self.den.data, self.world.node_vol) write_3D(self.world, self.den.data, name, self.world.getTs(), 0) return