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