def __init__(self, CurrentConstitutiveLaw, Section, Jx, Iyy, Izz, k=0, ID = ""): #k: shear shape factor if isinstance(CurrentConstitutiveLaw, str): CurrentConstitutiveLaw = ConstitutiveLaw.GetAll()[CurrentConstitutiveLaw] if ID == "": ID = CurrentConstitutiveLaw.GetID() WeakForm.__init__(self,ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == '3D': Variable("DispZ") Variable("RotX") #torsion rotation Variable("RotY") Variable("RotZ") Variable.SetVector('Disp' , ('DispX', 'DispY', 'DispZ') , 'global') Variable.SetVector('Rot' , ('RotX', 'RotY', 'RotZ') , 'global') elif ProblemDimension.Get() == '2Dplane': Variable("RotZ") Variable.SetVector('Disp' , ['DispX', 'DispY'], 'global' ) Variable.SetVector('Rot' , ['RotZ'] ) elif ProblemDimension.Get() == '2Dstress': assert 0, "No 2Dstress model for a beam kinematic. Choose '2Dplane' instead." self.__ConstitutiveLaw = CurrentConstitutiveLaw self.__parameters = {'Section': Section, 'Jx': Jx, 'Iyy':Iyy, 'Izz':Izz, 'k':k}
def __init__(self, E=None, nu = None, S=None, Jx=None, Iyy=None, Izz = None, R = None, k=0, ID = ""): """ Weak formulation dedicated to treat parametric problems using Bernoulli beams for isotropic materials Arugments ---------- ID: str ID of the weak formulation List of other optional parameters E: Young Modulus nu: Poisson Ratio S: Section area Jx: Torsion constant Iyy, Izz: Second moment of area, if Izz is not specified, Iyy = Izz is assumed k is a scalar. k=0 for no shear effect. For other values, k is the shear area coefficient When the differntial operator is generated (using PGD.Assembly) the parameters are searched among the CoordinateID defining the associated mesh. If a parameter is not found , a Numeric value should be specified in argument. In the particular case of cylindrical beam, the radius R can be specified instead of S, Jx, Iyy and Izz. S = pi * R**2 Jx = pi * R**4/2 Iyy = Izz = pi * R**4/4 """ WeakForm.__init__(self,ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == '3D': Variable("DispZ") Variable("RotX") #torsion rotation Variable('RotY') Variable('RotZ') # Variable.SetDerivative('DispZ', 'RotY', sign = -1) #only valid with Bernoulli model # Variable.SetDerivative('DispY', 'RotZ') #only valid with Bernoulli model Variable.SetVector('Disp' , ('DispX', 'DispY', 'DispZ') , 'global') Variable.SetVector('Rot' , ('RotX', 'RotY', 'RotZ') , 'global') elif ProblemDimension.Get() == '2Dplane': Variable('RotZ') Variable.SetVector('Disp' , ('DispX', 'DispY'), 'global' ) Variable.SetVector('Rot' , ('RotZ')) elif ProblemDimension.Get() == '2Dstress': assert 0, "No 2Dstress model for a beam kinematic. Choose '2Dplane' instead." if R is not None: S = np.pi * R**2 Jx = np.pi * R**4/2 Iyy = Izz = np.pi * R**4/4 self.__parameters = {'E':E, 'nu':nu, 'S':S, 'Jx':Jx, 'Iyy':Iyy, 'Izz':Izz, 'k':k}
def __init__(self, NodeCoordinates, ElementTable=None, ElementShape=None, LocalFrame=None, ID=""): MeshBase.__init__(self, ID) self.__NodeCoordinates = NodeCoordinates #node coordinates self.__ElementTable = ElementTable #element self.__ElementShape = ElementShape self.__SetOfNodes = {} #node on the boundary for instance self.__SetOfElements = {} self.__LocalFrame = LocalFrame #contient le repere locale (3 vecteurs unitaires) en chaque noeud. Vaut 0 si pas de rep locaux definis n = ProblemDimension.Get() N = self.__NodeCoordinates.shape[0] if self.__NodeCoordinates.shape[1] == 1: self.__CoordinateID = ('X') elif self.__NodeCoordinates.shape[1] == 2: self.__CoordinateID = ('X', 'Y') elif n == '2Dplane' or n == '2Dstress': self.__CoordinateID = ('X', 'Y') else: self.__CoordinateID = ('X', 'Y', 'Z') if n == '3D' and self.__NodeCoordinates.shape[1] == 2: self.__NodeCoordinates = np.c_[self.__NodeCoordinates, np.zeros(N)] if LocalFrame != None: LocalFrameTemp = np.zeros((N, 3, 3)) LocalFrameTemp[:, :2, :2] = self.__LocalFrame LocalFrameTemp[:, 2, 2] = 1 self.__LocalFrame = LocalFrameTemp
def GetGradOperator(): if ProblemDimension.Get() == "3D": GradOperator = [[OpDiff(IDvar, IDcoord,1) for IDcoord in ['X','Y','Z']] for IDvar in ['DispX','DispY','DispZ']] else: GradOperator = [[OpDiff(IDvar, IDcoord,1) for IDcoord in ['X','Y']] + [0] for IDvar in ['DispX','DispY']] GradOperator += [[0,0,0]] return GradOperator
def __init__(self, Density, ID = ""): if ID == "": ID = "Inertia" WeakForm.__init__(self,ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == "3D": Variable("DispZ") self.__Density = Density
def __init__(self, CurrentConstitutiveLaw, ID=""): if isinstance(CurrentConstitutiveLaw, str): CurrentConstitutiveLaw = ConstitutiveLaw.GetAll( )[CurrentConstitutiveLaw] if ID == "": ID = CurrentConstitutiveLaw.GetID() WeakForm.__init__(self, ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == "3D": Variable("DispZ") self.__ConstitutiveLaw = CurrentConstitutiveLaw self.__InitialStressVector = 0
def __init__(self, Density, ID=""): if ID == "": ID = "Inertia" WeakForm.__init__(self, ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == "3D": Variable("DispZ") Variable.SetVector('Disp', ('DispX', 'DispY', 'DispZ')) else: Variable.SetVector('Disp', ('DispX', 'DispY')) self.__Density = Density
def LineMesh(N=11, x_min=0, x_max=1, ElementShape='lin2', ID=""): """ Define the mesh of a line Parameters ---------- N : int Numbers of nodes (default = 11). x_min, x_max : int,float,list The boundary of the line as scalar (1D) or list (default : 0, 1). ElementShape : {'lin2', 'lin3', 'lin4'} The shape of the elements (default='lin2') * 'lin2' -- 2 node line * 'lin3' -- 3 node line * 'lin4' -- 4 node line Returns ------- Mesh The generated geometry in Mesh format. See the Mesh class for more details. See Also -------- RectangleMesh : Surface mesh of a rectangle BoxMesh : Volume mesh of a box GridMeshCylindric : Surface mesh of a grid in cylindrical coodrinate LineMeshCylindric : Line mesh in cylindrical coordinate """ if np.isscalar(x_min): m = LineMesh1D(N, x_min, x_max, ElementShape, ID) if ProblemDimension.Get() in ['2Dplane', '2Dstress']: dim = 2 else: dim = 3 crd = np.c_[m.GetNodeCoordinates(), np.zeros((N, dim - 1))] elm = m.GetElementTable() ReturnedMesh = Mesh(crd, elm, ElementShape, None, ID) ReturnedMesh.AddSetOfNodes(m.GetSetOfNodes("left"), "left") ReturnedMesh.AddSetOfNodes(m.GetSetOfNodes("right"), "right") else: m = LineMesh1D(N, 0., 1., ElementShape, ID) crd = m.GetNodeCoordinates() crd = (np.array(x_max) - np.array(x_min)) * crd + np.array(x_min) elm = m.GetElementTable() ReturnedMesh = Mesh(crd, elm, ElementShape, None, ID) ReturnedMesh.AddSetOfNodes(m.GetSetOfNodes("left"), "left") ReturnedMesh.AddSetOfNodes(m.GetSetOfNodes("right"), "right") return ReturnedMesh
def GetBernoulliBeamStrainOperator(): n = ProblemDimension.Get() epsX = OpDiff('DispX', 'X', 1) # dérivée en repère locale xsiZ = OpDiff('RotZ', 'X', 1) # flexion autour de Z if n == "2Dplane": eps = [epsX, 0, 0, 0, 0, xsiZ] elif n == "2Dstress": assert 0, "no 2Dstress for a beam kinematic, use '2Dplane' instead" elif n == "3D": xsiX = OpDiff('RotX', 'X', 1) # torsion autour de X xsiY = OpDiff('RotY', 'X', 1) # flexion autour de Y eps = [epsX, 0, 0, xsiX, xsiY, xsiZ] eps_vir = [e.virtual() if e != 0 else 0 for e in eps] return eps, eps_vir
def GetBeamStrainOperator(): n = ProblemDimension.Get() epsX = OpDiff('DispX', 'X', 1) # dérivée en repère locale xsiZ = OpDiff('RotZ', 'X', 1) # flexion autour de Z gammaY = OpDiff('DispY', 'X', 1) - OpDiff('RotZ') #shear/Y if n == "2Dplane": eps = [epsX, gammaY, 0, 0, 0, xsiZ] elif n == "2Dstress": assert 0, "no 2Dstress for a beam kinematic" elif n == "3D": xsiX = OpDiff('RotX', 'X', 1) # torsion autour de X xsiY = OpDiff('RotY', 'X', 1) # flexion autour de Y gammaZ = OpDiff('DispZ', 'X', 1) + OpDiff('RotY') #shear/Z eps = [epsX, gammaY, gammaZ, xsiX, xsiY, xsiZ] eps_vir = [e.virtual() if e != 0 else 0 for e in eps] return eps, eps_vir
def __init__(self, CurrentConstitutiveLaw, ID="", nlgeom=False): if isinstance(CurrentConstitutiveLaw, str): CurrentConstitutiveLaw = ConstitutiveLaw.GetAll( )[CurrentConstitutiveLaw] if ID == "": ID = CurrentConstitutiveLaw.GetID() WeakForm.__init__(self, ID) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == "3D": Variable("DispZ") Variable.SetVector('Disp', ('DispX', 'DispY', 'DispZ')) else: #2D assumed Variable.SetVector('Disp', ('DispX', 'DispY')) self.__ConstitutiveLaw = CurrentConstitutiveLaw self.__InitialStressTensor = 0 self.__InitialGradDispTensor = None self.__nlgeom = nlgeom #geometric non linearities if nlgeom: if ProblemDimension.Get() == "3D": GradOperator = [[ OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y', 'Z'] ] for IDvar in ['DispX', 'DispY', 'DispZ']] #NonLinearStrainOperatorVirtual = 0.5*(vir(duk/dxi) * duk/dxj + duk/dxi * vir(duk/dxj)) using voigt notation and with a 2 factor on non diagonal terms NonLinearStrainOperatorVirtual = [ sum([ GradOperator[k][i].virtual() * GradOperator[k][i] for k in range(3) ]) for i in range(3) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][1] + GradOperator[k][1].virtual() * GradOperator[k][0] for k in range(3) ]) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][2] + GradOperator[k][2].virtual() * GradOperator[k][0] for k in range(3) ]) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][1].virtual() * GradOperator[k][2] + GradOperator[k][2].virtual() * GradOperator[k][1] for k in range(3) ]) ] else: GradOperator = [[ OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y'] ] for IDvar in ['DispX', 'DispY']] NonLinearStrainOperatorVirtual = [ sum([ GradOperator[k][i].virtual() * GradOperator[k][i] for k in range(2) ]) for i in range(2) ] + [0] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][1] + GradOperator[k][1].virtual() * GradOperator[k][0] for k in range(2) ]) ] + [0, 0] self.__NonLinearStrainOperatorVirtual = NonLinearStrainOperatorVirtual else: self.__NonLinearStrainOperatorVirtual = 0
def Get(InitialGradDisp=None): n = ProblemDimension.Get() # InitialGradDisp = StrainOperator.__InitialGradDisp if (InitialGradDisp is None) or (InitialGradDisp is 0): du_dx = OpDiff('DispX', 'X', 1) dv_dy = OpDiff('DispY', 'Y', 1) du_dy = OpDiff('DispX', 'Y', 1) dv_dx = OpDiff('DispY', 'X', 1) if n == "2Dplane" or n == "2Dstress": eps = [du_dx, dv_dy, 0, du_dy + dv_dx, 0, 0] # elif n == "2Dstress": # dw_dx = OpDiff('DispZ', 'X', 1) # dw_dy = OpDiff('DispZ', 'Y', 1) # eps = [du_dx, dv_dy, 0, dw_dy, dw_dx, du_dy+dv_dx] elif n == "3D": dw_dz = OpDiff('DispZ', 'Z', 1) du_dz = OpDiff('DispX', 'Z', 1) dv_dz = OpDiff('DispY', 'Z', 1) dw_dx = OpDiff('DispZ', 'X', 1) dw_dy = OpDiff('DispZ', 'Y', 1) eps = [ du_dx, dv_dy, dw_dz, du_dy + dv_dx, du_dz + dw_dx, dv_dz + dw_dy ] else: if n == "2Dplane" or n == "2Dstress": GradOperator = [[ OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y'] ] for IDvar in ['DispX', 'DispY']] eps = [ GradOperator[i][i] + sum([ GradOperator[k][i] * InitialGradDisp[k][i] for k in range(2) ]) for i in range(2) ] eps += [0] eps += [ GradOperator[0][1] + GradOperator[1][0] + sum([ GradOperator[k][0] * InitialGradDisp[k][1] + GradOperator[k][1] * InitialGradDisp[k][0] for k in range(2) ]) ] eps += [0, 0] elif n == "3D": GradOperator = [[ OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y', 'Z'] ] for IDvar in ['DispX', 'DispY', 'DispZ']] eps = [ GradOperator[i][i] + sum([ GradOperator[k][i] * InitialGradDisp[k][i] for k in range(3) ]) for i in range(3) ] eps += [ GradOperator[0][1] + GradOperator[1][0] + sum([ GradOperator[k][0] * InitialGradDisp[k][1] + GradOperator[k][1] * InitialGradDisp[k][0] for k in range(3) ]) ] eps += [ GradOperator[0][2] + GradOperator[2][0] + sum([ GradOperator[k][0] * InitialGradDisp[k][2] + GradOperator[k][2] * InitialGradDisp[k][0] for k in range(3) ]) ] eps += [ GradOperator[1][2] + GradOperator[2][1] + sum([ GradOperator[k][1] * InitialGradDisp[k][2] + GradOperator[k][2] * InitialGradDisp[k][1] for k in range(3) ]) ] eps_vir = [e.virtual() if e != 0 else 0 for e in eps] return eps, eps_vir
] eps += [ GradOperator[0][2] + GradOperator[2][0] + sum([ GradOperator[k][0] * InitialGradDisp[k][2] + GradOperator[k][2] * InitialGradDisp[k][0] for k in range(3) ]) ] eps += [ GradOperator[1][2] + GradOperator[2][1] + sum([ GradOperator[k][1] * InitialGradDisp[k][2] + GradOperator[k][2] * InitialGradDisp[k][1] for k in range(3) ]) ] eps_vir = [e.virtual() if e != 0 else 0 for e in eps] return eps, eps_vir def GetStrainOperator(InitialGradDisp=None): #return linear the operator to get the strain tensor (to use in weakform) #InitialGradDisp is used for initial displacement effect in incremental approach return StrainOperator.Get(InitialGradDisp) if __name__ == "__main__": ProblemDimension("3D") A, B = GetStrainOperator()
def __init__(self, InitialStressTensor=0, ID=""): if ID == "": ID = "InitialStress" WeakForm.__init__(self, ID) if InitialStressTensor == 0: InitialStressTensor = [ 0, 0, 0, 0, 0, 0 ] #list of the six stress component (sig_xx, sig_yy, sig_zz, sig_yz, sig_xz, sigxy) Variable("DispX") Variable("DispY") if ProblemDimension.Get() == "3D": Variable("DispZ") if ProblemDimension.Get() == "3D": GradOperator = [[ OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y', 'Z'] ] for IDvar in ['DispX', 'DispY', 'DispZ']] #NonLinearStrainOperatorVirtual = 0.5*(vir(duk/dxi) * duk/dxj + duk/dxi * vir(duk/dxj)) using voigt notation and with a 2 factor on non diagonal terms NonLinearStrainOperatorVirtual = [ sum([ GradOperator[k][i].virtual() * GradOperator[k][i] for k in range(3) ]) for i in range(3) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][1].virtual() * GradOperator[k][2] + GradOperator[k][2].virtual() * GradOperator[k][1] for k in range(3) ]) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][2] + GradOperator[k][2].virtual() * GradOperator[k][0] for k in range(3) ]) ] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][1] + GradOperator[k][1].virtual() * GradOperator[k][0] for k in range(3) ]) ] else: GradOperator = [[ Util.OpDiff(IDvar, IDcoord, 1) for IDcoord in ['X', 'Y'] ] for IDvar in ['DispX', 'DispY']] NonLinearStrainOperatorVirtual = [ sum([ GradOperator[k][i].virtual() * GradOperator[k][i] for k in range(2) ]) for i in range(2) ] + [0, 0, 0] NonLinearStrainOperatorVirtual += [ sum([ GradOperator[k][0].virtual() * GradOperator[k][1] + GradOperator[k][1].virtual() * GradOperator[k][0] for k in range(2) ]) ] self.__NonLinearStrainOperatorVirtual = NonLinearStrainOperatorVirtual self.__InitialStressTensor = InitialStressTensor self.__typeOperator = 'all'
def __GetChangeOfBasisMatrix( mesh): # change of basis matrix for beam or plate elements if not (mesh.GetID()) in Assembly.__saveMatrixChangeOfBasis: ### change of basis treatment for beam or plate elements MatrixChangeOfBasis = 1 computeMatrixChangeOfBasis = False Nnd = mesh.GetNumberOfNodes() Nel = mesh.GetNumberOfElements() elm = mesh.GetElementTable() nNd_elm = np.shape(elm)[1] crd = mesh.GetNodeCoordinates() dim = ProblemDimension.GetDoF() localFrame = mesh.GetLocalFrame() elmRefGeom = eval(mesh.GetElementShape())() # xi_nd = elmRefGeom.xi_nd xi_nd = GetNodePositionInElementCoordinates( mesh.GetElementShape(), nNd_elm) #function to define if 'X' in mesh.GetCoordinateID() and 'Y' in mesh.GetCoordinateID( ): #if not in physical space, no change of variable for nameVector in Variable.ListVector(): if Variable.GetVectorCoordinateSystem( nameVector) == 'global': if computeMatrixChangeOfBasis == False: range_nNd_elm = np.arange(nNd_elm) computeMatrixChangeOfBasis = True Nvar = Variable.GetNumberOfVariable() listGlobalVector = [] listLocalVariable = list(range(Nvar)) # MatrixChangeOfBasis = sparse.lil_matrix((Nvar*Nel*nNd_elm, Nvar*Nnd)) #lil is very slow because it change the sparcity of the structure listGlobalVector.append(Variable.GetVector(nameVector)) listLocalVariable = [ i for i in listLocalVariable if not (i in listGlobalVector[-1]) ] #Data to build MatrixChangeOfBasis with coo sparse format if computeMatrixChangeOfBasis: rowMCB = np.empty( (len(listGlobalVector) * Nel, nNd_elm, dim, dim)) colMCB = np.empty( (len(listGlobalVector) * Nel, nNd_elm, dim, dim)) dataMCB = np.empty( (len(listGlobalVector) * Nel, nNd_elm, dim, dim)) LocalFrameEl = elmRefGeom.GetLocalFrame( crd[elm], xi_nd, localFrame ) #array of shape (Nel, nb_nd, nb of vectors in basis = dim, dim) for ivec, vec in enumerate(listGlobalVector): dataMCB[ivec * Nel:(ivec + 1) * Nel] = LocalFrameEl rowMCB[ivec * Nel:(ivec + 1) * Nel] = np.arange( Nel).reshape(-1, 1, 1, 1) + range_nNd_elm.reshape( 1, -1, 1, 1) * Nel + np.array(vec).reshape( 1, 1, -1, 1) * (Nel * nNd_elm) colMCB[ivec * Nel:(ivec + 1) * Nel] = elm.reshape( Nel, nNd_elm, 1, 1) + np.array(vec).reshape(1, 1, 1, -1) * Nnd if computeMatrixChangeOfBasis: MatrixChangeOfBasis = sparse.coo_matrix( (sp.reshape(dataMCB, -1), (sp.reshape(rowMCB, -1), sp.reshape(colMCB, -1))), shape=(Nel * nNd_elm * Nvar, Nnd * Nvar)) for var in listLocalVariable: MatrixChangeOfBasis = MatrixChangeOfBasis.tolil() MatrixChangeOfBasis[range(var * Nel * nNd_elm, (var + 1) * Nel * nNd_elm), range(var * Nel * nNd_elm, (var + 1) * Nel * nNd_elm)] = 1 MatrixChangeOfBasis = MatrixChangeOfBasis.tocsr() Assembly.__saveMatrixChangeOfBasis[ mesh.GetID()] = MatrixChangeOfBasis return MatrixChangeOfBasis return Assembly.__saveMatrixChangeOfBasis[mesh.GetID()]
def PreComputeElementaryOperators( mesh, elementType, nb_pg=None, **kargs ): #Précalcul des opérateurs dérivés suivant toutes les directions (optimise les calculs en minimisant le nombre de boucle) #initialisation if nb_pg is None: NumberOfGaussPoint = GetDefaultNbPG(elementType, mesh) else: NumberOfGaussPoint = nb_pg objElement = eval(elementType) if isinstance(objElement, dict): for val in set(objElement.values()): Assembly.PreComputeElementaryOperators(mesh, val, nb_pg, **kargs) return elmRef = objElement(NumberOfGaussPoint) Nnd = mesh.GetNumberOfNodes() Nel = mesh.GetNumberOfElements() elm = mesh.GetElementTable() nNd_elm = np.shape(elm)[1] crd = mesh.GetNodeCoordinates() dim = ProblemDimension.GetDoF() if NumberOfGaussPoint == 0: # in this case, it is a finite difference mesh # we compute the operators directly from the element library OP = elmRef.computeOperator(crd, elm) Assembly.__saveMatGaussianQuadrature[( mesh.GetID(), NumberOfGaussPoint)] = sparse.identity( OP[0][0].shape[0], 'd', format='csr' ) #No gaussian quadrature in this case : nodal identity matrix Assembly.__savePGtoNodeMatrix[( mesh.GetID(), NumberOfGaussPoint )] = 1 #no need to translate between pg and nodes because no pg Assembly.__saveNodeToPGMatrix[(mesh.GetID(), NumberOfGaussPoint)] = 1 Assembly.__saveMatrixChangeOfBasis[mesh.GetID( )] = 1 # No change of basis: MatrixChangeOfBasis = 1 Assembly.__saveOperator[( mesh.GetID(), elementType, NumberOfGaussPoint)] = OP #elmRef.computeOperator(crd,elm) return elmRefGeom = eval(mesh.GetElementShape())(NumberOfGaussPoint) nNd_elm_geom = len(elmRefGeom.xi_nd) elm_geom = elm[:, :nNd_elm_geom] localFrame = mesh.GetLocalFrame() nb_elm_nd = np.bincount(elm_geom.reshape(-1)) #len(nb_elm_nd) = Nnd vec_xi = elmRef.xi_pg PGtoNode = np.linalg.pinv( elmRefGeom.ShapeFunctionPG) #pseudo-inverse of NodeToPG # PGtoNode = np.linalg.inv(np.dot(elmRef.GeometricalShapeFunctionPG.T , elmRef.GeometricalShapeFunctionPG)) # PGtoNode = np.dot(PGtoNode , elmRef.GeometricalShapeFunctionPG.T) #inverse of the NodeToPG matrix (built from the values of the shapeFuctions at PG) based on the least square method elmRefGeom.ComputeJacobianMatrix( crd[elm_geom], vec_xi, localFrame ) #elmRef.JacobianMatrix, elmRef.detJ, elmRef.inverseJacobian derivativePG = np.matmul(elmRefGeom.inverseJacobian, elmRef.ShapeFunctionDerivativePG) nb_dir_deriv = derivativePG.shape[-2] nop = nb_dir_deriv + 1 #nombre d'opérateur à discrétiser if hasattr(elmRef, 'ShapeFunctionSecondDerivativePG'): #TODO : only work for beam. Consider revising in the future # secondDerivativePG = np.matmul(elmRefGeom.inverseJacobian , elmRef.ShapeFunctionSecondDerivativePG) secondDerivativePG = np.matmul( elmRefGeom.inverseJacobian**2, elmRef.ShapeFunctionSecondDerivativePG) nop += nb_dir_deriv computeSecondDerivativeOp = True else: computeSecondDerivativeOp = False NbDoFperNode = np.shape(elmRef.ShapeFunctionPG)[1] // nNd_elm if NbDoFperNode > 1: #for bernoulli beam (of plate in the future) AngularDoF = True else: AngularDoF = False range_nbPG = np.arange(NumberOfGaussPoint) if Assembly.__GetChangeOfBasisMatrix(mesh) is 1: ChangeOfBasis = False else: ChangeOfBasis = True range_nNd_elm = np.arange(nNd_elm) #------------------------------------------------------------------- # Assemblage #------------------------------------------------------------------- gaussianQuadrature = (elmRefGeom.detJ * elmRefGeom.w_pg).T.reshape(-1) row = np.empty((Nel, NumberOfGaussPoint, nNd_elm)) col = np.empty((Nel, NumberOfGaussPoint, nNd_elm)) col2 = np.empty((Nel, NumberOfGaussPoint, nNd_elm)) data = [[ np.empty((Nel, NumberOfGaussPoint, nNd_elm)) for j in range(NbDoFperNode) ] for i in range(nop)] dataNodeToPG = np.empty((Nel, NumberOfGaussPoint, nNd_elm_geom)) row[:] = np.arange(Nel).reshape( (-1, 1, 1)) + range_nbPG.reshape(1, -1, 1) * Nel col[:] = elm.reshape((Nel, 1, nNd_elm)) if ChangeOfBasis: col2[:] = np.arange(Nel).reshape( (-1, 1, 1)) + range_nNd_elm.reshape((1, 1, -1)) * Nel dataPGtoNode = PGtoNode.T.reshape( (1, NumberOfGaussPoint, nNd_elm_geom)) / nb_elm_nd[elm_geom].reshape( (Nel, 1, nNd_elm_geom)) #shape = (Nel, NumberOfGaussPoint, nNd_elm) dataNodeToPG[:] = elmRefGeom.ShapeFunctionPG.reshape( (1, NumberOfGaussPoint, nNd_elm_geom)) data[0][0][:] = elmRef.ShapeFunctionPG[:, :nNd_elm].reshape( (1, NumberOfGaussPoint, nNd_elm)) for dir_deriv in range(nb_dir_deriv): data[dir_deriv + 1][0][:] = derivativePG[..., dir_deriv, :nNd_elm] if computeSecondDerivativeOp: data[1 + nb_dir_deriv + dir_deriv][0][:] = secondDerivativePG[..., dir_deriv, :nNd_elm] if AngularDoF: #angular dof for C1 elements for j in range(1, NbDoFperNode): data[0][j][:] = elmRef.ShapeFunctionPG[:, j * nNd_elm:(j + 1) * nNd_elm].reshape( (1, NumberOfGaussPoint, nNd_elm)) for dir_deriv in range(nb_dir_deriv): data[dir_deriv + 1][j][:] = derivativePG[..., dir_deriv, j * nNd_elm:(j + 1) * nNd_elm] if computeSecondDerivativeOp: data[1 + nb_dir_deriv + dir_deriv][j][:] = secondDerivativePG[ ..., dir_deriv, j * nNd_elm:(j + 1) * nNd_elm] row_geom = np.reshape(row[..., :nNd_elm_geom], -1) col_geom = np.reshape(col[..., :nNd_elm_geom], -1) row = np.reshape(row, -1) col = np.reshape(col, -1) col2 = np.reshape(col2, -1) if ChangeOfBasis: Ncol = Nel * nNd_elm else: Ncol = Nnd col2 = col op_dd = [[ sparse.coo_matrix((data[i][j].reshape(-1), (row, col2)), shape=(Nel * NumberOfGaussPoint, Ncol)).tocsr() for j in range(NbDoFperNode) ] for i in range(nop)] # data = [sparse.diags(gaussianQuadrature, 0, format='csr')] #matrix to get the gaussian quadrature (integration over each element) Assembly.__saveMatGaussianQuadrature[(mesh.GetID( ), NumberOfGaussPoint)] = sparse.diags( gaussianQuadrature, 0, format='csr' ) #matrix to get the gaussian quadrature (integration over each element) #matrix to compute the node values from pg # data.extend([sparse.coo_matrix((sp.reshape(dataPGtoNode,-1),(col,row)), shape=(Nel * nNd_elm , Nel*NumberOfGaussPoint) )]) Assembly.__savePGtoNodeMatrix[(mesh.GetID( ), NumberOfGaussPoint)] = sparse.coo_matrix( (dataPGtoNode.reshape(-1), (col_geom, row_geom)), shape=(Nnd, Nel * NumberOfGaussPoint) ).tocsr( ) #matrix to compute the node values from pg using the geometrical shape functions #matrix to compute the pg values from nodes using the geometrical shape functions (no angular dof) Assembly.__saveNodeToPGMatrix[(mesh.GetID( ), NumberOfGaussPoint)] = sparse.coo_matrix( (sp.reshape(dataNodeToPG, -1), (row_geom, col_geom)), shape=(Nel * NumberOfGaussPoint, Nnd) ).tocsr( ) #matrix to compute the pg values from nodes using the geometrical shape functions (no angular dof) data = {0: op_dd[0]} #data is a dictionnary for i in range(nb_dir_deriv): data[1, i] = op_dd[i + 1] if computeSecondDerivativeOp: data[2, i] = op_dd[i + 1 + nb_dir_deriv] Assembly.__saveOperator[(mesh.GetID(), elementType, NumberOfGaussPoint)] = data