Source code for gmshModel.Model.GenericRVE

################################################################################
#          CLASS FOR RVE MESHES GENERATED USING THE GMSH-PYTHON-API            #
################################################################################
# This file provides a class definition for an RVE generation using Python and
# Gmsh. The class inherits from the GenericModel class and extends it in order
# order to handle the problems that are connected with the generation of models
# with periodicity constraints.
#
# Currently, the class is restricted to RVEs with rectangular (2D)/ box-shaped
# (3D) domains (explicitly assumed within the setupPeriodicity() method).

###########################
# Load required libraries #
###########################
# Standard Python libraries
import numpy as np                                                              # numpy for array computations
import copy as cp                                                               # copy for deepcopies of arrays

# self-defined class definitions and modules
from .GenericModel import GenericModel                                          # generic model class definition (parent class)


###########################
# Define GenericRVE class #
###########################
[docs] class GenericRVE(GenericModel): """Generic class for RVEs created using the Gmsh-Python-API Based on the GenericModel class, this class provides extra attributes and methods that all box-shaped RVEs should have: the definition of size, origin and periodicityFlags as additional attributes facilitates an update of the parents class placeholder method setupPeriodicity(). Attributes: ----------- dimension: int dimension of the model instance size: list/array size of the box-shaped RVE model -> size=[Lx, Ly, (Lz)] origin: list/array origin of the box-shaped RVE model -> origin=[Ox, Oy, (Oz)] periodicityFlags: list/array flags indicating the periodic axes of the box-shaped RVE model -> periodicityFlags=[0/1, 0/1, 0/1] """ ######################### # Initialization method # ######################### def __init__(self,size=None,origin=[0,0,0],periodicityFlags=[1,1,1],gmshConfigChanges={}): """Initialization method for box-shaped RVE models Parameters: ----------- size: list/array size of the box-shaped RVE model -> size=[Lx, Ly, (Lz)] origin: list/array origin of the box-shaped RVE model -> origin=[Ox, Oy, (Oz)] periodicityFlags: list/array flags indicating the periodic axes of the box-shaped RVE model -> periodicityFlags=[0/1, 0/1, 0/1] gmshConfigChanges: dict dictionary for user updates of the default Gmsh configuration """ # plausibility checks for input variables: for varName, varValue in {"size": size, "origin": origin, "periodicityFlags": periodicityFlags}.items(): if varValue is None: # check if variable has a value raise TypeError("Variable \"{}\" not set! Check your input data.".format(varName)) elif len(np.shape(varValue)) > 1: # check for right amount of array dimensions raise ValueError("Wrong amount of array dimensions for variable \"{}\"! For a cuboid RVE, the variable \"{}\" can only be one-dimensional. Check your input data.".format(varName)) elif not len(varValue) in [2,3]: # check for right amount of values raise ValueError("Wrong number of values for variable \"{}\"! For a cuboid RVE, the variable \"{}\" has to have 2 or 3 values. Check your input data.".format(varName)) elif varName is "size" and np.count_nonzero(varValue) not in [2,3]: # check for right amount of non-zero values (size only) raise ValueError("Wrong number of non-zero values for variable \"{}\"! Only 2D/3D RVEs supported: the variable \"{}\" has to have 2 or 3 non-zero values. Check your input data.".format(varName)) # type conversion for input arguments size=np.asarray(size) # type conversion for size to numpy array origin=np.asarray(origin) # type conversion for origin to numpy array periodicityFlags=np.asarray(periodicityFlags) # type conversion for periodicityFlags to numpy array # get dimension of RVE and correct potentially two-dimensional arrays dimension=np.count_nonzero(size) # dimension equals number non-zero sizes if len(size) != 3: # check if size is not a three-dimensional array size=np.r_[size,0] # -> append 0 if len(origin) != 3: # check if origin is not a three-dimensional array newOrigin=np.zeros(3) # -> create new three-dimensional array newOrigin[size != 0]=origin # -> assign values of origin to non-zero dimensions of new array origin=newOrigin # -> overwrite origin with new array # initialize parent (GenericModel) class attributes and methods super().__init__(dimension=dimension,gmshConfigChanges=gmshConfigChanges) # initialize attributes that all instances of GenericRVE should have self.origin=origin # initialize unset RVE origin self.size=size # initialize unset RVE size self.periodicityFlags=periodicityFlags # initialize unset periodic axes flags for the RVE ################################################################################ # SPECIFIED/OVERWRITTEN PLACEHOLDER METHODS # ################################################################################ ######################################################### # Method to set up periodicity constraints for the mesh # #########################################################
[docs] def setupPeriodicity(self): """Overwritten method of the parent GenericModel class to define periodicity constraints for the model""" for iAx in range(0,self.dimension): # loop over all axes if self.periodicityFlags[iAx] == 1: # -> check if current axis is supposed to be periodic assocEnts,affineMatrix=self._getAssociatedEntities(iAx) # ->-> get associated entities for the current axis self.gmshAPI.mesh.setPeriodic(self.dimension-1,assocEnts[1],assocEnts[0],affineMatrix) # ->-> set periodicity constraints
################################################################################ # ADDITIONAL PRIVATE/HIDDEN METHODS FOR INTERNAL USE ONLY # ################################################################################ ################################################################### # Method to determine assosciated geometric entities of the model # ################################################################### def _getAssociatedEntities(self,axis): """Internal method to search for associated Gmsh entities on opposing sides of the RVE boundary""" # calculate required information from the RVE data bboxRVE=np.r_[[self.origin], [self.origin+self.size]] # bounding box of the RVE: bboxRVE=[[minBBoxPoint],[maxBBoxPoint]] tol=100*self.getGmshOption("Geometry.Tolerance") # tolerance for entity detection (factor 100 to find entities in wider bounding boxes) # calculate translation vector and affine transformation matrix transVec=np.zeros((3,1)) # initialize translation vector for the current pair of boundary entities transVec[axis]=bboxRVE[1,axis]-bboxRVE[0,axis] # update translation vector for the current pair of boundary entities affineMatrix=np.eye(4) # initialize affine transformation matrix for current pair of boundary entities affineMatrix[0:3,[-1]]=transVec # update affine transformation matrix with translation information affineMatrix=affineMatrix.reshape(16).tolist() # convert affine transformation matrix to list for output # find associated entities for the current axes associatedEntities=[] # initialize list of associated entities for iSide in range(0,2): # loop over both sides (positive/negative) bboxBnd=cp.deepcopy(bboxRVE) # -> initialze bounding box for the current boundary as copy of bboxRVE bboxBnd[1-iSide,axis]=bboxRVE[iSide,axis] # -> modify the coordinate of the relevant dimension to match the boundary under consideration bboxBnd=bboxBnd+np.array([[-tol],[tol]]) # -> add tolerances to the bounding box # find entities on boundary under consideration entityTags=self.getIDsFromTags(self.gmshAPI.getEntitiesInBoundingBox(*bboxBnd[0], *bboxBnd[1], self.dimension-1)) associatedEntities.append(entityTags) # sort associated entities of current pair to prevent errors associatedEntities=self._sortAssociatedEntities(associatedEntities,transVec) # return associated entities and affine transformation matrix return associatedEntities, affineMatrix ############################################################### # Security function to sort entities in associated boundaries # ############################################################### def _sortAssociatedEntities(self,associatedEntities,translationVector): """Internal method to ensure a 1-1-mapping of associated Gmsh entities Parameters: ----------- associatedEntities: list list of associated entities for the current pair of boundaries translationVector: array array defining the translation vector from one boundary to the other """ # get required information from input arguments dim=self.dimension-1 # entity dimension nEntities=len(associatedEntities[0]) # number of associated entities # convert associatedEntites to numpy array for calculations associatedEntities=np.asarray(associatedEntities) # get bounding boxes of all boundary entities on the negative side bboxesNeg=np.zeros((nEntities,6)) for iEnt in range(0,nEntities): bboxesNeg[iEnt,:]=np.array(self.gmshAPI.getBoundingBox(dim,associatedEntities[0][iEnt])) # calculate associated boxes of corresponding entities on the positive side bboxesPos=bboxesNeg+np.c_[translationVector.T, translationVector.T] # search matching entities on positive side sortedIndices=np.zeros((nEntities),dtype=np.int8) for jEnt in range(0,nEntities): bboxEnt=np.array(self.gmshAPI.getBoundingBox(dim,associatedEntities[1][jEnt])) # find bounding box of current entity bboxesDiff=bboxesPos-bboxEnt # calculate difference to all precomputed bounding boxes on positive side sortedIndices[jEnt]=np.argmin(np.linalg.norm(bboxesDiff,axis=1)) # determine index of entity with minimum distance of actual and precomputed bounding boxes # update sorting of entities on positive side associatedEntities[0]=associatedEntities[0][sortedIndices] # return updated associated entites return associatedEntities