Package: gpgi
GPGIFast particle deposition at post-processing timeThis small Python library implements fundamental grid deposition algorithms to
analyse (rectilinear) grid + particle datasets, with an emphasize on
performance. Core algorithms are implemented as Cython extensions.GPGI stands forGenericParticle +Grid dataInterfaceTable of ContentsInstallationSupported applicationsBuiltin deposition methodsSupported geometriesTime complexityUsageSupplying arbitrary metadataBoundary conditionsBuiltin recipesDefine custom recipesWeight fields (Depositing intensive quantities)Deposition algorithmInstallationpython-mpipinstall--upgradepip
python-mpipinstallgpgiSupported applicationsA rectilinear grid is defined as 1D arrays representing cell left edges in each directions. Note that the last point of such an array is interpreted as the right edge of the rightmost cell, so for instance, a 1D grid containing 100 cells is defined by 101 edges.Particles are defined as points that live within the grid's bounds.Deposition is the action of going from particle description
to a grid description of a field.
It is useful to analyze, compare and combine simulation data that exists in a combination of the two formalisms.
This process is not reversible as it degrades information.For instance, here's a simple overlay of a particle set (red dots)
against a background that represents the deposited particle count.This example illustrates the simplest possible deposition method "Particle in Cell", in which each particle contributes only to the cell
that contains it.More refined methods are also available.Builtin deposition methodsmethod nameabreviated nameorderNearest Grid PointNGP0Cloud in CellCIC1Triangular Shaped CloudTSC2new in gpgi 0.12User-defined alternative methods may be provided toDataset.depositasmethod=my_func.
Their signature need to be compatible withgpgi.types.DepositionMethodT.Supported geometriesgeometry nameaxes ordercartesianx, y, zpolarradius, z, azimuthcylindricalradius, azimuth, zsphericalradius, colatitude, azimuthequatorialradius, azimuth, latitudeTime complexityAn important step in perfoming deposition is to associate particle indices to cell indices. This step is called "particle indexing".
In directions where the grid is uniformly stepped (if any), indexing a particle is an O(1) operation.
In the more general case, indexing is performed by bisection, which is a O(log(nx))) operation (where nx represents the number of cells in the direction of interest).UsageThe API consists in aloadfunction, which returns aDatasetobject.Loaddataimportnumpyasnpimportgpginx=ny=64nparticles=600_000prng=np.random.RandomState(0)ds=gpgi.load(geometry="cartesian",grid={"cell_edges":{"x":np.linspace(-1,1,nx),"y":np.linspace(-1,1,ny),},},particles={"coordinates":{"x":2*(prng.normal(0.5,0.25,nparticles)%1-0.5),"y":2*(prng.normal(0.5,0.25,nparticles)%1-0.5),},"fields":{"mass":np.ones(nparticles),},},)TheDatasetobject holds agridand aparticlesattribute,
which both hold afieldsattribute for accessing their data.
But more importantly, theDatasethas adepositmethod to
translate particle fields to the grid formalism.Deposit Particle fields on the gridparticle_mass=ds.deposit("mass",method="nearest_grid_point")# or "ngp" for shortsVisualizeIn this example we'll usematplotlibfor rendering, but note thatmatplotlibis not a dependency togpgiimportmatplotlib.pyplotaspltfig,ax=plt.subplots()ax.set(aspect=1,xlabel="x",ylabel="y")im=ax.pcolormesh("x","y",particle_mass.T,data=ds.grid.cell_edges,cmap="viridis",)fig.colorbar(im,ax=ax)The example script given here takes about a second (top to bottom).Supplying arbitrary metadatanew in gpgi 0.4.0Datasetobjects have a special attributemetadatawhich is a dictionary with string keys.
This attribute is meant to hold any special metadata that may be relevant for labelling or processing (e.g. simulation time, author, ...).
Metadata can be supplied at load time asds=gpgi.load(geometry="cartesian",grid=...,particles=...,metadata={"simulation_time":12.5,"author":"Clément Robert"})Boundary conditionsnew in gpgi 0.5.0With CIC and TSC deposition, particles contribute to cells neighbouring the one
that contains them. For particles that live in the outermost layer of the
domain, this means some of their contribution is lost. This behaviour
corresponds to the default'open'boundary condition, butgpgihas builtin
support for more conservative boundary conditions.Boundary conditions can selected per field, per axis and per side. Builtin
recipes all perform linear combinations of ghost layers (same-side and opposite
side) and active domain layers (same-side and opposite side), and replace the
same-side active layer with the result.User-selected boundary conditions take the form of an optional argument toDataset.deposit, as dictionnary with keys being axes names, and values being
2-tuples of boundary conditions names (for left and right side respectively).
For instance, here's how one would require periodic boundary conditions on all axes:ds.deposit("mass",method="cic",boundaries={"x":("periodic","periodic"),"y":("periodic","periodic"),})Unspecified axes will use the default'open'boundary.Builtin recipesboundary conditionsdescriptionconservative ?open (default)no special treatmentnoperiodicadd opposite ghost layer to the active domainyeswalladd same-side ghost layer to the active domainyesantisymmetricsubstract same-side ghost layer from the active domainnoDefine custom recipesgpgi's boundary recipes can be customized. Let's illustrate this feature with a simple example.
Say we want to fix the value of the deposited field in some outer layer.
This is done by defining a new function on the user side:defones(same_side_active_layer,same_side_ghost_layer,opposite_side_active_layer,opposite_side_ghost_layer,weight_same_side_active_layer,weight_same_side_ghost_layer,weight_opposite_side_active_layer,weight_opposite_side_ghost_layer,side,metadata,):return1.0where all first eight arguments arenumpy.ndarrayobjects with the same shape (which includes ghost padding !),
to which the return value must be broadcastable,sidecan only be either"left"or"right", andmetadatais the specialDataset.metadataattribute. Not all arguments need be used in the body of the function, but this
signature is required.The method must then be registered as a boundary condition recipe asds.boundary_recipes.register("ones",ones)where the associated key (here"ones") is arbitrary. The recipe can now be
used exactly as builtin ones, and all of them can be mixed arbitrarily.ds.deposit("mass",method="cic",boundaries={"x":("ones","wall"),"y":("periodic","periodic"),})Note that all first eight arguments in a boundary recipe function should
represent anextensivephysical quantity (as opposed tointensive). When
depositing anintensivequantityu, a weight fieldwshould be supplied
(see next section), in which case, the first four arguments representu*wand
the following four representw, so thatucan still be obtained within the
function as a ratio if needed.Weight fields (Depositing intensive quantities)new in gpgi 0.7.0Fundamentally, deposition algorithms construct on-grid fields by performingsummations. An implication is that the physical quantities being deposited are
required to beextensive(like mass or momentum).Intensivequantities (like
velocity or temperature) require additional operations, and necessitate the use
of an additionalweight field.This section provides showcases theirusage. For a detailled explanation of the deposition algorithm for intensive quantities, seeDeposition algorithm.In order to deposit anintensivefield (e.g.,vx), an additionalweight_fieldargument must be provided asds.deposit("vx",method="cic",boundaries={"y":("periodic","periodic"),"x":("antisymmetric","antisymmetric"),},weight_field="mass",weight_field_boundaries={"y":("periodic","periodic"),"x":("open","open"),},)Boundary recipes may be also associated to the weight field with theweight_field_boundariesargument. This arguments becomesrequiredifboundariesandweight_fieldare both provided.Callhelp(ds.deposit)for more detail.Deposition algorithmThis section provides details on the general deposition algorithm, as
implemented ingpgi.Without loss of generality, we will illustrate how  anintensivefield (v)
is deposited, since this case requires the most computational steps. As it
happens, depositing anextensivefield (w) separately is actually part of
the algorithm.Definitionsvis an intensive field that we want to deposit on the gridwis an extensive field that will be used as weightsu = v * wis an extensive equivalent tov(conceptually, ifvis a velocity andwis a mass,ucorresponds to a momentum)u(i),v(i)andw(i)are defined for each particlei.We noteU(x),V(x)andW(x)the corresponding on-grid fields, whereV(x)is the final output of the algorithm. These are defined at grid cell centersx, within theactive domain.Last, we noteU'(x),V'(x)andW'(x)therawdeposited fields, meaning
no special treatment is applied to the outermost layers (boundary conditions).
These are defined at grid cell centers,including one ghost layerthat will be
used to apply boundary conditions.AlgorithmW'andU'are computed asW'(x) = Σ c(i,x) w(i)
U'(x) = Σ c(i,x) w(i) v(i)wherec(i,x)are geometric coefficients associated with the deposition method. Taking the nearest grid point (NGP) method for illustration,c(i,x) = 1if particleiis contained in the cell whose center isx, andc(i,x) = 0elsewhere.boundary conditions are appliedW(x) = W_BCO(W', 1, metadata)
U(x) = U_BCO(U', W', metadata)whereW_BCOandU_BCOdenote arbitrary boundary condition operators
associated withWandUrespectively, and which take 3 arguments,
representing the field to be transformed, its associated weight field and a
wildcardmetadataargument which may contain any additional data relevant to
the operator.Note1is used a placeholder "weight" forW, for symmetry reasons: all boundary condition operators must expose a similar interface, as explained inDefine custom recipes.Finally,V(x)is obtained asV(x) = (U/W)(x)