Source code for underworld.swarm._swarmabstract

##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
##                                                                                   ##
##  This file forms part of the Underworld geophysics modelling application.         ##
##                                                                                   ##
##  For full license and copyright information, please refer to the LICENSE.md file  ##
##  located at the project root, or contact the authors.                             ##
##                                                                                   ##
##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
import underworld as uw
import underworld._stgermain as _stgermain
import weakref
import underworld.libUnderworld as libUnderworld
from . import _swarmvariable as svar
import abc

[docs]class SwarmAbstract(_stgermain.StgCompoundComponent, metaclass = abc.ABCMeta): """ The SwarmAbstract class supports particle like data structures. Each instance of this class will store a set of unique particles. In this context, particles are data structures which store a location variable, along with any other variables the user requests. Parameters ---------- mesh : underworld.mesh.FeMesh The FeMesh the swarm is supported by. See Swarm.mesh property docstring for further information. """ _objectsDict = { "_swarm" : None, "_cellLayout" : None } _selfObjectName = "_swarm" _supportedDataTypes = ["char","short","int","float", "double"] def __init__(self, mesh, **kwargs): if not isinstance(mesh, uw.mesh.FeMesh): raise TypeError("'mesh' object passed in must be of type 'FeMesh'") self._mesh = mesh # lets init these guy self._variables = [] self._livingArrays = weakref.WeakValueDictionary() # add a state identifier... this is incremented whenever the swarm is modified. self._stateId = 0 # add a lock to prevent changes to swarm state.. or rather to signify # that the state shouldn't have change (though it may well have) self._locked = False # build parent super(SwarmAbstract,self).__init__(**kwargs) @property def mesh(self): """ Returns ------- underworld.mesh.FeMesh Supporting FeMesh for this Swarm. All swarms are required to be supported by mesh (or similar) objects, which provide the data structures necessary for efficient particle locating/tracking, as well as the necessary mechanism for the swarm parallel decomposition. """ return self._mesh def _setup(self): # add coord swarm variable now (if available) if self._cself.owningCellVariable: self._owningCell = svar.SwarmVariable(self, "int", 1, _cself=self._cself.owningCellVariable, writeable=False) if self._cself.globalIdVariable: self._globalId = svar.SwarmVariable(self, "int", 1, _cself=self._cself.globalIdVariable, writeable=False) @property def variables(self): """ Returns ------- list List of swarm variables associated with this swarm. """ return self._variables @property def particleLocalCount(self): """ Returns ------- int Number of swarm particles within the current processes local space. """ return self._cself.particleLocalCount @property def owningCell(self): """ Returns ------- underworld.swarm.SwarmVariable Swarm variable recording the owning cell of the swarm particles. This will usually correspond to the owning element local id. """ return self._owningCell @property def globalId(self): """ Returns ------- underworld.swarm.SwarmVariable Swarm variable recording a particle global identifier. Not yet implemented. """ raise RuntimeError("Sorry, this function is not yet implemented.") return self._globalId def _add_to_stg_dict(self,componentDictionary): # call parents method super(SwarmAbstract,self)._add_to_stg_dict(componentDictionary) componentDictionary[ self._swarm.name ][ "dim"] = self._mesh.generator.dim componentDictionary[ self._swarm.name ][ "CellLayout"] = self._cellLayout.name componentDictionary[ self._swarm.name ][ "createGlobalId"] = False componentDictionary[ self._swarm.name ][ "FeMesh"] = self._mesh._cself.name componentDictionary[ self._cellLayout.name ]["Mesh"] = self._mesh._cself.name
[docs] def add_variable(self, dataType, count): """ Add a variable to each particle in this swarm. Variables can be added at any point. Removal of variables is however not currently supported. See help(SwarmVariable) for further information. Parameters ---------- dataType: str The data type for the variable. Available types are "char", "short", "int", "float" or "double". count: unsigned The number of values to be stored for each particle. Returns ------- underworld.swarm.SwarmVariable The newly created swarm variable. Example ------- >>> # first we need a mesh >>> mesh = uw.mesh.FeMesh_Cartesian( elementType='Q1/dQ0', elementRes=(16,16), minCoord=(0.,0.), maxCoord=(1.,1.) ) >>> # create swarm >>> swarm = uw.swarm.Swarm(mesh) >>> # add a variable >>> svar = swarm.add_variable("char",1) >>> # add another >>> svar = swarm.add_variable("double",3) >>> # add some particles >>> swarm.populate_using_layout(uw.swarm.layouts.PerCellGaussLayout(swarm,2)) >>> # add another variable >>> svar = swarm.add_variable("double",5) """ return svar.SwarmVariable( self, dataType, count )
[docs] def populate_using_layout( self, layout ): """ This method uses the provided layout to populate the swarm with particles. Usually layouts add particles across the entire domain. Available layouts may be found in the swarm.layouts module. Note that Layouts can only currently be used on empty swarms. Also note that all numpy arrays associated with swarm variables must be deleted before a layout can be applied. Parameters ---------- layout: underworld.swarm.layouts._ParticleLayoutAbstract The layout which determines where particles are created and added. Example ------- >>> # first we need a mesh >>> mesh = uw.mesh.FeMesh_Cartesian( elementType='Q1/dQ0', elementRes=(16,16), minCoord=(0.,0.), maxCoord=(1.,1.) ) >>> # create swarm >>> swarm = uw.swarm.Swarm(mesh) >>> # add populate >>> swarm.populate_using_layout(uw.swarm.layouts.PerCellGaussLayout(swarm,2)) """ if self.particleLocalCount > 0: raise RuntimeError("Swarm appears to already have particles. \nLayouts can only be used with empty swarms.") if not isinstance( layout, uw.swarm.layouts._ParticleLayoutAbstract ): raise TypeError("Provided layout does not appear to be a subclass of ParticleLayout") if not (self == layout.swarm): raise ValueError("The swarm associated with the layout appears to be a different swarm from self.") if len(self._livingArrays) != 0: raise RuntimeError(""" There appears to be {} swarm variable numpy array objects still in existance. Adding particles to a swarm results in the modification of existing swarm variable memory layouts and locations, and therefore existing numpy array views of swarm variables will cease to be valid. Potential modification of these invalid numpy arrays is dangerous, and therefore they must be removed before particles can be added. The python 'del' command may be useful, though be aware that an object cannot be destroyed while another object retains a reference to it. Once you have added the required particles, you can easily regenerate the numpy views of other variables again using the 'data' property.""".format(len(self._livingArrays))) libUnderworld.StgDomain.ParticleLayout_SetInitialCounts( layout._cself, self._cself ) libUnderworld.StgDomain._Swarm_BuildParticles( self._cself, None ) libUnderworld.StgDomain.ParticleLayout_InitialiseParticles( layout._cself, self._cself ) libUnderworld.StgDomain._Swarm_InitialiseParticles( self._cself, None )
@property def particleCoordinates(self): """ Returns ------- underworld.swarm.SwarmVariable Swarm variable recording the coordinates of the swarm particles. """ return self._particleCoordinates @property def stateId(self): """ Returns ------- int Swarm state identifier. This is incremented whenever the swarm is modified. """ return self._stateId def _toggle_state(self): """ Increment swarm state id, and updates swarm variable arrays. """ self._stateId+=1 self._clear_variable_arrays() if self._locked: raise RuntimeError(""" Swarm is in a locked state and yet it appears an attempt has been made to modify it (via change in particle population or the addition of variables. This is not allowed and your current swarm state may now be invalid. """) def _clear_variable_arrays(self): """ As the underlying memory may change when swarm population changes, or when a new variable is added, the Numpy views to this memory must be removed as they are potentially no longer valid. """ for var in self._variables: var._clear_array() @property def data(self): """ Returns ------- numpy.ndarray Numpy proxy array to underlying particle coordinate data. Note that this is an alias to `swarm.particleCoordinates.data`. Check `SwarmVariable.data` for further info. """ return self.particleCoordinates.data