aboutsummaryrefslogtreecommitdiff
path: root/Python/pywarpx
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pywarpx')
-rw-r--r--Python/pywarpx/AMReX.py16
-rw-r--r--Python/pywarpx/PGroup.py5
-rw-r--r--Python/pywarpx/WarpXPIC.py50
-rw-r--r--Python/pywarpx/__init__.py3
-rw-r--r--Python/pywarpx/callbacks.py481
-rw-r--r--Python/pywarpx/timestepper.py329
6 files changed, 562 insertions, 322 deletions
diff --git a/Python/pywarpx/AMReX.py b/Python/pywarpx/AMReX.py
index 4ce7ac61c..55c11c432 100644
--- a/Python/pywarpx/AMReX.py
+++ b/Python/pywarpx/AMReX.py
@@ -16,8 +16,8 @@ from ._libwarpx import amrex_init
class AMReX(object):
- def init(self):
- argv = ['warpx']
+ def create_argv_list(self):
+ argv = []
argv += warpx.attrlist()
argv += amr.attrlist()
argv += geometry.attrlist()
@@ -36,7 +36,19 @@ class AMReX(object):
for particle in particles_list:
argv += particle.attrlist()
+ return argv
+
+ def init(self):
+ argv = ['warpx'] + self.create_argv_list()
amrex_init(argv)
def finalize(self, finalize_mpi=1):
libwarpx.amrex_finalize(finalize_mpi)
+
+ def write_inputs(self, filename='inputs'):
+ argv = self.create_argv_list()
+ with open(filename, 'w') as ff:
+
+ for arg in argv:
+ ff.write('{0}\n'.format(arg))
+
diff --git a/Python/pywarpx/PGroup.py b/Python/pywarpx/PGroup.py
index fa8572c0a..64e396d38 100644
--- a/Python/pywarpx/PGroup.py
+++ b/Python/pywarpx/PGroup.py
@@ -148,7 +148,7 @@ class PGroups(object):
self._pgroups = []
for igroup in range(self.ngroups):
self._pgroups.append(PGroup(igroup, self.ispecie))
-
+
def __iter__(self):
self.setuppgroups()
for igroup in range(self.ngroups):
@@ -158,3 +158,6 @@ class PGroups(object):
self.setuppgroups()
return self._pgroups[key]
+ def __len__(self):
+ self.setuppgroups()
+ return len(self._pgroups)
diff --git a/Python/pywarpx/WarpXPIC.py b/Python/pywarpx/WarpXPIC.py
new file mode 100644
index 000000000..77ab8464f
--- /dev/null
+++ b/Python/pywarpx/WarpXPIC.py
@@ -0,0 +1,50 @@
+from warp.run_modes.timestepper import PICAPI
+from ._libwarpx import libwarpx
+
+class WarpXPIC(PICAPI):
+
+ def get_time(self):
+ return libwarpx.warpx_gett_new(0)
+
+ def set_time(self, time):
+ for i in range(libwarpx.warpx_finestLevel()+1):
+ libwarpx.warpx_sett_new(i, time)
+
+ def get_step_size(self):
+ libwarpx.warpx_ComputeDt()
+ return libwarpx.warpx_getdt(0)
+
+ def get_step_number(self):
+ return libwarpx.warpx_getistep(0)
+
+ def set_step_number(self, it):
+ for i in range(libwarpx.warpx_finestLevel()+1):
+ libwarpx.warpx_setistep(i, it)
+
+ def push_positions(self, dt):
+ libwarpx.warpx_PushX(0, dt)
+
+ def push_velocities_withE(self, dt):
+ libwarpx.warpx_EPushV(0, dt)
+
+ def push_velocities_withB(self, dt):
+ libwarpx.warpx_BPushV(0, dt)
+
+ def get_self_fields(self):
+ libwarpx.warpx_FieldGather(0)
+
+ def calculate_source(self):
+ libwarpx.warpx_CurrentDeposition(0)
+
+ def push_Efields(self, dt):
+ libwarpx.warpx_EvolveE(0, dt)
+ libwarpx.warpx_FillBoundaryE(0, True)
+
+ def push_Bfields(self, dt):
+ libwarpx.warpx_EvolveB(0, dt)
+ libwarpx.warpx_FillBoundaryB(0, True)
+
+ def apply_particle_boundary_conditions(self):
+ libwarpx.mypc_Redistribute() # Redistribute particles
+ libwarpx.warpx_MoveWindow() # !!! not the correct place yet
+
diff --git a/Python/pywarpx/__init__.py b/Python/pywarpx/__init__.py
index bddbb78b7..a4c83deab 100644
--- a/Python/pywarpx/__init__.py
+++ b/Python/pywarpx/__init__.py
@@ -12,5 +12,8 @@ from .AMReX import AMReX
#from .timestepper import TimeStepper
from .PGroup import PGroup
from .PGroup import PGroups
+from .WarpXPIC import WarpXPIC
from ._libwarpx import add_particles
+
+from .callbacks import *
diff --git a/Python/pywarpx/callbacks.py b/Python/pywarpx/callbacks.py
new file mode 100644
index 000000000..68629d164
--- /dev/null
+++ b/Python/pywarpx/callbacks.py
@@ -0,0 +1,481 @@
+"""call back operations
+=====================
+
+These are the functions which allow installing user created functions so that
+they are called at various places along the time step.
+
+For each call back type, the following three functions are defined.
+ - install___: Installs a function to be called at that specified time
+ - uninstall___: Uninstalls the function (so it won't be called anymore)
+ - isinstalled___: Checks if the function is installed
+
+These functions all take a function or instance method as an argument. Note that
+if an instance method is used, an extra reference to the method's object is saved.
+
+The install can be done using a decorator, which has the prefix "callfrom". See example below.
+
+Functions can be called at the following times:
+ - afterinit <installafterinit>: immediately after the init is complete
+ - beforeEsolve <installbeforeEsolve>: before the solve for E fields
+ - afterEsolve <installafterEsolve>: after the solve for E fields
+ - beforedeposition <installbeforedeposition>: before the particle deposition (for charge and/or current)
+ - afterdeposition <installafterdeposition>: after particle deposition (for charge and/or current)
+ - beforestep <installbeforestep>: before the time step
+ - afterstep <installafterstep>: after the time step
+ - particlescraper <installparticlescraper>: just after the particle boundary conditions are applied
+ but before lost particles are processed
+ - particleloader <installparticleloader>: at the time that the standard particle loader is called
+ - particleinjection <installparticleinjection>: called when particle injection happens, after the position
+ advance and before deposition is called, allowing a user defined
+ particle distribution to be injected each time step
+ - appliedfields <installappliedfields>: allows directly specifying any fields to be applied to the particles
+ during the advance
+
+To use a decorator, the syntax is as follows. This will install the function myplots to be called after each step.
+
+@callfromafterstep
+def myplots():
+ ppzx()
+
+This is equivalent to the following:
+
+def myplots():
+ ppzx()
+installafterstep(myplots)
+
+"""
+from __future__ import generators
+
+import types
+import copy
+import time
+import ctypes
+import sys
+import numpy
+
+from ._libwarpx import libwarpx
+
+class CallbackFunctions(object):
+ """
+ Class to handle call back function lists.
+
+ Note that for functions passed in that are methods of a class instance,
+ a full reference of the instance is saved. This extra reference means
+ that the object will not actually deleted if the user deletes the
+ original reference. This is good since the user does not need to keep
+ the reference to the object (for example it can be created using a local
+ variable in a function). It may be bad if the user thinks an object was
+ deleted, but it actually isn't since it had (unkown to the user)
+ installed a method in one of the call back lists
+ """
+
+ def __init__(self,name=None,lcallonce=0):
+ self.funcs = []
+ self.time = 0.
+ self.timers = {}
+ self.name = name
+ self.lcallonce = lcallonce
+
+ def __call__(self,*args,**kw):
+ "Call all of the functions in the list"
+ tt = self.callfuncsinlist(*args,**kw)
+ self.time = self.time + tt
+ if self.lcallonce: self.funcs = []
+
+ def clearlist(self):
+ self.funcs = []
+
+ def __nonzero__(self):
+ "Returns True if functions are installed, otherwise False"
+ return self.hasfuncsinstalled()
+
+ def __len__(self):
+ "Returns number of functions installed"
+ return len(self.funcs)
+
+ def hasfuncsinstalled(self):
+ "Checks if there are any functions installed"
+ return len(self.funcs) > 0
+
+ def _getmethodobject(self,func):
+ "For call backs that are methods, returns the method's instance"
+ return func[0]
+
+ def callbackfunclist(self):
+ "Generator returning callable functions from the list"
+ funclistcopy = copy.copy(self.funcs)
+ for f in funclistcopy:
+ if isinstance(f,list):
+ object = self._getmethodobject(f)
+ if object is None:
+ self.funcs.remove(f)
+ continue
+ result = getattr(object,f[1])
+ elif isinstance(f,basestring):
+ import __main__
+ if f in __main__.__dict__:
+ result = __main__.__dict__[f]
+ # --- If the function with the name is found, then replace the
+ # --- name in the list with the function.
+ self.funcs[self.funcs.index(f)] = result
+ else:
+ continue
+ else:
+ result = f
+ if not callable(result):
+ print("\n\nWarning: a call back was found that is not callable.")
+ if self.name is not None:
+ print("For %s"%self.name)
+ print("Only callable objects can be installed.")
+ print("It is possible that the callable's name has been overwritten")
+ print("by something not callable. This can happen during restart")
+ print("if a function name had later been used as a variable name.")
+ print(self.name)
+ if isinstance(f,basestring):
+ print("The name of the call back is %s"%f)
+ print("\n\n")
+ continue
+ yield result
+
+ def installfuncinlist(self,f):
+ "Check if the specified function is installed"
+ if isinstance(f,types.MethodType):
+ # --- If the function is a method of a class instance, then save a full
+ # --- reference to that instance and the method name.
+ finstance = f.im_self
+ fname = f.__name__
+ self.funcs.append([finstance,fname])
+ elif callable(f):
+ # --- If a function had already been installed by name, then skip the install.
+ # --- This is problematic, since no warning message is given, but it is unlikely
+ # --- to arise under normal circumstances.
+ # --- The purpose of this check is to avoid redundant installation of functions
+ # --- during a restore from a dump file. Without the check, functions that had been
+ # --- installed via a decorator would be installed an extra time since the source
+ # --- of the function contains the decoration (which is activated when the source
+ # --- is exec'd).
+ if f.__name__ not in self.funcs:
+ self.funcs.append(f)
+ else:
+ self.funcs.append(f)
+
+ def uninstallfuncinlist(self,f):
+ "Uninstall the specified function"
+ # --- An element by element search is needed
+ # --- f can be a function or method object, or a name (string).
+ # --- Note that method objects can not be removed by name.
+ funclistcopy = copy.copy(self.funcs)
+ for func in funclistcopy:
+ if f == func:
+ self.funcs.remove(f)
+ return
+ elif isinstance(func,list) and isinstance(f,types.MethodType):
+ object = self._getmethodobject(func)
+ if f.im_self is object and f.__name__ == func[1]:
+ self.funcs.remove(func)
+ return
+ elif isinstance(func,basestring):
+ if f.__name__ == func:
+ self.funcs.remove(func)
+ return
+ elif isinstance(f,basestring):
+ if isinstance(func,basestring): funcname = func
+ elif isinstance(func,list): funcname = None
+ else: funcname = func.__name__
+ if f == funcname:
+ self.funcs.remove(func)
+ return
+ raise Exception('Warning: no such function had been installed')
+
+ def isinstalledfuncinlist(self,f):
+ "Checks if the specified function is installed"
+ # --- An element by element search is needed
+ funclistcopy = copy.copy(self.funcs)
+ for func in funclistcopy:
+ if f == func:
+ return 1
+ elif isinstance(func,list) and isinstance(f,types.MethodType):
+ object = self._getmethodobject(func)
+ if f.im_self is object and f.__name__ == func[1]:
+ return 1
+ elif isinstance(func,basestring):
+ if f.__name__ == func:
+ return 1
+ return 0
+
+ def callfuncsinlist(self,*args,**kw):
+ "Call the functions in the list"
+ bb = time.time()
+ for f in self.callbackfunclist():
+ #barrier()
+ t1 = time.time()
+ f(*args,**kw)
+ #barrier()
+ t2 = time.time()
+ # --- For the timers, use the function (or method) name as the key.
+ self.timers[f.__name__] = self.timers.get(f.__name__,0.) + (t2 - t1)
+ aa = time.time()
+ return aa - bb
+
+#=============================================================================
+
+# --- Now create the actual instances.
+_afterinit = CallbackFunctions('afterinit')
+_beforeEsolve = CallbackFunctions('beforeEsolve')
+_afterEsolve = CallbackFunctions('afterEsolve')
+_beforedeposition = CallbackFunctions('beforedeposition')
+_afterdeposition = CallbackFunctions('afterdeposition')
+_particlescraper = CallbackFunctions('particlescraper')
+_particleloader = CallbackFunctions('particleloader')
+_beforestep = CallbackFunctions('beforestep')
+_afterstep = CallbackFunctions('afterstep')
+_afterrestart = CallbackFunctions('afterrestart',lcallonce=1)
+_particleinjection = CallbackFunctions('particleinjection')
+_appliedfields = CallbackFunctions('appliedfields')
+
+# --- Create the objects that can be called from C.
+# --- Note that each of the CFUNCTYPE instances need to be saved
+_CALLBACK_FUNC_0 = ctypes.CFUNCTYPE(None)
+_c_afterinit = _CALLBACK_FUNC_0(_afterinit)
+libwarpx.warpx_set_callback_py_afterinit(_c_afterinit)
+_c_beforeEsolve = _CALLBACK_FUNC_0(_beforeEsolve)
+libwarpx.warpx_set_callback_py_beforeEsolve(_c_beforeEsolve)
+_c_afterEsolve = _CALLBACK_FUNC_0(_afterEsolve)
+libwarpx.warpx_set_callback_py_afterEsolve(_c_afterEsolve)
+_c_beforedeposition = _CALLBACK_FUNC_0(_beforedeposition)
+libwarpx.warpx_set_callback_py_beforedeposition(_c_beforedeposition)
+_c_afterdeposition = _CALLBACK_FUNC_0(_afterdeposition)
+libwarpx.warpx_set_callback_py_afterdeposition(_c_afterdeposition)
+_c_particlescraper = _CALLBACK_FUNC_0(_particlescraper)
+libwarpx.warpx_set_callback_py_particlescraper(_c_particlescraper)
+_c_particleloader = _CALLBACK_FUNC_0(_particleloader)
+libwarpx.warpx_set_callback_py_particleloader(_c_particleloader)
+_c_beforestep = _CALLBACK_FUNC_0(_beforestep)
+libwarpx.warpx_set_callback_py_beforestep(_c_beforestep)
+_c_afterstep = _CALLBACK_FUNC_0(_afterstep)
+libwarpx.warpx_set_callback_py_afterstep(_c_afterstep)
+_c_afterrestart = _CALLBACK_FUNC_0(_afterrestart)
+libwarpx.warpx_set_callback_py_afterrestart(_c_afterrestart)
+_c_particleinjection = _CALLBACK_FUNC_0(_particleinjection)
+libwarpx.warpx_set_callback_py_particleinjection(_c_particleinjection)
+_c_appliedfields = _CALLBACK_FUNC_0(_appliedfields)
+libwarpx.warpx_set_callback_py_appliedfields(_c_appliedfields)
+
+#=============================================================================
+def printcallbacktimers(tmin=1.,lminmax=False,ff=None):
+ """Prints timings of installed functions.
+ - tmin=1.: only functions with time greater than tmin will be printed
+ - lminmax=False: If True, prints the min and max times over all processors
+ - ff=None: If given, timings will be written to the file object instead of stdout
+ """
+ if ff is None: ff = sys.stdout
+ for c in [_afterinit,_beforeEsolve,_afterEsolve,
+ _beforedeposition,_afterdeposition,
+ _particlescraper,
+ _particleloader,
+ _beforestep,_afterstep,
+ _afterrestart,
+ _particleinjection,
+ _appliedfields]:
+ for fname, time in c.timers.items():
+ #vlist = numpy.array(gather(time))
+ vlist = numpy.array([time])
+ #if me > 0: continue
+ vsum = numpy.sum(vlist)
+ if vsum <= tmin: continue
+ vrms = numpy.sqrt(max(0.,numpy.sum(vlist**2)/len(vlist) - (numpy.sum(vlist)/len(vlist))**2))
+ npes = 1. # Only works for one processor
+ ff.write('%20s %s %10.4f %10.4f %10.4f'%(c.name,fname,vsum,vsum/npes,vrms))
+ if lminmax:
+ vmin = numpy.min(vlist)
+ vmax = numpy.max(vlist)
+ ff.write(' %10.4f %10.4f'%(vmin,vmax))
+ it = libwarpx.warpx_getistep(0)
+ if it > 0:
+ ff.write(' %10.4f'%(vsum/npes/(it)))
+ ff.write('\n')
+
+#=============================================================================
+# ----------------------------------------------------------------------------
+def callfromafterinit(f):
+ installafterinit(f)
+ return f
+def installafterinit(f):
+ "Adds a function to the list of functions called after the init"
+ _afterinit.installfuncinlist(f)
+def uninstallafterinit(f):
+ "Removes the function from the list of functions called after the init"
+ _afterinit.uninstallfuncinlist(f)
+def isinstalledafterinit(f):
+ "Checks if the function is called after a init"
+ return _afterinit.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfrombeforeEsolve(f):
+ installbeforeEsolve(f)
+ return f
+def installbeforeEsolve(f):
+ "Adds a function to the list of functions called before an E solve"
+ _beforeEsolve.installfuncinlist(f)
+def uninstallbeforeEsolve(f):
+ "Removes the function from the list of functions called before an E solve"
+ _beforeEsolve.uninstallfuncinlist(f)
+def isinstalledbeforeEsolve(f):
+ "Checks if the function is called before an E solve"
+ return _beforeEsolve.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromafterEsolve(f):
+ installafterEsolve(f)
+ return f
+def installafterEsolve(f):
+ "Adds a function to the list of functions called after an E solve"
+ _afterEsolve.installfuncinlist(f)
+def uninstallafterEsolve(f):
+ "Removes the function from the list of functions called after an E solve"
+ _afterEsolve.uninstallfuncinlist(f)
+def isinstalledafterEsolve(f):
+ "Checks if the function is called after an E solve"
+ return _afterEsolve.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfrombeforedeposition(f):
+ installbeforedeposition(f)
+ return f
+def installbeforedeposition(f):
+ "Adds a function to the list of functions called before a particle deposition"
+ _beforedeposition.installfuncinlist(f)
+def uninstallbeforedeposition(f):
+ "Removes the function from the list of functions called before a particle deposition"
+ _beforedeposition.uninstallfuncinlist(f)
+def isinstalledbeforedeposition(f):
+ "Checks if the function is called before a particle deposition"
+ return _beforedeposition.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromafterdeposition(f):
+ installafterdeposition(f)
+ return f
+def installafterdeposition(f):
+ "Adds a function to the list of functions called after a particle deposition"
+ _afterdeposition.installfuncinlist(f)
+def uninstallafterdeposition(f):
+ "Removes the function from the list of functions called after a particle deposition"
+ _afterdeposition.uninstallfuncinlist(f)
+def isinstalledafterdeposition(f):
+ "Checks if the function is called after a particle deposition"
+ return _afterdeposition.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromparticlescraper(f):
+ installparticlescraper(f)
+ return f
+def installparticlescraper(f):
+ "Adds a function to the list of functions called to scrape particles"
+ _particlescraper.installfuncinlist(f)
+def uninstallparticlescraper(f):
+ "Removes the function from the list of functions called to scrape particles"
+ _particlescraper.uninstallfuncinlist(f)
+def isinstalledparticlescraper(f):
+ "Checks if the function is called to scrape particles"
+ return _particlescraper.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromparticleloader(f):
+ installparticleloader(f)
+ return f
+def installparticleloader(f):
+ "Adds a function to the list of functions called to load particles"
+ _particleloader.installfuncinlist(f)
+def uninstallparticleloader(f):
+ "Removes the function from the list of functions called to load particles"
+ _particleloader.uninstallfuncinlist(f)
+def isinstalledparticleloader(f):
+ "Checks if the function is called to load particles"
+ return _particleloader.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfrombeforestep(f):
+ installbeforestep(f)
+ return f
+def installbeforestep(f):
+ "Adds a function to the list of functions called before a step"
+ _beforestep.installfuncinlist(f)
+def uninstallbeforestep(f):
+ "Removes the function from the list of functions called before a step"
+ _beforestep.uninstallfuncinlist(f)
+def isinstalledbeforestep(f):
+ "Checks if the function is called before a step"
+ return _beforestep.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromafterstep(f):
+ installafterstep(f)
+ return f
+def installafterstep(f):
+ "Adds a function to the list of functions called after a step"
+ _afterstep.installfuncinlist(f)
+def uninstallafterstep(f):
+ "Removes the function from the list of functions called after a step"
+ _afterstep.uninstallfuncinlist(f)
+def isinstalledafterstep(f):
+ "Checks if the function is called after a step"
+ return _afterstep.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromafterrestart(f):
+ raise Exception('restart call back not implemented yet')
+ installafterrestart(f)
+ return f
+def installafterrestart(f):
+ "Adds a function to the list of functions called immediately after a restart"
+ raise Exception('restart call back not implemented yet')
+ _afterrestart.installfuncinlist(f)
+def uninstallafterrestart(f):
+ "Removes the function from the list of functions called immediately after a restart"
+ raise Exception('restart call back not implemented yet')
+ _afterrestart.uninstallfuncinlist(f)
+def isinstalledafterrestart(f):
+ "Checks if the function is called immediately after a restart"
+ raise Exception('restart call back not implemented yet')
+ return _afterrestart.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromparticleinjection(f):
+ installparticleinjection(f)
+ return f
+def installparticleinjection(f):
+ """
+ Adds a user defined function that is to be called when particle
+ injection happens, after the position advance and before deposition is
+ called, allowing a user defined particle distribution to be injected
+ each time step"""
+ _particleinjection.installfuncinlist(f)
+def uninstallparticleinjection(f):
+ "Removes the function installed by installparticleinjection"
+ _particleinjection.uninstallfuncinlist(f)
+def isinstalledparticleinjection(f):
+ "Checks if the function is called when particles injection happens"
+ return _particleinjection.isinstalledfuncinlist(f)
+
+# ----------------------------------------------------------------------------
+def callfromappliedfields(f):
+ raise Exception('applied fields call back not implemented yet')
+ installappliedfields(f)
+ return f
+def installappliedfields(f):
+ """
+ Adds a user defined function which can specify E and B fields which are applied
+ to the particles during the particle advance.
+ """
+ raise Exception('applied fields call back not implemented yet')
+ _appliedfields.installfuncinlist(f)
+def uninstallappliedfields(f):
+ "Removes the function installed by installappliedfields"
+ raise Exception('applied fields call back not implemented yet')
+ _appliedfields.uninstallfuncinlist(f)
+def isinstalledappliedfields(f):
+ "Checks if the function is called when which applies fields"
+ raise Exception('applied fields call back not implemented yet')
+ return _appliedfields.isinstalledfuncinlist(f)
+
diff --git a/Python/pywarpx/timestepper.py b/Python/pywarpx/timestepper.py
index f72b1669f..fe58c623f 100644
--- a/Python/pywarpx/timestepper.py
+++ b/Python/pywarpx/timestepper.py
@@ -1,8 +1,5 @@
-import warp
-from warp import top
-from warp import w3d
-
from ._libwarpx import libwarpx
+import .callbacks
class TimeStepper(object):
@@ -12,6 +9,8 @@ class TimeStepper(object):
def onestep(self):
+ callbacks._beforestep()
+
self.cur_time = libwarpx.warpx_gett_new(0)
self.istep = libwarpx.warpx_getistep(0)
@@ -33,7 +32,11 @@ class TimeStepper(object):
# --- Evolve particles to p^{n+1/2} and x^{n+1}
# --- Depose current, j^{n+1/2}
+ callbacks._particleinjection()
+ callbacks._particlescraper()
+ callbacks._beforedeposition()
libwarpx.warpx_PushParticlesandDepose(self.cur_time)
+ callbacks._afterdeposition()
libwarpx.mypc_Redistribute() # Redistribute particles
@@ -43,7 +46,9 @@ class TimeStepper(object):
libwarpx.warpx_SyncCurrent()
libwarpx.warpx_FillBoundaryB()
+ callbacks._beforeEsolve()
libwarpx.warpx_EvolveE(dt,0) # We now have E^{n+1}
+ callbacks._afterEsolve()
self.istep += 1
@@ -67,318 +72,4 @@ class TimeStepper(object):
if libwarpx.warpx_checkInt() > 0 and (self.istep+1)%libwarpx.warpx_plotInt() == 0 or max_time_reached:
libwarpx.warpx_WriteCheckPointFile()
-
-
-
-
-
-
-
-
-
-
-
-# --- This is not used
-class TimeStepperFromPICSAR(object):
-
- def __init__(self, package=None, solver=None, l_debug=False):
- self.package = package
- self.solver = solver
- self.l_debug = l_debug
-
- def setpackagestepnumber(self, it):
- if self.package is not None:
- self.package.setstepnumber(it)
-
- def step(self, n=1, freq_print=10, lallspecl=0):
- """
- This function performs a range of Particle-In-Cell iterations
-
- Inputs:
- - n: number of iterations
- - freq_print: print frequency
- """
-
- if (self.l_debug): print("Call step")
-
- for i in range(n):
- if(me == 0):
- if top.it%freq_print == 0:
- print('it = %g time = %g'%(top.it, top.time))
-
- l_first = (lallspecl or (i == 0))
- l_last = (lallspecl or (i == n-1))
-
- self.onestep(l_first, l_last)
-
- if (self.l_debug): print("End step")
-
- def onestep(self, l_first, l_last):
- """
- Perform a single particle-in-cell step
- """
-
- if (self.l_debug): print("Call onestep")
-
- # --- Iteration number
- self.setpackagestepnumber(top.it)
-
- # --- call beforestep functions
- if (self.l_debug): print("Call beforestep functions")
- warp.callbeforestepfuncs.callfuncsinlist()
-
- # --- gather fields from grid to particles
- if (self.l_debug): print("Call Field gathering and particle push")
-
- # --- push
- if l_first:
- if self.package is None:
- # --- Standard Warp advance
- for specie in warp.listofallspecies:
- for pg in specie.flatten(specie.pgroups):
- for js in range(pg.ns):
- self.push_velocity_second_part(js, pg)
- self.push_positions(js, pg)
- warp.particleboundaries3d(pg, -1, False)
-
- else:
- # --- Particle pusher
- if (self.l_debug): print("Call package particle push")
- pxr.pxrpush_particles_part2()
-
- # --- Particle boundary consitions
- if (self.l_debug): print("Call package particle boundary conditions")
- pxr.particle_bcs()
-
- if (self.l_debug): print("Call aliasparticlearrays()")
- self.aliasparticlearrays()
-
- else:
- if self.package is None:
- # --- Standard Warp advance
-
- for specie in warp.listofallspecies:
- for pg in specie.flatten(specie.pgroups):
- for js in range(pg.ns):
- self.push_velocity_full(js, pg)
- self.push_positions(js, pg)
-
- warp.particleboundaries3d(pg, -1, False)
-
- else:
- # --- Particle pusher
- if (self.l_debug): print("Call package particle pusher")
- pxr.field_gathering_plus_particle_pusher()
-
- # --- Particle boundary conditions
- if (self.l_debug): print("Call package particle boundary conditions")
- pxr.particle_bcs()
-
- self.aliasparticlearrays()
-
- # --- Particle sorting
- if (self.l_debug): print("Call Particle Sorting")
- if self.package is not None:
- # --- This should be a function installed before load rho
- pxr.particle_sorting_sub()
-
- # --- call beforeloadrho functions
- if (self.l_debug): print("Call beforeloadrho functions")
- warp.beforeloadrho.callfuncsinlist()
-
- pgroups = []
- for specie in warp.listofallspecies:
- pgroups += specie.flatten(specie.pgroups)
-
- self.pgroups = pgroups
-
- # --- Call user-defined injection routines
- if (self.l_debug): print("Call user-defined injection routines")
- warp.userinjection.callfuncsinlist()
-
- xgriddiff = w3d.xmmin - pxr.xmin
- ygriddiff = w3d.ymmin - pxr.ymin
- zgriddiff = w3d.zmmin - pxr.zmin
-
- if (xgriddiff != 0 or ygriddiff != 0 or zgriddiff != 0):
- pxr.pxr_move_sim_boundaries(xgriddiff, ygriddiff, zgriddiff)
-
-
- if (self.l_debug): print("Call loadrho")
- self.solver.loadrho(pgroups = pgroups)
- if (self.l_debug): print("Call loadj")
- self.solver.loadj(pgroups = pgroups)
-
- if (self.l_debug): print("Call dosolve")
- self.solver.dosolve()
-
- if self.package is None:
- for specie in warp.listofallspecies:
- for pg in specie.flatten(specie.pgroups):
- for js in range(pg.ns):
- self.fetcheb(js, pg)
- if l_last:
- self.push_velocity_first_part(js, pg)
-
- else:
- if l_last:
- if (self.l_debug): print("Call package push particles 1")
- pxr.pxrpush_particles_part1()
-
- # --- update time, time counter
- top.time += top.dt
- if top.it%top.nhist == 0:
-# zmmnt()
- minidiag(top.it, top.time, top.lspecial)
- top.it += 1
-
- # --- Load balance function should be installed after step
-
- # --- call afterstep functions
- if (self.l_debug): print("Call callafterstepfuncs.callfuncsinlist()")
- warp.callafterstepfuncs.callfuncsinlist()
-
-
- # --- The following methods use the standard Warp routines
- def fetcheb(self, js, pg=None):
- if self.l_verbose:print(me, 'enter fetcheb')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- w3d.pgroupfsapi = pg
- w3d.ipminfsapi = pg.ins[js]
- w3d.npfsapi = pg.nps[js]
- pg.ex[il:iu] = 0.
- pg.ey[il:iu] = 0.
- pg.ez[il:iu] = 0.
- pg.bx[il:iu] = 0.
- pg.by[il:iu] = 0.
- pg.bz[il:iu] = 0.
- self.fetche()
- self.fetchb()
- w3d.pgroupfsapi = None
-
- def push_velocity_full(self, js, pg=None):
- if self.l_verbose:print(me, 'enter push_velocity_full')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- if pg.lebcancel_pusher:
- warp.ebcancelpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], top.dt, 0)
- else:
- # --- push velocity from electric field (half step)
- warp.epush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt)
- # --- update gamma
- self.set_gamma(js, pg)
- # --- push velocity from magnetic field
- warp.bpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], top.dt, top.ibpush)
- # --- push velocity from electric field (half step)
- warp.epush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt)
- # --- update gamma
- self.set_gamma(js, pg)
-
- if self.l_verbose:print(me, 'exit push_velocity_first_part')
-
- def push_velocity_first_part(self, js, pg=None):
- if self.l_verbose:print(me, 'enter push_velocity_first_part')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- if pg.lebcancel_pusher:
- warp.ebcancelpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], top.dt, 1)
- else:
- # --- push velocity from electric field (half step)
- warp.epush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt)
- # --- update gamma
- self.set_gamma(js,pg)
- # --- push velocity from magnetic field
- warp.bpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt, top.ibpush)
-
- if self.l_verbose:print(me, 'exit push_velocity_first_part')
-
- def push_velocity_second_part(self, js, pg=None):
- if self.l_verbose:print(me, 'enter push_velocity_second_part')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- if pg.lebcancel_pusher:
- warp.ebcancelpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], top.dt, 2)
- else:
- # --- push velocity from magnetic field
- warp.bpush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], pg.gaminv[il:iu],
- pg.bx[il:iu], pg.by[il:iu], pg.bz[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt, top.ibpush)
- # --- push velocity from electric field (half step)
- warp.epush3d(np, pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu],
- pg.ex[il:iu], pg.ey[il:iu], pg.ez[il:iu],
- pg.sq[js], pg.sm[js], 0.5*top.dt)
- # --- update gamma
- self.set_gamma(js, pg)
-
- if self.l_verbose:print(me, 'exit push_velocity_second_part')
-
- def set_gamma(self, js, pg=None):
- if self.l_verbose:print(me, 'enter set_gamma')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- # --- update gamma
- warp.gammaadv(np, pg.gaminv[il:iu], pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu], top.gamadv, top.lrelativ)
-
- if self.l_verbose:print(me, 'exit set_gamma')
-
- def push_positions(self, js, pg=None):
- if self.l_verbose:print(me, 'enter push_positions')
- if pg is None:
- pg = top.pgroup
- np = pg.nps[js]
- if np == 0: return
- il = pg.ins[js] - 1
- iu = il + pg.nps[js]
- warp.xpush3d(np, pg.xp[il:iu], pg.yp[il:iu], pg.zp[il:iu],
- pg.uxp[il:iu], pg.uyp[il:iu], pg.uzp[il:iu],
- pg.gaminv[il:iu], top.dt)
-
- if self.l_verbose:print(me, 'exit push_positions')
-
-
-
-
-
-
-
-
+ callbacks._afterstep()