2 #===============================================================================================================================
3 # Name : Résolution EF de l'équation de Laplace 3D -\Delta T = 0 avec conditions aux limites de Dirichlet u non nulle
4 # Authors : Michaël Ndjinga, Sédrick Kameni Ngwamou
5 # Copyright : CEA Saclay 2019
6 # Description : Utilisation de la méthode des éléménts finis P1 avec champs u discrétisés aux noeuds d'un maillage tétraédrique
7 # Condition limites correspondant au refroidissement dû à une fenêtre et au chauffage dû à un radiateur
8 # Création et sauvegarde du champ résultant ainsi que du champ second membre en utilisant la librairie CDMATH
9 #================================================================================================================================
14 # Fonction qui remplace successivement les colonnes d'une matrices par un vecteur donné et retourne la liste des déterminants
15 def gradientNodal(M, values):
16 matrices=[0]*(len(values)-1)
17 for i in range(len(values)-1):
18 matrices[i] = M.deepCopy()
19 for j in range(len(values)):
20 matrices[i][j,i] = values[j]
22 result = cdmath.Vector(len(values)-1)
23 for i in range(len(values)-1):
24 result[i] = matrices[i].determinant()
28 # Fonction qui calcule la valeur de la condition limite en un noeud donné
29 def boundaryValue(nodeId):
30 Ni=my_mesh.getNode(nodeId)
32 # 4 groupes sont considérés sur le bord
33 if boundaryNodes.count(nodeId)==0:
35 elif Ni.getGroupName()=='Fenetre':
37 elif Ni.getGroupName()=='Radiateur_sous-fenetre':
39 #elif Ni.getGroupName()=='Radiateur_Devant':
41 #elif Ni.getGroupName()=='Radiateur_droit':
46 #Chargement du maillage tétraédrique du domaine
47 #==============================================
48 my_mesh = cdmath.Mesh("Mesh_RadiatorAndWindow.med")
49 if( my_mesh.getSpaceDimension()!=3 or my_mesh.getMeshDimension()!=3) :
50 raise ValueError("Wrong space or mesh dimension : space and mesh dimensions should be 3")
51 if(not my_mesh.isTetrahedral()) :
52 raise ValueError("Wrong cell types : mesh is not made of tetrahedra")
54 nbNodes = my_mesh.getNumberOfNodes()
55 nbCells = my_mesh.getNumberOfCells()
57 print("Mesh loading done")
58 print("nb of nodes=", nbNodes)
59 print("nb of cells=", nbCells)
66 #Détermination des noeuds intérieurs
67 #======================================================================
70 maxNbNeighbours = 0#This is to determine the number of non zero coefficients in the sparse finite element rigidity matrix
74 #parcours des noeuds pour discrétisation du second membre et extraction 1) des noeuds intérieur 2) des noeuds frontière 3) du nb max voisins d'un noeud
75 for i in range(nbNodes):
78 if my_mesh.isBorderNode(i): # Détection des noeuds frontière getGroupName my_mesh.getNode(i)
79 boundaryNodes.append(i)
80 else: # Détection des noeuds intérieurs
81 interiorNodes.append(i)
82 maxNbNeighbours= max(1+Ni.getNumberOfEdges(),maxNbNeighbours)
84 nbInteriorNodes=len(interiorNodes)
85 nbBoundaryNodes=len(boundaryNodes)
88 print("nb of interior nodes=", nbInteriorNodes)
89 print("nb of Boundary nodes=", nbBoundaryNodes)
90 print("Max nb of neighbours=", maxNbNeighbours)
93 # Construction de la matrice de rigidité et du vecteur second membre du système linéaire
94 #=======================================================================================
95 Rigidite=cdmath.SparseMatrixPetsc(nbInteriorNodes,nbInteriorNodes,maxNbNeighbours)
96 RHS=cdmath.Vector(nbInteriorNodes)
98 # Vecteurs gradient de la fonction de forme associée à chaque noeud d'un tétrèdre (hypothèse 3D)
100 GradShapeFunc0=cdmath.Vector(3)
101 GradShapeFunc1=cdmath.Vector(3)
102 GradShapeFunc2=cdmath.Vector(3)
103 GradShapeFunc3=cdmath.Vector(3)
105 #On parcourt les tétraèdres du domaine pour remplir la matrice
106 for i in range(nbCells):
108 Ci=my_mesh.getCell(i)
110 #Extraction des noeuds de la cellule
111 nodeId0=Ci.getNodeId(0)
112 nodeId1=Ci.getNodeId(1)
113 nodeId2=Ci.getNodeId(2)
114 nodeId3=Ci.getNodeId(3)
115 N0=my_mesh.getNode(nodeId0)
116 N1=my_mesh.getNode(nodeId1)
117 N2=my_mesh.getNode(nodeId2)
118 N3=my_mesh.getNode(nodeId3)
137 #Values of each shape function at each node
143 GradShapeFunc0 = gradientNodal(M,values0)*(1./6)
144 GradShapeFunc1 = gradientNodal(M,values1)*(1./6)
145 GradShapeFunc2 = gradientNodal(M,values2)*(1./6)
146 GradShapeFunc3 = gradientNodal(M,values3)*(1./6)
148 #Création d'un tableau (numéro du noeud, gradient de la fonction de forme)
149 GradShapeFuncs={nodeId0 : GradShapeFunc0}
150 GradShapeFuncs[nodeId1]=GradShapeFunc1
151 GradShapeFuncs[nodeId2]=GradShapeFunc2
152 GradShapeFuncs[nodeId3]=GradShapeFunc3
154 # Remplissage de la matrice de rigidité et du second membre
155 for j in [nodeId0,nodeId1,nodeId2,nodeId3] :
156 if boundaryNodes.count(j)==0: #seuls les noeuds intérieurs contribuent au système linéaire (matrice de rigidité et second membre)
157 j_int=interiorNodes.index(j)#indice du noeud j en tant que noeud intérieur
158 boundaryContributionAdded=False#Needed in case j is a border cell
159 #Ajout de la contribution de la cellule ttétraédrique i au second membre du noeud j
160 for k in [nodeId0,nodeId1,nodeId2,nodeId3] :
161 if boundaryNodes.count(k)==0 : #seuls les noeuds intérieurs contribuent à la matrice du système linéaire
162 k_int = interiorNodes.index(k)#indice du noeud k en tant que noeud intérieur
163 Rigidite.addValue(j_int,k_int,GradShapeFuncs[j]*GradShapeFuncs[k]/Ci.getMeasure())
164 elif boundaryContributionAdded == False: #si condition limite non nulle au bord (ou maillage non recouvrant), ajouter la contribution du bord au second membre de la cellule j
165 # Valeurs de g_h aux noeuds du tétraèdre
166 T0 = boundaryValue(nodeId0)
167 T1 = boundaryValue(nodeId1)
168 T2 = boundaryValue(nodeId2)
169 T3 = boundaryValue(nodeId3)
170 boundaryContributionAdded=True#Contribution from the boundary to matrix line j is done in one step
171 GradGh = gradientNodal(M,[T0,T1,T2,T3])*(1./6)
172 RHS[j_int] += -(GradGh*GradShapeFuncs[j])/Ci.getMeasure()
176 print("Linear system matrix building done")
178 LS=cdmath.LinearSolver(Rigidite,RHS,100,1.E-6,"CG","ILU")#,"ILU" Remplacer CG par CHOLESKY pour solveur direct
182 # Création du champ résultat
183 #===========================
184 my_Temperature = cdmath.Field("Temperature", cdmath.NODES, my_mesh, 1)
185 for j in range(nbInteriorNodes):
186 my_Temperature[interiorNodes[j]]=SolSyst[j];#remplissage des valeurs pour les noeuds intérieurs SolSyst[j]
187 #Remplissage des valeurs pour les noeuds frontière (condition limite)
188 for j in boundaryNodes:
189 my_Temperature[j]=boundaryValue(j)
192 #sauvegarde sur le disque dur du résultat dans un fichier paraview
193 my_Temperature.writeVTK("FiniteElements3DTemperature")
195 print( "Minimum temperature= ", my_Temperature.min(), ", maximum temperature= ", my_Temperature.max() )
196 assert my_Temperature.min()>= min(Tmur,Tfenetre,Tradiateur) and my_Temperature.max()<= max(Tmur,Tfenetre,Tradiateur)
198 print( "Numerical solution of 3D Laplace equation using finite elements done" )