Source code for underworld.swarm._integration_swarm

##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
##                                                                                   ##
##  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 underworld.libUnderworld as libUnderworld
from . import _swarmabstract
from . import _swarmvariable as svar
from abc import ABCMeta
import weakref
import underworld.function as function


[docs]class IntegrationSwarm(_swarmabstract.SwarmAbstract, metaclass = ABCMeta): """ Abstract class definition for IntegrationSwarms. All IntegrationSwarms have the following SwarmVariables from this class: 1. localCoordVariable : double (number of particle, dimensions) For local element coordinates of the particle 2. weightVariable : double (number of particles) For the integration weight of each particle """ _objectsDict = { "_swarm": "IntegrationPointsSwarm" } def _setup(self): if self._cself.localCoordVariable: self._particleCoordinates = svar.SwarmVariable(self, "double", self.mesh.generator.dim, _cself=self._cself.localCoordVariable, writeable=False) if self._cself.weightVariable: self._weightsVariable = svar.SwarmVariable(self, "double", 1, _cself=self._cself.weightVariable, writeable=False) @property def particleWeights(self): """ Returns ------- underworld.swarm.SwarmVariable Swarm variable recording the weight of the swarm particles. """ return self._weightsVariable
[docs]class VoronoiIntegrationSwarm(IntegrationSwarm,function.FunctionInput): """ Class for an IntegrationSwarm that maps to another Swarm Note that this swarm can act as a function input. In this capacity, the fundamental function input type is the FEMCoordinate (ie, the particle local coordinate, the owning mesh, and the owning element). This input can be reduced to the global coordinate when returned within python. The FEMCoordinate particle representation is useful when deforming a mesh, as it is possible to deform the mesh, and then use the FEMCoordinate to reset the particles within the moved mesh. Parameters ---------- swarm : underworld.swarm.Swarm The PIC integration swarm maps to this user provided swarm. Example ------- This simple example checks that the true global coordiante, and that derived from the local coordinate, are close to equal. Note that the VoronoiIntegrationSwarm uses a voronoi centroid algorithm so we do not expect particle to exactly coincide. >>> import underworld as uw >>> import numpy as np >>> mesh = uw.mesh.FeMesh_Cartesian() >>> swarm = uw.swarm.Swarm(mesh) >>> swarm.populate_using_layout(uw.swarm.layouts.PerCellGaussLayout(swarm,4)) >>> vswarm = uw.swarm.VoronoiIntegrationSwarm(swarm) >>> vswarm.repopulate() >>> np.allclose(swarm.particleCoordinates.data, uw.function.input().evaluate(vswarm),atol=1e-1) True """ _objectsDict = { "_cellLayout" : "ElementCellLayout", "_mapper" : "CoincidentMapper" } def __init__(self, swarm, **kwargs): if not isinstance(swarm, uw.swarm.Swarm): raise ValueError("Provided swarm must be of class 'Swarm'.") self._mappedSwarm = weakref.ref(swarm) # keep weakref to avoid circular dependency self._weights = uw.swarm._weights.DVC() # init this to ensure we do mapping on first pass self._mappedToState = -1 # build parent super(VoronoiIntegrationSwarm,self).__init__(swarm.mesh, **kwargs) def _add_to_stg_dict(self,componentDictionary): # call parents method super(VoronoiIntegrationSwarm,self)._add_to_stg_dict(componentDictionary) componentDictionary[ self._cellLayout.name ][ "Mesh"] = self._mesh._cself.name componentDictionary[ self._mapper.name ][ "GeneralSwarm"] = self._mappedSwarm()._cself.name # note _mappedSwarm is a weakref componentDictionary[ self._mapper.name ]["IntegrationPointsSwarm"] = self._swarm.name componentDictionary[ self._swarm.name ][ "CellLayout"] = self._cellLayout.name componentDictionary[ self._swarm.name ][ "IntegrationPointMapper"] = self._mapper.name
[docs] def repopulate(self, weights_calculator=None): """ This method repopulates the voronoi swarm using the provided global swarm. The weights are also recalculated. Parameters ---------- weights_calculator: underworld.swarm.Weights The weights calculator for the Voronoi swarm. If none is provided, a default DVCWeights calculator is used. """ if weights_calculator is None: weights_calculator = self._weights if not isinstance( weights_calculator, uw.swarm._weights.DVC ): raise TypeError("Provided 'weights_calculator' does not appear to be of correct class.") # only update if necessary if self._mappedToState != self._mappedSwarm().stateId: libUnderworld.PICellerator._CoincidentMapper_Map( self._mapper ) # if weights calculator is PCDVC, then we need to always run as it potentially performs population control if (self._mappedToState != self._mappedSwarm().stateId) or isinstance(weights_calculator,uw.swarm._weights.PCDVC): libUnderworld.PICellerator.WeightsCalculator_CalculateAll( weights_calculator._cself, self._cself ) libUnderworld.PICellerator.IntegrationPointsSwarm_ClearSwarmMaps( self._cself ) self._mappedToState = self._mappedSwarm().stateId
def _get_iterator(self): """ This is the concrete method required by the FunctionInput class. It affects using the voronoi swarm as an input to functions. """ return libUnderworld.Function.IntegrationSwarmInput(self._cself)
[docs]class GaussIntegrationSwarm(IntegrationSwarm): """ Integration swarm which creates particles within an element at the Gauss points. Parameters ---------- mesh : underworld.mesh.FeMesh The FeMesh the swarm is supported by. See Swarm.mesh property docstring for further information. particleCount : unsigned. Default is 3, unless Q2 mesh which takes default 5. Number of gauss particles in each direction. Must take value in [1,5]. """ _objectsDict = { "_cellLayout" : "SingleCellLayout", "_particleLayout" : "GaussParticleLayout" } def __init__(self, mesh, particleCount=None, **kwargs): if particleCount == None: # this is fragile..... partCountMap = { "DQ0" : 3, "Q1" : 3, "DQ1" : 3, "DPC1" : 3, "Q2" : 5 } particleCount = partCountMap[ mesh.elementType.upper() ] if not isinstance(particleCount, int): raise ValueError("'particleCount' parameter must be of type 'int'.") if particleCount not in [1,2,3,4,5]: raise ValueError("'particleCount' must take a value in [1,5].") self._particleCount = particleCount # build parent super(GaussIntegrationSwarm,self).__init__(mesh, **kwargs) def _add_to_stg_dict(self,componentDictionary): # call parents method super(GaussIntegrationSwarm,self)._add_to_stg_dict(componentDictionary) componentDictionary[ self._swarm.name ][ "CellLayout"] = self._cellLayout.name componentDictionary[ self._swarm.name ][ "ParticleLayout"] = self._particleLayout.name componentDictionary[ self._particleLayout.name ][ "dim"] = self._mesh.generator.dim componentDictionary[ self._particleLayout.name ][ "gaussParticles"] = self._particleCount
[docs]class GaussBorderIntegrationSwarm(GaussIntegrationSwarm): """ Integration swarm which creates particles within the boundary faces of an element, at the Gauss points. See parent class for parameters. """ _objectsDict = { "_particleLayout" : "GaussBorderParticleLayout" } def __init__(self, mesh, particleCount=None, **kwargs): # build parent super(GaussBorderIntegrationSwarm,self).__init__(mesh, particleCount, **kwargs)