# Copyright 2017-2019 David Grote # # This file is part of WarpX. # # License: BSD-3-Clause-LBNL import numpy as np from ._libwarpx import libwarpx from ._picmi import constants class PGroup(object): """Implements a class that has the same API as a warp ParticleGroup instance. """ def __init__(self, igroup, ispecie, level=0): self.igroup = igroup self.ispecie = ispecie self.level = level self.ns = 1 # Number of species self.gallot() def name(self): return 'WarpXParticleGroup' def gallot(self): self.lebcancel_pusher = 0 # turns on/off cancellation of E+VxB within V push (logical type) self.lebcancel = 0 # turns on/off cancellation of E+VxB before V push (logical type) self.sm = np.zeros(self.ns) # Species mass [kg] self.sq = np.zeros(self.ns) # Species charge [C] self.sw = np.ones(self.ns) # Species weight, (real particles per simulation particles) self.sid = np.arange(self.ns, dtype=int) # Global species index for each species self.ndts = np.ones(self.ns, dtype=int) # Stride for time step advance for each species self.ldts = np.ones(self.ns, dtype=int) # (logical type) self.lvdts = np.ones(self.ns, dtype=int) # (logical type) self.iselfb = np.zeros(self.ns, dtype=int) # Group number for particles that are affected by # their own magnetic field, using the 1/gamma**2 # approximation. The correction is not applied to # group number -1. self.fselfb = np.zeros(self.ns) # The scaling factor, vz. self.l_maps = np.zeros(self.ns) self.dtscale = np.ones(self.ns) # Scale factor applied to time step size for each # species. Only makes sense in steaday and and # transverse slice modes. self.limplicit = np.zeros(self.ns, dtype=int) # Flags implicit particle species (logical type) self.iimplicit = np.full(self.ns, -1, dtype=int) # Group number for implicit particles self.ldoadvance = np.ones(self.ns, dtype=int) # Flags whether particles are time advanced (logical type) self.lboundaries = np.ones(self.ns, dtype=int) # Flags whether boundary conditions need to be applied (logical type) self.lparaxial = np.zeros(self.ns, dtype=int) # Flags to turn on/off paraxial approximation (logical type) self.zshift = np.zeros(self.ns) self.gamma_ebcancel_max = np.ones(self.ns) # maximum value allowed for ExB cancellation # --- Temporary fix gchange = gallot def allocated(self, name): return True def addspecies(self): pass def getnpid(self): return libwarpx.get_nattr() npid = property(getnpid) def getnps(self): return np.array([len(self.xp)], dtype='l') nps = property(getnps) def getins(self): return np.ones(self.ns, dtype='l') ins = property(getins) def getipmax(self): return np.array([0, len(self.xp)], dtype='l') ipmax = property(getipmax) def getnpmax(self): return self.nps.sum() npmax = property(getnpmax) def getxp(self): return libwarpx.get_particle_x(self.ispecie, self.level)[self.igroup] xp = property(getxp) def getyp(self): return libwarpx.get_particle_y(self.ispecie, self.level)[self.igroup] yp = property(getyp) def getrp(self): return libwarpx.get_particle_r(self.ispecie, self.level)[self.igroup] rp = property(getrp) def getzp(self): return libwarpx.get_particle_z(self.ispecie, self.level)[self.igroup] zp = property(getzp) def getuxp(self): return libwarpx.get_particle_ux(self.ispecie, self.level)[self.igroup] uxp = property(getuxp) def getuyp(self): return libwarpx.get_particle_uy(self.ispecie, self.level)[self.igroup] uyp = property(getuyp) def getuzp(self): return libwarpx.get_particle_uz(self.ispecie, self.level)[self.igroup] uzp = property(getuzp) def getw(self): return libwarpx.get_particle_weight(self.ispecie, self.level)[self.igroup] def getpid(self, id): pid = libwarpx.get_particle_arrays(self.ispecie, id, self.level)[self.igroup] return np.array([pid]).T def getgaminv(self): uxp = self.getuxp() uyp = self.getuyp() uzp = self.getuzp() return np.sqrt(1. - (uxp**2 + uyp**2 + uzp**2)/constants.c**2) gaminv = property(getgaminv) def getex(self): raise Exception('Particle E fields not supported') ex = property(getex) def getey(self): raise Exception('Particle E fields not supported') ey = property(getey) def getez(self): raise Exception('Particle E fields not supported') ez = property(getez) def getbx(self): raise Exception('Particle B fields not supported') bx = property(getbx) def getby(self): raise Exception('Particle B fields not supported') by = property(getby) def getbz(self): raise Exception('Particle B fields not supported') bz = property(getbz) def gettheta(self): return libwarpx.get_particle_theta(self.ispecie, self.level)[self.igroup] theta = property(gettheta) class PGroups(object): def __init__(self, ispecie=0, level=0): self.ispecie = ispecie self.level = level def setuppgroups(self): xall = libwarpx.get_particle_x(self.ispecie, self.level) self.ngroups = len(xall) self._pgroups = [] for igroup in range(self.ngroups): self._pgroups.append(PGroup(igroup, self.ispecie, self.level)) def __iter__(self): self.setuppgroups() for igroup in range(self.ngroups): yield self._pgroups[igroup] def __getitem__(self, key): self.setuppgroups() return self._pgroups[key] def __len__(self): self.setuppgroups() return len(self._pgroups)